Documentation
¶
Overview ¶
Package kit is the server runtime for Tern applications.
A route directory's Go package defines what the route does on the server, by exporting:
func Load(ctx *kit.Ctx) (T, error) // data for +page.svelte
func LayoutLoad(ctx *kit.Ctx) (T, error) // data for +layout.svelte
var Actions = kit.Actions{...} // form actions for the page
func GET(ctx *kit.Ctx) error // endpoint handlers: GET, POST, …
T must encode to a JSON object: a struct or a map. Its JSON is passed to the Svelte component as data, with TypeScript types generated from T.
Slow data can be streamed: put a Deferred in the data, created with Defer, and render it with {#await} in Svelte.
Errors control the response. Return Error or NotFound for an expected error page, Redirect to redirect, and Fail from an action to report a validation error. Any other error is logged and shown as "Internal Error".
`tern gen` writes tern.gen.go, which wires every route into a Manifest and defines ternApp. A typical main.go:
func main() {
app, err := ternApp(kit.WithHook(auth))
if err != nil {
log.Fatal(err)
}
if err := app.ListenAndServe(); err != nil {
log.Fatal(err)
}
}
Index ¶
- Constants
- Variables
- func ClientIP(r *http.Request) string
- func DecodeQuery(q url.Values, dst any) error
- func Error(status int, message string) error
- func Fail(status int, data any) error
- func FieldError(field, message string) error
- func FormError(message string) error
- func FromTrustedProxy(r *http.Request) bool
- func IsHTTPS(r *http.Request) bool
- func NotFound() error
- func Parent[T any](c *Ctx) (T, error)
- func QueryName(f reflect.StructField) string
- func Redirect(status int, location string) error
- func Seconds(n int64) time.Duration
- func ValidateInput(c *Ctx, in any) error
- type Access
- type ActionFunc
- type Actions
- type App
- func (a *App) Close() error
- func (a *App) ConfigureServer(srv *http.Server)
- func (a *App) ListenAndServe() error
- func (a *App) NewCtx(w http.ResponseWriter, r *http.Request, o CtxOptions) (*Ctx, error)
- func (a *App) RenderEmail(ctx context.Context, name string, props any) (Email, error)
- func (a *App) Revalidate(ctx context.Context, tags ...string) error
- func (a *App) Serve(ctx context.Context) error
- func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request)
- func (a *App) Start() error
- func (a *App) WrapListener(ln net.Listener) (net.Listener, error)
- type AutoTLS
- type Ctx
- func (c *Ctx) BasePath() string
- func (c *Ctx) Cache() *cache.Cache
- func (c *Ctx) ClientIP() string
- func (c *Ctx) Content() fs.FS
- func (c *Ctx) Cookie(name string) (string, bool)
- func (c *Ctx) Deadline() (time.Time, bool)
- func (c *Ctx) DeleteCookie(name string)
- func (c *Ctx) Depends(keys ...string)
- func (c *Ctx) Dev() bool
- func (c *Ctx) Done() <-chan struct{}
- func (c *Ctx) Err() error
- func (c *Ctx) EventStream(fn func(ctx context.Context, s *EventStream) error) error
- func (c *Ctx) Flush()
- func (c *Ctx) Form() (url.Values, error)
- func (c *Ctx) Header(name string) string
- func (c *Ctx) JSON(status int, v any) error
- func (c *Ctx) Locale() string
- func (c *Ctx) LogWith(attrs ...slog.Attr)
- func (c *Ctx) Logger() *slog.Logger
- func (c *Ctx) Param(name string) string
- func (c *Ctx) Params() map[string]string
- func (c *Ctx) Path(p string) string
- func (c *Ctx) Revalidate(tags ...string) error
- func (c *Ctx) RouteID() string
- func (c *Ctx) SetBodyLimit(n int64)
- func (c *Ctx) SetCookie(ck *http.Cookie)
- func (c *Ctx) SetHeader(name, value string)
- func (c *Ctx) SetLocale(locale string)
- func (c *Ctx) T(key string, params ...map[string]any) string
- func (c *Ctx) TB(key string, params map[string]any) string
- func (c *Ctx) Tag(tags ...string)
- func (c *Ctx) Text(status int, s string) error
- func (c *Ctx) URL() *url.URL
- func (c *Ctx) UserError(err error) (status int, message string)
- func (c *Ctx) Value(key any) any
- func (c *Ctx) WebSocket(opts *WebSocketOptions, fn func(ctx context.Context, ws *WebSocket) error) error
- func (c *Ctx) Writer() http.ResponseWriter
- type CtxOptions
- type Deferred
- type Email
- type Event
- type EventStream
- type FailError
- type FallbackDef
- type FieldErrors
- type HTTPError
- type HandlerFunc
- func OutputEndpoint[Out any](fn func(*Ctx) (Out, error)) HandlerFunc
- func StreamEndpoint[In, T any](method string, fn func(*Ctx, In, *Stream[T]) error) HandlerFunc
- func StreamOutputEndpoint[T any](fn func(*Ctx, *Stream[T]) error) HandlerFunc
- func TypedEndpoint[In, Out any](method string, fn func(*Ctx, In) (Out, error)) HandlerFunc
- type HookFunc
- type Invalid
- type LayoutDef
- type LiveOptions
- type LoadFunc
- type Manifest
- type Option
- func WithAddr(addr string) Option
- func WithAutoTLS(a AutoTLS) Option
- func WithBodyLimit(n int64) Option
- func WithCSPSources(directive string, sources ...string) Option
- func WithCache(c *cache.Cache) Option
- func WithContentSecurityPolicy(policy string) Option
- func WithContentSecurityPolicyReportOnly(policy string) Option
- func WithHook(h HookFunc) Option
- func WithLive(o LiveOptions) Option
- func WithLiveBroker(b goscript.Broker) Option
- func WithLogger(l *slog.Logger) Option
- func WithMatcher(name string, match func(value string) bool) Option
- func WithMiddleware(mw ...func(http.Handler) http.Handler) Option
- func WithOpenAPI(path, title, version string) Option
- func WithPermissions(check func(c *Ctx, perms ...string) error) Option
- func WithPlugins(plugins ...Plugin) Option
- func WithReporter(r *report.Reporter) Option
- func WithSSRCache(bytes int) Option
- func WithSSRTimeout(d time.Duration) Option
- func WithSSRWorkers(n int) Option
- func WithSignedIn(check func(*Ctx) error) Option
- func WithTLS(cfg *tls.Config) Option
- func WithTracer(t *tracing.Tracer) Option
- func WithTrustedOrigins(origins ...string) Option
- func WithTrustedProxies(proxies ...string) Option
- func WithoutCSRFProtection() Option
- func WithoutContentSecurityPolicy() Option
- func WithoutNativeSSR() Option
- func WithoutRateLimit() Option
- func WithoutSSR() Option
- func WithoutSecurityHeaders() Option
- type PageDef
- type Plugin
- type RedirectError
- type RouteDef
- type Setup
- func (s *Setup) AllowCrossOrigin(pattern string)
- func (s *Setup) ConnContext(fn func(ctx context.Context, c net.Conn) context.Context)
- func (s *Setup) Endpoint(pattern string, fn HandlerFunc)
- func (s *Setup) Handle(pattern string, h http.Handler)
- func (s *Setup) Head(html string)
- func (s *Setup) HeadFunc(fn func(c *Ctx, nonce string) string)
- func (s *Setup) Hook(h HookFunc)
- func (s *Setup) Logger() *slog.Logger
- func (s *Setup) Matcher(name string, match func(value string) bool)
- func (s *Setup) Middleware(mw func(http.Handler) http.Handler)
- func (s *Setup) Name() string
- func (s *Setup) OnClientHello(fn func(hello *tls.ClientHelloInfo))
- func (s *Setup) OnShutdown(fn func(ctx context.Context) error)
- func (s *Setup) OnStart(fn func(ctx context.Context) error)
- func (s *Setup) TrustOrigin(origin string)
- func (s *Setup) WrapConn(fn func(net.Conn) net.Conn)
- type Stream
- type Validator
- type WebSocket
- type WebSocketOptions
Constants ¶
const BrowserErrorsPath = "/__tern/errors"
BrowserErrorsPath is where pages send their uncaught errors.
const DefaultContentSecurityPolicy = "default-src 'self'; script-src 'self' 'unsafe-hashes' '" + svelteEventHash + "'; style-src 'self' 'unsafe-inline'; " +
"img-src 'self' data: blob: https:; font-src 'self' data:; connect-src 'self'; object-src 'none'; base-uri 'self'"
DefaultContentSecurityPolicy is the Content-Security-Policy pages are sent unless the app sets its own (WithContentSecurityPolicy) or none (WithoutContentSecurityPolicy); see ADR 0027. Only the app's own scripts, and inline scripts with the request's nonce, run.
The one inline event handler allowed is Svelte's: server rendering puts onload="this.__e=event" and onerror="this.__e=event" on elements whose load and error events must not be lost before hydration. 'unsafe-hashes' with its hash allows that exact text and no other handler.
const Forever = time.Duration(-1)
Forever is the Revalidate of a route whose page is cached until a tag it used is revalidated.
const LivePath = "/_tern/live"
LivePath is the WebSocket that live Go components (<script lang="go" live>) talk to.
const RequestIDHeader = "X-Request-Id"
RequestIDHeader is the header that carries a request's ID.
Variables ¶
var ( // Public lets anyone use the route. Public = Access{/* contains filtered or unexported fields */} // SignedIn lets signed-in users use the route; see WithSignedIn. SignedIn = Access{/* contains filtered or unexported fields */} )
var ErrNoEmails = ssr.ErrNoEmails
ErrNoEmails is returned by RenderEmail when the app has no src/emails.
Functions ¶
func ClientIP ¶ added in v1.4.0
ClientIP returns the address of the client that made r, taking WithTrustedProxies into account. Middleware and plugins can use it; handlers with a Ctx can call Ctx.ClientIP.
func DecodeQuery ¶ added in v1.2.0
DecodeQuery fills the struct dst points to from query parameters, by each field's query tag, else its json tag name, else its name. Fields may be strings, booleans, numbers, time.Time (RFC 3339), types with UnmarshalText, pointers to these (nil when absent), or slices of them (from repeated parameters).
func Error ¶
Error returns an expected error with the given status and a message that is safe to show to users. Return it from Load, an action or an endpoint to render the nearest +error.svelte.
Any other error returned from your code is treated as unexpected: it is logged, and users see only "Internal Error".
func Fail ¶
Fail returns an action failure with a 4xx status. The page re-renders with data available as the `form` prop, so it can show errors next to fields and keep the user's input.
func FieldError ¶ added in v1.4.0
FieldError fails a FormAction with a message on one field, such as kit.FieldError("email", "That email is already registered.").
func FromTrustedProxy ¶ added in v1.9.0
FromTrustedProxy reports whether r came through one of the proxies named with WithTrustedProxies, so headers that proxy sets can be believed. Without any configured, it is false.
func IsHTTPS ¶ added in v1.9.0
IsHTTPS reports whether r came over HTTPS: to the App itself, or to a proxy whose X-Forwarded-Proto the App trusts.
func Parent ¶
Parent waits for the layouts above the current load, merges their data, and decodes it into T. Use it in Load or LayoutLoad to build on data a parent layout loaded:
type Parent struct{ User User `json:"user"` }
p, err := kit.Parent[Parent](ctx)
Loads that do not call Parent run fully in parallel.
func QueryName ¶ added in v1.2.0
func QueryName(f reflect.StructField) string
QueryName is the query parameter name of a struct field; see DecodeQuery.
func Redirect ¶
Redirect returns an error that redirects to location with a 3xx status. Use 303 after a form submission, 307 or 308 to keep the request method, and 301 or 302 for permanent and temporary moves.
func Seconds ¶ added in v1.12.0
Seconds turns a route's `const Revalidate = 60` into a duration; the generated code calls it.
func ValidateInput ¶ added in v1.14.0
ValidateInput checks in as a typed endpoint checks its input: the validate tags of its fields, then its Validate method, if it has one. The error is a 400 with the messages. MCP tools use it for their arguments. in may be a struct or a pointer to one.
Types ¶
type Access ¶ added in v1.16.0
type Access struct {
// contains filtered or unexported fields
}
Access says who may use a route: its pages, their data, its form actions and its endpoints. A route package declares it, and code generation reads it (ADR 0027):
var Access = kit.Public // anyone
var Access = kit.SignedIn // a signed-in user
var Access = kit.Permission("links.edit") // a signed-in user with the permission
SignedIn and Permission apply to the directories below too, unless one declares its own; Public applies to its own directory only. A route with server code (a load, actions or an endpoint) that has no Access, of its own or from above, doesn't build: routes are closed by default.
func Permission ¶ added in v1.16.0
Permission lets signed-in users with all of perms use the route; see WithPermissions.
func (Access) Restricted ¶ added in v1.16.0
Restricted reports whether the rule keeps anyone out.
type ActionFunc ¶
ActionFunc handles a form submission. Its result is sent to the page as the `form` prop. Use Action to write actions with a typed result, so the generated `form` type is precise.
func Action ¶
func Action[T any](f func(*Ctx) (T, error)) ActionFunc
Action adapts a typed action function:
var Actions = kit.Actions{
"default": kit.Action(create),
}
func create(ctx *kit.Ctx) (Created, error) { ... }
func FormAction ¶ added in v1.4.0
func FormAction[In, Out any](fn func(*Ctx, In) (Out, error)) ActionFunc
FormAction adapts an action that takes its form as a struct. The form is decoded into In and checked against its validate tags, and then its Validate method if it has one, before fn runs:
type NewProject struct {
Name string `form:"name" validate:"required,min=3,max=60" label:"Project name"`
Email string `form:"email" validate:"required,email"`
Seats int `form:"seats" validate:"min=1,max=500"`
}
var Actions = kit.Actions{
"create": kit.FormAction(func(ctx *kit.Ctx, in NewProject) (Created, error) { ... }),
}
If a check fails, the action fails with status 400 and an Invalid. tern gen writes the same rules to ./$forms, so the page can check them as the user types. The rules are:
required the field must have a value; a checkbox must be checked
min=N, max=N characters for text, the value for numbers, the date for
dates, choices for []string
len=N exactly N characters
email, url an email address; an http or https URL
pattern=RE the whole value matches RE; it must be the last rule
oneof=a|b|c one of these values
same=field equal to another field, such as a password confirmation
Fields are named by their form tag, else their json name. The label tag names them in messages (default: the name, humanized), and message replaces all of a field's messages. form:"name,secret" keeps a value out of Invalid.Values; fields named like "password" are secret anyway. time.Time fields are dates (YYYY-MM-DD), or with form:"name,datetime", a datetime-local input's value.
type Actions ¶
type Actions map[string]ActionFunc
Actions maps action names to handlers. A form posting to the page with no ?/name runs "default"; <form action="?/create"> runs "create".
type App ¶
type App struct {
// contains filtered or unexported fields
}
App serves a Tern application. It implements http.Handler, so it can be used with any server or router; App.ListenAndServe runs it with production-ready defaults.
func New ¶
New creates an App from a generated manifest. Generated code calls it for you; see the ternApp function in tern.gen.go.
func (*App) Close ¶
Close releases the app's resources, such as server-rendering VMs. ListenAndServe and Serve call it on shutdown.
func (*App) ConfigureServer ¶ added in v1.9.0
ConfigureServer sets srv up for listeners from WrapListener: HTTP/2 on the connections it terminated TLS on, and the plugins' connection contexts.
func (*App) ListenAndServe ¶
ListenAndServe runs the app until it receives SIGINT or SIGTERM, then shuts down gracefully, letting in-flight requests finish for up to 10 seconds.
It listens on the address set with WithAddr, or HOST:PORT from the environment, defaulting to port 3000 on all interfaces, or 443 when the App serves HTTPS (WithTLS, WithAutoTLS).
func (*App) NewCtx ¶ added in v1.10.0
func (a *App) NewCtx(w http.ResponseWriter, r *http.Request, o CtxOptions) (*Ctx, error)
NewCtx makes the Ctx of request r, written to w, as the App makes one for a route: with the request's locale and its hooks run. Call a load, an action or an endpoint with it: in tests (see kit/kittest), or from a handler of your own. A hook's error is returned, as it would end the request.
func (*App) RenderEmail ¶ added in v1.2.0
RenderEmail renders the email template src/emails/<name>.svelte with props, which are passed to the component as JSON. The template's <svelte:head><title> is the subject:
<script lang="ts">
let { name, link }: { name: string; link: string } = $props();
</script>
<svelte:head><title>Welcome, {name}</title></svelte:head>
<h1>Welcome, {name}!</h1>
<p><a href={link}>Confirm your email</a></p>
Templates render with the same engine as pages; under tern dev they update as you edit them, and /__tern/emails previews them. Send the result with the mail package.
func (*App) Revalidate ¶ added in v1.12.0
Revalidate is Ctx.Revalidate, for jobs and other code without a request.
func (*App) ServeHTTP ¶
func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP implements http.Handler.
func (*App) Start ¶ added in v1.2.0
Start runs the plugins' start functions. Serve and ListenAndServe call it; call it yourself when serving the App with your own http.Server. It runs once; later calls return the first result.
func (*App) WrapListener ¶ added in v1.9.0
WrapListener wraps ln as Serve does: terminating TLS if the App has it (WithTLS, WithAutoTLS) and applying the plugins' connection wrappers. Use it with ConfigureServer to serve the App with an http.Server of your own.
type AutoTLS ¶ added in v1.9.0
type AutoTLS struct {
// Domains are the host names to get certificates for. Requests for
// other names get no certificate.
Domains []string
// Email is given to the ACME service for notices about the
// certificates. Optional.
Email string
// Cache is the directory certificates are kept in across restarts.
// The default is "tern-certs" in the working directory. Keep it: an
// app that asks for new certificates on every start runs into the
// service's rate limits.
Cache string
// DirectoryURL is the ACME service. The default is Let's Encrypt;
// use https://acme-staging-v02.api.letsencrypt.org/directory to test.
DirectoryURL string
// HTTPAddr is where plain HTTP is served: the service's http-01
// challenges, and redirects to HTTPS for everything else. The default
// is ":80"; "-" serves no plain HTTP, leaving the tls-alpn-01
// challenge, which needs port 443.
HTTPAddr string
}
AutoTLS gets and renews certificates from Let's Encrypt, or another ACME service, for WithAutoTLS.
type Ctx ¶
type Ctx struct {
// Request is the incoming request.
Request *http.Request
// Locals holds request-scoped values, typically set by a hook (for
// example the signed-in user) and read by loads and actions.
Locals map[string]any
// contains filtered or unexported fields
}
Ctx is the request context passed to Load, LayoutLoad, actions, endpoints and hooks. It implements context.Context, so it can be passed directly to database calls and other APIs that take a context.
func (*Ctx) BasePath ¶ added in v1.0.0
BasePath returns the app's base path from tern.json, such as "/app", or "".
func (*Ctx) Cache ¶ added in v1.12.0
Cache is the App's cache, for loads to keep results in:
posts, err := cache.Get(c, c.Cache(), "posts:recent", cache.For(time.Minute, "posts"), db.RecentPosts)
func (*Ctx) ClientIP ¶ added in v1.4.0
ClientIP returns the client's IP address. Behind a reverse proxy, configure it with WithTrustedProxies; otherwise it is the proxy's address.
func (*Ctx) Content ¶ added in v1.2.0
Content returns the app's src/content directory: embedded in the binary in production, and read from disk in development so edits show on reload. The content package builds typed collections on top of it.
func (*Ctx) DeleteCookie ¶
DeleteCookie removes a cookie set with the default path "/".
func (*Ctx) Depends ¶ added in v0.4.0
Depends declares that the running load depends on keys, such as "app:projects" or a URL it fetches. Calling invalidate("app:projects") in the browser reruns every load that depends on it.
On client navigation, a layout's load only runs again when something it used changed: a parameter read with Param, the URL read with URL, the data of a parent it read with Parent, or an invalidated dependency. Reading Request directly is not tracked; use these methods instead.
func (*Ctx) EventStream ¶ added in v1.2.0
EventStream responds with a stream of server-sent events. fn runs until it returns, the client disconnects, or the server shuts down; its ctx is canceled in the last two cases. A comment is sent every 20 seconds to keep proxies from closing an idle connection.
func GET(ctx *kit.Ctx) error {
updates := hub.Subscribe(ctx, "status")
return ctx.EventStream(func(ctx context.Context, s *kit.EventStream) error {
for {
select {
case <-ctx.Done():
return nil
case u := <-updates:
if err := s.Send("status", u); err != nil {
return err
}
}
}
})
}
func (*Ctx) Flush ¶ added in v1.10.0
func (c *Ctx) Flush()
Flush writes the headers and cookies set on c to its response, for a Ctx made with NewCtx whose handler doesn't write one itself.
func (*Ctx) Locale ¶ added in v1.1.0
Locale returns the request's locale, such as "de", or "" if the app has no src/locales. Tern picks it from a "lang" route parameter, the locale cookie (set by setLocale in the browser), Accept-Language, and finally the default locale. A load that reads it reruns when it changes.
func (*Ctx) LogWith ¶ added in v1.9.0
LogWith adds attributes to the records logged in this request from now on, through its context and Ctx.Logger: in a hook, the signed-in user's ID, say.
func (*Ctx) Logger ¶ added in v1.9.0
Logger returns the app's logger with the request's attributes: its ID (and trace, if any) and route. Records logged with it carry them even when logged without a context.
func (*Ctx) Param ¶
Param returns the value of a route parameter, or "" if it is absent. A load that reads a parameter runs again when the parameter changes.
func (*Ctx) Path ¶ added in v1.0.0
Path prefixes an app path such as "/projects/1" with the base path, for links built in Go. kit.Redirect adds the base path itself.
func (*Ctx) Revalidate ¶ added in v1.12.0
Revalidate makes the cached pages and load results with any of tags stale, in every process that shares the cache. The next request renders them again. Call it after the data changes:
err := c.Revalidate("posts", "post:"+id)
func (*Ctx) SetBodyLimit ¶ added in v1.2.0
SetBodyLimit raises (or lowers) the request body limit for this request, such as for an action that accepts file uploads. Call it before reading the body, with ctx.Form or otherwise. The default is set with WithBodyLimit.
func (*Ctx) SetCookie ¶
SetCookie adds a Set-Cookie header; setting the same cookie again in a request replaces it. Unset fields get safe defaults: Path "/", SameSite=Lax, and Secure unless the request is plain HTTP to localhost. Set HttpOnly yourself for cookies scripts must not read, such as session cookies. Loads may call it concurrently.
func (*Ctx) SetLocale ¶ added in v1.1.0
SetLocale overrides the request's locale, for example from a user's saved preference in a hook. Unsupported locales are ignored.
func (*Ctx) T ¶ added in v1.1.0
T translates key into the request's locale, for text produced on the server such as validation errors. params fill placeholders like {name} and plurals; see the guide for the message syntax.
func (*Ctx) TB ¶ added in v1.6.0
TB translates one of Tern's own texts, whose keys start with "tern.", such as a validation message or an accounts email: the app's message for its key in src/locales, else Tern's English.
func (*Ctx) Tag ¶ added in v1.12.0
Tag names data the request used, such as "posts" or "post:42", so Revalidate with the tag refreshes a page rendered on demand that used it. Outside such a render it does nothing.
func (*Ctx) URL ¶
URL returns the request URL. A load that reads it runs again on every client navigation that changes the URL.
func (*Ctx) UserError ¶ added in v1.14.0
UserError is what a user may see of err: its status and message. An error from Error or NotFound keeps its own; any other is logged with the request, and its message is "Internal Error", except under tern dev. Code that answers requests its own way, such as an MCP server, uses it to report errors as endpoints do.
func (*Ctx) WebSocket ¶ added in v1.2.0
func (c *Ctx) WebSocket(opts *WebSocketOptions, fn func(ctx context.Context, ws *WebSocket) error) error
WebSocket upgrades the request to a WebSocket connection and runs fn until it returns, the client disconnects, or the server shuts down; its ctx is canceled in the last two cases. Connections from other sites are refused unless their origin is allowed in opts.
func GET(ctx *kit.Ctx) error {
return ctx.WebSocket(nil, func(ctx context.Context, ws *kit.WebSocket) error {
for {
var msg Message
if err := ws.Receive(&msg); err != nil {
return err
}
if err := ws.Send(Reply{Text: msg.Text}); err != nil {
return err
}
}
})
}
func (*Ctx) Writer ¶
func (c *Ctx) Writer() http.ResponseWriter
Writer returns the response writer for endpoints, with headers and cookies set through the Ctx already applied. Do not use it in loads.
type CtxOptions ¶ added in v1.10.0
type CtxOptions struct {
// Route is the route's ID, as RouteID returns it: "/blog/slug.param".
Route string
// Params are the route's parameters.
Params map[string]string
// Parent is the layouts' data, which Parent[T] decodes: a struct or a
// map, encoded as a load's result is.
Parent any
// Locals are added to the Ctx's Locals.
Locals map[string]any
// NoHooks skips the App's hooks, the plugins' among them, which a Ctx
// runs through by default, as a request does before its loads.
NoHooks bool
}
CtxOptions describes the request a Ctx made with NewCtx belongs to.
type Deferred ¶
type Deferred[T any] struct { // contains filtered or unexported fields }
Deferred is a value that a load starts computing but does not wait for. The page renders as soon as the rest of the data is ready, and the deferred value streams to the browser when it resolves. In Svelte it is a Promise:
type Data struct {
Post Post `json:"post"`
Comments kit.Deferred[[]Comment] `json:"comments"`
}
{#await data.comments}Loading…{:then comments}…{:catch error}…{/await}
Create one with Defer.
func Defer ¶
Defer runs fn in the background and returns its result as a Deferred, so slow data does not hold up the page. fn should respect ctx for cancellation; it runs only for the lifetime of the request.
func (Deferred[T]) MarshalJSON ¶
MarshalJSON encodes a placeholder that the client replaces with a Promise.
func (*Deferred[T]) UnmarshalJSON ¶
UnmarshalJSON ignores its input: deferred values are not available to kit.Parent, only to the browser.
type Email ¶ added in v1.2.0
type Email struct {
Subject string
HTML string
// Text is the plain-text version, made from the HTML.
Text string
}
Email is a rendered email template; see App.RenderEmail.
type Event ¶ added in v1.2.0
type Event struct {
// Name is the event type; "" is the default "message" event.
Name string
// ID is stored by the browser and sent back as Last-Event-ID when it
// reconnects, so a stream can resume.
ID string
// Data is sent as JSON, unless it is a string.
Data any
// Retry tells the browser how long to wait before reconnecting.
Retry time.Duration
}
Event is one server-sent event.
type EventStream ¶ added in v1.2.0
type EventStream struct {
// contains filtered or unexported fields
}
EventStream is a server-sent events connection, opened with Ctx.EventStream. Browsers read it with EventSource, or with stream() from @ternkit/tern.
func (*EventStream) LastEventID ¶ added in v1.2.0
func (s *EventStream) LastEventID() string
LastEventID returns the ID of the last event the browser received before it reconnected, or "".
func (*EventStream) Send ¶ added in v1.2.0
func (s *EventStream) Send(name string, data any) error
Send sends an event with a name and data, which is encoded as JSON unless it is a string.
func (*EventStream) SendEvent ¶ added in v1.2.0
func (s *EventStream) SendEvent(e Event) error
SendEvent sends an event with all its fields.
type FailError ¶
FailError is returned by an action to report a failed submission, typically a validation error. Its Data is sent to the page as `form`.
type FallbackDef ¶
type FallbackDef struct {
// Layouts are indices into Manifest.Layouts: the root layout, if any.
Layouts []int
}
FallbackDef renders 404s for URLs that match no route.
type FieldErrors ¶ added in v1.4.0
FieldErrors maps field names to messages. Return it from a FormAction, or from an input's Validate method, to fail with errors on those fields; the key "" is a message for the whole form.
func (FieldErrors) Error ¶ added in v1.4.0
func (e FieldErrors) Error() string
type HTTPError ¶
HTTPError is an expected error with an HTTP status, such as a 404. Its message is shown to the user, so it must not contain secrets.
type HandlerFunc ¶
HandlerFunc handles an endpoint request, such as an exported GET.
func OutputEndpoint ¶ added in v1.2.0
func OutputEndpoint[Out any](fn func(*Ctx) (Out, error)) HandlerFunc
OutputEndpoint adapts a typed endpoint without input.
func StreamEndpoint ¶ added in v1.15.0
StreamEndpoint adapts a streaming endpoint with input; generated code calls it.
func StreamOutputEndpoint ¶ added in v1.15.0
func StreamOutputEndpoint[T any](fn func(*Ctx, *Stream[T]) error) HandlerFunc
StreamOutputEndpoint adapts a streaming endpoint without input.
func TypedEndpoint ¶ added in v1.2.0
func TypedEndpoint[In, Out any](method string, fn func(*Ctx, In) (Out, error)) HandlerFunc
TypedEndpoint adapts a typed endpoint; generated code calls it.
type HookFunc ¶
HookFunc runs before every page, data, action and endpoint request. It can read the request, set Locals, and return an error such as kit.Redirect to stop the request.
type Invalid ¶ added in v1.4.0
type Invalid struct {
// Errors maps field names to messages.
Errors map[string]string `json:"errors"`
// Values holds what was posted, except secret fields such as passwords.
Values map[string]any `json:"values"`
// Message is an error about the whole form, such as "Wrong email or password."
Message string `json:"message,omitempty"`
}
Invalid is what a FormAction fails with when the form doesn't pass its rules: the page's form prop, so it can show each error next to its field and refill what was typed. Tern UI's Form reads it.
type LayoutDef ¶
type LayoutDef struct {
Dir string
// Component is the +layout.svelte path relative to the project root,
// or "" if the directory only has a LayoutLoad.
Component string
Load LoadFunc
}
LayoutDef is a layout directory.
type LiveOptions ¶ added in v1.3.0
type LiveOptions struct {
// MaxInstances caps the components one page may mount at once. The
// default is 256.
MaxInstances int
// Rate is how many calls and bound values per second one page may
// send, with bursts of twice that. The default is 50.
Rate float64
// SendBuffer is how many messages may wait for a slow browser before
// the connection is closed; the page reconnects and starts fresh. The
// default is 256.
SendBuffer int
// PingInterval keeps idle connections open through proxies. The
// default is 25 seconds.
PingInterval time.Duration
// Origins lists other origins allowed to connect, as for
// [WebSocketOptions].
Origins []string
}
LiveOptions tune live components; see WithLive.
type LoadFunc ¶
LoadFunc is a Load or LayoutLoad function with its result type erased. Generated code builds it with LoadOf.
type Manifest ¶
type Manifest struct {
// Layouts are shared by routes and referenced by index.
Layouts []LayoutDef
// Routes, in matching priority order.
Routes []RouteDef
// Fallback describes what renders when no route matches.
Fallback FallbackDef
// AppHTML is the contents of src/app.html.
AppHTML string
// Client is the built client: Vite's output directory, including
// .vite/manifest.json. It may be nil in development.
Client fs.FS
// ClientEntry is the Vite manifest key of the client entry module.
ClientEntry string
// WebAssembly is set when components run Go in the browser, compiled
// to WebAssembly: the Content-Security-Policy allows compiling it
// ('wasm-unsafe-eval'), which JavaScript's eval stays outside of.
WebAssembly bool
// ServerBundle is the server renderer built by `vite build --ssr`, or
// "" to render on the client only.
ServerBundle string
// Native is the server renderer compiled to Go by tern build, if it
// was: it makes a realm with the app's modules evaluated. Pages render
// with it, and with ServerBundle only what it can't render. The
// generated tern_native.gen.go sets it.
Native func(setup func(*js.Realm)) *js.Realm
// BasePath is the URL prefix the app is served under, from tern.json,
// such as "/app"; "" serves it at the root.
BasePath string
// Locales holds src/locales/<locale>.json, or nil without i18n.
Locales fs.FS
// Content holds src/content, or nil if the app has none. See Ctx.Content.
Content fs.FS
// Live is set when the app has live Go components; tern gen registers
// them with the goscript package.
Live bool
// OpenAPI is the OpenAPI description of the typed endpoints, served
// with WithOpenAPI.
OpenAPI []byte
// DefaultLocale is the fallback locale; "" picks "en" or the first.
DefaultLocale string
}
Manifest describes an app's routes and assets. It is produced by `tern gen` in tern.gen.go; you do not write it by hand.
Manifest and the *Def types are the interface between generated code and the runtime. They are not covered by Tern's compatibility promise and may change in minor releases: `go get -tool` keeps the CLI that generates them and the kit package that reads them at the same version.
type Option ¶
type Option func(*options)
Option configures an App.
func WithAddr ¶
WithAddr sets the address ListenAndServe listens on. The default is HOST:PORT from the environment, or ":3000".
func WithAutoTLS ¶ added in v1.9.0
WithAutoTLS serves HTTPS with certificates from Let's Encrypt for the domains in a, and redirects plain HTTP to it. The machine must be reachable at those domains on port 443, or 80.
The environment can set it instead: TERN_TLS_DOMAINS, a comma-separated list, with TERN_TLS_EMAIL and TERN_TLS_CACHE.
func WithBodyLimit ¶
WithBodyLimit sets the maximum request body size for actions and endpoints, in bytes. The default is 1 MiB.
func WithCSPSources ¶ added in v1.16.0
WithCSPSources allows more sources for one directive of the Content-Security-Policy, such as an analytics script:
kit.WithCSPSources("script-src", "https://plausible.io")
kit.WithCSPSources("frame-src", "https://www.youtube-nocookie.com")
A directive the policy doesn't have is created from default-src's sources and these.
func WithCache ¶ added in v1.12.0
WithCache sets the cache that pages rendered on demand, and loads that use Ctx.Cache, keep their entries in. The default is 64 MB in memory, per process; a cache on a shared store (cache/redis, or cache.Disk on a shared volume) is shared by every process.
func WithContentSecurityPolicy ¶ added in v0.4.0
WithContentSecurityPolicy replaces the Content-Security-Policy every page is sent (see DefaultContentSecurityPolicy). Tern adds a per-request nonce to script-src (creating the directive if needed) and puts it on its own scripts and on inline scripts in <svelte:head>, so a strict policy such as "default-src 'self'" works without 'unsafe-inline'. Use %tern.nonce% in src/app.html for your own inline scripts.
func WithContentSecurityPolicyReportOnly ¶ added in v0.4.0
WithContentSecurityPolicyReportOnly is like WithContentSecurityPolicy but sends Content-Security-Policy-Report-Only, to test a policy without enforcing it.
func WithHook ¶
WithHook adds a hook. Hooks run in the order they are added, after the hooks of plugins, so they can use sessions and other plugin state.
func WithLiveBroker ¶ added in v1.3.0
WithLiveBroker connects live components in every process of the app through b, so goscript.Shared values, goscript.Presence counts and goscript.Publish reach all of them. Any realtime.Broker works, such as the PostgreSQL one in realtime/pgbroker.
func WithLogger ¶
WithLogger sets the logger for unexpected errors and server events. The default logs to slog.Default().
func WithMatcher ¶
WithMatcher registers a parameter matcher used by directories such as id.param.integer. The built-in matchers "integer" and "uuid" are always available.
func WithMiddleware ¶
WithMiddleware wraps the app in standard net/http middleware. The first middleware added is the outermost.
func WithOpenAPI ¶ added in v1.2.0
WithOpenAPI serves the OpenAPI description that tern gen writes for the app's typed endpoints at path, such as "/openapi.json", with the given title and version.
func WithPermissions ¶ added in v1.16.0
WithPermissions gives Tern the check for kit.Permission routes: it returns nil when the user has every one of perms, and otherwise an error, such as kit.Error(403, …). It runs after the WithSignedIn check. rbac provides one: policy.Guard().
func WithPlugins ¶ added in v1.0.0
WithPlugins adds plugins. They are set up in order, after the other options, so a plugin's hooks and middleware run after those added directly with WithHook and WithMiddleware.
func WithReporter ¶ added in v1.10.0
WithReporter sends the App's unexpected errors and panics, and uncaught errors in its pages' browsers, to r, grouped by cause. Without it, the App uses report.Default(), and under tern dev a reporter that only feeds the dev server's log view.
func WithSSRCache ¶ added in v1.2.0
WithSSRCache sets how much server-rendered HTML is kept in memory, in bytes. A render depends only on the page's data and URL, so a page that looks the same for many visitors renders once; pages with per-user data get their own entries, evicted least recently used first. The default is 32 MiB; 0 turns the cache off.
func WithSSRTimeout ¶
WithSSRTimeout bounds how long a server render may take before the page falls back to client rendering. The default is 5 seconds.
func WithSSRWorkers ¶
WithSSRWorkers sets how many pages can render on the server at once. The default is GOMAXPROCS. Pages compiled to Go render in parallel on every worker; with the JavaScript engine, each worker is a VM using roughly 5 to 20 MB, and the default is at most 4: the VMs share a lock in the C runtime, so more rarely render faster.
func WithSignedIn ¶ added in v1.16.0
WithSignedIn gives Tern the check for kit.SignedIn routes: it returns nil for a signed-in user, and otherwise an error such as a redirect to the sign-in page. accounts provides one: acc.Guard().
func WithTLS ¶ added in v1.9.0
WithTLS serves HTTPS with cfg, which must have certificates (Certificates or GetCertificate). HTTP/2 is offered as well as HTTP/1.1.
Without a port set (WithAddr, or PORT), HTTPS is served on :443.
func WithTracer ¶ added in v1.10.0
WithTracer records each request as a trace with t: a server span for the request, and spans for its loads, actions, endpoints and server render. Without it, the App uses tracing.Default(), and under tern dev a tracer that only feeds the dev server's log view.
func WithTrustedOrigins ¶
WithTrustedOrigins allows cross-origin form submissions and endpoint requests from the given origins, such as "https://example.com".
func WithTrustedProxies ¶ added in v1.4.0
WithTrustedProxies names the reverse proxies in front of the app, as IP addresses or CIDR ranges such as "10.0.0.0/8" or "127.0.0.1". Requests from these peers may set X-Forwarded-For, which Ctx.ClientIP and ClientIP then read, and X-Forwarded-Proto.
Once any proxy is configured, X-Forwarded-Proto from other peers is ignored. Without this option, ClientIP is the connection's own address and X-Forwarded-Proto is honored from anyone, as before.
func WithoutCSRFProtection ¶
func WithoutCSRFProtection() Option
WithoutCSRFProtection disables cross-origin request protection. Only use it if something in front of the app already provides it.
func WithoutContentSecurityPolicy ¶ added in v1.16.0
func WithoutContentSecurityPolicy() Option
WithoutContentSecurityPolicy sends pages without a Content-Security-Policy. Injected markup can then run scripts; prefer WithCSPSources to allow what a page needs.
func WithoutNativeSSR ¶ added in v1.5.0
func WithoutNativeSSR() Option
WithoutNativeSSR renders every page with the JavaScript engine, even when tern build compiled the server renderer to Go. Setting TERN_NATIVE=0 does the same without a rebuild.
func WithoutRateLimit ¶ added in v1.16.0
func WithoutRateLimit() Option
WithoutRateLimit turns off the default limit on state-changing requests (POST, PUT, PATCH and DELETE): 120 a minute per client, in bursts of 60. To change the limit instead, add plugins/ratelimit, which replaces it.
func WithoutSSR ¶
func WithoutSSR() Option
WithoutSSR renders every page on the client only. Use `const SSR = false` in a route's package to turn it off for one page instead.
func WithoutSecurityHeaders ¶ added in v1.16.0
func WithoutSecurityHeaders() Option
WithoutSecurityHeaders stops sending the security headers Tern sends by default: X-Frame-Options, Referrer-Policy, Permissions-Policy, Cross-Origin-Opener-Policy and X-Permitted-Cross-Domain-Policies. To change one instead, use plugins/secure.
type PageDef ¶
type PageDef struct {
// Component is the +page.svelte path relative to the project root.
Component string
Load LoadFunc
Actions Actions
// NoSSR disables server-side rendering for this page; set by
// `const SSR = false` in the route's package.
NoSSR bool
// NoCSR sends the page without JavaScript: the server's HTML is all
// there is, and links to it load it in full. Set by `const CSR = false`.
NoCSR bool
}
PageDef is a route's page.
type Plugin ¶ added in v1.0.0
type Plugin interface {
// Name identifies the plugin in logs and errors.
Name() string
// Setup registers the plugin's behavior. It runs once, when the App is
// created; an error stops the App from starting.
Setup(s *Setup) error
}
Plugin extends an App with reusable behavior: hooks, middleware, matchers, extra HTTP handlers, tags in every page's <head>, and work to do on shutdown. Add plugins with WithPlugins.
type Plugin struct{}
func (Plugin) Name() string { return "hello" }
func (Plugin) Setup(s *kit.Setup) error {
s.Handle("GET /hello", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "hello")
}))
return nil
}
type RedirectError ¶
RedirectError tells Tern to redirect the request.
func (*RedirectError) Error ¶
func (e *RedirectError) Error() string
type RouteDef ¶
type RouteDef struct {
// ID is the route ID, e.g. "/blog/slug.param".
ID string
// Layouts are indices into Manifest.Layouts, outermost first.
Layouts []int
// Page is nil for endpoint-only routes.
Page *PageDef
// Endpoint maps HTTP methods to handlers.
Endpoint map[string]HandlerFunc
// Access says who may use the route; set by `var Access` in the route's
// package or a directory above it.
Access Access
// Prerender renders the page to static HTML at build time; set by
// `const Prerender = true` in the route's package.
Prerender bool
// Entries lists the parameters to prerender a dynamic route with; set
// by an exported Entries function in the route's package.
Entries func(*Ctx) ([]map[string]string, error)
// Revalidate renders the page on demand and caches it for this long,
// or until its tags are revalidated with Forever; set by
// `const Revalidate = 60` (seconds) in the route's package.
Revalidate time.Duration
// CacheControl is the Cache-Control header of the route's successful
// GET responses, for CDNs and browsers; set by `const CacheControl`.
CacheControl string
}
RouteDef is a page, an endpoint, or both.
type Setup ¶ added in v1.0.0
type Setup struct {
// contains filtered or unexported fields
}
Setup is how a plugin registers its behavior.
func (*Setup) AllowCrossOrigin ¶ added in v1.2.0
AllowCrossOrigin exempts requests matching pattern (net/http.ServeMux syntax) from cross-origin protection. Use it only for endpoints that don't act on cookies, such as an OAuth token endpoint that clients on other sites call with their own credentials.
func (*Setup) ConnContext ¶ added in v1.9.0
ConnContext adds to the context of the requests on a connection, as http.Server.ConnContext does; c is the connection as WrapConn's wrappers left it.
func (*Setup) Endpoint ¶ added in v1.2.0
func (s *Setup) Endpoint(pattern string, fn HandlerFunc)
Endpoint serves pattern with fn like a route's endpoint: after the hooks, with a Ctx, so it can use sessions and Locals, and with errors handled like an endpoint's. Patterns use net/http.ServeMux syntax, such as "GET /auth/{provider}/login"; read wildcards with ctx.Request.PathValue. Endpoints take precedence over the app's routes.
func (*Setup) Handle ¶ added in v1.0.0
Handle serves pattern with h, before Tern's routes. Patterns use net/http.ServeMux syntax, such as "GET /healthz" or "/metrics".
func (*Setup) Head ¶ added in v1.0.0
Head adds HTML to the <head> of every page, such as a <meta> tag or an analytics script. Scripts should use the per-request nonce; see Setup.HeadFunc.
func (*Setup) HeadFunc ¶ added in v1.0.0
HeadFunc adds HTML computed per request to the <head> of every page. nonce is the request's Content-Security-Policy nonce, for inline scripts.
func (*Setup) Hook ¶ added in v1.0.0
Hook adds a hook that runs before every page, data, action and endpoint request.
func (*Setup) Middleware ¶ added in v1.0.0
Middleware wraps the App in net/http middleware.
func (*Setup) OnClientHello ¶ added in v1.9.0
func (s *Setup) OnClientHello(fn func(hello *tls.ClientHelloInfo))
OnClientHello is called with each TLS ClientHello, when the App serves HTTPS itself (WithTLS, WithAutoTLS). hello.Conn is the connection under TLS; a WrapConn wrapper's connection has it as NetConn(). fn must not keep hello.
func (*Setup) OnShutdown ¶ added in v1.0.0
OnShutdown registers work to do when the App shuts down, such as flushing buffers. Functions run in reverse order of registration.
func (*Setup) OnStart ¶ added in v1.2.0
OnStart registers work to do when the server starts, before it accepts requests, such as running migrations or starting background workers. ctx is canceled when the server begins shutting down; start long-running work in a goroutine that stops then. An error stops the server from starting. Start functions run in order of registration, and not while tern build prerenders pages.
func (*Setup) TrustOrigin ¶ added in v1.1.0
TrustOrigin allows cross-origin form posts and endpoint requests from origin, as WithTrustedOrigins does. CORS plugins use it.
func (*Setup) WrapConn ¶ added in v1.9.0
WrapConn wraps each connection the server accepts, after TLS: what the wrapper reads is the client's HTTP, as sent. The server sees the wrapper, so it must pass reads, writes, deadlines and Close through. It applies when the App runs the server (ListenAndServe, Serve, or WrapListener with ConfigureServer).
type Stream ¶ added in v1.15.0
type Stream[T any] struct { // contains filtered or unexported fields }
Stream sends values to the client of a streaming endpoint as they come, such as the text of a model's answer as it's written:
type Ask struct {
Question string `json:"question" validate:"required,max=2000"`
}
type Delta struct {
Text string `json:"text"`
}
func POST(ctx *kit.Ctx, in Ask, out *kit.Stream[Delta]) error {
for text := range model.Answer(ctx, in.Question) {
if err := out.Send(Delta{Text: text}); err != nil {
return err // the client went away
}
}
return nil
}
The values go out as server-sent events, which proxies know not to buffer, and $api's stream reads them in the browser, typed as T. An error returned before the first Send is an ordinary error response; after it, the client gets it as the stream's error. An endpoint without input is func(ctx *kit.Ctx, out *kit.Stream[T]) error.
type Validator ¶ added in v1.2.0
type Validator interface{ Validate() error }
Validator is implemented by inputs that check themselves.
type WebSocket ¶ added in v1.2.0
type WebSocket struct {
// contains filtered or unexported fields
}
WebSocket is a WebSocket connection, opened with Ctx.WebSocket.
func (*WebSocket) Conn ¶ added in v1.2.0
Conn returns the underlying github.com/coder/websocket connection, for binary messages, pings and other details.
func (*WebSocket) Receive ¶ added in v1.2.0
Receive reads the next message, which must be JSON, into v. It returns an error when the connection closes; WebSocket treats a normal close as the end of the connection, not a failure.
func (*WebSocket) ReceiveText ¶ added in v1.2.0
ReceiveText reads the next message as text.
func (*WebSocket) Subprotocol ¶ added in v1.2.0
Subprotocol returns the subprotocol the client and server agreed on.
type WebSocketOptions ¶ added in v1.2.0
type WebSocketOptions struct {
// Origins lists other origins allowed to connect, as host patterns
// such as "app.example.com" or "*.example.com". Pages on the app's own
// origin can always connect; browsers on other sites cannot, unless
// they are listed here.
Origins []string
// Subprotocols the server accepts, in order of preference.
Subprotocols []string
// ReadLimit caps the size of a received message, in bytes. The default
// is 1 MiB.
ReadLimit int64
}
WebSocketOptions configure Ctx.WebSocket.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package kittest tests Tern apps from Go: call a load, an action or an endpoint with a request made up for the test, and check what it returns; or run the whole app and query the HTML its pages render.
|
Package kittest tests Tern apps from Go: call a load, an action or an endpoint with a request made up for the test, and check what it returns; or run the whole app and query the HTML its pages render. |