> For the complete documentation index, see [llms.txt](https://docs.frayme.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.frayme.ai/sdk-reference/catalog.md).

# @frayme/catalog

The component vocabulary a Frayme spec may use — 189 components as Zod schemas — plus the public validation surface, the BYOC manifest kernel, and the canonical event contract.

```bash
npm i @frayme/catalog
```

ESM, MIT, Node ≥ 20.19. Version 0.3.0. The catalog is fully open: the same schemas that gate every generated spec server-side are on npm for you to validate against locally.

## The catalog instance

```ts
import { fraymeCatalog, CATALOG_COMPONENT_COUNT, CATALOG_VERSION } from '@frayme/catalog';

const result = fraymeCatalog.validate(spec); // { success, data?, error? }

CATALOG_COMPONENT_COUNT; // 189 — derived from the catalog, never hardcoded
CATALOG_VERSION;         // 'frayme-0.18.0'
```

`fraymeCatalog.validate(spec)` is the same gate the API runs before a generation is finalized and billed. `componentEvents(type)` returns the canonical verbs a component type can emit.

## Validation surfaces (`@frayme/catalog/validate`)

The `/validate` entrypoint is the single source of truth for compiling json-render operation streams into specs and validating them:

```ts
import { validatePair, compileOps, validateSpec } from '@frayme/catalog/validate';

// End-to-end: JSONL ops string → compiled spec → catalog + referential checks
const r = validatePair(opsJsonl, { resolution: true });
if (!r.valid) console.error(r.failureCategory, r.errors);
```

| Function                                 | Description                                                                                                                                                            |
| ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `compileOps(opsJsonl)`                   | Parse JSONL lines and replay them through the stream compiler → `{ spec }` or a categorized `{ failure }`.                                                             |
| `validateSpec(spec, opts?)`              | Validate a compiled spec: catalog type check + referential integrity (dangling root, missing children). Returns the original spec on success, preserving event wiring. |
| `validatePair(opsJsonl, opts?)`          | Convenience: `validateSpec(compileOps(opsJsonl))`.                                                                                                                     |
| `validateManifestProps(spec, manifests)` | BYOC: validate the props of every custom-typed element against its manifest's compiled schema.                                                                         |

`ValidateOptions`:

* `resolution: true` — additionally run the render-resolution gate (binding syntax, visibility directives, action kinds, value-channel safety). Off by default; authoring and QA pipelines turn it on.
* `catalog` — validate against a [union catalog](#byoc-authoring) instead of the built-in singleton.

Failed results carry a `FailureCategory` so you can track which kinds of mistakes occur:

```
empty_input · malformed_jsonl · compiler_error · empty_spec · catalog_validation_failed ·
unknown_element_key · invalid_binding · invalid_directive · invalid_action_kind ·
resource_limit · unsafe_value
```

The entrypoint also exports the value-channel guards used across the SDK (`safeColor`, `safeDimension`, `safeLatLng`, …) and re-exports the json-render primitives (`createSpecStreamCompiler`, `diffToPatches`, `formatSpecIssues`, `Spec`) so your code never imports `@json-render/core` directly.

## BYOC authoring

A custom component is described by a manifest — the only thing Frayme sees. Your React code stays in your app; the manifest teaches the composer, constrains the validator, and generates the props types your component honours.

```ts
import { defineFraymeComponent } from '@frayme/catalog';

export const SeatMapManifest = defineFraymeComponent({
  name: 'SeatMap', // PascalCase, unique vs the built-in catalog
  description:
    'An interactive venue seat map showing availability per seat. Use when the user ' +
    'is choosing seats for an event, screening, or venue booking flow.',
  props: {
    venue: { kind: 'string', doc: 'Venue display name.' },
    rows: { kind: 'count', doc: 'Number of seat rows.', min: 1, max: 80 },
    accent: { kind: 'color', doc: 'Accent color for selected seats.' },
    seats: {
      kind: 'array',
      doc: 'Seat inventory.',
      of: {
        id: { kind: 'string', doc: 'Seat identifier, e.g. "B12".' },
        status: { kind: 'enum', doc: 'Availability.', values: ['free', 'held', 'sold'] },
      },
    },
  },
  events: ['select', 'commit'], // ⊂ the 8 canonical verbs
  example: { venue: 'Rialto Screen 2', rows: 12, seats: [{ id: 'B12', status: 'free' }] },
});
```

Prop kinds are a closed vocabulary — `string`, `text`, `number`, `boolean`, `enum`, `color`, `dimension`, `count`, `icon`, plus one level of `array` / `object` containers — compiled to the same Zod atoms the built-ins use, so `safeColor` / `safeDimension` gate automatically. Custom components are leaf components (no children). `defineFraymeComponent` lints the manifest (a `ManifestLintError` names the rule) and returns a `CompiledManifest`:

| Member                 | Description                                                                                 |
| ---------------------- | ------------------------------------------------------------------------------------------- |
| `manifest`             | The original manifest (literal-typed). Send it as `custom_components` on a compose request. |
| `zod`                  | The compiled props schema, shape-identical to a built-in catalog entry.                     |
| `version`              | Content hash of the canonical manifest — a stable identity token for change detection.      |
| `promptChars`          | Measured size of the manifest's prompt slice — your budget signal.                          |
| `validateProps(props)` | Check a props object against the compiled schema.                                           |
| `cleanProps(props)`    | Strip-mode cleaner: unknown keys removed, invalid values dropped to `null` — never a crash. |

`extendCatalog(manifests)` builds a `FraymeCatalogUnion` — built-ins ∪ your manifests — whose `.validate()` plugs into `validateSpec({ catalog })` and `<FraymeRenderer catalog>`. On the rendering side, pair manifests with your components via [`createCustomComponents`](/sdk-reference/runtime.md#custom-components-byoc); the full walkthrough is the [custom components guide](/guides/custom-components.md).

## The event contract

Every interaction a spec can emit collapses into 8 canonical verbs — exported as `CANONICAL_EVENTS`:

| Verb      | Meaning                                                                       |
| --------- | ----------------------------------------------------------------------------- |
| `commit`  | The primary affordance fired — a CTA press, Enter in an input, a form submit. |
| `select`  | Pick an item from a set (row, date, option, menu item).                       |
| `change`  | A value or disclosure state changed.                                          |
| `dismiss` | Close, discard, remove, or clear.                                             |
| `search`  | Query-text input for filtering.                                               |
| `sort`    | Request a sort column/direction.                                              |
| `page`    | Pagination change.                                                            |
| `move`    | Reposition — reorder, drag, resize.                                           |

`EVENT_CONTRACT` is the machine-readable version: per verb, a description plus the documented payload keys the runtime intrinsically attaches (e.g. `commit` may carry `value`, `fields`, `label`, `index`). It is the same data the `frayme_compose` tool definition embeds, and the runtime's payload types are conformance-tested against it — the documented contract cannot drift from the implemented one.

Helpers: `canonicalize(name)` maps legacy event names to their verb (`press` → `commit`, `selectRow` → `select`); `isCanonical(name)` tests membership; `EVENT_ALIASES` is the full mapping.

`IconName` exports the closed icon vocabulary — 280 glyph names, matched exactly by the runtime's icon registry.

## Version policy

Two versions travel with the package, deliberately decoupled:

* **npm version** (`0.3.0`) — the JavaScript API surface. Semver over exports and types.
* **`CATALOG_VERSION`** (`'frayme-0.18.0'`) — the component vocabulary. Bumped whenever component schemas, prop shapes, or descriptions change, even when the code surface is untouched.

Pin against `CATALOG_VERSION` when you cache prompts, store specs long-term, or assert vocabulary compatibility; the live API reports its own as `catalog_version` on [`GET /v1/health`](/api-reference/me-and-health.md). `JSON_RENDER_PIN` (`'0.19.x'`) records the upstream `@json-render/core` line this catalog targets. `CATALOG_COMPONENT_COUNT` is computed from the catalog at import time, so a count you display can never go stale.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.frayme.ai/sdk-reference/catalog.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
