Skip to content

Function: defineComponents()

Documentation / @xmachines/play-catalog / defineComponents

function defineComponents<TCatalog, TComponents>(
catalog,
components,
): NoExtraKeys<TComponents, TCatalog>;

Defined in: define-components.ts:116

Define components matching catalog schema with compile-time validation

Enforces bidirectional validation between catalog and component implementations:

  • All catalog keys must have corresponding components (no missing components)
  • No extra components outside catalog (no undefined keys)
  • TypeScript compilation fails for mismatches (build-time safety)

Architectural Context: Implements Strict Separation (INV-02) by validating that framework components (React/Vue/etc.) exactly match the framework-agnostic catalog that the Actor references. This ensures the Actor can reference component names in meta.view without importing framework code.

Type Parameters

Type Parameter
TCatalog extends Record<string, ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>>
TComponents extends ComponentsFor<TCatalog>

Parameters

ParameterTypeDescription
catalogTCatalogComponent catalog with Zod schemas
componentsNoExtraKeys<TComponents, TCatalog>Component implementations matching catalog keys exactly

Returns

NoExtraKeys<TComponents, TCatalog>

Component map validated at compile time (frozen in development)

Throws

Error if runtime validation detects missing or extra keys

Examples

Valid component definition

import { defineComponents } from "@xmachines/play-catalog";
import { catalog } from "./catalog.js";
import { DashboardComponent, LoginFormComponent } from "./components.js";
// ✅ Valid - all keys match catalog
const components = defineComponents(catalog, {
Dashboard: DashboardComponent,
LoginForm: LoginFormComponent,
});

TypeScript compile errors

import { defineComponents } from "@xmachines/play-catalog";
import { catalog } from "./catalog.js";
// ❌ TypeScript error - missing 'LoginForm'
const components = defineComponents(catalog, {
Dashboard: DashboardComponent,
});
// ❌ TypeScript error - extra 'UnknownComponent' not in catalog
const components = defineComponents(catalog, {
Dashboard: DashboardComponent,
LoginForm: LoginFormComponent,
UnknownComponent: SomeComponent,
});

Progressive example with Actor integration

import { z } from "zod";
import { defineCatalog, defineComponents } from "@xmachines/play-catalog";
import { definePlayer } from "@xmachines/play-xstate";
import { setup } from "xstate";
// 1. Define catalog
const catalog = defineCatalog({
HomePage: z.object({}),
Dashboard: z.object({ userId: z.string() })
});
// 2. Define components
const components = defineComponents(catalog, {
HomePage: () => <div>Home</div>,
Dashboard: ({ userId }) => <div>Dashboard for {userId}</div>
});
// 3. Create machine referencing catalog components
const machine = setup({}).createMachine({
initial: 'home',
states: {
home: {
meta: {
route: '/',
view: { component: 'HomePage' } // References catalog key
}
},
dashboard: {
meta: {
route: '/dashboard',
view: { component: 'Dashboard', userId: '123' }
}
}
}
});
// 4. Create actor with catalog
const createPlayer = definePlayer({ machine, catalog });
const actor = createPlayer();

See