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)
Index ¶
- Constants
- Variables
- func AcceptsHTML(r *http.Request) bool
- func AddPanel(name string, render func(notes []any) ui.Node)
- func Asset(ctx context.Context, p string) string
- func AssetURL(ctx context.Context, p string) string
- 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 Flash(ctx context.Context, level, text string) error
- func FullLoad() vuka.Bundle
- 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 LogError(ctx context.Context, err error)
- func MaxBody(n int64) vuka.Bundle
- func MustEnv[T any](key string) T
- func Note(ctx context.Context, panel string, v any)
- func NoteGraphQLBatch(ctx context.Context, b GraphQLBatch)
- func NoteGraphQLField(ctx context.Context, f GraphQLField)
- 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 RecordInput(ctx context.Context, in Input)
- func Recorded(ctx context.Context) bool
- func RefreshVars(ctx context.Context, names ...string)
- func RegisterAssets(manifest []byte)
- func RegisterCSS(name string, css []byte)
- func RegisterEncoded(path, encoding string, data []byte)
- func RegisterGraphQL(build func(GraphQLHost) (GraphQLHandler, error))
- func RegisterIsland(name string, js []byte)
- func RegisterScript(name string, js []byte)
- func RegisterStatic(manifest map[string]string)
- func Request(ctx context.Context) *http.Request
- func Resolve[T any](a *App) (T, error)
- func ResponseWriter(ctx context.Context) http.ResponseWriter
- func Slice[T any](rows []T, page, size int) ui.Page[T]
- func StatementOrigin(ctx context.Context, path, field string) context.Context
- func ValidCSRFToken(ctx context.Context, token string) bool
- func Var[T any](ctx context.Context, name string) T
- func VarErr[T any](ctx context.Context, name string) (T, error)
- func WithPendingParts(ctx context.Context) context.Context
- func WithVars(ctx context.Context, s any) context.Context
- 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 BodyLimit
- type CORSConfig
- type CSRFVars
- type CompressionLevel
- type CronLock
- type Error
- type FrontendOption
- type GraphQLBatch
- type GraphQLField
- type GraphQLHandler
- type GraphQLHost
- type Input
- type Invalid
- type LiveCompressionMode
- type LiveEvent
- type LiveHandler
- type LiveOption
- type Message
- type MessagesVars
- type Middleware
- type NotForBrowser
- type Option
- func BaseURL(u string) Option
- func CORS(prefix string, cfg CORSConfig) Option
- func CSRF(trusted ...string) Option
- func CSRFBypass(patterns ...string) Option
- func Check(fn func(*App) error) Option
- func Compression(level CompressionLevel) Option
- func ContextProcessors(processors ...any) 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 FormStore(s ui.FormStore) Option
- func Frontend(fsys fs.FS, root string, opts ...FrontendOption) Option
- func GraphQL(path string, opts ...any) Option
- func GuardCalls(f func(ctx context.Context, attrs []any, byDefault bool) error) Option
- func GuardLiveEvents(f func(ctx context.Context, h LiveHandler) error) Option
- func GuardVars(f func(ctx context.Context, gates []any) 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 LiveCompression(m LiveCompressionMode) 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 ...PageAsset) 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 LiveTrimAfter(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 PartsWindow(d time.Duration) Option
- func PersonalPackage(path string) Option
- func Provide(ctors ...any) Option
- func Require(keys ...string) Option
- func SessionCookies(names ...string) Option
- func Tailwind(path string) Option
- func TokenOnly(prefix 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 PageAsset
- 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 Processor
- type QueryEdits
- func (QueryEdits) Del(ctx context.Context, key string, opts ...QueryOption)
- func (QueryEdits) Get(ctx context.Context, key string) string
- func (QueryEdits) Set(ctx context.Context, key string, value any, opts ...QueryOption)
- func (QueryEdits) Update(ctx context.Context, edit func(url.Values), opts ...QueryOption)
- type QueryOption
- 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
- type Relay
- type RequestInfo
- type RequestNote
- type RequestVars
- 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 Receiver(ctor any) RouteOption
- func ReloadLayout[T 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 AnyHeader = "*"
AnyHeader, in CORSConfig.Headers, allows whatever request headers a preflight asks for, Authorization included. (A browser reads a literal "Access-Control-Allow-Headers: *" as every header but Authorization, and as the header named "*" on a request with credentials: AnyHeader answers with the headers asked for instead.)
const AnyOrigin = "*"
AnyOrigin, as CORSConfig's only origin, lets every origin read the answers. It can't go with Credentials.
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 FormHeader = "Vk-Form"
FormHeader marks a form the runtime sends over HTTP, from a page that isn't live: "check" for its checks as the visitor leaves a field (the page answers its errors, nothing saved), "submit" for its submit (its errors, 422, or where to go). Either answer is JSON (formAnswer), so the runtime puts the errors in place without touching the fields.
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.
const (
)
In-place navigation for pages that aren't live: a click on a ui.Link (data-vk-link) on a page the runtime runs without a connection is fetched with the header NavigateHeader, its value the build the page was rendered by (the runtime's script tag says, data-vk-build-id). The route runs as for a page load — guards, every layout's Load, the page — and answers with the page's HTML, which the browser patches into <body> in place; a few response headers tell it what it needs to:
Vk-Page the page's root layout (data-vk-root), "-" for none: a
page under another one is another document, a page load
Vk-Live a live page's join token: the runtime joins it
Vk-Reload why the browser must load the URL instead: the build
changed ("build"), the route asks for it ("route", FullLoad),
the page's body has scripts of its own ("scripts")
Vk-Reload-Detail in development, why in words, for the browser's
console and the toolbar (the terminal says it too, once)
A response without Vk-Page (a redirect elsewhere, a file, JSON, a page the app wrote itself) is the browser's to load too. Nothing in the request is trusted for auth or rendering: the header only picks the response's format. A GET without it is answered as ever.
const Off = -1
Off turns a compression option off: LiveCompression(Off) sends live messages as they are.
const OptionsHeader = "Vk-Options"
OptionsHeader is PushHeader as an Options step's call sent it before Options became a Push; a call carrying it is answered the same.
const PartsHeader = "Vk-Parts"
PartsHeader, on a GET of a page, asks for the page's deferred parts named in its value (space-separated ids) instead of the page; a reply carrying it is the parts.
const PushHeader = "Vk-Push"
PushHeader carries a Push step's call: the method's reference as the page rendered it.
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 and controllers.
Functions ¶
func AcceptsHTML ¶
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 ¶
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 Asset ¶
Asset is the URL path of a file an app.Static mount serves, given as the path it is served at: web.Asset(ctx, "/static/images/logo.png"). In a binary vuka build made it is the file's content-hashed name, cached for good; under vuka run, the path as given. The path the app is mounted under (http.StripPrefix, or BaseURL's path without a request) comes first. A path no mount serves stays as given, and a warning is logged once. In markup the context is implicit: {web.Asset("/static/site.css")}.
func AssetURL ¶ added in v2.11.0
AssetURL is Asset's path as an absolute URL, for an email or a feed: the scheme and host the request came to, or, without one (a cron, a job), the app's BaseURL. Without either it is Asset's path, and a warning is logged once.
func Broadcast ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 Flash ¶ added in v2.4.0
Flash adds a message for the visitor, which the next page reading the variable messages (MessagesContext) shows once: a form's handler flashes "Saved" and redirects, and the page it redirects to shows it. Over HTTP it travels in a signed cookie, so it must be called before the response is written; from a live event it shows in the page's next render. It needs a request's or a live page's context of an app listing MessagesContext.
func FullLoad ¶ added in v2.8.0
FullLoad has a route's page always load as a document: a link to it on a page that isn't live is followed by the browser, not fetched and patched in place. For a page whose body runs its own scripts as it loads, or that must start from a fresh document. On a page type:
vuka.RegisterType[Report](web.FullLoad())
and in a .vuka file @web.FullLoad above the type. A link marked data-vk-reload does the same for one link.
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 ¶
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 ¶
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 ¶
IsHTTPS reports whether r came over HTTPS: its own TLS, or a trusted proxy's X-Forwarded-Proto.
func IsLiveJoin ¶
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 ¶
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 ¶
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 ¶
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 ¶
LoadEnv reads env files into the process environment, as New does. The default list is already read when the program starts, before main runs, so options built from the environment see it without a call; LoadEnv is for another list, read before New. 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 LogError ¶ added in v2.8.0
LogError logs err for the request ctx belongs to, as a handler's server error is logged: one line with the method and path.
func MaxBody ¶
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 ¶
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 ¶
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 NoteGraphQLBatch ¶ added in v2.8.0
func NoteGraphQLBatch(ctx context.Context, b GraphQLBatch)
NoteGraphQLBatch records a loader's batch for the toolbar's GraphQL panel.
func NoteGraphQLField ¶ added in v2.8.0
func NoteGraphQLField(ctx context.Context, f GraphQLField)
NoteGraphQLField records a resolved field for the toolbar's GraphQL panel.
func NoteRequest ¶
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 ¶
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 ¶
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 ¶
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 ¶
PaginateSource is Paginate over a Source: a store that counts and slices itself, with no ORM.
func RecordInput ¶ added in v2.4.0
RecordInput records in, when ctx is a context processor's: what makes its result. A library whose values processors read records its own — package auth records the identity and its permission version when auth.Current is called with a processor's context. Elsewhere it does nothing.
func Recorded ¶ added in v2.8.0
Recorded reports whether the development toolbar records the request (or live message) of ctx: what is worth noting only for it.
func RefreshVars ¶ added in v2.4.0
RefreshVars makes the processors giving the variables names run again on their next read, as when an input of theirs changed: an event that changed what a processor loads (the user's own record) calls it, and the components showing the variables render again if their values changed.
func RegisterAssets ¶
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 ¶
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 RegisterEncoded ¶ added in v2.6.0
RegisterEncoded registers a fixed asset's encoding made at build time: path is where the app serves it (/_vuka/app.css, /_vuka/c/<module>, /_vuka/i/<chunk>), encoding "br" or "gzip", data the encoded bytes. vuka build calls it from the file it generates, so the binary serves its assets compressed with no work at run time.
func RegisterGraphQL ¶ added in v2.8.0
func RegisterGraphQL(build func(GraphQLHost) (GraphQLHandler, error))
RegisterGraphQL is how package graphql serves web.GraphQL: build makes an app's endpoint when the app is built. Package graphql calls it at init; nothing else needs to.
func RegisterIsland ¶
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 ¶
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 RegisterStatic ¶ added in v2.11.0
RegisterStatic registers the content-hashed names of the files app.Static serves: each plain URL path (/static/images/logo.png) to its hashed one (/static/images/logo.3f9a1c2b.png). vuka build calls it from a file it generates in the main package.
func Request ¶
Request is the request being served, from a context.Context a handler received: call decorators reach it through c.Context().
Called with a context processor's context, it records the URL as an input of the processor's result (RecordInput).
func ResponseWriter ¶
func ResponseWriter(ctx context.Context) http.ResponseWriter
ResponseWriter is the response being written, from a handler's context.
func StatementOrigin ¶ added in v2.8.0
StatementOrigin is ctx with the statements noted under it (NoteStatement) marked as path's and field's — a GraphQL field's, "pets.0.owner" of "Pet.owner", or a loader's ("" and "loader OrdersByUser"): the SQL panel names them, and an N+1 the field repeating it. It is ctx itself when no toolbar records the request.
func ValidCSRFToken ¶ added in v2.4.0
ValidCSRFToken reports whether token is the csrfToken CSRFContext gives the visitor of ctx's request.
func Var ¶ added in v2.4.0
Var is the template variable name, as a context processor gives it: what a template's bare name compiles to, and what Go code calls for one. Its processor runs on the first read of the request (or live event) and every later read shares the result; on a live page the read is recorded against the component rendering, which renders again when the value changes. Var returns the zero T when the read fails — no such variable, a T that isn't the variable's type, a processor's error, a gate refusing (Auth) — and VarErr says why.
func WithPendingParts ¶ added in v2.9.0
WithPendingParts is ctx with the deferred parts of the page it renders left pending: the page shows their fallbacks and their methods never run. viewtest.Pending sets it, to test a page's first paint.
func WithVars ¶ added in v2.4.0
WithVars is ctx in which the variables of s — a struct, the result type of a context processor — are s's fields: below ctx, every read of them returns s's values, and the processor returning that type doesn't run. A part of a page shows other values this way, and a test gives its own (viewtest.With, viewtest.Context). s may be a pointer to the struct; it panics for anything else.
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 ¶
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 ¶
Check adds fn to the checks Build runs once the routes are compiled; its error is reported with Build's own.
func (*App) CloseLive ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
Service registers a service: ctor, func(deps…) *T or func(deps…) (*T, error) with T a struct, builds the app's one T, when something first needs it, its parameters filled from the container. Hooks, handlers, constructors, layouts' and guards' Load and middleware constructors then take *T as a parameter. It is how an app declares its services.
app := web.New() app.Service(NewTodos) // func NewTodos(db *DB) *Todos
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 ¶
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 ¶
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 ¶
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 CORSConfig ¶ added in v2.14.0
type CORSConfig struct {
// Origins are the origins allowed, exactly as a browser sends them:
// scheme, host and port, no path ("https://shop.example",
// "http://localhost:5173", "capacitor://localhost"). AnyOrigin alone
// allows every one.
Origins []string
// Methods a request may use: GET, HEAD and POST when empty.
Methods []string
// Headers a request may carry besides the CORS-safelisted ones:
// Content-Type when empty, and Authorization too under a TokenOnly
// prefix. AnyHeader allows every one.
Headers []string
// ExposeHeaders are the response headers a script may read besides
// the CORS-safelisted ones (Content-Type, Cache-Control…).
ExposeHeaders []string
// Credentials lets the origins send cookies: the browser sends them
// and the app's cross-origin check lets these origins' requests
// through under the prefix (as web.CSRF's trusted origins). It needs
// listed origins, never AnyOrigin, and can't go with TokenOnly.
Credentials bool
// MaxAge is how long a browser may keep a preflight's answer: 5 minutes
// when zero, never with a negative value. (Browsers cap it: Chrome at 2
// hours.)
MaxAge time.Duration
}
CORSConfig is what CORS lets other origins do under its prefix.
type CSRFVars ¶ added in v2.4.0
type CSRFVars struct {
CSRFToken string
}
CSRFVars is CSRFContext's result: the variable csrfToken.
func CSRFContext ¶ added in v2.4.0
CSRFContext is a context processor giving templates the variable csrfToken: a token bound to the visitor's browser (a random cookie it sets the first time) and sign-in (LiveIdentity), for a form whose handler checks it with ValidCSRFToken — one posted from another origin the app trusts, say. web's own protection (CSRF) checks the request's origin and needs no token.
type CompressionLevel ¶ added in v2.6.0
type CompressionLevel int
CompressionLevel is how hard the app compresses what handlers write: 1 (fastest) to 11 (smallest), brotli's quality; gzip's level is the same, up to 9. Fixed content (the runtime, stylesheets, scripts) is compressed once, harder.
const ( // DefaultCompression is brotli's quality 5 (gzip's level 5): a page of // 250 KB in about 2 ms, to a 30th of its size. DefaultCompression CompressionLevel = 0 // BestSpeed and BestCompression are the ends of the scale. BestSpeed CompressionLevel = 1 BestCompression CompressionLevel = 11 )
type CronLock ¶
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 ¶
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 ¶
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.
type FrontendOption ¶
type FrontendOption func(*frontend)
FrontendOption configures a Frontend.
func FrontendAt ¶
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 ¶
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 ¶
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 GraphQLBatch ¶ added in v2.8.0
GraphQLBatch is a batch a GraphQL loader ran.
type GraphQLField ¶ added in v2.8.0
type GraphQLField struct {
Path string // "pets.0.owner"
Field string // "Pet.owner"
Start time.Time
Dur time.Duration
Err bool
}
GraphQLField is a field a GraphQL request resolved, for the toolbar's GraphQL panel.
type GraphQLHandler ¶ added in v2.8.0
GraphQLHandler answers a GraphQL request, given the request's context: the one the app's handlers get, with its injector, identity and toolbar.
type GraphQLHost ¶ added in v2.8.0
type GraphQLHost struct {
Path string
Options []any // GraphQL's options, as given
Dev bool // the app runs in development
Where string
// Dependency fills a resolver's parameter of type t, as a handler's
// is filled: a provided service, the request, a ParamFrom type. Its
// error is a Build error naming where.
Dependency func(t reflect.Type, where string) (func(context.Context) (any, error), error)
// Guard decides a call by its declaration's attributes (GuardCalls);
// nil when no library gates calls. byDefault applies the library's
// rule for a declaration carrying no gate (an operation's), rather than
// letting it through (a field's).
Guard func(ctx context.Context, attrs []any, byDefault bool) error
// SetCheck, when the development toolbar is on, takes what checks a
// document for its GraphiQL panel as the query is typed: given a
// request's JSON body, the errors running it would meet before
// execution, as a JSON object {"errors": […]}. Nil otherwise.
SetCheck func(check func(body []byte) []byte)
}
GraphQLHost is what web gives package graphql to build an app's endpoint (RegisterGraphQL).
type Input ¶ added in v2.4.0
type Input struct {
// Key names it, once per processor run: "auth", "request".
Key string
// Now is its current value for ctx — fast, and recording nothing.
Now func(ctx context.Context) string
// Personal says it is who the visitor is: a Browser processor reading
// it must be Personal.
Personal bool
}
Input is something a context processor's result depends on, which RecordInput records: a live page runs the processor again when Now returns another value than it did when the processor ran.
type Invalid ¶
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 ¶
func (Invalid) PublicMessage ¶
func (Invalid) StatusCode ¶
type LiveCompressionMode ¶ added in v2.6.0
type LiveCompressionMode int
LiveCompressionMode is how live sockets compress their messages (permessage-deflate, RFC 7692), set with LiveCompression.
const ( // NoContextTakeover, the default, compresses each message on its own: // a compressor is borrowed from a pool for the write and given back, // so an open socket holds none. The browser does the same for its // messages. NoContextTakeover LiveCompressionMode = iota // ContextTakeover keeps each socket's compressor and its 32 KB window // between messages, so a message can refer to the ones before it: a // few percent fewer bytes on a live page's session (2% on a 200-row // list), for about 800 KB more held by every open socket (8 GB at // LiveMaxConns' 10,000; NoContextTakeover adds about 2 KB). ContextTakeover )
type LiveEvent ¶
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 ¶
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 ¶
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 ¶
Handler names the handler the event targets: "Admin.Delete", or "Admin.Render.func1" for a function literal.
func (*LiveEvent) Handling ¶
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 ¶
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 ¶
type LiveOption interface {
RouteOption
// contains filtered or unexported methods
}
LiveOption configures a page declared with Live; it is a RouteOption too.
type Message ¶ added in v2.4.0
type Message struct {
Level string `json:"level"` // "info", "success", "warning", "error": the app's own words
Text string `json:"text"`
}
Message is a flash message: one Flash added, shown by the next page that renders the variable messages.
type MessagesVars ¶ added in v2.4.0
type MessagesVars struct {
Messages []Message
}
MessagesVars is MessagesContext's result: the variable messages.
func MessagesContext ¶ added in v2.4.0
func MessagesContext(ctx context.Context) (MessagesVars, error)
MessagesContext is a context processor giving templates the variable messages: what Flash added for the visitor, each shown once — a page rendering them clears them.
type Middleware ¶
Middleware wraps an http.Handler.
type NotForBrowser ¶ added in v2.4.0
type NotForBrowser interface{ NotForBrowser() }
NotForBrowser is a type whose values Browser never sends to the browser: auth.Perms, a list of what the visitor may do, is one.
type Option ¶
type Option interface {
// contains filtered or unexported methods
}
Option configures an App.
func BaseURL ¶ added in v2.11.0
BaseURL is the app's public URL (https://example.com, or with the path it is mounted under): what AssetURL builds on where there is no request.
func CORS ¶ added in v2.14.0
func CORS(prefix string, cfg CORSConfig) Option
CORS lets other origins call the routes under prefix ("/api/" covers /api and every path under it): a preflight (OPTIONS with Access-Control-Request-Method) is answered 204 before any routing, middleware or cross-origin check, and every answer under prefix says which origin may read it. An origin not listed gets answers without those headers, which its browser keeps from the page; CORS is not access control, so gate the routes as any others.
web.New(
web.TokenOnly("/api/"),
web.CORS("/api/", web.CORSConfig{Origins: []string{"https://shop.example"}}),
)
The most specific prefix wins when several cover a path. A config that can't work (no origins, an origin with a path, Credentials with AnyOrigin) fails Build.
func CSRF ¶
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, and so are those of a TokenOnly route, whose cookies are taken off, and those from an origin a CORS rule with Credentials lists. Live connections check their origin themselves (LiveOrigins).
func CSRFBypass ¶ added in v2.8.0
CSRFBypass lets cross-origin requests of these patterns (net/http ServeMux patterns, "/webhooks/", "POST /sso/callback") through the app's CSRF protection: a payment provider's webhook, an SSO callback.
func Compression ¶ added in v2.6.0
func Compression(level CompressionLevel) Option
Compression sets how hard the app compresses its responses; Off turns compression off, for an app behind a proxy that already compresses. It is on, at DefaultCompression, unless set. Live sockets' messages are LiveCompression's.
func ContextProcessors ¶ added in v2.4.0
ContextProcessors lists the app's context processors: functions func(ctx context.Context, deps…) (S, error) with S a struct, whose parameters after ctx the app fills as it fills a handler's (what app.Service and Provide register, ParamFrom types, the request); or one wrapped by Auth, Browser or Personal. Build fails when one has another shape, a parameter nothing provides, or a variable another processor gives too (each error names both). It may be given more than once; the lists add up.
func CronClock ¶
CronClock runs the app's crons by clock instead of the system's: a virtual one in tests (crontest.Clock).
func CronContext ¶
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 ¶
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 ¶
Eager provides constructors whose values are built and started by Start even when no route needs them (workers, schedulers).
func ExactPaths ¶
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 FormStore ¶ added in v2.8.0
FormStore keeps what came of the app's form submits, by their idempotency keys, instead of in this process's memory: an app with several replicas keeps them where every replica sees them.
func Frontend ¶
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 GraphQL ¶ added in v2.8.0
GraphQL serves the app's GraphQL schema at path, by GET and POST: the operations its packages declare with @graphql.Query and @graphql.Mutation (package github.com/vuka-lang/web/v2/graphql, which the code the compiler generates for them imports). opts are that package's options: graphql.Naming, graphql.Limits, graphql.Introspection.
app := web.New(web.GraphQL("/graphql"))
The route runs the app's middleware, CSRF protection and toolbar as any other; a library gating routes (web/auth) leaves the route itself open and gates each operation and field by its attributes instead (GuardCalls). Build fails when no operation is linked into the program.
func GuardCalls ¶ added in v2.8.0
GuardCalls has f decide calls the app serves by their declarations' attributes: GraphQL's operations and fields (web.GraphQL). An error refuses the call — web.Unauthorized, web.Forbidden. byDefault asks for the library's rule for a declaration carrying none of its attributes (an operation: web/auth needs a sign-in unless the app is public by default); without it such a declaration passes (a field, gated by its operation already). A library gating routes registers one.
func GuardLiveEvents ¶
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 GuardVars ¶ added in v2.4.0
GuardVars has f decide each read of a variable of a processor wrapped in Auth, given the read's context and the gates Auth was given (each vuka.Bundle flattened into its attributes): an error refuses the read. A redirect (Redirect) or a *Error answers a page with it; on a live page the connection closes, and the page loads again. Package auth installs one.
func Headers ¶
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 ¶
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 ¶
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 ¶
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 ¶
LiveCheckEvery runs the app's LiveChecks on each joined page this often: 30 seconds by default; 0 never runs them. A connection's first check comes at a random point of the period, so pages opened together don't all check together. 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 LiveCompression ¶ added in v2.6.0
func LiveCompression(m LiveCompressionMode) Option
LiveCompression sets how live sockets compress their messages: NoContextTakeover by default, ContextTakeover, or Off. Compression is negotiated with each client when its socket opens (every current browser offers it); one that doesn't offer it gets messages as they are. Messages under 256 bytes are sent as they are.
func LiveIdentity ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 LiveTrimAfter ¶ added in v2.16.0
LiveTrimAfter makes a joined page that sends and receives nothing for d release what its session can rebuild (the hashes of what the browser holds), to make them again on its next event or broadcast. It is off by default (0): it saves about a tenth of an idle page's memory, and a mistake in it would show as a wrong update rather than an error. Replies don't change, and the page stays open; LiveIdleTimeout is what ends it.
func MaxRequestBody ¶
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 ¶
func NoCrons() Option
NoCrons keeps Start from scheduling the crons, for a replica that only serves requests; RunCron still runs them.
func NoSecurityHeaders ¶
func NoSecurityHeaders() Option
NoSecurityHeaders turns the default security headers off: a proxy in front of the app sets them, say.
func NoTailwind ¶
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 Only ¶
Only limits the declared routes and controllers an app takes to those of the given packages (import paths; "main" for the main package).
func ParamFrom ¶
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 PartsWindow ¶ added in v2.13.0
PartsWindow is how long a page's deferred parts, their methods started with the page's render, wait on the server for the runtime's request for them once the page has loaded: 30 seconds by default. A request after it (or on another replica, or after a restart) renders the page again for its parts. 0 (or less) parks nothing: the methods run on that request, after the page has loaded.
func PersonalPackage ¶ added in v2.4.0
PersonalPackage says that what package path (an import path) provides reads who the visitor is: a Browser processor of that package, or taking one of its types as a parameter, must be Personal too. Package auth declares itself.
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 ¶
Require fails Build when one of the environment variables is unset or empty, naming each missing one and the env files looked for.
func SessionCookies ¶ added in v2.8.0
SessionCookies names cookies that carry a visitor's session, for the CSRF protection to know a request carrying one: an unsafe request saying nothing of where it comes from (no Sec-Fetch-Site, no Origin) needs the form's token when it carries one of them, or the app's own (vk_csrf). The auth module names its session cookie itself.
func Tailwind ¶
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 TokenOnly ¶ added in v2.14.0
TokenOnly declares the routes under prefix ("/api/" covers /api and every path under it) a token API: a request there signs in with its Authorization header alone. Its cookies are taken off before any middleware reads them, so no session or CSRF cookie rides along, the Set-Cookie headers of its answers are dropped, and, with no cookie to abuse, the app's cross-origin check is off there: another origin's script may POST, with a bearer token or none (a sign-in). The two can't be had apart: the check is never off for a request carrying cookies.
Pages, forms and live views need cookies: keep them off a TokenOnly prefix. Pair it with CORS for browsers on other origins.
func TrackRequests ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 PageAsset ¶ added in v2.11.0
type PageAsset struct {
// contains filtered or unexported fields
}
PageAsset 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 Script ¶
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 ¶
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 PageParams ¶
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 ¶
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 ¶
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/v2's QuerySet[T] satisfies Pageable[orm.QuerySet[T], T], so Paginate takes one directly with no import of orm here.
type Pager ¶
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]
}
@ui.Mount
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 ¶
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 ¶
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 ¶
Reload returns to page 1 and fetches it, after the data behind the pager changed (a new search or sort).
type Processor ¶ added in v2.4.0
type Processor struct {
// contains filtered or unexported fields
}
Processor is a context processor wrapped by Auth, Browser or Personal, for ContextProcessors' list.
func Auth ¶ added in v2.4.0
Auth makes reads of p's variables pass gates first, as a route's: given auth.Required, a read by an anonymous visitor sends them down the sign-in path — a page answers with the redirect to the login page (or 401), a live page loads again — instead of the processor running. A gate is an attribute value, a vuka.Bundle, or a function returning one (pass auth.Required itself, or auth.Requires("x")); a library guarding reads (GuardVars, which auth.Module installs) decides it, and Build fails when the app has none.
func Browser ¶ added in v2.4.0
Browser lets the browser read p's variables: islands' components and the page's scripts get them with ctx.value("name"). No other processor's variable reaches the browser. Build refuses a Browser processor that reads the signed-in user (one of package auth, or taking one of its types) unless it is wrapped in Personal too, and a value whose type is NotForBrowser (auth.Perms) is never sent.
type QueryEdits ¶ added in v2.4.0
type QueryEdits struct{}
QueryEdits is the type of Query.
var Query QueryEdits
Query edits the URL of a live page from one of its events: the change is a live patch of the page, sent once the event's reply is applied, that keeps the other parameters and takes the current history entry's place (Push adds one instead). The page then takes the URL as any patch does — its Update taking web.URLParams, or else it mounts again — and its components' @ui.URL fields fill from it.
func (t *Requests) Filter(ctx context.Context, status string) {
web.Query.Set(ctx, "status", status)
web.Query.Del(ctx, "page")
}
Several edits in one event are one patch. Outside a live page's event an edit does nothing.
func (QueryEdits) Del ¶ added in v2.4.0
func (QueryEdits) Del(ctx context.Context, key string, opts ...QueryOption)
Del removes the query parameter key.
func (QueryEdits) Get ¶ added in v2.4.0
func (QueryEdits) Get(ctx context.Context, key string) string
Get is the query parameter key of the page whose event (or request) ctx belongs to, with the event's edits so far.
func (QueryEdits) Set ¶ added in v2.4.0
func (QueryEdits) Set(ctx context.Context, key string, value any, opts ...QueryOption)
Set sets the query parameter key to value: a string as it is, a number or bool as strconv writes it, a type with MarshalText as it writes it, a slice as one value per element.
func (QueryEdits) Update ¶ added in v2.4.0
func (QueryEdits) Update(ctx context.Context, edit func(url.Values), opts ...QueryOption)
Update edits the query in place: edit gets the page's parameters, as the event's edits so far left them.
type QueryOption ¶ added in v2.4.0
type QueryOption interface {
// contains filtered or unexported methods
}
A QueryOption changes how an edit of the query moves the page.
var Push QueryOption = pushOption{}
Push makes an edit of the query add a history entry, so that back returns to the URL before it.
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.
func (Redirect) RedirectLocation ¶ added in v2.8.0
RedirectLocation is the URL: a live page's handler returning a Redirect moves the page there (ui.RedirectOf).
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry holds what an app is made of: routes, live pages and controllers. 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.Routes.Live("/board", NewBoard)
}
app.Get("/pets/{id}", ShowPet)
A handler registered from Go binds its scalar parameters to the path's segments in order; one the compiler registers binds them by name, which it records (vuka.RegisterParams), from the path or the query. Services are the app's: app.Service in main.
It is what the decorators are: @web.Get("/pets/{id}") on ShowPet is the call above.
func (*Registry) Controller ¶
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 ¶
func (g *Registry) Delete(path string, fn any, opts ...RouteOption) *Registry
func (*Registry) Handle ¶
func (g *Registry) Handle(method, path string, fn any, opts ...RouteOption) *Registry
Handle registers fn for method and path: a handler, its parameters filled by type (see inject.go), or the constructor of an @ui.Island struct (a page backed by its island).
func (*Registry) Live ¶
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.
type Relay ¶
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 RequestInfo ¶ added in v2.4.0
type RequestInfo struct {
Path string // "/users/42"
Query url.Values // the URL's query parameters
Params map[string]string // the route's path parameters by name: {"id": "42"}
Name string // the route's handler: "users.Show"
Pattern string // the route: "GET /users/{id}"
}
RequestInfo is the request a page renders for, as templates read it.
type RequestNote ¶
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 RequestVars ¶ added in v2.4.0
type RequestVars struct {
Request RequestInfo
}
RequestVars is RequestContext's result: the variable request.
func RequestContext ¶ added in v2.4.0
func RequestContext(ctx context.Context) (RequestVars, error)
RequestContext is a context processor giving templates the variable request: the path, query, path parameters and route of the request the page renders for. Its input is the URL: on a live page that moves to another, the components showing it render again, a layout that stays among them.
type Rest ¶
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 ¶
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 ¶
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
// RootLayout is the route's root layout, the outermost of its
// web.LayoutOf layouts (the struct type); nil when it has none. Pages
// under one root layout share a document: in-app navigation between
// them patches the page, and between two roots is a full page load.
RootLayout reflect.Type
// Calls says the route gates each call it serves itself, by the
// call's declaration (GuardCalls): web.GraphQL's operations and
// fields. A library gating routes leaves such a route open unless its
// attributes carry a gate.
Calls 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 (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 ¶
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 ¶
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 ¶
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 and its unexported fields of provided types injected (as PageRoute's page's), 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 hook. 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 ¶
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. Its unexported fields of provided types are its dependencies, injected after the constructor runs, as a page-backed island's are, so a field users *users.Service needs no NewT, and its other unexported fields, its tracked state among them, are left as the constructor made them; a dependency field the URL would bind too (named as a path parameter, tagged url:) is E0419 at start-up. A page whose pointer has Title() string titles the document: ui.PageTitle reads it in the layouts. One with Crumbs() []ui.Crumb gives them its breadcrumbs, read with ui.PageCrumbs. A stateless page's Title and Crumbs are read after its mount hook; a Live page's once its fields are bound, before its mount hook, which its session runs as it renders. On Get, the constructor's struct is a stateless page, a ui.Node: the mount hook its LiveSchema declares (ui.MountPage), if any, runs once its fields are bound, and the page renders. LayoutOf implies PageRoute. On a function handler (a folder's method func, GET in [id].vuka) a scalar parameter bound from a path segment that doesn't parse answers 404 too.
func Receiver ¶
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 and injecting its dependency fields as PageRoute does a page's (404 when a parameter 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 ReloadLayout ¶ added in v2.1.0
func ReloadLayout[T any]() RouteOption
ReloadLayout has the layout T of a route's LayoutOf load again on every in-app navigation, what vuka's folder routes register for a layout whose Load or markup reads the request — web.Request, its URL or query. Other layouts persist: navigating over a page's live connection to another page under the same layouts, their path parameters the same, keeps them as they are — their Load doesn't run again, and the elements, scroll, client state and live components they render stay — and renders the page below them. Their markup renders again for the new page, the page getting only what changed of it: a header of ui.PageTitle and ui.PageCrumbs read there follows the page, where one Load stored stays the first page's (vuka registers a layout reading them in Load with the option). A layout reloading persists all the same in the page, its Load running again. A layout whose Load takes the *http.Request reloads without the option. Guards (GuardOf) run on every navigation.
func UseOf ¶
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 ¶
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 ¶
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 ¶
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
// Origin and Field are what ran it, as StatementOrigin marked its
// context: a GraphQL field's path and name; NoteStatement fills them.
Origin, Field string
}
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 ¶
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 ¶
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.
An Update taking URLParams itself keeps the page mounted taking nothing of the URL: for a page whose components keep their state in it (@ui.URL fields, Query), which fill from the new URL as they render.
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
- compress.go
- contextvars.go
- cors.go
- crons.go
- csrf.go
- devsocket.go
- devtools.go
- devtools_unix.go
- di.go
- diag.go
- dotenv.go
- env.go
- errors.go
- fixed.go
- folder.go
- forms.go
- frontend.go
- graphqlhost.go
- hooks.go
- inject.go
- islandpage.go
- live.go
- livepark.go
- livews.go
- nav.go
- oneway.go
- pager.go
- pagination.go
- parts.go
- push.go
- query.go
- recovery.go
- register.go
- registry.go
- router.go
- security.go
- slash.go
- staticassets.go
- tailwind.go
- toolbar.go
- toolbar_crons.go
- toolbar_graphql.go
- toolbar_panels.go
- toolbar_wire.go
- vars.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
|
|
|
auth/accounts
Package accounts is the app's users: who can sign in, and as whom.
|
Package accounts is the app's users: who can sign in, and as whom. |
|
auth/notices
Package notices is the board's notices, shared by every page and the API.
|
Package notices is the board's notices, shared by every page and the API. |
|
islands/registry
Package registry is the applicants' store, provided to the app.
|
Package registry is the applicants' store, provided to the app. |
|
live/library
Package library is the books the shelf and the book pages show.
|
Package library is the books the shelf and the book pages show. |
|
live/pages/books
Package books is the shelf, /books, and in [id] a page per book, /books/{id}, all in a layout of their own.
|
Package books is the shelf, /books, and in [id] a page per book, /books/{id}, all in a layout of their own. |
|
live/rooms
Package rooms is the chat's: what it broadcasts, and each room's history.
|
Package rooms is the chat's: what it broadcasts, and each room's history. |
|
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. |
|
pets/pages/signup
Package signup is /signup: the form, its POST, and the page after it.
|
Package signup is /signup: the form, its POST, and the page after it. |
|
Package graphql serves GraphQL: a schema of Go values, the validation and execution of operations against it, introspection, the schema's SDL, and the GraphQL-over-HTTP transport.
|
Package graphql serves GraphQL: a schema of Go values, the validation and execution of operations against it, introspection, the schema's SDL, and the GraphQL-over-HTTP transport. |
|
language
Package language reads and writes GraphQL documents: a lexer and a parser for executable documents (operations and fragments) and type-system documents (schema definitions, the SDL), the syntax tree both produce, and a printer.
|
Package language reads and writes GraphQL documents: a lexer and a parser for executable documents (operations and fragments) and type-system documents (schema definitions, the SDL), the syntax tree both produce, and a printer. |
|
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. |
|
Package viewtest helps test pages: it gives pages that read template variables the values a test chooses instead of what the app's context processors (web.ContextProcessors) compute, and sets how a page's deferred parts render.
|
Package viewtest helps test pages: it gives pages that read template variables the values a test chooses instead of what the app's context processors (web.ContextProcessors) compute, and sets how a page's deferred parts render. |