import { pageMetadata } from "@/lib/page-metadata"
export const metadata = pageMetadata("docs/migration")

# Migration Guide

This guide covers breaking changes introduced in v0.6.0 and how to update your code.

## State Provider

`DataProvider` has been renamed to `StateProvider`, and its props have changed.

**Before:**

```tsx
import { DataProvider } from "@json-render/react";

<DataProvider data={myData} getValue={getter} setValue={setter}>
  {children}
</DataProvider>
```

**After:**

```tsx
import { StateProvider } from "@json-render/react";

<StateProvider initialState={myData} onStateChange={(path, value) => console.log(path, value)}>
  {children}
</StateProvider>
```

`StateProvider` now manages state internally. Use `useStateStore()` to access `get`, `set`, and `update`.

| Before | After |
|--------|-------|
| `DataProvider` | `StateProvider` |
| `data` prop | `initialState` prop |
| `getValue` / `setValue` props | Removed (use `useStateStore()` hook for `get` / `set`) |
| `useData` | `useStateStore` |
| `useDataValue` | `useStateValue` |
| `useDataBinding` | `useStateBinding` (deprecated, use `useBoundProp` instead) |
| `DataModel` type | `StateModel` type |

## Dynamic Expressions

All dynamic value expressions have been renamed to use `$state`, `$item`, and `$index`.

**Before:**

```json
{
  "type": "Text",
  "props": {
    "label": { "$path": "/user/name" },
    "count": { "$data": "/items/length" }
  }
}
```

**After:**

```json
{
  "type": "Text",
  "props": {
    "label": { "$state": "/user/name" },
    "count": { "$state": "/items/length" }
  }
}
```

Inside repeat scopes, use `$item` and `$index`:

```json
{
  "type": "Card",
  "props": {
    "title": { "$item": "name" },
    "subtitle": { "$index": true }
  }
}
```

| Before | After |
|--------|-------|
| `{ "$path": "/..." }` | `{ "$state": "/..." }` |
| `{ "$data": "/..." }` | `{ "$state": "/..." }` |

## Two-Way Binding

Form components no longer use `valuePath` / `statePath` props. Instead, use `$bindState` expressions on the value prop, and `useBoundProp` in your registry.

**Before (catalog):**

```typescript
Input: {
  props: z.object({
    label: z.string(),
    valuePath: z.string(),
    placeholder: z.string().optional(),
  }),
}
```

**Before (spec):**

```json
{
  "type": "Input",
  "props": { "label": "Email", "valuePath": "/form/email" }
}
```

**Before (registry):**

```tsx
Input: ({ props }) => {
  const [value, setValue] = useStateBinding(props.valuePath);
  return <input value={value ?? ""} onChange={(e) => setValue(e.target.value)} />;
}
```

**After (catalog):**

```typescript
Input: {
  props: z.object({
    label: z.string(),
    value: z.string().optional(),
    placeholder: z.string().optional(),
  }),
}
```

**After (spec):**

```json
{
  "type": "Input",
  "props": { "label": "Email", "value": { "$bindState": "/form/email" } }
}
```

**After (registry):**

```tsx
Input: ({ props, bindings }) => {
  const [value, setValue] = useBoundProp<string>(props.value, bindings?.value);
  return <input value={value ?? ""} onChange={(e) => setValue(e.target.value)} />;
}
```

`$bindState` reads from and writes to the given state path. Inside repeat scopes, use `$bindItem` to bind to a field on the current item:

```json
{
  "type": "Checkbox",
  "props": { "checked": { "$bindItem": "completed" } }
}
```

## Visibility Conditions

Visibility conditions have been renamed to use `$state`, `$and`, and `$or`.

**Before:**

```json
{ "path": "/isAdmin" }
{ "eq": [{ "path": "/role" }, "admin"] }
{ "and": [{ "path": "/isAdmin" }, { "path": "/feature" }] }
{ "or": [{ "path": "/roleA" }, { "path": "/roleB" }] }
```

**After:**

```json
{ "$state": "/isAdmin" }
{ "$state": "/role", "eq": "admin" }
{ "$and": [{ "$state": "/isAdmin" }, { "$state": "/feature" }] }
{ "$or": [{ "$state": "/roleA" }, { "$state": "/roleB" }] }
```

You can also use an array as shorthand for `$and`:

```json
[{ "$state": "/isAdmin" }, { "$state": "/feature" }]
```

Inside repeat scopes, use `$item` and `$index`:

```json
{ "$item": "isActive" }
{ "$index": true, "eq": 0 }
```

## Event System

Components now use `emit` to fire named events. `onAction` has been removed.

**Before:**

```tsx
Button: ({ props, onAction }) => (
  <button onClick={() => onAction?.("press")}>{props.label}</button>
)
```

**After:**

```tsx
Button: ({ props, emit }) => (
  <button onClick={() => emit("press")}>{props.label}</button>
)
```

`emit` is always defined (never `undefined`), so optional chaining is not needed.

## Actions Context

`dispatch` has been renamed to `execute`, and the provider prop has been renamed from `actionHandlers` to `handlers`.

**Before:**

```tsx
const { dispatch } = useActions();
dispatch({ action: "submit", params: {} });

<ActionProvider actionHandlers={myHandlers}>
```

**After:**

```tsx
const { execute } = useActions();
execute({ action: "submit", params: {} });

<ActionProvider handlers={myHandlers}>
```

## Repeat / List Rendering

The `repeat` field now uses `statePath` instead of `path`.

**Before:**

```json
{
  "type": "Column",
  "repeat": { "path": "/todos", "key": "id" },
  "children": ["todo-item"]
}
```

**After:**

```json
{
  "type": "Column",
  "repeat": { "statePath": "/todos", "key": "id" },
  "children": ["todo-item"]
}
```

## Catalog Creation

`createCatalog` and `generateSystemPrompt` have been replaced by `defineSchema` + `defineCatalog`.

**Before:**

```typescript
import { createCatalog, generateSystemPrompt } from "@json-render/core";

const catalog = createCatalog({
  name: "my-app",
  components: { /* ... */ },
  actions: { /* ... */ },
});

const prompt = generateSystemPrompt(catalog);
```

**After:**

```typescript
import { defineCatalog } from "@json-render/core";
import { schema } from "@json-render/react/schema";

const catalog = defineCatalog(schema, {
  components: { /* ... */ },
  actions: { /* ... */ },
});

const prompt = catalog.prompt();

// Chat mode prompt
const chatPrompt = catalog.prompt({ mode: "chat" });
```

## Validation

`ValidationCheck` now uses `type` instead of `fn`, `ValidationProvider` uses `customFunctions` instead of `functions`, and `useFieldValidation` takes a config object instead of a checks array.

**Before:**

```json
{ "fn": "required", "message": "Required" }
{ "fn": "minLength", "args": { "length": 8 }, "message": "Too short" }
```

**After:**

```json
{ "type": "required", "message": "Required" }
{ "type": "minLength", "args": { "min": 8 }, "message": "Too short" }
```

| Before | After |
|--------|-------|
| `{ fn: "required" }` | `{ type: "required" }` |
| `ValidationProvider functions={...}` | `ValidationProvider customFunctions={...}` |
| `useFieldValidation(path, checks)` | `useFieldValidation(path, config)` where config is `{ checks, validateOn? }` |

## Visibility Provider

The `auth` prop has been removed from `VisibilityProvider`. Auth state should be modeled as regular state.

**Before:**

```tsx
<VisibilityProvider auth={{ isSignedIn: true, role: "admin" }}>
```

```json
{ "auth": "signedIn" }
```

**After:**

```tsx
<StateProvider initialState={{ auth: { isSignedIn: true, role: "admin" } }}>
  <VisibilityProvider>
```

```json
{ "$state": "/auth/isSignedIn" }
```

## Codegen

`traverseTree` has been renamed to `traverseSpec`, `SpecVisitor` to `TreeVisitor`, and the visitor callback now receives a `key` parameter.

**Before:**

```typescript
import { traverseTree } from "@json-render/codegen";

traverseTree(tree, (element) => {
  // ...
});
```

**After:**

```typescript
import { traverseSpec } from "@json-render/codegen";

traverseSpec(spec, (element, key) => {
  // ...
});
```

## Action Params

Action params in specs now use `statePath` instead of `path`.

**Before:**

```json
{
  "on": {
    "press": { "action": "setState", "params": { "path": "/count", "value": 0 } }
  }
}
```

**After:**

```json
{
  "on": {
    "press": { "action": "setState", "params": { "statePath": "/count", "value": 0 } }
  }
}
```

## Removed Exports

The following exports have been removed from `@json-render/core`:

| Removed | Replacement |
|---------|-------------|
| `createCatalog` | `defineCatalog(schema, config)` |
| `generateCatalogPrompt` | `catalog.prompt()` |
| `generateSystemPrompt` | `catalog.prompt()` |
| `ComponentDefinition` | Use catalog component config directly |
| `CatalogConfig` | Use `defineCatalog` parameters |
| `SystemPromptOptions` | Use `PromptOptions` |
| `LogicExpression` | Use `VisibilityCondition` |
| `AuthState` | Model auth as regular state (e.g. `/auth/isSignedIn`) |
| `evaluateLogicExpression` | Use `evaluateVisibility` |
| `createRendererFromCatalog` | Use `defineRegistry` |
| `traverseTree` (codegen) | Use `traverseSpec` |
