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`.

<table>
<thead>
<tr>
<th>Before</th>
<th>After</th>
</tr>
</thead>
<tbody>
<tr><td><code>DataProvider</code></td><td><code>StateProvider</code></td></tr>
<tr><td><code>data</code> prop</td><td><code>initialState</code> prop</td></tr>
<tr><td><code>getValue</code> / <code>setValue</code> props</td><td>Removed (use <code>useStateStore()</code> hook for <code>get</code> / <code>set</code>)</td></tr>
<tr><td><code>useData</code></td><td><code>useStateStore</code></td></tr>
<tr><td><code>useDataValue</code></td><td><code>useStateValue</code></td></tr>
<tr><td><code>useDataBinding</code></td><td><code>useStateBinding</code> (deprecated, use <code>useBoundProp</code> instead)</td></tr>
<tr><td><code>DataModel</code> type</td><td><code>StateModel</code> type</td></tr>
</tbody>
</table>

## 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 }
  }
}
```

<table>
<thead>
<tr>
<th>Before</th>
<th>After</th>
</tr>
</thead>
<tbody>
<tr><td><code>{'{ "$path": "/..." }'}</code></td><td><code>{'{ "$state": "/..." }'}</code></td></tr>
<tr><td><code>{'{ "$data": "/..." }'}</code></td><td><code>{'{ "$state": "/..." }'}</code></td></tr>
</tbody>
</table>

## 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();

// Inline mode prompt (formerly "chat")
const inlinePrompt = catalog.prompt({ mode: "inline" });
```

## Generation Modes

The generation mode values passed to `catalog.prompt()` have been renamed for clarity:

- `"generate"` is now `"standalone"`
- `"chat"` is now `"inline"`

The old names are accepted as deprecated aliases, so existing code will continue to work. Update when convenient.

**Before:**

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

**After:**

```typescript
const prompt = catalog.prompt({ mode: "standalone" });
const inlinePrompt = catalog.prompt({ mode: "inline" });
```

The default mode (when no `mode` option is provided) is `"standalone"`, which behaves identically to the previous `"generate"` default.

## 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" }
```

<table>
<thead>
<tr>
<th>Before</th>
<th>After</th>
</tr>
</thead>
<tbody>
<tr><td><code>{'{ fn: "required" }'}</code></td><td><code>{'{ type: "required" }'}</code></td></tr>
<tr><td><code>{'ValidationProvider functions={...}'}</code></td><td><code>{'ValidationProvider customFunctions={...}'}</code></td></tr>
<tr><td><code>useFieldValidation(path, checks)</code></td><td><code>useFieldValidation(path, config)</code> where config is <code>{'{ checks, validateOn? }'}</code></td></tr>
</tbody>
</table>

## 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`:

<table>
  <thead>
    <tr>
      <th>Removed</th>
      <th>Replacement</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code>createCatalog</code></td>
      <td><code>defineCatalog(schema, config)</code></td>
    </tr>
    <tr>
      <td><code>generateCatalogPrompt</code></td>
      <td><code>catalog.prompt()</code></td>
    </tr>
    <tr>
      <td><code>generateSystemPrompt</code></td>
      <td><code>catalog.prompt()</code></td>
    </tr>
    <tr>
      <td><code>ComponentDefinition</code></td>
      <td>Use catalog component config directly</td>
    </tr>
    <tr>
      <td><code>CatalogConfig</code></td>
      <td>Use <code>defineCatalog</code> parameters</td>
    </tr>
    <tr>
      <td><code>SystemPromptOptions</code></td>
      <td>Use <code>PromptOptions</code></td>
    </tr>
    <tr>
      <td><code>LogicExpression</code></td>
      <td>Use <code>VisibilityCondition</code></td>
    </tr>
    <tr>
      <td><code>AuthState</code></td>
      <td>Model auth as regular state (e.g. <code>/auth/isSignedIn</code>)</td>
    </tr>
    <tr>
      <td><code>evaluateLogicExpression</code></td>
      <td>Use <code>evaluateVisibility</code></td>
    </tr>
    <tr>
      <td><code>createRendererFromCatalog</code></td>
      <td>Use <code>defineRegistry</code></td>
    </tr>
    <tr>
      <td><code>traverseTree</code> (codegen)</td>
      <td>Use <code>traverseSpec</code></td>
    </tr>
  </tbody>
</table>
