Documentation
¶
Overview ¶
Package web is a web framework for Vuka: routes, views and services are declared with decorators, and web.New gathers them into an app.
@web.Get("/pets/{id}")
@web.Template("pet.html")
func ShowPet(id int, store *PetStore) Result[Pet] { … }
func main() { web.New().Run(":8080") }
Everything a decorator declares is a call of the Routes registry, and an App's methods of the same names register on it alone:
app.Get("/pets/{id}", ShowPet, web.Params("id", "store"))
Index ¶
- Constants
- Variables
- func AcceptsHTML(r *http.Request) bool
- func AddPanel(name string, render func(notes []any) ui.Node)
- func Broadcast(ctx context.Context, topic string, msg any) error
- func ClientIP(r *http.Request) string
- func Controller(prefix string) func(*vuka.Type)
- func Cron(spec string) func(*vuka.Decl)
- func CurrentRequest() (ctx context.Context, serving bool)
- func Delete(path string) vuka.Bundle
- func Env[T any](key string, def ...T) T
- func Get(path string) vuka.Bundle
- func Handle(method, path string) vuka.Bundle
- func Implementation[T any](a *App) (T, error)
- func IsDev() bool
- func IsHTTPS(r *http.Request) bool
- func IsLiveJoin(ctx context.Context) bool
- func Live(path string, opts ...LiveOption) func(*vuka.Type)
- func LiveOrigin() context.Context
- func LivePage() context.Context
- func LoadEnv(files ...string) error
- func MaxBody(n int64) vuka.Bundle
- func MustEnv[T any](key string) T
- func Note(ctx context.Context, panel string, v any)
- func NoteRequest(ctx context.Context, n RequestNote)
- func NoteStatement(ctx context.Context, q Statement)
- func PageQuery(r *http.Request) (page, size int)
- func Paginate[Q Pageable[Q, T], T any](ctx context.Context, q Q, page, size int) (ui.Page[T], error)
- func PaginateSource[T any](ctx context.Context, src Source[T], page, size int) (ui.Page[T], error)
- func Patch(path string) vuka.Bundle
- func Post(path string) vuka.Bundle
- func Put(path string) vuka.Bundle
- func RegisterAssets(manifest []byte)
- func RegisterCSS(name string, css []byte)
- func RegisterIsland(name string, js []byte)
- func RegisterScript(name string, js []byte)
- func Request(ctx context.Context) *http.Request
- func Resolve[T any](a *App) (T, error)
- func ResponseWriter(ctx context.Context) http.ResponseWriter
- func Service(t *vuka.Type)
- func Slice[T any](rows []T, page, size int) ui.Page[T]
- type App
- func (a *App) Broadcast(ctx context.Context, topic string, msg any) error
- func (a *App) Build() error
- func (a *App) Check(fn func(*App) error) *App
- func (a *App) CloseLive(match func(ctx context.Context) bool) int
- func (a *App) Controller(prefix string, ctor any, opts ...RouteOption) *App
- func (a *App) Cron(name, spec string, fn any) *App
- func (a *App) Delete(path string, fn any, opts ...RouteOption) *App
- func (a *App) Eager(ctors ...any) *App
- func (a *App) Get(path string, fn any, opts ...RouteOption) *App
- func (a *App) Handle(method, path string, fn any, opts ...RouteOption) *App
- func (a *App) Handler() http.Handler
- func (a *App) Live(path string, page any, opts ...RouteOption) *App
- func (a *App) OnStart(fn func(context.Context) error) *App
- func (a *App) OnStop(fn func(context.Context) error) *App
- func (a *App) Page(path string, component any, attrs ...any) *App
- func (a *App) Patch(path string, fn any, opts ...RouteOption) *App
- func (a *App) Post(path string, fn any, opts ...RouteOption) *App
- func (a *App) Provide(ctors ...any) *App
- func (a *App) Put(path string, fn any, opts ...RouteOption) *App
- func (a *App) Run(addr string) error
- func (a *App) RunCron(ctx context.Context, name string) error
- func (a *App) Service(ctor any) *App
- func (a *App) Start(ctx context.Context) error
- func (a *App) Static(prefix string, fsys fs.FS) *App
- func (a *App) Stop(ctx context.Context) error
- func (a *App) Toolbar() bool
- func (a *App) Use(mw ...Middleware) *App
- func (a *App) WaitCrons(ctx context.Context) error
- func (a *App) WriteError(w http.ResponseWriter, r *http.Request, err error, html bool)
- type Asset
- type BodyLimit
- type CronLock
- type Error
- type FrontendOption
- type Invalid
- type LiveEvent
- type LiveHandler
- type LiveOption
- type Middleware
- type Option
- func CSRF(trusted ...string) Option
- func Check(fn func(*App) error) Option
- func CronClock(c cron.Clock) Option
- func CronContext(f func(context.Context) context.Context) Option
- func Dev() Option
- func DotEnv(files ...string) Option
- func Eager(ctors ...any) Option
- func ExactPaths() Option
- func Frontend(fsys fs.FS, root string, opts ...FrontendOption) Option
- func GuardLiveEvents(f func(ctx context.Context, h LiveHandler) error) Option
- func Headers(h SecurityHeaders) Option
- func IslandRoot(dir string) Option
- func LiveAdopt(window time.Duration) Option
- func LiveCheck(f func(ctx context.Context) error) Option
- func LiveCheckEvery(d time.Duration) Option
- func LiveIdentity(f func(ctx context.Context) string) Option
- func LiveIdleTimeout(d time.Duration) Option
- func LiveJoinTimeout(d time.Duration) Option
- func LiveKey(key []byte) Option
- func LiveLayout(layout func(page ui.Node) ui.Node, assets ...Asset) Option
- func LiveMaxConns(n int) Option
- func LiveMaxConnsPerIP(n int) Option
- func LiveMaxConnsPerPage(n int) Option
- func LiveOrigins(patterns ...string) Option
- func LiveProtocol(v int) Option
- func LiveTokenTTL(d time.Duration) Option
- func MaxRequestBody(n int64) Option
- func NoCrons() Option
- func NoDotEnv() Option
- func NoSecurityHeaders() Option
- func NoTailwind() Option
- func NoToolbar() Option
- func OnError(f func(w http.ResponseWriter, r *http.Request, e *Error, html bool)) Option
- func OnStart(fn func(context.Context) error) Option
- func OnStop(fn func(context.Context) error) Option
- func Only(pkgs ...string) Option
- func Options(opts ...Option) Option
- func ParamFrom[T any](f func(r *http.Request) (T, error)) Option
- func Provide(ctors ...any) Option
- func Require(keys ...string) Option
- func Tailwind(path string) Option
- func TrackRequests() Option
- func TrustedProxies(cidrs ...string) Option
- func WithRelay(r Relay) Option
- func WithRouter(r Router) Option
- func WrapRoutes(name string, f func(RouteInfo) Middleware) Option
- type OptionFunc
- type PageParams
- type Pageable
- type Pager
- func (p *Pager[T]) Err() error
- func (p *Pager[T]) GoTo(ctx context.Context, n int) error
- func (p *Pager[T]) Load(ctx context.Context) error
- func (p *Pager[T]) Next(ctx context.Context) error
- func (p *Pager[T]) Page() int
- func (p *Pager[T]) Prev(ctx context.Context) error
- func (p *Pager[T]) Reload(ctx context.Context) error
- func (p *Pager[T]) Result() ui.Page[T]
- func (p *Pager[T]) SetSize(ctx context.Context, size int) error
- func (p *Pager[T]) Size() int
- type Redirect
- type Registry
- func (g *Registry) Controller(prefix string, ctor any, opts ...RouteOption) *Registry
- func (g *Registry) Delete(path string, fn any, opts ...RouteOption) *Registry
- func (g *Registry) Get(path string, fn any, opts ...RouteOption) *Registry
- func (g *Registry) Handle(method, path string, fn any, opts ...RouteOption) *Registry
- func (g *Registry) Live(path string, page any, opts ...RouteOption) *Registry
- func (g *Registry) Patch(path string, fn any, opts ...RouteOption) *Registry
- func (g *Registry) Post(path string, fn any, opts ...RouteOption) *Registry
- func (g *Registry) Put(path string, fn any, opts ...RouteOption) *Registry
- func (g *Registry) Service(ctor any) *Registry
- type Relay
- type RequestNote
- type Rest
- type RouteAttr
- type RouteInfo
- type RouteOption
- func Attrs(attrs ...any) RouteOption
- func GuardOf[T any]() RouteOption
- func LayoutOf[T any]() RouteOption
- func PageRoute() RouteOption
- func Params(names ...string) RouteOption
- func Receiver(ctor any) RouteOption
- func UseOf(mw any) RouteOption
- func View(f vuka.File, name ...string) RouteOption
- func Wrap(mw ...Middleware) RouteOption
- type Router
- type SecurityHeaders
- type ServeMux
- type Source
- type Starter
- type Statement
- type Stopper
- type Templates
- type URLParams
- type Uses
Constants ¶
const ( // ToolbarHeader names the toolbar record of a development app's // response; the toolbar lists a page's fetch and XHR calls by it. ToolbarHeader = "X-Vuka-Toolbar" // DevDirEnv names the session directory vuka run passes the app: the // toolbar is on, and with -watch the directory's build.json says how // the next build is going. DevDirEnv = "VUKA_DEV_DIR" )
const ( // AssetsEnv names the directory vuka run keeps the compiled assets in: // manifest.json, the modules (c/), and the island chunks (i/). AssetsEnv = "VUKA_ASSETS" )
const (
// CSSEnv names the stylesheet file vuka run keeps up to date.
CSSEnv = "VUKA_TAILWIND_CSS"
)
const DefaultPageSize = 20
DefaultPageSize is the page size Paginate and the request binders use when none is given or asked for.
const Global = ":global"
Global, among an asset's arguments, makes it unscoped: web.Style("print.css", web.Global) styles the whole page, and a global script's hooks run once on the page, el being document.body.
const MaxPageSize = 200
MaxPageSize caps a size taken from the request, so a client can't ask for an unbounded page.
Variables ¶
var ( NotFound = &Error{Status: http.StatusNotFound, Message: "not found"} Forbidden = &Error{Status: http.StatusForbidden, Message: "forbidden"} Conflict = &Error{Status: http.StatusConflict, Message: "conflict"} )
var ErrBodyTooLarge = &Error{Status: http.StatusRequestEntityTooLarge, Message: "request body too large"}
ErrBodyTooLarge answers a request whose body is over its limit.
var ErrCronRunning = errors.New("the previous run is still running")
ErrCronRunning is RunCron's error when the cron's previous run hasn't finished: runs never overlap.
var Routes = &Registry{r: registry{views: map[string]declView{}, uses: map[string][]Middleware{}}}
Routes is the registry of the program's declared routes, pages, controllers and services.
Functions ¶
func AcceptsHTML ¶ added in v0.11.0
AcceptsHTML reports whether r asks for an HTML page (its Accept header names text/html): a browser's navigation or form post, not a script's fetch.
func AddPanel ¶ added in v0.9.0
AddPanel adds a panel to the development toolbar: render draws it for a request from the values the request noted for it (Note), in order. A panel added again under the same name replaces it.
func Broadcast ¶ added in v0.2.0
Broadcast delivers msg to the Update methods of every live component subscribed to topic, in every connected page of the app serving ctx (a request's, or a live event's), and sends each page what changed. A topic of "" reaches every component.
func ClientIP ¶ added in v0.15.0
ClientIP is the address r comes from: its connection's peer, or, when that is one of the app's TrustedProxies, the nearest address in X-Forwarded-For (else Forwarded) that isn't. Throttles and connection caps count by it.
func Controller ¶
Controller makes a struct a service whose route methods are served under prefix.
func Cron ¶ added in v0.11.0
Cron runs the function on a schedule (see package cron for the syntax):
@web.Cron("@every 30s")
@web.Cron("0 7 * * *")
@web.Cron("CRON_TZ=Africa/Dar_es_Salaam 0 7 * * 1-5")
func cleanUp(ctx context.Context, store *PetStore) error { … }
Its parameters are a context.Context and provided types; on a method, the receiver is provided too. The cron's name is the function's ("cleanUp"; "jobs.cleanUp" outside package main; "PetStore.Refresh" for a method). An invalid spec fails Build, naming the function and where it is declared.
func CurrentRequest ¶ added in v0.15.0
CurrentRequest reports whether the calling goroutine serves an HTTP request of an app with TrackRequests — a live page's connection included — rather than being one the app's own work runs on (a cron's, a job's, main), and the context of the route it is running: a handler's, with its middleware's values; nil outside a route (in app-wide middleware, on a live connection between its routes' runs).
func Env ¶ added in v0.2.0
Env returns the environment variable key as a T: a string, bool, integer, float, time.Duration, *url.URL (absolute), a type implementing encoding.TextUnmarshaler, or a slice of those (comma-separated). When key is unset or empty it returns def (or the zero value); when the value doesn't convert, it returns def too and the next Build or Start reports the error.
func Handle ¶
Handle declares a route for method and path: @web.Handle("OPTIONS", "/x"). Path segments in braces bind parameters of the same name: "/pets/{id}"; "{rest...}" matches the rest of the path.
On a type — an @ui.Island struct, with @web.Get — it declares a stateless page backed by that component (see "Pages backed by a component" in the islands guide): its exported fields are the component's props, its exported methods what the component calls. A live one (embedding ui.Live) is declared with @web.Live.
func Implementation ¶ added in v0.11.0
Implementation returns the app's value implementing the interface T: the one provided as T, else the one provided type implementing it. It is an error when none does, or several do. Unlike Resolve it doesn't build the app, so a Check may call it.
func IsDev ¶ added in v0.15.0
func IsDev() bool
IsDev reports whether the process runs in development: VUKA_ENV is "development", VUKA_DEV is 1, or vuka run names its session directory (VUKA_DEV_DIR). Both come from the real environment only — an env file can't set them — so a deployed binary is never switched into development by a file next to it. (An app made with the Dev option is in development whatever IsDev says.)
func IsHTTPS ¶ added in v0.15.0
IsHTTPS reports whether r came over HTTPS: its own TLS, or a trusted proxy's X-Forwarded-Proto.
func IsLiveJoin ¶ added in v0.13.0
IsLiveJoin reports whether ctx is a live page's route running again for its connection's join: the page was served already (its GET), and the connection rebuilds it — on the first join, and on each reconnect. A handler with side effects — sending mail, counting a visit — skips them then, so they happen once per visit:
if !web.IsLiveJoin(ctx) {
mailer.Send(ctx, welcome)
}
It is false for the GET itself, for a navigation over the connection (a visit of its own, replacing an HTTP load), and for the page's events and renders after the join, though their context descends from the join's. A client can't make it true: web marks its own run, not a header.
The route runs for every join whether or not the join adopts the GET's session (LiveAdopt) — middleware, gates and the handler itself, which sees IsLiveJoin true either way. Adoption only skips mounting the page's components again, so Mount runs once per view when the join arrives in time; an expired window, a restart or a reconnect mounts them again.
func Live ¶ added in v0.2.0
func Live(path string, opts ...LiveOption) func(*vuka.Type)
Live serves a stateful component as a page at path, a GET route:
@web.Live("/rooms/{room}")
type Chat struct {
vuka.Live
Room string // a prop bound from the path segment, else ?room=
Store *ChatStore // a prop of a provided type, injected
log []string // state
}
Its exported fields of provided types are injected; the others that hold text bind from the path segment or query parameter of their name. The page is the component inside the app's layout (LiveLayout), or the one Layout names, or else a bare HTML document. Use adds middleware.
On an @ui.Island struct embedding ui.Live it is a live page backed by that component (see islandpage.go); the island's ui.Layout wins over Layout.
func LiveOrigin ¶ added in v0.13.0
LiveOrigin is the context of the live page whose connection started the calling goroutine — the page's route's, carrying its identity — when a goroutine the connection's own goroutine started calls it: a goroutine an event's handler starts (go b.Clear()), or one a decorator runs a call on. It is nil on any other goroutine: one started further down (by such a goroutine), a pool's worker, a request's, a cron's; and once the page's connection has closed. It reads the goroutine's stack (a few microseconds): a check that has a context.Context asks it instead.
func LivePage ¶ added in v0.15.0
LivePage is the context of the live page whose connection the calling goroutine serves — the page's route's, carrying its identity — whatever it is doing: handling an event, rendering, delivering a broadcast. It is nil on any other goroutine, and before the connection's first join.
func LoadEnv ¶ added in v0.2.0
LoadEnv reads env files into the process environment, as New does: for code that needs their values before New, such as options built from them. No files means the default list (see the README); a key is set only when the environment doesn't have it yet, and the first file listed wins over the later ones. Loading the same files again is a no-op, so New reading them afterwards changes nothing.
func MaxBody ¶ added in v0.15.0
MaxBody bounds the request body of one route, in bytes (negative for none), in place of the app's MaxRequestBody:
@web.Post("/videos")
@web.MaxBody(512 << 20)
func Upload(r *http.Request) error { … }
From Go: app.Post("/videos", h, web.Attrs(web.MaxBody(512 << 20))).
func MustEnv ¶ added in v0.2.0
MustEnv is Env for values that must be there: it panics, naming key, when the variable is unset, empty or doesn't convert.
func Note ¶ added in v0.9.0
Note records v for the toolbar's panel named panel, drawn by the function AddPanel registered for it, else as a list of the values. It does nothing for a context that no development toolbar watches, so it can stay in production code.
func NoteRequest ¶ added in v0.11.0
func NoteRequest(ctx context.Context, n RequestNote)
NoteRequest adds a row to the development toolbar's Request panel for the request (or live message) of ctx: what a library decided about it — who is signed in, which gate let it through. It does nothing for a context that no toolbar watches. Never note a secret.
func NoteStatement ¶ added in v0.9.0
NoteStatement records a statement the request of ctx (or its live page's socket message) ran, for the toolbar's SQL panel. It does nothing for a context that no development toolbar watches.
func PageQuery ¶ added in v0.21.0
PageQuery reads page and size straight from a request's query string, for a handler that took the *http.Request rather than a bound struct.
func Paginate ¶ added in v0.21.0
func Paginate[Q Pageable[Q, T], T any](ctx context.Context, q Q, page, size int) (ui.Page[T], error)
Paginate reads page (1-based) and size from q — an ORM query (orm.For[T](), a QuerySet) or anything matching Query — with one COUNT and one windowed fetch, and returns the page:
p, err := web.Paginate(ctx, orm.For[Book]().Where(…), page, size)
for _, b := range p.Items { … }
page and size are clamped (size to at least 1, page to the list); it does not run the window fetch when the count is zero.
func PaginateSource ¶ added in v0.21.0
PaginateSource is Paginate over a Source: a store that counts and slices itself, with no ORM.
func RegisterAssets ¶ added in v0.10.0
func RegisterAssets(manifest []byte)
RegisterAssets registers the manifest of the app's compiled styles and scripts. vuka build calls it from a file it generates in the main package.
func RegisterCSS ¶ added in v0.6.0
RegisterCSS registers a stylesheet the app serves at /_vuka/<name>; the one named "app.css" is linked from every full page. vuka build calls it from a file it generates in the main package, with the Tailwind CSS built for the binary.
func RegisterIsland ¶ added in v0.22.0
RegisterIsland registers an island's bundled chunk (component or adapter), served at /_vuka/i/<name> and cached for good: its name carries its content's hash. vuka build calls it from the file it generates.
func RegisterScript ¶ added in v0.10.0
RegisterScript registers a component's module, served at /_vuka/c/<name> and cached for good: its name carries its content's hash. vuka build calls it from the file it generates.
func Request ¶
Request is the request being served, from a context.Context a handler received: call decorators reach it through c.Context().
func ResponseWriter ¶
func ResponseWriter(ctx context.Context) http.ResponseWriter
ResponseWriter is the response being written, from a handler's context.
Types ¶
type App ¶
type App struct {
// contains filtered or unexported fields
}
App is a web application: the routes, services and controllers declared with decorators, and whatever was added in code.
func New ¶
New makes an app of every route, service and controller declared so far (each app takes its own snapshot, with its own singletons). It first reads the env files into the process environment (see LoadEnv), so constructors and Start methods see their values through os.Getenv.
func (*App) Broadcast ¶ added in v0.2.0
Broadcast delivers msg to the components subscribed to topic, as the package's Broadcast does.
func (*App) Build ¶
Build resolves the app: every route's parameters, results and view, and the dependency graph. It reports every problem at once, before anything is served; Handler, Start and Run call it.
func (*App) Check ¶ added in v0.2.0
Check adds fn to the checks Build runs once the routes are compiled; its error is reported with Build's own.
func (*App) CloseLive ¶ added in v0.17.0
CloseLive closes the app's joined live pages whose context — the one their route ran with, as LiveCheck gets it — match reports true for, with code 4001: each page reloads, its route running again for the browser. It returns how many it closes. A page closes before it handles another message or broadcast; one handling a message closes once that is done. match runs on the caller's goroutine, once per joined page: a library ending a session closes the pages signed in with it this way.
func (*App) Controller ¶ added in v1.0.0
func (a *App) Controller(prefix string, ctor any, opts ...RouteOption) *App
Controller makes the struct ctor builds a service whose route methods are served under prefix, as Routes.Controller does for every app.
func (*App) Cron ¶ added in v0.11.0
Cron runs fn on a schedule, as @web.Cron does; name names it for RunCron, the logs and the toolbar. fn takes a context.Context and provided types.
func (*App) Eager ¶ added in v0.2.0
Eager adds constructors whose values Start builds (and starts, when they implement Starter) even when nothing needs them. Provided values that aren't functions always start; constructors otherwise only when needed.
func (*App) Handle ¶
func (a *App) Handle(method, path string, fn any, opts ...RouteOption) *App
Handle registers fn for method and path, as Routes.Handle does for every app.
func (*App) Handler ¶
Handler is the app as an http.Handler, to serve or mount anywhere. It panics when Build fails.
func (*App) Live ¶ added in v0.2.0
func (a *App) Live(path string, page any, opts ...RouteOption) *App
Live serves a stateful component as a page at path, as Routes.Live does for every app: page is its constructor (NewBoard), or a pointer to the component whose fields are the defaults.
func (*App) OnStart ¶ added in v0.2.0
OnStart adds fn to the app's start sequence, after the values supplied and the hooks added before it and before the values Start constructs.
func (*App) OnStop ¶ added in v0.2.0
OnStop adds fn to the app's stop sequence, which runs in the reverse order of the start sequence: fn runs after everything started after it was added has stopped.
func (*App) Page ¶ added in v0.30.0
Page serves a page backed by component's island at path, as @web.Get on its type does: component is a pointer to the @ui.Island struct (only its type counts). attrs are the type's attributes as a decorator would give them (auth gates…).
func (*App) Run ¶
Run serves the app on addr (":" + $PORT, else ":8080", when empty) until SIGINT or SIGTERM, then shuts down gracefully and stops the services.
func (*App) RunCron ¶ added in v0.11.0
RunCron runs the cron name now, in the caller's goroutine with ctx, and returns its error (a panic's too). It doesn't wait for a run in progress: it returns ErrCronRunning. For tests, and commands running a cron by hand; the schedule's lock isn't asked.
func (*App) Service ¶ added in v1.0.0
Service provides the struct ctor builds as a lazy singleton, as Routes.Service does for every app.
func (*App) Start ¶
Start builds every dependency the routes and crons need and every Eager one, then runs the start sequence: each supplied or built value implementing Starter and each OnStart hook, in dependency order (supplied values and hooks as added, before the values built from constructors), and last the crons' scheduler, which Stop stops first.
func (*App) Static ¶
Static serves the files of fsys under prefix: a directory as its index.html, never as a listing, and no dotfile or dot directory but .well-known.
func (*App) Stop ¶
Stop runs the stop sequence: each value implementing Stopper and each OnStop hook, in the reverse order of the start sequence.
func (*App) Toolbar ¶ added in v0.9.0
Toolbar reports whether the app shows the development toolbar: for a library feeding it (NoteStatement) only then.
func (*App) Use ¶
func (a *App) Use(mw ...Middleware) *App
Use adds app-wide middleware; the first added runs first.
func (*App) WaitCrons ¶ added in v0.11.0
WaitCrons waits until no run the schedule (or the toolbar) started is in progress, or ctx is done: for tests moving a virtual clock, before they move it again — a tick finding the previous run still going is skipped.
func (*App) WriteError ¶ added in v0.11.0
WriteError answers r with err as a handler's error is answered: its status and message (a *web.Error), an HTML page when html is set, JSON otherwise, or what OnError writes.
type Asset ¶ added in v0.10.0
type Asset struct {
// contains filtered or unexported fields
}
Asset is a stylesheet or script for a page: an option of @web.Live, web.Layout, web.LiveLayout and web.New. The vuka command reads it from the source and compiles it; the app itself only serves what it compiled.
func CSS ¶ added in v0.10.0
CSS is a stylesheet written inline, as Style's files are: web.CSS(`output { … }`).
func Script ¶ added in v0.10.0
Script names hook modules (TypeScript or JavaScript) for the component the option belongs to, as Style does stylesheets. They are bundled in order into one module, loaded when the component first renders on a page; each may export default { mounted(el, ctx), updated(el, ctx), destroyed(el, ctx), …methods }. ui.JS.Call("name") reaches the component's own methods before vuka.register's and window's.
func Style ¶ added in v0.10.0
Style names stylesheets for the component the option belongs to: the live component, or the layout's own markup (app-wide, for web.New). Paths are relative to the .vuka file and may be globs (parts/*.css, styles/**/*.css), expanded in name order; they must be string literals, since the vuka command reads them from the source. Selectors match only the elements the component's own markup renders: :global(…) and :root stay unscoped, :deep(…) reaches into child components, :scope is the component's root element. The stylesheets compile with the app's Tailwind CSS, in the order listed (later ones win), so @apply, theme() and variants work.
type BodyLimit ¶ added in v0.15.0
type BodyLimit int64
BodyLimit is the attribute MaxBody puts on a route.
type CronLock ¶ added in v0.11.0
CronLock decides which process runs a scheduled tick. Each process of an app schedules its crons by itself, so with several replicas every one runs every tick; provide a CronLock (backed by a database or Redis) and only the replica that claims a tick runs it. Every replica asks for the same name and tick — @every ticks too are aligned to a fixed origin — so a lock is a unique key: an INSERT of (name, tick) into a table with a unique index, or Redis's SET name:tick NX with an expiry. Without one, the app uses NewMemoryCronLock, which claims every tick. RunCron doesn't ask.
func NewMemoryCronLock ¶ added in v0.11.0
func NewMemoryCronLock() CronLock
NewMemoryCronLock is the lock of a single process: it claims each tick of a cron once.
type Error ¶
Error is an error with an HTTP status. Returned by a handler (wrapped or not), it answers with that status and message: an HTML page for an HTML route, JSON otherwise.
func (*Error) PublicMessage ¶ added in v0.15.0
PublicMessage is the message the browser reads (live.PublicError): a live page's handler returning an *Error shows its message, as an HTTP route's answer does, where any other error's text stays in the log.
func (*Error) StatusCode ¶ added in v0.30.0
StatusCode is the error's status.
type FrontendOption ¶ added in v0.11.0
type FrontendOption func(*frontend)
FrontendOption configures a Frontend.
func FrontendAt ¶ added in v0.11.0
func FrontendAt(prefix string) FrontendOption
FrontendAt serves the frontend under prefix ("/admin") instead of the root: build it with the same base (Vite's base: "/admin/").
func FrontendEnv ¶ added in v0.11.0
func FrontendEnv(keys ...string) FrontendOption
FrontendEnv passes the values of these environment variables (and .env keys, which New has loaded) to the frontend's pages: index.html sets them on a global before any module runs, and the Vite plugin turns import.meta.env.KEY into a read of it. Only the keys named are passed; they reach every visitor, so name only public values. Unset keys are left out.
func FrontendRoot ¶ added in v0.22.0
func FrontendRoot(dir string) FrontendOption
FrontendRoot names the frontend project folder — the directory that holds the Vite project (package.json, src, node_modules) whose build output is the dist Frontend serves. The vuka command reads it to root the island build: it is esbuild's resolve base (node_modules for framework islands) and where islands.d.ts is written. It does not change where an @ui.Island("…") path resolves — that is relative to the Go file declaring the island. When unset, the build uses the parent of Frontend's root (its dist's directory).
type Invalid ¶ added in v0.30.0
Invalid is an error naming fields and what is wrong with each: a component's call answering with it shows each field's message (live.error.fields), a route answers 422 with them.
return web.Invalid{"q": "type at least 2 letters"}
func (Invalid) FieldErrors ¶ added in v0.30.0
func (Invalid) PublicMessage ¶ added in v0.30.0
func (Invalid) StatusCode ¶ added in v0.30.0
type LiveEvent ¶ added in v0.11.0
type LiveEvent struct {
// contains filtered or unexported fields
}
LiveEvent is the live event a goroutine is handling: CurrentLiveEvent returns it to the event's handler and to its decorators, which, unlike the handler, may have no context.Context to read.
func CurrentLiveEvent ¶ added in v0.11.0
func CurrentLiveEvent() *LiveEvent
CurrentLiveEvent is the live event the calling goroutine handles, nil outside one: web's connections handle an event's handler, and every decorator around it, on the connection's goroutine.
func (*LiveEvent) Context ¶ added in v0.11.0
Context is the context the page's route ran with (the join's, or the latest navigation's): its request's values, cookies and middleware's.
func (*LiveEvent) Handler ¶ added in v0.11.0
Handler names the handler the event targets: "Admin.Delete", or "Admin.Render.func1" for a function literal.
func (*LiveEvent) Handling ¶ added in v0.11.0
Handling reports whether the event's handler is running — the handler itself, or anything it calls, a function literal's calls included — rather than the render of what it changed, which follows it on the same goroutine. A gate refusing an event checks calls made while Handling.
type LiveHandler ¶ added in v0.13.0
type LiveHandler struct {
Name string // "Board.Clear"; "Board.Render.func1" for a function literal
// Component is the struct type of the component whose event it is; nil
// for a handler of a page that is no component.
Component reflect.Type
// Recv and Method are a method handler's receiver type (Board for
// *Board) and name; Recv is nil for a type Vuka registered nothing for.
Recv reflect.Type
Method string
// Attrs are the handler's typed attributes, written on its declaration
// (vuka.FuncOf); none for a function literal or a function without.
// The types' own are vuka.TypeAttrs(Component) and
// vuka.TypeAttrs(Recv).
Attrs []any
// Literal says the handler is a function literal (onClick={func() { … }}):
// it has no attributes, and the methods it calls are checked by their
// own decorators when it runs.
Literal bool
}
LiveHandler describes the handler a live event targets, for a guard (GuardLiveEvents): what Vuka's registry knows of its declaration and of the component it belongs to.
type LiveOption ¶ added in v0.2.0
type LiveOption interface {
RouteOption
// contains filtered or unexported methods
}
LiveOption configures a page declared with Live; it is a RouteOption too.
type Middleware ¶
Middleware wraps an http.Handler.
type Option ¶
type Option interface {
// contains filtered or unexported methods
}
Option configures an App.
func CSRF ¶ added in v0.11.0
CSRF refuses cross-origin requests that change state: a POST, PUT, PATCH or DELETE a browser sends from another site's page (a form posted to the app, a fetch) answers 403, so a signed-in user's cookie can't be used by a page they happen to visit. Browsers say where a request comes from (Sec-Fetch-Site, else Origin), and net/http's CrossOriginProtection reads that: no token to put in every form. GET, HEAD and OPTIONS are never refused, so keep them free of side effects.
trusted are other origins allowed to send such requests ("https://admin.example.com"). A request carrying an Authorization: Bearer header is let through: a browser never adds one by itself, so it can't be forged by another site. Requests that say nothing of where they come from (curl, servers) are let through too. Live connections check their origin themselves (LiveOrigins).
func CronClock ¶ added in v0.11.0
CronClock runs the app's crons by clock instead of the system's: a virtual one in tests (crontest.Clock).
func CronContext ¶ added in v0.15.0
CronContext has every cron run with its context passed through f: a library marking crons as the app's own work.
func Dev ¶
func Dev() Option
Dev turns on development where the process runs in it: VUKA_DEV=1 or VUKA_ENV=development (`VUKA_DEV=1 go run .`). Responses then show internal errors' messages and the toolbar is on, and Start runs tailwindcss --watch and each frontend's Vite dev server, when installed, and, for an app registering islands, vuka build-islands -watch (the vuka command a tool of the module or on PATH), the app serving through them; Stop ends them. Without the tools the app serves the last build and logs one line saying how to get them. Under vuka run, which runs them itself, it starts nothing. In a test binary it turns on development without starting anything; elsewhere — a production binary — it does nothing, so main can keep it.
func DotEnv ¶ added in v0.2.0
DotEnv replaces the env files New reads (see LoadEnv); the first listed wins. Relative paths are read from the working directory, or the nearest parent up to the module root holding one of them.
func Eager ¶ added in v0.2.0
Eager provides constructors whose values are built and started by Start even when no route needs them (workers, schedulers).
func ExactPaths ¶ added in v0.14.0
func ExactPaths() Option
ExactPaths turns off the trailing-slash redirect: a path differing from a route only by a trailing slash is then a 404 (or whatever else answers it, a frontend's index.html).
func Frontend ¶ added in v0.11.0
func Frontend(fsys fs.FS, root string, opts ...FrontendOption) Option
Frontend serves a built single-page app from fsys, the files under root:
//go:embed all:frontend/dist var dist embed.FS app := web.New(web.Frontend(dist, "frontend/dist"))
A GET path naming a file (its last segment has an extension) is that file, or 404; any other GET path is index.html, so the app's client-side routes work on a reload and from a link. The app's own routes, live pages and /_vuka/ are matched first. Files Vite's manifest lists as build output whose names carry a content hash are cached for good; everything else, index.html included, is revalidated by its ETag. The .vite directory is never served.
The app's islands are embedded with it: go tool vuka build-islands writes them into root's .vuka directory, and the app serves them from there. A build of islands alone, with no index.html, serves them and answers other paths 404:
//go:generate go tool vuka build-islands
Outside development a root without index.html or islands is a Build error. In development (Dev, VUKA_ENV=development, or vuka run) the files are read from disk when root is found there (beside the source calling Frontend, else in the working directory), a running Vite dev server serves the page's modules, and a frontend not built yet shows a page saying how to build it.
func GuardLiveEvents ¶ added in v0.13.0
func GuardLiveEvents(f func(ctx context.Context, h LiveHandler) error) Option
GuardLiveEvents has f decide each live event before its handler runs, given the page's context (the identity it joined with) and the handler: an error refuses the event — the handler doesn't run, and the page receives the error as a handler's (vk:error). A library gating events reads the handler's attributes and its component's type's here, where web dispatches, rather than only inside the handler's call.
func Headers ¶ added in v0.15.0
func Headers(h SecurityHeaders) Option
Headers replaces the defaults of the security headers every response carries (see SecurityHeaders): a field left zero keeps its default.
func IslandRoot ¶ added in v0.30.0
IslandRoot names the folder island components build in, for an app that serves no frontend of its own (no Frontend): the one holding package.json and node_modules (vue, react, svelte, esbuild…), where islands.d.ts and vuka-live.d.ts are written. The vuka command reads it; it changes nothing at run time. The module root is the default.
func LiveAdopt ¶ added in v0.16.0
LiveAdopt is how long after a live page's GET its join may adopt the session the render mounted, instead of mounting the page's components again: 10 seconds by default, enough for the page to load and connect. 0 (or less) turns adoption off: every join mounts fresh, and renders park nothing. The join still runs the page's route — middleware, gates and the handler, with the connection's own cookies — whether or not it adopts; only the mounting of components it skips.
func LiveCheck ¶ added in v0.11.0
LiveCheck has each joined live page checked with f every LiveCheckEvery (30 seconds by default), given the context the page's route ran with: an error closes the connection with code 4001 and the page reloads, its route running again for the browser. A page is checked when it mounts by its route itself — the join and each navigation run it through the app's middleware and gates — and its events and broadcasts trust what it mounted with; LiveCheck is the net for what changes while it sits open without the app hearing of it (a session deleted by another process). A library that ends sessions itself closes their pages at once with CloseLive.
func LiveCheckEvery ¶ added in v0.15.0
LiveCheckEvery runs the app's LiveChecks on each joined page this often: 30 seconds by default; 0 never runs them. A page some check fails closes with code 4001 and reloads, so a session ended where the app doesn't hear of it (another process, a store edited by hand) or a revoked permission reaches pages that sit open; an ending the app hears of closes them at once (CloseLive).
func LiveIdentity ¶ added in v0.16.0
LiveIdentity tells live adoption and stateless pages' signed props who a context is signed in as: a stable key for the identity and its sign-in ("" for no one). A join adopts a parked session only when every registered function returns for the join's context what it returned for the render's — the same sign-in, or anonymous both times — and a stateless page's call verifies only for the visitor its props were signed for. A library that signs visitors in registers one, so one visitor's page is never adopted, or its props replayed, by another.
func LiveIdleTimeout ¶ added in v0.15.0
LiveIdleTimeout is how long a joined page may send nothing before its connection closes and its session, with its components' state, is dropped: 30 minutes by default; 0 never closes one. The runtime connects again, rebuilding the page's session, at once when the page is in view and otherwise when it is shown again; broadcasts don't keep a page from being idle.
func LiveJoinTimeout ¶ added in v0.15.0
LiveJoinTimeout is how long a live connection may stay open without joining its page: 10 seconds by default. A socket that sends nothing, or anything but its join, is closed when it runs out.
func LiveKey ¶ added in v0.2.0
LiveKey is the key signing live pages' join tokens. Without it each app draws a random one, so a page rendered by one process can't join another: set it when several replicas serve the app.
func LiveLayout ¶ added in v0.2.0
LiveLayout is the layout of every Live page without its own. Styles and scripts given with it belong to the layout's own markup, as Layout's do.
func LiveMaxConns ¶ added in v0.15.0
LiveMaxConns caps the app's live connections: 10,000 by default. A socket beyond it is refused with 503 Service Unavailable before it opens, and the runtime tries again later; 0 sets no cap.
func LiveMaxConnsPerIP ¶ added in v0.15.0
LiveMaxConnsPerIP caps the live connections from one client address: 100 by default (a building behind one address has many visitors). A socket beyond it is refused with 429 Too Many Requests; 0 sets no cap.
func LiveMaxConnsPerPage ¶ added in v0.15.0
LiveMaxConnsPerPage caps the live connections open with the same page token, one rendered page's: 4 by default, room for a reconnect overlapping its old socket. A socket beyond it is refused with 429; 0 sets no cap.
func LiveOrigins ¶ added in v0.2.0
LiveOrigins lets pages of other origins open live connections: host patterns as path.Match takes them ("app.example.com", "*.example.com"). By default only the app's own origin may.
func LiveProtocol ¶ added in v0.3.0
LiveProtocol caps the live protocol version the app speaks: 2 (render trees: a page gets each component's statics once, then only the dynamics that changed) by default, or 1 (each changed component's HTML). A page asks for the newest it knows and gets at most this; v2 also needs a Vuka live session that speaks it, else pages get v1.
func LiveTokenTTL ¶ added in v0.2.0
LiveTokenTTL is how long a rendered live page may (re)join; 12 hours by default. A page older than that reloads when it reconnects.
func MaxRequestBody ¶ added in v0.15.0
MaxRequestBody bounds every request's body, in bytes: a body longer gets 413. 32 MiB by default; negative for none. A route sets its own with @web.MaxBody.
func NoCrons ¶ added in v0.11.0
func NoCrons() Option
NoCrons keeps Start from scheduling the crons, for a replica that only serves requests; RunCron still runs them.
func NoSecurityHeaders ¶ added in v0.15.0
func NoSecurityHeaders() Option
NoSecurityHeaders turns the default security headers off: a proxy in front of the app sets them, say.
func NoTailwind ¶ added in v0.6.0
func NoTailwind() Option
NoTailwind turns the app's stylesheet off: pages get no link to /_vuka/app.css and the vuka command builds none.
func NoToolbar ¶ added in v0.9.0
func NoToolbar() Option
NoToolbar turns the development toolbar off.
func Only ¶
Only limits the declared routes, services and controllers an app takes to those of the given packages (import paths; "main" for the main package).
func Options ¶ added in v0.2.0
Options is one Option applying opts in order; nil options are skipped.
func ParamFrom ¶ added in v0.11.0
ParamFrom binds handler parameters of type T with f, before any other rule: f's error answers the request as a handler's would. A library gives handlers its own values this way — the signed-in user, say.
func Provide ¶
Provide adds constructors to the app's container: func(deps…) T or func(deps…) (T, error). A value that isn't a function is provided as is.
func Require ¶ added in v0.2.0
Require fails Build when one of the environment variables is unset or empty, naming each missing one and the env files looked for.
func Tailwind ¶ added in v0.6.0
Tailwind names the project's Tailwind stylesheet, relative to the module root, when it isn't app.css or styles/app.css. The vuka command reads the path from the source, so it must be a string literal.
func TrackRequests ¶ added in v0.15.0
func TrackRequests() Option
TrackRequests has the app note the goroutine serving each request, and the context of the route it runs, for CurrentRequest: a library checking work a request's goroutine does without being given the request's context.
func TrustedProxies ¶ added in v0.15.0
TrustedProxies names the proxies in front of the app, as CIDRs ("10.0.0.0/8", "fd00::/8") or single addresses: from them, and only from them, ClientIP reads X-Forwarded-For (else Forwarded), and an HTTPS request is told by X-Forwarded-Proto. An entry that doesn't parse fails Build.
func WithRelay ¶ added in v0.2.0
WithRelay sends the app's broadcasts through r instead of delivering them in-process only.
func WithRouter ¶
WithRouter serves the app's routes with r instead of net/http's ServeMux.
func WrapRoutes ¶ added in v0.11.0
func WrapRoutes(name string, f func(RouteInfo) Middleware) Option
WrapRoutes has Build call f with each route of the app (decorated or added in code, Live pages included); the middleware f returns, if not nil, wraps the route outside its own (@web.Use) middleware and inside the app's. Functions added first wrap outermost. name is what a RouteAttr names.
type OptionFunc ¶ added in v0.2.0
type OptionFunc func(*App)
OptionFunc is an Option written as a function of the App, for libraries defining their own options: it runs inside New, before Build, and may call any of the App's methods (Provide, Use, Handle, OnStart, Check, …).
type PageParams ¶ added in v0.21.0
PageParams binds ?page= and ?size= for a plain handler:
func List(p web.PageParams, …) (…) {
page, size := p.Normalize()
…
}
Its tags let it bind as a handler's request struct; Normalize applies the defaults and the size cap.
func (PageParams) Normalize ¶ added in v0.21.0
func (p PageParams) Normalize() (page, size int)
Normalize returns the 1-based page and the size, defaulting size to DefaultPageSize and capping it at MaxPageSize.
type Pageable ¶ added in v0.21.0
type Pageable[Q any, T any] interface { Count(ctx context.Context) (int64, error) Limit(n int) Q Offset(n int) Q All(ctx context.Context) ([]T, error) }
Pageable is the shape of an ORM query a paginator needs: count the matches, narrow to a window with Limit and Offset, and fetch it with All. Q is the query's own type, so Limit and Offset chain. github.com/vuka-lang/orm's QuerySet[T] satisfies Pageable[orm.QuerySet[T], T], so Paginate takes one directly with no import of orm here.
type Pager ¶ added in v0.21.0
type Pager[T any] struct { // contains filtered or unexported fields }
Pager is a live view's paging state: the current page and size, and the events a ui.DataTable's controls call (GoTo, Next, Prev, SetSize). A view keeps one, gives it a loader that fetches a page, and renders Result — so it never hand-rolls the page arithmetic or the event handlers:
type Books struct {
web.Live // ui.Live
DB *DB // injected
pager *web.Pager[Book]
}
func (b *Books) Mount(ctx context.Context) error {
b.pager = web.NewPager(20, func(ctx context.Context, page, size int) (ui.Page[Book], error) {
return web.Paginate(ctx, orm.For[Book](), page, size)
})
return b.pager.Load(ctx)
}
func (b *Books) Render() ui.Node {
return ui.DataTable[Book]{
Columns: cols,
Page: b.pager.Result(),
OnPage: b.pager.GoTo, // func(context.Context, int) error
}
}
The loader is called with the page and size the pager holds; after each load the pager adopts the page the result settled on, so Next past the last page is a no-op (and the DataTable disables it anyway).
func NewPager ¶ added in v0.21.0
func NewPager[T any](size int, load func(ctx context.Context, page, size int) (ui.Page[T], error)) *Pager[T]
NewPager makes a pager starting on page 1 with the given size (or DefaultPageSize when size < 1); load fetches one page.
func (*Pager[T]) Load ¶ added in v0.21.0
Load fetches the current page, remembering the result and any error, and clamps the current page to the page the result settled on.
func (*Pager[T]) Reload ¶ added in v0.21.0
Reload returns to page 1 and fetches it, after the data behind the pager changed (a new search or sort).
func (*Pager[T]) Result ¶ added in v0.21.0
Result is the last loaded page (the zero Page before the first Load).
type Redirect ¶
type Redirect string
Redirect, returned by a handler as its result or as its error, redirects to the URL: 302 for GET and HEAD, 303 See Other otherwise. It is also a vuka.Node, so a handler returning a page can redirect instead.
type Registry ¶ added in v1.0.0
type Registry struct {
// contains filtered or unexported fields
}
Registry holds what an app is made of: routes, live pages, controllers and services. Routes is the package's, filled at init by the declarations of every package the program links; New gives each app a copy, and an App's methods of the same names add to its own:
func init() {
web.Routes.Get("/pets/{id}", ShowPet, web.Params("id", "store"))
web.Routes.Live("/board", NewBoard)
}
app.Get("/pets/{id}", ShowPet, web.Params("id", "store"))
It is what the decorators are: @web.Get("/pets/{id}") on ShowPet is the call above.
func (*Registry) Controller ¶ added in v1.0.0
func (g *Registry) Controller(prefix string, ctor any, opts ...RouteOption) *Registry
Controller makes the struct ctor builds a service whose route methods (their method expressions registered: (*PetAdmin).Index) are served under prefix.
func (*Registry) Delete ¶ added in v1.0.0
func (g *Registry) Delete(path string, fn any, opts ...RouteOption) *Registry
func (*Registry) Get ¶ added in v1.0.0
func (g *Registry) Get(path string, fn any, opts ...RouteOption) *Registry
func (*Registry) Handle ¶ added in v1.0.0
func (g *Registry) Handle(method, path string, fn any, opts ...RouteOption) *Registry
Handle registers fn for method and path: a handler, its parameters bound from the request (scalars by the names Params gives them), or the constructor of an @ui.Island struct (a page backed by its island).
func (*Registry) Live ¶ added in v1.0.0
func (g *Registry) Live(path string, page any, opts ...RouteOption) *Registry
Live serves a stateful component as a page at path (see the Live decorator): page is its constructor, NewBoard, which builds each page's component, its parameters of provided types injected and the others zero. With ui.Island among opts it is a page backed by its island.
func (*Registry) Patch ¶ added in v1.0.0
func (g *Registry) Patch(path string, fn any, opts ...RouteOption) *Registry
func (*Registry) Post ¶ added in v1.0.0
func (g *Registry) Post(path string, fn any, opts ...RouteOption) *Registry
type Relay ¶ added in v0.2.0
type Relay interface {
Publish(ctx context.Context, topic string, msg any) error
Subscribe(deliver func(topic string, msg any))
}
Relay carries broadcasts between the replicas of an app: Publish sends a message to every replica, and each calls the function Subscribe was given with what arrives (its own messages included). Encoding the messages, whose dynamic types pick the Update methods they reach, is the relay's business.
type RequestNote ¶ added in v0.11.0
type RequestNote struct {
Section string // the heading it goes under: "Auth"
Name string
Value string
Refused bool // shown as a failure
}
RequestNote is a row of the development toolbar's Request panel.
type Rest ¶ added in v1.1.0
type Rest string
Rest is a catch-all path parameter: a field of type Rest binds the rest of the path a "{name...}" segment matches, slashes included.
type FileParams struct{ Path web.Rest } // GET /files/{path...}
type RouteAttr ¶ added in v0.11.0
type RouteAttr interface{ RouteWrapper() string }
A RouteAttr is a typed attribute that only a WrapRoutes function gives a meaning to — a permission a route requires, say. Build fails when a route carries one and the app has no WrapRoutes of the name RouteWrapper returns, so a route never goes unguarded because the library guarding it wasn't installed.
type RouteInfo ¶ added in v0.11.0
type RouteInfo struct {
Method, Path string
Where string // "main.ShowPet at pages.vuka:24", "live page main.Counter"
// Attrs are the typed attributes of the route's declaration, after those
// of its controller's type: the attributes of a @web.Live page's type,
// or those given with web.Attrs from Go.
Attrs []any
Live bool // a Live page
// LiveType is a Live page's component type (the struct, not a
// pointer); nil for other routes.
LiveType reflect.Type
// HTML says the route answers with a page (a ui.Node, a view or a Live
// page): its errors are pages, and a browser may be sent elsewhere.
HTML bool
}
RouteInfo describes a route for the function WrapRoutes registers.
type RouteOption ¶
type RouteOption interface{ Attrs() []any }
RouteOption configures a registration (App.Get, Registry.Live, …): one of web's (Params, Template, Use, Layout, Wrap, Attrs), another package's (an auth gate, ui.Island) or any value carrying typed attributes, which become the route's, as attributes written on a decorated handler are.
func Attrs ¶ added in v0.11.0
func Attrs(attrs ...any) RouteOption
Attrs gives a route registered in code typed attributes, as attributes written on a decorated handler are: they reach WrapRoutes. A vuka.Bundle (a composed decorator, such as one a library's attribute function returns) gives its attributes. It also goes to App.Live.
func GuardOf ¶ added in v1.2.0
func GuardOf[T any]() RouteOption
GuardOf runs the guard T before a route's page or handler: a struct, rendering nothing, whose Load(ctx context.Context, deps…) error, its dependencies provided types, checks the request:
type Guard struct{ Params }
func (g *Guard) Load(ctx context.Context, perms *Perms) error {
if !perms.Can(ctx, "employers.view", g.Employer) {
return web.Forbidden
}
return nil
}
Per request it is a zero T, its path parameters bound as a page's are, then its Load run; guards and layouts load in the order their options are given, before the page mounts or the handler runs, and the first error answers the request as the handler's would: NotFound a 404, Forbidden a 403, a Redirect redirects, as a page or as JSON. A Live page's guards run on its request and on its join. GuardOf implies PageRoute.
func LayoutOf ¶ added in v1.1.0
func LayoutOf[T any]() RouteOption
LayoutOf wraps a route's page in the layout T, a stateless struct component (T or *T a ui.Node) with a field Children ui.Node, the page or the next layout. Several nest in the order given, the first outermost, and replace Layout and LiveLayout for the route:
type Layout struct {
Params // path parameters, bound as a page's are
Children ui.Node
Employer Employer
}
func (l *Layout) Load(ctx context.Context, store *Store) error { … }
Per request each layout is a zero T, its path parameters bound, then its Load run, if it has one: Load(ctx, deps…) returning nothing or an error, its dependencies provided types; its error answers the request. Layouts and guards (GuardOf) load in the order their options are given, before a stateless page's Mount. A Live page's layouts render around it on the request's render; the page's patches render the page. On a handler's route (Post, …) every layout is loaded before the handler runs, whatever it answers, a Load error answering instead; a ui.Node the handler returns for a whole page is wrapped in them. LayoutOf implies PageRoute.
func PageRoute ¶ added in v1.1.0
func PageRoute() RouteOption
PageRoute makes a route a page as a pages/ folder declares it, what vuka's folder routes register:
web.Routes.Get("/employers/{employer}", employer.NewIndex, web.PageRoute())
The page's struct is built by its constructor, its parameters of provided types injected and the others zero; its exported fields, promoted ones from embedded structs included, bind as a Live page's do (a provided type injected, text from the path segment, else the query parameter, of its name); a path parameter that doesn't parse or fails its validate: tag answers 404. A page whose pointer has Title() string titles the document: ui.PageTitle reads it in the layouts. A stateless page's Title is read after its Mount; a Live page's once its fields are bound, before Mount, which its session runs as it renders. On Get, the constructor's struct is a stateless page, a ui.Node: its Mount, Mount() or Mount(ctx) returning nothing or an error, runs once its fields are bound, and the page renders. LayoutOf implies PageRoute.
func Params ¶
func Params(names ...string) RouteOption
Params names a handler's parameters in order (a method's receiver excluded), which reflection can't see: scalar parameters bind by name.
func Receiver ¶ added in v1.1.0
func Receiver(ctor any) RouteOption
Receiver builds the receiver of a method handler for each request with ctor, a page's constructor, binding the route's path parameters into it as PageRoute does a page's (404 when one doesn't parse):
web.Routes.Post("/employers/{employer}/edit", (*edit.Edit).Post, web.Receiver(edit.NewEdit))
The handler's other parameters bind as any handler's.
func UseOf ¶ added in v1.3.0
func UseOf(mw any) RouteOption
UseOf wraps the route in mw, HTTP middleware: a func(next http.Handler) http.Handler, or its constructor, func(deps…) web.Middleware, its dependencies provided types, resolved when the app is built; what vuka's folder routes register for a folder's Middleware. Several wrap in the order given, the first outermost, and they wrap the whole route — outside its Use middleware, its guards and layouts, and its binding, so a path parameter's 404 happens inside them. Not calling next answers the request itself. On a Live page it wraps the GET and the join, never the socket's events.
type Router ¶
type Router interface {
http.Handler
Handle(method, pattern string, h http.Handler)
Param(r *http.Request, name string) string
}
Router matches requests to routes. Patterns use the canonical syntax: "/pets/{id}" binds a segment, "/files/{path...}" the rest of the path, and a pattern matches its path exactly. Param returns a bound value for a request the router dispatched.
type SecurityHeaders ¶ added in v0.15.0
type SecurityHeaders struct {
// ContentTypeOptions: "nosniff" by default, so a browser never runs
// an upload or a JSON answer as a script or a page.
ContentTypeOptions string
// ReferrerPolicy: "strict-origin-when-cross-origin" by default, so a
// URL's path and query stay on the site.
ReferrerPolicy string
// FrameOptions: "DENY" by default, or "SAMEORIGIN": who may show the
// app's pages in a frame. The CSP's frame-ancestors follows it.
FrameOptions string
// CSP is the Content-Security-Policy: "frame-ancestors 'none'" by
// default ('self' with FrameOptions "SAMEORIGIN"; left out with "-").
CSP string
// HSTS is the Strict-Transport-Security max-age sent on HTTPS requests
// (r.TLS, or X-Forwarded-Proto: https from a trusted proxy); 365 days
// by default. HSTSSubdomains adds includeSubDomains.
HSTS time.Duration
HSTSSubdomains bool
}
SecurityHeaders are the headers every response of an app carries unless its handler sets them itself. The zero value of a field is its default; "-" (or, for HSTS, a negative duration) leaves the header out.
type ServeMux ¶
type ServeMux struct {
// contains filtered or unexported fields
}
ServeMux is the default Router: net/http's ServeMux with Go 1.22 patterns.
func NewServeMux ¶
func NewServeMux() *ServeMux
type Source ¶ added in v0.21.0
type Source[T any] interface { Count(ctx context.Context) (int, error) Slice(ctx context.Context, limit, offset int) ([]T, error) }
Source is where a paginator reads: the total number of rows a query matches, and one window of them. It has no dependency on any ORM, so web does not import one; an orm.QuerySet[T] is adapted by Paginate (it satisfies Query), and any store can implement Source directly.
type Starter ¶
Starter is a provided value with work to do when the app starts: Run calls Start in dependency order.
type Statement ¶ added in v0.9.0
type Statement struct {
SQL string
Args []any
DB string // the database's name
Start time.Time
Duration time.Duration
Rows int64 // rows affected; -1 for a query
Err error
// Refused is a statement the database layer refused before sending it;
// Err says why.
Refused bool
}
Statement is a statement for the toolbar's SQL panel: what a database layer passes NoteStatement. orm's ormweb reports the ORM's statements by itself.
type Stopper ¶
Stopper is a provided value with work to do when the app stops: Stop is called in the reverse order of construction.
type Templates ¶ added in v1.0.0
Templates is what Template returns: a declarer giving a route its view, and an option doing so.
func Template ¶
Template renders a route's result with a view: a .templ file of the same package (its one exported component, or the one named) or an html/template file (its main template, or the one named). The order of @web.Template and the route's method decorator doesn't matter; from Go it is an option: app.Get("/pets/{id}", ShowPet, web.Template(petView)).
type URLParams ¶ added in v0.7.0
type URLParams struct{}
URLParams, embedded in a struct, makes it the parameters a Live page's Update method takes when the page is patched — moved to another URL of its route, the component staying mounted:
type ShelfParams struct {
web.URLParams
Page int // ?page=
}
func (s *Shelf) Update(p ShelfParams) { s.load(p.Page) }
Its fields bind from the new URL as the component's props do (path segment, else query parameter, of their name); the component's props are bound again too. Update runs before the patch renders. Mount, not Update, sees the first URL.
Only a page with such an Update stays mounted across a patch, whatever the browser asks: any other mounts again, Mount seeing the new URL. So Update must make every check Mount makes of what the URL names — that the visitor may see order {id}, say. An error it returns fails the patch: nothing of the page stays mounted, and the browser loads the URL, the route answering as for any request.
type Uses ¶
Uses is what Use returns: a declarer adding middleware to one route, a route's option doing so, and an Option adding it to the whole app.
func Use ¶
func Use(mw ...Middleware) Uses
Use adds HTTP middleware, the first given outermost: @web.Use(mw) on a route, or web.New(web.Use(mw)) for every request of the app.
Source Files
¶
- app.go
- assets.go
- bind.go
- crons.go
- csrf.go
- devstream.go
- devtools.go
- devtools_unix.go
- di.go
- diag.go
- dotenv.go
- env.go
- errors.go
- folder.go
- frontend.go
- hooks.go
- islandpage.go
- live.go
- livepark.go
- livews.go
- pager.go
- pagination.go
- recovery.go
- register.go
- registry.go
- router.go
- security.go
- slash.go
- tailwind.go
- toolbar.go
- toolbar_crons.go
- toolbar_panels.go
- toolbar_wire.go
- view.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Package auth signs users in to a web app and guards its routes, live pages and live events.
|
Package auth signs users in to a web app and guards its routes, live pages and live events. |
|
authtest
Package authtest helps test apps using auth: requests and live sessions signed in as anyone, without a login, and Users kept in memory.
|
Package authtest helps test apps using auth: requests and live sessions signed in as anyone, without a login, and Users kept in memory. |
|
Package cron parses cron schedules and computes their next run: five fields (minute hour day-of-month month day-of-week), the descriptors @yearly, @monthly, @weekly, @daily, @hourly and @every <duration>, and a CRON_TZ= prefix naming the time zone the fields are read in.
|
Package cron parses cron schedules and computes their next run: five fields (minute hour day-of-month month day-of-week), the descriptors @yearly, @monthly, @weekly, @daily, @hourly and @every <duration>, and a CRON_TZ= prefix naming the time zone the fields are read in. |
|
Package crontest tests an app's crons: Run runs one by name with its dependencies, and Clock is a virtual clock to test schedules with — time stands still until the test moves it.
|
Package crontest tests an app's crons: Run runs one by name with its dependencies, and Clock is a virtual clock to test schedules with — time stands still until the test moves it. |
|
examples
|
|
|
pets/model
Package model holds the pets app's data types, shared by its handlers and its views.
|
Package model holds the pets app's data types, shared by its handlers and its views. |
|
internal
|
|
|
chrome
Package chrome drives a headless Chrome over the DevTools protocol, for the tests of live pages: open pages, click, type, press keys and evaluate JavaScript.
|
Package chrome drives a headless Chrome over the DevTools protocol, for the tests of live pages: open pages, click, type, press keys and evaluate JavaScript. |
|
liveproto
Package liveproto is the seam between the web framework's live transport and the Vuka live session it drives: a Session speaks the protocol version its page joined with (v1: HTML, v2: render trees) and answers each client message with the message to send back.
|
Package liveproto is the seam between the web framework's live transport and the Vuka live session it drives: a Session speaks the protocol version its page joined with (v1: HTML, v2: render trees) and answers each client message with the message to send back. |
|
livetree
Package livetree is a stand-in for the Vuka live session's protocol v2 until the session speaks it: it runs a v1 session and turns the HTML of each changed instance into render trees and their changes, in the format of Vuka's "Protocol v2: render trees" (docs/features/components.md).
|
Package livetree is a stand-in for the Vuka live session's protocol v2 until the session speaks it: it runs a v1 session and turns the HTML of each changed instance into render trees and their changes, in the format of Vuka's "Protocol v2: render trees" (docs/features/components.md). |
|
testenv
Package testenv decides what a test does when a tool it needs is missing.
|
Package testenv decides what a test does when a tool it needs is missing. |
|
Package mail sends email from a web app: a Mailer service apps inject, configured from the environment (or .env), with a log driver for development and tests and an SMTP driver for production.
|
Package mail sends email from a web app: a Mailer service apps inject, configured from the environment (or .env), with a log driver for development and tests and an SMTP driver for production. |
|
mailtest
Package mailtest tests the mail an app sends: a quiet log-driver Mailer to provide, and helpers reading what it kept.
|
Package mailtest tests the mail an app sends: a quiet log-driver Mailer to provide, and helpers reading what it kept. |