Handlers
cross.stream handlers use Nushell
closures
to process and act on incoming frames as they are appended to the store.
{ process: {|frame| if $frame.topic == "ping" { "pong" # Will be appended to handler.out } }}
The handler closure receives each new frame and can:
- Process the frame’s content
- Return a value (which gets automatically appended to
<handler-name>.out
) - Explicitly append new frames using the
.append
command - Filter which frames to process using conditionals
Registering
To register a handler, append a registration script with the topic
<handler-name>.register
. The script must return a record that configures the
handler’s behavior:
r###'{ # Required: Handler closure process: {|frame| if $frame.topic == "ping" { "pong" # Will be appended to handler.out } }
# Optional: Where to resume processing from # "tail" (default), "head", or scru128 ID resume_from: "tail"
# Optional: Module definitions modules: { "my-math": "def double [x] { $x * 2 }" }
# Optional: Heartbeat interval in ms pulse: 1000
# Optional: Control output frame behavior return_options: { suffix: ".response" # Output topic suffix ttl: "head:1" # Keep only most recent frame }}'### | .append echo.register
The registration script is stored in CAS and evaluated to obtain the handler’s configuration.
Configuration Record Fields
Field | Description |
---|---|
process | Required handler closure that processes each frame |
resume_from | ”tail” (default), “head”, or scru128 ID to control where processing starts |
pulse | Interval in milliseconds to send synthetic xs.pulse events |
return_options | Controls output frames: see Return Options |
modules | Map of module name to the string content of the module |
Return Options
The return_options
field controls how return values are handled:
suffix
: String appended to handler’s name for output topic (default: “.out”)ttl
: Time-to-live for output frames"forever"
: Never expire"ephemeral"
: Remove after reading"time:<milliseconds>"
: Expire after duration"head:<n>"
: Keep only N most recent frames
Modules
The modules
option allows handlers to use custom Nushell modules:
r###'{ process: {|frame| my-math double 8 # Use module command } modules: { "my-math": "export def double [x] { $x * 2 }" }}'### | .append processor.register
State and Environment
Handlers can maintain state using environment variables which persist between calls:
{ process: {|frame| # Initialize or increment counter let env.count = ($env | get -i count | default 0) + 1 $"Processed ($env.count) frames" }} | .append counter.register
Output
Handlers can produce output in two ways:
- Return Values: Any non-null return value is automatically appended to the
handler’s output topic (
<handler-name>.out
by default unless modified by return_options.suffix)
{|frame| if $frame.topic == "ping" { "pong" # Automatically appended to handler.out }}
- Explicit Appends: Use the
.append
command to create frames on any topic
{|frame| if $frame.topic == "ping" { "pong" | .append response.topic --meta { "type": "response" } "logged" | .append audit.topic }}
All output frames automatically include:
handler_id
: ID of the handler that created the frameframe_id
: ID of the frame that triggered the handler
Lifecycle
Unregistering
A handler can be unregistered by:
- Appending
<handler-name>.unregister
- Registering a new handler with the same name
- Runtime errors in the handler closure
When unregistered, the handler appends a confirmation frame
<handler-name>.unregistered
. If unregistered due to an error, the frame
includes an error
field in its metadata.
Error Handling
If a handler encounters an error during execution:
- The handler is automatically unregistered
- A frame is appended to
<handler-name>.unregistered
with:- The error message in metadata
- Reference to the triggering frame