@tmorrow/cre8-wc 2.0.7 → 2.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (319) hide show
  1. package/a2ui/catalog +5 -5
  2. package/a2ui/catalog-kg.json +156 -20
  3. package/a2ui/catalog.json +393 -34
  4. package/a2ui/catalog.svg +2445 -2445
  5. package/a2ui/examples/compound-families.json +298 -0
  6. package/a2ui/examples/llm-observability.json +678 -650
  7. package/a2ui/generate-catalog.mjs +116 -3
  8. package/a2ui/inert-props.json +32 -0
  9. package/a2ui/render-audit.mjs +294 -0
  10. package/a2ui/renderer.d.ts +2 -1
  11. package/a2ui/renderer.js +1 -1
  12. package/a2ui/renderer.ts +1 -1
  13. package/a2ui/smoke-test.mjs +3 -3
  14. package/a2ui/stream/diff-test.mjs +299 -0
  15. package/a2ui/stream/diff.d.ts +28 -0
  16. package/a2ui/stream/diff.js +130 -0
  17. package/a2ui/stream/diff.ts +145 -0
  18. package/a2ui/stream/index.d.ts +11 -0
  19. package/a2ui/stream/index.js +11 -0
  20. package/a2ui/stream/index.ts +43 -0
  21. package/a2ui/stream/model.d.ts +66 -0
  22. package/a2ui/stream/model.js +429 -0
  23. package/a2ui/stream/model.ts +472 -0
  24. package/a2ui/stream/pointer.d.ts +34 -0
  25. package/a2ui/stream/pointer.js +146 -0
  26. package/a2ui/stream/pointer.ts +147 -0
  27. package/a2ui/stream/renderer.d.ts +51 -0
  28. package/a2ui/stream/renderer.js +282 -0
  29. package/a2ui/stream/renderer.ts +291 -0
  30. package/a2ui/stream/stream-test.mjs +592 -0
  31. package/a2ui/stream/types.d.ts +173 -0
  32. package/a2ui/stream/types.js +17 -0
  33. package/a2ui/stream/types.ts +127 -0
  34. package/cdn/cre8-wc.esm.js +3527 -3226
  35. package/cdn/cre8-wc.esm.js.map +1 -1
  36. package/cdn/cre8-wc.min.js +289 -289
  37. package/cdn/cre8-wc.min.js.map +1 -1
  38. package/dist/assets/{Changelog-Documentation-BguNswiM.js → Changelog-Documentation-CeOetLxK.js} +1 -1
  39. package/dist/assets/{Code-Guidelines-BM3dZyzL.js → Code-Guidelines-BjdezAjj.js} +1 -1
  40. package/dist/assets/{Color-6AOWCN3U-C4AEGNk3.js → Color-6AOWCN3U-DPFGZitI.js} +1 -1
  41. package/dist/assets/{Contributing-Guidelines-DZURVehh.js → Contributing-Guidelines-B1M2V1hW.js} +1 -1
  42. package/dist/assets/{DocsRenderer-JROSPFPF-DeZmCI9u.js → DocsRenderer-JROSPFPF-BBy8KWNl.js} +1 -1
  43. package/dist/assets/{Tokens-Documentation-ZecGbtQg.js → Tokens-Documentation-CdTxFDLe.js} +1 -1
  44. package/dist/assets/{WithTooltip-7X4HQR7L-2Hfrkir-.js → WithTooltip-7X4HQR7L-DZzTNMQ7.js} +1 -1
  45. package/dist/assets/{accordion-item.stories-D-MjrX5G.js → accordion-item.stories-Bl26JlHp.js} +1 -9
  46. package/dist/assets/{accordion.stories-icgNa1dl.js → accordion.stories-DjoLApQw.js} +3 -11
  47. package/dist/assets/{alert.stories-BzDaXtj-.js → alert.stories-pO1y3YCQ.js} +15 -31
  48. package/dist/assets/{badge-C5wOWgyg.js → badge-BTxW3bN2.js} +3 -11
  49. package/dist/assets/{badge.stories-5GfB4jVI.js → badge.stories-vqh63RJ2.js} +1 -1
  50. package/dist/assets/{band.stories-DhXaP_kL.js → band.stories-CjKRppSb.js} +1 -9
  51. package/dist/assets/{blocks-DhUVut8Z.js → blocks-1be_-L6v.js} +5 -5
  52. package/dist/assets/{breadcrumbs.stories-CrPUBVk1.js → breadcrumbs.stories-Doyfy3V9.js} +2 -18
  53. package/dist/assets/{button-CJL8yzAT.js → button-CgaWukz0.js} +1 -9
  54. package/dist/assets/{button-group-BgwgtSFC.js → button-group-G7qN4MwM.js} +1 -9
  55. package/dist/assets/{button-group.stories-W-07gO63.js → button-group.stories-cL3Kay6M.js} +1 -1
  56. package/dist/assets/{button.stories-BPzJ-sCv.js → button.stories-C9HSpxWb.js} +6 -6
  57. package/dist/assets/{card.stories-mlFGY2Xn.js → card.stories-BbU8YymD.js} +3 -11
  58. package/dist/assets/{chart.stories-C8yuHHeK.js → chart.stories-BL4FxIA_.js} +1 -1
  59. package/dist/assets/{checkbox-field-item-iHcdmTNw.js → checkbox-field-item-DIMHcWM0.js} +1 -9
  60. package/dist/assets/{checkbox-field-item.stories-D1aVLDXn.js → checkbox-field-item.stories-XbB4x_lO.js} +1 -1
  61. package/dist/assets/{checkbox-field.stories-BoVB2jCx.js → checkbox-field.stories-YnqFUI4K.js} +2 -10
  62. package/dist/assets/{cre8-element-DfmMtHJO.js → cre8-element-B35i4Lyg.js} +1 -1
  63. package/dist/assets/{cre8-form-element-bQpK8UYN.js → cre8-form-element-Cfc5VeYV.js} +1 -1
  64. package/dist/assets/{danger-button.stories-Cb4ljnhQ.js → danger-button.stories-xh25hmfd.js} +1 -9
  65. package/dist/assets/{date-picker.stories-Cje3qk8D.js → date-picker.stories-DD_ctzEF.js} +1 -49
  66. package/dist/assets/{divider-B_PXBGWD.js → divider-xZ2KARHf.js} +2 -10
  67. package/dist/assets/{divider.stories-CtfeBqVV.js → divider.stories-Bk9oF-OV.js} +1 -1
  68. package/dist/assets/{dropdown-Cgwne34I.js → dropdown-BEP5x3CW.js} +6 -22
  69. package/dist/assets/{dropdown-item.stories-pXkDLRuv.js → dropdown-item.stories-Iaw_UrEP.js} +1 -1
  70. package/dist/assets/{dropdown.stories-DZnmHieY.js → dropdown.stories-B0OlKD7h.js} +1 -1
  71. package/dist/assets/{example-drug-card.stories-DTzLXTFx.js → example-drug-card.stories-Bp5JIeE_.js} +1 -1
  72. package/dist/assets/{f-po-C2WpbOFJ.js → f-po-BdPzDRD-.js} +1 -1
  73. package/dist/assets/{feature.stories-DffXdJch.js → feature.stories-DYE1Js70.js} +1 -9
  74. package/dist/assets/{field-DzYRqQb2.js → field-BpUYyRW1.js} +1 -9
  75. package/dist/assets/{field-note-DKXZiXLU.js → field-note-fccBckVl.js} +3 -11
  76. package/dist/assets/{field-note.stories-DS_0bQul.js → field-note.stories-BH9AZMbF.js} +1 -1
  77. package/dist/assets/{field.stories-BvXn0oG6.js → field.stories-BQPtmziK.js} +1 -1
  78. package/dist/assets/{footer.stories-C3ZibisU.js → footer.stories-DCWEciqD.js} +1 -9
  79. package/dist/assets/{form-example.stories-C-TuOeF2.js → form-example.stories-9IDC0AgX.js} +1 -1
  80. package/dist/assets/{formatter-L2YTDE3Q-CJVnxOUW.js → formatter-L2YTDE3Q-B9u1AOWn.js} +1 -1
  81. package/dist/assets/{global-nav-item-xQL7wVjY.js → global-nav-item-T4htN-yO.js} +6 -14
  82. package/dist/assets/{global-nav-item.stories-Dp9TE3aR.js → global-nav-item.stories-2dWRfCXv.js} +1 -1
  83. package/dist/assets/{global-nav.stories-B5Nk8JZ0.js → global-nav.stories-Fh1gScqg.js} +2 -10
  84. package/dist/assets/{grid-DYgnZBsd.js → grid-C5CKMXNe.js} +3 -19
  85. package/dist/assets/{grid.stories-BFiXTq2C.js → grid.stories-BLmCNdeA.js} +1 -1
  86. package/dist/assets/{header.stories-D1f7xhjV.js → header.stories-BkDrjJtj.js} +1 -9
  87. package/dist/assets/{heading-485Jz7JZ.js → heading-Dbka33DK.js} +2 -10
  88. package/dist/assets/{heading.stories-gPF_SmTc.js → heading.stories-Be40_bmG.js} +1 -1
  89. package/dist/assets/{hero.stories-BHlFNdvF.js → hero.stories-vRu7PZG9.js} +1 -9
  90. package/dist/assets/{icon-DeTLDzTS.js → icon-ChfRWXWy.js} +2 -10
  91. package/dist/assets/{icon-description-o-YaiEjK.js → icon-description-BBSwDykJ.js} +1 -1
  92. package/dist/assets/{icon-description.stories-BdPRvEwe.js → icon-description.stories-Cg6ekOme.js} +1 -1
  93. package/dist/assets/{icon.stories-BxWpqxT5.js → icon.stories-Cj4U7L5G.js} +1 -1
  94. package/dist/assets/if-defined-Bxnzk64Q.js +1 -0
  95. package/dist/assets/{iframe-dHvya7sk.js → iframe-BNJP6ovU.js} +35 -35
  96. package/dist/assets/{inline-alert.stories-FH0bQ0jh.js → inline-alert.stories-CygBYI02.js} +1 -9
  97. package/dist/assets/{layout-container.stories-D6Z6Kx78.js → layout-container.stories-TMyIJteJ.js} +2 -10
  98. package/dist/assets/{layout.stories-CNYuwC_r.js → layout.stories-ylRM-ANL.js} +7 -23
  99. package/dist/assets/{linelength-container.stories-pCRXZvxS.js → linelength-container.stories-Br2v8jXE.js} +1 -9
  100. package/dist/assets/{link-BpCiLxNW.js → link-CRE0Eqdo.js} +1 -9
  101. package/dist/assets/{link-list.stories-12vsAa1P.js → link-list.stories--y-urrVo.js} +7 -23
  102. package/dist/assets/{link.stories-DAoQtY4_.js → link.stories-C3WQOgkY.js} +2 -2
  103. package/dist/assets/{list.stories-D0N3yBIf.js → list.stories-Bd2bxkxn.js} +7 -23
  104. package/dist/assets/{loading-spinner-g6iJajXH.js → loading-spinner-C52KNq6y.js} +4 -12
  105. package/dist/assets/{loading-spinner.stories-B6_mgPxB.js → loading-spinner.stories-DPRGEeHT.js} +1 -1
  106. package/dist/assets/{logo.stories-BvMwN2qg.js → logo.stories-2LBqqraT.js} +1 -9
  107. package/dist/assets/{main.stories-pL4zefbs.js → main.stories-BQzD7iWD.js} +2 -10
  108. package/dist/assets/{medication-card.stories-DK1XGJic.js → medication-card.stories-Wc-hA0-s.js} +5 -5
  109. package/dist/assets/{medication-form.stories-Bj5dLp5S.js → medication-form.stories-DfFOCDaU.js} +1 -1
  110. package/dist/assets/{modal-CO0M4e_8.js → modal-CCfbY4fF.js} +1 -9
  111. package/dist/assets/{modal-BNu8xy08.js → modal-GglWRNJp.js} +1 -1
  112. package/dist/assets/{modal.stories-CHYBrq_E.js → modal.stories-CpybCs79.js} +1 -1
  113. package/dist/assets/{multi-select.stories-C9XXsWio.js → multi-select.stories-BhzEKRui.js} +1 -9
  114. package/dist/assets/{nav-container.stories-DU9fhvDu.js → nav-container.stories-Be1YlegP.js} +3 -11
  115. package/dist/assets/{page-header.stories-BrstKiEY.js → page-header.stories-BOVLQ0YF.js} +1 -9
  116. package/dist/assets/{pagination.stories-Bw2M2iOn.js → pagination.stories-D62sa_1K.js} +9 -25
  117. package/dist/assets/{percent-bar.stories-UQRXYUqd.js → percent-bar.stories-CYi4El7T.js} +1 -9
  118. package/dist/assets/{popover.stories-ZkwrRFpR.js → popover.stories-B9O8F4yv.js} +1 -9
  119. package/dist/assets/{primary-nav.stories-wqOKwgQn.js → primary-nav.stories-Bt-o7h45.js} +1 -17
  120. package/dist/assets/{progress-meter-TzASasu0.js → progress-meter-Dit59091.js} +1 -9
  121. package/dist/assets/{progress-meter.stories-qfJALZf8.js → progress-meter.stories-CYnOCmjo.js} +1 -1
  122. package/dist/assets/{progress-steps-item-KvW1d_7L.js → progress-steps-item-CWJsad0s.js} +1 -9
  123. package/dist/assets/{progress-steps-item.stories-uOn6tJ1W.js → progress-steps-item.stories-ePn_6bPn.js} +1 -1
  124. package/dist/assets/{progress-steps.stories-CKFPx8D0.js → progress-steps.stories-CblrEcXF.js} +1 -9
  125. package/dist/assets/{radio-field-item-CTaR50XR.js → radio-field-item-Bm-0RWPe.js} +5 -21
  126. package/dist/assets/{radio-field-item.stories-DQSLj8W1.js → radio-field-item.stories-y_TrTRIW.js} +1 -1
  127. package/dist/assets/{radio-field.stories-Btmc_Iep.js → radio-field.stories-CSWrbUrn.js} +1 -1
  128. package/dist/assets/{remove-tag-BSEZ7VT8.js → remove-tag-lCr57c5r.js} +1 -9
  129. package/dist/assets/{remove-tag.stories-diRKi7Ua.js → remove-tag.stories-CDfwCdvZ.js} +1 -1
  130. package/dist/assets/{request-prescription.stories-DQvra7ia.js → request-prescription.stories-O4zqokfb.js} +1 -1
  131. package/dist/assets/{sample-modal.stories-deE1EBfZ.js → sample-modal.stories-BZ4N2R2Z.js} +1 -1
  132. package/dist/assets/{section.stories-DF4mcy2O.js → section.stories-D_1InThX.js} +1 -9
  133. package/dist/assets/{select-tile-D-C8oSrm.js → select-tile-BqqB3ZtE.js} +2 -10
  134. package/dist/assets/{select-tile-list.stories-vsBQUIcl.js → select-tile-list.stories-Di4ILgV8.js} +1 -9
  135. package/dist/assets/{select-tile.stories-CqHzN5fc.js → select-tile.stories-D3h_6zlV.js} +6 -6
  136. package/dist/assets/{select.stories-8aOGxTtr.js → select.stories-BB2phP9A.js} +1 -9
  137. package/dist/assets/{skeleton-loader.stories-CwR2-aC5.js → skeleton-loader.stories-y4uml_kV.js} +5 -13
  138. package/dist/assets/{split-button.stories-CyfdrK4u.js → split-button.stories-C_82I4ME.js} +2 -10
  139. package/dist/assets/{spread-BPiNgQQF.js → spread-CnPic3C2.js} +1 -1
  140. package/dist/assets/state-DkQhleoO.js +1 -0
  141. package/dist/assets/{submenu.stories-CDhNzZzh.js → submenu.stories-UDtaREr1.js} +2 -18
  142. package/dist/assets/{syntaxhighlighter-T5IQBZDB-BzI6WxUR.js → syntaxhighlighter-T5IQBZDB-D-I_s_yD.js} +1 -1
  143. package/dist/assets/{table-header-CfcchoYz.js → table-header-2837i43H.js} +3 -51
  144. package/dist/assets/{table-object.stories-Bd5qdfMU.js → table-object.stories-D4KoI-wA.js} +1 -9
  145. package/dist/assets/{table.stories-DxuBe9dJ.js → table.stories-DUJDibEp.js} +1 -1
  146. package/dist/assets/{tabs.stories-Cpc3OHJx.js → tabs.stories-C06SngOF.js} +5 -29
  147. package/dist/assets/{tag-list.stories-BmyCmrTF.js → tag-list.stories-Du-IoYzu.js} +2 -10
  148. package/dist/assets/{tag-vQFuZneb.js → tag-oHGdclJl.js} +1 -9
  149. package/dist/assets/{tag.stories-DGzrNUbb.js → tag.stories-C5D5IbuK.js} +1 -1
  150. package/dist/assets/{tertiary-nav.stories-D4q5K-am.js → tertiary-nav.stories-B1GU8Zi4.js} +3 -19
  151. package/dist/assets/{text-link-4qLRIeJB.js → text-link-B3m3KEqY.js} +1 -9
  152. package/dist/assets/{text-link.stories-D6BRm2I5.js → text-link.stories-4O6qn82H.js} +1 -1
  153. package/dist/assets/{text-passage-DGaI5i2I.js → text-passage-xQVosjOr.js} +3 -19
  154. package/dist/assets/{text-passage.stories-DkmXVUhH.js → text-passage.stories-Da1nspwG.js} +1 -1
  155. package/dist/assets/{tooltip.stories-B7ADDq7o.js → tooltip.stories-DLKyyeKn.js} +6 -14
  156. package/dist/assets/{utility-nav.stories-ffXVvyLp.js → utility-nav.stories-B6xBFe7z.js} +5 -21
  157. package/dist/design-tokens/brands/cre8/css/tokens_brand.css +1 -1
  158. package/dist/design-tokens/core/scss/theming/component.css +1 -1
  159. package/dist/design-tokens/core/scss/theming/variables.css +1 -7
  160. package/dist/design-tokens/core/scss/theming/variables.scss +0 -11
  161. package/dist/icons/cre8-icons.svg +1 -1
  162. package/dist/iframe.html +1 -24
  163. package/dist/index.html +25 -77
  164. package/dist/project.json +1 -1
  165. package/lib/a2ui/renderer.d.ts +2 -1
  166. package/lib/a2ui/renderer.d.ts.map +1 -1
  167. package/lib/a2ui/renderer.js +1 -1
  168. package/lib/a2ui/renderer.js.map +1 -1
  169. package/lib/a2ui/stream/diff.d.ts +29 -0
  170. package/lib/a2ui/stream/diff.d.ts.map +1 -0
  171. package/lib/a2ui/stream/diff.js +131 -0
  172. package/lib/a2ui/stream/diff.js.map +1 -0
  173. package/lib/a2ui/stream/index.d.ts +12 -0
  174. package/lib/a2ui/stream/index.d.ts.map +1 -0
  175. package/lib/a2ui/stream/index.js +12 -0
  176. package/lib/a2ui/stream/index.js.map +1 -0
  177. package/lib/a2ui/stream/model.d.ts +67 -0
  178. package/lib/a2ui/stream/model.d.ts.map +1 -0
  179. package/lib/a2ui/stream/model.js +423 -0
  180. package/lib/a2ui/stream/model.js.map +1 -0
  181. package/lib/a2ui/stream/pointer.d.ts +35 -0
  182. package/lib/a2ui/stream/pointer.d.ts.map +1 -0
  183. package/lib/a2ui/stream/pointer.js +147 -0
  184. package/lib/a2ui/stream/pointer.js.map +1 -0
  185. package/lib/a2ui/stream/renderer.d.ts +52 -0
  186. package/lib/a2ui/stream/renderer.d.ts.map +1 -0
  187. package/lib/a2ui/stream/renderer.js +279 -0
  188. package/lib/a2ui/stream/renderer.js.map +1 -0
  189. package/lib/a2ui/stream/types.d.ts +174 -0
  190. package/lib/a2ui/stream/types.d.ts.map +1 -0
  191. package/lib/a2ui/stream/types.js +18 -0
  192. package/lib/a2ui/stream/types.js.map +1 -0
  193. package/lib/components/accordion/accordion.d.ts +15 -0
  194. package/lib/components/accordion/accordion.d.ts.map +1 -1
  195. package/lib/components/accordion/accordion.js +23 -0
  196. package/lib/components/accordion/accordion.js.map +1 -1
  197. package/lib/components/badge/badge.d.ts +4 -17
  198. package/lib/components/badge/badge.d.ts.map +1 -1
  199. package/lib/components/badge/badge.js +0 -15
  200. package/lib/components/badge/badge.js.map +1 -1
  201. package/lib/components/breadcrumbs/breadcrumbs.d.ts +14 -0
  202. package/lib/components/breadcrumbs/breadcrumbs.d.ts.map +1 -1
  203. package/lib/components/breadcrumbs/breadcrumbs.js +21 -0
  204. package/lib/components/breadcrumbs/breadcrumbs.js.map +1 -1
  205. package/lib/components/chart/chart.d.ts +3 -3
  206. package/lib/components/chart/chart.js +6 -6
  207. package/lib/components/chart/chart.js.map +1 -1
  208. package/lib/components/checkbox-field/checkbox-field.d.ts +19 -0
  209. package/lib/components/checkbox-field/checkbox-field.d.ts.map +1 -1
  210. package/lib/components/checkbox-field/checkbox-field.js +28 -0
  211. package/lib/components/checkbox-field/checkbox-field.js.map +1 -1
  212. package/lib/components/cre8-element.d.ts +19 -0
  213. package/lib/components/cre8-element.d.ts.map +1 -1
  214. package/lib/components/cre8-element.js +51 -2
  215. package/lib/components/cre8-element.js.map +1 -1
  216. package/lib/components/divider/divider.d.ts +1 -1
  217. package/lib/components/divider/divider.d.ts.map +1 -1
  218. package/lib/components/divider/divider.js.map +1 -1
  219. package/lib/components/dropdown/dropdown.d.ts +13 -0
  220. package/lib/components/dropdown/dropdown.d.ts.map +1 -1
  221. package/lib/components/dropdown/dropdown.js +19 -0
  222. package/lib/components/dropdown/dropdown.js.map +1 -1
  223. package/lib/components/dropdown-item/dropdown-item.d.ts +1 -1
  224. package/lib/components/dropdown-item/dropdown-item.js +2 -2
  225. package/lib/components/dropdown-item/dropdown-item.js.map +1 -1
  226. package/lib/components/heading/heading.d.ts +3 -5
  227. package/lib/components/heading/heading.d.ts.map +1 -1
  228. package/lib/components/heading/heading.js +2 -4
  229. package/lib/components/heading/heading.js.map +1 -1
  230. package/lib/components/link-list/link-list.d.ts +19 -0
  231. package/lib/components/link-list/link-list.d.ts.map +1 -1
  232. package/lib/components/link-list/link-list.js +19 -0
  233. package/lib/components/link-list/link-list.js.map +1 -1
  234. package/lib/components/list/list.d.ts +13 -0
  235. package/lib/components/list/list.d.ts.map +1 -1
  236. package/lib/components/list/list.js +15 -0
  237. package/lib/components/list/list.js.map +1 -1
  238. package/lib/components/modal/modal.d.ts +2 -2
  239. package/lib/components/modal/modal.js +2 -2
  240. package/lib/components/modal/modal.js.map +1 -1
  241. package/lib/components/multi-select/multi-select.d.ts +2 -2
  242. package/lib/components/multi-select/multi-select.js +5 -5
  243. package/lib/components/multi-select/multi-select.js.map +1 -1
  244. package/lib/components/pagination/pagination.d.ts +1 -1
  245. package/lib/components/pagination/pagination.js +2 -2
  246. package/lib/components/pagination/pagination.js.map +1 -1
  247. package/lib/components/percent-bar/percent-bar.d.ts +2 -0
  248. package/lib/components/percent-bar/percent-bar.d.ts.map +1 -1
  249. package/lib/components/percent-bar/percent-bar.js +3 -1
  250. package/lib/components/percent-bar/percent-bar.js.map +1 -1
  251. package/lib/components/popover/popover.d.ts +6 -7
  252. package/lib/components/popover/popover.d.ts.map +1 -1
  253. package/lib/components/popover/popover.js +4 -4
  254. package/lib/components/popover/popover.js.map +1 -1
  255. package/lib/components/progress-meter/progress-meter.d.ts +0 -8
  256. package/lib/components/progress-meter/progress-meter.d.ts.map +1 -1
  257. package/lib/components/progress-meter/progress-meter.js +0 -7
  258. package/lib/components/progress-meter/progress-meter.js.map +1 -1
  259. package/lib/components/progress-steps/progress-steps.d.ts +16 -0
  260. package/lib/components/progress-steps/progress-steps.d.ts.map +1 -1
  261. package/lib/components/progress-steps/progress-steps.js +25 -0
  262. package/lib/components/progress-steps/progress-steps.js.map +1 -1
  263. package/lib/components/progress-steps-item/progress-steps-item.d.ts +1 -3
  264. package/lib/components/progress-steps-item/progress-steps-item.d.ts.map +1 -1
  265. package/lib/components/progress-steps-item/progress-steps-item.js +0 -1
  266. package/lib/components/progress-steps-item/progress-steps-item.js.map +1 -1
  267. package/lib/components/radio-field/radio-field.d.ts +33 -0
  268. package/lib/components/radio-field/radio-field.d.ts.map +1 -1
  269. package/lib/components/radio-field/radio-field.js +62 -0
  270. package/lib/components/radio-field/radio-field.js.map +1 -1
  271. package/lib/components/remove-tag/remove-tag.d.ts +1 -1
  272. package/lib/components/remove-tag/remove-tag.js +3 -3
  273. package/lib/components/remove-tag/remove-tag.js.map +1 -1
  274. package/lib/components/split-button/split-button.d.ts +3 -0
  275. package/lib/components/split-button/split-button.d.ts.map +1 -1
  276. package/lib/components/split-button/split-button.js +5 -2
  277. package/lib/components/split-button/split-button.js.map +1 -1
  278. package/lib/components/tab/tab.d.ts +1 -1
  279. package/lib/components/tab/tab.js +3 -3
  280. package/lib/components/tab/tab.js.map +1 -1
  281. package/lib/components/table/table.d.ts +36 -0
  282. package/lib/components/table/table.d.ts.map +1 -1
  283. package/lib/components/table/table.js +55 -0
  284. package/lib/components/table/table.js.map +1 -1
  285. package/lib/components/tabs/tabs.d.ts +20 -3
  286. package/lib/components/tabs/tabs.d.ts.map +1 -1
  287. package/lib/components/tabs/tabs.js +33 -5
  288. package/lib/components/tabs/tabs.js.map +1 -1
  289. package/lib/components/tag-list/tag-list.d.ts +17 -0
  290. package/lib/components/tag-list/tag-list.d.ts.map +1 -1
  291. package/lib/components/tag-list/tag-list.js +25 -0
  292. package/lib/components/tag-list/tag-list.js.map +1 -1
  293. package/lib/components/tooltip/tooltip.d.ts +2 -2
  294. package/lib/components/tooltip/tooltip.js +4 -4
  295. package/lib/components/tooltip/tooltip.js.map +1 -1
  296. package/lib/components/utils/light-children.d.ts +40 -0
  297. package/lib/components/utils/light-children.d.ts.map +1 -0
  298. package/lib/components/utils/light-children.js +100 -0
  299. package/lib/components/utils/light-children.js.map +1 -0
  300. package/lib/directives/if-truthy.d.ts +1 -1
  301. package/lib/light-children-TlEXev5S.js +39 -0
  302. package/lib/scripts/generate-react-wrappers.d.ts +8 -1
  303. package/lib/scripts/generate-react-wrappers.d.ts.map +1 -1
  304. package/lib/scripts/generate-react-wrappers.js +45 -1
  305. package/lib/scripts/generate-react-wrappers.js.map +1 -1
  306. package/mcp-manifest.json +162 -52
  307. package/package.json +11 -4
  308. package/react-manifest.json +153 -47
  309. package/dist/assets/if-defined-HKKhGupN.js +0 -1
  310. package/dist/assets/iframe-CWwoGI65.css +0 -1
  311. package/dist/assets/state-CfuknrX8.js +0 -1
  312. package/dist/fonts/PlusJakartaSans-Bold.woff2 +0 -0
  313. package/dist/fonts/PlusJakartaSans-Medium.woff2 +0 -0
  314. package/dist/fonts/PlusJakartaSans-Regular.woff2 +0 -0
  315. package/dist/fonts/PlusJakartaSans-SemiBold.woff2 +0 -0
  316. package/dist/logo.svg +0 -1
  317. package/dist/sb-addons/storybook-4/manager-bundle.js +0 -8
  318. /package/dist/icons/{express-scripts copy.svg → pharmacy copy.svg} +0 -0
  319. /package/dist/icons/{express-scripts.svg → pharmacy.svg} +0 -0
@@ -0,0 +1,592 @@
1
+ /**
2
+ * Tests for the A2UI streaming surface layer.
3
+ *
4
+ * npx tsx a2ui/stream/stream-test.mjs
5
+ *
6
+ * Covers the model's apply-semantics (including that a rejected patch leaves the
7
+ * surface untouched), binding resolution, path shifting, and the renderer's
8
+ * incremental DOM behaviour under jsdom.
9
+ */
10
+
11
+ import { readFileSync } from 'node:fs';
12
+ import { fileURLToPath } from 'node:url';
13
+ import { dirname, resolve } from 'node:path';
14
+ import { JSDOM } from 'jsdom';
15
+
16
+ const __dirname = dirname(fileURLToPath(import.meta.url));
17
+ const catalogJson = JSON.parse(readFileSync(resolve(__dirname, '../catalog.json'), 'utf8'));
18
+
19
+ const dom = new JSDOM('<!doctype html><html><body><div id="app"></div></body></html>');
20
+ globalThis.document = dom.window.document;
21
+ globalThis.HTMLElement = dom.window.HTMLElement;
22
+ globalThis.Event = dom.window.Event;
23
+ globalThis.CustomEvent = dom.window.CustomEvent;
24
+
25
+ const { registerCatalog } = await import('../index.ts');
26
+ const { SurfaceModel, SurfaceRenderer, SurfaceSeqGapError } = await import('./index.ts');
27
+
28
+ const catalog = registerCatalog(catalogJson);
29
+
30
+ let passed = 0;
31
+ const failures = [];
32
+
33
+ function test(name, fn) {
34
+ try {
35
+ fn();
36
+ passed++;
37
+ console.log(` ok ${name}`);
38
+ } catch (err) {
39
+ failures.push({ name, err });
40
+ console.log(`FAIL ${name}\n ${err.message}`);
41
+ }
42
+ }
43
+
44
+ function assert(cond, message) {
45
+ if (!cond) throw new Error(message ?? 'assertion failed');
46
+ }
47
+
48
+ function assertEqual(actual, expected, message) {
49
+ if (actual !== expected) {
50
+ throw new Error(`${message ?? 'not equal'}: expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`);
51
+ }
52
+ }
53
+
54
+ function assertThrows(fn, match, message) {
55
+ let threw = null;
56
+ try {
57
+ fn();
58
+ } catch (err) {
59
+ threw = err;
60
+ }
61
+ if (!threw) throw new Error(`${message ?? 'expected a throw'} — nothing was thrown`);
62
+ if (match && !String(threw.message).includes(match)) {
63
+ throw new Error(`${message ?? 'wrong error'}: expected message containing "${match}", got "${threw.message}"`);
64
+ }
65
+ return threw;
66
+ }
67
+
68
+ function newModel(options = {}) {
69
+ const model = new SurfaceModel(catalog, { surfaceId: 's1', ...options });
70
+ model.apply({
71
+ v: 1,
72
+ type: 'surface.create',
73
+ surfaceId: 's1',
74
+ seq: 0,
75
+ catalogId: catalog.id,
76
+ root: { component: 'cre8-layout-container' },
77
+ data: options.data ?? {},
78
+ });
79
+ return model;
80
+ }
81
+
82
+ function patch(model, ops, seq) {
83
+ return model.apply({
84
+ v: 1,
85
+ type: 'surface.patch',
86
+ surfaceId: model.surfaceId,
87
+ seq: seq ?? model.seq + 1,
88
+ ops,
89
+ });
90
+ }
91
+
92
+ // ─── model: structure ───────────────────────────────────────────────────────
93
+
94
+ test('create establishes a validated root', () => {
95
+ const model = newModel();
96
+ assertEqual(model.root.component, 'cre8-layout-container');
97
+ assertEqual(model.state, 'streaming');
98
+ });
99
+
100
+ test('create rejects a root that fails validation, before any state changes', () => {
101
+ const model = new SurfaceModel(catalog, { surfaceId: 's1' });
102
+ assertThrows(
103
+ () =>
104
+ model.apply({
105
+ v: 1,
106
+ type: 'surface.create',
107
+ surfaceId: 's1',
108
+ seq: 0,
109
+ catalogId: catalog.id,
110
+ root: { component: 'cre8-nonexistent' },
111
+ }),
112
+ 'not registered in catalog'
113
+ );
114
+ assertEqual(model.root, null);
115
+ });
116
+
117
+ test('create rejects a catalogId that is not the registered one', () => {
118
+ const model = new SurfaceModel(catalog, { surfaceId: 's1' });
119
+ assertThrows(
120
+ () =>
121
+ model.apply({
122
+ v: 1,
123
+ type: 'surface.create',
124
+ surfaceId: 's1',
125
+ seq: 0,
126
+ catalogId: 'some-other-catalog',
127
+ }),
128
+ 'does not match registered catalog'
129
+ );
130
+ });
131
+
132
+ test('append adds to children and the node is addressable by path', () => {
133
+ const model = newModel();
134
+ patch(model, [
135
+ {
136
+ op: 'append',
137
+ path: '$',
138
+ nodes: [{ component: 'cre8-heading', props: { tagVariant: 'h1' }, children: ['Live'] }],
139
+ },
140
+ ]);
141
+ assertEqual(model.root.children.length, 1);
142
+ assertEqual(model.nodeAt('$.children[0]').component, 'cre8-heading');
143
+ });
144
+
145
+ test('append into a named slot works and is path-addressable', () => {
146
+ const model = newModel();
147
+ patch(model, [{ op: 'append', path: '$', nodes: [{ component: 'cre8-card' }] }]);
148
+ patch(model, [
149
+ {
150
+ op: 'append',
151
+ path: '$.children[0]',
152
+ slot: 'footer',
153
+ nodes: [{ component: 'cre8-button', props: { text: 'Upgrade', variant: 'primary' } }],
154
+ },
155
+ ]);
156
+ assertEqual(model.nodeAt('$.children[0].slots.footer[0]').props.text, 'Upgrade');
157
+ });
158
+
159
+ test('the children-vs-slots rule is enforced, and a rejected patch changes nothing', () => {
160
+ const model = newModel();
161
+ patch(model, [{ op: 'append', path: '$', nodes: [{ component: 'cre8-card' }] }]);
162
+ const before = JSON.stringify(model.root);
163
+ assertThrows(
164
+ () => patch(model, [{ op: 'append', path: '$.children[0]', nodes: ['illegal body text'] }]),
165
+ 'does not accept default children'
166
+ );
167
+ assertEqual(JSON.stringify(model.root), before, 'surface must be untouched after a rejected patch');
168
+ });
169
+
170
+ test('an undeclared slot is rejected', () => {
171
+ const model = newModel();
172
+ patch(model, [{ op: 'append', path: '$', nodes: [{ component: 'cre8-card' }] }]);
173
+ assertThrows(
174
+ () =>
175
+ patch(model, [
176
+ { op: 'append', path: '$.children[0]', slot: 'nope', nodes: [{ component: 'cre8-heading' }] },
177
+ ]),
178
+ 'not a declared slot'
179
+ );
180
+ });
181
+
182
+ test('a bad enum value is rejected with a path-qualified message', () => {
183
+ const model = newModel();
184
+ const err = assertThrows(
185
+ () =>
186
+ patch(model, [
187
+ { op: 'append', path: '$', nodes: [{ component: 'cre8-button', props: { variant: 'bogus' } }] },
188
+ ]),
189
+ 'not in enum'
190
+ );
191
+ assert(err.message.includes('props.variant'), `message should name the prop path, got: ${err.message}`);
192
+ });
193
+
194
+ test('remove splices and later siblings shift down', () => {
195
+ const model = newModel();
196
+ patch(model, [
197
+ {
198
+ op: 'append',
199
+ path: '$',
200
+ nodes: [
201
+ { component: 'cre8-heading', children: ['one'] },
202
+ { component: 'cre8-heading', children: ['two'] },
203
+ { component: 'cre8-heading', children: ['three'] },
204
+ ],
205
+ },
206
+ ]);
207
+ patch(model, [{ op: 'remove', path: '$.children[0]' }]);
208
+ assertEqual(model.root.children.length, 2);
209
+ assertEqual(model.nodeAt('$.children[0]').children[0], 'two');
210
+ });
211
+
212
+ test('insert places a node at an index and rejects an out-of-range one', () => {
213
+ const model = newModel();
214
+ patch(model, [{ op: 'append', path: '$', nodes: [{ component: 'cre8-heading', children: ['b'] }] }]);
215
+ patch(model, [
216
+ { op: 'insert', path: '$', index: 0, nodes: [{ component: 'cre8-heading', children: ['a'] }] },
217
+ ]);
218
+ assertEqual(model.nodeAt('$.children[0]').children[0], 'a');
219
+ assertThrows(
220
+ () => patch(model, [{ op: 'insert', path: '$', index: 9, nodes: [] }]),
221
+ 'out of range'
222
+ );
223
+ });
224
+
225
+ test('clear empties a container and does not leave an illegal empty children array', () => {
226
+ const model = newModel();
227
+ patch(model, [{ op: 'append', path: '$', nodes: [{ component: 'cre8-card' }] }]);
228
+ patch(model, [
229
+ {
230
+ op: 'append',
231
+ path: '$.children[0]',
232
+ slot: 'default',
233
+ nodes: [{ component: 'cre8-text-passage', children: ['x'] }],
234
+ },
235
+ ]);
236
+ patch(model, [{ op: 'clear', path: '$.children[0]', slot: 'default' }]);
237
+ assertEqual(model.nodeAt('$.children[0]').slots, undefined, 'emptied slots map should be pruned');
238
+ });
239
+
240
+ test('replace at $ swaps the root; remove at $ clears it', () => {
241
+ const model = newModel();
242
+ patch(model, [{ op: 'replace', path: '$', node: { component: 'cre8-grid' } }]);
243
+ assertEqual(model.root.component, 'cre8-grid');
244
+ patch(model, [{ op: 'remove', path: '$' }]);
245
+ assertEqual(model.root, null);
246
+ });
247
+
248
+ // ─── model: props, text, data ───────────────────────────────────────────────
249
+
250
+ test('setProps merges, and a null value deletes a prop', () => {
251
+ const model = newModel();
252
+ patch(model, [
253
+ { op: 'append', path: '$', nodes: [{ component: 'cre8-button', props: { text: 'Go', variant: 'primary' } }] },
254
+ ]);
255
+ patch(model, [{ op: 'setProps', path: '$.children[0]', props: { variant: 'secondary' } }]);
256
+ assertEqual(model.nodeAt('$.children[0]').props.variant, 'secondary');
257
+ assertEqual(model.nodeAt('$.children[0]').props.text, 'Go', 'merge must not drop other props');
258
+ patch(model, [{ op: 'setProps', path: '$.children[0]', props: { variant: null } }]);
259
+ assertEqual('variant' in model.nodeAt('$.children[0]').props, false);
260
+ });
261
+
262
+ test('appendText concatenates into the trailing text node — the token path', () => {
263
+ const model = newModel();
264
+ patch(model, [{ op: 'append', path: '$', nodes: [{ component: 'cre8-text-passage' }] }]);
265
+ const first = patch(model, [{ op: 'appendText', path: '$.children[0]', text: 'Hel' }]);
266
+ assertEqual(first.changes[0].kind, 'content', 'first token creates the text node');
267
+ const second = patch(model, [{ op: 'appendText', path: '$.children[0]', text: 'lo' }]);
268
+ assertEqual(second.changes[0].kind, 'text', 'later tokens take the cheap path');
269
+ assertEqual(model.nodeAt('$.children[0]').children[0], 'Hello');
270
+ });
271
+
272
+ test('setText replaces the whole content list', () => {
273
+ const model = newModel();
274
+ patch(model, [
275
+ { op: 'append', path: '$', nodes: [{ component: 'cre8-text-passage', children: ['old', 'er'] }] },
276
+ ]);
277
+ patch(model, [{ op: 'setText', path: '$.children[0]', text: 'new' }]);
278
+ assertEqual(JSON.stringify(model.nodeAt('$.children[0]').children), JSON.stringify(['new']));
279
+ });
280
+
281
+ test('a bound prop resolves from the data model', () => {
282
+ const model = newModel({ data: { cta: { label: 'Buy now' } } });
283
+ patch(model, [
284
+ {
285
+ op: 'append',
286
+ path: '$',
287
+ nodes: [{ component: 'cre8-button', props: { text: { $bind: '/cta/label' } } }],
288
+ },
289
+ ]);
290
+ assertEqual(model.snapshot().children[0].props.text, 'Buy now');
291
+ assertEqual(model.nodeAt('$.children[0]').props.text.$bind, '/cta/label', 'raw tree keeps the binding');
292
+ });
293
+
294
+ test('an unresolved binding falls back to default, then drops out entirely', () => {
295
+ const model = newModel();
296
+ patch(model, [
297
+ {
298
+ op: 'append',
299
+ path: '$',
300
+ nodes: [
301
+ {
302
+ component: 'cre8-button',
303
+ props: { text: { $bind: '/missing', default: 'Fallback' }, variant: { $bind: '/nope' } },
304
+ },
305
+ ],
306
+ },
307
+ ]);
308
+ const resolved = model.snapshot().children[0].props;
309
+ assertEqual(resolved.text, 'Fallback');
310
+ assertEqual('variant' in resolved, false, 'an unresolvable binding with no default must not emit undefined');
311
+ });
312
+
313
+ test('surface.data updates bound values', () => {
314
+ const model = newModel({ data: { cta: 'Buy' } });
315
+ patch(model, [
316
+ { op: 'append', path: '$', nodes: [{ component: 'cre8-button', props: { text: { $bind: '/cta' } } }] },
317
+ ]);
318
+ model.apply({
319
+ v: 1,
320
+ type: 'surface.data',
321
+ surfaceId: 's1',
322
+ seq: model.seq + 1,
323
+ patches: [{ pointer: '/cta', value: 'Checkout' }],
324
+ });
325
+ assertEqual(model.snapshot().children[0].props.text, 'Checkout');
326
+ });
327
+
328
+ test('a bound value of the wrong type is caught by the prop schema', () => {
329
+ const model = newModel({ data: { cta: 'Buy' } });
330
+ patch(model, [
331
+ { op: 'append', path: '$', nodes: [{ component: 'cre8-button', props: { text: { $bind: '/cta' } } }] },
332
+ ]);
333
+ assertThrows(
334
+ () =>
335
+ model.apply({
336
+ v: 1,
337
+ type: 'surface.data',
338
+ surfaceId: 's1',
339
+ seq: model.seq + 1,
340
+ patches: [{ pointer: '/cta', value: 42 }],
341
+ }),
342
+ 'expected type string'
343
+ );
344
+ });
345
+
346
+ test('a rolled-back patch is flagged so a renderer knows to remount', () => {
347
+ const model = newModel();
348
+ patch(model, [{ op: 'append', path: '$', nodes: [{ component: 'cre8-heading', children: ['a'] }] }]);
349
+ const err = assertThrows(
350
+ () =>
351
+ patch(model, [
352
+ { op: 'append', path: '$', nodes: [{ component: 'cre8-heading', children: ['b'] }] },
353
+ { op: 'append', path: '$', nodes: [{ component: 'cre8-nope' }] },
354
+ ]),
355
+ 'not registered in catalog'
356
+ );
357
+ assertEqual(err.rolledBack, true);
358
+ assertEqual(model.root.children.length, 1, 'the first op in the failed message must not survive');
359
+ });
360
+
361
+ test('surface.data that would make the tree invalid is rejected and the data is unchanged', () => {
362
+ const model = newModel({ data: { v: 'primary' } });
363
+ patch(model, [
364
+ { op: 'append', path: '$', nodes: [{ component: 'cre8-button', props: { variant: { $bind: '/v' } } }] },
365
+ ]);
366
+ assertThrows(
367
+ () =>
368
+ model.apply({
369
+ v: 1,
370
+ type: 'surface.data',
371
+ surfaceId: 's1',
372
+ seq: model.seq + 1,
373
+ patches: [{ pointer: '/v', value: 'bogus' }],
374
+ }),
375
+ 'not in enum'
376
+ );
377
+ assertEqual(model.data.v, 'primary');
378
+ });
379
+
380
+ test('a data pointer can be removed', () => {
381
+ const model = newModel({ data: { a: { b: 1 } } });
382
+ model.apply({
383
+ v: 1,
384
+ type: 'surface.data',
385
+ surfaceId: 's1',
386
+ seq: model.seq + 1,
387
+ patches: [{ pointer: '/a/b', op: 'remove' }],
388
+ });
389
+ assertEqual('b' in model.data.a, false);
390
+ });
391
+
392
+ // ─── model: envelope ────────────────────────────────────────────────────────
393
+
394
+ test('a sequence gap is reported rather than silently applied', () => {
395
+ const model = newModel();
396
+ const err = assertThrows(() => patch(model, [], model.seq + 5), 'out of order');
397
+ assert(err instanceof SurfaceSeqGapError, 'should raise SurfaceSeqGapError');
398
+ });
399
+
400
+ test('an unknown protocol version is rejected', () => {
401
+ const model = newModel();
402
+ assertThrows(
403
+ () => model.apply({ v: 99, type: 'surface.status', surfaceId: 's1', seq: model.seq + 1, state: 'done' }),
404
+ 'unsupported surface protocol version'
405
+ );
406
+ });
407
+
408
+ test('status and delete move surface state', () => {
409
+ const model = newModel();
410
+ model.apply({ v: 1, type: 'surface.status', surfaceId: 's1', seq: model.seq + 1, state: 'done' });
411
+ assertEqual(model.state, 'done');
412
+ model.apply({ v: 1, type: 'surface.delete', surfaceId: 's1', seq: model.seq + 1 });
413
+ assertEqual(model.root, null);
414
+ });
415
+
416
+ // ─── renderer ───────────────────────────────────────────────────────────────
417
+
418
+ function newRendered(options = {}) {
419
+ const host = dom.window.document.createElement('div');
420
+ dom.window.document.body.appendChild(host);
421
+ const model = new SurfaceModel(catalog, { surfaceId: 's1' });
422
+ const events = [];
423
+ const renderer = new SurfaceRenderer(model, {
424
+ root: host,
425
+ doc: dom.window.document,
426
+ onEvent: (e) => events.push(e),
427
+ ...options,
428
+ });
429
+ renderer.apply({
430
+ v: 1,
431
+ type: 'surface.create',
432
+ surfaceId: 's1',
433
+ seq: 0,
434
+ catalogId: catalog.id,
435
+ root: { component: 'cre8-layout-container' },
436
+ });
437
+ return { host, model, renderer, events };
438
+ }
439
+
440
+ function rpatch(renderer, ops) {
441
+ return renderer.apply({
442
+ v: 1,
443
+ type: 'surface.patch',
444
+ surfaceId: renderer.model.surfaceId,
445
+ seq: renderer.model.seq + 1,
446
+ ops,
447
+ });
448
+ }
449
+
450
+ test('renderer mounts the root and appends real elements', () => {
451
+ const { host, renderer } = newRendered();
452
+ assertEqual(host.firstElementChild.tagName.toLowerCase(), 'cre8-layout-container');
453
+ rpatch(renderer, [
454
+ {
455
+ op: 'append',
456
+ path: '$',
457
+ nodes: [
458
+ {
459
+ component: 'cre8-heading',
460
+ props: { tagVariant: 'h1', type: 'title-default' },
461
+ children: ['Live'],
462
+ },
463
+ ],
464
+ },
465
+ ]);
466
+ const heading = host.querySelector('cre8-heading');
467
+ assert(heading, 'heading element should exist');
468
+ // The catalog's x-kind decides attribute vs property, and the streaming
469
+ // renderer must honour it the same way the one-shot renderer does.
470
+ assertEqual(heading.tagVariant, 'h1', 'x-kind "property" props are set as JS properties');
471
+ assertEqual(heading.getAttribute('type'), 'title-default', 'other props become attributes');
472
+ assertEqual(heading.textContent, 'Live');
473
+ });
474
+
475
+ test('appending a sibling preserves the existing element instance', () => {
476
+ const { renderer } = newRendered();
477
+ rpatch(renderer, [{ op: 'append', path: '$', nodes: [{ component: 'cre8-heading', children: ['a'] }] }]);
478
+ const first = renderer.elementAt('$.children[0]');
479
+ first.dataset.marker = 'kept';
480
+ rpatch(renderer, [{ op: 'append', path: '$', nodes: [{ component: 'cre8-heading', children: ['b'] }] }]);
481
+ assertEqual(renderer.elementAt('$.children[0]').dataset.marker, 'kept');
482
+ assertEqual(renderer.elementAt('$.children[1]').textContent, 'b');
483
+ });
484
+
485
+ test('inserting above a node moves it in the DOM without recreating it', () => {
486
+ const { host, renderer } = newRendered();
487
+ rpatch(renderer, [{ op: 'append', path: '$', nodes: [{ component: 'cre8-heading', children: ['b'] }] }]);
488
+ const original = renderer.elementAt('$.children[0]');
489
+ rpatch(renderer, [
490
+ { op: 'insert', path: '$', index: 0, nodes: [{ component: 'cre8-heading', children: ['a'] }] },
491
+ ]);
492
+ assertEqual(renderer.elementAt('$.children[1]'), original, 'the moved node must be the same element');
493
+ const order = [...host.querySelectorAll('cre8-heading')].map((el) => el.textContent);
494
+ assertEqual(order.join(','), 'a,b');
495
+ });
496
+
497
+ test('token streaming mutates one text node instead of replacing it', () => {
498
+ const { renderer } = newRendered();
499
+ rpatch(renderer, [{ op: 'append', path: '$', nodes: [{ component: 'cre8-text-passage' }] }]);
500
+ rpatch(renderer, [{ op: 'appendText', path: '$.children[0]', text: 'Hel' }]);
501
+ const el = renderer.elementAt('$.children[0]');
502
+ const textNode = el.firstChild;
503
+ rpatch(renderer, [{ op: 'appendText', path: '$.children[0]', text: 'lo world' }]);
504
+ assertEqual(el.firstChild, textNode, 'the same Text node should be reused across tokens');
505
+ assertEqual(el.textContent, 'Hello world');
506
+ });
507
+
508
+ test('a removed prop is removed from the element', () => {
509
+ const { renderer } = newRendered();
510
+ rpatch(renderer, [
511
+ { op: 'append', path: '$', nodes: [{ component: 'cre8-button', props: { text: 'Go', variant: 'secondary' } }] },
512
+ ]);
513
+ assertEqual(renderer.elementAt('$.children[0]').getAttribute('variant'), 'secondary');
514
+ rpatch(renderer, [{ op: 'setProps', path: '$.children[0]', props: { variant: null } }]);
515
+ assertEqual(renderer.elementAt('$.children[0]').hasAttribute('variant'), false);
516
+ });
517
+
518
+ test('slotted children get a slot attribute; text in a named slot is wrapped', () => {
519
+ const { renderer } = newRendered();
520
+ rpatch(renderer, [{ op: 'append', path: '$', nodes: [{ component: 'cre8-card' }] }]);
521
+ rpatch(renderer, [
522
+ {
523
+ op: 'append',
524
+ path: '$.children[0]',
525
+ slot: 'footer',
526
+ nodes: [{ component: 'cre8-button', props: { text: 'Upgrade' } }],
527
+ },
528
+ ]);
529
+ rpatch(renderer, [{ op: 'appendText', path: '$.children[0]', slot: 'header', text: 'Plan' }]);
530
+ const card = renderer.elementAt('$.children[0]');
531
+ assertEqual(card.querySelector('cre8-button').getAttribute('slot'), 'footer');
532
+ assertEqual(card.querySelector('span[slot="header"]').textContent, 'Plan');
533
+ });
534
+
535
+ test('a data-model change re-resolves bound props on live elements', () => {
536
+ const { renderer, model } = newRendered();
537
+ rpatch(renderer, [
538
+ { op: 'append', path: '$', nodes: [{ component: 'cre8-button', props: { text: { $bind: '/label' } } }] },
539
+ ]);
540
+ renderer.apply({
541
+ v: 1,
542
+ type: 'surface.data',
543
+ surfaceId: 's1',
544
+ seq: model.seq + 1,
545
+ patches: [{ pointer: '/label', value: 'Checkout' }],
546
+ });
547
+ assertEqual(renderer.elementAt('$.children[0]').getAttribute('text'), 'Checkout');
548
+ });
549
+
550
+ test('an event reports the handler name and the node path as it stands now', () => {
551
+ const { renderer, events } = newRendered();
552
+ rpatch(renderer, [
553
+ {
554
+ op: 'append',
555
+ path: '$',
556
+ nodes: [{ component: 'cre8-button', props: { text: 'Go' }, events: { click: 'go-clicked' } }],
557
+ },
558
+ ]);
559
+ rpatch(renderer, [
560
+ { op: 'insert', path: '$', index: 0, nodes: [{ component: 'cre8-heading', children: ['title'] }] },
561
+ ]);
562
+ renderer.elementAt('$.children[1]').dispatchEvent(new dom.window.Event('click'));
563
+ assertEqual(events.length, 1);
564
+ assertEqual(events[0].handler, 'go-clicked');
565
+ assertEqual(events[0].path, '$.children[1]', 'the path must reflect the tree at fire time, not at bind time');
566
+ });
567
+
568
+ test('removing an event binding detaches the listener', () => {
569
+ const { renderer, events } = newRendered();
570
+ rpatch(renderer, [
571
+ {
572
+ op: 'append',
573
+ path: '$',
574
+ nodes: [{ component: 'cre8-button', props: { text: 'Go' }, events: { click: 'go-clicked' } }],
575
+ },
576
+ ]);
577
+ rpatch(renderer, [{ op: 'setEvents', path: '$.children[0]', events: { click: null } }]);
578
+ renderer.elementAt('$.children[0]').dispatchEvent(new dom.window.Event('click'));
579
+ assertEqual(events.length, 0);
580
+ });
581
+
582
+ test('deleting the surface empties the host element', () => {
583
+ const { host, renderer, model } = newRendered();
584
+ rpatch(renderer, [{ op: 'append', path: '$', nodes: [{ component: 'cre8-heading', children: ['x'] }] }]);
585
+ renderer.apply({ v: 1, type: 'surface.delete', surfaceId: 's1', seq: model.seq + 1 });
586
+ assertEqual(host.childNodes.length, 0);
587
+ });
588
+
589
+ // ─── report ─────────────────────────────────────────────────────────────────
590
+
591
+ console.log(`\n${passed} passed, ${failures.length} failed`);
592
+ if (failures.length) process.exit(1);