# Handlers & helpers

Build with HyperChi’s handler, rendering, response, and collection APIs. Keep standard HTTP access available for the parts that actually need it.

## Choose the handler for the job {#overview}

- `app.Get`, `app.Post`, `app.Put`, `app.Patch`, and `app.Delete` take a `func(c *hyperchi.Context) error`. They register the HTTP method and pass returned errors to the application error handler. Optional middleware for that route follows the handler.
- The handler chooses the response. `c.View` renders the full page, or only the template for an htmx partial request. `c.Page` always renders the page and `c.Fragment` never adds a layout.
- `app.Handle(method, pattern, handler)` and `app.Mount` integrate existing `net/http` handlers or routers. They are interoperability APIs, not necessary boilerplate for a new endpoint.

The runnable [integration examples](/docs/integrations), the main showcase, and these documentation pages all use context handlers.

## A complete context handler {#example}

With the core `hyperchi` package imported and `app` initialized:

```go
app.Get("/hello/{name}", func(c *hyperchi.Context) error {
    greeting := c.QueryDefault("greeting", "Hello")
    return c.JSON(hyperchi.H{
        "message": greeting + ", " + c.Param("name"),
    })
})
```

A request to `/hello/Morgan?greeting=Welcome` returns a JSON object containing `"message": "Welcome, Morgan"`. You do not need to set a JSON content type or construct an encoder.

### Read inputs without repeating request plumbing

```go
name := c.Param("name")
query := c.Query("q")
terrain := c.QueryDefault("terrain", "all")
tags := c.Queries("tag")
isHTMX := c.IsHTMX()
```

These helpers read inputs; they do not validate or authorize them. Keep allowlists, schemas, and permission checks explicit. `c.Ctx()` returns the request context for cancellation, and `c.Request()` remains available for HTTP features without a framework helper.

To decode and check a whole form or JSON body, describe it as a struct and call `hyperchi.Bind`:

```go
type Signup struct {
    Email string `form:"email" validate:"required,email"`
}

in, err := hyperchi.Bind[Signup](c)
if err != nil {
    return err // 400 for an unreadable body, 422 when a check fails
}
```

### Render through HyperChi

```go
helpers.Must(app.LoadTemplates("templates/*.tmpl"))

app.Get("/trips", func(c *hyperchi.Context) error {
    return c.View("trips.tmpl", catalog)
})
app.Get("/trip-cards", func(c *hyperchi.Context) error {
    return c.Fragment("trip-cards.tmpl", catalog)
})
```

For a typed slice, the template reads the data through `.Result`:

```html
{{range .Result}}
  <article><h2>{{.Name}}</h2></article>
{{end}}
```

For top-level template keys, pass `hyperchi.H`, `helpers.JSON`, or `map[string]any`. All three merge keys into the template context, so `hyperchi.H{"Title": "Trips"}` is available as `.Title`. Strings still receive HTML escaping. A slice stays under `.Result`; a struct is under `.Result` too, and its exported fields are also top-level keys, so `c.Fragment("trip-card", trip)` renders a template that reads `{{.Name}}`. `c.Layout("admin").Page(...)` selects an explicit layout. Values set once at startup with `app.SetGlobal`, such as a site name, reach every template; handler data wins over a global, and a global wins over a query parameter. These helpers supply the registered [template filters](/docs/filters) and request context.

### Transform typed collections

Import `github.com/regiellis/hyperchi/hyperchi/helpers` for generic collection operations. Go’s method rules mean the typed helpers are package functions: `helpers.FromSlice` wraps an existing slice, and `helpers.NewSlice` builds one from values.

```go
trips := helpers.FromSlice(catalog).Filter(func(trip Trip) bool {
    return trip.Terrain == "coast"
}).OrEmpty()
count := trips.Len()
first, found := trips.First()
```

`Filter` produces a new result and preserves the element type. Chain `OrEmpty()` when JSON must contain `[]` instead of `null`. It normalizes a nil result without copying a non-nil slice. Use `helpers.Map` when the output type differs, and `helpers.Reduce` to aggregate values. See the [trip finder recipe](/docs/data) for these helpers in an actual handler.

### Build a response with status or htmx headers

Chain `c.Status` before a response method to change the status, and use `c.HX()` to set htmx response headers:

```go
return c.Status(http.StatusBadRequest).
    JSON(hyperchi.H{"error": "Unknown terrain"})
```

```go
c.HX().Retarget("#result").Reswap("innerHTML")
return c.HTML("<p>Ready.</p>")
```

The status example also imports `net/http` for its named constant. `c.HX()` also supports `Trigger`, `PushURL`, `Location`, and `Refresh`, and a chain can end in a response method: `View`, `Fragment`, `HTML`, `JSON`, or `Empty`. `c.Redirect` sends `HX-Redirect` to htmx and a 303 to any other client. Writing literal HTML is fine for application-owned markup; render visitor values through an escaping template.

### Return errors at request time

Use `helpers.Must` for startup requirements such as loading templates. Inside a handler, return `hyperchi.NotFound`, `hyperchi.BadRequest`, or `hyperchi.NewHTTPError` to answer with that status. HyperChi renders it for the client: JSON for `Accept: application/json`, an error fragment for htmx, and an error page for a browser. Any other error is a 500 whose text stays in the log. An endpoint that promises JSON to every client sets the status itself, as in the JSON 400 above.

```go
trip, ok := trips[c.Param("id")]
if !ok {
    return hyperchi.NotFound("No such trip")
}
return c.View("trip", trip)
```

`c.JSON` and the template methods finish rendering before committing the status or body. Encoding and template errors can therefore reach the application error handler. A network write can still fail after committing the response; do not attempt a second response then. External renderers also need buffering. The templ and Jet integrations supply that boundary. Register them once and use `c.RenderWith`; the manager handles buffering and HTML response headers.

`c.RenderWith` works in any handler, so a page and its fragment can share one adapter. The integration pages deliberately use explicit HTML and JSON endpoints, so each handler keeps its contract direct.

### Confirm a post with a flash message {#flash}

After a form post, redirect, and carry the confirmation to the next page with `c.Flash`:

```go
app.Post("/items", func(c *hyperchi.Context) error {
    in, err := hyperchi.Bind[NewItem](c)
    if err != nil {
        return err
    }
    if err := store.Add(c.Ctx(), in.Name); err != nil {
        return err
    }
    if err := c.Flash("success", "Item added."); err != nil {
        return err
    }
    return c.Redirect("/items")
})
```

The page the redirect lands on receives the messages as `.flashes` when it renders with `c.View`, `c.Page`, or `c.Fragment`:

```html
{{range .flashes}}<p class="flash flash-{{.Kind}}" role="status">{{.Message}}</p>{{end}}
```

Rendering reads the messages once, so a reload does not show them again. `c.Flashes()` reads them in Go. The messages travel in a signed cookie, or in the session when the route runs behind `app.SessionMiddleware()`. At most ten are kept, each up to 1024 bytes. The kind is your own label, usually turned into a class. This request's own render does not show a flash; pass the message in the template data for that.

### Sign a cookie {#signed-cookies}

`c.SetSignedCookie` signs a value so the browser can hold it but not change it, and `c.SignedCookie` checks the signature and the age:

```go
err := c.SetSignedCookie(&http.Cookie{Name: "theme", Value: "dark", MaxAge: 365 * 24 * 60 * 60})
if err != nil {
    return err
}

theme, err := c.SignedCookie("theme", 365*24*time.Hour)
if err != nil {
    theme = "light" // missing, tampered with, or older than a year
}
```

The signature (HMAC-SHA256) covers the cookie name, the value, and when it was signed, so a value cannot be edited, moved to another cookie, or replayed after `maxAge`. Test the error with `errors.Is` against `hyperchi.ErrCookieMissing`, `ErrCookieInvalid`, or `ErrCookieExpired`. The value is signed, not encrypted: anyone holding the cookie can read it, so never put a secret in it. The cookie is sent `HttpOnly`, `SameSite=Lax` unless you set another mode, and `Secure` in production.

Both signed cookies and flash messages use the application secret key. Set `SECRET_KEY` (or `config.Security.SecretKey`) to at least 32 bytes in production, for example the output of `openssl rand -base64 32`; without it they fail with `hyperchi.ErrNoSecretKey`. Development signs with a random key, so signed values do not survive a restart. To rotate the key, move the old one to `SECRET_KEY_PREVIOUS` (comma-separated): values it signed keep verifying, and new ones use the new key.

### Split a long form into steps {#wizards}

`MultiStepForm` serves a form over several pages and keeps the answers in the visitor's session until the last step. It needs sessions, and it needs them before the routes are registered:

```go
helpers.Must(app.UseKVStoreMemoryAdvanced("sessions"))
helpers.Must(app.UseSessions(hyperchi.KVSessionConfig{Backend: "sessions"}))
app.Use(app.SessionMiddleware())

pm := app.Extensions().GetPatternManager()
pm.MultiStepForm("/signup", patterns.MultiStepFormConfig{
    SuccessURL: "/signup/done",
    Steps: []patterns.FormStep{
        {
            Name: "account", Title: "Your account", Template: "signup-account",
            Fields: []string{"email"},
            Validate: patterns.SchemaStep(validator.NewSchema().
                Field("email", validator.String().Required().Email())),
        },
        {Name: "plan", Title: "Choose a plan", Template: "signup-plan", Fields: []string{"plan"}},
    },
    Complete: func(r *http.Request, answers url.Values) error {
        return accounts.Create(r.Context(), answers.Get("email"), answers.Get("plan"))
    },
})
```

This also imports `net/http`, `net/url`, and the `patterns` and `validator` packages, and assumes your `accounts` store. Each step's template posts its number in a `step` field and reads `.values` (the answers so far) and `.errors` (messages per field):

```html
{{define "signup-account"}}
<form hx-post="/signup" hx-target="this" hx-swap="outerHTML">
  <input type="hidden" name="step" value="{{.step}}">
  <label>Email <input name="email" type="email" value="{{.values.Get "email"}}"></label>
  {{range index .errors "email"}}<p class="field-error">{{.}}</p>{{end}}
  <button>Continue</button>
</form>
{{end}}
```

- A `GET /signup?step=2` renders that step with the stored answers, so going back shows what the visitor entered. A step cannot be reached before the ones ahead of it are answered.
- A post keeps only the step's `Fields`, then runs its `Validate`. A rejected step renders again with status 422, its messages, and the submitted values, and nothing is stored. An accepted step is stored, and the response is the next step, with its URL pushed.
- After the last step, `Complete` receives every answer, the stored answers are cleared, and the visitor is redirected to `SuccessURL`. If `Complete` returns an error, the answers are kept for another try.
- `patterns.SchemaStep` adapts a `validator.Schema`; any `func(url.Values) map[string][]string` works as `Validate`.
- Without session middleware every wizard request fails with a 500 whose cause is `patterns.ErrNoWizardStore`.

### Store an upload {#uploads}

Validate an upload, then store it with `SaveAll`, which names each file itself:

```go
uploads := security.NewUploadValidator(security.DefaultUploadConfig())

app.Post("/photos", func(c *hyperchi.Context) error {
    result := uploads.ValidateUpload(c.Request())
    if !result.Valid {
        return hyperchi.Unprocessable("That file was not accepted.", map[string][]string{"file": result.Errors})
    }
    saved, err := result.SaveAll("data/uploads", security.SaveOptions{})
    if err != nil {
        return err
    }
    return c.View("photo-saved", hyperchi.H{"files": saved})
})
```

A stored file's name is 128 random bits plus an extension chosen from the type sniffed from its content, never from the visitor's file name or declared type. Files are written through an `os.Root`, so nothing lands outside the folder, and an existing file is never overwritten. `SaveAll` stores every file or none. HTML, SVG, XML, and JavaScript are refused even when listed. `security.DefaultSaveExtensions()` returns the default type-to-extension map; copy it and add a type to store more. For one file, `security.SaveUpload(dir, fileHeader, opts)` does the same. Show `SavedFile.OriginalName` to the visitor, but never use it as a path.

`DefaultUploadConfig` sets `RequireAuth`, which rejects an upload unless the request context carries a verified user (`security.WithUserID`). Serve stored files with their content type and `X-Content-Type-Options: nosniff`.

### Call another service with request cancellation

Configure one HTTP client at startup. The `With*` configuration methods mutate it, so finish configuration before concurrent requests. Inside handlers, use `GetWithContext`, `PostWithContext`, `PutWithContext`, `PatchWithContext`, `DeleteWithContext`, or `PostFormWithContext` to carry cancellation and deadlines.

```go
client := helpers.NewHTTPClient().
    WithBaseURL("https://api.example.com").
    WithTimeout(5 * time.Second).
    WithJSONHeaders()

app.Get("/inventory", func(c *hyperchi.Context) error {
    res, err := client.GetWithContext(c.Ctx(), "/items")
    if err != nil { return err }
    defer res.Close()
    if !res.IsSuccess() {
        return fmt.Errorf("inventory service returned %d", res.StatusCode())
    }
    var items []Item
    if err := res.Parse(&items); err != nil { return err }
    return c.JSON(helpers.FromSlice(items).OrEmpty())
})
```

This recipe also imports `fmt` and `time` and assumes your `Item` model. HTTP 4xx and 5xx responses do not produce a transport error: check the status. `Parse`, `Text`, and `JSON` consume and close the body; defer `Close` to cover early returns. The shorter HTTP methods use a background context and the configured client timeout (30 seconds by default). Strings are sent raw; other non-nil request bodies are JSON encoded. Form requests set their own content type.

### JSON objects and reliable defaults

```go
options := helpers.NewJSON().
    Set("limit", "24").
    Set("enabled", false)
limit := options.GetIntOr("limit", 12)
enabled := options.GetBoolOr("enabled", true)
```

Defaults apply to absent or invalid values; valid zero and false values are preserved. Integer access accepts ints, float64 values (truncated), and integer strings. `GetJSON` accepts both plain maps and nested `helpers.JSON` values. `FromJSON("null")` returns an empty object. Retain the returned map when calling `Set` or `Merge` on a possibly nil object. Prefer typed structs for stable service contracts.

### Choose intentional error handling

```go
value, err := strconv.Atoi(configuredPort)
port := helpers.Default(value, err, 8080)
```

`Default` takes three arguments: value, error, and fallback. Use it only when a fallback is acceptable. Return errors from request handlers, and reserve `helpers.Must` for startup requirements. String JSON conveniences such as `helpers.ToJSON` and `JSON.ToString` return `{}` on encoding errors; use `c.JSON` or `json.Marshal` when errors must be reported.

### Use the other helpers where they fit

- `app.Helpers.Env.GetOr("PORT", "8080")` reads configuration with a fallback.
- `helpers.S(value).Trim().Lower().String()` composes string transformations.
- `app.Helpers.Logger.Info`, `Warn`, and `Error` write to the application’s logger.
- `helpers.NewJSON().Set("status", "ok")` builds a JSON object; `hyperchi.H` is convenient for a small response literal.

Typed models, `context.Context`, embedded assets, and standard HTTP middleware remain useful Go primitives. The goal is to avoid reimplementing conveniences that HyperChi already supplies, while keeping application logic clear.

## Every helper {#reference}

The complete list, grouped by what it is for: the request and response helpers on `Context`, input binding and errors from the core package, then the `helpers` package. Each entry shows its signature and what it does; open a group to browse it, or search the docs for a name.

### Request (Context) (22 entries)

`import "github.com/regiellis/hyperchi/hyperchi"`

- `Context`: `type Context struct` Context carries one request through a Handler: read the request with its accessors, then answer with one of its response methods.
- `Context.App`: `func (c *Context) App() *HyperChi` App returns the application serving this request.
- `Context.CSRFToken`: `func (c *Context) CSRFToken() string` CSRFToken returns the request's CSRF token for forms and htmx headers that post back, or "" outside Middleware.CSRF.
- `Context.Cookie`: `func (c *Context) Cookie(name string) string` Cookie returns a request cookie's value, or "" when it is not set.
- `Context.Ctx`: `func (c *Context) Ctx() context.Context` Ctx returns the request's context.Context, for cancellation and deadlines.
- `Context.FormValue`: `func (c *Context) FormValue(key string) string` FormValue returns a submitted form field, parsing the body on first use.
- `Context.Header`: `func (c *Context) Header(key string) string` Header returns a request header.
- `Context.IsHTMX`: `func (c *Context) IsHTMX() bool` IsHTMX reports whether htmx sent the request.
- `Context.Method`: `func (c *Context) Method() string` Method returns the request method.
- `Context.Param`: `func (c *Context) Param(key string) string` Param returns a route parameter, such as {id} in "/users/{id}".
- `Context.ParamInt`: `func (c *Context) ParamInt(key string) (int, error)` ParamInt returns a route parameter as an int.
- `Context.Path`: `func (c *Context) Path() string` Path returns the request URL path.
- `Context.Queries`: `func (c *Context) Queries(key string) []string` Queries returns every value of a query parameter.
- `Context.Query`: `func (c *Context) Query(key string) string` Query returns the first value of a query string parameter.
- `Context.QueryDefault`: `func (c *Context) QueryDefault(key, fallback string) string` QueryDefault returns the first query value, or fallback when it is absent or empty.
- `Context.QueryInt`: `func (c *Context) QueryInt(key string, fallback int) int` QueryInt returns a query parameter as an int, or fallback when it is absent or not an integer.
- `Context.Request`: `func (c *Context) Request() *http.Request` Request returns the underlying *http.Request.
- `Context.Response`: `func (c *Context) Response() http.ResponseWriter` Response returns the underlying http.ResponseWriter, for code that has to write the response itself (streaming, a third-party encoder).
- `Context.Session`: `func (c *Context) Session() *KVSession` Session returns the request's session, or nil when the route does not run behind SessionMiddleware (see UseSessions).
- `Context.UserID`: `func (c *Context) UserID() string` UserID returns the authenticated user's ID set by the auth middleware, or "" for an anonymous request.
- `Context.Value`: `func (c *Context) Value(key string) any` Value returns a value attached to the request with WithContext, typically by middleware.
- `HyperChi.NewContext`: `func (g *HyperChi) NewContext(w http.ResponseWriter, r *http.Request) *Context` NewContext builds a Context outside a route, for tests and for adapters that receive a plain net/http request.

### Responses (Context) (38 entries)

`import "github.com/regiellis/hyperchi/hyperchi"`

- `Context.Blob`: `func (c *Context) Blob(contentType string, body []byte) error` Blob writes body with the given content type, for responses Text and HTML do not cover (Markdown, CSV, images).
- `Context.Empty`: `func (c *Context) Empty() error` Empty sends the status (200 unless set with Status) and headers with no body, for responses whose headers are the answer.
- `Context.Fragment`: `func (c *Context) Fragment(name string, data any) error` Fragment renders a template without a layout, for htmx swaps.
- `Context.HTML`: `func (c *Context) HTML(markup template.HTML) error` HTML writes trusted HTML.
- `Context.HX`: `func (c *Context) HX() *HX` HX returns the htmx helper for this request.
- `Context.JSON`: `func (c *Context) JSON(data any) error` JSON writes data as a JSON response.
- `Context.Layout`: `func (c *Context) Layout(name string) *Context` Layout picks the layout template ("layout-<name>") for Page and View.
- `Context.NoContent`: `func (c *Context) NoContent() error` NoContent sends 204 No Content.
- `Context.Page`: `func (c *Context) Page(name string, data any) error` Page renders a template inside its layout, whatever sent the request.
- `Context.Redirect`: `func (c *Context) Redirect(url string) error` Redirect sends the client to url.
- `Context.RenderWith`: `func (c *Context) RenderWith(name string, data any) error` RenderWith renders through a named integration (see Integrations), such as a templ or Jet adapter, buffering before any header is sent.
- `Context.SetCookie`: `func (c *Context) SetCookie(cookie *http.Cookie) *Context` SetCookie adds a Set-Cookie header to the response.
- `Context.SetHeader`: `func (c *Context) SetHeader(key, value string) *Context` SetHeader sets a response header.
- `Context.Status`: `func (c *Context) Status(code int) *Context` Status sets the status code for the response this handler sends next.
- `Context.Text`: `func (c *Context) Text(s string) error` Text writes a plain-text response.
- `Context.View`: `func (c *Context) View(name string, data any) error` View renders a template the way the request needs it: the full page inside its layout for a normal or boosted navigation, and the template alone for an htmx partial request.
- `HX`: `type HX struct` HX reads htmx request headers and sets htmx response headers.
- `HX.Boosted`: `func (h *HX) Boosted() bool` Boosted reports a boosted link or form navigation (HX-Boosted).
- `HX.CurrentURL`: `func (h *HX) CurrentURL() string` CurrentURL is the page the browser was on (HX-Current-URL).
- `HX.Empty`: `func (h *HX) Empty() error` Empty finishes with an empty 200 response, for a request that only needs the htmx headers (a trigger, a refresh, a location change).
- `HX.Fragment`: `func (h *HX) Fragment(name string, data any) error` Fragment finishes with Context.Fragment.
- `HX.HTML`: `func (h *HX) HTML(markup template.HTML) error` HTML finishes with Context.HTML.
- `HX.JSON`: `func (h *HX) JSON(data any) error` JSON finishes with Context.JSON.
- `HX.Location`: `func (h *HX) Location(url string) *HX` Location navigates without a full reload (HX-Location).
- `HX.NoContent`: `func (h *HX) NoContent() error` NoContent finishes with Context.NoContent, which still sends the headers.
- `HX.Page`: `func (h *HX) Page(name string, data any) error` Page finishes with Context.Page.
- `HX.PushURL`: `func (h *HX) PushURL(url string) *HX` PushURL pushes url onto browser history (HX-Push-Url).
- `HX.Refresh`: `func (h *HX) Refresh() *HX` Refresh makes the browser reload the page (HX-Refresh).
- `HX.ReplaceURL`: `func (h *HX) ReplaceURL(url string) *HX` ReplaceURL replaces the current history entry (HX-Replace-Url).
- `HX.Reselect`: `func (h *HX) Reselect(selector string) *HX` Reselect picks part of the response to swap (HX-Reselect).
- `HX.Reswap`: `func (h *HX) Reswap(swap string) *HX` Reswap changes the swap strategy, such as "outerHTML" (HX-Reswap).
- `HX.Retarget`: `func (h *HX) Retarget(selector string) *HX` Retarget swaps the response into a different element (HX-Retarget).
- `HX.Source`: `func (h *HX) Source() string` Source is the element that issued the request, as "tag#id" (HX-Source).
- `HX.Status`: `func (h *HX) Status(code int) *HX` Status sets the response status, as Context.Status does.
- `HX.Target`: `func (h *HX) Target() string` Target identifies the element htmx will swap into, as "tagName#id" such as "div#results" (HX-Target).
- `HX.Trigger`: `func (h *HX) Trigger(event string, detail ...any) *HX` Trigger fires a client-side event after the swap.
- `HX.View`: `func (h *HX) View(name string, data any) error` View finishes with Context.View.
- `HyperChi.SetGlobal`: `func (g *HyperChi) SetGlobal(key string, value any)` SetGlobal makes a value available to every template rendered through a Context, such as a site name or the Vite dev-server URL.

### Input binding (3 entries)

`import "github.com/regiellis/hyperchi/hyperchi"`

- `Bind`: `func Bind[T any](c *Context) (T, error)` Bind decodes the request into a new T and validates it.
- `Context.Bind`: `func (c *Context) Bind(dst any) error` Bind decodes and validates the request into dst, a pointer to a struct.
- `Context.ValidateSchema`: `func (c *Context) ValidateSchema(name string) (map[string]any, error)` ValidateSchema checks the submitted form or JSON body against a schema registered on app.Schemas, returning the cleaned data, or a 422 error with per-field messages.

### Errors and statuses (18 entries)

`import "github.com/regiellis/hyperchi/hyperchi"`

- `BadRequest`: `func BadRequest(message string) *HTTPError` BadRequest answers 400.
- `Conflict`: `func Conflict(message string) *HTTPError` Conflict answers 409.
- `ErrorFunc`: `type ErrorFunc func(c *Context, err error)` ErrorFunc renders an error a Handler returned.
- `Errorf`: `func Errorf(code int, format string, args ...any) *HTTPError` Errorf returns an HTTPError with a formatted message.
- `FieldErrors`: `func FieldErrors(err error) map[string][]string` FieldErrors returns the per-field messages of a validation error from Bind or BindMessage, or nil for any other error.
- `Forbidden`: `func Forbidden(message string) *HTTPError` Forbidden answers 403.
- `HTTPError`: `type HTTPError struct` HTTPError is an error with an HTTP status.
- `HTTPError.Error`: `func (e *HTTPError) Error() string` Error reports the status code, message, and wrapped cause.
- `HTTPError.Unwrap`: `func (e *HTTPError) Unwrap() error` Unwrap returns the wrapped cause, for errors.Is and errors.As.
- `HTTPError.Wrap`: `func (e *HTTPError) Wrap(err error) *HTTPError` Wrap attaches the underlying cause for logging.
- `HyperChi.DefaultError`: `func (g *HyperChi) DefaultError(c *Context, err error)` DefaultError renders err for the client that asked:
- `HyperChi.OnError`: `func (g *HyperChi) OnError(fn ErrorFunc)` OnError replaces the default error handler.
- `HyperChi.RespondStatus`: `func (g *HyperChi) RespondStatus(w http.ResponseWriter, r *http.Request, code int, err error)` RespondStatus answers a refusal from net/http middleware (a rate limiter, an auth check, an optional module) through the app's error pipeline, so it renders like every framework denial: OnError or DefaultError, with no-store headers.
- `JSONErrors`: `func JSONErrors(next http.Handler) http.Handler` JSONErrors makes the routes it wraps answer every error as JSON, whatever the request's Accept header says, for API routes whose clients never ask for HTML.
- `NewHTTPError`: `func NewHTTPError(code int, message string) *HTTPError` NewHTTPError returns an error that answers with code and message.
- `NotFound`: `func NotFound(message string) *HTTPError` NotFound answers 404.
- `Unauthorized`: `func Unauthorized(message string) *HTTPError` Unauthorized answers 401.
- `Unprocessable`: `func Unprocessable(message string, fields map[string][]string) *HTTPError` Unprocessable answers 422 with per-field messages, the shape Bind returns when validation fails.

### Strings (67 entries)

`import "github.com/regiellis/hyperchi/hyperchi/helpers"`

- `CamelCase`: `func CamelCase(s string) string` CamelCase converts s to camelCase: "hello world" and "hello_world" become "helloWorld".
- `Dedent`: `func Dedent(s string) string` Dedent removes the leading whitespace that every non-blank line shares.
- `Indent`: `func Indent(s string, indent string) string` Indent prefixes each non-blank line of s with indent.
- `Join`: `func Join(sep string, parts ...string) string` Join joins parts with sep.
- `JoinLines`: `func JoinLines(lines ...string) string` JoinLines joins lines with "\n".
- `JoinSpaces`: `func JoinSpaces(parts ...string) string` JoinSpaces joins parts with single spaces.
- `KebabCase`: `func KebabCase(s string) string` KebabCase converts s to kebab-case, splitting words as CamelCase does.
- `PascalCase`: `func PascalCase(s string) string` PascalCase converts s to PascalCase, splitting words as CamelCase does.
- `RandomString`: `func RandomString(length int) string` RandomString returns length random characters from a-z, A-Z, and 0-9.
- `S`: `func S(s string) Str` S converts s to a Str.
- `Slugify`: `func Slugify(s string) string` Slugify creates a URL-friendly slug of lowercase ASCII letters, digits, and hyphens: "Crème Brûlée à la carte" becomes "creme-brulee-a-la-carte".
- `SnakeCase`: `func SnakeCase(s string) string` SnakeCase converts s to snake_case, splitting words as CamelCase does: "userID" becomes "user_id" and "HTTPServer" becomes "http_server".
- `Str`: `type Str string` Str is a string with chainable methods: S(" Hi ").Trim().Lower().
- `Str.Capitalize`: `func (s Str) Capitalize() Str` Capitalize uppercases the first rune and leaves the rest unchanged.
- `Str.Center`: `func (s Str) Center(width int, fill ...string) Str` Center centers the string in a field width runes wide.
- `Str.CollapseWhitespace`: `func (s Str) CollapseWhitespace() Str` CollapseWhitespace trims s and replaces each run of whitespace with one space.
- `Str.Contains`: `func (s Str) Contains(substr string) bool` Contains reports whether substr is in s.
- `Str.ContainsAny`: `func (s Str) ContainsAny(chars string) bool` ContainsAny reports whether any rune in chars is in s.
- `Str.Count`: `func (s Str) Count(substr string) int` Count returns the number of non-overlapping occurrences of substr.
- `Str.EndsWith`: `func (s Str) EndsWith(suffix string) bool` EndsWith reports whether s ends with suffix.
- `Str.FindAll`: `func (s Str) FindAll(pattern string) ([]string, error)` FindAll returns every match of pattern, or the compile error for an invalid pattern.
- `Str.Index`: `func (s Str) Index(substr string) int` Index returns the rune offset of the first occurrence of substr, or -1.
- `Str.IsAlpha`: `func (s Str) IsAlpha() bool` IsAlpha reports whether s is non-empty and every rune is a letter.
- `Str.IsAlphaNumeric`: `func (s Str) IsAlphaNumeric() bool` IsAlphaNumeric reports whether s is non-empty and every rune is a letter or digit.
- `Str.IsBlank`: `func (s Str) IsBlank() bool` IsBlank reports whether s is empty or only whitespace.
- `Str.IsDigits`: `func (s Str) IsDigits() bool` IsDigits reports whether s is non-empty and every rune is a Unicode digit.
- `Str.IsEmpty`: `func (s Str) IsEmpty() bool` IsEmpty reports whether s is "".
- `Str.IsNotBlank`: `func (s Str) IsNotBlank() bool` IsNotBlank reports whether s has a non-whitespace character.
- `Str.IsNotEmpty`: `func (s Str) IsNotEmpty() bool` IsNotEmpty reports whether s is not "".
- `Str.Join`: `func (s Str) Join(parts []string) Str` Join joins parts with s as the separator.
- `Str.LastIndex`: `func (s Str) LastIndex(substr string) int` LastIndex returns the rune offset of the last occurrence of substr, or -1.
- `Str.Left`: `func (s Str) Left(n int) Str` Left returns the leftmost n runes.
- `Str.Len`: `func (s Str) Len() int` Len returns the number of runes in the string.
- `Str.Lower`: `func (s Str) Lower() Str` Lower returns s in lowercase.
- `Str.Matches`: `func (s Str) Matches(pattern string) (bool, error)` Matches reports whether the string contains a match of the regular expression pattern.
- `Str.PadLeft`: `func (s Str) PadLeft(width int, fill ...string) Str` PadLeft pads the string on the left to width runes, filling with the first rune of fill (a space by default).
- `Str.PadRight`: `func (s Str) PadRight(width int, fill ...string) Str` PadRight pads the string on the right to width runes, filling with the first rune of fill (a space by default).
- `Str.RegexReplace`: `func (s Str) RegexReplace(pattern, replacement string) (Str, error)` RegexReplace replaces every match of pattern, or returns the compile error for an invalid pattern.
- `Str.RemoveWhitespace`: `func (s Str) RemoveWhitespace() Str` RemoveWhitespace removes every whitespace rune.
- `Str.Repeat`: `func (s Str) Repeat(n int) Str` Repeat repeats the string n times.
- `Str.Replace`: `func (s Str) Replace(old, new string) Str` Replace replaces every occurrence of old with new.
- `Str.ReplaceN`: `func (s Str) ReplaceN(old, new string, n int) Str` ReplaceN replaces the first n occurrences of old with new.
- `Str.Reverse`: `func (s Str) Reverse() Str` Reverse reverses s rune by rune.
- `Str.Right`: `func (s Str) Right(n int) Str` Right returns the rightmost n runes.
- `Str.Slice`: `func (s Str) Slice(start, end int) Str` Slice returns the runes from start up to end.
- `Str.Split`: `func (s Str) Split(sep string) []string` Split splits s around each sep (see strings.Split).
- `Str.SplitLines`: `func (s Str) SplitLines() []string` SplitLines splits s at each "\n".
- `Str.SplitN`: `func (s Str) SplitN(sep string, n int) []string` SplitN splits s around sep into at most n parts (see strings.SplitN).
- `Str.SplitWhitespace`: `func (s Str) SplitWhitespace() []string` SplitWhitespace splits s around runs of whitespace (see strings.Fields).
- `Str.StartsWith`: `func (s Str) StartsWith(prefix string) bool` StartsWith reports whether s begins with prefix.
- `Str.String`: `func (s Str) String() string` String returns s as a plain string.
- `Str.Title`: `func (s Str) Title() Str` Title uppercases the first letter of each word.
- `Str.ToBool`: `func (s Str) ToBool() bool` ToBool reports whether the trimmed s is "true", "1", "yes", or "on", ignoring case.
- `Str.ToFloat`: `func (s Str) ToFloat() (float64, error)` ToFloat parses the string as a float64.
- `Str.ToFloatOr`: `func (s Str) ToFloatOr(defaultValue float64) float64` ToFloatOr parses the string as a float64, returning defaultValue when it does not parse.
- `Str.ToInt`: `func (s Str) ToInt() (int, error)` ToInt parses the string as a base-10 int.
- `Str.ToIntOr`: `func (s Str) ToIntOr(defaultValue int) int` ToIntOr parses the string as a base-10 int, returning defaultValue when it does not parse.
- `Str.Trim`: `func (s Str) Trim() Str` Trim removes leading and trailing whitespace.
- `Str.TrimLeft`: `func (s Str) TrimLeft() Str` TrimLeft removes leading whitespace.
- `Str.TrimPrefix`: `func (s Str) TrimPrefix(prefix string) Str` TrimPrefix removes prefix from the start of s if it is there.
- `Str.TrimRight`: `func (s Str) TrimRight() Str` TrimRight removes trailing whitespace.
- `Str.TrimSuffix`: `func (s Str) TrimSuffix(suffix string) Str` TrimSuffix removes suffix from the end of s if it is there.
- `Str.Upper`: `func (s Str) Upper() Str` Upper returns s in uppercase.
- `Template`: `func Template(template string, vars map[string]string) string` Template replaces each {{key}} in template with vars[key].
- `Truncate`: `func Truncate(s string, maxLen int, suffix ...string) string` Truncate shortens s to at most maxLen runes, ending in suffix ("..." by default) when it cuts.
- `WordCount`: `func WordCount(s string) int` WordCount returns the number of whitespace-separated words in s.
- `Words`: `func Words(s string) []string` Words splits s around runs of whitespace.

### Collections (66 entries)

`import "github.com/regiellis/hyperchi/hyperchi/helpers"`

- `FlatMap`: `func FlatMap[T, U any](slice Slice[T], fn func(T) Slice[U]) Slice[U]` FlatMap calls fn on each element and concatenates the results.
- `Flatten`: `func Flatten[T any](slices []Slice[T]) Slice[T]` Flatten concatenates slices into one new slice.
- `FromSlice`: `func FromSlice[T any](slice []T) Slice[T]` FromSlice converts slice to a Slice.
- `GroupBy`: `func GroupBy[T any, K comparable](slice Slice[T], keyFn func(T) K) map[K]Slice[T]` GroupBy returns the elements grouped by keyFn, each group in its original order.
- `Keys`: `func Keys[K comparable, V any](m map[K]V) Slice[K]` Keys returns the keys of m in no particular order.
- `Map`: `func Map[T, U any](slice Slice[T], fn func(T) U) Slice[U]` Map returns a new slice holding fn applied to each element.
- `MapWithIndex`: `func MapWithIndex[T, U any](slice Slice[T], fn func(int, T) U) Slice[U]` MapWithIndex is Map with each element's index passed to fn.
- `MaxBy`: `func MaxBy[T any](slice Slice[T], less func(T, T) bool) (T, bool)` MaxBy returns the largest element by less and true, or the zero value and false when slice is empty.
- `MinBy`: `func MinBy[T any](slice Slice[T], less func(T, T) bool) (T, bool)` MinBy returns the smallest element by less and true, or the zero value and false when slice is empty.
- `NewSlice`: `func NewSlice[T any](items ...T) Slice[T]` NewSlice returns a Slice of items.
- `Pair`: `type Pair struct` Pair is one element of a Zip result.
- `Range`: `func Range(start, end int) Slice[int]` Range returns the integers from start up to but not including end.
- `RangeStep`: `func RangeStep(start, end, step int) Slice[int]` RangeStep returns start, start+step, and so on, stopping before end.
- `Reduce`: `func Reduce[T, U any](slice Slice[T], fn func(U, T) U, initial U) U` Reduce folds the slice into one value, starting from initial and calling fn(accumulator, element) for each element in order.
- `ReduceWithIndex`: `func ReduceWithIndex[T, U any](slice Slice[T], fn func(U, int, T) U, initial U) U` ReduceWithIndex is Reduce with each element's index passed to fn.
- `Repeat`: `func Repeat[T any](value T, n int) Slice[T]` Repeat creates a slice with value repeated n times.
- `Slice`: `type Slice []T` Slice is a slice with chainable query and transform methods.
- `Slice.Append`: `func (s Slice[T]) Append(items ...T) Slice[T]` Append returns s with items added, using the built-in append: when s has spare capacity the result shares its backing array.
- `Slice.Chunk`: `func (s Slice[T]) Chunk(size int) []Slice[T]` Chunk splits s into consecutive pieces of size elements; the last piece may be shorter.
- `Slice.Contains`: `func (s Slice[T]) Contains(value T) bool` Contains reports whether an element equals value.
- `Slice.ContainsAll`: `func (s Slice[T]) ContainsAll(values ...T) bool` ContainsAll reports whether s contains every one of values.
- `Slice.ContainsAny`: `func (s Slice[T]) ContainsAny(values ...T) bool` ContainsAny reports whether s contains at least one of values.
- `Slice.Count`: `func (s Slice[T]) Count(value T) int` Count returns the number of elements equal to value.
- `Slice.CountIf`: `func (s Slice[T]) CountIf(predicate func(T) bool) int` CountIf returns the number of elements for which predicate is true.
- `Slice.Difference`: `func (s Slice[T]) Difference(other Slice[T]) Slice[T]` Difference returns the elements of s that are not in other, in s's order.
- `Slice.Every`: `func (s Slice[T]) Every(predicate func(T) bool) bool` Every reports whether predicate is true for every element.
- `Slice.Filter`: `func (s Slice[T]) Filter(predicate func(T) bool) Slice[T]` Filter returns a new slice of the elements for which predicate is true.
- `Slice.FilterWithIndex`: `func (s Slice[T]) FilterWithIndex(predicate func(int, T) bool) Slice[T]` FilterWithIndex is Filter with each element's index passed to predicate.
- `Slice.Find`: `func (s Slice[T]) Find(predicate func(T) bool) (T, bool)` Find returns the first element for which predicate is true and true, or the zero value and false.
- `Slice.FindIndex`: `func (s Slice[T]) FindIndex(predicate func(T) bool) int` FindIndex returns the index of the first element for which predicate is true, or -1.
- `Slice.First`: `func (s Slice[T]) First() (T, bool)` First returns the first element and true, or the zero value and false when s is empty.
- `Slice.ForEach`: `func (s Slice[T]) ForEach(fn func(T))` ForEach calls fn with each element in order.
- `Slice.ForEachWithIndex`: `func (s Slice[T]) ForEachWithIndex(fn func(int, T))` ForEachWithIndex calls fn with each index and element in order.
- `Slice.Get`: `func (s Slice[T]) Get(index int) (T, bool)` Get returns the element at index and true, or the zero value and false when index is out of range.
- `Slice.GetOr`: `func (s Slice[T]) GetOr(index int, defaultValue T) T` GetOr returns the element at index, or defaultValue when index is out of range.
- `Slice.Index`: `func (s Slice[T]) Index(value T) int` Index returns the index of the first element equal to value, or -1.
- `Slice.Insert`: `func (s Slice[T]) Insert(index int, items ...T) Slice[T]` Insert returns a new slice with items placed before index.
- `Slice.Intersect`: `func (s Slice[T]) Intersect(other Slice[T]) Slice[T]` Intersect returns the elements of s that are also in other, without duplicates, in s's order.
- `Slice.IsEmpty`: `func (s Slice[T]) IsEmpty() bool` IsEmpty reports whether s has no elements.
- `Slice.IsNotEmpty`: `func (s Slice[T]) IsNotEmpty() bool` IsNotEmpty reports whether s has at least one element.
- `Slice.Last`: `func (s Slice[T]) Last() (T, bool)` Last returns the last element and true, or the zero value and false when s is empty.
- `Slice.LastIndex`: `func (s Slice[T]) LastIndex(value T) int` LastIndex returns the index of the last element equal to value, or -1.
- `Slice.Len`: `func (s Slice[T]) Len() int` Len returns the number of elements.
- `Slice.None`: `func (s Slice[T]) None(predicate func(T) bool) bool` None reports whether predicate is false for every element.
- `Slice.OrEmpty`: `func (s Slice[T]) OrEmpty() Slice[T]` OrEmpty returns a non-nil slice so JSON encodes an empty collection as [].
- `Slice.Partition`: `func (s Slice[T]) Partition(predicate func(T) bool) (Slice[T], Slice[T])` Partition returns the elements for which predicate is true, then the rest, each in original order.
- `Slice.Prepend`: `func (s Slice[T]) Prepend(items ...T) Slice[T]` Prepend returns a new slice with items followed by the elements of s.
- `Slice.Remove`: `func (s Slice[T]) Remove(index int) Slice[T]` Remove returns a new slice without the element at index.
- `Slice.RemoveAll`: `func (s Slice[T]) RemoveAll(value T) Slice[T]` RemoveAll returns a new slice without any element equal to value.
- `Slice.RemoveValue`: `func (s Slice[T]) RemoveValue(value T) Slice[T]` RemoveValue returns a new slice without the first element equal to value, or s itself when there is none.
- `Slice.Reverse`: `func (s Slice[T]) Reverse() Slice[T]` Reverse returns a reversed copy of s.
- `Slice.Skip`: `func (s Slice[T]) Skip(n int) Slice[T]` Skip returns s without its first n elements.
- `Slice.SkipLast`: `func (s Slice[T]) SkipLast(n int) Slice[T]` SkipLast returns s without its last n elements.
- `Slice.Slice`: `func (s Slice[T]) Slice(start, end int) Slice[T]` Slice returns the elements from start up to end.
- `Slice.Some`: `func (s Slice[T]) Some(predicate func(T) bool) bool` Some reports whether predicate is true for at least one element.
- `Slice.Sort`: `func (s Slice[T]) Sort(less func(T, T) bool) Slice[T]` Sort returns a sorted copy of s, ordered by less.
- `Slice.Take`: `func (s Slice[T]) Take(n int) Slice[T]` Take returns the first n elements, or all of s when it is shorter.
- `Slice.TakeLast`: `func (s Slice[T]) TakeLast(n int) Slice[T]` TakeLast returns the last n elements, or all of s when it is shorter.
- `Slice.ToSlice`: `func (s Slice[T]) ToSlice() []T` ToSlice returns s as a plain slice, sharing its backing array.
- `Slice.Union`: `func (s Slice[T]) Union(other Slice[T]) Slice[T]` Union returns the elements of s followed by those of other, without duplicates (see Unique).
- `Slice.Unique`: `func (s Slice[T]) Unique() Slice[T]` Unique returns the first occurrence of each element, preserving order.
- `Times`: `func Times[T any](n int, fn func(int) T) Slice[T]` Times calls fn with 0..n-1 and collects the results.
- `ToMap`: `func ToMap[T any, K comparable](slice Slice[T], keyFn func(T) K) map[K]T` ToMap returns a map from keyFn(element) to element.
- `ToMapValue`: `func ToMapValue[T any, K comparable, V any](slice Slice[T], keyFn func(T) K, valueFn func(T) V) map[K]V` ToMapValue returns a map from keyFn(element) to valueFn(element).
- `Values`: `func Values[K comparable, V any](m map[K]V) Slice[V]` Values returns the values of m in no particular order.
- `Zip`: `func Zip[T, U any](slice1 Slice[T], slice2 Slice[U]) Slice[Pair[T, U]]` Zip pairs elements at the same index.

### JSON and HTTP client (82 entries)

`import "github.com/regiellis/hyperchi/hyperchi/helpers"`

- `Delete`: `func Delete(url string) (*HTTPResponse, error)` Delete sends a DELETE request with a new default HTTPClient.
- `FetchJSON`: `func FetchJSON(url string) (JSON, error)` FetchJSON GETs url and parses the body as a JSON object.
- `FetchJSONOr`: `func FetchJSONOr(url string) JSON` FetchJSONOr is FetchJSON, returning an empty object on any error (request, body read, or decode).
- `FetchText`: `func FetchText(url string) (string, error)` FetchText GETs url and returns the body as a string.
- `FetchTextOr`: `func FetchTextOr(url, defaultValue string) string` FetchTextOr is FetchText, returning defaultValue on any error.
- `FromJSON`: `func FromJSON(jsonStr string) (JSON, error)` FromJSON parses a JSON object.
- `FromJSONOr`: `func FromJSONOr(jsonStr string) JSON` FromJSONOr parses a JSON object, or returns an empty one when jsonStr is not a valid object.
- `Get`: `func Get(url string) (*HTTPResponse, error)` Get sends a GET request with a new default HTTPClient.
- `HTTPClient`: `type HTTPClient struct` HTTPClient wraps http.Client with a base URL and default headers.
- `HTTPClient.Delete`: `func (h *HTTPClient) Delete(path string) (*HTTPResponse, error)` Delete sends a DELETE request.
- `HTTPClient.DeleteWithContext`: `func (h *HTTPClient) DeleteWithContext(ctx context.Context, path string) (*HTTPResponse, error)` DeleteWithContext is Delete with the caller's cancellation and deadline.
- `HTTPClient.Get`: `func (h *HTTPClient) Get(path string) (*HTTPResponse, error)` Get sends a GET request.
- `HTTPClient.GetWithContext`: `func (h *HTTPClient) GetWithContext(ctx context.Context, path string) (*HTTPResponse, error)` GetWithContext is Get with the caller's cancellation and deadline.
- `HTTPClient.Patch`: `func (h *HTTPClient) Patch(path string, body any) (*HTTPResponse, error)` Patch sends a PATCH request, encoding body as Post does.
- `HTTPClient.PatchWithContext`: `func (h *HTTPClient) PatchWithContext(ctx context.Context, path string, body any) (*HTTPResponse, error)` PatchWithContext is Patch with the caller's cancellation and deadline.
- `HTTPClient.Post`: `func (h *HTTPClient) Post(path string, body any) (*HTTPResponse, error)` Post sends a POST request.
- `HTTPClient.PostForm`: `func (h *HTTPClient) PostForm(path string, data url.Values) (*HTTPResponse, error)` PostForm sends data as an application/x-www-form-urlencoded POST.
- `HTTPClient.PostFormWithContext`: `func (h *HTTPClient) PostFormWithContext(ctx context.Context, path string, data url.Values) (*HTTPResponse, error)` PostFormWithContext is PostForm with the caller's cancellation and deadline.
- `HTTPClient.PostWithContext`: `func (h *HTTPClient) PostWithContext(ctx context.Context, path string, body any) (*HTTPResponse, error)` PostWithContext is Post with the caller's cancellation and deadline.
- `HTTPClient.Put`: `func (h *HTTPClient) Put(path string, body any) (*HTTPResponse, error)` Put sends a PUT request, encoding body as Post does.
- `HTTPClient.PutWithContext`: `func (h *HTTPClient) PutWithContext(ctx context.Context, path string, body any) (*HTTPResponse, error)` PutWithContext is Put with the caller's cancellation and deadline.
- `HTTPClient.WithAuth`: `func (h *HTTPClient) WithAuth(username, password string) *HTTPClient` WithAuth sends HTTP Basic credentials with every request.
- `HTTPClient.WithBaseURL`: `func (h *HTTPClient) WithBaseURL(baseURL string) *HTTPClient` WithBaseURL sets the URL that relative request paths are joined to.
- `HTTPClient.WithBearerToken`: `func (h *HTTPClient) WithBearerToken(token string) *HTTPClient` WithBearerToken sends "Authorization: Bearer <token>" with every request.
- `HTTPClient.WithHeader`: `func (h *HTTPClient) WithHeader(key, value string) *HTTPClient` WithHeader sets a header sent with every request.
- `HTTPClient.WithHeaders`: `func (h *HTTPClient) WithHeaders(headers map[string]string) *HTTPClient` WithHeaders sets several headers sent with every request.
- `HTTPClient.WithJSONHeaders`: `func (h *HTTPClient) WithJSONHeaders() *HTTPClient` WithJSONHeaders sends Content-Type and Accept as application/json.
- `HTTPClient.WithTimeout`: `func (h *HTTPClient) WithTimeout(timeout time.Duration) *HTTPClient` WithTimeout sets the total time limit for each request.
- `HTTPResponse`: `type HTTPResponse struct` HTTPResponse wraps an http.Response.
- `HTTPResponse.Close`: `func (r *HTTPResponse) Close() error` Close closes the response body.
- `HTTPResponse.Header`: `func (r *HTTPResponse) Header(key string) string` Header returns the first value of the response header key.
- `HTTPResponse.Headers`: `func (r *HTTPResponse) Headers() map[string][]string` Headers returns the response headers.
- `HTTPResponse.IsError`: `func (r *HTTPResponse) IsError() bool` IsError reports whether the status is 400 or above.
- `HTTPResponse.IsOK`: `func (r *HTTPResponse) IsOK() bool` IsOK reports whether the status is exactly 200.
- `HTTPResponse.IsSuccess`: `func (r *HTTPResponse) IsSuccess() bool` IsSuccess reports whether the status is 2xx.
- `HTTPResponse.JSON`: `func (r *HTTPResponse) JSON() (JSON, error)` JSON reads and closes the body and parses it as a JSON object.
- `HTTPResponse.JSONOr`: `func (r *HTTPResponse) JSONOr() JSON` JSONOr returns JSON, or an empty object when reading or parsing fails.
- `HTTPResponse.Parse`: `func (r *HTTPResponse) Parse(v any) error` Parse decodes the JSON body into v and closes the body.
- `HTTPResponse.StatusCode`: `func (r *HTTPResponse) StatusCode() int` StatusCode returns the HTTP status code.
- `HTTPResponse.Text`: `func (r *HTTPResponse) Text() (string, error)` Text reads and closes the body and returns it as a string.
- `HTTPResponse.TextOr`: `func (r *HTTPResponse) TextOr(defaultValue string) string` TextOr returns Text, or defaultValue when reading fails.
- `JSON`: `type JSON map[string]any` JSON is a decoded JSON object with typed getters.
- `JSON.Clone`: `func (j JSON) Clone() JSON` Clone returns a shallow copy: nested maps and slices are shared.
- `JSON.Delete`: `func (j JSON) Delete(key string) JSON` Delete removes key and returns j.
- `JSON.Get`: `func (j JSON) Get(key string) any` Get returns the value at key, or nil when it is missing.
- `JSON.GetBool`: `func (j JSON) GetBool(key string) bool` GetBool returns the bool at key, or false when it is missing or not a bool.
- `JSON.GetBoolOr`: `func (j JSON) GetBoolOr(key string, defaultValue bool) bool` GetBoolOr returns the bool at key, or defaultValue when it is missing or not a bool.
- `JSON.GetFloat`: `func (j JSON) GetFloat(key string) float64` GetFloat returns GetFloatOr(key, 0).
- `JSON.GetFloatOr`: `func (j JSON) GetFloatOr(key string, defaultValue float64) float64` GetFloatOr returns the value at key as a float64.
- `JSON.GetInt`: `func (j JSON) GetInt(key string) int` GetInt returns GetIntOr(key, 0).
- `JSON.GetIntOr`: `func (j JSON) GetIntOr(key string, defaultValue int) int` GetIntOr returns the value at key as an int.
- `JSON.GetJSON`: `func (j JSON) GetJSON(key string) JSON` GetJSON returns the nested object at key, or an empty JSON when it is missing, nil, or not an object.
- `JSON.GetSlice`: `func (j JSON) GetSlice(key string) []any` GetSlice returns the array at key, or an empty slice when it is missing or not a []any.
- `JSON.GetString`: `func (j JSON) GetString(key string) string` GetString returns the string at key, or "" when it is missing or not a string.
- `JSON.GetStringOr`: `func (j JSON) GetStringOr(key, defaultValue string) string` GetStringOr returns the string at key, or defaultValue when it is missing or not a string.
- `JSON.Has`: `func (j JSON) Has(key string) bool` Has reports whether key is present, even with a null value.
- `JSON.Keys`: `func (j JSON) Keys() []string` Keys returns the keys in no particular order.
- `JSON.Merge`: `func (j JSON) Merge(other JSON) JSON` Merge copies every key of other into j, overwriting existing keys, and returns j.
- `JSON.Set`: `func (j JSON) Set(key string, value any) JSON` Set stores value at key and returns j, allocating a map when j is nil.
- `JSON.ToPrettyString`: `func (j JSON) ToPrettyString() string` ToPrettyString encodes j as JSON indented by two spaces, or "{}" when encoding fails.
- `JSON.ToString`: `func (j JSON) ToString() string` ToString encodes j as compact JSON, or "{}" when encoding fails.
- `JSON.Values`: `func (j JSON) Values() []any` Values returns the values in no particular order.
- `NewHTTPClient`: `func NewHTTPClient() *HTTPClient` NewHTTPClient returns an HTTPClient with a 30-second timeout and no default headers.
- `NewJSON`: `func NewJSON() JSON` NewJSON returns an empty JSON object.
- `NewQueryParams`: `func NewQueryParams() *QueryParams` NewQueryParams returns an empty QueryParams.
- `ParseJSON`: `func ParseJSON[T any](jsonStr string) (T, error)` ParseJSON decodes jsonStr into a new T.
- `ParseJSONOr`: `func ParseJSONOr[T any](jsonStr string, defaultValue T) T` ParseJSONOr decodes jsonStr into a new T, or returns defaultValue when decoding fails.
- `Post`: `func Post(url string, body any) (*HTTPResponse, error)` Post sends body as a JSON POST with a new default HTTPClient.
- `PostForm`: `func PostForm(url string, data url.Values) (*HTTPResponse, error)` PostForm sends data as a form POST with a new default HTTPClient.
- `Put`: `func Put(url string, body any) (*HTTPResponse, error)` Put sends body as a JSON PUT with a new default HTTPClient.
- `QueryParams`: `type QueryParams struct` QueryParams builds a URL query string.
- `QueryParams.Add`: `func (qp *QueryParams) Add(key, value string) *QueryParams` Add adds value to key, keeping earlier values.
- `QueryParams.Set`: `func (qp *QueryParams) Set(key, value string) *QueryParams` Set sets key to value, replacing earlier values.
- `QueryParams.SetBool`: `func (qp *QueryParams) SetBool(key string, value bool) *QueryParams` SetBool sets key to "true" or "false".
- `QueryParams.SetIf`: `func (qp *QueryParams) SetIf(condition bool, key, value string) *QueryParams` SetIf sets key to value when condition is true.
- `QueryParams.SetIfNotEmpty`: `func (qp *QueryParams) SetIfNotEmpty(key, value string) *QueryParams` SetIfNotEmpty sets key to value when value is not "".
- `QueryParams.SetInt`: `func (qp *QueryParams) SetInt(key string, value int) *QueryParams` SetInt sets key to the decimal form of value.
- `QueryParams.String`: `func (qp *QueryParams) String() string` String returns the encoded query, sorted by key, without a leading "?".
- `QueryParams.URL`: `func (qp *QueryParams) URL(baseURL string) string` URL appends the query to baseURL with "?", or with "&" when baseURL already has a query.
- `QueryParams.Values`: `func (qp *QueryParams) Values() url.Values` Values returns the underlying url.Values, not a copy.
- `ToJSON`: `func ToJSON(v any) string` ToJSON encodes v as compact JSON, or "{}" when encoding fails.
- `ToPrettyJSON`: `func ToPrettyJSON(v any) string` ToPrettyJSON encodes v as JSON indented by two spaces, or "{}" when encoding fails.

### Error handling (29 entries)

`import "github.com/regiellis/hyperchi/hyperchi/helpers"`

- `Catch`: `func Catch(fn func() error, handler func(error))` Catch calls fn through Try and passes any error, including a recovered panic, to handler.
- `CatchValue`: `func CatchValue[T any](fn func() (T, error), handler func(error) T) T` CatchValue calls fn through TryValue and returns its value.
- `Chain`: `type Chain struct` Chain runs a sequence of steps and stops at the first error, which it keeps.
- `Chain.Do`: `func (c *Chain) Do(fn func() error) *Chain` Do calls fn unless an earlier step failed.
- `Chain.DoValue`: `func (c *Chain) DoValue(fn func() error) *Chain` DoValue is the same as Do.
- `Chain.Error`: `func (c *Chain) Error() error` Error returns the first error a step returned, or nil if every step succeeded.
- `Chain.Result`: `func (c *Chain) Result() error` Result is the same as Error.
- `Chain.Success`: `func (c *Chain) Success() bool` Success reports whether every step so far succeeded.
- `Default`: `func Default[T any](value T, err error, defaultValue T) T` Default returns value if err is nil, otherwise defaultValue.
- `ErrorHandler`: `type ErrorHandler struct` ErrorHandler controls what Must does with a non-nil error: log it with the caller's file and line, then panic.
- `ErrorHandler.Must`: `func (eh *ErrorHandler) Must(err error)` Must panics with err if err is not nil.
- `ErrorHandler.Silent`: `func (eh *ErrorHandler) Silent() *ErrorHandler` Silent makes Must panic without logging, for code that reports the panic at a higher level.
- `ErrorHandler.WithLogger`: `func (eh *ErrorHandler) WithLogger(logger func(string, ...any)) *ErrorHandler` WithLogger sets the printf-style function Must logs through.
- `ErrorInfo`: `type ErrorInfo struct` ErrorInfo pairs an error with the file, line, and function where GetErrorInfo was called.
- `ErrorInfo.String`: `func (ei *ErrorInfo) String() string` String formats the error as "file:line in function: error".
- `GetErrorInfo`: `func GetErrorInfo(err error) *ErrorInfo` GetErrorInfo records err with the caller's file, line, and function name.
- `Ignore`: `func Ignore(err error)` Ignore discards err.
- `IgnoreValue`: `func IgnoreValue[T any](value T, err error) T` IgnoreValue returns value and discards err.
- `Must`: `func Must(err error)` Must panics with err if err is not nil, logging the caller's file and line first through the package default handler (see SetDefaultLogger and SetDefaultSilent).
- `MustValue`: `func MustValue[T any](value T, err error) T` MustValue returns value if err is nil and otherwise panics like Must.
- `NewChain`: `func NewChain() *Chain` NewChain returns an empty Chain with no error.
- `NewErrorHandler`: `func NewErrorHandler() *ErrorHandler` NewErrorHandler returns an ErrorHandler that logs with log.Printf before panicking.
- `OrLog`: `func OrLog[T any](value T, err error) T` OrLog returns value if err is nil.
- `OrLogDefault`: `func OrLogDefault[T any](value T, err error, defaultValue T) T` OrLogDefault returns value if err is nil.
- `OrPanic`: `func OrPanic[T any](value T, err error) T` OrPanic returns value if err is nil and otherwise panics with err.
- `SetDefaultLogger`: `func SetDefaultLogger(logger func(string, ...any))` SetDefaultLogger sets the printf-style function the package-level Must and MustValue log through.
- `SetDefaultSilent`: `func SetDefaultSilent(silent bool)` SetDefaultSilent sets whether the package-level Must and MustValue skip logging before they panic.
- `Try`: `func Try(fn func() error) (err error)` Try calls fn and returns its error.
- `TryValue`: `func TryValue[T any](fn func() (T, error)) (result T, err error)` TryValue calls fn and returns its result, recovering a panic as Try does.

### Pointers and values (42 entries)

`import "github.com/regiellis/hyperchi/hyperchi/helpers"`

- `IsNil`: `func IsNil(v any) bool` IsNil reports whether v is nil, including a typed nil pointer, slice, map, channel, or func stored in an interface.
- `IsNotNil`: `func IsNotNil(v any) bool` IsNotNil is !IsNil(v).
- `NewSafe`: `func NewSafe[T any](ptr *T) Safe[T]` NewSafe wraps ptr, which may be nil.
- `NewSafeMap`: `func NewSafeMap[K comparable, V any](m map[K]V) SafeMap[K, V]` NewSafeMap wraps m, which may be nil.
- `NewSafeSlice`: `func NewSafeSlice[T any](slice []T) SafeSlice[T]` NewSafeSlice wraps slice, which may be nil.
- `NewSafeString`: `func NewSafeString(s *string) SafeString` NewSafeString wraps s, which may be nil.
- `Ptr`: `func Ptr[T any](v T) *T` Ptr returns a pointer to a copy of v, for filling pointer fields from literals: Ptr(42), Ptr("name").
- `Safe`: `type Safe struct` Safe wraps a possibly nil pointer so a value can be read and transformed without nil checks.
- `Safe.Filter`: `func (s Safe[T]) Filter(predicate func(T) bool) Safe[T]` Filter returns s when its value is non-nil and predicate is true for it, and a nil Safe otherwise.
- `Safe.FlatMap`: `func (s Safe[T]) FlatMap(fn func(T) Safe[T]) Safe[T]` FlatMap returns fn applied to the value.
- `Safe.Get`: `func (s Safe[T]) Get() T` Get returns the wrapped value, or the zero value of T when it is nil.
- `Safe.GetOr`: `func (s Safe[T]) GetOr(defaultValue T) T` GetOr returns the wrapped value, or defaultValue when it is nil.
- `Safe.IfPresent`: `func (s Safe[T]) IfPresent(fn func(T))` IfPresent calls fn with the value when it is not nil.
- `Safe.IsNil`: `func (s Safe[T]) IsNil() bool` IsNil reports whether the wrapped pointer is nil.
- `Safe.IsNotNil`: `func (s Safe[T]) IsNotNil() bool` IsNotNil reports whether the wrapped pointer is not nil.
- `Safe.Map`: `func (s Safe[T]) Map(fn func(T) T) Safe[T]` Map returns a Safe holding fn applied to the value.
- `Safe.OrElse`: `func (s Safe[T]) OrElse(other Safe[T]) Safe[T]` OrElse returns s when its value is not nil, and other otherwise.
- `SafeMap`: `type SafeMap struct` SafeMap wraps a map, which may be nil, for lookups that return Safe values.
- `SafeMap.Get`: `func (sm SafeMap[K, V]) Get(key K) Safe[V]` Get returns a Safe holding a copy of the value at key, or a nil Safe when key is absent.
- `SafeMap.GetOr`: `func (sm SafeMap[K, V]) GetOr(key K, defaultValue V) V` GetOr returns the value at key, or defaultValue when key is absent.
- `SafeMap.Has`: `func (sm SafeMap[K, V]) Has(key K) bool` Has reports whether key is present.
- `SafeMap.IsEmpty`: `func (sm SafeMap[K, V]) IsEmpty() bool` IsEmpty reports whether the map has no entries.
- `SafeMap.IsNotEmpty`: `func (sm SafeMap[K, V]) IsNotEmpty() bool` IsNotEmpty reports whether the map has at least one entry.
- `SafeMap.Keys`: `func (sm SafeMap[K, V]) Keys() []K` Keys returns the keys in no particular order, or an empty slice for a nil map.
- `SafeMap.Len`: `func (sm SafeMap[K, V]) Len() int` Len returns the number of entries.
- `SafeMap.Values`: `func (sm SafeMap[K, V]) Values() []V` Values returns the values in no particular order, or an empty slice for a nil map.
- `SafeSlice`: `type SafeSlice struct` SafeSlice wraps a slice for bounds-checked element access.
- `SafeSlice.First`: `func (ss SafeSlice[T]) First() Safe[T]` First returns Get(0).
- `SafeSlice.Get`: `func (ss SafeSlice[T]) Get(index int) Safe[T]` Get returns a Safe pointing at the element at index, or a nil Safe when index is out of range.
- `SafeSlice.IsEmpty`: `func (ss SafeSlice[T]) IsEmpty() bool` IsEmpty reports whether the slice has no elements.
- `SafeSlice.IsNotEmpty`: `func (ss SafeSlice[T]) IsNotEmpty() bool` IsNotEmpty reports whether the slice has at least one element.
- `SafeSlice.Last`: `func (ss SafeSlice[T]) Last() Safe[T]` Last returns a Safe pointing at the last element, or a nil Safe when the slice is empty.
- `SafeSlice.Len`: `func (ss SafeSlice[T]) Len() int` Len returns the number of elements.
- `SafeString`: `type SafeString struct` SafeString wraps a possibly nil *string.
- `SafeString.Contains`: `func (ss SafeString) Contains(substr string) bool` Contains reports whether the string contains substr.
- `SafeString.Get`: `func (ss SafeString) Get() string` Get returns the string, or "" when the pointer is nil.
- `SafeString.GetOr`: `func (ss SafeString) GetOr(defaultValue string) string` GetOr returns the string, or defaultValue when the pointer is nil.
- `SafeString.IsEmpty`: `func (ss SafeString) IsEmpty() bool` IsEmpty reports whether the pointer is nil or the string is "".
- `SafeString.IsNotEmpty`: `func (ss SafeString) IsNotEmpty() bool` IsNotEmpty reports whether the pointer is non-nil and the string is not "".
- `SafeString.Trim`: `func (ss SafeString) Trim() SafeString` Trim returns a SafeString holding the string without leading and trailing whitespace.
- `Value`: `func Value[T any](ptr *T) T` Value returns *ptr, or the zero value of T when ptr is nil.
- `ValueOr`: `func ValueOr[T any](ptr *T, defaultValue T) T` ValueOr returns *ptr, or defaultValue when ptr is nil.

### Files (49 entries)

`import "github.com/regiellis/hyperchi/hyperchi/helpers"`

- `AppendFile`: `func AppendFile(filename, content string) error` AppendFile appends content to filename, creating it with mode 0644 if it does not exist.
- `AppendLine`: `func AppendLine(filename, line string) error` AppendLine appends line to filename, adding a trailing newline when line lacks one.
- `CopyFile`: `func CopyFile(src, dst string) error` CopyFile copies the contents of src to dst, creating dst's parent directories and truncating an existing dst.
- `CreateDir`: `func CreateDir(path string) error` CreateDir creates path and any missing parents with mode 0755.
- `Dir`: `func Dir(path string) *DirHelper` Dir returns a DirHelper for path.
- `DirHelper`: `type DirHelper struct` DirHelper chains operations on one directory path.
- `DirHelper.Create`: `func (d *DirHelper) Create() *DirHelper` Create creates the directory and any missing parents.
- `DirHelper.Dirs`: `func (d *DirHelper) Dirs() []string` Dirs returns the paths of the directory's subdirectories.
- `DirHelper.Error`: `func (d *DirHelper) Error() error` Error returns the error from the first failed operation, or nil.
- `DirHelper.Exists`: `func (d *DirHelper) Exists() bool` Exists reports whether the path exists and is a directory.
- `DirHelper.Files`: `func (d *DirHelper) Files() []string` Files returns the paths of the directory's non-directory entries.
- `DirHelper.Find`: `func (d *DirHelper) Find(pattern string) []string` Find returns the files under the directory whose base name matches pattern (see FindFiles).
- `DirHelper.List`: `func (d *DirHelper) List() ([]string, error)` List returns the paths of the directory's entries (see ListAll), or the stored error from an earlier step.
- `DirHelper.Path`: `func (d *DirHelper) Path() string` Path returns the directory path.
- `DirHelper.Remove`: `func (d *DirHelper) Remove() *DirHelper` Remove removes the directory and everything under it.
- `DirHelper.Walk`: `func (d *DirHelper) Walk() []string` Walk returns every non-directory under the directory (see WalkFiles).
- `File`: `func File(path string) *FileHelper` File returns a FileHelper for path.
- `FileExists`: `func FileExists(filename string) bool` FileExists reports whether filename can be stat'd.
- `FileHelper`: `type FileHelper struct` FileHelper chains operations on one file path.
- `FileHelper.Append`: `func (f *FileHelper) Append(content string) *FileHelper` Append appends content to the file (see AppendFile).
- `FileHelper.Copy`: `func (f *FileHelper) Copy(dst string) *FileHelper` Copy copies the file to dst (see CopyFile).
- `FileHelper.Delete`: `func (f *FileHelper) Delete() *FileHelper` Delete removes the file.
- `FileHelper.Error`: `func (f *FileHelper) Error() error` Error returns the error from the first failed operation, or nil.
- `FileHelper.Exists`: `func (f *FileHelper) Exists() bool` Exists reports whether the path exists (see FileExists).
- `FileHelper.Move`: `func (f *FileHelper) Move(dst string) *FileHelper` Move renames the file to dst (see MoveFile).
- `FileHelper.Path`: `func (f *FileHelper) Path() string` Path returns the file path, which Move updates.
- `FileHelper.Read`: `func (f *FileHelper) Read() (string, error)` Read returns the file's contents, or the stored error from an earlier step.
- `FileHelper.ReadOr`: `func (f *FileHelper) ReadOr(defaultContent string) string` ReadOr returns the file's contents, or defaultContent when Read fails.
- `FileHelper.Size`: `func (f *FileHelper) Size() int64` Size returns the file size in bytes, or 0 when it cannot be stat'd.
- `FileHelper.Write`: `func (f *FileHelper) Write(content string) *FileHelper` Write replaces the file's contents (see WriteFile).
- `FileNotExists`: `func FileNotExists(filename string) bool` FileNotExists is !FileExists(filename).
- `FileSize`: `func FileSize(filename string) int64` FileSize returns the size of filename in bytes, or 0 when it cannot be stat'd.
- `FindFiles`: `func FindFiles(root, pattern string) []string` FindFiles returns every non-directory under root whose base name matches pattern (filepath.Match syntax).
- `IsDir`: `func IsDir(path string) bool` IsDir reports whether path exists and is a directory.
- `IsFile`: `func IsFile(path string) bool` IsFile reports whether path exists and is not a directory.
- `ListAll`: `func ListAll(dir string) []string` ListAll returns the paths of every entry in dir, not recursing.
- `ListDirs`: `func ListDirs(dir string) []string` ListDirs returns the paths of the subdirectories of dir, not recursing.
- `ListFiles`: `func ListFiles(dir string) []string` ListFiles returns the paths (dir joined with the name) of the non-directory entries in dir, not recursing.
- `MoveFile`: `func MoveFile(src, dst string) error` MoveFile renames src to dst, creating dst's parent directories.
- `ReadFile`: `func ReadFile(filename string) (string, error)` ReadFile reads a whole file as a string.
- `ReadFileLines`: `func ReadFileLines(filename string) ([]string, error)` ReadFileLines reads a file and returns its lines without line endings ("\n" or "\r\n").
- `ReadFileOr`: `func ReadFileOr(filename, defaultContent string) string` ReadFileOr reads a file, returning defaultContent when it cannot be read for any reason (missing, unreadable, a directory).
- `RemoveDir`: `func RemoveDir(path string) error` RemoveDir removes path and everything under it.
- `RemoveFile`: `func RemoveFile(filename string) error` RemoveFile removes filename, or an empty directory.
- `TempDir`: `func TempDir(pattern string) (string, error)` TempDir creates a directory in os.TempDir (see os.MkdirTemp) and returns its path.
- `TempFile`: `func TempFile(pattern string) (string, error)` TempFile creates an empty file in os.TempDir with a name built from pattern (see os.CreateTemp) and returns its path.
- `WalkDirs`: `func WalkDirs(root string) []string` WalkDirs returns the path of every directory under root, recursively, excluding root itself.
- `WalkFiles`: `func WalkFiles(root string) []string` WalkFiles returns the path of every non-directory under root, recursively.
- `WriteFile`: `func WriteFile(filename, content string) error` WriteFile writes content to filename with mode 0644, creating parent directories as needed.

### Environment (36 entries)

`import "github.com/regiellis/hyperchi/hyperchi/helpers"`

- `Env`: `type Env struct` Env reads configuration from .env files, the process environment, and values set in code.
- `Env.All`: `func (e *Env) All() map[string]string` All returns every variable from every source, merged with the same precedence as Get.
- `Env.Clear`: `func (e *Env) Clear()` Clear forgets every .env and Set value and the list of loaded files, so files can be loaded again.
- `Env.Dump`: `func (e *Env) Dump() map[string]string` Dump returns a copy of the values loaded from .env files and set with Set.
- `Env.Get`: `func (e *Env) Get(key string) string` Get returns the value for key: a value set in code, else the process environment, else a .env value (see Env for OverrideSystemEnv).
- `Env.GetBool`: `func (e *Env) GetBool(key string) bool` GetBool reports whether the value for key is "true", "1", "yes", or "on", ignoring case.
- `Env.GetBoolOr`: `func (e *Env) GetBoolOr(key string, defaultValue bool) bool` GetBoolOr returns defaultValue when key is unset or empty, and GetBool otherwise.
- `Env.GetFloat`: `func (e *Env) GetFloat(key string) (float64, error)` GetFloat parses the value for key as a float64.
- `Env.GetFloatOr`: `func (e *Env) GetFloatOr(key string, defaultValue float64) float64` GetFloatOr returns the value for key as a float64, or defaultValue when it is unset, empty, or not a number.
- `Env.GetInt`: `func (e *Env) GetInt(key string) (int, error)` GetInt parses the value for key as an int.
- `Env.GetIntOr`: `func (e *Env) GetIntOr(key string, defaultValue int) int` GetIntOr returns the value for key as an int, or defaultValue when it is unset, empty, or not an integer.
- `Env.GetOr`: `func (e *Env) GetOr(key, defaultValue string) string` GetOr returns the value for key, or defaultValue when it is unset or empty.
- `Env.GetSlice`: `func (e *Env) GetSlice(key string) []string` GetSlice splits the value for key on commas and trims each part.
- `Env.Has`: `func (e *Env) Has(key string) bool` Has reports whether any source has key, even with an empty value.
- `Env.Keys`: `func (e *Env) Keys() []string` Keys returns the keys of All in no particular order.
- `Env.LoadConfig`: `func (e *Env) LoadConfig(config EnvConfig) error` LoadConfig loads config.Files (or the LoadDefault files), applies config.Defaults, and returns an error listing any config.Required key that is still unset.
- `Env.LoadDefault`: `func (e *Env) LoadDefault() error` LoadDefault loads .env, .env.local, and .env.development from the working directory, in that order, skipping any that do not exist.
- `Env.LoadFile`: `func (e *Env) LoadFile(filename string) error` LoadFile reads KEY=value lines from filename.
- `Env.LoadFiles`: `func (e *Env) LoadFiles(filenames ...string) error` LoadFiles calls LoadFile for each name in order and stops at the first error.
- `Env.LoadedFiles`: `func (e *Env) LoadedFiles() []string` LoadedFiles returns a copy of the names of the files loaded so far, in load order.
- `Env.Save`: `func (e *Env) Save(filename string) error` Save writes the current .env variables to filename, sorted by key.
- `Env.Set`: `func (e *Env) Set(key, value string)` Set sets a value in code (in memory only).
- `Env.SetSystem`: `func (e *Env) SetSystem(key, value string) error` SetSystem calls Set and also sets key in the process environment.
- `Env.Validate`: `func (e *Env) Validate(required []string) error` Validate returns an error listing every key in required that no source has.
- `EnvConfig`: `type EnvConfig struct` EnvConfig configures Env.LoadConfig.
- `GetEnv`: `func GetEnv(key string) string` GetEnv returns the package-level Env's value for key (see Env.Get).
- `GetEnvBool`: `func GetEnvBool(key string) bool` GetEnvBool reports whether the value for key is "true", "1", "yes", or "on", ignoring case.
- `GetEnvBoolOr`: `func GetEnvBoolOr(key string, defaultValue bool) bool` GetEnvBoolOr returns defaultValue when key is unset or empty, and GetEnvBool otherwise.
- `GetEnvInt`: `func GetEnvInt(key string) (int, error)` GetEnvInt parses the value for key as an int.
- `GetEnvIntOr`: `func GetEnvIntOr(key string, defaultValue int) int` GetEnvIntOr returns the value for key as an int, or defaultValue when it is unset, empty, or not an integer.
- `GetEnvOr`: `func GetEnvOr(key, defaultValue string) string` GetEnvOr returns the value for key, or defaultValue when it is unset or empty.
- `HasEnv`: `func HasEnv(key string) bool` HasEnv reports whether any source has key, even with an empty value.
- `LoadEnv`: `func LoadEnv(filenames ...string) error` LoadEnv loads the named .env files into the package-level Env, or the LoadDefault files when no names are given.
- `NewEnv`: `func NewEnv() *Env` NewEnv returns an empty Env.
- `RequireEnv`: `func RequireEnv(keys ...string) error` RequireEnv returns an error listing every key that no source has.
- `SetEnv`: `func SetEnv(key, value string) error` SetEnv sets key on the package-level Env and in the process environment.

### Logging (37 entries)

`import "github.com/regiellis/hyperchi/hyperchi/helpers"`

- `ConfigureLogger`: `func ConfigureLogger(config LoggerConfig)` ConfigureLogger applies config to the logger behind the package-level functions.
- `Debug`: `func Debug(format string, args ...any)` Debug logs a formatted message at DEBUG level through the default logger.
- `Error`: `func Error(format string, args ...any)` Error logs a formatted message at ERROR level through the default logger.
- `Fatal`: `func Fatal(format string, args ...any)` Fatal logs a formatted message at FATAL level through the default logger, then exits the process with status 1.
- `Info`: `func Info(format string, args ...any)` Info logs a formatted message at INFO level through the default logger.
- `Log`: `func Log(args ...any)` Log logs its arguments, joined as fmt.Sprint does, at DEBUG level.
- `LogError`: `func LogError(err error, context ...string)` LogError logs err at ERROR level as "Error (context...): err".
- `LogErrorf`: `func LogErrorf(err error, format string, args ...any)` LogErrorf logs err at ERROR level with a formatted context, as "Error (context): err".
- `LogLevel`: `type LogLevel int` LogLevel is a Logger severity.
- `LogLevel.Color`: `func (l LogLevel) Color() string` Color returns the ANSI escape that colors the level name, or the reset code for an unknown level.
- `LogLevel.String`: `func (l LogLevel) String() string` String returns the level name, such as "WARN", or "UNKNOWN".
- `Logf`: `func Logf(format string, args ...any)` Logf logs a formatted message at DEBUG level.
- `Logger`: `type Logger struct` Logger writes leveled, printf-style log lines of the form "2006-01-02 15:04:05 [LEVEL] [prefix] file:line message".
- `Logger.Debug`: `func (l *Logger) Debug(format string, args ...any)` Debug logs a formatted message at DEBUG level.
- `Logger.Error`: `func (l *Logger) Error(format string, args ...any)` Error logs a formatted message at ERROR level.
- `Logger.Fatal`: `func (l *Logger) Fatal(format string, args ...any)` Fatal logs a formatted message at FATAL level, then exits the process with status 1.
- `Logger.Info`: `func (l *Logger) Info(format string, args ...any)` Info logs a formatted message at INFO level.
- `Logger.SetColored`: `func (l *Logger) SetColored(colored bool) *Logger` SetColored turns ANSI colors on the level name on or off.
- `Logger.SetLevel`: `func (l *Logger) SetLevel(level LogLevel) *Logger` SetLevel sets the lowest level the logger writes.
- `Logger.SetOutput`: `func (l *Logger) SetOutput(w io.Writer) *Logger` SetOutput sets the writer log lines go to.
- `Logger.SetPrefix`: `func (l *Logger) SetPrefix(prefix string) *Logger` SetPrefix sets text written in brackets after the level on every line.
- `Logger.SetShowCaller`: `func (l *Logger) SetShowCaller(show bool) *Logger` SetShowCaller turns the caller's file:line on or off.
- `Logger.Warn`: `func (l *Logger) Warn(format string, args ...any)` Warn logs a formatted message at WARN level.
- `Logger.WithField`: `func (l *Logger) WithField(key, value string) *Logger` WithField returns a copy of the logger whose prefix gains "key=value".
- `Logger.WithFields`: `func (l *Logger) WithFields(fields map[string]string) *Logger` WithFields returns a copy of the logger whose prefix gains "key=value" for each field, sorted by key.
- `LoggerConfig`: `type LoggerConfig struct` LoggerConfig holds the settings ConfigureLogger applies.
- `NewLogger`: `func NewLogger() *Logger` NewLogger returns a Logger that writes INFO and above to os.Stdout with colored level names and no caller information.
- `Print`: `func Print(args ...any)` Print logs its arguments, joined as fmt.Sprint does, at INFO level.
- `Printf`: `func Printf(format string, args ...any)` Printf logs a formatted message at INFO level.
- `SetLogColored`: `func SetLogColored(colored bool)` SetLogColored turns colored level names on or off for the package-level functions.
- `SetLogLevel`: `func SetLogLevel(level LogLevel)` SetLogLevel sets the lowest level the package-level functions write.
- `SetLogOutput`: `func SetLogOutput(w io.Writer)` SetLogOutput sets the writer the package-level functions write to.
- `SetLogShowCaller`: `func SetLogShowCaller(show bool)` SetLogShowCaller turns caller file:line on or off for the package-level functions.
- `SetupDevelopmentLogging`: `func SetupDevelopmentLogging()` SetupDevelopmentLogging makes the package-level functions write DEBUG and above to stdout, colored, with caller information.
- `SetupProductionLogging`: `func SetupProductionLogging()` SetupProductionLogging makes the package-level functions write INFO and above to stdout, uncolored, without caller information.
- `SetupTestLogging`: `func SetupTestLogging()` SetupTestLogging discards all package-level log output.
- `Warn`: `func Warn(format string, args ...any)` Warn logs a formatted message at WARN level through the default logger.

### Profiler (23 entries)

`import "github.com/regiellis/hyperchi/hyperchi/helpers"`

- `MemoryStats`: `type MemoryStats struct` MemoryStats is a snapshot of runtime memory figures, in megabytes, plus the GC count and goroutine count.
- `NewProfiler`: `func NewProfiler() *Profiler` NewProfiler returns an enabled Profiler with no data.
- `PathStats`: `type PathStats struct` PathStats summarizes the recorded requests for one path.
- `ProfileStats`: `type ProfileStats struct` ProfileStats is the snapshot GetStats returns.
- `Profiler`: `type Profiler struct` Profiler records request timings through its Middleware and named timings through StartTimer and TimeOperation.
- `Profiler.Disable`: `func (p *Profiler) Disable() *Profiler` Disable turns recording off and returns p.
- `Profiler.Enable`: `func (p *Profiler) Enable() *Profiler` Enable turns recording on and returns p.
- `Profiler.GetRequestsByPath`: `func (p *Profiler) GetRequestsByPath() map[string]PathStats` GetRequestsByPath returns timing statistics for each recorded path.
- `Profiler.GetStats`: `func (p *Profiler) GetStats() ProfileStats` GetStats returns a snapshot of the recorded requests, the named timers with at least one completed run, and current memory figures.
- `Profiler.GetTopSlowRequests`: `func (p *Profiler) GetTopSlowRequests(n int) []RequestProfile` GetTopSlowRequests returns up to n recorded requests, slowest first.
- `Profiler.IsEnabled`: `func (p *Profiler) IsEnabled() bool` IsEnabled reports whether the profiler is recording.
- `Profiler.Middleware`: `func (p *Profiler) Middleware() func(http.Handler) http.Handler` Middleware returns middleware that records each request's method, path, status, duration, and bytes allocated, for app.Use.
- `Profiler.PrintStats`: `func (p *Profiler) PrintStats()` PrintStats writes a readable summary of GetStats to stdout.
- `Profiler.Reset`: `func (p *Profiler) Reset()` Reset discards every recorded request and timer and restarts the uptime clock.
- `Profiler.StartTimer`: `func (p *Profiler) StartTimer(name string) *Timer` StartTimer starts a new run of the named timer and returns it.
- `Profiler.StatsHandler`: `func (p *Profiler) StatsHandler() http.Handler` StatsHandler returns an http.Handler that serves GetStats as JSON.
- `Profiler.StopTimer`: `func (p *Profiler) StopTimer(name string) time.Duration` StopTimer stops the most recently started run of the named timer that is still running and returns its duration, or 0 when none is running.
- `Profiler.TimeOperation`: `func (p *Profiler) TimeOperation(name string, fn func()) (duration time.Duration)` TimeOperation calls fn, records the run under name, and returns its duration.
- `Profiler.TimeOperationWithResult`: `func (p *Profiler) TimeOperationWithResult(name string, fn func() any) (result any, duration time.Duration)` TimeOperationWithResult is TimeOperation for a function that returns a value; it returns that value and the duration.
- `RequestProfile`: `type RequestProfile struct` RequestProfile is one request the Middleware recorded.
- `Timer`: `type Timer struct` Timer is one run of a named timer, returned by StartTimer.
- `Timer.Stop`: `func (t *Timer) Stop() time.Duration` Stop ends this run, records it in the timer's statistics, and returns its duration.
- `TimerStats`: `type TimerStats struct` TimerStats summarizes the completed runs of one named timer.

### Live reload (Air) (25 entries)

`import "github.com/regiellis/hyperchi/hyperchi/helpers"`

- `AirBuildConfig`: `type AirBuildConfig struct` AirBuildConfig is the [build] section of an Air configuration.
- `AirConfig`: `type AirConfig struct` AirConfig is an Air live-reload configuration.
- `AirConfig.SaveAirConfig`: `func (ac *AirConfig) SaveAirConfig(filename string) error` SaveAirConfig writes ToTOML to filename, or to .air.toml when filename is empty.
- `AirConfig.ToTOML`: `func (ac *AirConfig) ToTOML() string` ToTOML renders the configuration as the contents of an .air.toml file, with the [build], [color], log, and [misc] sections.
- `AirHelper`: `type AirHelper struct` AirHelper sets up Air for the project in one directory.
- `AirHelper.AddBuildArgs`: `func (ah *AirHelper) AddBuildArgs(args ...string) *AirHelper` AddBuildArgs adds arguments Air passes to the binary when it runs it.
- `AirHelper.AddExcludeDir`: `func (ah *AirHelper) AddExcludeDir(directories ...string) *AirHelper` AddExcludeDir adds directories Air does not watch.
- `AirHelper.AddWatchExt`: `func (ah *AirHelper) AddWatchExt(extensions ...string) *AirHelper` AddWatchExt adds file extensions, without dots, that trigger a rebuild.
- `AirHelper.CreateDevScript`: `func (ah *AirHelper) CreateDevScript() error` CreateDevScript writes an executable dev.sh to the project root.
- `AirHelper.GetInstallInstructions`: `func (ah *AirHelper) GetInstallInstructions() string` GetInstallInstructions returns text describing the ways to install Air.
- `AirHelper.InitAir`: `func (ah *AirHelper) InitAir() error` InitAir creates tmp/ and writes .air.toml in the project root.
- `AirHelper.IsAirInstalled`: `func (ah *AirHelper) IsAirInstalled() bool` IsAirInstalled reports whether an air binary exists in /usr/local/bin, /usr/bin, $GOPATH/bin, or $HOME/go/bin.
- `AirHelper.SetBinPath`: `func (ah *AirHelper) SetBinPath(path string) *AirHelper` SetBinPath sets the path of the binary Air runs after a build.
- `AirHelper.SetBuildCmd`: `func (ah *AirHelper) SetBuildCmd(cmd string) *AirHelper` SetBuildCmd sets the command Air runs to build the binary.
- `AirHelper.UpdateConfig`: `func (ah *AirHelper) UpdateConfig(modifier func(*AirConfig)) *AirHelper` UpdateConfig calls modifier with the configuration so it can change any field.
- `AirInstallHelp`: `func AirInstallHelp() string` AirInstallHelp returns text describing the ways to install Air.
- `AirLogConfig`: `type AirLogConfig struct` AirLogConfig is the log section of an Air configuration.
- `AirMiscConfig`: `type AirMiscConfig struct` AirMiscConfig is the [misc] section of an Air configuration.
- `AirWatchConfig`: `type AirWatchConfig struct` AirWatchConfig holds the watch lists kept in AirConfig.Watch.
- `CreateHyperChiAirConfig`: `func CreateHyperChiAirConfig(filename string) error` CreateHyperChiAirConfig writes HyperChiAirConfig to filename, or to .air.toml when filename is empty.
- `DefaultAirConfig`: `func DefaultAirConfig() *AirConfig` DefaultAirConfig returns an Air configuration that builds ./tmp/main from the current directory and watches Go, template, HTML, JS, and CSS files.
- `HyperChiAirConfig`: `func HyperChiAirConfig() *AirConfig` HyperChiAirConfig returns DefaultAirConfig extended for a HyperChi project: it also watches .md, .env, .gotmpl, and .gohtml files and skips the static, uploads, logs, storage, and cache directories.
- `InitAirConfig`: `func InitAirConfig() error` InitAirConfig runs AirHelper.InitAir in the current directory.
- `IsAirAvailable`: `func IsAirAvailable() bool` IsAirAvailable reports whether Air is installed (see AirHelper.IsAirInstalled).
- `NewAirHelper`: `func NewAirHelper(projectRoot string) *AirHelper` NewAirHelper returns an AirHelper for projectRoot, or for the current directory when projectRoot is empty.

### Deployment (19 entries)

`import "github.com/regiellis/hyperchi/hyperchi/helpers"`

- `CreateDeploymentFiles`: `func CreateDeploymentFiles(projectRoot, appName string) error` CreateDeploymentFiles writes every deployment file for appName into projectRoot: Dockerfile, compose file, production .env, build and deploy scripts, nginx and systemd configs, and a health check.
- `DeploymentConfig`: `type DeploymentConfig struct` DeploymentConfig describes how an app is deployed.
- `DeploymentHelper`: `type DeploymentHelper struct` DeploymentHelper writes deployment files (Dockerfile, compose file, scripts, server configs) for one project directory and app name.
- `DeploymentHelper.CreateBuildScript`: `func (dh *DeploymentHelper) CreateBuildScript() error` CreateBuildScript writes an executable build.sh that cross-compiles release archives into dist/ and builds a Docker image.
- `DeploymentHelper.CreateDeployScript`: `func (dh *DeploymentHelper) CreateDeployScript() error` CreateDeployScript writes an executable deploy.sh.
- `DeploymentHelper.CreateDeploymentFiles`: `func (dh *DeploymentHelper) CreateDeploymentFiles() error` CreateDeploymentFiles writes the Dockerfile, docker-compose.yml, systemd unit, deploy.sh, and nginx.conf.
- `DeploymentHelper.CreateDockerCompose`: `func (dh *DeploymentHelper) CreateDockerCompose() error` CreateDockerCompose writes docker-compose.yml with the app service on port 8080, a data volume, and an nginx service in front of it.
- `DeploymentHelper.CreateDockerfile`: `func (dh *DeploymentHelper) CreateDockerfile() error` CreateDockerfile writes a multi-stage Dockerfile, and a .dockerignore when the project has none.
- `DeploymentHelper.CreateHealthCheck`: `func (dh *DeploymentHelper) CreateHealthCheck() error` CreateHealthCheck writes health.go, standard-library health, readiness, and liveness handlers for package main.
- `DeploymentHelper.CreateNginxConfig`: `func (dh *DeploymentHelper) CreateNginxConfig() error` CreateNginxConfig writes nginx.conf, a reverse proxy in front of the app on port 8080.
- `DeploymentHelper.CreateProductionEnv`: `func (dh *DeploymentHelper) CreateProductionEnv() error` CreateProductionEnv writes a .env.production template with mode 0600.
- `DeploymentHelper.CreateSystemdService`: `func (dh *DeploymentHelper) CreateSystemdService() error` CreateSystemdService writes <appName>.service, a systemd unit that runs the app.
- `DeploymentHelper.DefaultDeploymentConfig`: `func (dh *DeploymentHelper) DefaultDeploymentConfig() *DeploymentConfig` DefaultDeploymentConfig returns a production config for linux/amd64 on port 8080 with a /health check.
- `DeploymentHelper.GetBuildInfo`: `func (dh *DeploymentHelper) GetBuildInfo() map[string]string` GetBuildInfo returns the Go version, OS, architecture, current UTC time, app name, and a Unix-time deployment ID as strings.
- `DeploymentHelper.GetProductionConfig`: `func (dh *DeploymentHelper) GetProductionConfig() *ProductionConfig` GetProductionConfig returns recommended production settings as a ProductionConfig.
- `DeploymentHelper.SwitchToProduction`: `func (dh *DeploymentHelper) SwitchToProduction() error` SwitchToProduction loads .env.production from the project root and returns an error when APP_NAME, ENVIRONMENT, JWT_SECRET, or SESSION_SECRET is missing.
- `NewDeploymentHelper`: `func NewDeploymentHelper(projectRoot, appName string) *DeploymentHelper` NewDeploymentHelper returns a DeploymentHelper for projectRoot (default ".") and appName (default "hyperchi-app").
- `ProductionConfig`: `type ProductionConfig struct` ProductionConfig is a set of recommended production settings returned by GetProductionConfig.
- `SwitchEnvironment`: `func SwitchEnvironment(env string) error` SwitchEnvironment copies .env.<env> over .env in the working directory.

## Follow the working examples {#next}

Start with [Passing data](/docs/data) for `app.Get`, query defaults, typed filtering, and JSON responses. Compare [templ](/docs/templ) and [Jet](/docs/jet) for the external rendering boundary. The [helpers package](https://github.com/regiellis/hyperchi/tree/main/hyperchi/helpers) contains the full collection, string, file, logging, and HTTP-client APIs.
