@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.
- package/a2ui/catalog +5 -5
- package/a2ui/catalog-kg.json +156 -20
- package/a2ui/catalog.json +393 -34
- package/a2ui/catalog.svg +2445 -2445
- package/a2ui/examples/compound-families.json +298 -0
- package/a2ui/examples/llm-observability.json +678 -650
- package/a2ui/generate-catalog.mjs +116 -3
- package/a2ui/inert-props.json +32 -0
- package/a2ui/render-audit.mjs +294 -0
- package/a2ui/renderer.d.ts +2 -1
- package/a2ui/renderer.js +1 -1
- package/a2ui/renderer.ts +1 -1
- package/a2ui/smoke-test.mjs +3 -3
- package/a2ui/stream/diff-test.mjs +299 -0
- package/a2ui/stream/diff.d.ts +28 -0
- package/a2ui/stream/diff.js +130 -0
- package/a2ui/stream/diff.ts +145 -0
- package/a2ui/stream/index.d.ts +11 -0
- package/a2ui/stream/index.js +11 -0
- package/a2ui/stream/index.ts +43 -0
- package/a2ui/stream/model.d.ts +66 -0
- package/a2ui/stream/model.js +429 -0
- package/a2ui/stream/model.ts +472 -0
- package/a2ui/stream/pointer.d.ts +34 -0
- package/a2ui/stream/pointer.js +146 -0
- package/a2ui/stream/pointer.ts +147 -0
- package/a2ui/stream/renderer.d.ts +51 -0
- package/a2ui/stream/renderer.js +282 -0
- package/a2ui/stream/renderer.ts +291 -0
- package/a2ui/stream/stream-test.mjs +592 -0
- package/a2ui/stream/types.d.ts +173 -0
- package/a2ui/stream/types.js +17 -0
- package/a2ui/stream/types.ts +127 -0
- package/cdn/cre8-wc.esm.js +3527 -3226
- package/cdn/cre8-wc.esm.js.map +1 -1
- package/cdn/cre8-wc.min.js +289 -289
- package/cdn/cre8-wc.min.js.map +1 -1
- package/dist/assets/{Changelog-Documentation-BguNswiM.js → Changelog-Documentation-CeOetLxK.js} +1 -1
- package/dist/assets/{Code-Guidelines-BM3dZyzL.js → Code-Guidelines-BjdezAjj.js} +1 -1
- package/dist/assets/{Color-6AOWCN3U-C4AEGNk3.js → Color-6AOWCN3U-DPFGZitI.js} +1 -1
- package/dist/assets/{Contributing-Guidelines-DZURVehh.js → Contributing-Guidelines-B1M2V1hW.js} +1 -1
- package/dist/assets/{DocsRenderer-JROSPFPF-DeZmCI9u.js → DocsRenderer-JROSPFPF-BBy8KWNl.js} +1 -1
- package/dist/assets/{Tokens-Documentation-ZecGbtQg.js → Tokens-Documentation-CdTxFDLe.js} +1 -1
- package/dist/assets/{WithTooltip-7X4HQR7L-2Hfrkir-.js → WithTooltip-7X4HQR7L-DZzTNMQ7.js} +1 -1
- package/dist/assets/{accordion-item.stories-D-MjrX5G.js → accordion-item.stories-Bl26JlHp.js} +1 -9
- package/dist/assets/{accordion.stories-icgNa1dl.js → accordion.stories-DjoLApQw.js} +3 -11
- package/dist/assets/{alert.stories-BzDaXtj-.js → alert.stories-pO1y3YCQ.js} +15 -31
- package/dist/assets/{badge-C5wOWgyg.js → badge-BTxW3bN2.js} +3 -11
- package/dist/assets/{badge.stories-5GfB4jVI.js → badge.stories-vqh63RJ2.js} +1 -1
- package/dist/assets/{band.stories-DhXaP_kL.js → band.stories-CjKRppSb.js} +1 -9
- package/dist/assets/{blocks-DhUVut8Z.js → blocks-1be_-L6v.js} +5 -5
- package/dist/assets/{breadcrumbs.stories-CrPUBVk1.js → breadcrumbs.stories-Doyfy3V9.js} +2 -18
- package/dist/assets/{button-CJL8yzAT.js → button-CgaWukz0.js} +1 -9
- package/dist/assets/{button-group-BgwgtSFC.js → button-group-G7qN4MwM.js} +1 -9
- package/dist/assets/{button-group.stories-W-07gO63.js → button-group.stories-cL3Kay6M.js} +1 -1
- package/dist/assets/{button.stories-BPzJ-sCv.js → button.stories-C9HSpxWb.js} +6 -6
- package/dist/assets/{card.stories-mlFGY2Xn.js → card.stories-BbU8YymD.js} +3 -11
- package/dist/assets/{chart.stories-C8yuHHeK.js → chart.stories-BL4FxIA_.js} +1 -1
- package/dist/assets/{checkbox-field-item-iHcdmTNw.js → checkbox-field-item-DIMHcWM0.js} +1 -9
- package/dist/assets/{checkbox-field-item.stories-D1aVLDXn.js → checkbox-field-item.stories-XbB4x_lO.js} +1 -1
- package/dist/assets/{checkbox-field.stories-BoVB2jCx.js → checkbox-field.stories-YnqFUI4K.js} +2 -10
- package/dist/assets/{cre8-element-DfmMtHJO.js → cre8-element-B35i4Lyg.js} +1 -1
- package/dist/assets/{cre8-form-element-bQpK8UYN.js → cre8-form-element-Cfc5VeYV.js} +1 -1
- package/dist/assets/{danger-button.stories-Cb4ljnhQ.js → danger-button.stories-xh25hmfd.js} +1 -9
- package/dist/assets/{date-picker.stories-Cje3qk8D.js → date-picker.stories-DD_ctzEF.js} +1 -49
- package/dist/assets/{divider-B_PXBGWD.js → divider-xZ2KARHf.js} +2 -10
- package/dist/assets/{divider.stories-CtfeBqVV.js → divider.stories-Bk9oF-OV.js} +1 -1
- package/dist/assets/{dropdown-Cgwne34I.js → dropdown-BEP5x3CW.js} +6 -22
- package/dist/assets/{dropdown-item.stories-pXkDLRuv.js → dropdown-item.stories-Iaw_UrEP.js} +1 -1
- package/dist/assets/{dropdown.stories-DZnmHieY.js → dropdown.stories-B0OlKD7h.js} +1 -1
- package/dist/assets/{example-drug-card.stories-DTzLXTFx.js → example-drug-card.stories-Bp5JIeE_.js} +1 -1
- package/dist/assets/{f-po-C2WpbOFJ.js → f-po-BdPzDRD-.js} +1 -1
- package/dist/assets/{feature.stories-DffXdJch.js → feature.stories-DYE1Js70.js} +1 -9
- package/dist/assets/{field-DzYRqQb2.js → field-BpUYyRW1.js} +1 -9
- package/dist/assets/{field-note-DKXZiXLU.js → field-note-fccBckVl.js} +3 -11
- package/dist/assets/{field-note.stories-DS_0bQul.js → field-note.stories-BH9AZMbF.js} +1 -1
- package/dist/assets/{field.stories-BvXn0oG6.js → field.stories-BQPtmziK.js} +1 -1
- package/dist/assets/{footer.stories-C3ZibisU.js → footer.stories-DCWEciqD.js} +1 -9
- package/dist/assets/{form-example.stories-C-TuOeF2.js → form-example.stories-9IDC0AgX.js} +1 -1
- package/dist/assets/{formatter-L2YTDE3Q-CJVnxOUW.js → formatter-L2YTDE3Q-B9u1AOWn.js} +1 -1
- package/dist/assets/{global-nav-item-xQL7wVjY.js → global-nav-item-T4htN-yO.js} +6 -14
- package/dist/assets/{global-nav-item.stories-Dp9TE3aR.js → global-nav-item.stories-2dWRfCXv.js} +1 -1
- package/dist/assets/{global-nav.stories-B5Nk8JZ0.js → global-nav.stories-Fh1gScqg.js} +2 -10
- package/dist/assets/{grid-DYgnZBsd.js → grid-C5CKMXNe.js} +3 -19
- package/dist/assets/{grid.stories-BFiXTq2C.js → grid.stories-BLmCNdeA.js} +1 -1
- package/dist/assets/{header.stories-D1f7xhjV.js → header.stories-BkDrjJtj.js} +1 -9
- package/dist/assets/{heading-485Jz7JZ.js → heading-Dbka33DK.js} +2 -10
- package/dist/assets/{heading.stories-gPF_SmTc.js → heading.stories-Be40_bmG.js} +1 -1
- package/dist/assets/{hero.stories-BHlFNdvF.js → hero.stories-vRu7PZG9.js} +1 -9
- package/dist/assets/{icon-DeTLDzTS.js → icon-ChfRWXWy.js} +2 -10
- package/dist/assets/{icon-description-o-YaiEjK.js → icon-description-BBSwDykJ.js} +1 -1
- package/dist/assets/{icon-description.stories-BdPRvEwe.js → icon-description.stories-Cg6ekOme.js} +1 -1
- package/dist/assets/{icon.stories-BxWpqxT5.js → icon.stories-Cj4U7L5G.js} +1 -1
- package/dist/assets/if-defined-Bxnzk64Q.js +1 -0
- package/dist/assets/{iframe-dHvya7sk.js → iframe-BNJP6ovU.js} +35 -35
- package/dist/assets/{inline-alert.stories-FH0bQ0jh.js → inline-alert.stories-CygBYI02.js} +1 -9
- package/dist/assets/{layout-container.stories-D6Z6Kx78.js → layout-container.stories-TMyIJteJ.js} +2 -10
- package/dist/assets/{layout.stories-CNYuwC_r.js → layout.stories-ylRM-ANL.js} +7 -23
- package/dist/assets/{linelength-container.stories-pCRXZvxS.js → linelength-container.stories-Br2v8jXE.js} +1 -9
- package/dist/assets/{link-BpCiLxNW.js → link-CRE0Eqdo.js} +1 -9
- package/dist/assets/{link-list.stories-12vsAa1P.js → link-list.stories--y-urrVo.js} +7 -23
- package/dist/assets/{link.stories-DAoQtY4_.js → link.stories-C3WQOgkY.js} +2 -2
- package/dist/assets/{list.stories-D0N3yBIf.js → list.stories-Bd2bxkxn.js} +7 -23
- package/dist/assets/{loading-spinner-g6iJajXH.js → loading-spinner-C52KNq6y.js} +4 -12
- package/dist/assets/{loading-spinner.stories-B6_mgPxB.js → loading-spinner.stories-DPRGEeHT.js} +1 -1
- package/dist/assets/{logo.stories-BvMwN2qg.js → logo.stories-2LBqqraT.js} +1 -9
- package/dist/assets/{main.stories-pL4zefbs.js → main.stories-BQzD7iWD.js} +2 -10
- package/dist/assets/{medication-card.stories-DK1XGJic.js → medication-card.stories-Wc-hA0-s.js} +5 -5
- package/dist/assets/{medication-form.stories-Bj5dLp5S.js → medication-form.stories-DfFOCDaU.js} +1 -1
- package/dist/assets/{modal-CO0M4e_8.js → modal-CCfbY4fF.js} +1 -9
- package/dist/assets/{modal-BNu8xy08.js → modal-GglWRNJp.js} +1 -1
- package/dist/assets/{modal.stories-CHYBrq_E.js → modal.stories-CpybCs79.js} +1 -1
- package/dist/assets/{multi-select.stories-C9XXsWio.js → multi-select.stories-BhzEKRui.js} +1 -9
- package/dist/assets/{nav-container.stories-DU9fhvDu.js → nav-container.stories-Be1YlegP.js} +3 -11
- package/dist/assets/{page-header.stories-BrstKiEY.js → page-header.stories-BOVLQ0YF.js} +1 -9
- package/dist/assets/{pagination.stories-Bw2M2iOn.js → pagination.stories-D62sa_1K.js} +9 -25
- package/dist/assets/{percent-bar.stories-UQRXYUqd.js → percent-bar.stories-CYi4El7T.js} +1 -9
- package/dist/assets/{popover.stories-ZkwrRFpR.js → popover.stories-B9O8F4yv.js} +1 -9
- package/dist/assets/{primary-nav.stories-wqOKwgQn.js → primary-nav.stories-Bt-o7h45.js} +1 -17
- package/dist/assets/{progress-meter-TzASasu0.js → progress-meter-Dit59091.js} +1 -9
- package/dist/assets/{progress-meter.stories-qfJALZf8.js → progress-meter.stories-CYnOCmjo.js} +1 -1
- package/dist/assets/{progress-steps-item-KvW1d_7L.js → progress-steps-item-CWJsad0s.js} +1 -9
- package/dist/assets/{progress-steps-item.stories-uOn6tJ1W.js → progress-steps-item.stories-ePn_6bPn.js} +1 -1
- package/dist/assets/{progress-steps.stories-CKFPx8D0.js → progress-steps.stories-CblrEcXF.js} +1 -9
- package/dist/assets/{radio-field-item-CTaR50XR.js → radio-field-item-Bm-0RWPe.js} +5 -21
- package/dist/assets/{radio-field-item.stories-DQSLj8W1.js → radio-field-item.stories-y_TrTRIW.js} +1 -1
- package/dist/assets/{radio-field.stories-Btmc_Iep.js → radio-field.stories-CSWrbUrn.js} +1 -1
- package/dist/assets/{remove-tag-BSEZ7VT8.js → remove-tag-lCr57c5r.js} +1 -9
- package/dist/assets/{remove-tag.stories-diRKi7Ua.js → remove-tag.stories-CDfwCdvZ.js} +1 -1
- package/dist/assets/{request-prescription.stories-DQvra7ia.js → request-prescription.stories-O4zqokfb.js} +1 -1
- package/dist/assets/{sample-modal.stories-deE1EBfZ.js → sample-modal.stories-BZ4N2R2Z.js} +1 -1
- package/dist/assets/{section.stories-DF4mcy2O.js → section.stories-D_1InThX.js} +1 -9
- package/dist/assets/{select-tile-D-C8oSrm.js → select-tile-BqqB3ZtE.js} +2 -10
- package/dist/assets/{select-tile-list.stories-vsBQUIcl.js → select-tile-list.stories-Di4ILgV8.js} +1 -9
- package/dist/assets/{select-tile.stories-CqHzN5fc.js → select-tile.stories-D3h_6zlV.js} +6 -6
- package/dist/assets/{select.stories-8aOGxTtr.js → select.stories-BB2phP9A.js} +1 -9
- package/dist/assets/{skeleton-loader.stories-CwR2-aC5.js → skeleton-loader.stories-y4uml_kV.js} +5 -13
- package/dist/assets/{split-button.stories-CyfdrK4u.js → split-button.stories-C_82I4ME.js} +2 -10
- package/dist/assets/{spread-BPiNgQQF.js → spread-CnPic3C2.js} +1 -1
- package/dist/assets/state-DkQhleoO.js +1 -0
- package/dist/assets/{submenu.stories-CDhNzZzh.js → submenu.stories-UDtaREr1.js} +2 -18
- package/dist/assets/{syntaxhighlighter-T5IQBZDB-BzI6WxUR.js → syntaxhighlighter-T5IQBZDB-D-I_s_yD.js} +1 -1
- package/dist/assets/{table-header-CfcchoYz.js → table-header-2837i43H.js} +3 -51
- package/dist/assets/{table-object.stories-Bd5qdfMU.js → table-object.stories-D4KoI-wA.js} +1 -9
- package/dist/assets/{table.stories-DxuBe9dJ.js → table.stories-DUJDibEp.js} +1 -1
- package/dist/assets/{tabs.stories-Cpc3OHJx.js → tabs.stories-C06SngOF.js} +5 -29
- package/dist/assets/{tag-list.stories-BmyCmrTF.js → tag-list.stories-Du-IoYzu.js} +2 -10
- package/dist/assets/{tag-vQFuZneb.js → tag-oHGdclJl.js} +1 -9
- package/dist/assets/{tag.stories-DGzrNUbb.js → tag.stories-C5D5IbuK.js} +1 -1
- package/dist/assets/{tertiary-nav.stories-D4q5K-am.js → tertiary-nav.stories-B1GU8Zi4.js} +3 -19
- package/dist/assets/{text-link-4qLRIeJB.js → text-link-B3m3KEqY.js} +1 -9
- package/dist/assets/{text-link.stories-D6BRm2I5.js → text-link.stories-4O6qn82H.js} +1 -1
- package/dist/assets/{text-passage-DGaI5i2I.js → text-passage-xQVosjOr.js} +3 -19
- package/dist/assets/{text-passage.stories-DkmXVUhH.js → text-passage.stories-Da1nspwG.js} +1 -1
- package/dist/assets/{tooltip.stories-B7ADDq7o.js → tooltip.stories-DLKyyeKn.js} +6 -14
- package/dist/assets/{utility-nav.stories-ffXVvyLp.js → utility-nav.stories-B6xBFe7z.js} +5 -21
- package/dist/design-tokens/brands/cre8/css/tokens_brand.css +1 -1
- package/dist/design-tokens/core/scss/theming/component.css +1 -1
- package/dist/design-tokens/core/scss/theming/variables.css +1 -7
- package/dist/design-tokens/core/scss/theming/variables.scss +0 -11
- package/dist/icons/cre8-icons.svg +1 -1
- package/dist/iframe.html +1 -24
- package/dist/index.html +25 -77
- package/dist/project.json +1 -1
- package/lib/a2ui/renderer.d.ts +2 -1
- package/lib/a2ui/renderer.d.ts.map +1 -1
- package/lib/a2ui/renderer.js +1 -1
- package/lib/a2ui/renderer.js.map +1 -1
- package/lib/a2ui/stream/diff.d.ts +29 -0
- package/lib/a2ui/stream/diff.d.ts.map +1 -0
- package/lib/a2ui/stream/diff.js +131 -0
- package/lib/a2ui/stream/diff.js.map +1 -0
- package/lib/a2ui/stream/index.d.ts +12 -0
- package/lib/a2ui/stream/index.d.ts.map +1 -0
- package/lib/a2ui/stream/index.js +12 -0
- package/lib/a2ui/stream/index.js.map +1 -0
- package/lib/a2ui/stream/model.d.ts +67 -0
- package/lib/a2ui/stream/model.d.ts.map +1 -0
- package/lib/a2ui/stream/model.js +423 -0
- package/lib/a2ui/stream/model.js.map +1 -0
- package/lib/a2ui/stream/pointer.d.ts +35 -0
- package/lib/a2ui/stream/pointer.d.ts.map +1 -0
- package/lib/a2ui/stream/pointer.js +147 -0
- package/lib/a2ui/stream/pointer.js.map +1 -0
- package/lib/a2ui/stream/renderer.d.ts +52 -0
- package/lib/a2ui/stream/renderer.d.ts.map +1 -0
- package/lib/a2ui/stream/renderer.js +279 -0
- package/lib/a2ui/stream/renderer.js.map +1 -0
- package/lib/a2ui/stream/types.d.ts +174 -0
- package/lib/a2ui/stream/types.d.ts.map +1 -0
- package/lib/a2ui/stream/types.js +18 -0
- package/lib/a2ui/stream/types.js.map +1 -0
- package/lib/components/accordion/accordion.d.ts +15 -0
- package/lib/components/accordion/accordion.d.ts.map +1 -1
- package/lib/components/accordion/accordion.js +23 -0
- package/lib/components/accordion/accordion.js.map +1 -1
- package/lib/components/badge/badge.d.ts +4 -17
- package/lib/components/badge/badge.d.ts.map +1 -1
- package/lib/components/badge/badge.js +0 -15
- package/lib/components/badge/badge.js.map +1 -1
- package/lib/components/breadcrumbs/breadcrumbs.d.ts +14 -0
- package/lib/components/breadcrumbs/breadcrumbs.d.ts.map +1 -1
- package/lib/components/breadcrumbs/breadcrumbs.js +21 -0
- package/lib/components/breadcrumbs/breadcrumbs.js.map +1 -1
- package/lib/components/chart/chart.d.ts +3 -3
- package/lib/components/chart/chart.js +6 -6
- package/lib/components/chart/chart.js.map +1 -1
- package/lib/components/checkbox-field/checkbox-field.d.ts +19 -0
- package/lib/components/checkbox-field/checkbox-field.d.ts.map +1 -1
- package/lib/components/checkbox-field/checkbox-field.js +28 -0
- package/lib/components/checkbox-field/checkbox-field.js.map +1 -1
- package/lib/components/cre8-element.d.ts +19 -0
- package/lib/components/cre8-element.d.ts.map +1 -1
- package/lib/components/cre8-element.js +51 -2
- package/lib/components/cre8-element.js.map +1 -1
- package/lib/components/divider/divider.d.ts +1 -1
- package/lib/components/divider/divider.d.ts.map +1 -1
- package/lib/components/divider/divider.js.map +1 -1
- package/lib/components/dropdown/dropdown.d.ts +13 -0
- package/lib/components/dropdown/dropdown.d.ts.map +1 -1
- package/lib/components/dropdown/dropdown.js +19 -0
- package/lib/components/dropdown/dropdown.js.map +1 -1
- package/lib/components/dropdown-item/dropdown-item.d.ts +1 -1
- package/lib/components/dropdown-item/dropdown-item.js +2 -2
- package/lib/components/dropdown-item/dropdown-item.js.map +1 -1
- package/lib/components/heading/heading.d.ts +3 -5
- package/lib/components/heading/heading.d.ts.map +1 -1
- package/lib/components/heading/heading.js +2 -4
- package/lib/components/heading/heading.js.map +1 -1
- package/lib/components/link-list/link-list.d.ts +19 -0
- package/lib/components/link-list/link-list.d.ts.map +1 -1
- package/lib/components/link-list/link-list.js +19 -0
- package/lib/components/link-list/link-list.js.map +1 -1
- package/lib/components/list/list.d.ts +13 -0
- package/lib/components/list/list.d.ts.map +1 -1
- package/lib/components/list/list.js +15 -0
- package/lib/components/list/list.js.map +1 -1
- package/lib/components/modal/modal.d.ts +2 -2
- package/lib/components/modal/modal.js +2 -2
- package/lib/components/modal/modal.js.map +1 -1
- package/lib/components/multi-select/multi-select.d.ts +2 -2
- package/lib/components/multi-select/multi-select.js +5 -5
- package/lib/components/multi-select/multi-select.js.map +1 -1
- package/lib/components/pagination/pagination.d.ts +1 -1
- package/lib/components/pagination/pagination.js +2 -2
- package/lib/components/pagination/pagination.js.map +1 -1
- package/lib/components/percent-bar/percent-bar.d.ts +2 -0
- package/lib/components/percent-bar/percent-bar.d.ts.map +1 -1
- package/lib/components/percent-bar/percent-bar.js +3 -1
- package/lib/components/percent-bar/percent-bar.js.map +1 -1
- package/lib/components/popover/popover.d.ts +6 -7
- package/lib/components/popover/popover.d.ts.map +1 -1
- package/lib/components/popover/popover.js +4 -4
- package/lib/components/popover/popover.js.map +1 -1
- package/lib/components/progress-meter/progress-meter.d.ts +0 -8
- package/lib/components/progress-meter/progress-meter.d.ts.map +1 -1
- package/lib/components/progress-meter/progress-meter.js +0 -7
- package/lib/components/progress-meter/progress-meter.js.map +1 -1
- package/lib/components/progress-steps/progress-steps.d.ts +16 -0
- package/lib/components/progress-steps/progress-steps.d.ts.map +1 -1
- package/lib/components/progress-steps/progress-steps.js +25 -0
- package/lib/components/progress-steps/progress-steps.js.map +1 -1
- package/lib/components/progress-steps-item/progress-steps-item.d.ts +1 -3
- package/lib/components/progress-steps-item/progress-steps-item.d.ts.map +1 -1
- package/lib/components/progress-steps-item/progress-steps-item.js +0 -1
- package/lib/components/progress-steps-item/progress-steps-item.js.map +1 -1
- package/lib/components/radio-field/radio-field.d.ts +33 -0
- package/lib/components/radio-field/radio-field.d.ts.map +1 -1
- package/lib/components/radio-field/radio-field.js +62 -0
- package/lib/components/radio-field/radio-field.js.map +1 -1
- package/lib/components/remove-tag/remove-tag.d.ts +1 -1
- package/lib/components/remove-tag/remove-tag.js +3 -3
- package/lib/components/remove-tag/remove-tag.js.map +1 -1
- package/lib/components/split-button/split-button.d.ts +3 -0
- package/lib/components/split-button/split-button.d.ts.map +1 -1
- package/lib/components/split-button/split-button.js +5 -2
- package/lib/components/split-button/split-button.js.map +1 -1
- package/lib/components/tab/tab.d.ts +1 -1
- package/lib/components/tab/tab.js +3 -3
- package/lib/components/tab/tab.js.map +1 -1
- package/lib/components/table/table.d.ts +36 -0
- package/lib/components/table/table.d.ts.map +1 -1
- package/lib/components/table/table.js +55 -0
- package/lib/components/table/table.js.map +1 -1
- package/lib/components/tabs/tabs.d.ts +20 -3
- package/lib/components/tabs/tabs.d.ts.map +1 -1
- package/lib/components/tabs/tabs.js +33 -5
- package/lib/components/tabs/tabs.js.map +1 -1
- package/lib/components/tag-list/tag-list.d.ts +17 -0
- package/lib/components/tag-list/tag-list.d.ts.map +1 -1
- package/lib/components/tag-list/tag-list.js +25 -0
- package/lib/components/tag-list/tag-list.js.map +1 -1
- package/lib/components/tooltip/tooltip.d.ts +2 -2
- package/lib/components/tooltip/tooltip.js +4 -4
- package/lib/components/tooltip/tooltip.js.map +1 -1
- package/lib/components/utils/light-children.d.ts +40 -0
- package/lib/components/utils/light-children.d.ts.map +1 -0
- package/lib/components/utils/light-children.js +100 -0
- package/lib/components/utils/light-children.js.map +1 -0
- package/lib/directives/if-truthy.d.ts +1 -1
- package/lib/light-children-TlEXev5S.js +39 -0
- package/lib/scripts/generate-react-wrappers.d.ts +8 -1
- package/lib/scripts/generate-react-wrappers.d.ts.map +1 -1
- package/lib/scripts/generate-react-wrappers.js +45 -1
- package/lib/scripts/generate-react-wrappers.js.map +1 -1
- package/mcp-manifest.json +162 -52
- package/package.json +11 -4
- package/react-manifest.json +153 -47
- package/dist/assets/if-defined-HKKhGupN.js +0 -1
- package/dist/assets/iframe-CWwoGI65.css +0 -1
- package/dist/assets/state-CfuknrX8.js +0 -1
- package/dist/fonts/PlusJakartaSans-Bold.woff2 +0 -0
- package/dist/fonts/PlusJakartaSans-Medium.woff2 +0 -0
- package/dist/fonts/PlusJakartaSans-Regular.woff2 +0 -0
- package/dist/fonts/PlusJakartaSans-SemiBold.woff2 +0 -0
- package/dist/logo.svg +0 -1
- package/dist/sb-addons/storybook-4/manager-bundle.js +0 -8
- /package/dist/icons/{express-scripts copy.svg → pharmacy copy.svg} +0 -0
- /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);
|