Documentation
¶
Overview ¶
Package pwruntime contains the narrow runtime contract used by generated Popcorn Web code. Handwritten applications should normally import pw.
Index ¶
- Constants
- Variables
- func AcceptsHTML(accept string) bool
- func AdmitLive(key string, maximum int) (func(), bool)
- func AppendProblemJSON(dst []byte, problem Problem) []byte
- func ApplyProblemHeaders(header http.Header, problem Problem) error
- func CSRFHTMLRequest(secFetchDest, accept string) bool
- func CSRFSafeMethod(method string) bool
- func CSRFSecret(ctx context.Context) (string, bool)
- func CSRFToken(secret string, random io.Reader) (string, error)
- func ClientAddress(ctx context.Context, r *http.Request) string
- func Compose[Handler any](handler Handler, frames []Frame[Handler]) Handler
- func Config[T any](ctx context.Context) (T, bool)
- func ConnectionExecutor(ctx context.Context) (sqlbind.SQLExecutor, bool)
- func DB(ctx context.Context) (*sql.DB, bool)
- func DBDriver(ctx context.Context) (string, bool)
- func DatabasesReady(parent context.Context, resources Resources) bool
- func DeclaredLocales() []string
- func DigestKeyMaterial(configured string) int
- func ErrorType(err error) string
- func ExpectedCSRFToken(secret, presented string) string
- func FirstValue(values []any) any
- func FormatInt(locale Locale, value int) string
- func LangSegment(ctx context.Context) string
- func LangTag(ctx context.Context) string
- func LiveDigest(key, html []byte) string
- func LiveDigestKey(configured string) []byte
- func LiveLifetime(maximum time.Duration, jitterPercent int) time.Duration
- func LocaleAlternateLinks(r *http.Request, origin string) string
- func LocaleMiddleware(next http.Handler) http.Handler
- func LocalePath(locale Locale, mode LocaleMode, path string) string
- func LogUpdateRefusal(ctx context.Context, failure UpdateFailure)
- func Memo[K CacheKey, T any](ctx context.Context, store *CacheStore, key K, ...) (T, error)
- func MemoHas[K CacheKey](ctx context.Context, store *CacheStore, key K) bool
- func MemoInvalidate[K CacheKey](ctx context.Context, store *CacheStore, key K)
- func MemoInvalidateScope(store *CacheStore, scope string)
- func MemoInvalidateTag(store *CacheStore, tag string)
- func MemoSet[K CacheKey, T any](ctx context.Context, store *CacheStore, key K, value T) error
- func NewCSRFSecret(random io.Reader) (string, error)
- func NewRedirect(location string, status int) error
- func NormalizeHTTPMethod(method string) string
- func NormalizeScheme(scheme string) string
- func ParseLiveManifest(value string, key []byte, maximum int) map[string]string
- func ProblemHeaders(problem Problem) (http.Header, error)
- func PublishBotSettings(settings BotSettings)
- func PublishChainSettings(settings ChainSettings)
- func PublishConfigLookup(lookup func(reflect.Type) (any, bool)) func(reflect.Type) (any, bool)
- func PublishUpdateSettings(settings UpdateSettings)
- func ReadClientAddress(ctx context.Context) string
- func RegisterCacheMetrics(meter *metric.Meter)
- func RegisterDatabaseMetrics(meter *metric.Meter, pools func() []NamedPool)
- func RegisterHTMLDocument(wrapper htmlbind.Wrapper)
- func RegisterHTMLErrorPage(resolve HTMLErrorPage)
- func RegisterLocaleRouting(routes []LocaleRoute, labels map[string]string, prefixDefault bool)
- func RegisterLocales(tags []string, defaultTag string)
- func RegisterPageActions(pattern string, actions ...PageAction)
- func RegisterReloadable(components ...UpdateReloadable) error
- func RegisteredConfig[T any]() (T, bool)
- func RegisteredHTMLDocument() []htmlbind.Wrapper
- func RegisteredHTMLDocumentWith(wrappers []htmlbind.Wrapper) []htmlbind.Wrapper
- func ReloadableRegistrationFailure() error
- func RenderMessage(row []Segment, args ...string) string
- func RenderRichMessage(row []RichSegment, args ...string) []htmlbind.MessageSegment
- func ReservedSignalName(name string) bool
- func ResetPageActions()
- func ResolveConfig[T any](ctx context.Context) T
- func RestoreReloadableForTest(registry *UpdateRegistry, count int, failure error)
- func SQLExecutor(ctx context.Context) (sqlbind.SQLExecutor, error)
- func SelectDB(ctx context.Context, group string) context.Context
- func SetLocale(w http.ResponseWriter, locale Locale)
- func SetSocketOriginPolicy(check func(origin, host string) bool)
- func SocketOriginCheck(handshake SocketHandshake, development bool) func(origin, host string) bool
- func StoreAuthentication(store ValueStore, authentication Authentication)
- func StoreCSRFSecret(store ValueStore, secret string)
- func StoreClientAddress(store ValueStore, address string)
- func StoreLogAttributes(store ValueStore, attributes ...Attribute)
- func StoreResources(store ValueStore, resources Resources)
- func SupportsExplain(driver string) bool
- func SupportsSavepoint(driver string) bool
- func SwapHTMLDocument(chain []htmlbind.Wrapper) []htmlbind.Wrapper
- func Transaction(ctx context.Context, fn func(context.Context) error) error
- func VerifyCSRFToken(secret, presented string) bool
- func WithAuthentication(ctx context.Context, authentication Authentication) context.Context
- func WithCSRFSecret(ctx context.Context, secret string) context.Context
- func WithClientAddress(ctx context.Context, address string) context.Context
- func WithLocale(ctx context.Context, locale Locale) context.Context
- func WithLocaleMode(ctx context.Context, mode LocaleMode) context.Context
- func WithLogAttributes(ctx context.Context, attributes ...Attribute) context.Context
- func WithLogBackend(ctx context.Context, backend *LogBackend) context.Context
- func WithResources(ctx context.Context, resources Resources) context.Context
- func WriteLiveClose(w io.Writer, scratch []byte, reason string, retryAfter time.Duration) ([]byte, error)
- func WriteLiveDelivery(w io.Writer, scratch []byte, content htmlbind.Content, digest string) ([]byte, error)
- func WriteLiveHead(w io.Writer, scratch []byte, build string, head []string) ([]byte, error)
- func WriteLiveRecord(w io.Writer, record []byte) ([]byte, error)
- func WriteLiveSignal(w io.Writer, scratch []byte, signal htmlbind.Signal) ([]byte, error)
- type Attribute
- func Bool(key string, value bool) Attribute
- func Duration(key string, value time.Duration) Attribute
- func Err(err error) Attribute
- func Float64(key string, value float64) Attribute
- func Int(key string, value int) Attribute
- func Int64(key string, value int64) Attribute
- func String(key, value string) Attribute
- type Authentication
- type BotSettings
- type CORSConfig
- type CORSDecision
- type CSRFConfig
- type CacheConfig
- type CacheKey
- type CacheStats
- type CacheStore
- func (s *CacheStore) Get[K CacheKey, T any](ctx context.Context, key K, fetch func(context.Context) (T, error)) (T, error)
- func (s *CacheStore) Has[K CacheKey](ctx context.Context, key K) bool
- func (s *CacheStore) Invalidate[K CacheKey](ctx context.Context, key K)
- func (s *CacheStore) InvalidateScope(scope string)
- func (s *CacheStore) InvalidateTag(tag string)
- func (s *CacheStore) Name() string
- func (s *CacheStore) Set[K CacheKey, T any](ctx context.Context, key K, value T) error
- func (s *CacheStore) Stats() CacheStats
- type CacheStoreConfig
- type CacheTagger
- type CaptureSink
- type ChainSettings
- type Connection
- type ConnectionSet
- func (set *ConnectionSet) Close() error
- func (set *ConnectionSet) Collapsed() bool
- func (set *ConnectionSet) Connections() []*Connection
- func (set *ConnectionSet) Count() int
- func (set *ConnectionSet) DefaultGroup() string
- func (set *ConnectionSet) Groups() []string
- func (set *ConnectionSet) Has(group string) bool
- type FieldError
- type Frame
- type HSTSConfig
- type HTMLErrorPage
- type HTMLFragment
- type Level
- type LiveDigester
- type LiveWatchdog
- type Locale
- func DefaultLocale() Locale
- func LocaleContext(ctx context.Context) Locale
- func MessageLocale(ctx context.Context) Locale
- func MustParseLocale(tag string) Locale
- func NewLocale(tag string, index int) Locale
- func ParseLocale(tag string) (Locale, bool)
- func StripLocalePrefix(path string) (string, Locale, bool)
- type LocaleChoice
- type LocaleMode
- type LocaleRoute
- type LogBackend
- type Logger
- func (logger Logger) Debug(message string, attributes ...Attribute)
- func (logger Logger) Enabled(level Level) bool
- func (logger Logger) Error(message string, attributes ...Attribute)
- func (logger Logger) Info(message string, attributes ...Attribute)
- func (logger Logger) Log(ctx context.Context, level Level, message string, attributes ...Attribute)
- func (logger Logger) SpanID() string
- func (logger Logger) Trace(message string, attributes ...Attribute)
- func (logger Logger) TraceID() string
- func (logger Logger) Warn(message string, attributes ...Attribute)
- func (logger Logger) With(attributes ...Attribute) Logger
- type MetricGroups
- type Metrics
- type NamedPool
- type NumberFormat
- type OtelSink
- type PageAction
- type Problem
- func BadRequest(values ...any) Problem
- func Conflict(values ...any) Problem
- func Forbidden(values ...any) Problem
- func InternalServerError(values ...any) Problem
- func MapProblem(err error) Problem
- func NewProblem(status int, title string, value any) Problem
- func NotFound(values ...any) Problem
- func PayloadTooLarge(values ...any) Problem
- func RateLimited(rate RateLimit, values ...any) Problem
- func SanitizeProblem(problem Problem) Problem
- func ServiceUnavailable(values ...any) Problem
- func TooManyRequests(values ...any) Problem
- func Unauthorized(values ...any) Problem
- func Validation(fields ...FieldError) Problem
- type PublicAssetSettings
- type QueryDiagnostics
- type RateLimit
- type RateLimitConfig
- type RateLimitRedisConfig
- type Record
- type RedirectError
- type ResolvedCORS
- type ResolvedSecurityHeaders
- type Resources
- type ResponseHeader
- type RichSegment
- type SecurityHeadersConfig
- type Segment
- type Signal
- type SignalPayload
- type Sink
- type SlogSink
- type Slot
- type SocketHandshake
- type Tracing
- type TransactionScope
- func (scope *TransactionScope) Active() bool
- func (scope *TransactionScope) ActiveExecutor() sqlbind.SQLExecutor
- func (scope *TransactionScope) Begin(ctx context.Context, options *sql.TxOptions) error
- func (scope *TransactionScope) Commit() error
- func (scope *TransactionScope) Group() string
- func (scope *TransactionScope) ReadOnly() bool
- func (scope *TransactionScope) Rollback() error
- func (scope *TransactionScope) Tx() *sql.Tx
- type UpdateFailure
- type UpdateRegion
- type UpdateRegistry
- type UpdateReloadable
- type UpdateSettings
- type ValueStore
Constants ¶
const ( CORSDeclinedOrigin = "origin" CORSDeclinedMethod = "method" CORSDeclinedHeader = "header" )
The reasons a request was not marked. They name which half did not match, so the record says what to fix; the response never says, because the browser was going to answer the question anyway and a precise refusal only helps a caller enumerate the policy.
const ( LiveCloseDone = "done" LiveCloseRetry = "retry" )
The close reasons. A clean transport close cannot say whether the sources finished or a bound ended a healthy response, and the two deserve opposite client behaviour.
const ( ResponseModeHeader = "Pw-Response-Mode" LiveResponseMode = "live" )
ResponseModeHeader is how a client asks for something other than a document on a route's own URL, and LiveResponseMode is the one value that does.
They are here rather than on either runtime because they are the wire between the browser runtime this framework ships and whichever half is serving. Two transports reading two different headers is a client that works against one build of an application and not the other, which is what happened.
const ( FieldTimestamp = "timestamp" FieldSeverity = "severity" FieldMessage = "message" FieldServiceName = "service_name" FieldTraceID = "trace_id" FieldSpanID = "span_id" FieldTraceFlags = "trace_flags" )
Reserved record fields. A user attribute may not take one of these names, because a consumer reading a stream has to be able to trust them.
const ( CacheResultHit = "hit" CacheResultMiss = "miss" CacheResultStaleHit = "stale_hit" CacheResultCoalesced = "coalesced" )
Cache result attribute values, a closed set so that a hit rate has a denominator and a reader can enumerate the outcomes.
const ( // RateLimitBackendMemory counts inside this process. It is correct on one replica // and enforces N times the configured limit on N of them. RateLimitBackendMemory = "memory" // RateLimitBackendRedis counts in a shared server, which is what a deployment // running more than one replica needs. RateLimitBackendRedis = "redis" )
Rate limit backend names a deployment selects with ratelimit.backend.
const ( // UpdateHeaderPrefix yields Pw-Render, Pw-Manifest, and Pw-Build. UpdateHeaderPrefix = "Pw" // UpdateAttributePrefix names the boundary attributes generation writes and // the placeholder element the render option spells, so one document holds // one spelling rather than two. // // It is the module's default rather than this framework's brand because // routetree compiles a page tree's templates without the prefix option, so // branding it here would split a document's naming in exactly the way the // option exists to prevent. internal/pwgen names the same value. UpdateAttributePrefix = "tb" // UpdateGlobalName is the browser namespace the client update API installs. UpdateGlobalName = "popcornweb" // UpdatePathPrefix is where the framework's own endpoints answer. UpdatePathPrefix = "/_pw" )
These are fixed rather than configured. They are contracts between this framework and the runtime it ships — the header namespace, the attribute prefix, the installed name, and the endpoint prefix all reach the browser as one configuration object, and a deployment changing one would be describing a framework it is not running.
They are here rather than on either runtime because the document a browser receives is the same document whichever transport wrote it, and the settings reduction that describes it is published by whichever half parsed the configuration.
const CSRFCookieName = "pw_csrf"
CSRFCookieName is the companion cookie the browser runtime reads a token from.
It is a constant rather than configuration because it is a contract between the framework and the shipped script, not a deployment choice. The runtime is a module script, so it cannot read a name off its own tag the way a classic script can; a renamed cookie would leave it looking for one that is not there, and nothing would say so.
const CSRFHeaderName = "X-CSRF-Token"
CSRFHeaderName is where the runtime puts the token. It is outside the framework's own header namespace because it is a name middleware already looks for.
const CSRFSecretBytes = 32
CSRFSecretBytes is the raw entropy of a session's CSRF secret. It matches the session token, because both are the same kind of unguessable value.
const DefaultConnectionGroup = "default"
DefaultConnectionGroup is the group name of a configuration that declares a single database without naming any group.
const DefaultContentSecurityPolicy = "script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'"
DefaultContentSecurityPolicy is the policy a project gets without naming one.
It restricts the four directives a web application can almost always accept, and leaves alone the ones it cannot: images, fonts, styles, and connections are unrestricted, so an ordinary page keeps working without anyone editing configuration.
- script-src 'self' is the load-bearing one. It refuses inline event handlers, inline <script>, and javascript: URLs, which together are how an HTML-injection sink becomes running code. It also matters more here than in a framework without a browser runtime: the CSRF companion cookie is readable by script on purpose, so script that runs on this origin can mint a valid token. The framework's own runtime is a same-origin module tag and needs nothing else.
- object-src 'none' closes <object> and <embed>, which route around script-src on some engines.
- base-uri 'self' stops an injected <base href> from re-pointing every relative URL on the page, including the runtime's own.
- frame-ancestors 'none' says what X-Frame-Options: DENY already says, in the header that is not deprecated.
A project that loads third-party script names its own policy. That is the conversation this default is for: a CSP that shipped empty was one nobody had.
const DefaultLiveManifestEntries = 64
DefaultLiveManifestEntries bounds an unbounded configuration's manifest parse. It is not a limit on the response: past it the extra claims are dropped and their boundaries are delivered rather than suppressed.
const DefaultRateLimitKeyPrefix = "pw:ratelimit:"
DefaultRateLimitKeyPrefix namespaces the keys this limiter owns.
const LiveMediaType = "application/x-ndjson"
LiveMediaType frames the record stream.
const LiveRetryHint = 2 * time.Second
LiveRetryHint is what a retry close suggests waiting.
const LocaleCookieMaxAge = 365 * 24 * 60 * 60
LocaleCookieMaxAge is how long a recorded choice lives. A year, because the choice is a preference rather than a session fact and re-asking a returning reader every month is the behaviour the cookie exists to remove.
const LocaleCookieName = "pw_lang"
LocaleCookieName is the cookie a reader's explicit choice is stored in.
It is separate from the preference cookie on purpose: requirement:user-preference-rendering forbids that cookie deciding text content, and language is content.
const MetricScope = "github.com/shibukawa/popcornweb"
MetricScope names the instrumentation scope of framework instruments, matching the scope its spans already carry.
const ReservedSignalPrefix = "pw."
ReservedSignalPrefix is this framework's signal namespace.
Every layer that produces signals reserves a prefix: the module holds tb., this framework holds pw., and an application uses what is left. The module cannot hold this one, because a signal constructor is called at a yield site inside a source and is not render-scoped, so it can reach no configured value.
What it protects is trust. The lifecycle names this framework's client runtime dispatches — a boundary settled, a live response opened, a delivery applied — arrive under this prefix, and a handler believes them precisely because application data has no route into the namespace. A source able to emit pw.delivery_applied could make a screen believe a render landed that never did.
It lives here rather than beside either live loop because both of them enforce it, and a prefix one backend reserved and the other did not would be a namespace an application could reach through the second one.
Variables ¶
var ErrCrossGroupTransaction = errors.New("popcornweb: transaction cannot span two connection groups")
ErrCrossGroupTransaction reports a nested transaction naming a different connection group than the one already open.
var ErrSavepointUnsupported = errors.New("popcornweb: driver does not support savepoints")
ErrSavepointUnsupported reports that the configured driver cannot nest a transaction because it has no known savepoint support.
var ErrSignal = htmlbind.ErrSignal
ErrSignal matches any signal under errors.Is, for code that wants the classification without the value.
var ErrTransactionFailed = errors.New("popcornweb: transaction is no longer usable")
ErrTransactionFailed reports that a savepoint operation left the transaction in an unknown state, so no further work may be committed on it.
var ErrUnknownConnectionGroup = errors.New("popcornweb: unknown database connection group")
ErrUnknownConnectionGroup reports a group name that no configured connection carries. A group name is data, so the failure surfaces at the statement that depended on it rather than where the name was written.
var OpenAPIDocumentOrigin = ResponseHeader{Name: "Access-Control-Allow-Origin", Value: corsWildcard}
OpenAPIDocumentOrigin is the marking the generated OpenAPI document carries, whatever security.cors says and whether or not it is enabled at all.
This one endpoint is answered by what it is rather than by a policy a deployment writes. The document describes a contract already chosen for publication, holds nothing that varies per visitor, and is read by tools whose origins nobody can enumerate in advance: a documentation UI hosted somewhere else, a client generator, a linter in someone's CI.
It is safe on a protected document too. A wildcard forbids credentials, so a cross-origin page reading one that sits behind the authenticated-path check receives the unauthenticated answer and learns nothing it could not have learned by asking for the path itself.
No Vary goes with it: the answer is the same for every caller, which is what keeps the document shared-cacheable.
var UpdateBuildID = sync.OnceValue(func() string { if info, ok := debug.ReadBuildInfo(); ok { for _, setting := range info.Settings { if setting.Key == "vcs.revision" { return setting.Value } } } return "" })
UpdateBuildID identifies the binary that rendered a page.
It answers the same question the live delivery stream's version does: was the page asking rendered by this build? A page from another one holds client state this binary cannot vouch for — a template it does not have, a runtime that renders differently — and none of that is visible in a validator.
The two differ on an unstamped binary, and the difference is deliberate. Live delivery reports nothing there, which disables its check rather than inventing a value that would differ per process and reload every client on every restart. An update falls back to the module's per-process identity instead, which costs a complete document after a restart and never a wrong delta. A frozen screen is worse than a re-transferred page.
Functions ¶
func AcceptsHTML ¶
AcceptsHTML reports whether a client would rather have a page than a document, given its Accept header.
An absent, empty, or unreadable header is not a preference, so it takes the API representation — which is also what a client that sent no opinion at all is most likely to be.
It is here rather than in either runtime because it reads a header and decides a representation, and two builds of one application answering the same request differently is exactly what a shared rule prevents.
func AdmitLive ¶
AdmitLive takes one slot for a client and returns the release. A maximum of zero or less admits everything.
The key is the caller's to choose, and it is the authenticated subject where there is one and the remote address otherwise: an anonymous screen is still one browser, and grouping every anonymous client together would refuse the second visitor of the day.
func AppendProblemJSON ¶
AppendProblemJSON appends the RFC problem document to dst.
It is here because it was written by hand in each runtime, and the two copies were one response body described twice — which is the shape of thing that stays identical until the day it does not, with nothing to say which client saw which. It is built by hand rather than marshalled because the document is flat and known, and this path must not fail: it is what answers when everything else already has.
The caller sanitizes and sets the headers; this writes the body alone.
func ApplyProblemHeaders ¶
ApplyProblemHeaders writes response metadata before a problem response is committed. Invalid optional metadata is returned to the caller and omitted.
It writes straight onto the caller's header rather than through the detached map ProblemHeaders builds: this runs on every error response, and the common problem carries zero or one header.
func CSRFHTMLRequest ¶ added in v0.5.2
CSRFHTMLRequest reports whether a safe request is expected to render HTML, given its Sec-Fetch-Dest and Accept header values. Browsers send either an HTML Accept value or a document navigation target; a generic */* request does not justify allocating session state merely in case the handler might render a form. Both transports call this one function so the answer cannot drift.
func CSRFSafeMethod ¶ added in v0.5.2
CSRFSafeMethod reports whether a method is one the cross-site check lets through — GET, HEAD, and OPTIONS, the set HTTP defines as not changing state. TRACE is deliberately absent: nothing in this framework routes it, and a state-changing handler bound to it would otherwise pass unchecked. Both transports call this one function so the set cannot drift.
func CSRFSecret ¶
CSRFSecret returns the request's CSRF secret, if the request carries a session that has one.
func CSRFToken ¶
CSRFToken derives the value a browser receives from the session's secret.
The pad is fresh for every call, and the emitted bytes therefore differ on every response. That is the whole point: a stable token reflected into a compressed body beside attacker-influenced input is what a compression oracle extracts one byte at a time, which is why Rails and Django both mask.
The pad travels in the clear because it has to: verification recomputes the expected value from it. Knowing the pad reveals nothing, since producing the rest of the token still requires the secret.
func ClientAddress ¶
ClientAddress returns the resolved caller address for the request.
It falls back to the request's peer for a chain that never resolved one, which is what a handler served outside the framework middleware stack sees.
func Compose ¶
Compose wraps handler in frames, outermost first by slot.
The sort is stable and equal slots keep their append order, so two frames registered at one number run in registration order rather than in whatever order the sort happened to produce. Both runtimes call this rather than sorting for themselves, because the ordering rule is the thing that has to be identical and a second implementation of it is a second chance to differ.
func ConnectionExecutor ¶
func ConnectionExecutor(ctx context.Context) (sqlbind.SQLExecutor, bool)
ConnectionExecutor returns the pool-level statement surface of the effective group, undecorated and outside any transaction. It exists for framework storage that holds one executor for the process lifetime, such as the rdb session backend; request statements resolve through SQLExecutor instead.
func DB ¶
DB returns the pool of the effective group, which is the group pinned by SelectDB, otherwise the group of an active transaction, otherwise the default group.
A connection whose engine bypasses database/sql has no *sql.DB, so DB reports false for it rather than fabricating a handle; ConnectionExecutor is the surface that exists on every connection.
func DBDriver ¶
DBDriver reports the driver scheme of the effective connection, which dialect-specific storage needs before it issues SQL.
func DatabasesReady ¶
DatabasesReady reports whether every configured connection answers.
It is here rather than in either runtime because readiness is a fact about the process rather than about the request that asked: the same probe on either transport must give the same answer, and a second implementation could disagree about the timeout or about which connections count.
The bound is one second. A readiness probe that hangs is worse than one that answers unavailable, because an orchestrator waiting on it cannot tell a slow database from a wedged process.
func DeclaredLocales ¶
func DeclaredLocales() []string
DeclaredLocales returns the declared tags in declaration order. It reports nil when no generated message package is linked, which is every project that has not adopted requirement:application-i18n.
func DigestKeyMaterial ¶ added in v0.5.2
DigestKeyMaterial measures the key material a configured validator key carries: a base64 value counts as its decoded length, anything else as its raw bytes. It is the one measure both the startup floor and LiveDigestKey use, so the two cannot drift on what counts as long enough.
func ErrorType ¶
ErrorType is the error.type attribute value for an error.
It reports the type name and never the message. A message carries values — a path, an identifier, a bind value policy:query-log-safety keeps off a span — and an attribute built from one is an unbounded series wearing a closed set's name.
func ExpectedCSRFToken ¶
ExpectedCSRFToken rebuilds the token a correct client would have sent, using the pad carried by the one that arrived.
The comparison stays an equality against a value this framework computed, which is what lets the masked form work with a verifier that only compares. A malformed token yields an empty string, so it cannot match and cannot panic.
func FirstValue ¶
FirstValue is the variadic-to-optional shim the constructors share.
func FormatInt ¶
FormatInt renders an integer argument for a message, grouped for the locale.
Plural selection reads the value and never this string, so a locale whose grouping is unknown still selects the right form.
func LangSegment ¶
LangSegment is the provider behind the path-segment binding: the tag under LocaleModePath and the empty string otherwise.
An empty value collapses the separator before it where it is written into a URL attribute, so one template serves every mode: "/{lang}/about" is /ja/about where the locale is in the path and /about where it is not.
func LangTag ¶
LangTag is the provider behind the ordinary string binding: the resolved tag, never empty, in every mode. It is what a document language attribute and a localized asset path are written with.
func LiveDigest ¶
LiveDigest is the validator of one delivery: what a screen holds, and what it returns on its next connection so the server can leave it alone.
Keyed, because it travels in a request header on every reconnect, and a request header is the most widely logged thing between a browser and a handler. An unkeyed digest there is a stable fingerprint of the region's content, and a live region with few possible renderings — a status badge, a queue depth, a seat count — is enumerable from a proxy log by anyone who can render the same page. Keying costs one HMAC per delivery and takes the fingerprint away.
Truncated to twelve bytes. The digest decides only whether to skip a transfer the client would have discarded, so the wrong answer costs one region one stale rendering, and ninety-six bits is far past where that becomes the system's most likely failure.
func LiveDigestKey ¶
LiveDigestKey keys the delivery validators, or reports nil where suppression has to be off. A configured validator key is used where there is one, and a per-process key otherwise, so suppression works in a single-instance deployment that configured nothing.
A configured key carrying fewer than 32 bytes of material is treated as if it were absent and the random per-process key is used instead. The digest keeps a proxy or log observer from enumerating a low-entropy live region by its digest, and a guessable key is no key at all — so a short one must not key it, even on the Live-without-Update path where the startup floor does not run.
func LiveLifetime ¶
LiveLifetime spreads a configured maximum around its value.
A fixed lifetime resynchronizes every client on every cycle, so one restart produces a herd that then repeats forever; a client cannot fix that with backoff, because it never chose the moment it was closed.
func LocaleAlternateLinks ¶
LocaleAlternateLinks renders the hreflang alternates of the current page.
It reads the same data the switcher does, so the two cannot drift. A mode with no per-locale URL produces nothing, because there is no URL to point at.
func LocaleMiddleware ¶
LocaleMiddleware resolves the request's locale before the handler runs and records the Vary the route's mode implies.
Resolution happens here rather than lazily at the first message because headers are final before the body under flow:initial-streaming-render, and a Vary decided mid-render is decided too late.
A project declaring no locale is passed through untouched.
func LocalePath ¶
func LocalePath(locale Locale, mode LocaleMode, path string) string
LocalePath builds the URL of a path in a locale.
It is the Go-side counterpart of the template binding: a redirect Location, a mail body, or a push deep link is composed here rather than in markup. The empty-prefix case is handled the same way, so a caller never branches on the mode.
func LogUpdateRefusal ¶
func LogUpdateRefusal(ctx context.Context, failure UpdateFailure)
LogUpdateRefusal records a refused update request.
Both runtimes install it as their failure hook, so one refusal reads the same whichever transport served it. Version skew is the ordinary case — a page loaded before a deploy asks for a component whose markup has changed, gets a 404, and reloads — so it is recorded rather than treated as a fault.
func Memo ¶
func Memo[K CacheKey, T any](ctx context.Context, store *CacheStore, key K, fetch func(context.Context) (T, error)) (T, error)
Memo returns what fetch produced for this key, reusing a stored result while it is fresh and coalescing concurrent misses onto one fetch.
A nil store, or a private store reached by an anonymous request, calls fetch and returns. No call site branches on whether caching is on.
func MemoHas ¶
func MemoHas[K CacheKey](ctx context.Context, store *CacheStore, key K) bool
MemoHas reports whether this key currently has a fresh entry. A stale one answers false, because the useful question here is whether the held value is current rather than whether a read would block.
It is racy by nature — the entry may expire between the answer and the next read — so it answers a diagnostic or a decision to skip expensive work, never control flow that assumes the following read hits.
func MemoInvalidate ¶
func MemoInvalidate[K CacheKey](ctx context.Context, store *CacheStore, key K)
MemoInvalidate drops one entry, taking the key the read took.
func MemoInvalidateScope ¶
func MemoInvalidateScope(store *CacheStore, scope string)
MemoInvalidateScope drops everything one reader holds, which the prepended scope makes a prefix rather than a scan.
func MemoInvalidateTag ¶
func MemoInvalidateTag(store *CacheStore, tag string)
MemoInvalidateTag drops everything a tag names.
It is the axis a prefix cannot serve: the scope comes first, so every entry of one key type across all readers is not a range. A tag is the reverse index that answers it.
func MemoSet ¶
MemoSet writes an entry without consulting one, which is how a writer refreshes what it just made wrong.
It bypasses the fetch and with it the coalescing, so not storing an error is the caller's to keep here. The lifetime comes from the store, so a call cannot mint a longer-lived entry than the configuration allows.
func NewCSRFSecret ¶
NewCSRFSecret returns a session's CSRF secret, encoded the way every other opaque value in this framework is.
func NewRedirect ¶
NewRedirect returns the value. The status comes from a named constructor rather than from a caller, so there is nothing here to validate: a redirect answering a status no browser follows would render the page it meant to leave, and no constructor can spell one.
func NormalizeHTTPMethod ¶ added in v0.5.2
NormalizeHTTPMethod maps a request method onto the OpenTelemetry HTTP semantic-convention verb set, collapsing anything outside it to "_OTHER".
A metric label must not carry an unbounded value. Both net/http and fasthttp accept any RFC 7230 token as a method, and the in-process aggregator never evicts a series, so a client sending a stream of distinct arbitrary method tokens would otherwise add a permanent time series per token until the process runs out of memory. The original method still belongs on a span as http.request.method_original; it never belongs on a metric label.
func NormalizeScheme ¶ added in v0.5.2
NormalizeScheme bounds a request scheme to the two an HTTP server metric can carry. An absolute-form target or a crafted URI could otherwise put an arbitrary scheme on the url.scheme label; anything that is not https is reported as http so the label stays two-valued.
func ParseLiveManifest ¶
ParseLiveManifest reads what the client claims each boundary is showing.
The bound is on entries rather than on bytes, and exceeding it drops the extra claims rather than refusing the request: the cost of a dropped claim is one region re-transferred, and refusing would cost the whole stream.
func ProblemHeaders ¶
ProblemHeaders returns a detached set of response metadata for either HTTP transport runtime.
func PublishBotSettings ¶
func PublishBotSettings(settings BotSettings)
PublishBotSettings records the resolved configuration for whichever runtime reads it.
func PublishChainSettings ¶
func PublishChainSettings(settings ChainSettings)
PublishChainSettings records the resolved configuration for whichever runtime builds a chain from it.
func PublishConfigLookup ¶
PublishConfigLookup records how to read the resolved configuration. Whichever runtime owns configuration parsing calls it once.
It returns whatever was published before, so a caller installing one temporarily can put that back rather than leaving nothing behind. Publishing nil is how a caller takes its own lookup away, and a process left with none answers every binding with its zero value.
func PublishUpdateSettings ¶
func PublishUpdateSettings(settings UpdateSettings)
PublishUpdateSettings records the resolved configuration for whichever runtime reads it. The transport that resolved the configuration and the transport that serves the request need not be the same one.
func ReadClientAddress ¶
ReadClientAddress returns the address the chain resolved, or the empty string where no frame resolved one.
It is the portable half of ClientAddress: the fallback that half takes needs a net/http request, and on the second transport the request value answers Value from the same store StoreClientAddress writes to, so there is nothing left to fall back to.
func RegisterCacheMetrics ¶
RegisterCacheMetrics registers the data result cache observables.
The counters already exist on every store, incremented on the lookup path, and this reads them at collection instead of recording a second time. It is one registration for every store rather than one per store, because the set is rebuilt whenever its configuration fingerprint changes and a per-store registration would leave an instrument behind pointing at a store nobody uses.
The result is an attribute of a closed set rather than four instruments, so a reader dividing hits by the total finds both terms under one name. No ratio is computed here: a quotient without its denominator cannot tell a cache that is working from one nothing is eligible for.
func RegisterDatabaseMetrics ¶
RegisterDatabaseMetrics registers the connection pool observables.
They matter because an exhausted pool makes every request slow with no statement being slow, which is the one failure the per-statement duration histogram cannot show.
pools is read at collection rather than captured, so a process that opens its databases after this registration still reports them.
func RegisterHTMLDocument ¶
RegisterHTMLDocument installs the document shell every rendered page is wrapped in. It is called once, from generated code, and a second call is a programming error rather than a replacement.
func RegisterHTMLErrorPage ¶
func RegisterHTMLErrorPage(resolve HTMLErrorPage)
RegisterHTMLErrorPage installs the application's error page resolver.
func RegisterLocaleRouting ¶
func RegisterLocaleRouting(routes []LocaleRoute, labels map[string]string, prefixDefault bool)
RegisterLocaleRouting records the per-prefix modes, the display labels, and whether the default locale carries a path prefix.
It is called by the generated message package beside RegisterLocales, because all of it is build configuration and none of it is knowable at run time.
func RegisterLocales ¶
RegisterLocales records the declared locale list. The generated message package calls it from an init function, because that package is the one holding the tables this list indexes.
tags is in declaration order and defaultTag must be a member. Registering a second, different set panics: the tables were generated against one list, and continuing with another would read them at subscripts they were never built for. Registering the identical set again is a no-op, so a test binary linking the generated package twice is not a failure.
func RegisterPageActions ¶
func RegisterPageActions(pattern string, actions ...PageAction)
RegisterPageActions publishes the actions reachable from one route.
The key is the route's own registered pattern, because that is what a request reports having matched, so nothing here re-matches a path the router already resolved.
The ordinary caller is a generated init beside the registry it reads, so a repeated pattern replaces rather than failing: regenerating is what produces the second call, and the two carry the same set.
func RegisterReloadable ¶
func RegisterReloadable(components ...UpdateReloadable) error
RegisterReloadable publishes generated components as redraw endpoints.
A repeated kind is an error rather than a silent overwrite: the kind covers a component's name, parameters, and markup but not its package, so two identical templates in different packages produce the same one and the wrong component could answer.
The failure is also kept, because the ordinary caller is a generated init beside the component it registers, and an init has nowhere to return to.
func RegisteredConfig ¶
RegisteredConfig returns the resolved value for T, and whether one was registered and parsed.
func RegisteredHTMLDocument ¶
RegisteredHTMLDocument returns the shell chain, or nil where none was registered. The slice is freshly allocated per call, so a caller appending its own wrappers cannot reach the chain another request is rendering.
func RegisteredHTMLDocumentWith ¶ added in v0.5.2
RegisteredHTMLDocumentWith returns the shell chain with wrappers appended after it, built in one allocation. It exists for the page render path: the plain accessor's copy carries no spare capacity, so appending a route's own wrappers to it re-allocated and re-copied on every rendered page. The result is freshly allocated for the same isolation reason as above.
func ReloadableRegistrationFailure ¶
func ReloadableRegistrationFailure() error
ReloadableRegistrationFailure reports a registration that failed before main ran, whatever the configuration says: a collision is a defect in what generation produced rather than a deployment choice.
func RenderMessage ¶
RenderMessage assembles one row into a string.
The result is unescaped: a message is an ordinary string value and the template escapes it once, together with its arguments, for the position it lands in. Escaping here would double-escape every one. See .knowledge decision:message-code-shape escaping_stays_where_it_is.
A row of exactly one literal segment is returned directly, so a message carrying no argument costs a table read and no allocation. Generated code for a message with no parameters skips this function entirely and indexes a plain string table, which is the same saving one call earlier.
func RenderRichMessage ¶
func RenderRichMessage(row []RichSegment, args ...string) []htmlbind.MessageSegment
RenderRichMessage assembles one row into the segment list system:tinybind interleaves with the markup a template bound to each hole.
Adjacent literal runs are merged so a hole's text arrives as one segment, which is what the interleaver writes between a bound element's tags.
This returns an upstream type on purpose. The generated function is the argument of htmlbind.Builder.Message, so the shape is fixed by that call rather than chosen here; policy:message-rich-text records why the framework produces the segments and upstream drives the interleaving.
func ReservedSignalName ¶
ReservedSignalName reports a name this framework refuses to put on the wire.
The module refuses its own prefix inside its constructors, where a bad name becomes a value that faults when the runtime reads it. This one is enforced where a signal is written instead, for two reasons: a constructor cannot carry a message of its own, since the fault field is the module's and unexported; and a constructor is not a chokepoint at all, because an application calling the module's constructor directly bypasses any wrapper this framework offers. A live loop is the only path a signal reaches a client through.
func ResetPageActions ¶
func ResetPageActions()
ResetPageActions clears the registry, for a test that publishes its own.
func ResolveConfig ¶
ResolveConfig answers the way both runtimes' Config accessors do: the value carried on the request if a middleware put one there, and otherwise the process-wide resolved value.
The per-request value comes first because that is what lets a test or a development mode serve one request with configuration that is not the process's. Falling back rather than requiring it is what lets a caller outside a request — startup validation, or a runtime with no middleware of its own — read the same settings.
func RestoreReloadableForTest ¶
func RestoreReloadableForTest(registry *UpdateRegistry, count int, failure error)
RestoreReloadableForTest puts back what ResetReloadableForTest returned.
func SQLExecutor ¶
func SQLExecutor(ctx context.Context) (sqlbind.SQLExecutor, error)
SQLExecutor is used by generated .pw.sql context wrappers. It is the one seam every generated statement passes through, so query diagnostics attach here instead of in generated code or in a wrapping driver.
func SelectDB ¶
SelectDB pins group onto ctx so generated SQL and Transaction use it instead of the default group.
An unknown name is not rejected here. The returned context fails at its first executor resolution, DB call, or Transaction with ErrUnknownConnectionGroup.
func SetLocale ¶
func SetLocale(w http.ResponseWriter, locale Locale)
SetLocale records a reader's explicit choice.
The value is validated against the declared set before it is written, because a cookie is client-writable under the plain mode of policy:cookie-value-protection and a decoded value is request input.
func SetSocketOriginPolicy ¶
SetSocketOriginPolicy records the check an application installed with the socket defaults. Both runtimes call it, so a policy installed once covers both, and passing nil restores this framework's own resolution.
func SocketOriginCheck ¶
func SocketOriginCheck(handshake SocketHandshake, development bool) func(origin, host string) bool
SocketOriginCheck returns the check one handshake is judged by, or nil when the module's own default should stand.
The resolution runs per handshake rather than per message, which is once for the life of a connection, so it compiles the declared proxies and the trusted origin set there rather than caching a value a republished configuration would leave stale.
func StoreAuthentication ¶
func StoreAuthentication(store ValueStore, authentication Authentication)
StoreAuthentication is WithAuthentication for a transport that cannot derive.
It is the write half of the pair described on ValueStore: the reader above is already portable, because a request value that answers Value from its own store reaches this capsule the same way a derived context does. Only the installation differs, and this is it.
func StoreCSRFSecret ¶
func StoreCSRFSecret(store ValueStore, secret string)
StoreCSRFSecret records the request's CSRF secret on a request value that carries its own state, which is WithCSRFSecret for a transport that cannot derive a context.
func StoreClientAddress ¶
func StoreClientAddress(store ValueStore, address string)
StoreClientAddress records the resolved caller on a request value that carries its own state, which is WithClientAddress for a transport that cannot derive a context.
An empty address is not stored, the same as the deriving form: the absence of a resolved address and a resolution to the empty string would otherwise be indistinguishable to every reader.
func StoreLogAttributes ¶
func StoreLogAttributes(store ValueStore, attributes ...Attribute)
StoreLogAttributes is WithLogAttributes for a value store.
func StoreResources ¶
func StoreResources(store ValueStore, resources Resources)
StoreResources writes the request resources into a value store, which is WithResources for a transport that cannot derive.
func SupportsExplain ¶
SupportsExplain reports whether driver has a known plan-only EXPLAIN form. An unsupported driver keeps the query log and loses only the plan.
func SupportsSavepoint ¶
SupportsSavepoint reports whether driver may nest transactions.
func SwapHTMLDocument ¶
SwapHTMLDocument installs a shell chain and returns what was there.
It exists for tests, which install a document and must put back what they found; the compare-and-swap above admits one registration and no undo, which is right for an init and unusable for a test. Passing nil clears it.
func Transaction ¶
Transaction executes fn with the active transaction stored in its context. The outermost call begins a real transaction; a nested call opens a savepoint, so an inner failure rolls back only the inner work and leaves the outer transaction usable.
The transaction runs on the effective group of ctx, so SelectDB names a connection group for a whole transaction exactly as it does for a single statement, and nothing here can move a transaction to another group.
func VerifyCSRFToken ¶
VerifyCSRFToken reports whether presented unmasks to secret.
It exists so a caller that does not route through the update options still compares in constant time rather than with ==.
func WithAuthentication ¶
func WithAuthentication(ctx context.Context, authentication Authentication) context.Context
WithAuthentication installs the verified authentication result while preserving every other runtime resource already present on ctx. Only framework authentication middleware calls it.
func WithCSRFSecret ¶
WithCSRFSecret carries the session's CSRF secret for the framework code that emits and verifies tokens. The session middleware sets it; nothing else does.
func WithClientAddress ¶
WithClientAddress records the address of the caller rather than of the relay in front of it, resolved once per request against this deployment's declared proxies.
It is recorded rather than recomputed because more than one consumer needs it — a rate limit bucket, a live subscription bound — and two resolutions of one question are one answer that drifts.
func WithLocale ¶
WithLocale pins a resolved locale onto ctx. Locale resolution runs once per request, before the first byte, because policy:locale-vary-correctness needs the answer while headers are still open and flow:initial-streaming-render closes them before the body renders.
func WithLocaleMode ¶
func WithLocaleMode(ctx context.Context, mode LocaleMode) context.Context
WithLocaleMode records the mode the matched route declared.
func WithLogAttributes ¶
WithLogAttributes adds stable request attributes to every record taken from ctx afterwards, preserving the other runtime resources already installed.
func WithLogBackend ¶
func WithLogBackend(ctx context.Context, backend *LogBackend) context.Context
WithLogBackend replaces only the emission policy, which is what a test needs to capture records without rebuilding every other resource.
func WriteLiveClose ¶
func WriteLiveClose(w io.Writer, scratch []byte, reason string, retryAfter time.Duration) ([]byte, error)
WriteLiveClose is always the last record.
func WriteLiveDelivery ¶
func WriteLiveDelivery(w io.Writer, scratch []byte, content htmlbind.Content, digest string) ([]byte, error)
WriteLiveDelivery writes one delivery, carrying the validator the client stores and returns on its next connection.
func WriteLiveHead ¶
WriteLiveHead opens the stream, on the record every other update response opens with. It carries the head a delivery may need, because a delivery whose content reaches a component the document never carried would otherwise install nothing and paint unstyled.
func WriteLiveRecord ¶
WriteLiveRecord writes one newline-terminated record and flushes it. A delivery the client cannot see until a buffer fills is a delivery that did not happen.
func WriteLiveSignal ¶
WriteLiveSignal writes one signal: a name the client looks up in the table it registered while the page loaded, and the payload the source encoded.
It carries no boundary id, no validator and no revision, because a signal addresses no region. It is dispatched rather than applied, so the suppression and manifest bookkeeping every delivery goes through has nothing to say about it, and a client that skips a malformed one desynchronizes nothing.
The name is escaped rather than written through: it reaches a client as a lookup key, and the payload is appended exactly as the generated encoder produced it, which htmlbind already escaped for a script context as well as a JSON one.
Types ¶
type Attribute ¶
Attribute is one scalar key-value pair on a record.
It is the same type contrib/otel puts on a span, so a value that annotates a span annotates a record without conversion, and neither surface can carry a structure whose encoding could fail while a request is being served.
func Duration ¶
Duration records a duration in milliseconds, which is the unit both the OpenTelemetry log model and a human reading a terminal expect.
func Err ¶
Err renders an error as a record attribute. A nil error yields an empty value instead of panicking, because a log site is the last place that should introduce a crash.
type Authentication ¶
type Authentication struct {
Authenticated bool
// Subject is the stable local identity identifier.
Subject string
// Method is session, oidc, passkey, bearer, or an application-defined name.
Method string
// Principal is an optional application-defined typed value. Middleware must
// freeze or copy mutable claims before installing it.
Principal any
// Scope carries optional tenant, role, or permission values.
Scope []string
AuthenticatedAt time.Time
ExpiresAt time.Time
}
Authentication is the immutable, protocol-neutral authentication result that middleware records for downstream handlers and authorization checks. The zero value is an explicitly unauthenticated request.
It never carries passwords, token bodies, cookie values, or provider secrets.
func RequestAuthentication ¶
func RequestAuthentication(ctx context.Context) Authentication
Authentication reports the verified authentication state of the request. A request without authentication middleware reports the unauthenticated zero value.
type BotSettings ¶
type BotSettings struct {
// Enabled is html.bot_detection. It is off by default, which is why an
// unpublished value is a usable answer rather than a missing one: a runtime
// that finds nothing behaves as a deployment that never turned it on.
Enabled bool
// UserAgents are the additional tokens from html.bot_user_agents, already
// lowercased, so classification stays a plain scan.
UserAgents []string
}
BotSettings is the resolved bot-detection configuration in a form that names no transport, so one resolution serves both runtimes.
It travels the way the update settings do, and for the same reason: a configuration file is not a transport concern, and the runtime that read it and the runtime that serves the request need not be the same one.
func ResolvedBotSettings ¶
func ResolvedBotSettings() BotSettings
ResolvedBotSettings returns the published configuration, or the zero value where nothing published one.
It reports no second value, unlike the update settings. Those have no safe default and a runtime that finds none must decline; this one does, because detection off is both the configured default and the conservative branch — every client gets the streamed render, which is correct for a browser and merely slower for a crawler.
type CORSConfig ¶
type CORSConfig struct {
Enabled bool `default:"false"`
// Include and Exclude scope the policy, in the segment grammar the
// authenticated-path check and the CSRF check already share.
//
// Scope earns its keep with credentials rather than without them: an
// unscoped credentialed grant covers every authenticated page, every
// side-effect-free GET the update runtime issues, and the live stream —
// not the API the deployment meant to open. That is why AllowCredentials
// refuses the default include rather than quietly inheriting it.
Include []string `default:"[\"/**\"]" dependon:".enabled"`
Exclude []string `env:"-" dependon:".enabled"`
// AllowedOrigins are exact scheme://host[:port] values, or the single
// literal "*".
//
// There is no pattern language. A subdomain wildcard is an expression in
// framework configuration, which this framework refuses for the reason the
// bearer admission policy gives: an expression is a second thing to get
// right, evaluated on every request, whose mistakes are silent.
AllowedOrigins []string `env:"-" dependon:".enabled"`
// AllowCredentials lets a listed origin read a response the browser sent
// cookies with.
//
// It grants a read and never a write. The CSRF token travels in a cookie
// only same-origin script can read, so a cross-origin page cannot attach
// the header the check wants and every unsafe request is refused whatever
// this says.
AllowCredentials bool `default:"false" dependon:".enabled"`
// AllowedMethods are the methods a preflight admits. The default stops at
// the three a browser can already send without one, so admitting a write is
// something a deployment states rather than inherits.
AllowedMethods []string `default:"[\"GET\",\"HEAD\",\"POST\"]" dependon:".enabled"`
// AllowedHeaders are the request headers a preflight admits. The configured
// CSRF header is added to this set on its own while credentials are on,
// because a header that has to be remembered is the one that is forgotten.
AllowedHeaders []string `default:"[\"Content-Type\",\"Authorization\"]" dependon:".enabled"`
// ExposedHeaders are the response headers script may read.
//
// The default is the set framework frames write and no browser exposes on
// its own: the correlation identifier, and the four fields a 429 carries to
// say when to come back. Without them a cross-origin client cannot be told
// to back off, and keeps retrying at the rate that limited it.
ExposedHeaders []string `` /* 134-byte string literal not displayed */
// MaxAge bounds how long a browser may cache one preflight. Browsers cap it
// themselves — Safari at ten minutes, Chrome at two hours — so a larger
// value is reduced rather than honoured, and the default is the smallest
// cap so every engine keeps the same answer.
MaxAge time.Duration `default:"10m" dependon:".enabled" help:"how long a browser may cache one preflight"`
}
CORSConfig is the cross-origin admission policy.
It lives in the shared leaf beside SecurityHeadersConfig for the reason that one does: this is arithmetic over configuration with no request in it, both chains read it, and one declaration means one set of binder tags with nothing that can drift between two readers of the same policy.
Enabled is false by default, and a deployment that turns it on names the origins in the same breath. An admission policy with an empty list is a frame that marks nothing, which reads as cross-origin access that is not there.
func DefaultCORS ¶
func DefaultCORS() CORSConfig
DefaultCORS returns the shipped defaults: off, and admitting the reads a bearer API needs once turned on.
func (CORSConfig) Validate ¶
func (c CORSConfig) Validate() error
Validate rejects a policy that cannot mean what it says.
Every refusal here is a configuration a browser would answer by dropping the response, or a grant wider than the deployment can have meant. Both fail at startup rather than per request, because the failure mode of a wrong CORS policy is a network error in somebody else's browser.
type CORSDecision ¶
type CORSDecision struct {
// Preflight is true when the frame answers 204 and stops. The rest of the
// chain never sees the request: a preflight carries no cookie, no
// Authorization and no token, so every frame below would read it as an
// anonymous caller asking for something it may not have.
Preflight bool
// Headers are set on the response before anything downstream commits one,
// so a refusal written by any frame below carries them and its status
// reaches the caller instead of an opaque network error.
Headers []ResponseHeader
// Vary is appended to the response's Vary header.
Vary []string
// Declined names which half did not match, empty when nothing did.
Declined string
// Origin is the caller's Origin, carried for the record.
Origin string
}
CORSDecision is what one request produced.
It is a value rather than a set of calls into a response, because the two transports write headers differently and decide identically. Everything below is what to write; nothing here writes it.
type CSRFConfig ¶
type CSRFConfig struct {
Enabled bool `default:"false"`
Include []string `default:"[\"/**\"]" dependon:".enabled"`
Exclude []string `env:"-" dependon:".enabled"`
FormField string `default:"_csrf" dependon:".enabled"`
Header string `default:"X-CSRF-Token" dependon:".enabled"`
// CookieName is the companion cookie carrying the masked token the browser
// runtime reads. It is never HttpOnly, because the runtime has to read it.
//
// It belongs here rather than to the session cookie policy: the secret is a
// registered session slot like any other, and writing this cookie is the
// check's own job rather than something the session does on its behalf.
CookieName string `default:"pw_csrf" dependon:".enabled" help:"companion cookie carrying the token the browser runtime reads"`
TrustedOrigins []string `env:"-" dependon:".enabled"`
// TTL bounds the companion cookie the runtime reads. The secret itself is
// bounded by the session slot that holds it.
TTL time.Duration `default:"12h" dependon:".enabled" help:"lifetime of the companion token cookie"`
}
CSRFConfig is the cross-site check's configuration.
It lives in the shared leaf for the reason the security headers do: both chains read it, none of it names a transport, and one declaration means one set of binder tags and nothing that can drift between two readers of the same policy.
Enabled is false by default. A project turns it on together with the include patterns that say what it covers, because a middleware installed over nothing reads as protection that is not there.
func DefaultCSRF ¶
func DefaultCSRF() CSRFConfig
DefaultCSRF returns the shipped defaults: off, and covering everything once turned on.
type CacheConfig ¶
type CacheConfig struct {
Enabled bool `default:"false" help:"reuse what a fetch returned for equal keys"`
// Stores is the array-of-tables form. An element takes no CLI option and no
// environment variable, because its identity is its position in the file.
Stores []CacheStoreConfig `dependon:".enabled" help:"cache store set, one element per store"`
}
CacheConfig is the named store set, configured as [[cache.stores]].
A store is configured rather than constructed because it outlives every request, carries an operational size, and one day may address a process outside this one. That is the same set of properties a database pool has, so it is configured the way one is.
type CacheKey ¶
type CacheKey = cachekeybind.CacheKey
CacheKey is what a Memo key type implements: the type's identity followed by the framed encoding of every field marked with the cache tag.
It is system:tinybind's interface rather than one of ours, so a key method emitted by that generator satisfies this without an adapter. A key may also be written by hand against the same package.
It is a method rather than a shape this framework walks because walking would mean reflection, and because the set of fields an entry depends on is exactly what has to be visible for the cache to be correct.
type CacheStats ¶
CacheStats is what one store has answered, for a diagnostic or a test.
type CacheStore ¶
type CacheStore struct {
// contains filtered or unexported fields
}
CacheStore is a handle to one configured store.
It holds no request state, so one handle serves every request and may be resolved once at setup or per call. The scope and the trace come from the context passed to each operation instead.
The typed operations are package functions rather than methods because a Go method may not declare its own type parameters. When that changes they become Get, Has, and Set on this type, and no call site that already holds a handle has to move. cache_go127.go carries them written already, behind the build tag for the release expected to allow them.
func MemoStore ¶
func MemoStore(ctx context.Context, name string) (*CacheStore, error)
MemoStore resolves a configured store by name.
A disabled section returns no store and no error, so removing caching from a deployment edits no call site: every operation on a nil store falls through to its fetch. A name the set does not hold is an error naming what is configured, because the alternative is a project that believes it caches.
The set is immutable once built, so an application whose store name is static may resolve once during setup and hold the handle, which turns an unknown name into a startup failure rather than a first-request one.
func (*CacheStore) Get ¶ added in v0.5.1
func (s *CacheStore) Get[K CacheKey, T any](ctx context.Context, key K, fetch func(context.Context) (T, error)) (T, error)
Get returns what fetch produced for this key, reusing a stored result while it is fresh and coalescing concurrent misses onto one fetch.
It is Memo with the store as the receiver, and that function's rule about the fetch context holds unchanged: the fetch receives a context detached from every waiter, so do not capture a request context inside the closure instead.
func (*CacheStore) Has ¶ added in v0.5.1
func (s *CacheStore) Has[K CacheKey](ctx context.Context, key K) bool
Has reports whether this key currently has a fresh entry; a stale one answers false. It is racy by nature, so it answers a diagnostic or a decision to skip expensive work, never control flow assuming the next read hits.
func (*CacheStore) Invalidate ¶ added in v0.5.1
func (s *CacheStore) Invalidate[K CacheKey](ctx context.Context, key K)
Invalidate drops one entry, taking the key the read took.
func (*CacheStore) InvalidateScope ¶ added in v0.5.1
func (s *CacheStore) InvalidateScope(scope string)
InvalidateScope drops everything one reader holds.
It declares no type parameter and could have been a method all along. It moves with the rest so that the store reads one way rather than two.
func (*CacheStore) InvalidateTag ¶ added in v0.5.1
func (s *CacheStore) InvalidateTag(tag string)
InvalidateTag drops everything a tag names, which is the axis the reader-first key layout cannot serve as a prefix. Like InvalidateScope it needs no type parameter and moves for the company rather than the language.
func (*CacheStore) Name ¶
func (s *CacheStore) Name() string
Name reports which configured store this handle addresses.
func (*CacheStore) Set ¶ added in v0.5.1
Set writes an entry without consulting one, which is how a writer refreshes what it just made wrong. The lifetime comes from the store.
func (*CacheStore) Stats ¶
func (s *CacheStore) Stats() CacheStats
Stats reports this store's counters. A hit count alone cannot distinguish a working cache from one nothing is eligible for, so every counter is reported together.
type CacheStoreConfig ¶
type CacheStoreConfig struct {
// Name is what a call site addresses this store by.
Name string `help:"name this store is addressed by"`
// Backend names where entries live. Only memory is implemented; any other
// value is refused rather than ignored, because a store that silently fell
// back to memory would be a shared cache that is not shared.
Backend string `default:"memory" help:"where entries live; memory is the only implemented backend"`
// TTL is how long an entry is fresh.
TTL time.Duration `default:"1m" help:"how long an entry is fresh"`
// Stale is how long past fresh a held entry may still answer while one
// revalidation runs. Zero disables the window.
Stale time.Duration `default:"0s" help:"how long a stale entry may answer while it revalidates"`
// Scope is private or public. Private prefixes every key with the reader's
// identity, which is the default for the same reason it is the render
// cache's: a shared entry holding one reader's data is the failure that
// does not degrade.
Scope string `default:"private" help:"private keys entries per reader; public shares them"`
// MaxEntries bounds the in-process store. Zero or less is unbounded.
MaxEntries int `default:"1024" help:"maximum entries this store holds"`
// FetchTimeout bounds a coalesced fetch. It exists because the shared fetch
// runs on a context detached from every waiter, so nothing else would ever
// stop it and a hung upstream would leak one goroutine per cold key.
FetchTimeout time.Duration `default:"30s" help:"bound on a fetch running detached from its waiters"`
}
CacheStoreConfig is one store of the set.
type CacheTagger ¶
type CacheTagger interface {
CacheTags() []string
}
CacheTagger is the optional half of a key type: the tags whose invalidation drops this entry. It is ours rather than the generator's, because a tag is an invalidation policy and the key method is an identity.
A key type implementing nothing is invalidated by key or by scope alone.
type CaptureSink ¶
type CaptureSink struct {
// contains filtered or unexported fields
}
CaptureSink collects records in memory instead of writing them anywhere.
It exists so a test can assert on what was logged without parsing formatted output, which is the difference between checking a fact and checking an encoder. It is safe for concurrent use.
func (*CaptureSink) Records ¶
func (sink *CaptureSink) Records() []Record
Records returns a copy of what has been captured so far.
func (*CaptureSink) Reset ¶
func (sink *CaptureSink) Reset()
Reset discards everything captured so far.
type ChainSettings ¶
type ChainSettings struct {
// The three frames a deployment turns on and off.
RequestID bool
AccessLog bool
Recovery bool
// RequestTimeout and MaxRequestBody install their frames when positive.
RequestTimeout time.Duration
MaxRequestBody int64
// SecurityHeaders is installed when Enabled says so, which is the switch
// the configuration keeps beside the values rather than a separate one.
SecurityHeaders SecurityHeadersConfig
// CORS is answered by the frame SecurityHeaders installs rather than by one
// of its own, so either half being enabled is what puts that frame in the
// chain. They are separate settings and one position.
CORS CORSConfig
// TrustedProxies are the networks whose forwarding headers this deployment
// reads, as configured. They are strings rather than parsed networks
// because that is what the configuration carries and parsing them is the
// caller's, which already has to report a bad one against a config key.
TrustedProxies []string
// Health and Readiness are the paths the two probes answer on, empty when
// a deployment turned one off.
Health string
Readiness string
// OpenAPI, APIDoc and APIDocPath are the document path, the UI kind, and
// the path that UI is read at. All three empty means the chain answers no
// documentation at all, which is the common case.
OpenAPI string
APIDoc string
APIDocPath string
// CSRF is the cross-site check's configuration, carried whole because the
// check reads most of it: the scope patterns, the token names, the cookie
// name and the lifetime.
CSRF CSRFConfig
// Public is the static asset configuration. The tree itself is not here
// because an embed is a fact of the binary rather than of a settings file.
Public PublicAssetSettings
// RateLimit is the limiter's configuration, carried whole because a chain
// builder reads most of it.
//
// Two frames come out of it, at two slots: the ceiling out where a refusal
// costs least, and the identity bucket below whatever establishes
// authentication. Neither can be the other's position.
RateLimit RateLimitConfig
}
ChainSettings is what building the request chain needs from configuration, in a form that names no transport.
It is the fourth thing to travel this way, after the update settings, the bot settings and the configuration lookup, and for the same reason each time: a configuration file is not a transport concern, and the runtime that read it and the runtime that serves the request need not be the same one.
It is a flat value rather than the configuration structs themselves because those carry the binder's tags, the scaffold help text and the defaults, none of which a chain builder reads. What a chain builder reads is this.
func ResolvedChainSettings ¶
func ResolvedChainSettings() (ChainSettings, bool)
ResolvedChainSettings returns the published configuration, and whether anything published one.
A runtime that finds none has no configuration to build a chain from and says so, rather than composing a chain out of zero values. Zero here would mean no recovery frame, no request ID, no security headers — a chain that serves requests and looks like a chain, with the frames a deployment configured silently missing.
type Connection ¶
type Connection struct {
DB *sql.DB
// Native is the handle of an engine that bypasses database/sql. A caller
// that needs a *sql.DB finds none on such a connection; statements run
// through Executor and transactions through the scope.
Native database.NativeDB
// Driver is the resolved driver scheme, which decides savepoint support.
Driver string
// Group is the name this connection is addressed by.
Group string
// Label identifies this connection inside its group, as group#ordinal.
Label string
// ReadOnly marks a replica. It selects a read-only transaction and, once
// the SQL runtime can classify statements, a read-only executor.
ReadOnly bool
}
Connection is one configured pool of the connection set. Exactly one of DB and Native is set: DB for an engine served through database/sql, Native for one whose request-time path bypasses it.
func (*Connection) Close ¶
func (connection *Connection) Close() error
Close releases the pool, whichever kind it is.
func (*Connection) Executor ¶
func (connection *Connection) Executor() sqlbind.SQLExecutor
Executor is the pool-level statement surface of this connection, which is what the executor seam stores when no transaction is active.
func (*Connection) Ping ¶
func (connection *Connection) Ping(ctx context.Context) error
Ping verifies the pool can reach its database.
func (*Connection) TransactionScope ¶
func (connection *Connection) TransactionScope() *TransactionScope
TransactionScope prepares an inactive transaction scope over this connection's pool, carrying its group and read-only marking. Begin activates it. It exists for the test bridge, whose shared test transaction must run on whichever kind of pool the connection holds.
type ConnectionSet ¶
type ConnectionSet struct {
// contains filtered or unexported fields
}
ConnectionSet is the immutable set of connection groups owned by the framework. Selection inside a group is round robin.
func NewConnectionSet ¶
func NewConnectionSet(defaultGroup string, connections []Connection) (*ConnectionSet, error)
NewConnectionSet groups connections by name in configuration order.
defaultGroup may be empty only when exactly one group is present.
func (*ConnectionSet) Close ¶
func (set *ConnectionSet) Close() error
Close closes every pool and joins the failures.
func (*ConnectionSet) Collapsed ¶
func (set *ConnectionSet) Collapsed() bool
Collapsed reports whether every group name resolves to one database.
A single-database configuration and a test both collapse, so application code that selects a replica group runs unchanged against one development sqlite file and against a reader-writer cluster.
func (*ConnectionSet) Connections ¶
func (set *ConnectionSet) Connections() []*Connection
Connections lists every connection in configuration order, which is what the startup summary and shutdown need.
func (*ConnectionSet) Count ¶
func (set *ConnectionSet) Count() int
Count reports how many connections are configured. It exists for callers that need only the number, which Connections would answer with a fresh slice per call.
func (*ConnectionSet) DefaultGroup ¶
func (set *ConnectionSet) DefaultGroup() string
DefaultGroup is the group serving statements that pin no group of their own.
func (*ConnectionSet) Groups ¶
func (set *ConnectionSet) Groups() []string
Groups lists the configured group names in configuration order.
func (*ConnectionSet) Has ¶
func (set *ConnectionSet) Has(group string) bool
Has reports whether group is configured. A collapsed set answers for every name, because it has only one database to answer with.
type FieldError ¶
type FieldError = tinybind.FieldError
FieldError describes a single field-level validation failure. It is the module's own type, already shared by both of its runtimes.
func Field ¶
func Field(field, location, message string) FieldError
Field builds a field-level validation error for Validation.
type Frame ¶
Frame is one positioned step of the chain.
It is generic over the handler because that is the one thing the two runtimes genuinely disagree about: net/http wraps an interface and fasthttp wraps a function. Everything else about a frame — where it sits, what it is called — is the same on both.
type HSTSConfig ¶
type HSTSConfig struct {
Enabled bool `default:"false"`
MaxAge time.Duration `default:"0s" dependon:".enabled"`
IncludeSubdomains bool `default:"false" dependon:".enabled"`
Preload bool `default:"false" dependon:".enabled"`
}
HSTSConfig controls Strict-Transport-Security on verified HTTPS requests. The dependon on the HSTS field itself carries the headers switch down over this whole block, so nothing here names an absolute key.
type HTMLErrorPage ¶
type HTMLErrorPage func(Problem) HTMLFragment
HTMLErrorPage renders an application error page from a problem. It names no transport, which is what lets one registration serve both runtimes.
func RegisteredHTMLErrorPage ¶
func RegisteredHTMLErrorPage() HTMLErrorPage
RegisteredHTMLErrorPage returns the resolver, or nil where none was registered and a problem therefore takes its document form.
type HTMLFragment ¶
HTMLFragment is a bound component ready to render. It is named here as well as on each runtime because an application file that registers an error page belongs to both builds, and a file naming a runtime does not.
type Level ¶
type Level int8
Level is the severity of a log record.
The numbers are slog's, so a Level converts to a slog.Level without a table and a handler written against slog filters these records correctly. Trace is the one severity slog does not name; it sits one step below debug.
const ( LevelTrace Level = Level(slog.LevelDebug) - 4 LevelDebug Level = Level(slog.LevelDebug) LevelInfo Level = Level(slog.LevelInfo) LevelWarn Level = Level(slog.LevelWarn) LevelError Level = Level(slog.LevelError) // LevelOff is above every severity, so nothing passes the filter. LevelOff Level = 127 )
type LiveDigester ¶ added in v0.5.2
type LiveDigester struct {
// contains filtered or unexported fields
}
LiveDigester is LiveDigest with the HMAC state built once and reset between records. A stream digests every boundary it sends, and each hmac.New allocates two SHA-256 states plus the key pads, so the loop that owns a response builds one of these instead. It is single-goroutine, like that loop.
func NewLiveDigester ¶ added in v0.5.2
func NewLiveDigester(key []byte) *LiveDigester
NewLiveDigester returns a digester over key, or nil for a nil key — the same "suppression off" answer LiveDigest gives, which Digest keeps by answering the empty string on a nil receiver.
func (*LiveDigester) Digest ¶ added in v0.5.2
func (d *LiveDigester) Digest(html []byte) string
Digest is the validator of one delivery, from the reused state.
type LiveWatchdog ¶
type LiveWatchdog struct {
// contains filtered or unexported fields
}
LiveWatchdog closes a response that has run long enough or delivered nothing for long enough. Both bounds cancel the render context, which breaks every pull loop and lets each source observe the cancellation through its context.
func StartLiveWatchdog ¶
func StartLiveWatchdog(cancel context.CancelFunc, lifetime, idle time.Duration) *LiveWatchdog
StartLiveWatchdog begins watching. A zero lifetime and a zero idle bound leave it inert, which is the configuration that asks for no bound at all.
func (*LiveWatchdog) Delivered ¶
func (d *LiveWatchdog) Delivered()
Delivered reports activity, which restarts the idle bound.
type Locale ¶
type Locale struct {
// contains filtered or unexported fields
}
Locale identifies one locale the project declared. It is opaque because the value that matters is the dense index the generated message tables are addressed by; the tag is carried so a document, a URL, and a Content-Language header can name it.
A zero Locale is the explicit "no locale resolved" value. It is never a silent stand-in for the default, per policy:absent-rather-than-stubbed.
See .knowledge api:locale-accessors.
func DefaultLocale ¶
func DefaultLocale() Locale
DefaultLocale returns the declared default. It reports the zero Locale when no message package is linked.
func LocaleContext ¶
LocaleContext returns the locale resolved for this request.
Unlike ParseLocale it always answers: a request that reached a route with no declared mode, or a context with no resolution at all, reports the declared default. A caller rendering text has no useful branch on absence, and the alternative is every message call site handling a case the router already decided.
func MessageLocale ¶
MessageLocale is the provider behind the implicit binding supplying every generated message symbol's leading argument.
It returns Locale rather than a tag so the generated catalog takes the type it declares. A typed binding cannot be written into markup, which is what keeps this value out of the positions LangTag and LangSegment serve.
See .knowledge data:locale-bindings.
func MustParseLocale ¶
MustParseLocale is ParseLocale for a tag the caller knows is declared, such as a constant in generated code. It panics on an undeclared tag.
func NewLocale ¶
NewLocale builds a Locale for a declared tag at a known position.
It exists for generated code, which holds the tables this index addresses and therefore already knows every position. Handwritten code has ParseLocale, which validates against the registered set; this one does not, because a generated constant is initialized before init functions run and so before any registration could have happened.
index is the zero-based position in the declared list.
func ParseLocale ¶
ParseLocale resolves a tag against the declared set by RFC 4647 lookup: an exact match wins, then progressively shorter prefixes, so ja-JP finds ja.
It reports absence rather than substituting the default, per policy:absent-rather-than-stubbed. A caller that wants the default asks for it.
This is the entry point for a locale that did not come from a request — a value read from a user record before sending mail, or from a job payload.
func StripLocalePrefix ¶
StripLocalePrefix removes a leading locale segment from a path and reports the locale it named.
The prefix position is always read as a locale, so an undeclared tag there is not reinterpreted as an ordinary path segment: a route literally named /de/ would otherwise break on the day German is added.
func (Locale) Index ¶
Index returns the position of this locale in the declared list. It is the subscript of every generated message table.
The zero Locale reports -1 rather than 0, so indexing with an unresolved locale panics at the read instead of silently serving the first declared language.
func (Locale) String ¶
String makes a Locale printable in a log or a test failure without reaching for Tag, and names the zero value rather than rendering as empty.
type LocaleChoice ¶
type LocaleChoice struct {
Locale Locale
// Label is the display name written in that locale itself, declared in the
// project's i18n block. It falls back to the tag, which is visibly wrong in
// a switcher rather than silently wrong.
Label string
// URL is this same page in that locale, empty in a mode with no per-locale
// URL.
URL string
Current bool
}
LocaleChoice is one entry of a language switcher, and one alternate link.
The switcher, the hreflang alternates, and the canonical all read this, so a page cannot have its switcher and its alternates disagree. See .knowledge requirement:locale-switching-surface.
func LocaleChoices ¶
func LocaleChoices(r *http.Request) []LocaleChoice
LocaleChoices reports the locales this page is available in.
A path-mode page carries a URL per locale, which is the same data the alternate links are built from. A cookie-mode page carries none, because switching there is a server action writing the cookie rather than a link.
func LocaleChoicesFor ¶
func LocaleChoicesFor(path, rawQuery string, current Locale, mode LocaleMode) []LocaleChoice
LocaleChoicesFor is LocaleChoices over the values a request carries rather than over a request.
The switcher is identical on both transports, so the computation lives here and each transport's entry supplies its own path, query, and resolved locale. Duplicating it per transport is how the two would drift.
type LocaleMode ¶
type LocaleMode uint8
LocaleMode is how a route decides its locale, declared per path prefix in the project's i18n block. It is carried on the request because it decides two things a handler cannot re-derive: whether a link carries a locale segment, and what the response varies on.
See .knowledge decision:locale-url-modes.
const ( // LocaleModePath reads the locale from a URL path prefix. Two languages are // two URLs, so the response varies on nothing. LocaleModePath LocaleMode = iota // LocaleModeCookie reads a stored reader choice, then Accept-Language. LocaleModeCookie // LocaleModeHeader reads Accept-Language only, which is the mode an API // answering a native client uses. LocaleModeHeader )
func LocaleModeContext ¶
func LocaleModeContext(ctx context.Context) LocaleMode
LocaleModeContext reports the mode of the matched route.
A request that reached no declared route reports LocaleModeHeader, which is the mode that puts nothing in a URL. That is the safe default: emitting a locale segment for a route that has no prefixed form produces links that 404, while omitting one produces links that work and merely do not carry the language.
type LocaleRoute ¶
type LocaleRoute struct {
Prefix string
Mode LocaleMode
}
LocaleRoute binds one path prefix to the way its locale is decided. The list is registered by the generated message package, because the modes are declared in the project's build configuration.
type LogBackend ¶
type LogBackend struct {
// contains filtered or unexported fields
}
LogBackend is the process-wide emission policy: one severity floor and the set of destinations a record reaches.
More than one sink is how a development run keeps its terminal stream while also feeding a collector. A production run configured for OTLP normally holds one sink, because duplicate delivery is a cost nobody asked for.
func NewLogBackend ¶
func NewLogBackend(minimum Level, sinks ...Sink) *LogBackend
NewLogBackend returns a backend that drops records below minimum and hands the rest to every sink in order. A backend with no sink is valid and silent.
func (*LogBackend) Enabled ¶
func (backend *LogBackend) Enabled(level Level) bool
Enabled reports whether a record at level would reach any destination.
func (*LogBackend) Minimum ¶
func (backend *LogBackend) Minimum() Level
Minimum reports the configured severity floor.
func (*LogBackend) Sinks ¶
func (backend *LogBackend) Sinks() int
Sinks reports how many destinations a record reaches, which is what distinguishes exclusive routing from the development fan-out.
type Logger ¶
type Logger struct {
// contains filtered or unexported fields
}
Logger is the context-bound logger of the framework logging API.
It is a value: With returns a new one and never mutates the receiver, so a logger captured by a handler cannot be changed underneath it. The zero value is usable and discards everything, which is what keeps every accessor free to return a logger rather than nil.
func NewLogger ¶
func NewLogger(ctx context.Context, backend *LogBackend, attributes ...Attribute) Logger
NewLogger binds backend and the trace correlation of ctx into a logger.
func ReadLogger ¶
ReadLogger returns a logger bound to the emission policy of ctx, its stable request attributes, and the span active on it. It never returns a logger that cannot be called: a context with nothing installed still yields a usable one.
Acquire again inside a child span to correlate with that span, because the correlation is captured here rather than at each call.
func (Logger) Enabled ¶
Enabled reports whether a record at level would reach any destination. Call it before assembling attributes that cost something to produce.
func (Logger) Log ¶
Log emits at an explicit level with the caller's context, which a sink may use for cancellation. There is deliberately no Fatal or Panic: logging must not decide whether the process lives.
type MetricGroups ¶
MetricGroups selects which instruments exist. A false group leaves its fields nil, which is what makes declining a group free rather than cheap.
type Metrics ¶
type Metrics struct {
// RequestDuration is http.server.request.duration. Its count answers how
// many requests were served and its buckets answer the percentile, so no
// counter sits beside it.
RequestDuration *metric.Histogram
// ActiveRequests is http.server.active_requests, the concurrency no
// per-request record shows.
ActiveRequests *metric.UpDownCounter
// RequestBodySize and ResponseBodySize are the http.server body size
// histograms. A request with no declared length records nothing rather than
// zero.
RequestBodySize *metric.Histogram
ResponseBodySize *metric.Histogram
// QueryDuration is db.client.operation.duration, recorded on the same
// resolver seam the statement span uses.
QueryDuration *metric.Histogram
// RenderDuration and RenderBytes describe one response per render mode,
// which is the branch nothing outside this process can attribute a response
// time to.
RenderDuration *metric.Histogram
RenderBytes *metric.Histogram
// BoundarySettle is how long a fallback held the screen. It carries no
// boundary id: positional is safe on a span and unbounded across pages.
BoundarySettle *metric.Histogram
// LiveDelivery is the interval between consecutive deliveries of one
// boundary, and LiveActive is how many subscriptions exist right now.
LiveDelivery *metric.Histogram
LiveActive *metric.UpDownCounter
LiveClosed *metric.Counter
// RenderCache counts component output cache operations, with a result
// attribute rather than one instrument per outcome, because a reader
// dividing them needs both under one name.
RenderCache *metric.Counter
// DataCache counts data result cache operations the same way.
DataCache *metric.Counter
}
Metrics is the resolved framework instrument set installed by pw.
A nil value records nothing, and so does a nil field inside it: a group the configuration declined leaves its instruments nil, and every recording site is one nil comparison. It mirrors Tracing in being a resolved policy rather than the configuration struct, and it differs from Tracing in one way that matters — a recording here never consults whether a span is being sampled, because a count of the sampled fraction is not a count.
func MetricPolicy ¶
MetricPolicy returns the framework instrument set of ctx, or nil when nothing is recorded. It is one context lookup, the same one every other resource accessor makes.
func NewMetrics ¶
func NewMetrics(meter *metric.Meter, groups MetricGroups) *Metrics
NewMetrics creates the instrument set for one meter.
Every name, unit, and attribute set of the http and db groups is the semantic convention's; the pw-prefixed ones are this framework's, one per question no external agent can answer.
type NumberFormat ¶
type NumberFormat struct {
// Group separates thousands, empty for a locale that does not group.
Group string
// Decimal separates the fractional part.
Decimal string
}
NumberFormat is how one locale groups digits.
Only grouping and the two separators are modelled. Digit shaping, currency placement, and ordinal words each need their own tables, and a message argument is a count or a quantity rather than a formatted money value — the framework offering half of currency formatting would be worse than offering none.
func FormatNumber ¶
func FormatNumber(locale Locale) NumberFormat
FormatNumber reports the grouping of a locale, falling back to the comma-and-period convention for one this build does not tabulate.
The fallback is a convention rather than a guess at the locale: a number that groups the wrong way is legible, and one that carries no separators at all is legible too, so neither failure is worth an error that stops a page.
type OtelSink ¶
type OtelSink struct {
// contains filtered or unexported fields
}
OtelSink emits records through an OpenTelemetry log provider.
type PageAction ¶
type PageAction struct {
// Name is the exported Go function, which is what a script names.
Name string
// Path is its direct endpoint, which holds no path parameter and is
// therefore a constant rather than something to build per request.
Path string
}
PageAction is one server function reachable from a page's scripts.
func PageActionsFor ¶
func PageActionsFor(pattern string) []PageAction
PageActionsFor returns what the route that matched publishes, or nothing.
Nothing is the ordinary answer: a project with no page tree registers none, a route whose package exports no handler has none, and both should cost a render exactly one map lookup.
type Problem ¶
type Problem struct {
Status int
Title string
Code string
Message string
Fields []FieldError
Cause error
RateLimit *RateLimit
}
Problem is the application-facing RFC problem value.
It lives here rather than in pw because both transport runtimes have to build, inspect, and unwrap the same type. Two structs that agree today are two chances to disagree later, and the failure is silent: an errors.As that stops matching, a problem that no longer unwraps. The module reached the same conclusion for its own error types and put them in a leaf both of its runtimes alias.
This is not the module's Problem, which carries a code and a message and is the body its error constructors take. This one is the whole answer: the status a response gets, the title it shows, the fields that failed, and the cause that stays server-side.
func BadRequest ¶
func InternalServerError ¶
func MapProblem ¶
MapProblem turns any error into the problem a response describes.
Three cases, in order: an error that is already a Problem keeps everything it declared and gains the defaults it left out; an error the binding layer recognizes carries its own status, title and field failures across; anything else is an internal error, which is the only honest answer for a cause this package cannot classify.
It is here rather than in either runtime because both answer the same failures, and a status that differed between two builds of one application would be a difference nothing in either response explained.
func NewProblem ¶
NewProblem builds one from a status, a title, and whatever the caller passed: nothing, another problem to carry through, an error to wrap, or a message.
func PayloadTooLarge ¶
func RateLimited ¶
RateLimited builds a 429 problem carrying retry and quota metadata.
func SanitizeProblem ¶
SanitizeProblem drops what a 5xx must never carry out of the process.
Applying it twice changes nothing, which is what makes it safe to put at each writer rather than at one entry point: a boundary that failed with no recover clause reaches a writer directly, and every path that can answer with a server error has to lose the cause on the way out.
func ServiceUnavailable ¶
func TooManyRequests ¶
func Unauthorized ¶
func Validation ¶
func Validation(fields ...FieldError) Problem
Validation reports a 400 response carrying every detected field failure.
type PublicAssetSettings ¶
type PublicAssetSettings struct {
Enabled bool `default:"true"`
Mount string `default:"/public" dependon:".enabled"`
ReadLocal bool `default:"false" dependon:".enabled"`
// SVGSandbox adds a sandbox Content-Security-Policy to an image/svg+xml
// response. An SVG is the one served image that executes, and it executes
// in the application origin on direct navigation, so this is on by default
// and is off only for a project deliberately serving an interactive SVG
// through object, embed, or a link.
SVGSandbox bool `default:"true" dependon:".enabled"`
}
PublicAssetConfig controls the framework-owned static asset endpoint.
type QueryDiagnostics ¶
type QueryDiagnostics struct {
// Level is the severity of an ordinary statement record.
Level Level
// SlowLevel is the severity once a statement exceeds SlowThreshold.
SlowLevel Level
// SlowThreshold is the duration above which a statement is slow. Zero
// disables slow detection, and with it EXPLAIN and reproduction.
SlowThreshold time.Duration
// BindValues allows argument values into the record. It is the only path by
// which application row data reaches a framework SQL log.
BindValues bool
// Explain captures a plan-only EXPLAIN for a slow statement.
Explain bool
// Reproduction renders a paste-able rerun snippet for a slow statement.
Reproduction bool
// MaxSQLLength bounds the logged statement text.
MaxSQLLength int
// MaxValueLength bounds each logged argument value.
MaxValueLength int
}
QueryDiagnostics is the resolved query diagnostics setting installed by pw.
A nil value disables the feature entirely, so SQLExecutor returns the bare database handle and no statement is timed.
type RateLimit ¶
RateLimit is the retry metadata attached to a 429 problem. X-RateLimit-* fields are compatibility conventions; Retry-After is the standard signal.
type RateLimitConfig ¶
type RateLimitConfig struct {
Enabled bool `default:"false"`
Backend string `default:"memory" enum:"memory,redis" dependon:".enabled" help:"counter storage: memory or redis"`
// Window is the period every count below is measured over. It is also the
// burst granularity, because the algorithm is a fixed window, and it is
// what X-RateLimit-Reset reports.
Window time.Duration `default:"1m" dependon:".enabled" help:"period every count is measured over"`
// PerSubject bounds one authenticated caller. Zero disables this bucket,
// because such a caller is accountable and revocable by other means.
PerSubject int `default:"600" dependon:".enabled" help:"requests one authenticated subject may make in a window; zero disables"`
// PerAddress bounds one caller with no session, and has no off position.
// It is the only bucket an unauthenticated flood meets, so an unlimited
// value here is an absent control rather than a permissive one.
PerAddress int `default:"300" dependon:".enabled" help:"requests one caller with no session may make in a window"`
// Process is the total arrival ceiling, unkeyed. It is the only layer that
// sees a distributed flood, since such a flood keeps every source under
// PerAddress by construction.
//
// It defaults to zero rather than to a guess: the right value follows from
// what a deployment can serve, and one set below real capacity refuses
// legitimate traffic globally.
Process int `default:"0" dependon:".enabled" help:"total arrivals allowed in a window, unkeyed; zero leaves only the identity buckets"`
// Redis names the backend it belongs to, so a memory-counted deployment
// reports no counter server. Backend already answers to Enabled, so the
// switch is not repeated here.
Redis RateLimitRedisConfig `dependon:".backend=redis"`
}
Config bounds how often one caller, and the process as a whole, may arrive within a window.
It deliberately declares no per-route rules. A per-operation quota belongs to an API gateway, and a pattern grammar with a precedence order would be most of the cost of this middleware for a capability the boundary in front of a normal deployment already sells.
func DefaultRateLimit ¶
func DefaultRateLimit() RateLimitConfig
DefaultConfig returns the shipped defaults: off, and bounded once on.
func (RateLimitConfig) Validate ¶
func (c RateLimitConfig) Validate() error
Validate rejects a configuration whose limits cannot bind.
type RateLimitRedisConfig ¶
type RateLimitRedisConfig struct {
DSN string `secret:"mask" env:"RATELIMIT_REDIS_DSN" help:"redis:// or rediss:// counter server"`
KeyPrefix string `default:"pw:ratelimit:" help:"key space this limiter owns"`
ConnectTimeout time.Duration `default:"5s" help:"bounds the startup ping and per-command deadlines"`
}
RedisConfig addresses the shared counter server.
type Record ¶
type Record struct {
Time time.Time
Level Level
Message string
Attributes []Attribute
TraceID string
SpanID string
TraceFlags byte
}
Record is one finished log record handed to a Sink. Correlation is resolved before the record is built, so a Sink never inspects the context for it.
type RedirectError ¶
type RedirectError struct {
// Location is the target, checked for navigability where it is written
// rather than here, so one check covers both the returned and the written
// form.
Location string
// Status is one of the five redirect codes.
Status int
}
RedirectError carries a location and the status that sends the browser to it.
It is exported so a caller can inspect one with errors.As, and constructed through the framework's own constructors so the status cannot be a value no browser treats as a redirect.
func (RedirectError) Error ¶
func (e RedirectError) Error() string
type ResolvedCORS ¶
type ResolvedCORS struct {
// contains filtered or unexported fields
}
ResolvedCORS is a validated policy reduced to what a request needs.
Everything a response can carry is precomputed: the header values are joined once here rather than per request, and the two lookups a request performs are map reads.
func ResolveCORS ¶
func ResolveCORS(config CORSConfig, csrfHeader string) (ResolvedCORS, error)
ResolveCORS validates a policy and reduces it to the values a response carries.
csrfHeader is the configured CSRF header name. It is admitted with the configured headers while credentials are on, because a credentialed origin that may write has to send it, and a name that must be remembered in a second place is the one that is forgotten. Admitting the name grants nothing on its own: the token check still runs, and still refuses a request that cannot present a value derived from the session's own secret.
func (ResolvedCORS) Decide ¶
func (c ResolvedCORS) Decide(path, rawPath, method, origin, requestMethod, requestHeaders string) CORSDecision
Decide answers one request.
path and rawPath are the decoded and raw request paths; a path that cannot be matched unambiguously is left unmarked rather than refused, since deciding about a target whose identity depends on who resolves it is the mistake the canonical form exists to prevent.
func (ResolvedCORS) Enabled ¶
func (c ResolvedCORS) Enabled() bool
Enabled reports whether the policy installs anything.
func (ResolvedCORS) RecordCORSDecline ¶
func (c ResolvedCORS) RecordCORSDecline(ctx context.Context, decision CORSDecision, path string)
RecordCORSDecline writes the one account of a refusal that exists.
A declined request is served like any other: the status is 200 or 204, the access log records a request that worked, and the browser reports the failure to its own console and to nobody else. Unlike a CSP violation this is not a Reporting API type, so no report is ever delivered anywhere — the frame that declined is the only thing that knows, and this is it saying so.
The level is info rather than debug because the shipped severity floor is info, and a record nobody sees is not an improvement on no record. The bound above is what makes that affordable.
type ResolvedSecurityHeaders ¶
type ResolvedSecurityHeaders struct {
// Always is set on every response. None of these values depends on the
// request, so they are computed once rather than per request.
Always []ResponseHeader
// HSTS is sent only on a request that arrived over HTTPS, and is empty when
// the feature is off. The condition is about the connection rather than the
// value, which is why this one is separate rather than in Always.
HSTS string
}
ResolvedSecurityHeaders is a validated configuration reduced to what a response actually carries.
func ResolveSecurityHeaders ¶
func ResolveSecurityHeaders(config SecurityHeadersConfig) (ResolvedSecurityHeaders, error)
ResolveSecurityHeaders validates a configuration and reduces it to the header set a response carries.
Both middleware chains call it at construction, so a misconfiguration is an error before the port is bound rather than a wrong header per request, and the two transports send the same headers because they are computed here rather than in either of them.
type Resources ¶
type Resources struct {
Configs map[reflect.Type]any
// Log is the process emission policy. A nil backend falls back to a plain
// stderr text handler so that a request served outside pw still logs.
Log *LogBackend
// LogAttributes are the stable request attributes every record carries,
// such as the request correlation ID.
LogAttributes []Attribute
// DB is the pool of the default group. It stays the whole database for a
// configuration that declares no connection set.
DB *sql.DB
// DBDriver is the driver scheme of DSN, used to decide savepoint support.
DBDriver string
// Connections is the configured connection set. A nil value means the
// single DB above is the only database.
Connections *ConnectionSet
// Group is the group pinned by SelectDB. Empty selects the default.
Group string
// TxScope is the active transaction scope, installed by the framework only.
TxScope *TransactionScope
// Query enables development query diagnostics. A nil value leaves the
// resolved executor undecorated.
Query *QueryDiagnostics
// Trace enables the framework's own spans. A nil value creates none.
Trace *Tracing
// Metrics is the framework instrument set. A nil value records nothing, and
// it is independent of Trace: a deployment sampling one trace in ten still
// counts every request.
Metrics *Metrics
// Authentication is the verified request authentication result, finalized
// by authentication middleware before handler dispatch.
Authentication Authentication
// contains filtered or unexported fields
}
Resources is the immutable process/request state installed by pw.
func DeriveResources ¶
DeriveResources copies the capsule a request carries, so a caller can change one field and store it back.
type ResponseHeader ¶
type ResponseHeader struct{ Name, Value string }
ResponseHeader is one name and value to set on a response.
type RichSegment ¶
type RichSegment struct {
// Hole names the hole this segment fills, empty for an ordinary run.
Hole string
Lit string
Arg uint8
}
RichSegment is a Segment that may also open a hole. A hole marker carries the translated text that belongs inside the template's markup.
type SecurityHeadersConfig ¶
type SecurityHeadersConfig struct {
Enabled bool `default:"true"`
ContentTypeOptions bool `default:"true" dependon:".enabled"`
FrameOptions string `default:"deny" dependon:".enabled"`
ReferrerPolicy string `default:"strict-origin-when-cross-origin" dependon:".enabled"`
// ContentSecurityPolicy ships with DefaultContentSecurityPolicy rather than
// empty. Setting it replaces that value entirely; "off" sends no policy.
ContentSecurityPolicy string `` /* 168-byte string literal not displayed */
ContentSecurityPolicyReportOnly string `env:"-" dependon:".enabled"`
PermissionsPolicy string `env:"-" dependon:".enabled"`
HSTS HSTSConfig `dependon:".enabled"`
}
SecurityHeadersConfig contains browser security response headers.
Enabled records whether a runtime selects this middleware at all; the middleware itself always applies the configured headers.
It lives in the shared leaf because the headers a browser is sent should not depend on which transport served the response, and because everything about resolving them — the validation, the policy defaults, the HSTS string — is arithmetic over configuration with no request in it.
func DefaultSecurityHeaders ¶
func DefaultSecurityHeaders() SecurityHeadersConfig
DefaultSecurityHeaders returns the classic mode defaults.
func (SecurityHeadersConfig) Validate ¶
func (c SecurityHeadersConfig) Validate() error
Validate rejects response splitting and unsupported fixed-value policies.
type Segment ¶
type Segment struct {
// Lit is the literal run, used when Arg is zero.
Lit string
// Arg is the one-based argument position this segment renders, zero for a
// literal. It is one-based so the zero value is a literal rather than the
// first argument.
Arg uint8
}
Segment is one piece of a generated message row: either a literal run of the translation, or the position an argument fills.
The table is data and the renderer below is code, which is what makes adding a locale cost no generated code at all — only another row. See .knowledge decision:message-code-shape.
type Signal ¶
Signal is one named instruction and its encoded payload.
func AsSignal ¶
AsSignal reports whether err is a signal, and returns it. It is what a live loop classifies with, and it is exported because an application wrapping a source of its own needs the same test.
func NamedSignal ¶
NamedSignal builds a signal with no payload.
func NewRawSignal ¶
NewRawSignal builds a signal from a payload that is already encoded JSON.
Nothing validates those bytes. A caller passing something that is not one JSON value produces a record a client cannot parse, which is why NewSignal is the ordinary path.
func NewSignal ¶
func NewSignal[T SignalPayload](name string, payload T) Signal
NewSignal builds a signal carrying an encoded payload.
The payload is encoded here, at the call site, so the value is immutable once yielded and the runtime holds bytes rather than something it would have to reflect on to write. A nil payload is legal and means an instruction with no arguments.
type SignalPayload ¶
type SignalPayload = htmlbind.SignalPayload
SignalPayload is what a signal carries. Generated encoders satisfy it, which is what keeps a payload encoded by the same codec as every other typed value this framework sends.
type Sink ¶
Sink receives records that passed the severity filter. Emitting must not panic and must not block the request goroutine for longer than a bounded enqueue: a logger that can stop a handler is worse than a missing record.
func NewOtelSink ¶
NewOtelSink returns a sink emitting through logger. A nil logger yields a nil Sink for the same reason NewSlogSink does.
func NewSlogSink ¶
NewSlogSink returns a sink writing to handler. A nil handler yields a nil Sink, which NewLogBackend drops.
The return type is the interface rather than *SlogSink on purpose: a typed nil pointer stored in an interface is not nil, so returning the concrete type would make "no destination configured" silently look like a destination.
type SlogSink ¶
type SlogSink struct {
// contains filtered or unexported fields
}
SlogSink writes records through a slog.Handler.
slog owns the encoding rather than a private formatter because it already ships the JSON and text forms this framework needs, an application can substitute any handler it already uses, and the result stays readable on TinyGo, where slog works apart from source locations.
type Slot ¶
type Slot int
Slot orders every frame of the request chain. A smaller slot runs earlier, which is to say outermost, so a guard always observes the session and authentication state established before it.
Framework frames sit at multiples of ten, BASIC style, so a middleware can be inserted between any two by picking a number in the gap: SlotAccessLog - 5 runs after the request ID is minted and before the access log times the request.
The numbers live here rather than in either runtime because the order is the part that must not differ. A chain whose frames run in a different order on the second transport is a different application: a guard that ran after the session on one and before it on the other would authorize differently, and nothing about the response would say so.
const ( // SlotMetrics records the http.server instruments of one request. // // It is outside the tracing frame rather than inside it, so that a duration // counted here covers everything a client waited for including the span the // next frame opens, and so that the two are installed independently: a // deployment sampling almost no traces still counts every request. SlotMetrics Slot = 5 // SlotTracing opens the request root span. The frame is installed only // when tracing has somewhere to export. SlotTracing Slot = 10 // SlotResources injects the logger, configuration, and database clients. SlotResources Slot = 20 // SlotClientAddress records the caller every downstream bound counts. SlotClientAddress Slot = 25 // SlotRequestID mints or accepts the correlation identifier. SlotRequestID Slot = 30 // SlotAccessLog times the request. SlotAccessLog Slot = 40 // SlotRecover converts a panic into a response. SlotRecover Slot = 50 // SlotSecurityHeaders sets the browser policy headers, answers a CORS // preflight, and marks a cross-origin response. // // It sits above every frame that can refuse because of the marking rather // than because of the headers. A response a browser will not hand to script // is a status nobody can read, so the frame that marks it has to have run // before the 429, the 413, the 401, the 403 and the 500 that frames below // write — and w.Header() is one map for the whole chain, so setting the // headers here puts them on every one of those. // // It was at 60 while it only set headers. The move gained the refusals // written between the two numbers, which is the process rate limit's 429 // and the 503 beside it. SlotSecurityHeaders Slot = 52 // SlotRateLimitProcess bounds work this process is already doing. SlotRateLimitProcess Slot = 55 // SlotRequestTimeout bounds how long a request may take. SlotRequestTimeout Slot = 70 // SlotMaxRequestBody refuses an oversized body. SlotMaxRequestBody Slot = 80 // SlotPublicAssets answers static files before anything authenticates. SlotPublicAssets Slot = 90 // SlotOperational answers the framework assets and the two probes. SlotOperational Slot = 100 // SlotStorage opens request-scoped storage. SlotStorage Slot = 110 // SlotSession resolves the session. SlotSession Slot = 120 // SlotAuthentication verifies the caller. SlotAuthentication Slot = 130 // SlotRateLimit bounds a caller's requests. SlotRateLimit Slot = 135 // SlotCSRF checks the cross-site request forgery defence. SlotCSRF Slot = 140 // SlotGuard authorizes. SlotGuard Slot = 150 // SlotAPIDoc answers the OpenAPI document and its UI beneath the guard. SlotAPIDoc Slot = 160 )
type SocketHandshake ¶
type SocketHandshake struct {
// TLS reports a direct TLS connection, which outranks any header.
TLS bool
// Host is the host the request was addressed to.
Host string
// RemoteAddress is the peer, which decides whether the forwarded header
// below is evidence or noise.
RemoteAddress string
// ForwardedProto is X-Forwarded-Proto, read only from a declared proxy.
ForwardedProto string
}
SocketHandshake is what an upgrade request tells the origin check.
type Tracing ¶
type Tracing struct {
// Render opens a span for one HTML response, with the initial build as its
// first child.
Render bool
// Boundary opens a span per settled await boundary and per live delivery.
// It depends on Render, because a boundary span with no render span to sit
// under would attach each fragment straight to the request root.
Boundary bool
// Database opens a client span per executed statement.
Database bool
// Statement puts the statement text on that span. Bind values never reach
// it: policy:query-log-safety keeps row data in the log record, which the
// span id correlates.
Statement bool
// MaxSQLLength bounds the statement text on a span. It is the bound query
// diagnostics already declares, so one setting bounds both surfaces.
MaxSQLLength int
}
Tracing is the resolved framework span policy installed by pw.
A nil value creates no framework span at all, which is what an application exporting nothing pays for the feature: one nil comparison per render and per statement, and no span object anywhere. It is a resolved policy rather than the configuration struct for the same reason QueryDiagnostics is — the auto/on/off vocabulary and the environment are read once at startup, not once per request.
func TracePolicy ¶
TracePolicy returns the framework span policy of ctx, or nil when no framework span should be created. It is one context lookup, the same one every other resource accessor makes.
type TransactionScope ¶
type TransactionScope struct {
// contains filtered or unexported fields
}
TransactionScope owns one open transaction and the savepoint stack nested inside it. The transaction is a *sql.Tx on a database/sql connection and a native transaction on one that bypasses it; the savepoint stack runs the same statements on either. The framework creates a scope; applications only observe it indirectly through Transaction.
func NewTransactionScope ¶
func NewTransactionScope(db *sql.DB, driver string) *TransactionScope
NewTransactionScope prepares an inactive scope over db. Begin activates it.
func (*TransactionScope) Active ¶
func (scope *TransactionScope) Active() bool
Active reports whether a depth 0 transaction is open.
func (*TransactionScope) ActiveExecutor ¶
func (scope *TransactionScope) ActiveExecutor() sqlbind.SQLExecutor
ActiveExecutor returns the statement surface of the open transaction — a *sql.Tx or the native transaction — or nil when the scope is inactive.
It exists beside Tx for test tooling that must run statements inside the same transaction as the requests under test, whichever kind of pool backs the connection. Application code uses Transaction instead.
func (*TransactionScope) Begin ¶
Begin opens the depth 0 transaction of an inactive scope.
A nil options on a read-only connection begins a read-only transaction, which is the one enforcement the database itself can apply today.
func (*TransactionScope) Commit ¶
func (scope *TransactionScope) Commit() error
Commit commits the depth 0 transaction. A scope marked failed by a savepoint operation is rolled back instead.
The native commit is sent on the wire without a caller context, because the owner that could carry one may already hold a cancelled context, and a commit must not be abandoned by the cancellation that ended the request.
func (*TransactionScope) Group ¶
func (scope *TransactionScope) Group() string
Group is the connection group the scope is bound to.
func (*TransactionScope) ReadOnly ¶
func (scope *TransactionScope) ReadOnly() bool
ReadOnly reports whether the scope runs on a read-only connection.
func (*TransactionScope) Rollback ¶
func (scope *TransactionScope) Rollback() error
Rollback rolls back the depth 0 transaction. Rolling back an inactive scope is not an error, so owners can defer it unconditionally.
func (*TransactionScope) Tx ¶
func (scope *TransactionScope) Tx() *sql.Tx
Tx returns the depth 0 transaction, or nil when the scope is inactive or runs on a native connection, whose transaction is no *sql.Tx.
It exists for test tooling that must run statements inside the same transaction as the requests under test, such as dataset seeding and database assertions. Application code uses Transaction instead.
type UpdateFailure ¶
type UpdateFailure = htmlupdate.Failure
The partial-update types are the module's shared ones. htmlupdate and its fasthttp sibling both alias a single declaration of each, so naming them through either package names one type — and one registry therefore serves both runtimes, which is what this file exists to make true.
They are reached through htmlupdate rather than the fasthttp package because this leaf already imports net/http for status constants, where importing the other would put the fasthttp fork into every project that never opens a socket with it.
type UpdateRegion ¶
type UpdateRegion = htmlupdate.Update
The partial-update types are the module's shared ones. htmlupdate and its fasthttp sibling both alias a single declaration of each, so naming them through either package names one type — and one registry therefore serves both runtimes, which is what this file exists to make true.
They are reached through htmlupdate rather than the fasthttp package because this leaf already imports net/http for status constants, where importing the other would put the fasthttp fork into every project that never opens a socket with it.
type UpdateRegistry ¶
type UpdateRegistry = htmlupdate.Registry
The partial-update types are the module's shared ones. htmlupdate and its fasthttp sibling both alias a single declaration of each, so naming them through either package names one type — and one registry therefore serves both runtimes, which is what this file exists to make true.
They are reached through htmlupdate rather than the fasthttp package because this leaf already imports net/http for status constants, where importing the other would put the fasthttp fork into every project that never opens a socket with it.
func ReloadableRegistry ¶
func ReloadableRegistry() *UpdateRegistry
ReloadableRegistry returns the published set, or nil where nothing published one, which is what tells a caller there is no redraw endpoint to serve.
func ResetReloadableForTest ¶
func ResetReloadableForTest() (*UpdateRegistry, int, error)
ResetReloadableForTest restores the registry to a known state and returns what it replaced, so a test that registers can put back what it found.
type UpdateReloadable ¶
type UpdateReloadable = htmlupdate.Reloadable
The partial-update types are the module's shared ones. htmlupdate and its fasthttp sibling both alias a single declaration of each, so naming them through either package names one type — and one registry therefore serves both runtimes, which is what this file exists to make true.
They are reached through htmlupdate rather than the fasthttp package because this leaf already imports net/http for status constants, where importing the other would put the fasthttp fork into every project that never opens a socket with it.
type UpdateSettings ¶
type UpdateSettings struct {
// Enabled is the update surface: navigation deltas, redraws, and action
// responses. It gates those and nothing else.
Enabled bool
// Live is the subscription that keeps a page updating after its document is
// complete. It is a separate switch because it answers a separate request,
// and gating it on the one above would turn a project that asked only for
// live rendering into one that got none — which is how the second transport
// answered a subscription with a whole document.
Live bool
ValidatorKey string
HeaderPrefix string
DataAttributePrefix string
GlobalName string
PathPrefix string
BuildID string
MaxManifestBytes int
CSRFHeaderName string
CallerOwnsRuntime bool
// AsyncTimeout bounds one await boundary and AsyncConcurrency bounds the
// boundary work running at once. They travel with the update settings
// because a streamed answer renders the same chain a document does, and a
// runtime that could not read them would settle boundaries on terms the
// deployment did not choose.
AsyncTimeout time.Duration
AsyncConcurrency int
// The live bounds, which the delivery loop reads on either transport. They
// travel here for the reason the async bounds do: the loop is shared, so
// the values it consults have to be.
LiveMaxResponses int
LiveMaxBoundaries int
// LiveMaxSignalBytes bounds the signal payloads one response may write. It
// travels here because a signal is written by both loops, and a budget one
// backend enforced and the other did not would be a bound an application
// escapes by changing transports.
LiveMaxSignalBytes int
LiveMaxDuration time.Duration
LiveDurationJitter int
LiveIdleTimeout time.Duration
}
UpdateSettings is the resolved partial-update configuration in a form that names no transport, so one resolution serves both runtimes.
It is values rather than a composed options struct because the two runtimes each declare their own, with the same fields and different method receivers. Copying the fields twice is cheaper than a conversion whose correctness would depend on two struct declarations staying identical.
func ResolvedUpdateSettings ¶
func ResolvedUpdateSettings() (UpdateSettings, bool)
ResolvedUpdateSettings returns the published configuration, and whether anything published one. A runtime that finds none has nothing to answer an update request with and says so rather than guessing at defaults.
func (UpdateSettings) RenderOptions ¶
func (s UpdateSettings) RenderOptions(ctx context.Context) []htmlbind.Option
RenderOptions is the option set a streamed answer renders with, built from the published settings so both runtimes bound a boundary the same way.
It is the shared subset rather than everything pw assembles: the cache store and its scope are resolved from the request context by whichever runtime owns that resolution, and a caller adds them after these.
type ValueStore ¶
A ValueStore is a request value that carries its own state instead of being replaced by a derived copy for each frame of the chain.
net/http middleware derives a context and hands it to the next handler, so a frame changes what the rest of the chain sees by returning something new. fasthttp has one request value, which is itself the context, and a frame changes what the chain sees by writing into it. Only the write side differs: that value answers Value from the same store, so every reader in this package works on both transports unchanged.
It is declared structurally rather than by naming the type, so this leaf stays free of the fasthttp fork the way it stays free of anything else a net/http project should not have to build.
Source Files
¶
- accept.go
- authentication.go
- botsettings.go
- cache.go
- cache_go127.go
- cachestores.go
- chain.go
- chainsettings.go
- clientaddress.go
- configlookup.go
- connection.go
- context.go
- cors.go
- csrf.go
- csrfconfig.go
- htmlregistry.go
- httpsemconv.go
- live.go
- locale.go
- localemiddleware.go
- localeroute.go
- logcapture.go
- logger.go
- logsink.go
- mapproblem.go
- message.go
- metrics.go
- metricsobserved.go
- pageactions.go
- problem.go
- publicassets.go
- querylog.go
- queryrepro.go
- ratelimitconfig.go
- redirecterror.go
- securityheaders.go
- signal.go
- socketorigin.go
- tracing.go
- transaction.go
- update.go
- updateidentity.go