htmlbind

package
v0.2.4 Latest Latest
Warning

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

Go to latest
Published: Jul 29, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

Documentation

Overview

Package htmlbind is the rendering runtime for generated HTML templates.

Generation produces one immutable Plan per component: an ordered instruction list typed by that component's parameter struct. A shared coordinator walks the plan and writes HTML, so composition concerns such as slots, chain nesting, and document head merging live here rather than in generated code.

The package depends on the standard library only, and never on net/http, so generated code stays usable on TinyGo and WebAssembly targets. Response concerns such as content type and content encoding belong to the caller.

Index

Constants

View Source
const (
	// ErrorCodeInternal is the code for any failure that supplied no public
	// projection of its own. Raw Go errors never reach a recover subtree.
	ErrorCodeInternal = "internal"
	// ErrorCodeTimeout is the code for a boundary that exceeded its deadline.
	ErrorCodeTimeout = "timeout"
)

Error codes carried by AsyncError. Generated templates compare against them through the built-in error type's code field.

Variables

View Source
var ErrNilWrapper = errors.New("htmlbind: chain contains an unset wrapper")

ErrNilWrapper reports a wrapper that was left unset.

View Source
var ErrNoLeaf = errors.New("htmlbind: chain needs a leaf component")

ErrNoLeaf reports a chain assembled without an innermost component.

Functions

func Concurrent added in v0.1.16

func Concurrent(ctx context.Context, tasks ...func() error) error

Concurrent runs every task in its own goroutine and reports the first failure in task order. Generated await clauses call it with one task per binding.

This is the whole reason an async external stays an ordinary blocking Go function: the function knows how to fetch its value, and the runtime owns running it off the render path and joining the results. Each task assigns its own field of the boundary scope, so the tasks share no memory and need no lock.

ctx bounds the wait, not the work. When it is cancelled Concurrent returns straight away and the still-running tasks are abandoned: their results are discarded and the caller must not read the scope they were writing. A task that wants to stop early has to take a context of its own.

func ErrUnsetPending added in v0.1.19

func ErrUnsetPending(path string) error

ErrUnsetPending builds the error generated code raises for an unset required async value.

func Escape added in v0.1.15

func Escape(value string) string

Escape applies HTML text and attribute escaping. It is exported so generated helpers can reuse exactly the runtime's rules.

func Flush added in v0.1.16

func Flush(w io.Writer)

Flush pushes buffered bytes toward the client when the writer can. io.Writer has no flush, so the capability is discovered by interface assertion rather than reflection, and a writer without it still produces correct output, only without progressive delivery.

The render entries flush after the initial pass. Call this after writing each Content, because a completion sitting in a buffer defeats the point of having sent it early.

func FormatBool added in v0.1.16

func FormatBool(value bool) string

FormatBool renders a bool as template text.

func FormatFloat added in v0.1.16

func FormatFloat(value float64) string

FormatFloat renders a float64 as template text.

func FormatInt added in v0.1.16

func FormatInt(value int) string

FormatInt renders an int as template text.

func HasAwaitBlock added in v0.1.18

func HasAwaitBlock(wrappers []Wrapper, leaf Fragment) bool

HasAwaitBlock reports whether any member of a chain can open an await boundary. It answers the one question a caller has to settle before rendering: whether this response needs the client runtime that applies settled boundaries.

Ask once for the whole chain rather than per member, so a chain whose layout and page both await still contributes one runtime script.

func JSONArray added in v0.1.16

func JSONArray[T any](values []T, encode func(T) string) string

JSONArray encodes a slice as a JSON array, delegating each element.

func JSONBool added in v0.1.16

func JSONBool(value bool) string

JSONBool encodes a bool as JSON.

func JSONFloat added in v0.1.16

func JSONFloat(value float64) string

JSONFloat encodes a float64 as JSON.

func JSONInt added in v0.1.16

func JSONInt(value int) string

JSONInt encodes an int as JSON.

func JSONOptional added in v0.1.16

func JSONOptional[T any](value *T, encode func(T) string) string

JSONOptional encodes a nil pointer as JSON null and otherwise delegates.

func JSONString added in v0.1.16

func JSONString[T ~string](value T) string

JSONString encodes any string-kinded value as a JSON string, covering plain strings, generated enums, and the trusted string types above. It escapes the characters that could close a script element or break a JavaScript line, so the result is safe to embed in inline script content.

func KeyArray added in v0.1.16

func KeyArray[T any](values []T, encode func(T) string) string

KeyArray frames a slice as its element count followed by its framed elements, so a slice of one two-element string cannot collide with two one-element ones.

func KeyBool added in v0.1.16

func KeyBool(value bool) string

KeyBool frames a bool for a cache key.

func KeyBytes added in v0.1.16

func KeyBytes(value []byte) string

KeyBytes frames a byte slice for a cache key.

func KeyFloat added in v0.1.16

func KeyFloat(value float64) string

KeyFloat frames a float64 for a cache key.

func KeyInt added in v0.1.16

func KeyInt(value int) string

KeyInt frames an int for a cache key.

func KeyOptional added in v0.1.16

func KeyOptional[T any](value *T, encode func(T) string) string

KeyOptional frames a pointer, distinguishing absence from any present value.

func KeyString added in v0.1.16

func KeyString[T ~string](value T) string

KeyString frames a string for a cache key. It is generic over ~string so a generated enum or trusted string type needs no conversion at the call site.

func KeyTime added in v0.1.16

func KeyTime(value time.Time) string

KeyTime frames a time for a cache key. It uses a fixed layout with nanosecond precision so two equal instants in different locations encode identically.

func MergeHead added in v0.1.15

func MergeHead(wrappers []Wrapper, leaf Fragment) []string

MergeHead collects head contributions in composition order, outermost first, dropping later duplicates so two components declaring the same stylesheet emit one tag.

func Render added in v0.1.15

func Render(w io.Writer, leaf Fragment, options ...Option) error

Render writes one component to w.

func RenderAsync added in v0.1.16

func RenderAsync(ctx context.Context, w io.Writer, leaf Fragment, options ...Option) iter.Seq2[Content, error]

RenderAsync renders one component and yields each await boundary as it settles.

func RenderChain added in v0.1.15

func RenderChain(w io.Writer, wrappers []Wrapper, leaf Fragment, options ...Option) error

RenderChain writes a composed document to w. Wrappers apply outermost first, so RenderChain(w, []Wrapper{document, layout}, page) renders page inside layout inside document. An empty wrapper list renders the leaf alone.

Head contributions are merged before the first byte is written, so the shell can emit them without buffering the body. Assembly is validated up front, so a malformed chain cannot leave a partial response behind.

An await boundary reached on this path blocks and emits its settled subtree in place, so one template renders correctly with or without progressive delivery. Use RenderChainAsync to send fallbacks first instead.

Bindings that fail in a clause with no recover subtree return an UnrecoveredError rather than writing the fallback, because a finished document holding a loading state is a document that lies. This path writes as it goes, so render into a buffer when you want that failure to become an error status.

func RenderChainAsync added in v0.1.16

func RenderChainAsync(ctx context.Context, w io.Writer, wrappers []Wrapper, leaf Fragment, options ...Option) iter.Seq2[Content, error]

RenderChainAsync renders a composed document to w and yields one Content per settled await boundary, in completion order.

Rendering starts on the first pull. The initial pass writes the document with every boundary's fallback inside its placeholder and flushes, so a slow dependency does not delay the first bytes. Each later item is the replacement for one placeholder, and the ranging caller writes it, because only the caller may touch the response:

for content, err := range htmlbind.RenderChainAsync(ctx, w, wrappers, page) {
	if err != nil {
		log.Printf("render failed: %v", err)
		break
	}
	if err := writeCompletion(w, content); err != nil {
		break
	}
	htmlbind.Flush(w)
}

A yielded item is the bare fragment plus the id of the placeholder it belongs to. How that pair travels — an inert template and a marker element, a JSON record, anything else — is the caller's choice, because it has to match the client runtime the caller ships. Nothing on this path writes script, and the merged head carries component contributions only.

There is no variant that hides this loop. How many boundaries a render produces is not knowable up front, least of all for a chain assembled at request time, so a handler that streams has to be written against the sequence anyway.

Once the initial pass commits, the response status can no longer change, so a later error is for logging rather than for rewriting the response.

One of those errors is UnrecoveredError: a boundary's bindings failed in a clause with no recover subtree. Ending the sequence there is the point, since the alternative is a page left showing a fallback that will never be replaced. The caller still owns the response, and what it writes next — an error screen replacing the document, whatever its runtime applies — is its own to frame.

The sequence is single-use and single-consumer. Stopping the range early ends the render without waiting for the outstanding boundaries.

Types

type AsyncError added in v0.1.16

type AsyncError struct {
	// Code is a stable classification, either an application code or one of the
	// ErrorCode constants above.
	Code string
	// Message is optional presentation-safe text. It is empty unless the failing
	// error supplied one.
	Message string
	// Retryable reports whether the UI may offer a retry.
	Retryable bool
	// Timeout reports whether the configured async deadline expired.
	Timeout bool
}

AsyncError is the presentation-safe failure value an await boundary's recover clause receives. It carries only fields a template may render; the original Go error stays server-side and reaches the caller through WithErrorReporter.

type Builder added in v0.1.15

type Builder[P any] struct{}

Builder constructs instructions for one component. Generated code declares one per component so the parameter type is written once instead of on every instruction.

func (Builder[P]) Attr added in v0.1.15

func (Builder[P]) Attr(name string, value func(P) (string, bool)) Op[P]

Attr writes one attribute. The value arrives already escaped, because a mixed value concatenates author literals with escaped expressions and only the expressions may be escaped. present reports whether an optional value exists; an absent value omits the whole attribute.

func (Builder[P]) BoolAttr added in v0.1.15

func (Builder[P]) BoolAttr(name string, value func(P) bool) Op[P]

BoolAttr writes a bare attribute name when the value is true and omits it otherwise.

func (Builder[P]) Component added in v0.1.15

func (Builder[P]) Component(bind func(P) Fragment) Op[P]

Component renders another component. bind pairs the callee's plan with arguments derived from the caller's parameters.

func (Builder[P]) If added in v0.1.15

func (Builder[P]) If(condition func(P) bool, then, otherwise []Op[P]) Op[P]

If selects one of two instruction lists.

func (Builder[P]) MergedHead added in v0.1.15

func (Builder[P]) MergedHead() Op[P]

MergedHead writes every chain member's head contributions. The document shell places it inside its own head element.

func (Builder[P]) Raw added in v0.1.15

func (Builder[P]) Raw(value func(P) string) Op[P]

Raw writes a value that the template already marked trusted for its context.

func (Builder[P]) Slot added in v0.1.15

func (Builder[P]) Slot(value func(P) Fragment, fallback []Op[P]) Op[P]

Slot inserts a bound slot argument. When the argument is absent the fallback instructions run, which is how default slot content is expressed. An absent slot with no fallback emits nothing at all.

func (Builder[P]) Static added in v0.1.15

func (Builder[P]) Static(text string) Op[P]

Static writes literal markup. Adjacent literal output is coalesced at generation time, so one instruction covers a whole run.

func (Builder[P]) Text added in v0.1.15

func (Builder[P]) Text(value func(P) string) Op[P]

Text writes a value into child-node or attribute position with HTML escaping.

type CachePolicy added in v0.1.16

type CachePolicy[P any] struct {
	// ID is the component identity plus a fingerprint of its generated plan, so
	// regenerated code cannot read entries written by the previous code.
	ID string
	// TTL is how long an entry may be reused.
	TTL time.Duration
	// Key appends the canonical encoding of every declared parameter.
	Key func(P) string
}

CachePolicy is the cache configuration compiled into a component's plan. Generated code builds one; application code never does.

type CacheStore added in v0.1.16

type CacheStore interface {
	// Get returns previously stored output for key. An expired or absent entry
	// reports false. The returned bytes are written unmodified, so a store must
	// not reuse or mutate the slice it hands back.
	Get(ctx context.Context, key string) ([]byte, bool)
	// Set stores output for at most ttl. It returns nothing: a cache write
	// failure must not fail a response that already rendered correctly, so an
	// implementation reports its own failures.
	Set(ctx context.Context, key string, value []byte, ttl time.Duration)
}

CacheStore holds rendered component output for components declared with the cache annotation. The caller supplies one per render through WithCache, so a store is an ordinary caller resource rather than package state.

An implementation is used from several goroutines during one render and must be safe for concurrent use.

type Content added in v0.1.16

type Content struct {
	// BoundaryID matches the placeholder written during the initial pass.
	BoundaryID string
	// HTML is the already escaped and context-checked replacement fragment.
	// Consumers must not escape it again.
	HTML []byte
}

Content is one settled await boundary: the placeholder it replaces and the HTML that replaces it. The runtime yields these after the initial document write, and the ranging caller is the only code that writes them.

func (Content) WriteTo added in v0.1.16

func (c Content) WriteTo(w io.Writer) (int64, error)

WriteTo writes the settled fragment and nothing else: no wrapper element, no marker, no script.

htmlbind does not pick a wire format for completions. The framing that carries a fragment and the client code that acts on it are one design, and it belongs to whoever ships the runtime — a framework built on htmlbind, or the handler itself. BoundaryID is what ties this fragment back to its placeholder, so the caller has both halves and writes the framing around this call.

type Fragment added in v0.1.15

type Fragment struct {
	// contains filtered or unexported fields
}

Fragment is a component with its parameters already bound. It is the runtime value behind the html template type, so a slot argument can be passed between components and across template files without either side naming the other's parameter struct.

The zero Fragment is absent, which is how an optional slot with no argument is represented.

func Bind added in v0.1.15

func Bind[P any](plan *Plan[P], params P) Fragment

Bind pairs a plan with parameters, producing the value a slot accepts.

func (Fragment) HasAwaitBlock added in v0.1.18

func (f Fragment) HasAwaitBlock() bool

HasAwaitBlock reports whether rendering this fragment can open an await boundary, so a caller knows whether a response will need the client runtime that applies settled boundaries. Reading it renders nothing.

A fragment passed in through a parameter is not counted, because the binder cannot look inside a caller's parameter struct without reflection. That fragment is in the caller's own hand, so a caller composing slots unions the flag across the values it holds. HasAwaitBlock over a chain does that for the ordinary document, layout, and page shape.

func (Fragment) Head added in v0.1.15

func (f Fragment) Head() []string

Head returns the fragment's own head contributions, one entry per tag.

func (Fragment) HeadSources added in v0.2.4

func (f Fragment) HeadSources() []string

HeadSources names the component that declared each Head entry, in the same order and with the same length. Head and HeadSources are two views of one list, so index i of either describes the same contributed tag.

A caller that cannot deliver a head contribution uses it to report which component to change. The merged head returned by MergeHead has no matching source list, because deduplication drops entries: ask a member for its own.

func (Fragment) Present added in v0.1.15

func (f Fragment) Present() bool

Present reports whether the fragment carries content. An absent optional slot renders its default instead.

func (Fragment) Validate added in v0.1.19

func (f Fragment) Validate() error

Validate runs the fragment's parameter check without rendering. Chain assembly calls it so a chain built from unrenderable parameters fails before any member writes.

type MemoryCache added in v0.1.16

type MemoryCache struct {
	// contains filtered or unexported fields
}

MemoryCache is an in-process CacheStore with TTL expiry and a maximum entry count. It is the default a single-process server needs; a shared store is an adapter the caller writes.

func NewMemoryCache added in v0.1.16

func NewMemoryCache(maxEntries int) *MemoryCache

NewMemoryCache returns a store holding at most maxEntries entries. A non-positive maxEntries means unbounded.

func (*MemoryCache) Get added in v0.1.16

func (c *MemoryCache) Get(_ context.Context, key string) ([]byte, bool)

Get implements CacheStore.

func (*MemoryCache) Len added in v0.1.16

func (c *MemoryCache) Len() int

Len reports how many entries the cache currently holds, including entries that have expired but not yet been evicted.

func (*MemoryCache) Set added in v0.1.16

func (c *MemoryCache) Set(_ context.Context, key string, value []byte, ttl time.Duration)

Set implements CacheStore. A non-positive ttl stores nothing.

type Op added in v0.1.15

type Op[P any] interface {
	// Exec writes this instruction's output. Implementations are immutable and
	// safe to share across goroutines.
	Exec(r *Renderer, params P) error
}

Op is one instruction of a render plan. P is the parameter struct of the component the instruction belongs to, so every step stays statically typed and no reflection is needed.

func Await added in v0.1.16

func Await[P, S, R any](
	resolve func(context.Context, P) (S, error),
	recovery func(P, AsyncError) R,
	primary []Op[S],
	fallback []Op[P],
	handler []Op[R],
) Op[P]

Await opens an await boundary. resolve runs the clause's bindings and builds the primary subtree's scope; recovery builds the recover subtree's scope from the outer parameters and the safe error. handler is nil when the clause declared no recover subtree, and then a failure becomes an UnrecoveredError for the caller instead of anything on the page.

It is a free function rather than a Builder method because the primary and recover subtrees each read their own generated scope type.

func For added in v0.1.15

func For[P, E, S any](items func(P) []E, scope func(P, E, int) S, body []Op[S]) Op[P]

For repeats body once per item. scope builds the body's parameter value from the enclosing parameters, the item, and its index, so the loop variable stays statically typed instead of becoming an untyped lookup.

func Require added in v0.1.19

func Require[P any](check func(P) error) Op[P]

Require fails the render when check rejects the parameters. Generation emits it ahead of an await boundary that binds a required async parameter, so a caller who left one unset gets an error before the boundary commits its fallback and fixes the response status.

It writes nothing, which is the point: the check has to run on the initial pass, where a failure can still become an error response, rather than in the boundary goroutine that runs after the response is already committed.

type Option added in v0.1.16

type Option func(*renderOptions)

Option configures one render. Options are variadic so a call that needs nothing beyond a writer and a component stays two arguments long.

func WithAsyncTimeout added in v0.1.16

func WithAsyncTimeout(timeout time.Duration) Option

WithAsyncTimeout bounds how long one await boundary's bindings may run. An expired boundary fails with ErrorCodeTimeout. Zero means no deadline beyond the request context.

func WithCache added in v0.1.16

func WithCache(store CacheStore) Option

WithCache supplies the store used by components declared with the cache annotation. Without it those components render normally, so caching is a deployment choice rather than a template rewrite.

The store belongs to the caller: passing it per render keeps two servers in one process from sharing entries through package state.

func WithConcurrencyLimit added in v0.1.16

func WithConcurrencyLimit(limit int) Option

WithConcurrencyLimit bounds how many await boundaries may have work running at once across the whole render. Zero or less means unbounded.

func WithContext added in v0.1.16

func WithContext(ctx context.Context) Option

WithContext supplies the context for the synchronous entries, which take no ctx parameter of their own. A cache store and a blocking await boundary both use it, so a cancelled request stops doing work. The async entries take their context directly and ignore this option.

func WithErrorReporter added in v0.1.16

func WithErrorReporter(report func(error)) Option

WithErrorReporter receives the original Go error behind every await boundary failure, including failures a recover clause handled and therefore never surfaced to the caller. Recover subtrees see only the safe AsyncError, so this is where logging and metrics attach.

It is called from each boundary's own goroutine, so a reporter that accumulates rather than logs has to guard its own state.

type Pending added in v0.1.19

type Pending[T any] struct {
	// contains filtered or unexported fields
}

Pending is one value the caller started before rendering and a template waits for in an await clause. It is the Go form of the template's `async T`.

It settles once and stays readable, which is the whole reason it is a handle and not a channel: a layout and the page inside it may hold the same value, and a channel would deliver it to whichever of them received first while the other blocked until the boundary deadline. Every await of one handle sees the same settled result, and the work behind it runs once.

The zero value is unset. Awaiting an unset handle is legal exactly where the template declared the awaited type optional, and yields an absent value rather than a failure; generation rejects an unset handle anywhere else before the response commits. Nothing about an unset handle panics or blocks.

This is a render parameter type, not a general future. It has no combinators and no caller-facing wait: code that wants to compose work does so with ordinary goroutines before handing the result over.

func Failed added in v0.1.19

func Failed[T any](err error) Pending[T]

Failed returns a handle that has already settled to err. The boundary that awaits it renders its recover subtree.

func Go added in v0.1.19

func Go[T any](ctx context.Context, work func(context.Context) (T, error)) Pending[T]

Go starts work in its own goroutine and returns the handle to pass as a template parameter. The work receives the caller's context, so bounding or cancelling it stays the caller's business; a render only bounds how long it waits.

A panic in the work becomes the handle's error, the way an async external's panic does, so a boundary reports it through its recover clause instead of taking the process down.

func Resolved added in v0.1.19

func Resolved[T any](value T) Pending[T]

Resolved returns a handle that has already settled to value. It is what a caller passes when it computed the value itself, and what a test passes instead of starting a goroutine.

func (Pending[T]) IsSet added in v0.1.19

func (p Pending[T]) IsSet() bool

IsSet reports whether this handle carries work. Generated code calls it before the response commits, so a caller that forgot to supply a required value gets an error response rather than a boundary that waits for nothing.

func (Pending[T]) Wait added in v0.1.19

func (p Pending[T]) Wait(ctx context.Context) (T, error)

Wait returns the settled value. ctx bounds the wait, never the work.

An unset handle returns the zero value and no error, because absence is data rather than failure wherever the template allowed it. It is deliberately not a panic and deliberately not an infinite wait: the zero value of a struct field is exactly what a caller who supplied nothing left behind.

type Plan added in v0.1.15

type Plan[P any] struct {
	// Head holds this component's document head contributions as ready to
	// write HTML, one entry per contributed tag. They merge into the shell head
	// before any body byte.
	Head []string
	// HeadSources names the component that declared each Head entry, in the same
	// order and with the same length. It exists so a caller that has to reject a
	// contribution can say which component to change, instead of printing head
	// markup a reader then has to grep for.
	//
	// It is generated data, so reading it costs nothing and needs no reflection.
	// It is nil for a component with no contribution, which is most of them.
	HeadSources []string
	// HasAwaitBlock reports whether this component, or any component it calls,
	// owns an await boundary. Generation computes it over the call graph, so a
	// component that only calls an async one still reports true.
	//
	// It exists so a caller can decide before rendering whether a response needs
	// the client runtime that applies settled boundaries, instead of that
	// decision being made for it inside the render entry points.
	HasAwaitBlock bool
	// Check rejects parameters this component cannot render, before it writes
	// anything. Generation emits it for a required async parameter, whose
	// absence has to be reported while the response can still carry an error
	// status rather than a half written document.
	//
	// It is nil for a component with nothing to check, which is most of them.
	Check func(P) error
	// Ops is the instruction list executed in order.
	Ops []Op[P]
	// Cache is set for a component declared with the cache annotation. It is
	// consulted only when the caller supplied a store through WithCache, so the
	// same generated code runs cached or uncached.
	Cache *CachePolicy[P]
}

Plan is a component compiled to instructions. A plan is built once at package initialization and shared by every render.

func (*Plan[P]) Exec added in v0.1.15

func (p *Plan[P]) Exec(r *Renderer, params P) error

Exec runs the plan against params.

type PublicError added in v0.1.16

type PublicError interface {
	error
	PublicError() AsyncError
}

PublicError is implemented by an error that supplies its own safe projection. Any other error is exposed to a recover clause as ErrorCodeInternal with no message, so error text cannot leak into a page by accident.

type Renderer added in v0.1.15

type Renderer struct {
	// contains filtered or unexported fields
}

Renderer is the coordinator walking plans. It owns the output stream and the merged head, so instructions never touch either directly.

func (*Renderer) MergedHead added in v0.1.15

func (r *Renderer) MergedHead() []string

MergedHead returns the head contributions collected for this render.

func (*Renderer) Write added in v0.1.15

func (r *Renderer) Write(value string) error

Write emits raw bytes. Instructions call it after applying their own context-appropriate escaping.

type ScriptJSON added in v0.1.16

type ScriptJSON string

ScriptJSON is a JSON document destined for a script element. It is produced by the JSON encoders below, which escape the characters that would otherwise terminate the element.

type TrustedCSS added in v0.1.16

type TrustedCSS string

TrustedCSS is stylesheet text the template author vouched for.

type TrustedHTML added in v0.1.16

type TrustedHTML string

TrustedHTML is markup the template author vouched for. It is written without escaping, so it must never carry unvalidated input.

type TrustedJavaScript added in v0.1.16

type TrustedJavaScript string

TrustedJavaScript is script text the template author vouched for.

type UnrecoveredError added in v0.1.21

type UnrecoveredError struct {
	// BoundaryID is the placeholder whose fallback is committed to the response.
	// It is empty on the synchronous path, which writes no placeholder.
	BoundaryID string
	// Err is the failure the bindings reported.
	Err error
}

UnrecoveredError reports an await boundary whose bindings failed in a clause that declared no recover subtree. The template said nothing about what to show, so the failure leaves the boundary instead of stopping there: the synchronous entries return it, and the streaming sequence yields it and ends.

It carries the original Go error rather than the safe AsyncError projection, because it reaches the caller's Go code and never a template. What a caller puts on the page in response is its own to write, and must not be this text.

func (*UnrecoveredError) Error added in v0.1.21

func (e *UnrecoveredError) Error() string

func (*UnrecoveredError) Unwrap added in v0.1.21

func (e *UnrecoveredError) Unwrap() error

type UnsetPendingError added in v0.1.19

type UnsetPendingError struct {
	// Path names the parameter or field as the template declared it.
	Path string
}

UnsetPendingError reports a required async parameter or record field the caller left unset. It is raised during the initial pass, before any byte commits, so a handler can still turn it into an error response.

func (*UnsetPendingError) Error added in v0.1.19

func (e *UnsetPendingError) Error() string

type Wrapper added in v0.1.15

type Wrapper struct {
	// contains filtered or unexported fields
}

Wrapper is a component that renders another one into its unnamed slot. Generated code returns one from Bind<Name> for a component with a children parameter.

func BindWrapper added in v0.1.15

func BindWrapper[P any](plan *Plan[P], params P, setChildren func(*P, Fragment)) Wrapper

BindWrapper pairs a plan with parameters and the setter that installs the child fragment. Generated code supplies the setter because only it knows which field the unnamed slot binds to.

func (Wrapper) HasAwaitBlock added in v0.1.18

func (w Wrapper) HasAwaitBlock() bool

HasAwaitBlock reports whether rendering this wrapper can open an await boundary. The child it wraps is counted separately, because a wrapper is bound before it is told what it wraps.

func (Wrapper) Head added in v0.1.18

func (w Wrapper) Head() []string

Head returns the wrapper's own head contributions, one entry per tag.

func (Wrapper) HeadSources added in v0.2.4

func (w Wrapper) HeadSources() []string

HeadSources names the component that declared each Head entry, in the same order and with the same length. It is the Wrapper form of the accessor documented on Fragment.

func (Wrapper) Validate added in v0.1.19

func (w Wrapper) Validate() error

Validate runs the wrapper's parameter check without rendering.

Jump to

Keyboard shortcuts

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