:sig
function component(name : string, props : as dval = {}, request : capture dval) string

:params
name : a component path, optionally followed by `:HANDLER`
props : optional input, received through the component's `props` parameter
request : the caller's request, captured when omitted

:returns
The component output as a string.

:content
`component()` renders a component and returns everything it printed as a string.

It collects the output into a buffer, which costs something. Reach for `component_render()` instead when you are writing the fragment straight into the current response and never look at the string — it skips the buffer entirely, so `component_render("card")` does less work than `print(component("card"))`.

Capture the string when the string is the goal: wrapping the fragment in surrounding markup, checking whether it produced anything before emitting a container, storing it in a `dval` beside other values, or emitting the same fragment more than once from one call.

## How a name resolves

A name is a path with an optional `:HANDLER` suffix. Bearer appends `.capy` when the path has no suffix, so `"card"` and `"card.capy"` mean the same file.

A path that starts with `/` is tried first as a real filesystem path and then as a site-root path, so `"/tests/capy-request-context.capy"` means that file under the site root.

A relative path is tried against three directories, in order:

1. the directory of the entry unit that is handling the request
2. the directory of the unit calling `component()`, when that differs
3. the site root

The first match wins. A resolved path that escapes the site root is rejected. Use `component_resolve()` to see which file a name reached. Test an optional name with `length(component_resolve(name)) > 0` before you render it.

## Named handlers

`"lib/widgets.capy:CARD"` calls the `COMPONENT:CARD` handler in that file. A name that is only `":CARD"` calls `COMPONENT:CARD` in the file that is currently rendering, which lets one file hold several related fragments.

A file with no named handler must define a plain `COMPONENT` handler.

## What the component sees

Declare `function COMPONENT(props : dval)` or `function COMPONENT:NAME(props : dval)` to receive the input.

Props are shared objects. Changes to their contents remain visible to the caller. Each nested component receives its own props argument.

The component binds `request` implicitly. Bearer does not refresh request keys between handlers. `request.unit` continues to identify the entry unit.

Relative paths inside the component resolve against the component's own file, not the caller's — a component in `lib/` can call `component("sibling")` and reach `lib/sibling.capy`.

The target unit's `ONCE` handler runs before the component handler the first time that unit is used in the request.

:errors
An unresolvable component does not raise an error. Bearer sets the response status to `500`, returns an empty string, and continues to render the page. Guard optional components with `length(component_resolve(name)) > 0` when a missing file should not be an error.

:example capy render
print(component("/doc/examples/card.capy:CARD", title: "Release notes"), "\n")

:example capy render wrap the captured output
var body := component("/doc/examples/card.capy:CARD", title: "Ada")
print("<section>", body, "</section>", "\n")

:example capy render render a list of components
var titles := ["Ada", "Grace"]
for title := titles
{
	print(component("/doc/examples/card.capy:CARD", title: title))
}
print("\n")

:example capy render skip a component that may not exist
if length(component_resolve("/doc/examples/card.capy")) > 0
{
	print(component("/doc/examples/card.capy:CARD", title: "Optional"), "\n")
}
else
{
	print("no card component\n")
}

:see-group
components
