Every function you hand FlowPanel — a widget query, a field's `defaultValue`, an
action's `run`, an adapter method — receives a context object. They all extend
`RequestContext`, so `session`, `role` and `scope` are always available.

## RequestContext

**RequestContext** — Everything the runtime knows about the caller. Every other context extends it.
| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `requestId` | `string` | no | Stable correlation id generated once per request. |
| `req` | `object` | yes | The incoming request. Built from the real headers, even during page renders. |
| `session` | `Session \| null` | yes | Result of `AuthConfig.session` for this request. |
| `role` | `string` | yes | Result of `AuthConfig.role` for this session. |
| `scope` | `Scope` | yes | Tenant scope resolved by the admin-wide `scope`. Null when unscoped. |
| `ip` | `string \| null` | yes | Caller IP from `x-forwarded-for`, when the proxy sets it. |
| `userAgent` | `string \| null` | yes |  |

`req` is built from the real incoming headers even during a server render, so
cookie- and header-based auth works identically in pages and API routes.

## QueryContext

Base context for reads. Widget queries and `options` resolvers receive this.

**QueryContext** — Base context for reads. Widget queries and option resolvers receive this.
| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `db` | `Db` | yes |  |
| `unsafe` | `object` | no | Explicit escape hatch for trusted application callbacks. |
| `dateRange` | `object` | yes | Active date range — the dashboard picker, or all-time elsewhere. |
| `searchParams` | `object` | yes | Query string of the page being rendered. |
| `signal` | `object` | yes | Aborted when the request is cancelled. Pass it to long queries. |
| `select` | `array` | no | Explicit adapter projection. Omitted only by the deprecated v1 bridge. |
| `boundScope` | `object` | no | Opaque request-bound tenant policy. |
| `applyScope` | `function` | no |  |
| `scopeRequired` | `boolean` | no |  |
| `requestId` | `string` | no | Stable correlation id generated once per request. |
| `req` | `object` | yes | The incoming request. Built from the real headers, even during page renders. |
| `session` | `Session \| null` | yes | Result of `AuthConfig.session` for this request. |
| `role` | `string` | yes | Result of `AuthConfig.role` for this session. |
| `scope` | `Scope` | yes | Tenant scope resolved by the admin-wide `scope`. Null when unscoped. |
| `ip` | `string \| null` | yes | Caller IP from `x-forwarded-for`, when the proxy sets it. |
| `userAgent` | `string \| null` | yes |  |

## ListQueryContext

What an adapter's `list` receives — everything needed to build one page of rows.

**ListQueryContext**
| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `filters` | `Record<string, unknown>` | yes | Filter values keyed by declared field name. |
| `sort` | `object \| null` | yes | Active sort, or null when the list is unsorted. |
| `page` | `number` | yes | 1-based page number. |
| `pageSize` | `number` | yes |  |
| `search` | `string` | yes | Current search term. Empty string when the box is blank. |
| `searchFields` | `array` | no | Allowlist of columns `search` is OR-ed across, taken verbatim from `resource.options.search`. |
| `softDelete` | `object` | no | Set when the resource declares `delete.softDelete` — deleted rows are filtered out. |
| `includeDeleted` | `boolean` | no | Include soft-deleted rows, for the "show deleted" view. |
| `db` | `Db` | yes |  |
| `unsafe` | `object` | no | Explicit escape hatch for trusted application callbacks. |
| `dateRange` | `object` | yes | Active date range — the dashboard picker, or all-time elsewhere. |
| `searchParams` | `object` | yes | Query string of the page being rendered. |
| `signal` | `object` | yes | Aborted when the request is cancelled. Pass it to long queries. |
| `select` | `array` | no | Explicit adapter projection. Omitted only by the deprecated v1 bridge. |
| `boundScope` | `object` | no | Opaque request-bound tenant policy. |
| `applyScope` | `function` | no |  |
| `scopeRequired` | `boolean` | no |  |
| `requestId` | `string` | no | Stable correlation id generated once per request. |
| `req` | `object` | yes | The incoming request. Built from the real headers, even during page renders. |
| `session` | `Session \| null` | yes | Result of `AuthConfig.session` for this request. |
| `role` | `string` | yes | Result of `AuthConfig.role` for this session. |
| `scope` | `Scope` | yes | Tenant scope resolved by the admin-wide `scope`. Null when unscoped. |
| `ip` | `string \| null` | yes | Caller IP from `x-forwarded-for`, when the proxy sets it. |
| `userAgent` | `string \| null` | yes |  |

`searchFields` is an allowlist taken verbatim from `resource.options.search`. An
adapter must never search columns outside it: that list is what keeps the search
box from becoming a way to probe columns the operator cannot otherwise see.

## ItemQueryContext

**ItemQueryContext** — Read context for a single row.
| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `string` | yes | Primary key of the requested row, as it appeared in the URL. |
| `db` | `Db` | yes |  |
| `unsafe` | `object` | no | Explicit escape hatch for trusted application callbacks. |
| `dateRange` | `object` | yes | Active date range — the dashboard picker, or all-time elsewhere. |
| `searchParams` | `object` | yes | Query string of the page being rendered. |
| `signal` | `object` | yes | Aborted when the request is cancelled. Pass it to long queries. |
| `select` | `array` | no | Explicit adapter projection. Omitted only by the deprecated v1 bridge. |
| `boundScope` | `object` | no | Opaque request-bound tenant policy. |
| `applyScope` | `function` | no |  |
| `scopeRequired` | `boolean` | no |  |
| `requestId` | `string` | no | Stable correlation id generated once per request. |
| `req` | `object` | yes | The incoming request. Built from the real headers, even during page renders. |
| `session` | `Session \| null` | yes | Result of `AuthConfig.session` for this request. |
| `role` | `string` | yes | Result of `AuthConfig.role` for this session. |
| `scope` | `Scope` | yes | Tenant scope resolved by the admin-wide `scope`. Null when unscoped. |
| `ip` | `string \| null` | yes | Caller IP from `x-forwarded-for`, when the proxy sets it. |
| `userAgent` | `string \| null` | yes |  |

## MutationContext

What an adapter's `create`, `update`, `delete` and `restore` receive.

**MutationContext** — Context for writes. Adapters receive this for create / update / delete / restore.
| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `db` | `Db` | yes | Your database client, exactly as handed to the adapter. |
| `input` | `Partial<Row>` | yes | Values to write, already coerced and validated. |
| `id` | `string` | no | Target row id. Absent on create. |
| `softDelete` | `object` | no | Set when the resource declares `delete.softDelete` — delete stamps the column. |
| `boundScope` | `object` | no | Opaque request-bound tenant policy. |
| `applyScope` | `function` | no |  |
| `scopeRequired` | `boolean` | no |  |
| `requestId` | `string` | no | Stable correlation id generated once per request. |
| `req` | `object` | yes | The incoming request. Built from the real headers, even during page renders. |
| `session` | `Session \| null` | yes | Result of `AuthConfig.session` for this request. |
| `role` | `string` | yes | Result of `AuthConfig.role` for this session. |
| `scope` | `Scope` | yes | Tenant scope resolved by the admin-wide `scope`. Null when unscoped. |
| `ip` | `string \| null` | yes | Caller IP from `x-forwarded-for`, when the proxy sets it. |
| `userAgent` | `string \| null` | yes |  |

## ActionContext

What every action's `run` receives.

**ActionContext** — What every action's `run` receives as its last argument.
| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `db` | `Db` | yes |  |
| `unsafe` | `object` | no | Explicit escape hatch for trusted action implementations. |
| `actorId` | `string \| null` | yes | Caller identity — same derivation the audit trail uses (`auth.userId`, then `session.id`, then `session.user.id`). |
| `publish` | `function` | yes | Push a realtime message to every connected admin. |
| `requestId` | `string` | no | Stable correlation id generated once per request. |
| `req` | `object` | yes | The incoming request. Built from the real headers, even during page renders. |
| `session` | `Session \| null` | yes | Result of `AuthConfig.session` for this request. |
| `role` | `string` | yes | Result of `AuthConfig.role` for this session. |
| `scope` | `Scope` | yes | Tenant scope resolved by the admin-wide `scope`. Null when unscoped. |
| `ip` | `string \| null` | yes | Caller IP from `x-forwarded-for`, when the proxy sets it. |
| `userAgent` | `string \| null` | yes |  |

## Filter values

Most filters arrive in `ctx.filters` as plain scalars. Two kinds arrive
structured, and ship with runtime guards so you can narrow `unknown` safely.

### FilterRangeValue

Produced by `daterange` and `numeric-range` filters.

**FilterRangeValue** — Structured decode of a `numeric-range` / `daterange` `FilterDef` value.
| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `op` | `"range"` | yes |  |
| `gte` | `object \| number` | no |  |
| `lte` | `object \| number` | no |  |

### FilterInValue

Produced by `multiselect` filters — match any of `values`.

**FilterInValue** — Structured decode of a `multiselect` `FilterDef` value: match ANY of `values`.
| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `op` | `"in"` | yes |  |
| `values` | `array` | yes |  |

### Narrowing them

```ts excerpt
import { isFilterInValue, isFilterRangeValue } from "@flowpanel/kit";

for (const [field, value] of Object.entries(ctx.filters)) {
  if (isFilterRangeValue(value)) {
    // value.gte / value.lte
  } else if (isFilterInValue(value)) {
    // value.values
  } else {
    // a scalar
  }
}
```

## Request-scoped access

When you cannot thread a context through — a helper several calls deep, say —
read it from AsyncLocalStorage instead of reaching for a global.

```ts excerpt
import { getRequestContext, tryGetRequestContext } from "@flowpanel/kit";

const ctx = getRequestContext();      // throws outside a request
const maybe = tryGetRequestContext(); // null outside a request
```

`runWithRequestContext(ctx, fn)` establishes the scope; FlowPanel already wraps
every adapter call and every action `run` in it. This ambient state lives in
AsyncLocalStorage so concurrent requests do not share it.
