@tmorrow/cre8-wc 2.0.7 → 2.1.0

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 (333) hide show
  1. package/a2ui/catalog +5 -5
  2. package/a2ui/catalog-kg.json +156 -20
  3. package/a2ui/catalog.compact.json +2466 -0
  4. package/a2ui/catalog.json +1636 -139
  5. package/a2ui/catalog.svg +2445 -2445
  6. package/a2ui/check-layer-parity.mjs +277 -0
  7. package/a2ui/examples/compound-families.json +298 -0
  8. package/a2ui/examples/llm-observability.json +678 -650
  9. package/a2ui/generate-catalog.mjs +256 -5
  10. package/a2ui/inert-props.json +32 -0
  11. package/a2ui/registry.js +28 -0
  12. package/a2ui/registry.ts +31 -0
  13. package/a2ui/render-audit.mjs +294 -0
  14. package/a2ui/renderer.d.ts +2 -1
  15. package/a2ui/renderer.js +1 -1
  16. package/a2ui/renderer.ts +1 -1
  17. package/a2ui/smoke-test.mjs +39 -5
  18. package/a2ui/stream/diff-test.mjs +299 -0
  19. package/a2ui/stream/diff.d.ts +28 -0
  20. package/a2ui/stream/diff.js +130 -0
  21. package/a2ui/stream/diff.ts +145 -0
  22. package/a2ui/stream/index.d.ts +11 -0
  23. package/a2ui/stream/index.js +11 -0
  24. package/a2ui/stream/index.ts +43 -0
  25. package/a2ui/stream/model.d.ts +66 -0
  26. package/a2ui/stream/model.js +429 -0
  27. package/a2ui/stream/model.ts +472 -0
  28. package/a2ui/stream/pointer.d.ts +34 -0
  29. package/a2ui/stream/pointer.js +146 -0
  30. package/a2ui/stream/pointer.ts +147 -0
  31. package/a2ui/stream/renderer.d.ts +51 -0
  32. package/a2ui/stream/renderer.js +282 -0
  33. package/a2ui/stream/renderer.ts +291 -0
  34. package/a2ui/stream/stream-test.mjs +592 -0
  35. package/a2ui/stream/types.d.ts +173 -0
  36. package/a2ui/stream/types.js +17 -0
  37. package/a2ui/stream/types.ts +127 -0
  38. package/a2ui/types.d.ts +5 -0
  39. package/a2ui/types.ts +5 -0
  40. package/cdn/cre8-wc.esm.js +3527 -3226
  41. package/cdn/cre8-wc.esm.js.map +1 -1
  42. package/cdn/cre8-wc.min.js +289 -289
  43. package/cdn/cre8-wc.min.js.map +1 -1
  44. package/dist/assets/{Changelog-Documentation-BguNswiM.js → Changelog-Documentation-CeOetLxK.js} +1 -1
  45. package/dist/assets/{Code-Guidelines-BM3dZyzL.js → Code-Guidelines-BjdezAjj.js} +1 -1
  46. package/dist/assets/{Color-6AOWCN3U-C4AEGNk3.js → Color-6AOWCN3U-DPFGZitI.js} +1 -1
  47. package/dist/assets/{Contributing-Guidelines-DZURVehh.js → Contributing-Guidelines-B1M2V1hW.js} +1 -1
  48. package/dist/assets/{DocsRenderer-JROSPFPF-DeZmCI9u.js → DocsRenderer-JROSPFPF-BBy8KWNl.js} +1 -1
  49. package/dist/assets/{Tokens-Documentation-ZecGbtQg.js → Tokens-Documentation-CdTxFDLe.js} +1 -1
  50. package/dist/assets/{WithTooltip-7X4HQR7L-2Hfrkir-.js → WithTooltip-7X4HQR7L-DZzTNMQ7.js} +1 -1
  51. package/dist/assets/{accordion-item.stories-D-MjrX5G.js → accordion-item.stories-Bl26JlHp.js} +1 -9
  52. package/dist/assets/{accordion.stories-icgNa1dl.js → accordion.stories-DjoLApQw.js} +3 -11
  53. package/dist/assets/{alert.stories-BzDaXtj-.js → alert.stories-pO1y3YCQ.js} +15 -31
  54. package/dist/assets/{badge-C5wOWgyg.js → badge-BTxW3bN2.js} +3 -11
  55. package/dist/assets/{badge.stories-5GfB4jVI.js → badge.stories-vqh63RJ2.js} +1 -1
  56. package/dist/assets/{band.stories-DhXaP_kL.js → band.stories-CjKRppSb.js} +1 -9
  57. package/dist/assets/{blocks-DhUVut8Z.js → blocks-1be_-L6v.js} +5 -5
  58. package/dist/assets/{breadcrumbs.stories-CrPUBVk1.js → breadcrumbs.stories-Doyfy3V9.js} +2 -18
  59. package/dist/assets/{button-CJL8yzAT.js → button-CgaWukz0.js} +1 -9
  60. package/dist/assets/{button-group-BgwgtSFC.js → button-group-G7qN4MwM.js} +1 -9
  61. package/dist/assets/{button-group.stories-W-07gO63.js → button-group.stories-cL3Kay6M.js} +1 -1
  62. package/dist/assets/{button.stories-BPzJ-sCv.js → button.stories-C9HSpxWb.js} +6 -6
  63. package/dist/assets/{card.stories-mlFGY2Xn.js → card.stories-BbU8YymD.js} +3 -11
  64. package/dist/assets/{chart.stories-C8yuHHeK.js → chart.stories-BL4FxIA_.js} +1 -1
  65. package/dist/assets/{checkbox-field-item-iHcdmTNw.js → checkbox-field-item-DIMHcWM0.js} +1 -9
  66. package/dist/assets/{checkbox-field-item.stories-D1aVLDXn.js → checkbox-field-item.stories-XbB4x_lO.js} +1 -1
  67. package/dist/assets/{checkbox-field.stories-BoVB2jCx.js → checkbox-field.stories-YnqFUI4K.js} +2 -10
  68. package/dist/assets/{cre8-element-DfmMtHJO.js → cre8-element-B35i4Lyg.js} +1 -1
  69. package/dist/assets/{cre8-form-element-bQpK8UYN.js → cre8-form-element-Cfc5VeYV.js} +1 -1
  70. package/dist/assets/{danger-button.stories-Cb4ljnhQ.js → danger-button.stories-xh25hmfd.js} +1 -9
  71. package/dist/assets/{date-picker.stories-Cje3qk8D.js → date-picker.stories-DD_ctzEF.js} +1 -49
  72. package/dist/assets/{divider-B_PXBGWD.js → divider-xZ2KARHf.js} +2 -10
  73. package/dist/assets/{divider.stories-CtfeBqVV.js → divider.stories-Bk9oF-OV.js} +1 -1
  74. package/dist/assets/{dropdown-Cgwne34I.js → dropdown-BEP5x3CW.js} +6 -22
  75. package/dist/assets/{dropdown-item.stories-pXkDLRuv.js → dropdown-item.stories-Iaw_UrEP.js} +1 -1
  76. package/dist/assets/{dropdown.stories-DZnmHieY.js → dropdown.stories-B0OlKD7h.js} +1 -1
  77. package/dist/assets/{example-drug-card.stories-DTzLXTFx.js → example-drug-card.stories-Bp5JIeE_.js} +1 -1
  78. package/dist/assets/{f-po-C2WpbOFJ.js → f-po-BdPzDRD-.js} +1 -1
  79. package/dist/assets/{feature.stories-DffXdJch.js → feature.stories-DYE1Js70.js} +1 -9
  80. package/dist/assets/{field-DzYRqQb2.js → field-BpUYyRW1.js} +1 -9
  81. package/dist/assets/{field-note-DKXZiXLU.js → field-note-fccBckVl.js} +3 -11
  82. package/dist/assets/{field-note.stories-DS_0bQul.js → field-note.stories-BH9AZMbF.js} +1 -1
  83. package/dist/assets/{field.stories-BvXn0oG6.js → field.stories-BQPtmziK.js} +1 -1
  84. package/dist/assets/{footer.stories-C3ZibisU.js → footer.stories-DCWEciqD.js} +1 -9
  85. package/dist/assets/{form-example.stories-C-TuOeF2.js → form-example.stories-9IDC0AgX.js} +1 -1
  86. package/dist/assets/{formatter-L2YTDE3Q-CJVnxOUW.js → formatter-L2YTDE3Q-B9u1AOWn.js} +1 -1
  87. package/dist/assets/{global-nav-item-xQL7wVjY.js → global-nav-item-T4htN-yO.js} +6 -14
  88. package/dist/assets/{global-nav-item.stories-Dp9TE3aR.js → global-nav-item.stories-2dWRfCXv.js} +1 -1
  89. package/dist/assets/{global-nav.stories-B5Nk8JZ0.js → global-nav.stories-Fh1gScqg.js} +2 -10
  90. package/dist/assets/{grid-DYgnZBsd.js → grid-C5CKMXNe.js} +3 -19
  91. package/dist/assets/{grid.stories-BFiXTq2C.js → grid.stories-BLmCNdeA.js} +1 -1
  92. package/dist/assets/{header.stories-D1f7xhjV.js → header.stories-BkDrjJtj.js} +1 -9
  93. package/dist/assets/{heading-485Jz7JZ.js → heading-Dbka33DK.js} +2 -10
  94. package/dist/assets/{heading.stories-gPF_SmTc.js → heading.stories-Be40_bmG.js} +1 -1
  95. package/dist/assets/{hero.stories-BHlFNdvF.js → hero.stories-vRu7PZG9.js} +1 -9
  96. package/dist/assets/{icon-DeTLDzTS.js → icon-ChfRWXWy.js} +2 -10
  97. package/dist/assets/{icon-description-o-YaiEjK.js → icon-description-BBSwDykJ.js} +1 -1
  98. package/dist/assets/{icon-description.stories-BdPRvEwe.js → icon-description.stories-Cg6ekOme.js} +1 -1
  99. package/dist/assets/{icon.stories-BxWpqxT5.js → icon.stories-Cj4U7L5G.js} +1 -1
  100. package/dist/assets/if-defined-Bxnzk64Q.js +1 -0
  101. package/dist/assets/{iframe-dHvya7sk.js → iframe-BNJP6ovU.js} +35 -35
  102. package/dist/assets/{inline-alert.stories-FH0bQ0jh.js → inline-alert.stories-CygBYI02.js} +1 -9
  103. package/dist/assets/{layout-container.stories-D6Z6Kx78.js → layout-container.stories-TMyIJteJ.js} +2 -10
  104. package/dist/assets/{layout.stories-CNYuwC_r.js → layout.stories-ylRM-ANL.js} +7 -23
  105. package/dist/assets/{linelength-container.stories-pCRXZvxS.js → linelength-container.stories-Br2v8jXE.js} +1 -9
  106. package/dist/assets/{link-BpCiLxNW.js → link-CRE0Eqdo.js} +1 -9
  107. package/dist/assets/{link-list.stories-12vsAa1P.js → link-list.stories--y-urrVo.js} +7 -23
  108. package/dist/assets/{link.stories-DAoQtY4_.js → link.stories-C3WQOgkY.js} +2 -2
  109. package/dist/assets/{list.stories-D0N3yBIf.js → list.stories-Bd2bxkxn.js} +7 -23
  110. package/dist/assets/{loading-spinner-g6iJajXH.js → loading-spinner-C52KNq6y.js} +4 -12
  111. package/dist/assets/{loading-spinner.stories-B6_mgPxB.js → loading-spinner.stories-DPRGEeHT.js} +1 -1
  112. package/dist/assets/{logo.stories-BvMwN2qg.js → logo.stories-2LBqqraT.js} +1 -9
  113. package/dist/assets/{main.stories-pL4zefbs.js → main.stories-BQzD7iWD.js} +2 -10
  114. package/dist/assets/{medication-card.stories-DK1XGJic.js → medication-card.stories-Wc-hA0-s.js} +5 -5
  115. package/dist/assets/{medication-form.stories-Bj5dLp5S.js → medication-form.stories-DfFOCDaU.js} +1 -1
  116. package/dist/assets/{modal-CO0M4e_8.js → modal-CCfbY4fF.js} +1 -9
  117. package/dist/assets/{modal-BNu8xy08.js → modal-GglWRNJp.js} +1 -1
  118. package/dist/assets/{modal.stories-CHYBrq_E.js → modal.stories-CpybCs79.js} +1 -1
  119. package/dist/assets/{multi-select.stories-C9XXsWio.js → multi-select.stories-BhzEKRui.js} +1 -9
  120. package/dist/assets/{nav-container.stories-DU9fhvDu.js → nav-container.stories-Be1YlegP.js} +3 -11
  121. package/dist/assets/{page-header.stories-BrstKiEY.js → page-header.stories-BOVLQ0YF.js} +1 -9
  122. package/dist/assets/{pagination.stories-Bw2M2iOn.js → pagination.stories-D62sa_1K.js} +9 -25
  123. package/dist/assets/{percent-bar.stories-UQRXYUqd.js → percent-bar.stories-CYi4El7T.js} +1 -9
  124. package/dist/assets/{popover.stories-ZkwrRFpR.js → popover.stories-B9O8F4yv.js} +1 -9
  125. package/dist/assets/{primary-nav.stories-wqOKwgQn.js → primary-nav.stories-Bt-o7h45.js} +1 -17
  126. package/dist/assets/{progress-meter-TzASasu0.js → progress-meter-Dit59091.js} +1 -9
  127. package/dist/assets/{progress-meter.stories-qfJALZf8.js → progress-meter.stories-CYnOCmjo.js} +1 -1
  128. package/dist/assets/{progress-steps-item-KvW1d_7L.js → progress-steps-item-CWJsad0s.js} +1 -9
  129. package/dist/assets/{progress-steps-item.stories-uOn6tJ1W.js → progress-steps-item.stories-ePn_6bPn.js} +1 -1
  130. package/dist/assets/{progress-steps.stories-CKFPx8D0.js → progress-steps.stories-CblrEcXF.js} +1 -9
  131. package/dist/assets/{radio-field-item-CTaR50XR.js → radio-field-item-Bm-0RWPe.js} +5 -21
  132. package/dist/assets/{radio-field-item.stories-DQSLj8W1.js → radio-field-item.stories-y_TrTRIW.js} +1 -1
  133. package/dist/assets/{radio-field.stories-Btmc_Iep.js → radio-field.stories-CSWrbUrn.js} +1 -1
  134. package/dist/assets/{remove-tag-BSEZ7VT8.js → remove-tag-lCr57c5r.js} +1 -9
  135. package/dist/assets/{remove-tag.stories-diRKi7Ua.js → remove-tag.stories-CDfwCdvZ.js} +1 -1
  136. package/dist/assets/{request-prescription.stories-DQvra7ia.js → request-prescription.stories-O4zqokfb.js} +1 -1
  137. package/dist/assets/{sample-modal.stories-deE1EBfZ.js → sample-modal.stories-BZ4N2R2Z.js} +1 -1
  138. package/dist/assets/{section.stories-DF4mcy2O.js → section.stories-D_1InThX.js} +1 -9
  139. package/dist/assets/{select-tile-D-C8oSrm.js → select-tile-BqqB3ZtE.js} +2 -10
  140. package/dist/assets/{select-tile-list.stories-vsBQUIcl.js → select-tile-list.stories-Di4ILgV8.js} +1 -9
  141. package/dist/assets/{select-tile.stories-CqHzN5fc.js → select-tile.stories-D3h_6zlV.js} +6 -6
  142. package/dist/assets/{select.stories-8aOGxTtr.js → select.stories-BB2phP9A.js} +1 -9
  143. package/dist/assets/{skeleton-loader.stories-CwR2-aC5.js → skeleton-loader.stories-y4uml_kV.js} +5 -13
  144. package/dist/assets/{split-button.stories-CyfdrK4u.js → split-button.stories-C_82I4ME.js} +2 -10
  145. package/dist/assets/{spread-BPiNgQQF.js → spread-CnPic3C2.js} +1 -1
  146. package/dist/assets/state-DkQhleoO.js +1 -0
  147. package/dist/assets/{submenu.stories-CDhNzZzh.js → submenu.stories-UDtaREr1.js} +2 -18
  148. package/dist/assets/{syntaxhighlighter-T5IQBZDB-BzI6WxUR.js → syntaxhighlighter-T5IQBZDB-D-I_s_yD.js} +1 -1
  149. package/dist/assets/{table-header-CfcchoYz.js → table-header-2837i43H.js} +3 -51
  150. package/dist/assets/{table-object.stories-Bd5qdfMU.js → table-object.stories-D4KoI-wA.js} +1 -9
  151. package/dist/assets/{table.stories-DxuBe9dJ.js → table.stories-DUJDibEp.js} +1 -1
  152. package/dist/assets/{tabs.stories-Cpc3OHJx.js → tabs.stories-C06SngOF.js} +5 -29
  153. package/dist/assets/{tag-list.stories-BmyCmrTF.js → tag-list.stories-Du-IoYzu.js} +2 -10
  154. package/dist/assets/{tag-vQFuZneb.js → tag-oHGdclJl.js} +1 -9
  155. package/dist/assets/{tag.stories-DGzrNUbb.js → tag.stories-C5D5IbuK.js} +1 -1
  156. package/dist/assets/{tertiary-nav.stories-D4q5K-am.js → tertiary-nav.stories-B1GU8Zi4.js} +3 -19
  157. package/dist/assets/{text-link-4qLRIeJB.js → text-link-B3m3KEqY.js} +1 -9
  158. package/dist/assets/{text-link.stories-D6BRm2I5.js → text-link.stories-4O6qn82H.js} +1 -1
  159. package/dist/assets/{text-passage-DGaI5i2I.js → text-passage-xQVosjOr.js} +3 -19
  160. package/dist/assets/{text-passage.stories-DkmXVUhH.js → text-passage.stories-Da1nspwG.js} +1 -1
  161. package/dist/assets/{tooltip.stories-B7ADDq7o.js → tooltip.stories-DLKyyeKn.js} +6 -14
  162. package/dist/assets/{utility-nav.stories-ffXVvyLp.js → utility-nav.stories-B6xBFe7z.js} +5 -21
  163. package/dist/design-tokens/brands/cre8/css/tokens_brand.css +1 -1
  164. package/dist/design-tokens/core/scss/theming/component.css +1 -1
  165. package/dist/design-tokens/core/scss/theming/variables.css +1 -7
  166. package/dist/design-tokens/core/scss/theming/variables.scss +0 -11
  167. package/dist/icons/cre8-icons.svg +1 -1
  168. package/dist/iframe.html +1 -24
  169. package/dist/index.html +25 -77
  170. package/dist/project.json +1 -1
  171. package/lib/a2ui/registry.d.ts.map +1 -1
  172. package/lib/a2ui/registry.js +28 -0
  173. package/lib/a2ui/registry.js.map +1 -1
  174. package/lib/a2ui/renderer.d.ts +2 -1
  175. package/lib/a2ui/renderer.d.ts.map +1 -1
  176. package/lib/a2ui/renderer.js +1 -1
  177. package/lib/a2ui/renderer.js.map +1 -1
  178. package/lib/a2ui/stream/diff.d.ts +29 -0
  179. package/lib/a2ui/stream/diff.d.ts.map +1 -0
  180. package/lib/a2ui/stream/diff.js +131 -0
  181. package/lib/a2ui/stream/diff.js.map +1 -0
  182. package/lib/a2ui/stream/index.d.ts +12 -0
  183. package/lib/a2ui/stream/index.d.ts.map +1 -0
  184. package/lib/a2ui/stream/index.js +12 -0
  185. package/lib/a2ui/stream/index.js.map +1 -0
  186. package/lib/a2ui/stream/model.d.ts +67 -0
  187. package/lib/a2ui/stream/model.d.ts.map +1 -0
  188. package/lib/a2ui/stream/model.js +423 -0
  189. package/lib/a2ui/stream/model.js.map +1 -0
  190. package/lib/a2ui/stream/pointer.d.ts +35 -0
  191. package/lib/a2ui/stream/pointer.d.ts.map +1 -0
  192. package/lib/a2ui/stream/pointer.js +147 -0
  193. package/lib/a2ui/stream/pointer.js.map +1 -0
  194. package/lib/a2ui/stream/renderer.d.ts +52 -0
  195. package/lib/a2ui/stream/renderer.d.ts.map +1 -0
  196. package/lib/a2ui/stream/renderer.js +279 -0
  197. package/lib/a2ui/stream/renderer.js.map +1 -0
  198. package/lib/a2ui/stream/types.d.ts +174 -0
  199. package/lib/a2ui/stream/types.d.ts.map +1 -0
  200. package/lib/a2ui/stream/types.js +18 -0
  201. package/lib/a2ui/stream/types.js.map +1 -0
  202. package/lib/a2ui/types.d.ts +5 -0
  203. package/lib/a2ui/types.d.ts.map +1 -1
  204. package/lib/a2ui/types.js.map +1 -1
  205. package/lib/components/accordion/accordion.d.ts +15 -0
  206. package/lib/components/accordion/accordion.d.ts.map +1 -1
  207. package/lib/components/accordion/accordion.js +23 -0
  208. package/lib/components/accordion/accordion.js.map +1 -1
  209. package/lib/components/badge/badge.d.ts +4 -17
  210. package/lib/components/badge/badge.d.ts.map +1 -1
  211. package/lib/components/badge/badge.js +0 -15
  212. package/lib/components/badge/badge.js.map +1 -1
  213. package/lib/components/breadcrumbs/breadcrumbs.d.ts +14 -0
  214. package/lib/components/breadcrumbs/breadcrumbs.d.ts.map +1 -1
  215. package/lib/components/breadcrumbs/breadcrumbs.js +21 -0
  216. package/lib/components/breadcrumbs/breadcrumbs.js.map +1 -1
  217. package/lib/components/chart/chart.d.ts +3 -3
  218. package/lib/components/chart/chart.js +6 -6
  219. package/lib/components/chart/chart.js.map +1 -1
  220. package/lib/components/checkbox-field/checkbox-field.d.ts +19 -0
  221. package/lib/components/checkbox-field/checkbox-field.d.ts.map +1 -1
  222. package/lib/components/checkbox-field/checkbox-field.js +28 -0
  223. package/lib/components/checkbox-field/checkbox-field.js.map +1 -1
  224. package/lib/components/cre8-element.d.ts +19 -0
  225. package/lib/components/cre8-element.d.ts.map +1 -1
  226. package/lib/components/cre8-element.js +51 -2
  227. package/lib/components/cre8-element.js.map +1 -1
  228. package/lib/components/divider/divider.d.ts +1 -1
  229. package/lib/components/divider/divider.d.ts.map +1 -1
  230. package/lib/components/divider/divider.js.map +1 -1
  231. package/lib/components/dropdown/dropdown.d.ts +13 -0
  232. package/lib/components/dropdown/dropdown.d.ts.map +1 -1
  233. package/lib/components/dropdown/dropdown.js +19 -0
  234. package/lib/components/dropdown/dropdown.js.map +1 -1
  235. package/lib/components/dropdown-item/dropdown-item.d.ts +1 -1
  236. package/lib/components/dropdown-item/dropdown-item.js +2 -2
  237. package/lib/components/dropdown-item/dropdown-item.js.map +1 -1
  238. package/lib/components/heading/heading.d.ts +3 -5
  239. package/lib/components/heading/heading.d.ts.map +1 -1
  240. package/lib/components/heading/heading.js +2 -4
  241. package/lib/components/heading/heading.js.map +1 -1
  242. package/lib/components/link-list/link-list.d.ts +19 -0
  243. package/lib/components/link-list/link-list.d.ts.map +1 -1
  244. package/lib/components/link-list/link-list.js +19 -0
  245. package/lib/components/link-list/link-list.js.map +1 -1
  246. package/lib/components/list/list.d.ts +13 -0
  247. package/lib/components/list/list.d.ts.map +1 -1
  248. package/lib/components/list/list.js +15 -0
  249. package/lib/components/list/list.js.map +1 -1
  250. package/lib/components/modal/modal.d.ts +2 -2
  251. package/lib/components/modal/modal.js +2 -2
  252. package/lib/components/modal/modal.js.map +1 -1
  253. package/lib/components/multi-select/multi-select.d.ts +2 -2
  254. package/lib/components/multi-select/multi-select.js +5 -5
  255. package/lib/components/multi-select/multi-select.js.map +1 -1
  256. package/lib/components/pagination/pagination.d.ts +1 -1
  257. package/lib/components/pagination/pagination.js +2 -2
  258. package/lib/components/pagination/pagination.js.map +1 -1
  259. package/lib/components/percent-bar/percent-bar.d.ts +2 -0
  260. package/lib/components/percent-bar/percent-bar.d.ts.map +1 -1
  261. package/lib/components/percent-bar/percent-bar.js +3 -1
  262. package/lib/components/percent-bar/percent-bar.js.map +1 -1
  263. package/lib/components/popover/popover.d.ts +6 -7
  264. package/lib/components/popover/popover.d.ts.map +1 -1
  265. package/lib/components/popover/popover.js +4 -4
  266. package/lib/components/popover/popover.js.map +1 -1
  267. package/lib/components/progress-meter/progress-meter.d.ts +0 -8
  268. package/lib/components/progress-meter/progress-meter.d.ts.map +1 -1
  269. package/lib/components/progress-meter/progress-meter.js +0 -7
  270. package/lib/components/progress-meter/progress-meter.js.map +1 -1
  271. package/lib/components/progress-steps/progress-steps.d.ts +16 -0
  272. package/lib/components/progress-steps/progress-steps.d.ts.map +1 -1
  273. package/lib/components/progress-steps/progress-steps.js +25 -0
  274. package/lib/components/progress-steps/progress-steps.js.map +1 -1
  275. package/lib/components/progress-steps-item/progress-steps-item.d.ts +1 -3
  276. package/lib/components/progress-steps-item/progress-steps-item.d.ts.map +1 -1
  277. package/lib/components/progress-steps-item/progress-steps-item.js +0 -1
  278. package/lib/components/progress-steps-item/progress-steps-item.js.map +1 -1
  279. package/lib/components/radio-field/radio-field.d.ts +33 -0
  280. package/lib/components/radio-field/radio-field.d.ts.map +1 -1
  281. package/lib/components/radio-field/radio-field.js +62 -0
  282. package/lib/components/radio-field/radio-field.js.map +1 -1
  283. package/lib/components/remove-tag/remove-tag.d.ts +1 -1
  284. package/lib/components/remove-tag/remove-tag.js +3 -3
  285. package/lib/components/remove-tag/remove-tag.js.map +1 -1
  286. package/lib/components/split-button/split-button.d.ts +3 -0
  287. package/lib/components/split-button/split-button.d.ts.map +1 -1
  288. package/lib/components/split-button/split-button.js +5 -2
  289. package/lib/components/split-button/split-button.js.map +1 -1
  290. package/lib/components/tab/tab.d.ts +1 -1
  291. package/lib/components/tab/tab.js +3 -3
  292. package/lib/components/tab/tab.js.map +1 -1
  293. package/lib/components/table/table.d.ts +36 -0
  294. package/lib/components/table/table.d.ts.map +1 -1
  295. package/lib/components/table/table.js +55 -0
  296. package/lib/components/table/table.js.map +1 -1
  297. package/lib/components/tabs/tabs.d.ts +20 -3
  298. package/lib/components/tabs/tabs.d.ts.map +1 -1
  299. package/lib/components/tabs/tabs.js +33 -5
  300. package/lib/components/tabs/tabs.js.map +1 -1
  301. package/lib/components/tag-list/tag-list.d.ts +17 -0
  302. package/lib/components/tag-list/tag-list.d.ts.map +1 -1
  303. package/lib/components/tag-list/tag-list.js +25 -0
  304. package/lib/components/tag-list/tag-list.js.map +1 -1
  305. package/lib/components/tooltip/tooltip.d.ts +2 -2
  306. package/lib/components/tooltip/tooltip.js +4 -4
  307. package/lib/components/tooltip/tooltip.js.map +1 -1
  308. package/lib/components/utils/light-children.d.ts +40 -0
  309. package/lib/components/utils/light-children.d.ts.map +1 -0
  310. package/lib/components/utils/light-children.js +100 -0
  311. package/lib/components/utils/light-children.js.map +1 -0
  312. package/lib/directives/if-truthy.d.ts +1 -1
  313. package/lib/light-children-TlEXev5S.js +39 -0
  314. package/lib/scripts/generate-mcp-manifest.js +38 -0
  315. package/lib/scripts/generate-mcp-manifest.js.map +1 -1
  316. package/lib/scripts/generate-react-wrappers.d.ts +8 -1
  317. package/lib/scripts/generate-react-wrappers.d.ts.map +1 -1
  318. package/lib/scripts/generate-react-wrappers.js +45 -1
  319. package/lib/scripts/generate-react-wrappers.js.map +1 -1
  320. package/mcp-manifest.json +169 -52
  321. package/package.json +14 -5
  322. package/react-manifest.json +153 -47
  323. package/dist/assets/if-defined-HKKhGupN.js +0 -1
  324. package/dist/assets/iframe-CWwoGI65.css +0 -1
  325. package/dist/assets/state-CfuknrX8.js +0 -1
  326. package/dist/fonts/PlusJakartaSans-Bold.woff2 +0 -0
  327. package/dist/fonts/PlusJakartaSans-Medium.woff2 +0 -0
  328. package/dist/fonts/PlusJakartaSans-Regular.woff2 +0 -0
  329. package/dist/fonts/PlusJakartaSans-SemiBold.woff2 +0 -0
  330. package/dist/logo.svg +0 -1
  331. package/dist/sb-addons/storybook-4/manager-bundle.js +0 -8
  332. /package/dist/icons/{express-scripts copy.svg → pharmacy copy.svg} +0 -0
  333. /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);