NAME

PAGI::Context - Per-request context with protocol-specific subclasses

SYNOPSIS

use PAGI::Context;

# Factory returns the right subclass based on scope type
my $ctx = PAGI::Context->new($scope, $receive, $send);

# Shared methods (all protocol types)
my $type = $ctx->type;        # 'http', 'websocket', 'sse'
my $path = $ctx->path;
my $stash = $ctx->stash;      # PAGI::Stash
my $session = $ctx->session;  # PAGI::Session

# Protocol-specific (only on the appropriate subclass)
my $req = $ctx->request;      # HTTP only
my $res = $ctx->response;     # HTTP only
my $ws  = $ctx->websocket;    # WebSocket only
my $sse = $ctx->sse;          # SSE only

DESCRIPTION

PAGI::Context is a factory and base class that provides a unified entry point for per-request context. Calling PAGI::Context->new(...) inspects $scope->{type} and returns the appropriate subclass: PAGI::Context::HTTP, PAGI::Context::WebSocket, or PAGI::Context::SSE.

Shared methods (scope accessors, stash, session, connection state) live on the base class. Protocol-specific methods (request/response, websocket, sse) live on subclasses and simply do not exist on other protocol types.

EXTENSIBILITY

Override _type_map to add or replace protocol types:

package MyApp::Context;
our @ISA = ('PAGI::Context');

sub _type_map {
    my ($class) = @_;
    return {
        %{ $class->SUPER::_type_map },
        grpc => 'MyApp::Context::GRPC',
    };
}

Override _resolve_class for custom resolution logic beyond the type map.

CONSTRUCTOR

new

my $ctx = PAGI::Context->new($scope, $receive, $send);

Factory constructor. Returns a subclass instance based on $scope->{type}. Defaults to HTTP if type is missing or unknown.

CLASS METHODS

_type_map

my $map = PAGI::Context->_type_map;

Returns a hashref mapping scope type strings to subclass package names. Override in a subclass to add or replace protocol types.

_resolve_class

my $class = PAGI::Context->_resolve_class($scope);

Resolves the scope to a subclass package name. Looks up $scope->{type} in _type_map; defaults to the http mapping if the type is missing or unknown. Override for custom resolution logic.

METHODS

Scope Accessors

$ctx->scope;          # raw $scope hashref
$ctx->type;           # $scope->{type}
$ctx->path;           # $scope->{path}
$ctx->raw_path;       # $scope->{raw_path} // $scope->{path}
$ctx->query_string;   # $scope->{query_string} // ''
$ctx->scheme;         # $scope->{scheme} // 'http'
$ctx->client;         # $scope->{client}
$ctx->server;         # $scope->{server}
$ctx->headers;        # $scope->{headers} arrayref of [name, value]

Path Parameters

my $params = $ctx->path_params;           # hashref
my $id     = $ctx->path_param('id');      # strict: dies if missing
my $id     = $ctx->path_param('id', strict => 0);  # returns undef

path_params returns the $scope->{path_params} hashref (set by the router), defaulting to {} if not present.

path_param returns a single parameter by name. By default it dies if the key is not found (strict mode). Pass strict => 0 to return undef for missing keys instead.

Protocol Introspection

$ctx->is_http;        # true if type eq 'http'
$ctx->is_websocket;   # true if type eq 'websocket'
$ctx->is_sse;         # true if type eq 'sse'
my $value = $ctx->header('Content-Type');

Returns the last value for the named header (case-insensitive), or undef if not found.

receive

my $receive = $ctx->receive;

Returns the raw $receive coderef. Calling it returns a Future that resolves to the next protocol event hashref from the client.

# Read an HTTP request body event
my $event = await $ctx->receive->();
# $event = { type => 'http.request', body => '...' }

# Read a WebSocket message
my $msg = await $ctx->receive->();
# $msg = { type => 'websocket.receive', text => 'hello' }

Most users should prefer the protocol helpers ($ctx->request, $ctx->websocket, $ctx->sse) which handle the event protocol internally. Use receive only for raw protocol access.

send

my $send = $ctx->send;

Returns the raw $send coderef. Calling it with an event hashref returns a Future that resolves when the event has been sent.

# Send an HTTP response (two events: start + body)
await $ctx->send->({ type => 'http.response.start', status => 200,
                     headers => [['content-type', 'text/plain']] });
await $ctx->send->({ type => 'http.response.body', body => 'Hello' });

# Accept a WebSocket connection
await $ctx->send->({ type => 'websocket.accept' });

Most users should prefer the protocol helpers ($ctx->response, $ctx->websocket, $ctx->sse) which build and send events for you. Use send only for raw protocol access.

stash

my $stash = $ctx->stash;   # PAGI::Stash instance

Returns a PAGI::Stash wrapping $scope->{'pagi.stash'}. Lazy-constructed and cached.

session

my $session = $ctx->session;   # PAGI::Session instance

Returns a PAGI::Session wrapping $scope->{'pagi.session'}. Lazy-constructed and cached. Dies if session middleware has not run. Use has_session to check availability first.

has_session

if ($ctx->has_session) {
    my $user_id = $ctx->session->get('user_id');
}

Returns true if session middleware has populated $scope->{'pagi.session'}.

state

my $state = $ctx->state;   # hashref

Returns $scope->{state} — the app/endpoint-level shared state.

Connection State

$ctx->connection;           # PAGI::Server::ConnectionState object
$ctx->is_connected;         # boolean
$ctx->is_disconnected;      # boolean
$ctx->disconnect_reason;    # string or undef
$ctx->on_disconnect($cb);   # register callback

Delegates to $scope->{'pagi.connection'}.

SEE ALSO

PAGI::Context::HTTP, PAGI::Context::WebSocket, PAGI::Context::SSE, PAGI::Stash, PAGI::Session

1 POD Error

The following errors were encountered while parsing the POD:

Around line 278:

Non-ASCII character seen before =encoding in '—'. Assuming UTF-8