npm package: @specverse/runtime
Description: SpecVerse runtime — view engine, React adapter, Tailwind renderer
Latest version: 4.1.12

# @specverse/runtime

Runtime view engine for SpecVerse — renders spec-driven and dev views at runtime in the browser.

## Architecture

```
@specverse/runtime
  src/runtime/views/
    core/                          Framework-agnostic
    ├── entity-display.ts           Entity name resolution (name > title > label > FK chain > ID)
    ├── field-classification.ts     Field categorization (business, metadata, auto, relationship)
    ├── pattern-engine.ts           View type → pattern detection, CURVED protocol mapping
    ├── composite-pattern-types.ts  9 interfaces for tech-independent view patterns
    ├── composite-patterns.ts       4 pattern definitions (form, list, detail, dashboard)
    ├── atomic-components-registry.ts  49 component type definitions
    └── types.ts                    RuntimeViewProviderValue contract

    react/                         React adapter
    ├── context.ts                  RuntimeViewProvider + useRuntimeContext()
    ├── react-pattern-adapter.tsx   1743-line pattern → HTML renderer
    ├── hooks/
    │   ├── useTheme.ts             Dark/light toggle with localStorage
    │   ├── useResizableSidebar.ts  Draggable sidebar width
    │   ├── useEntityHelpers.ts     Field separation, relationship resolution
    │   └── useEventStream.ts       WebSocket event streaming
    └── components/
        ├── DevShell.tsx            Main dev GUI (tabs: Views, Models, Events)
        ├── ViewRouter.tsx          Sidebar + routing (display, forms, operations)
        ├── RuntimeView.tsx         Pattern-based view renderer
        ├── FormView.tsx            View-spec adapter → ModelManager
        ├── ModelSelector.tsx       Model browser sidebar
        ├── ModelManager.tsx        Full CRUD + lifecycle transitions
        ├── ViewSidebar.tsx         View selection sidebar
        ├── RelationshipField.tsx   Smart FK dropdowns (belongsTo/hasMany/hasOne)
        ├── FieldInput.tsx          Type-aware input (bool, number, datetime, etc.)
        ├── EntitySelect.tsx        Entity dropdown with cascading filters
        ├── OperationView.tsx       Service operation header + executor
        ├── OperationExecutor.tsx   Parameter inputs, execute, result display
        ├── OperationResultView.tsx Formatted results with entity name resolution
        ├── EventStream.tsx         Real-time event log with type filtering
        └── ui/
            └── ResizeHandle.tsx    Draggable resize handle

    tailwind/                      Styling
    └── universal-adapter.ts        48 component types → Tailwind HTML markup
```

## Subpath Exports

```ts
// Core utilities (framework-agnostic)
import { getEntityDisplayName, classifyFields, detectPattern, COMPOSITE_VIEW_PATTERNS } from '@specverse/runtime/views';

// React adapter (hooks, components)
import { DevShell, RuntimeView, ViewRouter, FormView, ModelManager, useTheme, useEventStream } from '@specverse/runtime/views/react';

// Tailwind markup generator
import { createUniversalTailwindAdapter } from '@specverse/runtime/views/tailwind';
```

## How It Works

### The RuntimeViewProvider Pattern

The runtime package's components are decoupled from any specific state management or API client. Instead, they consume data and hooks through a React context:

```tsx
// Generated App.tsx (or app-demo's RuntimeBridge)
import { RuntimeViewProvider } from '@specverse/runtime/views/react';

<RuntimeViewProvider value={{
  // Instance-specific hooks — how to fetch data
  useEntitiesQuery: (controller, model) => useMyApiHook(controller, model),
  useModelSchemaQuery: (model) => useMySchemaHook(model),
  useExecuteOperationMutation: () => useMyMutation(),
  useTransitionStateMutation: () => useMyTransitionMutation(),

  // Current state
  entities: { Order: [...], Customer: [...] },
  modelSchemas: { Order: { attributes: {...}, relationships: {...} } },
  views: [...],
  spec: parsedSpec,
  apiBaseUrl: '/api',
}}>
  <DevShell />
</RuntimeViewProvider>
```

Components call `useRuntimeContext()` to access this — no direct imports of appStore, API clients, or framework-specific state.

### Two Realize Modes

```bash
# Runtime mode (default) — slim frontend, views rendered at runtime
specverse realize all specs/main.specly

# Static mode — full frontend, all views pre-generated as code
specverse realize all specs/main.specly --static
```

**Runtime mode** generates ~12 frontend files:
- `App.tsx` — imports DevShell, sets up RuntimeViewProvider
- `apiClient.ts`, `useApi.ts`, `api.ts` — instance-specific API wiring
- `dev.specly` — auto-generated dev GUI views
- Config files (vite, tsconfig, package.json, tailwind, postcss)

**Static mode** generates ~185 frontend files:
- Per-model view components (list, detail, form, dashboard)
- Per-model hooks, types, forms
- Full pattern adapter and tailwind adapter bundled

### dev.specly — The Dev GUI Specification

Inference automatically generates `dev.specly` alongside the inferred spec. It describes the development GUI:

```yaml
metadata:
  name: MyApp-dev
  type: development
  generated: true

navigation:
  sidebar:
    General:
      - Customer
      - Product
    Customer:
      - Order
    Services:
      - ShippingService

views:
  # Standard views for every model
  CustomerList:
    type: list
    model: Customer

  CustomerDetail:
    type: detail
    model: Customer

  CustomerForm:
    type: form
    model: Customer

  # Specialist views (auto-detected from model characteristics)
  OrderBoard:              # lifecycle → board view
    type: board
    model: Order

  OrderTimeline:           # date fields → timeline view
    type: timeline
    model: Order

  ProductDashboard:        # numeric fields → dashboard
    type: dashboard
    model: Product

  # Service operations
  ShippingService_calculateShipping:
    type: operation
    service: ShippingService
    operation: calculateShipping
    parameters:
      orderId: UUID
      weight: Decimal
    returns: Decimal
    source: service
```

### View Promotion Path

Users can progressively promote dev views to spec views:

```
1. Dev view (automatic)     — generated in dev.specly, rendered by DevShell
   ↓
2. Named spec view          — copy to main.specly, same rendering
   ↓
3. Configured spec view     — add columns, sort, filters
   ↓
4. Custom spec view         — add full uiComponents, user-owned code
```

Each step is additive. The runtime engine handles all levels.

## How to Use

### In a Generated Project (realize)

```bash
specverse init my-app --demo
cd my-app
specverse realize all specs/main.specly
cd generated/code
npm run setup
npm run dev:backend   # Start Fastify + Prisma
npm run dev:frontend  # Start Vite dev server → DevShell
```

The frontend opens with three tabs:
- **Views** — browse all spec and dev views
- **Models** — CRUD any model, manage lifecycle states
- **Events** — real-time event stream from the backend

### In app-demo (RuntimeBridge)

```tsx
// main.tsx
import { RuntimeBridge } from './providers/RuntimeBridge';

<QueryProvider>
  <ApiInitializer>
    <RuntimeBridge>
      <App />
    </RuntimeBridge>
  </ApiInitializer>
</QueryProvider>
```

Then import components from the runtime package:

```tsx
import { ViewRouter, FormView, OperationView, usePatternAdapter } from '@specverse/runtime/views/react';
import { ModelSelector } from '@specverse/runtime/views/react';
```

### Standalone Usage

```tsx
import { RuntimeViewProvider, DevShell } from '@specverse/runtime/views/react';

function App() {
  return (
    <RuntimeViewProvider value={myRuntimeConfig}>
      <DevShell
        devSpec={parsedDevSpec}
        appSpec={parsedAppSpec}
        title="My App"
      />
    </RuntimeViewProvider>
  );
}
```

## How to Extend

### Add a New View Type

1. Add pattern detection in `core/pattern-engine.ts`:
```ts
const VIEW_TYPE_TO_PATTERN = {
  // ... existing
  'kanban': { patternId: 'kanban-view', category: 'data-display' },
};
```

2. Add rendering in `react/react-pattern-adapter.tsx` (or the built-in renderer in `RuntimeView.tsx`):
```ts
if (pattern.patternId === 'kanban-view') {
  return this.renderKanbanView(context);
}
```

3. Add to dev.specly generator in `engines/src/inference/dev-specly-generator.ts`:
```ts
if (someModelCharacteristic) {
  views[`${modelName}Kanban`] = { type: 'kanban', model: modelName };
}
```

### Add a New DevShell Tab

Edit `runtime/src/runtime/views/react/components/DevShell.tsx`:

```ts
type TabId = 'views' | 'models' | 'events' | 'mytab';

const TABS: Tab[] = [
  // ... existing
  { id: 'mytab', label: 'My Tab' },
];

// In render:
{activeTab === 'mytab' && <MyComponent />}
```

Or pass a custom tab via props (not yet implemented — would need a `tabs` prop on DevShell).

### Add a New Tailwind Component

Edit `runtime/src/runtime/views/tailwind/universal-adapter.ts`:

```ts
case 'kanban-column':
  return `
    <div class="bg-gray-100 dark:bg-gray-800 rounded-lg p-4 min-w-[280px]">
      <h3 class="font-semibold text-sm mb-3">${properties.title}</h3>
      <div class="space-y-2">${children}</div>
    </div>
  `;
```

### Create a Framework Adapter (Vue, Svelte, etc.)

The core layer (`@specverse/runtime/views`) is framework-agnostic. To create a Vue adapter:

1. Create `runtime/src/runtime/views/vue/` directory
2. Implement the same component set (DevShell, ViewRouter, etc.) using Vue
3. Use the same core utilities (entity-display, field-classification, pattern-engine)
4. Add a subpath export: `@specverse/runtime/views/vue`

The core does the thinking (pattern detection, field classification, entity resolution). The adapter does the rendering.

### Bridge to a Custom State Manager

The `RuntimeViewProviderValue` interface is the only contract. Implement it with any state manager:

```ts
// Redux
const value: RuntimeViewProviderValue = {
  useEntitiesQuery: (ctrl, model) => useSelector(state => state.entities[model]),
  entities: store.getState().entities,
  // ...
};

// MobX
const value: RuntimeViewProviderValue = {
  useEntitiesQuery: (ctrl, model) => ({ data: entityStore.get(model), isLoading: false, error: null }),
  entities: entityStore.all,
  // ...
};
```

## Package Dependencies

```
@specverse/runtime
  ├── @specverse/types (workspace dependency)
  ├── react (peer dependency, ^18.0.0 || ^19.0.0)
  └── react-dom (peer dependency, ^18.0.0 || ^19.0.0)
```

No other runtime dependencies. The tailwind adapter generates HTML strings — Tailwind CSS is a dev dependency of the consuming project, not of the runtime package.

## Known Limitations (v4.1.x)

- **Board, timeline, calendar views** — not yet rendered (filtered from sidebar)
- **Service operation routes** — generated Fastify backend has no service endpoints
- **Custom controller operations** — only standard CRUD routes generated
- **WebSocket events** — generated backend has no `/ws` endpoint (Events tab won't connect)
- **Entity format** — handles both flat (Prisma) and wrapped (app-demo) but FK naming varies (camelCase vs snake_case)
- **No tests** — runtime components have no unit test coverage

See `TODO.md` for the full roadmap including the planned backend runtime engine extraction.

## Relationship to Other Packages

```
@specverse/types      Contracts (both build-time and runtime)
@specverse/entities   Language definition (build-time only)
@specverse/engines    Toolchain — parser, inference, realize (build-time only)
@specverse/runtime    View engine (runtime, browser)         ← this package
```

The engines package knows about runtime (the slim factory generates code that imports it). The runtime package does not know about engines — it only depends on types.