page.mdx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. import { pageMetadata } from "@/lib/page-metadata"
  2. export const metadata = pageMetadata("docs/migration")
  3. # Migration Guide
  4. This guide covers breaking changes introduced in v0.6.0 and how to update your code.
  5. ## State Provider
  6. `DataProvider` has been renamed to `StateProvider`, and its props have changed.
  7. **Before:**
  8. ```tsx
  9. import { DataProvider } from "@json-render/react";
  10. <DataProvider data={myData} getValue={getter} setValue={setter}>
  11. {children}
  12. </DataProvider>
  13. ```
  14. **After:**
  15. ```tsx
  16. import { StateProvider } from "@json-render/react";
  17. <StateProvider initialState={myData} onStateChange={(path, value) => console.log(path, value)}>
  18. {children}
  19. </StateProvider>
  20. ```
  21. `StateProvider` now manages state internally. Use `useStateStore()` to access `get`, `set`, and `update`.
  22. <table>
  23. <thead>
  24. <tr>
  25. <th>Before</th>
  26. <th>After</th>
  27. </tr>
  28. </thead>
  29. <tbody>
  30. <tr><td><code>DataProvider</code></td><td><code>StateProvider</code></td></tr>
  31. <tr><td><code>data</code> prop</td><td><code>initialState</code> prop</td></tr>
  32. <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>
  33. <tr><td><code>useData</code></td><td><code>useStateStore</code></td></tr>
  34. <tr><td><code>useDataValue</code></td><td><code>useStateValue</code></td></tr>
  35. <tr><td><code>useDataBinding</code></td><td><code>useStateBinding</code> (deprecated, use <code>useBoundProp</code> instead)</td></tr>
  36. <tr><td><code>DataModel</code> type</td><td><code>StateModel</code> type</td></tr>
  37. </tbody>
  38. </table>
  39. ## Dynamic Expressions
  40. All dynamic value expressions have been renamed to use `$state`, `$item`, and `$index`.
  41. **Before:**
  42. ```json
  43. {
  44. "type": "Text",
  45. "props": {
  46. "label": { "$path": "/user/name" },
  47. "count": { "$data": "/items/length" }
  48. }
  49. }
  50. ```
  51. **After:**
  52. ```json
  53. {
  54. "type": "Text",
  55. "props": {
  56. "label": { "$state": "/user/name" },
  57. "count": { "$state": "/items/length" }
  58. }
  59. }
  60. ```
  61. Inside repeat scopes, use `$item` and `$index`:
  62. ```json
  63. {
  64. "type": "Card",
  65. "props": {
  66. "title": { "$item": "name" },
  67. "subtitle": { "$index": true }
  68. }
  69. }
  70. ```
  71. <table>
  72. <thead>
  73. <tr>
  74. <th>Before</th>
  75. <th>After</th>
  76. </tr>
  77. </thead>
  78. <tbody>
  79. <tr><td><code>{'{ "$path": "/..." }'}</code></td><td><code>{'{ "$state": "/..." }'}</code></td></tr>
  80. <tr><td><code>{'{ "$data": "/..." }'}</code></td><td><code>{'{ "$state": "/..." }'}</code></td></tr>
  81. </tbody>
  82. </table>
  83. ## Two-Way Binding
  84. Form components no longer use `valuePath` / `statePath` props. Instead, use `$bindState` expressions on the value prop, and `useBoundProp` in your registry.
  85. **Before (catalog):**
  86. ```typescript
  87. Input: {
  88. props: z.object({
  89. label: z.string(),
  90. valuePath: z.string(),
  91. placeholder: z.string().optional(),
  92. }),
  93. }
  94. ```
  95. **Before (spec):**
  96. ```json
  97. {
  98. "type": "Input",
  99. "props": { "label": "Email", "valuePath": "/form/email" }
  100. }
  101. ```
  102. **Before (registry):**
  103. ```tsx
  104. Input: ({ props }) => {
  105. const [value, setValue] = useStateBinding(props.valuePath);
  106. return <input value={value ?? ""} onChange={(e) => setValue(e.target.value)} />;
  107. }
  108. ```
  109. **After (catalog):**
  110. ```typescript
  111. Input: {
  112. props: z.object({
  113. label: z.string(),
  114. value: z.string().optional(),
  115. placeholder: z.string().optional(),
  116. }),
  117. }
  118. ```
  119. **After (spec):**
  120. ```json
  121. {
  122. "type": "Input",
  123. "props": { "label": "Email", "value": { "$bindState": "/form/email" } }
  124. }
  125. ```
  126. **After (registry):**
  127. ```tsx
  128. Input: ({ props, bindings }) => {
  129. const [value, setValue] = useBoundProp<string>(props.value, bindings?.value);
  130. return <input value={value ?? ""} onChange={(e) => setValue(e.target.value)} />;
  131. }
  132. ```
  133. `$bindState` reads from and writes to the given state path. Inside repeat scopes, use `$bindItem` to bind to a field on the current item:
  134. ```json
  135. {
  136. "type": "Checkbox",
  137. "props": { "checked": { "$bindItem": "completed" } }
  138. }
  139. ```
  140. ## Visibility Conditions
  141. Visibility conditions have been renamed to use `$state`, `$and`, and `$or`.
  142. **Before:**
  143. ```json
  144. { "path": "/isAdmin" }
  145. { "eq": [{ "path": "/role" }, "admin"] }
  146. { "and": [{ "path": "/isAdmin" }, { "path": "/feature" }] }
  147. { "or": [{ "path": "/roleA" }, { "path": "/roleB" }] }
  148. ```
  149. **After:**
  150. ```json
  151. { "$state": "/isAdmin" }
  152. { "$state": "/role", "eq": "admin" }
  153. { "$and": [{ "$state": "/isAdmin" }, { "$state": "/feature" }] }
  154. { "$or": [{ "$state": "/roleA" }, { "$state": "/roleB" }] }
  155. ```
  156. You can also use an array as shorthand for `$and`:
  157. ```json
  158. [{ "$state": "/isAdmin" }, { "$state": "/feature" }]
  159. ```
  160. Inside repeat scopes, use `$item` and `$index`:
  161. ```json
  162. { "$item": "isActive" }
  163. { "$index": true, "eq": 0 }
  164. ```
  165. ## Event System
  166. Components now use `emit` to fire named events. `onAction` has been removed.
  167. **Before:**
  168. ```tsx
  169. Button: ({ props, onAction }) => (
  170. <button onClick={() => onAction?.("press")}>{props.label}</button>
  171. )
  172. ```
  173. **After:**
  174. ```tsx
  175. Button: ({ props, emit }) => (
  176. <button onClick={() => emit("press")}>{props.label}</button>
  177. )
  178. ```
  179. `emit` is always defined (never `undefined`), so optional chaining is not needed.
  180. ## Actions Context
  181. `dispatch` has been renamed to `execute`, and the provider prop has been renamed from `actionHandlers` to `handlers`.
  182. **Before:**
  183. ```tsx
  184. const { dispatch } = useActions();
  185. dispatch({ action: "submit", params: {} });
  186. <ActionProvider actionHandlers={myHandlers}>
  187. ```
  188. **After:**
  189. ```tsx
  190. const { execute } = useActions();
  191. execute({ action: "submit", params: {} });
  192. <ActionProvider handlers={myHandlers}>
  193. ```
  194. ## Repeat / List Rendering
  195. The `repeat` field now uses `statePath` instead of `path`.
  196. **Before:**
  197. ```json
  198. {
  199. "type": "Column",
  200. "repeat": { "path": "/todos", "key": "id" },
  201. "children": ["todo-item"]
  202. }
  203. ```
  204. **After:**
  205. ```json
  206. {
  207. "type": "Column",
  208. "repeat": { "statePath": "/todos", "key": "id" },
  209. "children": ["todo-item"]
  210. }
  211. ```
  212. ## Catalog Creation
  213. `createCatalog` and `generateSystemPrompt` have been replaced by `defineSchema` + `defineCatalog`.
  214. **Before:**
  215. ```typescript
  216. import { createCatalog, generateSystemPrompt } from "@json-render/core";
  217. const catalog = createCatalog({
  218. name: "my-app",
  219. components: { /* ... */ },
  220. actions: { /* ... */ },
  221. });
  222. const prompt = generateSystemPrompt(catalog);
  223. ```
  224. **After:**
  225. ```typescript
  226. import { defineCatalog } from "@json-render/core";
  227. import { schema } from "@json-render/react/schema";
  228. const catalog = defineCatalog(schema, {
  229. components: { /* ... */ },
  230. actions: { /* ... */ },
  231. });
  232. const prompt = catalog.prompt();
  233. // Inline mode prompt (formerly "chat")
  234. const inlinePrompt = catalog.prompt({ mode: "inline" });
  235. ```
  236. ## Generation Modes
  237. The generation mode values passed to `catalog.prompt()` have been renamed for clarity:
  238. - `"generate"` is now `"standalone"`
  239. - `"chat"` is now `"inline"`
  240. The old names are accepted as deprecated aliases, so existing code will continue to work. Update when convenient.
  241. **Before:**
  242. ```typescript
  243. const prompt = catalog.prompt({ mode: "generate" });
  244. const chatPrompt = catalog.prompt({ mode: "chat" });
  245. ```
  246. **After:**
  247. ```typescript
  248. const prompt = catalog.prompt({ mode: "standalone" });
  249. const inlinePrompt = catalog.prompt({ mode: "inline" });
  250. ```
  251. The default mode (when no `mode` option is provided) is `"standalone"`, which behaves identically to the previous `"generate"` default.
  252. ## Validation
  253. `ValidationCheck` now uses `type` instead of `fn`, `ValidationProvider` uses `customFunctions` instead of `functions`, and `useFieldValidation` takes a config object instead of a checks array.
  254. **Before:**
  255. ```json
  256. { "fn": "required", "message": "Required" }
  257. { "fn": "minLength", "args": { "length": 8 }, "message": "Too short" }
  258. ```
  259. **After:**
  260. ```json
  261. { "type": "required", "message": "Required" }
  262. { "type": "minLength", "args": { "min": 8 }, "message": "Too short" }
  263. ```
  264. <table>
  265. <thead>
  266. <tr>
  267. <th>Before</th>
  268. <th>After</th>
  269. </tr>
  270. </thead>
  271. <tbody>
  272. <tr><td><code>{'{ fn: "required" }'}</code></td><td><code>{'{ type: "required" }'}</code></td></tr>
  273. <tr><td><code>{'ValidationProvider functions={...}'}</code></td><td><code>{'ValidationProvider customFunctions={...}'}</code></td></tr>
  274. <tr><td><code>useFieldValidation(path, checks)</code></td><td><code>useFieldValidation(path, config)</code> where config is <code>{'{ checks, validateOn? }'}</code></td></tr>
  275. </tbody>
  276. </table>
  277. ## Visibility Provider
  278. The `auth` prop has been removed from `VisibilityProvider`. Auth state should be modeled as regular state.
  279. **Before:**
  280. ```tsx
  281. <VisibilityProvider auth={{ isSignedIn: true, role: "admin" }}>
  282. ```
  283. ```json
  284. { "auth": "signedIn" }
  285. ```
  286. **After:**
  287. ```tsx
  288. <StateProvider initialState={{ auth: { isSignedIn: true, role: "admin" } }}>
  289. <VisibilityProvider>
  290. ```
  291. ```json
  292. { "$state": "/auth/isSignedIn" }
  293. ```
  294. ## Codegen
  295. `traverseTree` has been renamed to `traverseSpec`, `SpecVisitor` to `TreeVisitor`, and the visitor callback now receives a `key` parameter.
  296. **Before:**
  297. ```typescript
  298. import { traverseTree } from "@json-render/codegen";
  299. traverseTree(tree, (element) => {
  300. // ...
  301. });
  302. ```
  303. **After:**
  304. ```typescript
  305. import { traverseSpec } from "@json-render/codegen";
  306. traverseSpec(spec, (element, key) => {
  307. // ...
  308. });
  309. ```
  310. ## Action Params
  311. Action params in specs now use `statePath` instead of `path`.
  312. **Before:**
  313. ```json
  314. {
  315. "on": {
  316. "press": { "action": "setState", "params": { "path": "/count", "value": 0 } }
  317. }
  318. }
  319. ```
  320. **After:**
  321. ```json
  322. {
  323. "on": {
  324. "press": { "action": "setState", "params": { "statePath": "/count", "value": 0 } }
  325. }
  326. }
  327. ```
  328. ## Removed Exports
  329. The following exports have been removed from `@json-render/core`:
  330. <table>
  331. <thead>
  332. <tr>
  333. <th>Removed</th>
  334. <th>Replacement</th>
  335. </tr>
  336. </thead>
  337. <tbody>
  338. <tr>
  339. <td><code>createCatalog</code></td>
  340. <td><code>defineCatalog(schema, config)</code></td>
  341. </tr>
  342. <tr>
  343. <td><code>generateCatalogPrompt</code></td>
  344. <td><code>catalog.prompt()</code></td>
  345. </tr>
  346. <tr>
  347. <td><code>generateSystemPrompt</code></td>
  348. <td><code>catalog.prompt()</code></td>
  349. </tr>
  350. <tr>
  351. <td><code>ComponentDefinition</code></td>
  352. <td>Use catalog component config directly</td>
  353. </tr>
  354. <tr>
  355. <td><code>CatalogConfig</code></td>
  356. <td>Use <code>defineCatalog</code> parameters</td>
  357. </tr>
  358. <tr>
  359. <td><code>SystemPromptOptions</code></td>
  360. <td>Use <code>PromptOptions</code></td>
  361. </tr>
  362. <tr>
  363. <td><code>LogicExpression</code></td>
  364. <td>Use <code>VisibilityCondition</code></td>
  365. </tr>
  366. <tr>
  367. <td><code>AuthState</code></td>
  368. <td>Model auth as regular state (e.g. <code>/auth/isSignedIn</code>)</td>
  369. </tr>
  370. <tr>
  371. <td><code>evaluateLogicExpression</code></td>
  372. <td>Use <code>evaluateVisibility</code></td>
  373. </tr>
  374. <tr>
  375. <td><code>createRendererFromCatalog</code></td>
  376. <td>Use <code>defineRegistry</code></td>
  377. </tr>
  378. <tr>
  379. <td><code>traverseTree</code> (codegen)</td>
  380. <td>Use <code>traverseSpec</code></td>
  381. </tr>
  382. </tbody>
  383. </table>