Processors
A processor fires after the server processes a request, before the response is sent to the client. Processors can inspect and modify the response — status code, headers, and body — or leave it unchanged.
Processors are stored as HTML Microdata items and discovered automatically via selector queries, the same way triggers are.
Quick example
This processor intercepts 404 responses and replaces the body with a custom error page:
<div itemscope itemtype="https://pagelove.org/Processor">
<meta itemprop="resource" content="*">
<meta itemprop="method" content="GET">
<meta itemprop="status" content="404">
<div itemprop="action" itemscope itemtype="https://pagelove.org/Sessel">
<script itemprop="source" type="text/sessel">
@schema Context url("https://pagelove.org/Context");
Context.response.body = "<h1>Page not found</h1><p>Sorry, that page doesn't exist.</p>"
</script>
</div>
</div>
Request lifecycle
Processors sit near the end of the request lifecycle:
- Request arrives
- Trigger phase — matching triggers execute
- Core processing (GET, PUT, POST, DELETE, etc.)
- Processor phase — matching processors execute in order
- Async dispatch — queued outbound HTTP requests fire
- Response sent
Properties
All filter properties are optional. Omitting a filter means "match all."
resource
One or more glob patterns. The processor fires only when the request path matches at least one pattern. Same syntax as trigger resource.
<meta itemprop="resource" content="/blog/*">
method
HTTP methods to match. Same rules as trigger method.
<meta itemprop="method" content="GET">
selector
A CSS selector with semantic matching. Same behaviour as trigger selector.
status
HTTP status codes or class prefixes. The processor fires only when the response status matches one of the listed values.
<!-- Exact match -->
<meta itemprop="status" content="404">
<!-- Class prefix — matches any status 400-499 -->
<meta itemprop="status" content="4xx">
<!-- Multiple patterns (OR'd) — use separate elements -->
<meta itemprop="status" content="404">
<meta itemprop="status" content="410">
<!-- Match all errors -->
<meta itemprop="status" content="4xx">
<meta itemprop="status" content="5xx">
An empty or omitted status filter matches any status code.
when
A Sessel or JavaScript expression that gates execution. Same behaviour as trigger when, but with access to the response for status-conditional gating.
<div itemprop="when" itemscope itemtype="https://pagelove.org/Sessel">
<script itemprop="source" type="text/sessel">
@schema Context url("https://pagelove.org/Context");
Context.response.status == 404
</script>
</div>
The JavaScript equivalent reads ctx.response instead of Context.response:
<div itemprop="when" itemscope itemtype="https://pagelove.org/JavaScript/Module">
<script type="module" itemprop="source">
export default function(ctx) {
return ctx.response.status === 404;
}
</script>
</div>
action
One or more actions, same types as trigger actions: Sessel, JavaScript, and outbound HTTP request.
<div itemprop="action" itemscope itemtype="https://pagelove.org/JavaScript/Module">
<script type="module" itemprop="source">
export default function(ctx) {
throw {
schema_url: "https://pagelove.org/HTTPResponse",
status: 200,
body: "<p>Custom 404 page</p>"
};
}
</script>
</div>
A JavaScript processor action cannot mutate the response directly (see Response-mutation asymmetry) — it must throw an HTTPResponse to replace it, the same shape used by trigger chain termination.
Context
Sessel expressions in processors have access to both Context.request and Context.response:
| Property | Type | Description |
|---|---|---|
Context.request.method |
String | HTTP method |
Context.request.path |
String | Request path |
Context.request.headers |
Map | Request headers |
Context.request.query |
Map | Query string parameters |
Context.response.status |
Number | HTTP status code |
Context.response.body |
String | Response body (fully buffered) |
Context.response.headers |
Map | Response headers |
JavaScript when/action modules receive the equivalent context as their first positional argument (conventionally named ctx):
| Property | Type | Description |
|---|---|---|
ctx.request |
Object | Same shape as the trigger JavaScript context |
ctx.response.status |
Number | HTTP status code |
ctx.response.body |
String | Response body (fully buffered) |
ctx.response.headers |
Object | Response headers |
Reading and writing the response
Both reading and writing use Context.response:
Context.response.status // 404
Context.response.body // "<p>Not Found</p>"
Context.response.status = 200
Context.response.body = "<p>Found it after all</p>"
Pass-through
If no Sessel action modifies any Context.response property, the original response is forwarded unchanged. Processors are pass-through by default — they only affect the response when an action explicitly writes to Context.response.status, Context.response.body, or Context.response.headers.
Response-mutation asymmetry
This direct-mutation style is Sessel-only. JavaScript processor actions run the same evaluation path used for when gates, which does not detect writes to ctx.response — assigning to it has no effect. A JavaScript processor action that needs to change the response must throw an HTTPResponse (see action above) rather than mutate ctx.response in place.
Writing from processors
Like triggers, processor Sessel actions can write to the database using Pagelove.PUT() and Pagelove.DELETE(). This, too, is Sessel-only — see Writing from triggers for the JavaScript alternative (an outbound HTTP request).
<div itemprop="action" itemscope itemtype="https://pagelove.org/Sessel">
<script itemprop="source" type="text/sessel">
@schema Pagelove url("https://pagelove.org/1.0");
@schema AuditLog url("https://example.com/AuditLog");
let log = new AuditLog { status: Context.response.status };
Pagelove.PUT(log, "/audit/latest.html")
</script>
</div>
Side-effect writes from processors follow the same semantics as trigger writes — see Writing from triggers for details.
Chain termination
A Sessel or JavaScript action can terminate the processor chain by throwing an HTTPResponse, the same mechanism as in triggers. The thrown response replaces whatever core produced.
Execution order
Same rules as triggers — lexicographic by document path, document order within each document.
Error handling
Same rules as triggers — malformed Microdata is skipped, Sessel or JavaScript runtime errors surface as error responses.
See also
- Triggers — fire before core processing
- Outbound HTTP requests — async outbound HTTP action
- Sessel language reference — expression language
- JavaScript in schemas — JavaScript as a Pagelove binding language