Intermediate Representation
The Intermediate Representation (IR) is the central data model in UIGen. The adapter produces it from your spec, and the React SPA consumes it to render the UI. Because the IR is framework-agnostic, it can drive any renderer (React today, Svelte and Vue in the future).
The IR is defined in packages/core/src/ir/types.ts. The types below are sourced directly from that file.
UIGenApp
The root object. Everything the SPA needs is here.
interface UIGenApp {
meta: AppMeta;
resources: Resource[];
auth: AuthConfig;
dashboard: DashboardConfig;
servers: ServerConfig[];
parsingErrors?: ParsingError[];
}
AppMeta
Basic metadata from the spec's info object.
interface AppMeta {
title: string;
version: string;
description?: string;
}
ServerConfig
Each entry in the spec's servers array becomes a ServerConfig. The SPA uses these to populate the environment switcher.
interface ServerConfig {
url: string;
description?: string;
}
Resource
A resource maps to a group of related endpoints (typically a REST resource like users or products).
interface Resource {
name: string; // "User", "Product"
slug: string; // "users", "products"
uigenId: string; // stable identifier for overrides
description?: string;
operations: Operation[];
schema: SchemaNode; // the primary response schema
relationships: Relationship[];
pagination?: PaginationHint;
}
Operation
An operation maps to a single HTTP endpoint. The viewHint tells the renderer which view to use.
interface Operation {
id: string;
uigenId: string;
method: HttpMethod; // 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
path: string;
summary?: string;
description?: string;
parameters: Parameter[];
requestBody?: SchemaNode;
requestContentType?: string;
responses: Record<string, ResponseDescriptor>;
viewHint: ViewHint;
security?: SecurityRequirement[];
}
ViewHint
The view hint tells the renderer which UI pattern to use for an operation.
type ViewHint =
| 'list' // GET /resources: paginated table
| 'detail' // GET /resources/{id}: read-only record
| 'create' // POST /resources: create form
| 'update' // PUT/PATCH /resources/{id}: edit form
| 'delete' // DELETE /resources/{id}: confirmation dialog
| 'search' // GET with query params: search/filter view
| 'wizard' // POST with large/nested body: multi-step form
| 'dashboard' // overview page
| 'action'; // non-CRUD operation: action button + dialog
SchemaNode
A SchemaNode represents a single field or nested object in a request or response schema.
interface SchemaNode {
type: FieldType;
key: string;
label: string;
required: boolean;
children?: SchemaNode[]; // for object types
items?: SchemaNode; // for array types
enumValues?: string[];
format?: string; // 'date', 'email', 'uri', etc.
validations?: ValidationRule[];
uiHint?: UIHint;
description?: string;
default?: unknown;
readOnly?: boolean;
writeOnly?: boolean;
nullable?: boolean;
deprecated?: boolean;
chartConfig?: ChartConfig; // set by x-uigen-chart on array response schemas
}
ChartConfig
Chart configuration stored on array response schemas when x-uigen-chart is present. Consumed by List View and the chart data pipeline in @uigen-dev/core.
interface ChartConfig {
chartType: 'line' | 'bar' | 'pie' | 'scatter' | 'area' | 'radar' | 'donut';
xAxis: string;
yAxis: string | string[];
series?: SeriesConfig[];
labels?: string;
options?: ChartOptions;
query?: {
limit?: number;
params?: Record<string, string>;
};
sampling?: {
strategy?: 'auto' | 'lttb' | 'bucket-mean' | 'none';
maxPoints?: number;
};
filters?: ChartFilterConfig[]; // rendered as chart filter controls in List View
}
The adapter pipeline prepares chart data before rendering:
interface ChartPreparedViewModel {
points: Record<string, unknown>[];
meta: {
totalPoints: number;
renderedPoints: number;
sampled: boolean;
xAxisType: 'time' | 'category' | 'number';
sortApplied: boolean;
samplingStrategy: 'auto' | 'lttb' | 'bucket-mean' | 'none';
};
}
FieldType
type FieldType =
| 'string'
| 'number'
| 'integer'
| 'boolean'
| 'object'
| 'array'
| 'enum'
| 'date'
| 'file';
AuthConfig
Authentication configuration extracted from the spec's securitySchemes.
interface AuthConfig {
schemes: AuthScheme[];
globalRequired: boolean;
loginEndpoints?: LoginEndpoint[];
refreshEndpoints?: RefreshEndpoint[];
}
interface AuthScheme {
type: 'bearer' | 'apiKey' | 'oauth2' | 'basic';
name: string;
in?: 'header' | 'query' | 'cookie';
scheme?: string;
bearerFormat?: string;
}
PaginationHint
The adapter detects the pagination strategy from query parameter names.
interface PaginationHint {
style: 'offset' | 'cursor' | 'page';
params: Record<string, string>; // e.g. { limit: 'limit', offset: 'offset' }
}
Relationship
Relationships define connections between resources. They can be explicitly configured or automatically detected from API paths.
interface Relationship {
type: 'hasMany' | 'belongsTo' | 'manyToMany';
targetResource: string;
path: string;
label?: string;
description?: string;
isReadOnly: boolean;
}
Relationship Types
-
hasMany: One-to-many relationship (e.g., a user has many orders)- Path pattern:
/{sourceResource}/{id}/{targetResource} - Example:
/users/{id}/orders
- Path pattern:
-
belongsTo: Many-to-one relationship (e.g., an order belongs to a user)- Path pattern:
/{targetResource}/{id}/{sourceResource} - Example:
/users/{id}/orders(from the order's perspective)
- Path pattern:
-
manyToMany: Many-to-many relationship (e.g., projects and tags)- Detected when symmetric relationships exist in both directions
- Example:
/projects/{id}/tagsand/tags/{id}/projects
Explicit Type Configuration
Relationship types can be explicitly specified in the .uigen/config.yaml file:
relationships:
- source: users
target: orders
path: /users/{id}/orders
type: hasMany # Explicit type
label: User Orders
When the type field is present, it takes precedence over path-based derivation. If omitted, the type is automatically derived from the path pattern for backward compatibility.
ValidationRule
Validation rules are extracted from JSON Schema constraints in the spec.
interface ValidationRule {
type: 'minLength' | 'maxLength' | 'pattern' | 'minimum' | 'maximum'
| 'minItems' | 'maxItems' | 'email' | 'url';
value: string | number;
message?: string;
}