paratext
API reference

paratext/plugin

Every export of paratext/plugin, with its signature and doc comment: validate, register, reset, registered, contributions, attach and 2 more, plus 4 types.

The plugin host for paratext's half of the contract (plugin-contract R1, R5a, R6, R7, R8).

A plugin is one plain object shared by the whole family. This file keeps the key paratext understands — capabilities (R5a), OSC records by name — and ignores every other key without complaining, which is what makes the same object work on any subset of the family that is installed. A plugin written for flagstaff registers here and contributes nothing; its tokens, spinners and components are not paratext's business and are not an error.

Why paratext needed this file more than the other hosts did. Until it existed, paratext was the one published package with no src/plugin.ts — and that file is the marker scripts/plugin-schema-lock.test.ts and scripts/schema-sync.mjs both look for. So paratext's copy of the family schema was outside the lock that keeps the copies byte-identical: it happened to match flagstaff's, and nothing in the repository would have said so if it stopped.

Nothing here imports another layer, and the plugin shape is declared rather than imported (R3). It imports ./capability.js because that is this package's own module: the capability shape, the registry and check() have to have exactly one home, or "fallback is required" means one thing to a plugin and another to a built-in.

R7, and why this host is the easy case. R7 asks that no key require a function. Here none does: a capability is { name, osc, when, encode, fallback }, five fields of plain data with two template strings, so a plugin can arrive as JSON, be diffed, and be printed by a plugin check without anybody running its author's code. closeout's handlers owes R7 an exemption; capabilities owes it nothing, which is the argument the design makes for holding capabilities as data in the first place.

import { validate, register, reset, … } from 'paratext/plugin';

Functions

attach

Hand every contributed capability to a registry.

Defaults to paratext's own, which is what a program wants: attach() after importing the plugins, and emit() can then reach them by name exactly as it reaches the built-ins. Passing a host is for a caller keeping its own registry, and for a test that wants to see the order without touching the global one.

function attach(host?: CapabilityHost): void;
ParameterType
host (optional)CapabilityHost

Returns void

contributions

Every capability every registered plugin contributed, with who won each name.

This is the static projection of the plugin set (R7): a caller — or burgee plugin check — reads what would be registered, and who lost, without registering anything and without a terminal being involved.

function contributions(): Contribution[];

Returns Contribution[]

register

Register a plugin. Later wins, like ESLint flat config: the array is ordered, a caller reads it top to bottom, and the last word on a capability name is the one nearest the program — which is also the registry's own rule, since register() in capability.ts replaces by name so a caller can correct a guess we got wrong.

Registering does not emit, and does not even reach the capability registry. It records the contribution; {@link attach} is what hands it over. That separation is what makes {@link contributions} a static projection rather than a side effect.

function register(plugin: unknown): void;
ParameterType
pluginunknown

Returns void

registered

The plugins registered, in registration order.

function registered(): readonly Plugin[];

Returns readonly Plugin[]

reset

Forget every registered plugin. For tests, and for a program that re-plugs at runtime.

function reset(): void;

Returns void

validate

Refuse a plugin that cannot contribute a capability, at the door.

Every refusal is a refusal rather than a silent drop. A capability quietly ignored looks like it worked right up until the day somebody runs the program on the terminal it was written for, and then the thing they debug is the terminal.

function validate(plugin: unknown): asserts plugin is Plugin;
ParameterType
pluginunknown

Returns asserts plugin is Plugin

Classes

PluginError

A refused plugin says what is wrong and what to do about it — the family's one vocabulary.

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

Constants

CONTRACT

The plugin contract version. One number for the family — the same 1 flagstaff, caique and closeout declare, written out rather than imported for the reason in the file comment.

const CONTRACT = 1;

Interfaces

CapabilityHost

The half of the capability registry this needs — declared structurally so a caller can attach to something it built itself, and so a host can watch what arrives.

interface CapabilityHost {
    register(capability: Capability): void;
}

Contribution

One contributed capability, with the plugin it came from and the plugins it displaced.

interface Contribution {
    name: string;
    from: string;
    /** Plugins that contributed this name earlier and were overridden, in order. */
    shadowed: string[];
    capability: Capability;
}

Plugin

The keys paratext reads. Declared structurally: any object with these fields is a plugin here, whatever else it carries.

interface Plugin {
    name: string;
    contract?: number;
    /** Capabilities by name. The key and the record's own `name` must agree; see {@link validate}. */
    capabilities?: Record<string, Capability>;
}

Types

PluginErrorCode

Three codes, all of them already in flagstaff's PluginErrorCode — the vocabulary home (scripts/plugin-error-vocabulary-lock.test.ts). E_NO_STATIC_PROJECTION is the one worth noticing: flagstaff raises it for a component with no static form and caique for a widget with no static, and a capability with no fallback is the same defect wearing OSC. One code, one fix shape, three layers.

type PluginErrorCode = 'E_PLUGIN_SCHEMA' | 'E_PLUGIN_CONTRACT' | 'E_NO_STATIC_PROJECTION' | 'E_NO_CONTRIBUTION';

On this page