# flagstaff/plugin

> Every export of flagstaff/plugin, with its signature and doc comment: validate, register, registered, lookupSpinner, lookupBorder, glyph and 2 more, plus 5 types.

Source: https://flagstaff.interlace.tools/docs/api/plugin

<!-- Generated by scripts/api-reference.ts from the built dist/*.d.ts. Do not edit; run `npx tsx scripts/api-reference.ts`. -->

```ts
import { validate, register, registered, … } from 'flagstaff/plugin';
```

## Functions

### glyph

A glyph by meaning; the built-ins define every meaning the built-in components use.

```ts
function glyph(meaning: string): string;
```

| Parameter | Type |
| :-- | :-- |
| `meaning` | `string` |

**Returns** `string`

### lookupBorder

A border by name, for `box()`; every style the built-ins ship is registered like any other.

```ts
function lookupBorder(name: string): BorderStyle;
```

| Parameter | Type |
| :-- | :-- |
| `name` | `string` |

**Returns** `BorderStyle`

### lookupSpinner

A spinner by name, or a refusal that lists the names that exist.

```ts
function lookupSpinner(name: string): SpinnerDef;
```

| Parameter | Type |
| :-- | :-- |
| `name` | `string` |

**Returns** `SpinnerDef`

### register

The only wiring (R4, U9): validate, then keep every key this package understands. A later
plugin's entry replaces an earlier one of the same name, so a user overrides a built-in
by registering their own — the built-ins go through this same door first.

```ts
function register(plugin: unknown): void;
```

| Parameter | Type |
| :-- | :-- |
| `plugin` | `unknown` |

**Returns** `void`

### registered

A copy of what has been registered — the docs gallery and `flagstaff check` are
projections of this. A copy rather than the registry itself, because `Readonly<T>` freezes
the property bindings and not the `Map`s behind them: handing the live registry out made
`set`, `delete` and `clear` a second door beside `register()`, through which a spinner
with no `static` — or a component with no projection at all — could be put in without
ever meeting `validate()` (#58). U3's refusal has to be structural to mean anything, so
there is one way in. The values are the frozen objects `register()` stored, so nothing
reached through here writes back.

```ts
function registered(): Readonly<Registry>;
```

**Returns** `Readonly<Registry>`

### validate

Refuse what the schema refuses, and say why. A missing `static` gets its own code and
fix, because it is the one mistake a plugin author makes on purpose (U3).

```ts
function validate(plugin: unknown): asserts plugin is Plugin;
```

| Parameter | Type |
| :-- | :-- |
| `plugin` | `unknown` |

**Returns** `asserts plugin is Plugin`

## Classes

### PluginError

A refused plugin says what is wrong, where, and what to do about it.

```ts
class PluginError extends Error {
    readonly code: PluginErrorCode;
    readonly fix: string;
    constructor(code: PluginErrorCode, message: string, fix: string);
}
```

## Constants

### CONTRACT

```ts
const CONTRACT = 1;
```

## Interfaces

### BorderStyle

A border style, in cli-boxes' shape exactly, so that corpus imports unchanged.

```ts
interface BorderStyle {
    topLeft: string;
    top: string;
    topRight: string;
    left: string;
    right: string;
    bottomLeft: string;
    bottom: string;
    bottomRight: string;
}
```

### Component

What the loop hoists: a static projection, and optionally the animated form.

```ts
interface Component<S = unknown> {
    name: string;
    static(state: S): string;
    frame?(t: number, state: S): string;
    /** Milliseconds between repaints when `frame` is given; `DEFAULT_INTERVAL` otherwise. */
    interval?: number;
    /**
     * The two states this component is *shown* with by `flagstaff check` and the docs gallery.
     * The loop never reads it: a component's real state comes from the program. Declared here
     * because a grader that invents a state renders the wrong thing and says `ok` (#59).
     */
    sample?: {
        running: S;
        done: S;
    };
}
```

### Plugin

The whole plugin contract. Keys another package in the family understands are kept for it.

```ts
interface Plugin {
    name: string;
    contract?: typeof CONTRACT;
    tokens?: Record<string, `#${string}`>;
    glyphs?: Record<string, string>;
    spinners?: Record<string, SpinnerDef>;
    borders?: Record<string, BorderStyle>;
    components?: Record<string, Omit<Component, 'name'>>;
}
```

### SpinnerDef

A spinner style, in cli-spinners' shape plus the projection a pipe prints.

```ts
interface SpinnerDef {
    frames: string[];
    interval: number;
    static: string;
}
```

## Types

### PluginErrorCode

The family's whole refusal vocabulary (plugin-contract R8). One union, and every refusal
any surface of this package prints is a member of it — the registry's five, and the two
`flagstaff check` adds for what only a renderer can discover: a plugin that validates but
contributes nothing renderable, and a component whose `static` throws on the state it is
shown with.

The last two lived as bare string literals in `cli.ts` until 2026-09-09, which is exactly
the hole R8 exists to close: a second host could spell `E_COMPONENT_THREW` its own way and
nothing would notice. `scripts/plugin-error-vocabulary-lock.test.ts` now reads this
declaration out of the source and refuses any `'E_…'` literal in a host that is not in it.

It stays a *type* rather than a `const` array on purpose. R3 forbids one layer importing
another, so a runtime list could not be shared with roundel or caique even if it existed —
the lock has to read the source to work across the family, and it does. A `const` array
would therefore buy nothing and cost ~195 B on `./spinner`, which has 82 B of headroom.

```ts
type PluginErrorCode = 'E_PLUGIN_SCHEMA' | 'E_NO_STATIC_PROJECTION' | 'E_PLUGIN_CONTRACT' | 'E_UNKNOWN_SPINNER' | 'E_UNKNOWN_BORDER' | 'E_NO_CONTRIBUTION' | 'E_COMPONENT_THREW' | 'E_UNKNOWN_KIND';
```
