# @gyral/core

> Gyral core: Model-View-Intent web components on the web platform, with their own view layer.

Source: https://gyral.dev/docs/api/core/

Gyral core: Model-View-Intent web components on the web platform, with their own view layer.

```sh
npm install @gyral/core
```

> **Writing code? Start from working code, not from this page.** [Recipes](https://gyral.dev/docs/recipes/) has a task list with filters and a signup form, with their tests; [Forms](https://gyral.dev/docs/forms/) opens with a complete validated form; [Getting started](https://gyral.dev/docs/getting-started/) builds a first component. Adapt the closest example and check it, then look a name up here when you are about to call it.

Generated from the package's type declarations. Entry points: [`@gyral/core`](#gyralcore), [`@gyral/core/storage`](#gyralcorestorage), [`@gyral/core/indexeddb`](#gyralcoreindexeddb), [`@gyral/core/vite`](#gyralcorevite), [`@gyral/core/eslint`](#gyralcoreeslint).

## `@gyral/core`

### Functions

| Name | Summary |
| --- | --- |
| [`call`](#core-call) | A command that calls `method` with `args` on the first element matching `selector` inside this component (its shadow root, or its own children in light-DOM mode) once the current update has rendered: |
| [`changed`](#core-changed) | Narrows a `StoreChanged` message to one store's typed state, or `undefined`. |
| [`child`](#core-child) | Intent parser for a child component's outputs, typed by the child's output union. |
| [`command`](#core-command) | Builds a command whose mappers are typed by the driver's input, output and error. |
| [`componentsReady`](#core-componentsready) | Resolves once every rendered tag the component loader knows (in the document and in the shadow roots of components it defined) is defined, and the page has settled. |
| [`css`](#core-css) | Component CSS: `` css`p { margin-block: ${GAP}px; }` ``. |
| [`define`](#core-define) | Compiles a Model-View-Intent spec into a custom element and registers it under `tag`. |
| [`defineDriver`](#core-definedriver) | Defines a driver. |
| [`defineForm`](#core-defineform) |  |
| [`defineHook`](#core-definehook) | Defines an element hook: a small behaviour attached to the element it sits on. |
| [`defineStore`](#core-definestore) | Defines a store. |
| [`each`](#core-each) | A keyed list. |
| [`field`](#core-field) | Intent parser for one control (pair with `data-intent-on="input"` for live checks). |
| [`fieldErrors`](#core-fielderrors) | Groups issues by field name. |
| [`focus`](#core-focus) | A command that focuses the first element matching `selector` inside this component (its shadow root, or its own children in light-DOM mode) once the current update has rendered: |
| [`form`](#core-form) | Intent parser for a `<form data-intent>` submission, validated by a schema. |
| [`html`](#core-html) | The template tag: `` html`<p>${s.text}</p>` ``. |
| [`intentsOf`](#core-intentsof) | A component's intent names as a module-level constant, so pure list rows can name intents without passing them through `pick` (rows must be pure). |
| [`invalid`](#core-invalid) | Mirrors model errors to native validity: `setCustomValidity` and `aria-invalid`. |
| [`keyed`](#core-keyed) | Renders `value`, and replaces it with a fresh copy whenever `key` changes: a form cleared after sending, a CSS animation restarted, a child component reset. |
| [`outputs`](#core-outputs) | `emit`, typed by a component's output union (the 4th `define` type parameter), so an output of the wrong shape fails to compile. |
| [`provideDrivers`](#core-providedrivers) | Makes `element` a driver provider for every Gyral component below it (including inside shadow roots). |
| [`raw`](#core-raw) | Trusted markup from your own code (Markdown output, JSON-LD), parsed in a child hole and re-parsed only when the string changes. |
| [`retry`](#core-retry) | The same driver (same name, so substitution by name still works), with `run` retried when it rejects: up to `policy.times` more attempts, after `delayMs` (doubling from it with `backoff: 'exponential'`; a random part of it with `jitter`), and only for failures `retryIf` accepts. |
| [`send`](#core-send) | A command that sends `msg` to the component's instance of `store`. |
| [`settled`](#core-settled) | Resolves once the page is quiet: every connected Gyral host has rendered its latest state, view-transition updates have run, focus commands and outputs have been delivered, and messages have stopped arriving (a stream's next value, already on its way in a microtask, is waited for; long-lived commands themselves are not). |
| [`subscription`](#core-subscription) | A streaming driver over a source Gyral doesn't own. |
| [`svg`](#core-svg) | The SVG fragment tag: `` svg`<path d=${s.d} />` ``. |

#### `call`

```ts
function call(selector: string, method: string, ...args: readonly unknown[]): Command<never>;
```

A command that calls `method` with `args` on the first element matching `selector` inside
this component (its shadow root, or its own children in light-DOM mode) once the current
update has rendered:


```ts
Start: (s) => [{ ...s, playing: true }, [call('video', 'play')]]
Join: (s, m) => [s, [call('room-stage', 'join', m.room)]]
```

`args` must be plain data (structured-cloneable), so the command stays data a test can
compare. A missing element or method, or a method that throws or rejects, is reported
through the error channel (ADR 0024); the error's cause says which.

#### `changed`

```ts
function changed<S>(store: StoreRef<S>, msg: StoreChanged): {
    readonly state: S;
    readonly prev: S;
} | undefined;
```

Narrows a `StoreChanged` message to one store's typed state, or `undefined`.

#### `child`

```ts
function child<O extends Tagged, E extends Element, M>(source: ChildSource<O, E>, toMsg: (output: O, el: E) => M | undefined): (input: IntentInput) => ReturnType<IntentParser<M>>;
```

Intent parser for a child component's outputs, typed by the child's output union.
`el` is the child element, for reading its props (an item id, for example).

Pass `() => Child` when the class isn't defined yet, e.g. a component that contains
itself; annotate the constant's type so TypeScript accepts the self-reference:


```ts
const Folder: GyralElementClass<State, Msg, Props, Out> = define()('x-folder', {
  intent: { Child: child(() => Folder, (out, el) => …) }, …
});
```


#### `command`

```ts
function command<I, O, E, const MS = never, const MF = MS>(driver: Driver<I, O, E>, input: I, handlers?: CommandHandlers<O, E, MS, MF>): Command<MS | MF>;
```

Builds a command whose mappers are typed by the driver's input, output and error. Without
handlers (or without `onSuccess`) it answers with no message: `command(log, line)` is a
`Command<never>`, which fits any `Command<Msg>`.

#### `componentsReady`

```ts
function componentsReady(): Promise<void>;
```

Resolves once every rendered tag the component loader knows (in the document and in the
shadow roots of components it defined) is defined, and the page has settled. Await it in
browser tests and page scripts after the loader has run (page scripts placed after it; in
tests, import `virtual:gyral/components` first).

#### `css`

```ts
function css(strings: TemplateStringsArray, ...values: readonly CssValue[]): StyleSource;
```

Component CSS: `` css`p { margin-block: ${GAP}px; }` ``. Strings and numbers are inserted as
written, so values from your own constants need no wrapper.

#### `define`

```ts
function define(): <S, M extends Tagged = never, P extends object = object, N extends string = never>(tag: string, spec: ComponentSpec<S, M, P, N>) => GyralElementClass<S, M, P, never, N>;
function define<S, M extends Tagged = never, P extends object = object, O extends Tagged = never>(): Definer<S, M, P, O>;
```

Compiles a Model-View-Intent spec into a custom element and registers it under `tag`. Two
calls (ADR 0023): the first takes the types, the second the tag and spec, from whose
`intent` keys it infers the component's intent names.


```ts
define<State, Msg>()('x-counter', { init, intent: { Increment: … }, update, view })
```

With no type arguments, `define()('x-badge', spec)` infers the state, messages, props and
intent names from the spec. Guide: https://gyral.dev/docs/components/

#### `defineDriver`

```ts
function defineDriver<I, O, E = unknown>(spec: Driver<I, O, E> | DriverToken<E> | string): Driver<I, O, E>;
```

Defines a driver. Given an implementation (`run`), it returns it as is, so literals infer
`I`, `O` and `E`. Given only a name, it returns a typed **token**: commands built from it are
typed, and it runs only when a provider (`provideDrivers`) or `el.drivers` supplies a driver of
that name. A command whose token nothing provides fails with G0043, naming the driver.

#### `defineForm`

```ts
function defineForm<Schema extends StandardSchemaV1>(schema: Schema, options?: FormOptions<Schema>): FormDefinition<Schema>;
```


#### `defineHook`

```ts
function defineHook<A extends readonly unknown[]>(spec: HookSpec<A>): (...args: A) => HookResult<A>;
```

Defines an element hook: a small behaviour attached to the element it sits on.

#### `defineStore`

```ts
function defineStore<S, M extends Tagged>(name: string, spec: StoreSpec<S, M>): Store<S, M>;
```

Defines a store. `name` keys it in registries, seeds and overrides, so keep it unique.

#### `each`

```ts
function each<T>(items: readonly T[], key: (item: T) => string | number, row: (item: T) => ChildValue): ListResult;
function each<T, P>(items: readonly T[], key: (item: T) => string | number, row: (item: T, picked: P) => ChildValue, pick: (item: T) => P): ListResult;
```

A keyed list. `key` must give each item a unique string or number; `row`
must depend only on its arguments: a row re-renders only when its item object or its `pick`
result changes.

#### `field`

```ts
function field<Schema extends StandardSchemaV1, M>(schema: Schema, toMsg: (value: Out<Schema>) => M | undefined): (input: IntentInput) => ReturnType<IntentParser<M>>;
```

Intent parser for one control (pair with `data-intent-on="input"` for live checks).
Validates `checked` for checkboxes and radios, otherwise `value`.

#### `fieldErrors`

```ts
function fieldErrors(issues: readonly FieldIssue[]): Readonly<Record<string, readonly string[]>>;
```

Groups issues by field name. Call it in the `IntentRejected` reducer and keep the result in
state: `invalid()` re-applies an error only when it gets a new array.

#### `focus`

```ts
function focus(selector: string, options?: FocusOptions): Command<never>;
```

A command that focuses the first element matching `selector` inside this component (its
shadow root, or its own children in light-DOM mode) once the current update has rendered:


```ts
NextPage: (s, m) => [{ ...s, page: m.page }, [focus('h2', { preventScroll: true })]]
```

Non-focusable targets (a heading) need `tabindex="-1"` in the view.

#### `form`

```ts
function form<Schema extends StandardSchemaV1, M>(definition: FormDefinition<Schema> | Schema, toMsg: (data: Out<Schema>, formData: FormData) => M | undefined): (input: IntentInput) => ReturnType<IntentParser<M>>;
```

Intent parser for a `<form data-intent>` submission, validated by a schema. `toMsg` also
gets the raw `FormData`, for sending the submission to the server (`submitForm` in
`@gyral/http`) after it passed client-side validation.

#### `html`

```ts
const html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult
```

The template tag: `` html`<p>${s.text}</p>` ``.

#### `intentsOf`

```ts
function intentsOf<C extends {
    readonly intentNames?: string;
}>(): IntentNames<NonNullable<C['intentNames']>>;
```

A component's intent names as a module-level constant, so pure list rows can name intents
without passing them through `pick` (rows must be pure). The same object
the view gets as `i`, typed by the component's class (ADR 0023). A row that names intents
this way needs an explicit return type, or the row, the view and the class infer each other
in a circle:


```ts
const i = intentsOf<typeof TodoList>();
const Row = (t: Todo): TemplateResult => html`<input value=${t.id} data-intent=${i.Toggle} />`;
```


#### `invalid`

```ts
const invalid: (errors?: Errors) => HookResult<[errors?: Errors]>
```

Mirrors model errors to native validity: `setCustomValidity` and `aria-invalid`.


```ts
<input name="email" ${invalid(s.errors.email)} />
```

The custom error clears when the control fires `input` or `change` (the user edited it), when
the bound errors become `undefined` or empty (the model cleared them), or when a submit finds
the error stale (the value changed without an event, e.g. set from code): then the error is
dropped and the form is submitted again once. An error is applied again only when a new value
arrives (keep `fieldErrors()` results in state): `client` runs only when the errors change.

#### `keyed`

```ts
function keyed(key: string | number, value: ChildValue): ListResult;
```

Renders `value`, and replaces it with a fresh copy whenever `key` changes: a form cleared
after sending, a CSS animation restarted, a child component reset.

#### `outputs`

```ts
function outputs<O extends Tagged>(): (output: O) => Command<never>;
```

`emit`, typed by a component's output union (the 4th `define` type parameter), so an
output of the wrong shape fails to compile. A module-level constant, like `intentsOf()`:


```ts
type Out = { readonly _tag: 'Picked'; readonly id: number };
const emit = outputs<Out>();
// in update: Pick: (s, m) => [s, [emit({ _tag: 'Picked', id: m.id })]]
```

Costs nothing at run time: it returns `emit` itself.

#### `provideDrivers`

```ts
function provideDrivers(element: Element, drivers: DriverOverrides): () => void;
```

Makes `element` a driver provider for every Gyral component below it (including inside
shadow roots). Returns a function that removes the overrides. Use it on a test container
or an app root; a `<gyral-drivers>` element with a `.drivers` property works the same way.

#### `raw`

```ts
function raw(html: string): RawResult;
```

Trusted markup from your own code (Markdown output, JSON-LD), parsed in a child hole and
re-parsed only when the string changes. Never pass user input.

#### `retry`

```ts
function retry<I, O, E>(driver: Driver<I, O, E>, policy: RetryPolicy<E>): Driver<I, O, E>;
```

The same driver (same name, so substitution by name still works), with `run` retried when it
rejects: up to `policy.times` more attempts, after `delayMs` (doubling from it with
`backoff: 'exponential'`; a random part of it with `jitter`), and only for failures
`retryIf` accepts. An abort (the command switched away, the component disconnected) ends it
at once and is never retried. Wrap where the driver is chosen: `provideDrivers` at app
setup, `el.drivers`, or a test fake.


```ts
const api = retry(makeHttpDriver({ baseUrl: '/api', timeoutMs: 8000 }), {
  times: 3, delayMs: 300, backoff: 'exponential', jitter: true, retryIf: retryableHttpError,
});
const feed = retry(subscription('feed', connect), { times: Infinity, delayMs: 1000 });
```


#### `send`

```ts
function send<S, M extends Tagged>(store: Store<S, M>, msg: NoInfer<M>): Command<never>;
```

A command that sends `msg` to the component's instance of `store`. Writes are commands.

#### `settled`

```ts
function settled(): Promise<void>;
```

Resolves once the page is quiet: every connected Gyral host has rendered its latest state,
view-transition updates have run, focus commands and outputs have been delivered, and
messages have stopped arriving (a stream's next value, already on its way in a microtask, is
waited for; long-lived commands themselves are not). Rejects with the loop guard's error
when rendering never settles in development (components feeding each other).


```ts
el.send({ _tag: 'Increment' });
await settled();
```


#### `subscription`

```ts
function subscription<O, I = undefined, E = unknown>(name: string, subscribe: (emit: (value: O) => void, ctx: SubscriptionContext<I>) => Unsubscribe, options?: SubscriptionOptions<E>): Driver<I, O, E>;
```

A streaming driver over a source Gyral doesn't own. `subscribe` starts listening, calls
`emit` with each value (it may emit the current value at once) and returns how to stop
(a function, or an object with `unsubscribe()`).
The subscription lasts while the command runs; aborting it (lane `switch`, disconnect)
releases the source, and emits after that are ignored. Substitute it by `name` in tests.


```ts
const cart = subscription<CartState>('cart', (emit) => {
  emit(reduxStore.getState());
  return reduxStore.subscribe(() => emit(reduxStore.getState()));
});
// subscriptions: () => [command(cart, undefined, { onSuccess: (c) => ({ _tag: 'Cart', c }) })]
```


#### `svg`

```ts
const svg: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult
```

The SVG fragment tag: `` svg`<path d=${s.d} />` ``. Its top level is SVG content, so it
renders only inside an `<svg>` (or another SVG element) of an html template. A whole `<svg>` graphic is written with html.

### Classes

| Name | Summary |
| --- | --- |
| [`GyralError`](#core-gyralerror) | A failure Gyral caught, with its context; `cause` is what was thrown. |

#### `GyralError`

```ts
class GyralError extends Error {
  readonly name = "GyralError";
  readonly component: string | undefined;
  readonly phase: ErrorPhase;
  readonly msg: string | undefined;
  constructor(phase: ErrorPhase, text: string, cause: unknown, component?: string, msg?: string);
}
```

A failure Gyral caught, with its context; `cause` is what was thrown.

### Constants

| Name | Summary |
| --- | --- |
| [`nothing`](#core-nothing) | Renders nothing in a child hole; removes the attribute in an attribute hole. |
| [`OUTPUT_EVENT`](#core-output_event) | Event a child component dispatches on its host to send an output up (ADR 0010); public. |
| [`prop`](#core-prop) | The prop builders. |

#### `nothing`

```ts
const nothing: typeof nothing
```

Renders nothing in a child hole; removes the attribute in an attribute hole.

#### `OUTPUT_EVENT`

```ts
const OUTPUT_EVENT: "gyral-output"
```

Event a child component dispatches on its host to send an output up (ADR 0010); public.

#### `prop`

```ts
const prop: { readonly string: Scalar<string>; readonly number: Scalar<number>; readonly boolean: <O extends boolean = boolean>(opts?: (Common & { readonly schema?: Schema<O> | undefined; } & { readonly default?: NoInfer<O> | undefined; }) | undefined) => Prop<O>; readonly json: WithSchema; readonly value: WithSchema; }
```

The prop builders.

### Types and interfaces

You rarely write most of these yourself: TypeScript infers them from the functions above. Look one up when an example or a compiler error names it.

| Name | Summary |
| --- | --- |
| [`AnyDriver`](#core-anydriver) | Any driver, with its input type erased. |
| [`AnyStore`](#core-anystore) | Any store, with its types erased (for declarations and registries). |
| [`AnyStoreInstance`](#core-anystoreinstance) | Any store instance, with its types erased. |
| [`CallInput`](#core-callinput) | The input of a `call()` command. |
| [`ChildSource`](#core-childsource) | A child class, or a function returning it (for recursive or later-defined components). |
| [`ChildValue`](#core-childvalue) | Everything a child hole accepts. |
| [`Command`](#core-command) | A side effect described as data, plus pure mappers from its outcome to messages. |
| [`CommandHandlers`](#core-commandhandlers) | How a command's outcome becomes a message. |
| [`CommandInfo`](#core-commandinfo) |  |
| [`ComponentSpec`](#core-componentspec) | A component spec. |
| [`Concurrency`](#core-concurrency) | How commands in the same lane interact. |
| [`CssValue`](#core-cssvalue) | A `css` interpolation: inserted as written (another `css` value inserts its text). |
| [`Ctx`](#core-ctx) | Read-only context handed to every reducer and to the view (ADR 0007, 0013). |
| [`Definer`](#core-definer) | The second call of `define()`: takes the tag and the spec, infers the intent names. |
| [`Driver`](#core-driver) | Performs one kind of side effect. |
| [`DriverContext`](#core-drivercontext) |  |
| [`DriverOverrides`](#core-driveroverrides) | Drivers substituted by name (test fakes, configured instances): `el.drivers`, `provideDrivers`. |
| [`DriverToken`](#core-drivertoken) | A driver with no implementation of its own (`defineDriver<I, O, E>('session')`): commands built from it are typed by `I`, `O` and `E`, and it runs only when `provideDrivers` or `el.drivers` supplies a driver of that name (ADR 0006 addendum "Driver tokens"). |
| [`Errored`](#core-errored) | Framework message (ADR 0024): an update, an intent parser or a command of this component failed. |
| [`ErrorPhase`](#core-errorphase) | Where a failure happened. |
| [`FieldIssue`](#core-fieldissue) | One validation problem. |
| [`FieldName`](#core-fieldname) | The definition's field names, when its input is an object; otherwise any string. |
| [`FocusOptions`](#core-focusoptions) |  |
| [`FormAssociation`](#core-formassociation) | Pure model projections applied by Gyral to the host's ElementInternals (ADR 0008). |
| [`FormControlValidity`](#core-formcontrolvalidity) | Native constraint flags and a validation bubble anchor in the component's render root. |
| [`FormControlValue`](#core-formcontrolvalue) | A native submitted value, or null to omit this component from the submission. |
| [`FormDefinition`](#core-formdefinition) | A form's schema, shared by the client component and (with `@gyral/ssr`) the server route. |
| [`FormDisabled`](#core-formdisabled) | Framework message: the host or its fieldset changed the effective disabled state. |
| [`FormFields`](#core-formfields) | Submitted text values of a form (Files are dropped: they can't be re-filled). |
| [`FormOptions`](#core-formoptions) |  |
| [`FormReset`](#core-formreset) | Framework message: the owning native form was reset. |
| [`FormResult`](#core-formresult) |  |
| [`FormStateRestore`](#core-formstaterestore) | Framework message: native session restoration or autofill supplied control state. |
| [`FormValue`](#core-formvalue) |  |
| [`FormValues`](#core-formvalues) | A form's submitted text values, keyed by the definition's fields, for re-rendering the form with what was typed (sticky fields). |
| [`GyralElement`](#core-gyralelement) | The custom element `define()` produces: a plain `HTMLElement`. |
| [`GyralElementClass`](#core-gyralelementclass) |  |
| [`Head`](#core-head) | A page's head: what `renderPage()` writes on the server and `setHead()` applies on the client. |
| [`HeadLink`](#core-headlink) | A metadata `<link>`: `alternate` (with `hreflang`), `icon` (with `sizes`, `type`), `manifest` (with `crossorigin`), `prev`/`next`, … Any other attribute is written as given. |
| [`HeadMeta`](#core-headmeta) | `<meta name content>`, or `<meta property content>` (Open Graph uses `property`). |
| [`HookAttributes`](#core-hookattributes) | Attributes a hook's server half adds to the start tag (`true`: present, no value). |
| [`HookResult`](#core-hookresult) | What a hook returns in a template: `<input ${invalid(errors)}>`. |
| [`HookSpec`](#core-hookspec) |  |
| [`Hydrated`](#core-hydrated) | Framework message: the component is live in the browser (ADR 0012 addendum). |
| [`HydrateStrategy`](#core-hydratestrategy) | When a server-rendered component hydrates. |
| [`IntentInput`](#core-intentinput) | What the intent layer sees when an element's intent fires. |
| [`IntentNames`](#core-intentnames) | A component's intent names, the keys of its `intent` object, as the view's `i` and as `intentsOf<typeof C>()`, so `data-intent=${i.Increment}` is checked (ADR 0023). |
| [`IntentParser`](#core-intentparser) | Parses a platform event into one message variant, `IntentRejected`, or `undefined` to decline it: a synchronous `undefined` passes the event to the next intent outward for the same event. |
| [`IntentRejected`](#core-intentrejected) | Framework message: an intent's input failed schema validation (ADR 0008). |
| [`Intents`](#core-intents) | Intent parsers keyed by intent name (ADR 0001, ADR 0023). |
| [`JsonValue`](#core-jsonvalue) | A JSON value: what a JSON-LD block holds. |
| [`ListResult`](#core-listresult) | A keyed list for a child hole: what `each(…)` returns. |
| [`Next`](#core-next) | What a reducer (or `init`) returns: new state, optionally with commands to run. |
| [`OutputEvent`](#core-outputevent) | The `gyral-output` event a child dispatches on its host for each output (ADR 0010): `detail` is the output. |
| [`OutputsOf`](#core-outputsof) | The output union of a `define()` class: `OutputsOf<typeof Item>`. |
| [`OutputSource`](#core-outputsource) | Anything `child()` can read an output type from: a `define()` class. |
| [`ParserCtx`](#core-parserctx) | What an intent parser gets besides the input: the reducers' context plus the component's current state, read-only. |
| [`ParseResult`](#core-parseresult) | What an intent parser returns: a message, `IntentRejected` (invalid input), or `undefined` to decline the event. |
| [`ParserFor`](#core-parserfor) | The parser for intent name `K` (ADR 0023): a name that is a message tag parses into that variant; any other name is an intent of its own whose parser may produce any message. |
| [`Prop`](#core-prop) | One declared prop: what a builder returns. |
| [`PropDeclarations`](#core-propdeclarations) | The prop builders for props type `P`: one `prop.*` builder per key. |
| [`PropGuard`](#core-propguard) | A plain type guard, accepted by `prop.value` and `prop.json` in place of a schema. |
| [`PropKind`](#core-propkind) |  |
| [`PropsChanged`](#core-propschanged) | Framework message: declared props changed after the first render (ADR 0007). |
| [`PropsOf`](#core-propsof) | The props type of a table of builders: `PropsOf<typeof props>`. |
| [`RawResult`](#core-rawresult) | Trusted markup for a child hole: what `raw(html)` returns. |
| [`Reducer`](#core-reducer) | One entry of `update`: takes the state, one message (`Msg`, a variant of `M` or a framework message) and the read-only context, and returns the next state, optionally with commands that produce more `M` messages. |
| [`RetryPolicy`](#core-retrypolicy) | How `retry(driver, policy)` retries a rejected run (retry.ts). |
| [`SeedCheck`](#core-seedcheck) | The result of checking a seed against a store's schema. |
| [`ShadowOption`](#core-shadowoption) | `spec.shadow`: `true` (the default) or an object for a shadow root, `false` for light DOM. |
| [`StandardSchemaV1`](#core-standardschemav1) | The Standard Schema interface. |
| [`Stateless`](#core-stateless) | State of a component that keeps none (a pure view of its props). |
| [`Store`](#core-store) |  |
| [`StoreChanged`](#core-storechanged) | Framework message: a store this component reads changed (ADR 0013). |
| [`StoreInstance`](#core-storeinstance) | A live store: one per page on the client, one per request on the server, one per test. |
| [`StoreOverrides`](#core-storeoverrides) | Store instances substituted by store name (tests, islands). |
| [`StoreReader`](#core-storereader) | Reads a declared store's current state, typed by the store (ADR 0013). |
| [`StoreRef`](#core-storeref) | What `ctx.read()` accepts: anything that names a store and carries its state type. |
| [`StoreResolver`](#core-storeresolver) | Finds a store's instance in a scope (a registry). |
| [`StoreSpec`](#core-storespec) |  |
| [`StoreUpdate`](#core-storeupdate) | One pure reducer per message tag, like a component's `update` but without context. |
| [`Styles`](#core-styles) | What `spec.styles` accepts: `css` values, plain CSS strings (e.g. |
| [`StyleSource`](#core-stylesource) | What `css` returns: CSS text, mapped to one shared `CSSStyleSheet` in the browser. |
| [`SubscriptionContext`](#core-subscriptioncontext) |  |
| [`SubscriptionOptions`](#core-subscriptionoptions) | Driver options a subscription may set. |
| [`Tagged`](#core-tagged) | Every message is a tagged object; the tag is also the intent name used in markup. |
| [`TemplateResult`](#core-templateresult) | What `html` returns: a template plus this render's values. |
| [`Unsubscribe`](#core-unsubscribe) | How to stop listening: a function, or an object with `unsubscribe()` (RxJS, XState). |
| [`Update`](#core-update) | One pure reducer per message tag (exhaustive by construction), plus optional reducers for framework messages. |
| [`Variant`](#core-variant) | The member of message union `M` whose tag is `K`: `Variant<Msg, 'Add'>`. |

#### `AnyDriver`

```ts
type AnyDriver = Driver<never, unknown>;
```

Any driver, with its input type erased. Every `Driver<I, O, E>` (from `defineDriver`,
`subscription`, a test fake) is assignable to it as is: no cast.

#### `AnyStore`

```ts
interface AnyStore {
    readonly name: string;
    instance(initial?: never): AnyStoreInstance;
    checkSeed(seed: unknown): SeedCheck<unknown>;
}
```

Any store, with its types erased (for declarations and registries).

#### `AnyStoreInstance`

```ts
interface AnyStoreInstance {
    readonly store: AnyStore;
    readonly state: unknown;
    send(msg: never): void;
    subscribe(listener: (state: unknown, prev: unknown) => void): () => void;
    dispose(): void;
    /**
     * Called by the registry that holds this instance, so the store's `send(other, msg)`
     * commands reach `other`'s instance in the same scope.
     */
    bindScope?(resolve: StoreResolver): void;
}
```

Any store instance, with its types erased.

#### `CallInput`

```ts
interface CallInput {
    readonly selector: string;
    readonly method: string;
    readonly args: readonly unknown[];
}
```

The input of a `call()` command.

#### `ChildSource`

```ts
type ChildSource<O extends Tagged, E extends Element> = OutputSource<O, E> | (() => OutputSource<O, E>);
```

A child class, or a function returning it (for recursive or later-defined components).

#### `ChildValue`

```ts
type ChildValue = string | number | boolean | null | undefined | typeof nothing | TemplateResult | ListResult | RawResult | readonly ChildValue[];
```

Everything a child hole accepts.

#### `Command`

```ts
interface Command<M> {
    readonly driver: AnyDriver;
    readonly input: unknown;
    /** Concurrency lane. Default: the driver's name. */
    readonly key?: string;
    readonly concurrency?: Concurrency;
    readonly onSuccess: (output: unknown) => M | undefined;
    /** If omitted, a failure is reported to the error channel (ADR 0024, G0041). */
    readonly onFailure?: (error: unknown) => M | undefined;
}
```

A side effect described as data, plus pure mappers from its outcome to messages. Commands
that answer with no message (`focus()`, `call()`, `navigate()`) are `Command<never>`,
which fits wherever a `Command<Msg>` is expected: no type argument needed.

#### `CommandHandlers`

```ts
interface CommandHandlers<O, E, MS, MF = MS> {
    readonly onSuccess?: (output: O) => MS | undefined;
    readonly onFailure?: (error: E) => MF | undefined;
    readonly key?: string;
    readonly concurrency?: Concurrency;
}
```

How a command's outcome becomes a message. Every field is optional: without `onSuccess` the
command answers with nothing (`Command<never>`); without `onFailure` a failure is reported to
the error channel (ADR 0024).

#### `CommandInfo`

```ts
interface CommandInfo {
    /** The command, e.g. `--clear`. */
    readonly command: string;
    /** The invoker (the button) that sent it. */
    readonly source: Element | undefined;
}
```


#### `ComponentSpec`

```ts
type ComponentSpec<S, M extends Tagged, P, N extends string = string> = SpecBody<S, M, P, N> & IntentField<S, M, P, N> & UpdateField<S, M, P> & InitField<S, M, P>;
```

A component spec. `N` is the component's intent names, the keys of `intent` (ADR 0023).

#### `Concurrency`

```ts
type Concurrency = 'merge' | 'switch' | 'exhaust' | 'queue';
```

How commands in the same lane interact. See ADR 0006 for the table.

#### `CssValue`

```ts
type CssValue = string | number | StyleSource;
```

A `css` interpolation: inserted as written (another `css` value inserts its text).

#### `Ctx`

```ts
interface Ctx<P> {
    /** Props from above. */
    readonly props: P;
    /** Stores from the side: `read(cart).lines`. The store must be in `spec.stores`. */
    readonly read: StoreReader;
}
```

Read-only context handed to every reducer and to the view (ADR 0007, 0013).

#### `Definer`

```ts
type Definer<S, M extends Tagged, P extends object, O extends Tagged> = <N extends string = never>(tag: string, spec: ComponentSpec<S, M, P, N>) => GyralElementClass<S, M, P, O, N>;
```

The second call of `define()`: takes the tag and the spec, infers the intent names.

#### `Driver`

```ts
interface Driver<I, O, E = unknown> {
    readonly name: string;
    readonly run: (input: I, ctx: DriverContext<O>) => O | Promise<O>;
    /** Default policy for this driver's commands. Default `'merge'`. */
    readonly concurrency?: Concurrency;
    /** Turns a thrown or rejected value into this driver's typed error. */
    readonly toError?: (cause: unknown) => E;
}
```

Performs one kind of side effect. A plain object: easy to fake in tests.

#### `DriverContext`

```ts
interface DriverContext<O = unknown> {
    /** Aborts when the command is switched away or its component disconnects. */
    readonly signal: AbortSignal;
    /**
     * Delivers an extra result while the command runs (streaming drivers: routers, timers,
     * sockets). Each value goes through the command's `onSuccess`. Ignored once the command
     * has settled or been aborted. A streaming `run` usually never resolves; abort ends it.
     */
    readonly emit: (output: O) => void;
}
```


#### `DriverOverrides`

```ts
type DriverOverrides = Readonly<Record<string, AnyDriver>>;
```

Drivers substituted by name (test fakes, configured instances): `el.drivers`,
`provideDrivers`. Annotate a driver map with this type (or `Record<string, AnyDriver>`), not
`Record<string, Driver<unknown, unknown>>`, which rejects drivers with a typed input.

#### `DriverToken`

```ts
interface DriverToken<E = unknown> {
    readonly name: string;
    readonly concurrency?: Concurrency;
    readonly toError?: (cause: unknown) => E;
}
```

A driver with no implementation of its own (`defineDriver<I, O, E>('session')`): commands built
from it are typed by `I`, `O` and `E`, and it runs only when `provideDrivers` or `el.drivers`
supplies a driver of that name (ADR 0006 addendum "Driver tokens").

#### `Errored`

```ts
interface Errored {
    readonly _tag: 'Errored';
    readonly phase: 'update' | 'parse' | 'command';
    readonly error: GyralError;
}
```

Framework message (ADR 0024): an update, an intent parser or a command of this component
failed. The failure was reported (`reportError`, or claimed by an ancestor's boundary) and
changed nothing; reduce it to show an error state or to reset. A failure while reducing
`Errored` is reported and not sent again.

#### `ErrorPhase`

```ts
type ErrorPhase = 'init' | 'update' | 'view' | 'parse' | 'command' | 'hook' | 'store' | 'subscribe';
```

Where a failure happened.

#### `FieldIssue`

```ts
interface FieldIssue {
    readonly path: string;
    readonly message: string;
}
```

One validation problem. `path` is dot-joined and matches the field's `name`.

#### `FieldName`

```ts
type FieldName<Schema extends StandardSchemaV1> = In<Schema> extends Readonly<Record<string, unknown>> ? keyof In<Schema> & string : string;
```

The definition's field names, when its input is an object; otherwise any string.

#### `FocusOptions`

```ts
interface FocusOptions {
    /** Don't scroll the element into view (default `false`: the browser scrolls if needed). */
    readonly preventScroll?: boolean;
    /** Also select the text of an input or textarea. */
    readonly select?: boolean;
    /**
     * If nothing matches after this render, keep the request and focus the target as soon as a
     * later render of this component produces it; a newer `focus()` from the component replaces
     * it, and after one second it gives up with the usual warning. `settled()` doesn't wait for it.
     */
    readonly wait?: boolean;
}
```


#### `FormAssociation`

```ts
interface FormAssociation<S, P> {
    readonly value: (state: S, ctx: Ctx<P>) => FormControlValue;
    readonly state?: (state: S, ctx: Ctx<P>) => FormControlValue;
    readonly validity?: (state: S, ctx: Ctx<P>) => FormControlValidity | undefined;
}
```

Pure model projections applied by Gyral to the host's ElementInternals (ADR 0008).

#### `FormControlValidity`

```ts
interface FormControlValidity {
    readonly flags: ValidityStateFlags;
    readonly message: string;
    readonly anchor?: string;
}
```

Native constraint flags and a validation bubble anchor in the component's render root.

#### `FormControlValue`

```ts
type FormControlValue = string | File | FormData | null;
```

A native submitted value, or null to omit this component from the submission.

#### `FormDefinition`

```ts
interface FormDefinition<Schema extends StandardSchemaV1> {
    readonly _tag: 'FormDefinition';
    readonly schema: Schema;
    /** The fields listed as secret (names containing `password`, `passcode`, `csrf` or `xsrf` are secret too). */
    readonly secret: readonly string[];
    /**
     * Validates submitted form data exactly as `form()` does; the server's `formAction` uses it,
     * so both paths produce the same `IntentRejected` (`intent` defaults to `''`).
     */
    readonly validate: (data: FormData, intent?: string) => FormResult<Out<Schema>> | Promise<FormResult<Out<Schema>>>;
    /** The submitted text values without secrets: what a re-rendered form may show again. */
    readonly values: (data: FormData) => FormValues<Schema>;
}
```

A form's schema, shared by the client component and (with `@gyral/ssr`) the server route.

#### `FormDisabled`

```ts
interface FormDisabled {
    readonly _tag: 'FormDisabled';
    readonly disabled: boolean;
}
```

Framework message: the host or its fieldset changed the effective disabled state.

#### `FormFields`

```ts
type FormFields = Readonly<Record<string, string | readonly string[]>>;
```

Submitted text values of a form (Files are dropped: they can't be re-filled).

#### `FormOptions`

```ts
interface FormOptions<Schema extends StandardSchemaV1> {
    /**
     * Fields whose submitted values are never sent back: not in `IntentRejected.values`, not in
     * `values()`, not in the devtools timeline. Any posted field name may be listed, including
     * fields outside the schema. Names containing `password`, `passcode`, `csrf` or `xsrf` are
     * secret without being listed (the browser never restores a password field either, and a
     * CSRF token must come fresh from the server, not from the echo).
     */
    readonly secret?: readonly (FieldName<Schema> | (string & {}))[];
}
```


#### `FormReset`

```ts
interface FormReset {
    readonly _tag: 'FormReset';
}
```

Framework message: the owning native form was reset.

#### `FormResult`

```ts
type FormResult<T> = {
    readonly ok: true;
    readonly data: T;
} | {
    readonly ok: false;
    readonly rejected: IntentRejected;
};
```


#### `FormStateRestore`

```ts
interface FormStateRestore {
    readonly _tag: 'FormStateRestore';
    readonly state: FormControlValue;
    readonly mode: 'restore' | 'autocomplete';
}
```

Framework message: native session restoration or autofill supplied control state.

#### `FormValue`

```ts
type FormValue = FormDataEntryValue | readonly FormDataEntryValue[];
```


#### `FormValues`

```ts
type FormValues<Schema extends StandardSchemaV1> = In<Schema> extends Readonly<Record<string, unknown>> ? {
    readonly [K in keyof In<Schema> & string]?: string | readonly string[];
} : FormFields;
```

A form's submitted text values, keyed by the definition's fields, for re-rendering the form
with what was typed (sticky fields). The same shape as `IntentRejected.values`: repeated names
(and `name[]`) are arrays; `File`s and secret fields are left out.

#### `GyralElement`

```ts
interface GyralElement<S, M extends Tagged> extends HTMLElement {
    /** Current model state. */
    readonly state: S;
    /** Feeds a message through `update`, as if an intent had produced it. */
    send(msg: M): void;
    /** Per-instance driver substitutions by name (test fakes). Checked before the spec's. */
    drivers: DriverOverrides;
    /**
     * Per-instance store instances by store name (tests, islands). Checked before the nearest
     * `<gyral-stores>` provider and the document default (ADR 0013).
     */
    stores: StoreOverrides;
    /**
     * Messages applied through `update` right after `init`, before the first render. The server
     * uses it to render a rejected form with the same reducer as the JS path (ADR 0008). Ignored
     * when the element resumes from a hydration seed (the seed already contains their effect).
     */
    initialMessages: readonly Tagged[];
}
```

The custom element `define()` produces: a plain `HTMLElement`. Tests wait for rendering with `settled()`.

#### `GyralElementClass`

```ts
interface GyralElementClass<S, M extends Tagged, P, O extends Tagged = never, N extends string = string> {
    /** Instances expose their declared props as settable properties. */
    new (): GyralElement<S, M> & {
        -readonly [K in keyof P]: P[K];
    };
    readonly spec: ComponentSpec<S, M, P, N>;
    readonly tagName: string;
    /** Native custom-element registration flag, true when the spec opts into forms. */
    readonly formAssociated?: boolean;
    /** Type-only: the outputs this component emits (read by `child()`). */
    readonly outputs?: O;
    /** Type-only: the component's intent names, the keys of `intent` (read by `intentsOf()`). */
    readonly intentNames?: N;
}
```


#### `Head`

```ts
interface Head {
    readonly title: string;
    readonly description?: string;
    /** Absolute URL, usually `new URL(match.path, origin).href` (ADR 0009 "Canonical paths"). */
    readonly canonical?: string;
    /** `<meta name="robots">`, e.g. `'noindex, follow'`. */
    readonly robots?: string;
    /** Later entries win over earlier ones with the same name or property. */
    readonly meta?: readonly HeadMeta[];
    /** Later entries win over earlier ones with the same attributes other than `href`. */
    readonly links?: readonly HeadLink[];
    /** Structured data, each written as one `<script type="application/ld+json">`. */
    readonly jsonLd?: readonly JsonValue[];
    /** `<html lang>`. */
    readonly lang?: string;
    /** `<html dir>`. */
    readonly dir?: 'ltr' | 'rtl' | 'auto';
}
```

A page's head: what `renderPage()` writes on the server and `setHead()` applies on the client.

#### `HeadLink`

```ts
interface HeadLink {
    readonly rel: string;
    readonly href: string;
    readonly [attribute: string]: string;
}
```

A metadata `<link>`: `alternate` (with `hreflang`), `icon` (with `sizes`, `type`), `manifest`
(with `crossorigin`), `prev`/`next`, … Any other attribute is written as given.

#### `HeadMeta`

```ts
type HeadMeta = {
    readonly name: string;
    readonly content: string;
    readonly media?: string;
} | {
    readonly property: string;
    readonly content: string;
    readonly media?: string;
};
```

`<meta name content>`, or `<meta property content>` (Open Graph uses `property`). `media`
makes one entry per media query (`theme-color` for light and dark).

#### `HookAttributes`

```ts
type HookAttributes = Readonly<Record<string, string | true>>;
```

Attributes a hook's server half adds to the start tag (`true`: present, no value).

#### `HookResult`

```ts
interface HookResult<A extends readonly unknown[] = readonly unknown[]> {
    readonly [HOOK]: HookSpec<A>;
    readonly [COMMIT_HOOK]: (part: HookPart, result: HookResult) => void;
    readonly args: A;
}
```

What a hook returns in a template: `<input ${invalid(errors)}>`.

#### `HookSpec`

```ts
interface HookSpec<A extends readonly unknown[]> {
    /** Attributes for the server-rendered start tag. */
    server?(args: A): HookAttributes;
    /** Runs after the commit when `args` changed; `prev` is undefined the first time. */
    client(el: Element, args: A, prev: A | undefined): void;
    /**
     * When `client` runs again: `'args'` (the default) when an argument changed (`Object.is`),
     * `'always'` on every render that commits the hook's position (a skipped `each` row doesn't).
     */
    readonly update?: 'args' | 'always';
    /**
     * Teardown, with the last arguments `client` got: runs
     * when Gyral removes the element, when the position stops holding this hook, or when the host
     * disconnects (not on moves). After a host reconnects, `client` runs again with `prev`
     * undefined.
     */
    dispose?(el: Element, args: A): void;
}
```


#### `Hydrated`

```ts
interface Hydrated {
    readonly _tag: 'Hydrated';
    /** `true` when the instance resumed from server-rendered markup (a hydration seed). */
    readonly serverRendered: boolean;
}
```

Framework message: the component is live in the browser (ADR 0012 addendum). Sent once,
after the first client render has completed, to every client-side instance. Use it for
progressive enhancement: render the no-JS form on the server and in the first client render
(so hydration matches), then switch to the enhanced UI in the `Hydrated` reducer.

#### `HydrateStrategy`

```ts
type HydrateStrategy = 'load' | 'idle' | 'visible' | 'interaction';
```

When a server-rendered component hydrates. `load` (the default) means right away.

#### `IntentInput`

```ts
interface IntentInput {
    /** The intent's name (a message tag): the `data-intent-<event>` or `data-intent` value. */
    readonly name: string;
    readonly event: Event;
    /** The element carrying the intent attribute (`event.currentTarget` is Gyral's root). */
    readonly target: Element;
    /** `value` of the input, select, textarea or button that carries the intent. */
    readonly value: string | undefined;
    /** `checked` for checkbox and radio inputs. */
    readonly checked: boolean | undefined;
    /** Submitted data (including the submitter button) when the intent is on a `<form>`. */
    readonly formData: FormData | undefined;
    /**
     * `detail` of any `CustomEvent`: a Gyral child's output, or the event of another custom
     * element (a map's `marker-select`). Unknown until the parser checks it (a guard, a schema).
     */
    readonly detail: unknown;
    /** `KeyboardEvent.key` for `keydown`/`keyup` intents. */
    readonly key: string | undefined;
    /** For `toggle` intents (popover, `<details>`): the state it changed to. */
    readonly newState: 'open' | 'closed' | undefined;
    /** For `command` intents: the invoker's command (e.g. `--clear`) and source. */
    readonly command: CommandInfo | undefined;
}
```

What the intent layer sees when an element's intent fires.

#### `IntentNames`

```ts
type IntentNames<N extends string> = {
    readonly [K in N]: K;
};
```

A component's intent names, the keys of its `intent` object, as the view's `i` and as
`intentsOf<typeof C>()`, so `data-intent=${i.Increment}` is checked (ADR 0023).

#### `IntentParser`

```ts
type IntentParser<M, P = unknown, S = unknown> = (input: IntentInput, ctx: ParserCtx<P, S>) => ParseResult<M> | Promise<ParseResult<M>>;
```

Parses a platform event into one message variant, `IntentRejected`, or `undefined` to
decline it: a synchronous `undefined` passes the event to the next intent outward for the
same event. May be async because schema validation may be;
an async parser keeps the event. `ctx` is the read-only context reducers get (props as
they are when the event fires, `read(store)`) plus `state`, the component's state at that
moment; parsers that don't need it take one parameter.

#### `IntentRejected`

```ts
interface IntentRejected {
    readonly _tag: 'IntentRejected';
    /** The `data-intent` name whose input was rejected. */
    readonly intent: string;
    readonly issues: readonly FieldIssue[];
    /**
     * For `form()` rejections: what the user submitted, so the view can re-fill the form. On
     * the no-JS path (ADR 0008 server half) the server re-renders the page from this.
     */
    readonly values?: FormFields | undefined;
}
```

Framework message: an intent's input failed schema validation (ADR 0008).

#### `Intents`

```ts
type Intents<M extends Tagged, P = unknown, N extends string = string, S = unknown> = {
    readonly [K in N]: ParserFor<M, P, K, S>;
};
```

Intent parsers keyed by intent name (ADR 0001, ADR 0023). The keys are the component's
intent names: `define<State, Msg>()(tag, { intent: { Archive: …, Increment: … } })` gives the
view `i.Archive` and `i.Increment`. A key that is a message tag must return that variant; any
other key may return any message (several controls that change one thing can also share one
intent and tell themselves apart by `name`). Messages from drivers need no parser.

#### `JsonValue`

```ts
type JsonValue = string | number | boolean | null | readonly JsonValue[] | {
    readonly [key: string]: JsonValue;
};
```

A JSON value: what a JSON-LD block holds.

#### `ListResult`

```ts
interface ListResult {
    readonly [EACH]: (part: ChildPart, list: ListResult) => void;
    readonly items: readonly unknown[];
    readonly key: (item: unknown) => unknown;
    readonly row: (item: unknown, picked: unknown) => unknown;
    readonly pick: ((item: unknown) => unknown) | undefined;
}
```

A keyed list for a child hole: what `each(…)` returns.

#### `Next`

```ts
type Next<S, M> = S | readonly [S, ReadonlyArray<Command<M | IntentRejected>>];
```

What a reducer (or `init`) returns: new state, optionally with commands to run. Commands may
also answer with the framework message `IntentRejected` (e.g. `submitForm` in `@gyral/http`
when the server rejects a form), which goes to the optional `IntentRejected` reducer.

#### `OutputEvent`

```ts
type OutputEvent<O extends Tagged = Tagged> = CustomEvent<O>;
```

The `gyral-output` event a child dispatches on its host for each output (ADR 0010): `detail`
is the output. Bubbles, not composed. A non-Gyral parent listens for `OUTPUT_EVENT`. Only the
direct parent's intent on the child host takes it, and once it does the event stops there.

#### `OutputsOf`

```ts
type OutputsOf<C> = C extends {
    readonly outputs?: infer O;
} ? Exclude<O, undefined> : never;
```

The output union of a `define()` class: `OutputsOf<typeof Item>`.

#### `OutputSource`

```ts
interface OutputSource<O extends Tagged, E extends Element> {
    new (): E;
    /** Type-only marker for the component's output union. Never set at runtime. */
    readonly outputs?: O;
}
```

Anything `child()` can read an output type from: a `define()` class.

#### `ParserCtx`

```ts
interface ParserCtx<P, S = unknown> extends Ctx<P> {
    /** The state when the event fires. */
    readonly state: Readonly<S>;
}
```

What an intent parser gets besides the input: the reducers' context plus the component's
current state, read-only. Parsers decide synchronously (`preventDefault()`), and where the
user is often decides it: Tab leaves a grid only from its last cell.

#### `ParseResult`

```ts
type ParseResult<M> = M | IntentRejected | undefined;
```

What an intent parser returns: a message, `IntentRejected` (invalid input), or `undefined` to
decline the event.

#### `ParserFor`

```ts
type ParserFor<M extends Tagged, P, K, S = unknown> = string extends K ? // The wide form (any intent name), which every specific spec is assignable to.
IntentParser<M, P, S> | true | TrueOnlyForFieldlessMessage : K extends M['_tag'] ? IntentParser<Variant<M, K>, P, S> | TagOnly<Variant<M, K>> : IntentParser<M, P, S>;
```

The parser for intent name `K` (ADR 0023): a name that is a message tag parses into that
variant; any other name is an intent of its own whose parser may produce any message.

#### `Prop`

```ts
interface Prop<T> {
    readonly kind: PropKind;
    /** The attribute name, `false` for property only, `undefined` for the kebab-case default. */
    readonly attribute: string | false | undefined;
    /**
     * Refines (string/number/boolean) or defines (json/value) the accepted values: a Standard
     * Schema, or for json/value a type guard (props.ts `checkValue` tells them apart).
     */
    readonly schema: StandardSchemaV1 | PropGuard<unknown> | undefined;
    /** A missing value is a bug: warned once per instance at first render. */
    readonly required: boolean;
    /** Used whenever the element's value is missing (`boolean` props default to `false`). */
    readonly default?: unknown;
    /**
     * When a new value counts as unchanged (no re-render, no `PropsChanged`): `Object.is`, the
     * same JSON for `prop.json`, or the `equals` option of `prop.json`/`prop.value`.
     */
    readonly equals: (a: unknown, b: unknown) => boolean;
    /** Type-only: the value components see. Never set. */
    readonly [OUTPUT]?: () => T;
}
```

One declared prop: what a builder returns.

#### `PropDeclarations`

```ts
type PropDeclarations<P> = {
    readonly [K in keyof P]-?: Prop<P[K]>;
};
```

The prop builders for props type `P`: one `prop.*` builder per
key. A builder's output type must fit the prop's type, so a prop whose type excludes
`undefined` needs `required: true` or a `default` (the honesty rule, ADR 0007 addendum).
Without explicit type arguments, `define()` infers `P` from the builders.

#### `PropGuard`

```ts
type PropGuard<T> = (value: unknown) => value is T;
```

A plain type guard, accepted by `prop.value` and `prop.json` in place of a schema.

#### `PropKind`

```ts
type PropKind = 'string' | 'number' | 'boolean' | 'json' | 'value';
```


#### `PropsChanged`

```ts
interface PropsChanged<P> {
    readonly _tag: 'PropsChanged';
    readonly props: P;
    readonly prev: P;
}
```

Framework message: declared props changed after the first render (ADR 0007).

#### `PropsOf`

```ts
type PropsOf<T extends Readonly<Record<string, Prop<unknown>>>> = {
    readonly [K in keyof T]: T[K] extends Prop<infer V> ? V : never;
};
```

The props type of a table of builders: `PropsOf<typeof props>`.

#### `RawResult`

```ts
interface RawResult {
    readonly [MARKUP]: (part: ChildPart, html: string) => void;
    readonly html: string;
}
```

Trusted markup for a child hole: what `raw(html)` returns.

#### `Reducer`

```ts
type Reducer<S, M, Msg, P = object> = (state: S, msg: Msg, ctx: Ctx<P>) => Next<S, M>;
```

One entry of `update`: takes the state, one message (`Msg`, a variant of `M` or a framework
message) and the read-only context, and returns the next state, optionally with commands
that produce more `M` messages. To type a reducer written outside the spec:


```ts
const add: Reducer<State, Msg, Variant<Msg, 'Add'>, object> = (s, m) => …
```


#### `RetryPolicy`

```ts
interface RetryPolicy<E = unknown> {
    /** Retries after the first failure. */
    readonly times: number;
    /** Delay before each retry (the base delay for exponential backoff). Default 0. */
    readonly delayMs?: number;
    readonly backoff?: 'fixed' | 'exponential';
    /** Full jitter: wait a random time between 0 and the computed delay, so clients spread out. */
    readonly jitter?: boolean;
    /**
     * Which failures to retry, given the driver's typed error (its `toError`, else the thrown
     * value). Default: every failure. For `@gyral/http`, `retryableHttpError`.
     */
    readonly retryIf?: (error: E) => boolean;
}
```

How `retry(driver, policy)` retries a rejected run (retry.ts). `E`: the driver's error.

#### `SeedCheck`

```ts
type SeedCheck<S> = {
    readonly ok: true;
    readonly state: S;
} | {
    readonly ok: false;
    readonly issues: readonly string[];
};
```

The result of checking a seed against a store's schema.

#### `ShadowOption`

```ts
type ShadowOption = boolean | {
    readonly delegatesFocus?: boolean;
};
```

`spec.shadow`: `true` (the default) or an object for a shadow root, `false` for light DOM.
`delegatesFocus: true`: focusing the host, or clicking a non-focusable part of it, focuses its
first focusable element, and `:focus` matches the host while focus is inside; the server
writes `shadowrootdelegatesfocus` on the declarative shadow root.

#### `StandardSchemaV1`

```ts
interface StandardSchemaV1<Input = unknown, Output = Input> {
    /** The Standard Schema properties. */
    readonly "~standard": StandardSchemaV1.Props<Input, Output>;
}
```

The Standard Schema interface.

#### `Stateless`

```ts
type Stateless = Readonly<Record<string, never>>;
```

State of a component that keeps none (a pure view of its props). Its `init` is optional.

#### `Store`

```ts
interface Store<S, M extends Tagged> extends StoreRef<S> {
    readonly spec: StoreSpec<S, M>;
    /**
     * A fresh, independent instance. `initial` skips `init`'s state (a server seed, a test
     * fixture); `init`'s commands still run on the client.
     */
    instance(initial?: S): StoreInstance<S, M>;
    /** Checks a seed with `spec.schema` (ok as-is without one). */
    checkSeed(seed: unknown): SeedCheck<S>;
    /** Type-only marker for the message type. Never set at runtime. */
    readonly messageType?: (msg: M) => M;
}
```


#### `StoreChanged`

```ts
interface StoreChanged {
    readonly _tag: 'StoreChanged';
    /** The store's name. Narrow with `changed(store, msg)`. */
    readonly store: string;
    readonly state: unknown;
    readonly prev: unknown;
}
```

Framework message: a store this component reads changed (ADR 0013). Optional reducer.

#### `StoreInstance`

```ts
interface StoreInstance<S, M extends Tagged> {
    readonly store: Store<S, M>;
    readonly state: S;
    /** Feeds a message through the store's `update` and notifies subscribers on change. */
    send(msg: M): void;
    /** Called after every state change. Returns an unsubscribe function. */
    subscribe(listener: (state: S, prev: S) => void): () => void;
    /** Per-instance driver substitutions (test fakes). Checked before the spec's. */
    drivers: DriverOverrides;
    /** Stops running commands and drops subscribers. */
    dispose(): void;
    /** Set by the registry that holds the instance (store-to-store `send`). */
    bindScope(resolve: StoreResolver): void;
}
```

A live store: one per page on the client, one per request on the server, one per test.

#### `StoreOverrides`

```ts
type StoreOverrides = Readonly<Record<string, AnyStoreInstance>>;
```

Store instances substituted by store name (tests, islands).

#### `StoreReader`

```ts
type StoreReader = <S>(store: StoreRef<S>) => S;
```

Reads a declared store's current state, typed by the store (ADR 0013).

#### `StoreRef`

```ts
interface StoreRef<S> {
    readonly name: string;
    /** Type-only marker for the state type. Never set at runtime. */
    readonly stateType?: (state: S) => S;
}
```

What `ctx.read()` accepts: anything that names a store and carries its state type.

#### `StoreResolver`

```ts
type StoreResolver = (store: AnyStore) => AnyStoreInstance;
```

Finds a store's instance in a scope (a registry).

#### `StoreSpec`

```ts
interface StoreSpec<S, M extends Tagged> {
    /** Initial state (and optional commands, e.g. load a saved cart). */
    readonly init: () => Next<S, M>;
    readonly update: StoreUpdate<S, M>;
    /** Driver substitutions by name for this store's commands. */
    readonly drivers?: DriverOverrides;
    /**
     * Validates the server's page seed before the client uses it (any synchronous Standard
     * Schema). An invalid seed is reported with console.error and the store starts from `init`.
     */
    readonly schema?: StandardSchemaV1<unknown, S>;
}
```


#### `StoreUpdate`

```ts
type StoreUpdate<S, M extends Tagged> = {
    readonly [K in M['_tag']]: (state: S, msg: Variant<M, K>) => Next<S, M>;
};
```

One pure reducer per message tag, like a component's `update` but without context.

#### `Styles`

```ts
type Styles = StyleSource | string | readonly Styles[];
```

What `spec.styles` accepts: `css` values, plain CSS strings (e.g.
`import base from './base.css?inline'`) and arrays of them, nested freely. CSS is trusted
author code: never interpolate user input.

#### `StyleSource`

```ts
interface StyleSource {
    readonly [STYLE]: true;
    readonly text: string;
}
```

What `css` returns: CSS text, mapped to one shared `CSSStyleSheet` in the browser.

#### `SubscriptionContext`

```ts
interface SubscriptionContext<I> {
    /** The command's input (a room code, a URL, a store key). */
    readonly input: I;
    /** Aborts when the command is switched away or its component disconnects. */
    readonly signal: AbortSignal;
    /**
     * Ends the subscription with an error: the source is released and the command's
     * `onFailure` gets it. Wrap the driver in `retry()` to subscribe again instead.
     */
    readonly fail: (error: unknown) => void;
}
```


#### `SubscriptionOptions`

```ts
interface SubscriptionOptions<E> {
    readonly concurrency?: Concurrency;
    readonly toError?: (cause: unknown) => E;
}
```

Driver options a subscription may set. `concurrency` defaults to `'switch'`.

#### `Tagged`

```ts
interface Tagged {
    readonly _tag: string;
}
```

Every message is a tagged object; the tag is also the intent name used in markup.

#### `TemplateResult`

```ts
interface TemplateResult {
    readonly [SOURCE]: readonly string[] | TemplateObject;
    readonly values: readonly unknown[];
}
```

What `html` returns: a template plus this render's values.

#### `Unsubscribe`

```ts
type Unsubscribe = (() => void) | {
    readonly unsubscribe: () => void;
};
```

How to stop listening: a function, or an object with `unsubscribe()` (RxJS, XState).

#### `Update`

```ts
type Update<S, M extends Tagged, P = object> = {
    readonly [K in M['_tag']]: Reducer<S, M, Variant<M, K>, P>;
} & {
    readonly PropsChanged?: Reducer<S, M, PropsChanged<P>, P>;
    readonly IntentRejected?: Reducer<S, M, IntentRejected, P>;
    readonly StoreChanged?: Reducer<S, M, StoreChanged, P>;
    readonly Hydrated?: Reducer<S, M, Hydrated, P>;
    readonly Errored?: Reducer<S, M, Errored, P>;
    readonly FormReset?: Reducer<S, M, FormReset, P>;
    readonly FormDisabled?: Reducer<S, M, FormDisabled, P>;
    readonly FormStateRestore?: Reducer<S, M, FormStateRestore, P>;
};
```

One pure reducer per message tag (exhaustive by construction), plus optional reducers
for framework messages. Reducers may return commands.

#### `Variant`

```ts
type Variant<M extends Tagged, K> = Extract<M, {
    readonly _tag: K;
}>;
```

The member of message union `M` whose tag is `K`: `Variant<Msg, 'Add'>`.

## `@gyral/core/storage`

### Functions

| Name | Summary |
| --- | --- |
| [`makeLocalStorageDriver`](#core-storage-makelocalstoragedriver) | JSON storage with typed reads/writes and cancellable native + own-write change streams. |

#### `makeLocalStorageDriver`

```ts
function makeLocalStorageDriver<T>(schema: StandardSchemaV1<unknown, T>, options?: LocalStorageOptions<T>): Driver<StorageInput<T>, StorageOutput<T>, StorageError>;
```

JSON storage with typed reads/writes and cancellable native + own-write change streams.

### Types and interfaces

You rarely write most of these yourself: TypeScript infers them from the functions above. Look one up when an example or a compiler error names it.

| Name | Summary |
| --- | --- |
| [`LocalStorageOptions`](#core-storage-localstorageoptions) |  |
| [`StorageChangeSource`](#core-storage-storagechangesource) |  |
| [`StorageDecodeError`](#core-storage-storagedecodeerror) |  |
| [`StorageError`](#core-storage-storageerror) |  |
| [`StorageInput`](#core-storage-storageinput) |  |
| [`StorageOutput`](#core-storage-storageoutput) |  |
| [`StoredValue`](#core-storage-storedvalue) | Absence differs from a decoded value, even when the schema accepts null or undefined. |

#### `LocalStorageOptions`

```ts
interface LocalStorageOptions<T> {
    /** Name for normal driver substitution. Default localStorage. */
    readonly name?: string;
    /** Keys and Clear are restricted to this namespace. Default empty (all keys). */
    readonly prefix?: string;
    /** Accessed only when a command runs. Default native localStorage. */
    readonly storage?: () => Storage;
    /** A transformed schema may need to encode model values back into its input shape. */
    readonly encode?: (value: T) => unknown;
}
```


#### `StorageChangeSource`

```ts
type StorageChangeSource = 'initial' | 'driver' | 'tab';
```


#### `StorageDecodeError`

```ts
interface StorageDecodeError {
    readonly _tag: 'StorageDecodeError';
    readonly key: string;
    readonly issues: readonly StandardSchemaV1.Issue[];
}
```


#### `StorageError`

```ts
type StorageError = StorageDecodeError | {
    readonly _tag: 'StorageAccessError';
    readonly operation: StorageInput<unknown>['_tag'];
    readonly key: string | undefined;
    readonly message: string;
};
```


#### `StorageInput`

```ts
type StorageInput<T> = {
    readonly _tag: 'Read';
    readonly key: string;
} | {
    readonly _tag: 'Write';
    readonly key: string;
    readonly value: T;
} | {
    readonly _tag: 'Remove';
    readonly key: string;
} | {
    readonly _tag: 'Clear';
} | {
    readonly _tag: 'Watch';
    readonly key: string;
};
```


#### `StorageOutput`

```ts
type StorageOutput<T> = {
    readonly _tag: 'StorageValue';
    readonly key: string;
    readonly entry: StoredValue<T>;
} | {
    readonly _tag: 'StorageWritten' | 'StorageRemoved';
    readonly key: string;
} | {
    readonly _tag: 'StorageCleared';
} | {
    readonly _tag: 'StorageChanged';
    readonly key: string;
    readonly source: StorageChangeSource;
    readonly entry: StoredValue<T>;
} | {
    readonly _tag: 'StorageDecodeFailed';
    readonly key: string;
    readonly source: StorageChangeSource;
    readonly error: StorageDecodeError;
};
```


#### `StoredValue`

```ts
type StoredValue<T> = {
    readonly found: false;
} | {
    readonly found: true;
    readonly value: T;
};
```

Absence differs from a decoded value, even when the schema accepts null or undefined.

## `@gyral/core/indexeddb`

### Functions

| Name | Summary |
| --- | --- |
| [`makeIndexedDBDriver`](#core-indexeddb-makeindexeddbdriver) | Typed record operations and atomic batches over application-owned stores and migrations. |

#### `makeIndexedDBDriver`

```ts
function makeIndexedDBDriver<S extends IndexedDBStores>(stores: S, options: IndexedDBOptions<S>): Driver<IndexedDBInput<S>, IndexedDBOutput<S>, IndexedDBError>;
```

Typed record operations and atomic batches over application-owned stores and migrations.

### Types and interfaces

You rarely write most of these yourself: TypeScript infers them from the functions above. Look one up when an example or a compiler error names it.

| Name | Summary |
| --- | --- |
| [`IndexedDBError`](#core-indexeddb-indexeddberror) |  |
| [`IndexedDBInput`](#core-indexeddb-indexeddbinput) |  |
| [`IndexedDBOperation`](#core-indexeddb-indexeddboperation) |  |
| [`IndexedDBOptions`](#core-indexeddb-indexeddboptions) |  |
| [`IndexedDBOutput`](#core-indexeddb-indexeddboutput) |  |
| [`IndexedDBRange`](#core-indexeddb-indexeddbrange) |  |
| [`IndexedDBRecord`](#core-indexeddb-indexeddbrecord) |  |
| [`IndexedDBResult`](#core-indexeddb-indexeddbresult) |  |
| [`IndexedDBStores`](#core-indexeddb-indexeddbstores) |  |

#### `IndexedDBError`

```ts
type IndexedDBError = {
    readonly _tag: 'IndexedDBBlockedError';
    readonly database: string;
    readonly version: number;
} | {
    readonly _tag: 'IndexedDBInputError';
    readonly message: string;
} | {
    readonly _tag: 'IndexedDBAccessError';
    readonly database: string;
    readonly phase: 'encode' | 'open' | 'upgrade' | 'transaction';
    readonly name: string;
    readonly message: string;
} | {
    readonly _tag: 'IndexedDBDecodeError';
    readonly store: string;
    readonly issues: readonly StandardSchemaV1.Issue[];
    /** Native transaction completed before decoding. Its writes cannot be undone. */
    readonly committed: true;
};
```


#### `IndexedDBInput`

```ts
type IndexedDBInput<S extends IndexedDBStores> = IndexedDBOperation<S> | {
    readonly _tag: 'Transaction';
    readonly operations: readonly IndexedDBOperation<S>[];
};
```


#### `IndexedDBOperation`

```ts
type IndexedDBOperation<S extends IndexedDBStores> = {
    [K in keyof S & string]: {
        readonly _tag: 'Read' | 'Delete';
        readonly store: K;
        readonly key: IDBValidKey;
    } | {
        readonly _tag: 'Put';
        readonly store: K;
        readonly value: IndexedDBRecord<S[K]>;
        readonly key?: IDBValidKey;
    } | {
        readonly _tag: 'Query';
        readonly store: K;
        readonly index?: string;
        readonly range?: IndexedDBRange;
        readonly direction?: IDBCursorDirection;
        readonly limit: number;
    };
}[keyof S & string];
```


#### `IndexedDBOptions`

```ts
interface IndexedDBOptions<S extends IndexedDBStores> {
    readonly database: string;
    readonly version: number;
    readonly name?: string;
    readonly factory?: () => IDBFactory;
    /** Synchronous: queue migration requests before returning; never await. */
    readonly upgrade?: (db: IDBDatabase, oldVersion: number, newVersion: number, transaction: IDBTransaction) => void;
    readonly encode?: <K extends keyof S & string>(store: K, value: IndexedDBRecord<S[K]>) => unknown;
}
```


#### `IndexedDBOutput`

```ts
type IndexedDBOutput<S extends IndexedDBStores> = IndexedDBResult<S> | {
    readonly _tag: 'Committed';
    readonly results: readonly IndexedDBResult<S>[];
};
```


#### `IndexedDBRange`

```ts
type IndexedDBRange = {
    readonly _tag: 'Only';
    readonly key: IDBValidKey;
} | {
    readonly _tag: 'Lower' | 'Upper';
    readonly key: IDBValidKey;
    readonly open?: boolean;
} | {
    readonly _tag: 'Bound';
    readonly lower: IDBValidKey;
    readonly upper: IDBValidKey;
    readonly lowerOpen?: boolean;
    readonly upperOpen?: boolean;
};
```


#### `IndexedDBRecord`

```ts
type IndexedDBRecord<S extends StandardSchemaV1> = StandardSchemaV1.InferOutput<S>;
```


#### `IndexedDBResult`

```ts
type IndexedDBResult<S extends IndexedDBStores> = {
    [K in keyof S & string]: {
        readonly _tag: 'Record';
        readonly store: K;
        readonly key: IDBValidKey;
        readonly entry: {
            readonly found: false;
        } | {
            readonly found: true;
            readonly value: IndexedDBRecord<S[K]>;
        };
    } | {
        readonly _tag: 'Put' | 'Deleted';
        readonly store: K;
        readonly key: IDBValidKey;
    } | {
        readonly _tag: 'Records';
        readonly store: K;
        readonly records: readonly {
            readonly key: IDBValidKey;
            readonly value: IndexedDBRecord<S[K]>;
        }[];
    };
}[keyof S & string];
```


#### `IndexedDBStores`

```ts
type IndexedDBStores = Readonly<Record<string, StandardSchemaV1>>;
```


## `@gyral/core/vite`

### Functions

| Name | Summary |
| --- | --- |
| [`gyralVitePreset`](#core-vite-gyralvitepreset) | Spread into a Vite config, or into each Vitest project: |

#### `gyralVitePreset`

```ts
function gyralVitePreset(options?: GyralViteOptions): GyralViteConfig;
```

Spread into a Vite config, or into each Vitest project:


```ts
export default defineConfig({ ...gyralVitePreset(), build: { … } });
```

With plugins of your own, list both: `plugins: [...preset.plugins, mine()]`.

### Types and interfaces

You rarely write most of these yourself: TypeScript infers them from the functions above. Look one up when an example or a compiler error names it.

| Name | Summary |
| --- | --- |
| [`ComponentsOptions`](#core-vite-componentsoptions) |  |
| [`ElementTypesOptions`](#core-vite-elementtypesoptions) | Where the preset looks for components and writes their typings. |
| [`GyralViteConfig`](#core-vite-gyralviteconfig) |  |
| [`GyralViteOptions`](#core-vite-gyralviteoptions) |  |
| [`TemplateCompilerOptions`](#core-vite-templatecompileroptions) |  |

#### `ComponentsOptions`

```ts
interface ComponentsOptions {
    /** Directories (relative to Vite's root) whose modules' `define()` tags are found. Default `['src']`. */
    readonly include?: readonly string[];
    /**
     * Tag → module (relative to Vite's root) for components the scan can't find: those defined
     * through a wrapper such as `defineMemberForm({ tag: 'member-form' })`, which calls
     * `define()` itself. Listed entries win over scanned ones.
     */
    readonly modules?: Readonly<Record<string, string>>;
}
```


#### `ElementTypesOptions`

```ts
interface ElementTypesOptions {
    /** Source directories to scan, relative to the Vite root. Default `['src']`. */
    readonly dirs?: readonly string[];
    /** The declaration file to write, relative to the Vite root. Default `.gyral/elements.d.ts`. */
    readonly out?: string;
}
```

Where the preset looks for components and writes their typings.

#### `GyralViteConfig`

```ts
interface GyralViteConfig {
    readonly plugins: Plugin[];
    readonly optimizeDeps: {
        readonly include: string[];
    };
}
```


#### `GyralViteOptions`

```ts
interface GyralViteOptions {
    /**
     * Generated element typings (`.gyral/elements.d.ts`, see `ElementTypesOptions`), or `false`
     * to write none. Default: on, scanning `src`.
     */
    readonly types?: ElementTypesOptions | false;
    /** Modules to pre-bundle in dev, so Vite doesn't discover them mid-run and reload. */
    readonly optimize?: readonly string[];
    /** Template compiler options (`vite build` only). */
    readonly compiler?: TemplateCompilerOptions;
    /**
     * The app is never server-rendered: leave the hydration code out of the browser bundle.
     * Default false.
     */
    readonly clientOnly?: boolean;
    /**
     * Automatic component loading (ADR 0016 addendum): every component defined under `include`
     * gets a client module the build splits out, and a loader (`virtual:gyral/components`, added
     * to the client build's input) imports a tag's module once that tag is in the page.
     * `renderPage({ components })` then links exactly the components a page rendered, so pages
     * need no client entry of their own and no side-effect imports. Default off.
     */
    readonly components?: boolean | ComponentsOptions;
}
```


#### `TemplateCompilerOptions`

```ts
interface TemplateCompilerOptions {
    /**
     * Module specifiers whose `html` (and `svg`) exports are the view layer's tags (default
     * `DEFAULT_TEMPLATE_SOURCES`). An import matches when its specifier is listed, or when it
     * resolves to the same module as a listed one; core's own view modules always match. A
     * source other than '@gyral/core' must also export `compiled` (and `compiledSvg` when it
     * exports `svg`).
     */
    readonly sources?: readonly string[];
    /**
     * Rule 7's extra check with parse5 (an optional peer dependency): `true` (default) uses
     * 'parse5' when it is installed, a string names the module to load, `false` skips it.
     */
    readonly parse5?: boolean | string;
}
```


## `@gyral/core/eslint`

### Constants

| Name | Summary |
| --- | --- |
| [`default`](#core-eslint-default) |  |

#### `default`

```ts
declare const _default: GyralPlugin;
export default _default;
```


### Types and interfaces

You rarely write most of these yourself: TypeScript infers them from the functions above. Look one up when an example or a compiler error names it.

| Name | Summary |
| --- | --- |
| [`GyralPlugin`](#core-eslint-gyralplugin) |  |

#### `GyralPlugin`

```ts
interface GyralPlugin extends ESLint.Plugin {
    readonly rules: {
        readonly template: typeof templateRule;
        readonly 'each-row-purity': typeof rowPurityRule;
        readonly 'unused-intent': typeof unusedIntentRule;
    };
    readonly configs: {
        readonly recommended: Linter.Config;
    };
}
```
