Ctx

~import orisha/ctx

Orisha's request context — the contract.

lib/ctx.k · 17 tors

Orisha's request context — the contract. · 41 more lines
Orisha's request context — the contract. What a ctx is: the request-scoped scratch a handler's flow needs while it runs. Two halves, both request-shaped: cells named slots for values that must cross a `! row`-arm boundary. A row arm fires once per row and cannot produce the enclosing tor's terminal, so a value the `| done` continuation needs has to ride something that outlives the arm. That something is a cell: `ctx:set-int`/`ctx:set-text` inside the arm, `ctx:int`/ `ctx:take-*` after it. body the outgoing-body accumulator. A handler produces ONE response but a `! row` arm fires per row, so fragments fold into the buffer and `ctx:body-take` hands the finished page out. Why there is no Request handle on any of these: orisha workers are single-threaded and a handler runs to completion, so at most one request's context is live on a thread at a time — the cells and buffers are threadlocal in the companion and request-scoped by construction. The thread locality is the implementation's business; nothing here ever asks a caller to think about it. Cell semantics: - names are looked up per call over a small fixed table (24 int cells, 16 text cells). Name collisions across an app's modules share a cell — pick per-purpose names ("session.uid", not "uid"). - text cells hold at most 2048 bytes. `set-text` stores the WHOLE value and answers `ok`; when the value doesn't fit it stores a 2048-byte prefix and answers `truncated` — the branch is the record that it happened, never a silent chop. - `take-*` reads the cell and clears it; `int`/`text` read without clearing. A cell with nothing stashed answers null (or `default` on the `-or` spellings — the caller's sentinel, not ours). - `ctx:clear` wipes every cell. Call it at a request head if your flow leaves cells untaken; `ctx:begin` does NOT clear cells — a csrf stash primed before the first `begin` must survive it. Usage: ! row r |> db:col.int(stmt: r, index: 0): u |> orisha/ctx:set-int(name: "app.uid", v: u) | done d |> orisha/ctx:take-int-or(name: "app.uid", default: 0): uid |> if(uid > 0) ...

Phantom lifecycles

Derived from the phantom labels in the declarations below — state! issues an obligation the compiler will chase, !state discharges it, a bare state holds it without moving it. Nothing here is hand-drawn.

string carries the label <*> with no obligation attached — a dimension, not a lifecycle.

set-int

lib/ctx.k:43

take-int

lib/ctx.k:46

take-int-or

lib/ctx.k:47

set-text

<*> lib/ctx.k:49

take-text

lib/ctx.k:54

body-write

<*> lib/ctx.k:73

body-take

lib/ctx.k:74

frag-write

<*> lib/ctx.k:75

frag-take

lib/ctx.k:76

has-content

lib/ctx.k:77

body-status

lib/ctx.k:83

body-status-or

lib/ctx.k:84