kit

package
v1.6.0 Latest Latest
Warning

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

Go to latest
Published: Nov 11, 2026 License: MIT Imports: 45 Imported by: 0

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

View Source
const LivePath = "/_tern/live"

LivePath is the WebSocket that live Go components (<script lang="go" live>) talk to.

Variables

View Source
var ErrNoEmails = ssr.ErrNoEmails

ErrNoEmails is returned by RenderEmail when the app has no src/emails.

Functions

func ClientIP added in v1.4.0

func ClientIP(r *http.Request) string

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

func DecodeQuery(q url.Values, dst any) error

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

func Error(status int, message string) 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

func Fail(status int, data any) error

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

func FieldError(field, message string) error

FieldError fails a FormAction with a message on one field, such as kit.FieldError("email", "That email is already registered.").

func FormError added in v1.4.0

func FormError(message string) error

FormError fails a FormAction with a message about the whole form.

func NotFound

func NotFound() error

NotFound returns a 404 error.

func Parent

func Parent[T any](c *Ctx) (T, error)

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

func Redirect(status int, location string) error

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.

Types

type ActionFunc

type ActionFunc func(*Ctx) (any, error)

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

func New(m *Manifest, opts ...Option) (*App, error)

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

func (a *App) Close() error

Close releases the app's resources, such as server-rendering VMs. ListenAndServe and Serve call it on shutdown.

func (*App) ListenAndServe

func (a *App) ListenAndServe() error

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.

func (*App) RenderEmail added in v1.2.0

func (a *App) RenderEmail(ctx context.Context, name string, props any) (Email, error)

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) Serve

func (a *App) Serve(ctx context.Context) error

Serve runs the app until ctx is canceled, then shuts down gracefully.

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

func (a *App) Start() error

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.

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

func (c *Ctx) BasePath() string

BasePath returns the app's base path from tern.json, such as "/app", or "".

func (*Ctx) ClientIP added in v1.4.0

func (c *Ctx) ClientIP() string

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

func (c *Ctx) Content() fs.FS

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) Cookie

func (c *Ctx) Cookie(name string) (string, bool)

Cookie returns the value of a request cookie.

func (*Ctx) Deadline

func (c *Ctx) Deadline() (time.Time, bool)

Deadline implements context.Context.

func (*Ctx) DeleteCookie

func (c *Ctx) DeleteCookie(name string)

DeleteCookie removes a cookie set with the default path "/".

func (*Ctx) Depends added in v0.4.0

func (c *Ctx) Depends(keys ...string)

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) Dev added in v1.2.0

func (c *Ctx) Dev() bool

Dev reports whether the app runs under tern dev.

func (*Ctx) Done

func (c *Ctx) Done() <-chan struct{}

Done implements context.Context.

func (*Ctx) Err

func (c *Ctx) Err() error

Err implements context.Context.

func (*Ctx) EventStream added in v1.2.0

func (c *Ctx) EventStream(fn func(ctx context.Context, s *EventStream) error) error

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) Form

func (c *Ctx) Form() (url.Values, error)

Form parses and returns the submitted form, including multipart forms.

func (*Ctx) Header

func (c *Ctx) Header(name string) string

Header returns a request header value.

func (*Ctx) JSON

func (c *Ctx) JSON(status int, v any) error

JSON writes v as a JSON response. It is meant for endpoints.

func (*Ctx) Locale added in v1.1.0

func (c *Ctx) Locale() string

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) Param

func (c *Ctx) Param(name string) string

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) Params

func (c *Ctx) Params() map[string]string

Params returns all route parameters. The map must not be modified.

func (*Ctx) Path added in v1.0.0

func (c *Ctx) Path(p string) string

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) RouteID

func (c *Ctx) RouteID() string

RouteID returns the matched route's ID, e.g. "/blog/slug.param".

func (*Ctx) SetBodyLimit added in v1.2.0

func (c *Ctx) SetBodyLimit(n int64)

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

func (c *Ctx) SetCookie(ck *http.Cookie)

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) SetHeader

func (c *Ctx) SetHeader(name, value string)

SetHeader sets a response header. Loads may call it concurrently.

func (*Ctx) SetLocale added in v1.1.0

func (c *Ctx) SetLocale(locale string)

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

func (c *Ctx) T(key string, params ...map[string]any) string

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

func (c *Ctx) TB(key string, params map[string]any) string

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) Text

func (c *Ctx) Text(status int, s string) error

Text writes a plain-text response. It is meant for endpoints.

func (*Ctx) URL

func (c *Ctx) URL() *url.URL

URL returns the request URL. A load that reads it runs again on every client navigation that changes the URL.

func (*Ctx) Value

func (c *Ctx) Value(key any) any

Value implements context.Context.

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 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

func Defer[T any](ctx *Ctx, fn func() (T, error)) Deferred[T]

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

func (x Deferred[T]) MarshalJSON() ([]byte, error)

MarshalJSON encodes a placeholder that the client replaces with a Promise.

func (*Deferred[T]) UnmarshalJSON

func (x *Deferred[T]) UnmarshalJSON([]byte) error

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

type FailError struct {
	Status int
	Data   any
}

FailError is returned by an action to report a failed submission, typically a validation error. Its Data is sent to the page as `form`.

func (*FailError) Error

func (e *FailError) Error() string

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

type FieldErrors map[string]string

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

type HTTPError struct {
	Status  int
	Message string
}

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.

func (*HTTPError) Error

func (e *HTTPError) Error() string

type HandlerFunc

type HandlerFunc func(*Ctx) error

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 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

type HookFunc func(*Ctx) error

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

type LoadFunc func(*Ctx) (any, error)

LoadFunc is a Load or LayoutLoad function with its result type erased. Generated code builds it with LoadOf.

func LoadOf

func LoadOf[T any](f func(*Ctx) (T, error)) LoadFunc

LoadOf adapts a typed load function.

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
	// 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

func WithAddr(addr string) Option

WithAddr sets the address ListenAndServe listens on. The default is HOST:PORT from the environment, or ":3000".

func WithBodyLimit

func WithBodyLimit(n int64) Option

WithBodyLimit sets the maximum request body size for actions and endpoints, in bytes. The default is 1 MiB.

func WithContentSecurityPolicy added in v0.4.0

func WithContentSecurityPolicy(policy string) Option

WithContentSecurityPolicy sends a Content-Security-Policy header on every page. Tern adds a per-request nonce to script-src (creating the directive if needed) and puts it on its own scripts, 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

func WithContentSecurityPolicyReportOnly(policy string) Option

WithContentSecurityPolicyReportOnly is like WithContentSecurityPolicy but sends Content-Security-Policy-Report-Only, to test a policy without enforcing it.

func WithHook

func WithHook(h HookFunc) Option

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 WithLive added in v1.3.0

func WithLive(o LiveOptions) Option

WithLive tunes live components.

func WithLiveBroker added in v1.3.0

func WithLiveBroker(b goscript.Broker) Option

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

func WithLogger(l *slog.Logger) Option

WithLogger sets the logger for unexpected errors and server events. The default logs to slog.Default().

func WithMatcher

func WithMatcher(name string, match func(value string) bool) Option

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

func WithMiddleware(mw ...func(http.Handler) http.Handler) Option

WithMiddleware wraps the app in standard net/http middleware. The first middleware added is the outermost.

func WithOpenAPI added in v1.2.0

func WithOpenAPI(path, title, version string) Option

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 WithPlugins added in v1.0.0

func WithPlugins(plugins ...Plugin) Option

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 WithSSRCache added in v1.2.0

func WithSSRCache(bytes int) Option

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

func WithSSRTimeout(d time.Duration) Option

WithSSRTimeout bounds how long a server render may take before the page falls back to client rendering. The default is 5 seconds.

func WithSSRWorkers

func WithSSRWorkers(n int) Option

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 WithTrustedOrigins

func WithTrustedOrigins(origins ...string) Option

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

func WithTrustedProxies(proxies ...string) Option

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 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 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.

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
}

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

type RedirectError struct {
	Status   int
	Location string
}

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
	// 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)
}

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

func (s *Setup) AllowCrossOrigin(pattern string)

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) 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

func (s *Setup) Handle(pattern string, h http.Handler)

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

func (s *Setup) Head(html string)

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

func (s *Setup) HeadFunc(fn func(c *Ctx, nonce string) string)

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

func (s *Setup) Hook(h HookFunc)

Hook adds a hook that runs before every page, data, action and endpoint request.

func (*Setup) Logger added in v1.0.0

func (s *Setup) Logger() *slog.Logger

Logger returns a logger tagged with the plugin's name.

func (*Setup) Matcher added in v1.0.0

func (s *Setup) Matcher(name string, match func(value string) bool)

Matcher registers a parameter matcher, as WithMatcher does.

func (*Setup) Middleware added in v1.0.0

func (s *Setup) Middleware(mw func(http.Handler) http.Handler)

Middleware wraps the App in net/http middleware.

func (*Setup) Name added in v1.0.0

func (s *Setup) Name() string

Name returns the plugin's name.

func (*Setup) OnShutdown added in v1.0.0

func (s *Setup) OnShutdown(fn func(ctx context.Context) error)

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

func (s *Setup) OnStart(fn func(ctx context.Context) error)

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

func (s *Setup) TrustOrigin(origin string)

TrustOrigin allows cross-origin form posts and endpoint requests from origin, as WithTrustedOrigins does. CORS plugins use it.

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

func (ws *WebSocket) Conn() *websocket.Conn

Conn returns the underlying github.com/coder/websocket connection, for binary messages, pings and other details.

func (*WebSocket) Receive added in v1.2.0

func (ws *WebSocket) Receive(v any) error

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

func (ws *WebSocket) ReceiveText() (string, error)

ReceiveText reads the next message as text.

func (*WebSocket) Send added in v1.2.0

func (ws *WebSocket) Send(v any) error

Send sends v as a JSON text message.

func (*WebSocket) SendText added in v1.2.0

func (ws *WebSocket) SendText(s string) error

SendText sends a text message as is.

func (*WebSocket) Subprotocol added in v1.2.0

func (ws *WebSocket) Subprotocol() string

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.

Jump to

Keyboard shortcuts

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