@seed-ship/mcp-ui-solid 6.15.0 → 6.17.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 (265) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/README.md +20 -3
  3. package/dist/_virtual/leaflet.markercluster-src.cjs +30 -7
  4. package/dist/_virtual/leaflet.markercluster-src.cjs.map +1 -1
  5. package/dist/_virtual/leaflet.markercluster-src.js +28 -5
  6. package/dist/_virtual/leaflet.markercluster-src.js.map +1 -1
  7. package/dist/_virtual/{leaflet.markercluster-src2.cjs → leaflet2.markercluster-src.cjs} +1 -1
  8. package/dist/_virtual/leaflet2.markercluster-src.cjs.map +1 -0
  9. package/dist/_virtual/{leaflet.markercluster-src2.js → leaflet2.markercluster-src.js} +1 -1
  10. package/dist/_virtual/leaflet2.markercluster-src.js.map +1 -0
  11. package/dist/adapters/connector.js +1 -1
  12. package/dist/components/ActionGroupRenderer.js +1 -1
  13. package/dist/components/ActionRenderer.js +1 -1
  14. package/dist/components/AgentCard.js +1 -1
  15. package/dist/components/AgentHandoff.js +1 -1
  16. package/dist/components/ArtifactRenderer.js +1 -1
  17. package/dist/components/AutocompleteDropdown.cjs +1 -2
  18. package/dist/components/AutocompleteDropdown.cjs.map +1 -1
  19. package/dist/components/AutocompleteDropdown.js +2 -3
  20. package/dist/components/AutocompleteFormField.cjs +1 -2
  21. package/dist/components/AutocompleteFormField.cjs.map +1 -1
  22. package/dist/components/AutocompleteFormField.js +2 -3
  23. package/dist/components/BriefingDiff.js +1 -1
  24. package/dist/components/CarouselRenderer.js +1 -1
  25. package/dist/components/ChartJSRenderer.js +1 -1
  26. package/dist/components/ChatPrompt.js +1 -1
  27. package/dist/components/CodeBlockRenderer.js +1 -1
  28. package/dist/components/ComponentToolbar.js +1 -1
  29. package/dist/components/DataPreviewSection.js +1 -1
  30. package/dist/components/DegradedFallback.cjs.map +1 -1
  31. package/dist/components/DegradedFallback.d.ts +1 -1
  32. package/dist/components/DegradedFallback.js +1 -1
  33. package/dist/components/DegradedFallback.js.map +1 -1
  34. package/dist/components/DraggableGridItem.cjs +1 -2
  35. package/dist/components/DraggableGridItem.cjs.map +1 -1
  36. package/dist/components/DraggableGridItem.js +2 -3
  37. package/dist/components/EditableUIResourceRenderer.cjs +1 -2
  38. package/dist/components/EditableUIResourceRenderer.cjs.map +1 -1
  39. package/dist/components/EditableUIResourceRenderer.js +2 -3
  40. package/dist/components/ExpandableWrapper.js +2 -2
  41. package/dist/components/FeedbackInline.js +1 -1
  42. package/dist/components/FooterRenderer.js +1 -1
  43. package/dist/components/FormFieldRenderer.js +1 -1
  44. package/dist/components/FormRenderer.js +1 -1
  45. package/dist/components/GenerativeUIErrorBoundary.js +1 -1
  46. package/dist/components/GhostText.cjs +1 -2
  47. package/dist/components/GhostText.cjs.map +1 -1
  48. package/dist/components/GhostText.js +2 -3
  49. package/dist/components/GraphRenderer.cjs +2 -19
  50. package/dist/components/GraphRenderer.cjs.map +1 -1
  51. package/dist/components/GraphRenderer.d.ts.map +1 -1
  52. package/dist/components/GraphRenderer.js +2 -19
  53. package/dist/components/GraphRenderer.js.map +1 -1
  54. package/dist/components/GridRenderer.cjs +1 -2
  55. package/dist/components/GridRenderer.cjs.map +1 -1
  56. package/dist/components/GridRenderer.js +2 -3
  57. package/dist/components/ImageGalleryRenderer.js +1 -1
  58. package/dist/components/LightboxOverlay.js +1 -1
  59. package/dist/components/MapRenderer.cjs +10 -11
  60. package/dist/components/MapRenderer.cjs.map +1 -1
  61. package/dist/components/MapRenderer.d.ts +6 -1
  62. package/dist/components/MapRenderer.d.ts.map +1 -1
  63. package/dist/components/MapRenderer.js +7 -8
  64. package/dist/components/MapRenderer.js.map +1 -1
  65. package/dist/components/ModalRenderer.js +1 -1
  66. package/dist/components/PortalDropdownMenu.js +1 -1
  67. package/dist/components/PresentationFeedback.js +1 -1
  68. package/dist/components/RenderContext.js +1 -1
  69. package/dist/components/ResizeHandle.cjs +1 -2
  70. package/dist/components/ResizeHandle.cjs.map +1 -1
  71. package/dist/components/ResizeHandle.js +2 -3
  72. package/dist/components/ScratchpadPanel.js +1 -1
  73. package/dist/components/SplitStepper.js +1 -1
  74. package/dist/components/StreamingUIRenderer.js +1 -1
  75. package/dist/components/UIResourceRenderer.cjs +12 -1
  76. package/dist/components/UIResourceRenderer.cjs.map +1 -1
  77. package/dist/components/UIResourceRenderer.d.ts.map +1 -1
  78. package/dist/components/UIResourceRenderer.js +13 -2
  79. package/dist/components/UIResourceRenderer.js.map +1 -1
  80. package/dist/components/VerifiedText.js +1 -1
  81. package/dist/components/VideoRenderer.js +1 -1
  82. package/dist/components/index.d.ts.map +1 -1
  83. package/dist/index.d.cts +1 -1
  84. package/dist/index.d.ts +1 -1
  85. package/dist/index.d.ts.map +1 -1
  86. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/core/component.cjs +1 -1
  87. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/core/component.js +1 -1
  88. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/axis/axis.cjs +1 -1
  89. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/axis/axis.js +1 -1
  90. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/axis/constant.cjs +1 -1
  91. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/axis/constant.js +1 -1
  92. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/axis/guides/grid.cjs +2 -2
  93. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/axis/guides/grid.js +2 -2
  94. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/axis/guides/labels.cjs +6 -6
  95. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/axis/guides/labels.js +6 -6
  96. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/axis/guides/line.cjs +1 -1
  97. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/axis/guides/line.js +1 -1
  98. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/axis/guides/ticks.cjs +2 -2
  99. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/axis/guides/ticks.js +2 -2
  100. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/axis/guides/title.cjs +1 -1
  101. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/axis/guides/title.js +1 -1
  102. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/axis/guides/utils.cjs +1 -1
  103. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/axis/guides/utils.js +1 -1
  104. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/grid/index.cjs +3 -3
  105. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/grid/index.js +3 -3
  106. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/indicator/indicator.cjs +4 -4
  107. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/indicator/indicator.js +4 -4
  108. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/legend/category/item.cjs +5 -5
  109. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/legend/category/item.js +5 -5
  110. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/legend/category/items.cjs +5 -5
  111. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/legend/category/items.js +5 -5
  112. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/legend/category.cjs +2 -2
  113. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/legend/category.js +2 -2
  114. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/legend/continuous/handle.cjs +1 -1
  115. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/legend/continuous/handle.js +1 -1
  116. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/legend/title/index.cjs +3 -3
  117. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/legend/title/index.js +3 -3
  118. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/marker/utils.cjs +1 -1
  119. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/marker/utils.js +1 -1
  120. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/navigator/index.cjs +5 -5
  121. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/navigator/index.js +5 -5
  122. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/poptip/index.cjs +2 -2
  123. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/poptip/index.js +2 -2
  124. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/select/option.cjs +2 -2
  125. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/select/option.js +2 -2
  126. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/select/select.cjs +2 -2
  127. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/select/select.js +2 -2
  128. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/slider/handle.cjs +2 -2
  129. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/slider/handle.js +2 -2
  130. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/slider/index.cjs +4 -4
  131. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/slider/index.js +4 -4
  132. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/sparkline/columns.cjs +1 -1
  133. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/sparkline/columns.js +1 -1
  134. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/sparkline/index.cjs +2 -2
  135. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/sparkline/index.js +2 -2
  136. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/sparkline/lines.cjs +1 -1
  137. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/sparkline/lines.js +1 -1
  138. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/sparkline/utils.cjs +2 -2
  139. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/sparkline/utils.js +2 -2
  140. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/timebar/controller.cjs +2 -2
  141. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/timebar/controller.js +2 -2
  142. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/timebar/handle.cjs +1 -1
  143. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/timebar/handle.js +1 -1
  144. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/timebar/icons.cjs +4 -4
  145. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/timebar/icons.js +4 -4
  146. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/timebar/timebar.cjs +3 -3
  147. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/timebar/timebar.js +3 -3
  148. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/tooltip/index.cjs +1 -1
  149. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/ui/tooltip/index.js +1 -1
  150. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/util/selection.cjs +1 -1
  151. package/dist/node_modules/.pnpm/@antv_component@2.1.11/node_modules/@antv/component/esm/util/selection.js +1 -1
  152. package/dist/node_modules/.pnpm/@antv_g-lite@2.7.0/node_modules/@antv/g-lite/dist/index.esm.js +1 -1
  153. package/dist/node_modules/.pnpm/@antv_g6@5.1.0/node_modules/@antv/g6/esm/behaviors/scroll-canvas.cjs +1 -1
  154. package/dist/node_modules/.pnpm/@antv_g6@5.1.0/node_modules/@antv/g6/esm/behaviors/scroll-canvas.js +1 -1
  155. package/dist/node_modules/.pnpm/@antv_g6@5.1.0/node_modules/@antv/g6/esm/elements/combos/base-combo.cjs +1 -1
  156. package/dist/node_modules/.pnpm/@antv_g6@5.1.0/node_modules/@antv/g6/esm/elements/combos/base-combo.js +1 -1
  157. package/dist/node_modules/.pnpm/@antv_g6@5.1.0/node_modules/@antv/g6/esm/elements/edges/base-edge.cjs +1 -1
  158. package/dist/node_modules/.pnpm/@antv_g6@5.1.0/node_modules/@antv/g6/esm/elements/edges/base-edge.js +1 -1
  159. package/dist/node_modules/.pnpm/@antv_g6@5.1.0/node_modules/@antv/g6/esm/index.cjs +58 -58
  160. package/dist/node_modules/.pnpm/@antv_g6@5.1.0/node_modules/@antv/g6/esm/plugins/legend.cjs +1 -1
  161. package/dist/node_modules/.pnpm/@antv_g6@5.1.0/node_modules/@antv/g6/esm/plugins/legend.js +1 -1
  162. package/dist/node_modules/.pnpm/@antv_g6@5.1.0/node_modules/@antv/g6/esm/plugins/minimap/index.cjs +1 -1
  163. package/dist/node_modules/.pnpm/@antv_g6@5.1.0/node_modules/@antv/g6/esm/plugins/minimap/index.js +1 -1
  164. package/dist/node_modules/.pnpm/@antv_g6@5.1.0/node_modules/@antv/g6/esm/plugins/snapline/index.cjs +1 -1
  165. package/dist/node_modules/.pnpm/@antv_g6@5.1.0/node_modules/@antv/g6/esm/plugins/snapline/index.js +1 -1
  166. package/dist/node_modules/.pnpm/@antv_g6@5.1.0/node_modules/@antv/g6/esm/plugins/tooltip.cjs +1 -1
  167. package/dist/node_modules/.pnpm/@antv_g6@5.1.0/node_modules/@antv/g6/esm/plugins/tooltip.js +1 -1
  168. package/dist/node_modules/.pnpm/@antv_g6@5.1.0/node_modules/@antv/g6/esm/registry/build-in.cjs +1 -1
  169. package/dist/node_modules/.pnpm/@antv_g6@5.1.0/node_modules/@antv/g6/esm/registry/build-in.js +1 -1
  170. package/dist/node_modules/.pnpm/@antv_g6@5.1.0/node_modules/@antv/g6/esm/runtime/data.cjs +1 -1
  171. package/dist/node_modules/.pnpm/@antv_g6@5.1.0/node_modules/@antv/g6/esm/runtime/data.js +1 -1
  172. package/dist/node_modules/.pnpm/@antv_g6@5.1.0/node_modules/@antv/g6/esm/runtime/element.cjs +1 -1
  173. package/dist/node_modules/.pnpm/@antv_g6@5.1.0/node_modules/@antv/g6/esm/runtime/element.js +1 -1
  174. package/dist/node_modules/.pnpm/@antv_g6@5.1.0/node_modules/@antv/g6/esm/runtime/graph.cjs +1 -1
  175. package/dist/node_modules/.pnpm/@antv_g6@5.1.0/node_modules/@antv/g6/esm/runtime/graph.js +1 -1
  176. package/dist/node_modules/.pnpm/@antv_g6@5.1.0/node_modules/@antv/g6/esm/runtime/layout.cjs +2 -2
  177. package/dist/node_modules/.pnpm/@antv_g6@5.1.0/node_modules/@antv/g6/esm/runtime/layout.js +2 -2
  178. package/dist/node_modules/.pnpm/@antv_g6@5.1.0/node_modules/@antv/g6/esm/runtime/viewport.cjs +1 -1
  179. package/dist/node_modules/.pnpm/@antv_g6@5.1.0/node_modules/@antv/g6/esm/runtime/viewport.js +1 -1
  180. package/dist/node_modules/.pnpm/@antv_g6@5.1.0/node_modules/@antv/g6/esm/utils/element.cjs +1 -1
  181. package/dist/node_modules/.pnpm/@antv_g6@5.1.0/node_modules/@antv/g6/esm/utils/element.js +1 -1
  182. package/dist/node_modules/.pnpm/@antv_g6@5.1.0/node_modules/@antv/g6/esm/utils/point.js +1 -1
  183. package/dist/node_modules/.pnpm/@antv_layout@2.0.0/node_modules/@antv/layout/lib/algorithm/antv-dagre/rank/index.js +1 -1
  184. package/dist/node_modules/.pnpm/@antv_layout@2.0.0/node_modules/@antv/layout/lib/algorithm/concentric/index.js +1 -1
  185. package/dist/node_modules/.pnpm/@antv_scale@0.4.16/node_modules/@antv/scale/esm/scales/continuous.cjs +1 -1
  186. package/dist/node_modules/.pnpm/@antv_scale@0.4.16/node_modules/@antv/scale/esm/scales/continuous.js +1 -1
  187. package/dist/node_modules/.pnpm/@tanstack_solid-virtual@3.13.18_solid-js@1.9.11/node_modules/@tanstack/solid-virtual/dist/esm/index.cjs +13 -0
  188. package/dist/node_modules/.pnpm/@tanstack_solid-virtual@3.13.18_solid-js@1.9.11/node_modules/@tanstack/solid-virtual/dist/esm/index.cjs.map +1 -1
  189. package/dist/node_modules/.pnpm/@tanstack_solid-virtual@3.13.18_solid-js@1.9.11/node_modules/@tanstack/solid-virtual/dist/esm/index.js +15 -2
  190. package/dist/node_modules/.pnpm/@tanstack_solid-virtual@3.13.18_solid-js@1.9.11/node_modules/@tanstack/solid-virtual/dist/esm/index.js.map +1 -1
  191. package/dist/node_modules/.pnpm/@tanstack_virtual-core@3.13.18/node_modules/@tanstack/virtual-core/dist/esm/index.cjs +64 -0
  192. package/dist/node_modules/.pnpm/@tanstack_virtual-core@3.13.18/node_modules/@tanstack/virtual-core/dist/esm/index.cjs.map +1 -1
  193. package/dist/node_modules/.pnpm/@tanstack_virtual-core@3.13.18/node_modules/@tanstack/virtual-core/dist/esm/index.js +66 -2
  194. package/dist/node_modules/.pnpm/@tanstack_virtual-core@3.13.18/node_modules/@tanstack/virtual-core/dist/esm/index.js.map +1 -1
  195. package/dist/node_modules/.pnpm/@tanstack_virtual-core@3.13.18/node_modules/@tanstack/virtual-core/dist/esm/utils.cjs +1 -1
  196. package/dist/node_modules/.pnpm/@tanstack_virtual-core@3.13.18/node_modules/@tanstack/virtual-core/dist/esm/utils.cjs.map +1 -1
  197. package/dist/node_modules/.pnpm/@tanstack_virtual-core@3.13.18/node_modules/@tanstack/virtual-core/dist/esm/utils.js +1 -1
  198. package/dist/node_modules/.pnpm/@tanstack_virtual-core@3.13.18/node_modules/@tanstack/virtual-core/dist/esm/utils.js.map +1 -1
  199. package/dist/node_modules/.pnpm/color-string@1.9.1/node_modules/color-string/index.cjs +6 -6
  200. package/dist/node_modules/.pnpm/{dompurify@3.4.1 → dompurify@3.4.13}/node_modules/dompurify/dist/purify.es.cjs +671 -233
  201. package/dist/node_modules/.pnpm/dompurify@3.4.13/node_modules/dompurify/dist/purify.es.cjs.map +1 -0
  202. package/dist/node_modules/.pnpm/{dompurify@3.4.1 → dompurify@3.4.13}/node_modules/dompurify/dist/purify.es.js +671 -233
  203. package/dist/node_modules/.pnpm/dompurify@3.4.13/node_modules/dompurify/dist/purify.es.js.map +1 -0
  204. package/dist/node_modules/.pnpm/leaflet.markercluster@1.5.3_leaflet@1.9.4/node_modules/leaflet.markercluster/dist/leaflet.markercluster-src.cjs +7 -7
  205. package/dist/node_modules/.pnpm/leaflet.markercluster@1.5.3_leaflet@1.9.4/node_modules/leaflet.markercluster/dist/leaflet.markercluster-src.cjs.map +1 -1
  206. package/dist/node_modules/.pnpm/leaflet.markercluster@1.5.3_leaflet@1.9.4/node_modules/leaflet.markercluster/dist/leaflet.markercluster-src.js +7 -7
  207. package/dist/node_modules/.pnpm/leaflet.markercluster@1.5.3_leaflet@1.9.4/node_modules/leaflet.markercluster/dist/leaflet.markercluster-src.js.map +1 -1
  208. package/dist/node_modules/.pnpm/leaflet@1.9.4/node_modules/leaflet/dist/leaflet-src.cjs +83 -83
  209. package/dist/node_modules/.pnpm/leaflet@1.9.4/node_modules/leaflet/dist/leaflet-src.cjs.map +1 -1
  210. package/dist/node_modules/.pnpm/leaflet@1.9.4/node_modules/leaflet/dist/leaflet-src.js +83 -83
  211. package/dist/node_modules/.pnpm/leaflet@1.9.4/node_modules/leaflet/dist/leaflet-src.js.map +1 -1
  212. package/dist/plugins/groq.cjs +1 -2
  213. package/dist/plugins/groq.cjs.map +1 -1
  214. package/dist/plugins/groq.js +1 -2
  215. package/dist/plugins/rest.cjs +1 -2
  216. package/dist/plugins/rest.cjs.map +1 -1
  217. package/dist/plugins/rest.js +1 -2
  218. package/dist/plugins/supabase.cjs +1 -2
  219. package/dist/plugins/supabase.cjs.map +1 -1
  220. package/dist/plugins/supabase.js +1 -2
  221. package/dist/services/component-registry.cjs +223 -7
  222. package/dist/services/component-registry.cjs.map +1 -1
  223. package/dist/services/component-registry.d.ts +0 -3
  224. package/dist/services/component-registry.d.ts.map +1 -1
  225. package/dist/services/component-registry.js +223 -7
  226. package/dist/services/component-registry.js.map +1 -1
  227. package/dist/services/validation.cjs +1 -23
  228. package/dist/services/validation.cjs.map +1 -1
  229. package/dist/services/validation.d.ts.map +1 -1
  230. package/dist/services/validation.js +2 -24
  231. package/dist/services/validation.js.map +1 -1
  232. package/dist/types/index.d.ts +12 -6
  233. package/dist/types/index.d.ts.map +1 -1
  234. package/dist/types.d.cts +12 -6
  235. package/dist/types.d.ts +12 -6
  236. package/dist/utils/escape-html.cjs +10 -0
  237. package/dist/utils/escape-html.cjs.map +1 -0
  238. package/dist/utils/escape-html.d.ts +13 -0
  239. package/dist/utils/escape-html.d.ts.map +1 -0
  240. package/dist/utils/escape-html.js +10 -0
  241. package/dist/utils/escape-html.js.map +1 -0
  242. package/docs/briefs/BRIEF-graph-component-g6.md +10 -0
  243. package/package.json +2 -2
  244. package/src/components/DegradedFallback.tsx +1 -1
  245. package/src/components/GraphRenderer.tsx +1 -19
  246. package/src/components/MapRenderer.security.test.ts +55 -2
  247. package/src/components/MapRenderer.tsx +15 -13
  248. package/src/components/UIResourceRenderer.tsx +8 -0
  249. package/src/components/UnsupportedComponent.test.tsx +28 -1
  250. package/src/components/index.ts +3 -0
  251. package/src/hooks/useStreamingUI.test.ts +194 -0
  252. package/src/index.ts +1 -0
  253. package/src/services/component-registry.test.ts +104 -2
  254. package/src/services/component-registry.ts +236 -7
  255. package/src/services/validation.test.ts +26 -0
  256. package/src/services/validation.ts +12 -24
  257. package/src/types/index.ts +12 -26
  258. package/src/types/map-types.test.ts +20 -0
  259. package/src/utils/escape-html.test.ts +30 -0
  260. package/src/utils/escape-html.ts +24 -0
  261. package/tsconfig.tsbuildinfo +1 -1
  262. package/dist/_virtual/leaflet.markercluster-src2.cjs.map +0 -1
  263. package/dist/_virtual/leaflet.markercluster-src2.js.map +0 -1
  264. package/dist/node_modules/.pnpm/dompurify@3.4.1/node_modules/dompurify/dist/purify.es.cjs.map +0 -1
  265. package/dist/node_modules/.pnpm/dompurify@3.4.1/node_modules/dompurify/dist/purify.es.js.map +0 -1
@@ -30,6 +30,7 @@ import { ExpandableWrapper, useExpanded } from './ExpandableWrapper';
30
30
  import { PortalDropdownMenu } from './PortalDropdownMenu';
31
31
  import { DegradedFallback } from './DegradedFallback';
32
32
  import { graphToDegradedTable } from '../utils/degraded-projections';
33
+ import { escapeHtml } from '../utils/escape-html';
33
34
  import { useTelemetry } from '../context/MCPUITelemetryContext';
34
35
 
35
36
  // Module-scoped lazy import promise — first call triggers the dynamic
@@ -479,24 +480,5 @@ async function downloadDataUrl(dataUrl: string, filename: string): Promise<void>
479
480
  downloadBlob(blob, filename);
480
481
  }
481
482
 
482
- function escapeHtml(s: string): string {
483
- return s.replace(/[&<>"']/g, (c) => {
484
- switch (c) {
485
- case '&':
486
- return '&amp;';
487
- case '<':
488
- return '&lt;';
489
- case '>':
490
- return '&gt;';
491
- case '"':
492
- return '&quot;';
493
- case "'":
494
- return '&#39;';
495
- default:
496
- return c;
497
- }
498
- });
499
- }
500
-
501
483
  // Re-export for tests + consumers that want to compose their own export menu
502
484
  export { toMermaid as graphToMermaid, toJSON as graphToJSON };
@@ -8,8 +8,8 @@
8
8
  * and not exercisable in jsdom).
9
9
  */
10
10
 
11
- import { describe, it, expect } from 'vitest';
12
- import { popupSafeText, buildPopupContent } from './MapRenderer';
11
+ import { describe, it, expect, vi } from 'vitest';
12
+ import { addGeoJSONLayer, popupSafeText, buildPopupContent } from './MapRenderer';
13
13
 
14
14
  const XSS = '<img src=x onerror=alert(1)>';
15
15
 
@@ -81,3 +81,56 @@ describe('buildPopupContent — GeoJSON popup (P1.2)', () => {
81
81
  expect(buildPopupContent({}, { titleField: 'a' })).toBeNull();
82
82
  });
83
83
  });
84
+
85
+ describe('addGeoJSONLayer — trusted-host wiring (UI-MAP-0a)', () => {
86
+ function renderPopup(allowHtml = false) {
87
+ const bindPopup = vi.fn();
88
+ const addTo = vi.fn();
89
+ type GeoJSONOptions = {
90
+ onEachFeature: (
91
+ feature: { properties: Record<string, unknown> },
92
+ featureLayer: { bindPopup: typeof bindPopup }
93
+ ) => void;
94
+ };
95
+ const leaflet = {
96
+ geoJSON: vi.fn((_geojson: unknown, options: GeoJSONOptions) => {
97
+ options.onEachFeature(
98
+ { properties: { name: '<script>evil</script>' } },
99
+ { bindPopup }
100
+ );
101
+ return { addTo };
102
+ }),
103
+ circleMarker: vi.fn(),
104
+ };
105
+
106
+ addGeoJSONLayer(
107
+ {},
108
+ leaflet,
109
+ { type: 'FeatureCollection', features: [] },
110
+ undefined,
111
+ { template: '<b>{{name}}</b>' },
112
+ allowHtml
113
+ );
114
+
115
+ return { bindPopup, addTo };
116
+ }
117
+
118
+ it('ignores raw template HTML on the default untrusted path', () => {
119
+ const { bindPopup, addTo } = renderPopup();
120
+ const html = bindPopup.mock.calls[0][0] as string;
121
+
122
+ expect(html).not.toContain('<b>');
123
+ expect(html).not.toContain('<script>');
124
+ expect(html).toContain('&lt;script&gt;');
125
+ expect(addTo).toHaveBeenCalledOnce();
126
+ });
127
+
128
+ it('honors trusted template structure while escaping substituted values', () => {
129
+ const { bindPopup } = renderPopup(true);
130
+ const html = bindPopup.mock.calls[0][0] as string;
131
+
132
+ expect(html).toContain('<b>');
133
+ expect(html).toContain('&lt;script&gt;');
134
+ expect(html).not.toContain('<script>');
135
+ });
136
+ });
@@ -18,6 +18,7 @@ import type {
18
18
  import { ExpandableWrapper, useExpanded } from './ExpandableWrapper';
19
19
  import { DegradedFallback } from './DegradedFallback';
20
20
  import { mapToDegradedTable } from '../utils/degraded-projections';
21
+ import { escapeHtml } from '../utils/escape-html';
21
22
  import { useTelemetry } from '../context/MCPUITelemetryContext';
22
23
 
23
24
  // Lazy load leaflet (it doesn't support SSR well)
@@ -172,14 +173,6 @@ export function buildPopupContent(
172
173
  return parts.join('<br/>');
173
174
  }
174
175
 
175
- function escapeHtml(str: string): string {
176
- return str
177
- .replace(/&/g, '&amp;')
178
- .replace(/</g, '&lt;')
179
- .replace(/>/g, '&gt;')
180
- .replace(/"/g, '&quot;');
181
- }
182
-
183
176
  /**
184
177
  * Resolve a marker's tooltip/popup string for binding (audit P1.2). Leaflet
185
178
  * renders bound strings as HTML; `marker.tooltip` / `marker.popup` come from
@@ -207,12 +200,13 @@ export function bindMarkerContent(
207
200
  * Add a GeoJSON layer to the map with style and popup support.
208
201
  * Returns the layer for bounds calculation.
209
202
  */
210
- function addGeoJSONLayer(
203
+ export function addGeoJSONLayer(
211
204
  mapInst: any,
212
205
  leaflet: any,
213
206
  geojson: unknown,
214
207
  style?: MapGeoJSONStyle,
215
- popup?: MapPopupConfig
208
+ popup?: MapPopupConfig,
209
+ allowHtml = false
216
210
  ): any {
217
211
  const styleFn = buildStyleFn(style);
218
212
 
@@ -231,7 +225,7 @@ function addGeoJSONLayer(
231
225
  });
232
226
  },
233
227
  onEachFeature: (feature: any, featureLayer: any) => {
234
- const html = buildPopupContent(feature, popup);
228
+ const html = buildPopupContent(feature, popup, allowHtml);
235
229
  if (html) {
236
230
  featureLayer.bindPopup(html, { maxWidth: 300 });
237
231
  }
@@ -420,7 +414,14 @@ export const MapRenderer: Component<MapRendererProps> = (props) => {
420
414
 
421
415
  // ─── GeoJSON (v3.1.0) ───────────────────────
422
416
  if (p?.geojson) {
423
- const geoLayer = addGeoJSONLayer(mapInstance, L, p.geojson, p.geojsonStyle, p.popup);
417
+ const geoLayer = addGeoJSONLayer(
418
+ mapInstance,
419
+ L,
420
+ p.geojson,
421
+ p.geojsonStyle,
422
+ p.popup,
423
+ allowHtml()
424
+ );
424
425
  allBoundsLayers.push(geoLayer);
425
426
  }
426
427
 
@@ -434,7 +435,8 @@ export const MapRenderer: Component<MapRendererProps> = (props) => {
434
435
  L,
435
436
  layerDef.geojson,
436
437
  layerDef.style || p?.geojsonStyle,
437
- layerDef.popup || p?.popup
438
+ layerDef.popup || p?.popup,
439
+ allowHtml()
438
440
  );
439
441
 
440
442
  overlays[layerDef.name] = geoLayer;
@@ -1600,6 +1600,14 @@ function ComponentRenderer(props: {
1600
1600
  <Show when={props.component.type === 'footer'}>
1601
1601
  <FooterRenderer params={props.component.params as any} />
1602
1602
  </Show>
1603
+ {/* `composite` is a UILayout discriminator, not a leaf renderer — a
1604
+ top-level composite is handled as a layout (see `layout()` above). If
1605
+ one arrives as a `components[]` entry or a streamed component it has no
1606
+ leaf branch and now passes the (spec-derived) validation gate, so
1607
+ surface the visible notice instead of a silent blank (audit P1.6). */}
1608
+ <Show when={props.component.type === 'composite'}>
1609
+ <UnsupportedComponentFallback component={props.component} />
1610
+ </Show>
1603
1611
  </GenerativeUIErrorBoundary>
1604
1612
  )
1605
1613
  }
@@ -11,7 +11,7 @@ import { describe, it, expect, afterEach } from 'vitest'
11
11
  import { render, cleanup, waitFor } from '@solidjs/testing-library'
12
12
  import { UIResourceRenderer } from './UIResourceRenderer'
13
13
  import { MCPUITelemetryProvider } from '../context/MCPUITelemetryContext'
14
- import type { UIComponent } from '../types'
14
+ import type { UIComponent, UILayout } from '../types'
15
15
 
16
16
  afterEach(cleanup)
17
17
 
@@ -59,6 +59,33 @@ describe('Unsupported component never renders blank (P1.6)', () => {
59
59
  })
60
60
  })
61
61
 
62
+ it('shows the notice for a composite nested as a components[] entry (not a silent blank)', async () => {
63
+ // A top-level composite is handled as a layout; but a composite that arrives
64
+ // as a leaf inside a layout's components[] has no dispatch branch. Since it
65
+ // now passes the spec-derived validation gate, it must surface the visible
66
+ // notice rather than render nothing (audit P1.6, Codex review follow-up).
67
+ const layoutWithNestedComposite: UILayout = {
68
+ id: 'L',
69
+ grid: { columns: 12, gap: '1rem' },
70
+ components: [
71
+ {
72
+ id: 'nested',
73
+ type: 'composite',
74
+ position: { colStart: 1, colSpan: 12 },
75
+ params: {},
76
+ } as UIComponent,
77
+ ],
78
+ }
79
+ const { container } = render(() => (
80
+ <UIResourceRenderer content={layoutWithNestedComposite} errorMode="silent" />
81
+ ))
82
+
83
+ await waitFor(() => {
84
+ expect(container.textContent ?? '').toContain('Unsupported component type')
85
+ })
86
+ expect(container.textContent).toContain('composite')
87
+ })
88
+
62
89
  it('renders a standalone footer component (previously a silent blank)', async () => {
63
90
  const footer: UIComponent = {
64
91
  id: 'f1',
@@ -27,6 +27,9 @@ export type { GenerativeUIErrorBoundaryProps } from './GenerativeUIErrorBoundary
27
27
  export { FooterRenderer } from './FooterRenderer'
28
28
  export type { FooterComponentParams } from './FooterRenderer'
29
29
 
30
+ // NOTE: kept as a published `./components` subpath export for backward compat.
31
+ // This is a legacy stub superseded by the inline ActionRenderer in
32
+ // UIResourceRenderer; slated for removal in the next MAJOR (not a minor).
30
33
  export { ActionRenderer } from './ActionRenderer'
31
34
  export type { ActionRendererProps } from './ActionRenderer'
32
35
 
@@ -0,0 +1,194 @@
1
+ /**
2
+ * useStreamingUI — behavioral coverage for the SSE streaming engine.
3
+ *
4
+ * The hook does NOT use `EventSource` (the field of that name is vestigial);
5
+ * it POSTs to /api/mcp/generative-ui-stream and reads an SSE-formatted body via
6
+ * `response.body.getReader()`. These tests stub `fetch` + the reader to drive
7
+ * scripted `status` / `component` / `complete` / `error` events and assert the
8
+ * four behaviors that had zero coverage: progressive accumulation, out-of-order
9
+ * buffering/reorder, terminal (non-recoverable) error, and recoverable-error
10
+ * reconnect with backoff.
11
+ *
12
+ * The hook auto-starts streaming on creation, so `fetch` is stubbed before the
13
+ * hook is instantiated inside a reactive root.
14
+ */
15
+
16
+ import { describe, it, expect, vi, afterEach } from 'vitest'
17
+ import { createRoot } from 'solid-js'
18
+ import { useStreamingUI, type UseStreamingUIOptions } from './useStreamingUI'
19
+
20
+ const T = '2026-07-03T00:00:00.000Z'
21
+
22
+ /** One SSE message: an `event:` line + a `data:` line + a blank terminator. */
23
+ function sse(event: string, data: unknown): string {
24
+ return `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`
25
+ }
26
+
27
+ /** A `component` event payload with the given sequence id. */
28
+ function comp(seq: number, type = 'metric') {
29
+ return {
30
+ componentId: `c${seq}`,
31
+ sequenceId: seq,
32
+ component: { id: `c${seq}`, type, position: { colStart: 1, colSpan: 12 }, params: {} },
33
+ position: { colStart: 1, colSpan: 12 },
34
+ }
35
+ }
36
+
37
+ /** A `complete` event payload (CompleteMetadata). */
38
+ function completeMeta() {
39
+ return {
40
+ layoutId: 'L1',
41
+ componentsCount: 2,
42
+ executionTimeMs: 1234,
43
+ firstTokenMs: 100,
44
+ provider: 'mock',
45
+ model: 'test',
46
+ cached: false,
47
+ }
48
+ }
49
+
50
+ /**
51
+ * A `fetch` stub. Each entry in `callSpecs` scripts one call (subsequent calls
52
+ * reuse the last entry) — either an HTTP failure (`ok: false`) or a readable
53
+ * body that yields the given SSE chunks then closes.
54
+ */
55
+ function makeFetchStub(callSpecs: Array<{ ok?: boolean; errorBody?: unknown; chunks?: string[] }>) {
56
+ let call = 0
57
+ return vi.fn(async () => {
58
+ const spec = callSpecs[Math.min(call, callSpecs.length - 1)]
59
+ call++
60
+ if (spec.ok === false) {
61
+ return { ok: false, json: async () => spec.errorBody ?? { message: 'http error' } }
62
+ }
63
+ const encoder = new TextEncoder()
64
+ const chunks = spec.chunks ?? []
65
+ let i = 0
66
+ const reader = {
67
+ read: async () =>
68
+ i < chunks.length
69
+ ? { done: false, value: encoder.encode(chunks[i++]) }
70
+ : { done: true, value: undefined },
71
+ }
72
+ return { ok: true, body: { getReader: () => reader } }
73
+ })
74
+ }
75
+
76
+ const disposers: Array<() => void> = []
77
+
78
+ /** Instantiate the hook inside a reactive root; returns its public API. */
79
+ function renderHook(options: UseStreamingUIOptions) {
80
+ let api!: ReturnType<typeof useStreamingUI>
81
+ createRoot((dispose) => {
82
+ disposers.push(dispose)
83
+ api = useStreamingUI(options)
84
+ })
85
+ return api
86
+ }
87
+
88
+ describe('useStreamingUI', () => {
89
+ afterEach(() => {
90
+ while (disposers.length) disposers.pop()!()
91
+ vi.unstubAllGlobals()
92
+ vi.clearAllMocks()
93
+ })
94
+
95
+ it('accumulates components in sequence order and finalizes on complete', async () => {
96
+ const onComplete = vi.fn()
97
+ const onComponentReceived = vi.fn()
98
+ vi.stubGlobal(
99
+ 'fetch',
100
+ makeFetchStub([
101
+ {
102
+ chunks: [
103
+ sse('status', { message: 'Working', timestamp: T, totalComponents: 2 }),
104
+ sse('component', comp(0, 'metric')),
105
+ sse('component', comp(1, 'text')),
106
+ sse('complete', completeMeta()),
107
+ ],
108
+ },
109
+ ])
110
+ )
111
+
112
+ const api = renderHook({ query: 'revenue', onComplete, onComponentReceived })
113
+
114
+ await vi.waitFor(() => expect(api.metadata()).not.toBeNull())
115
+
116
+ expect(api.components().map((c) => c.id)).toEqual(['c0', 'c1'])
117
+ expect(api.progress().totalCount).toBe(2)
118
+ expect(api.progress().receivedCount).toBe(2)
119
+ expect(api.metadata()?.executionTime).toBe(1234) // executionTimeMs aliased
120
+ expect(api.isStreaming()).toBe(false)
121
+ expect(api.isLoading()).toBe(false)
122
+ expect(onComponentReceived).toHaveBeenCalledTimes(2)
123
+ expect(onComplete).toHaveBeenCalledTimes(1)
124
+ })
125
+
126
+ it('buffers an out-of-order component and flushes in sequence order', async () => {
127
+ vi.stubGlobal(
128
+ 'fetch',
129
+ makeFetchStub([
130
+ {
131
+ chunks: [
132
+ sse('status', { message: 's', timestamp: T }),
133
+ sse('component', comp(1, 'text')), // arrives before its predecessor
134
+ sse('component', comp(0, 'metric')), // unblocks the buffer
135
+ sse('complete', completeMeta()),
136
+ ],
137
+ },
138
+ ])
139
+ )
140
+
141
+ const api = renderHook({ query: 'q' })
142
+
143
+ await vi.waitFor(() => expect(api.components().length).toBe(2))
144
+ // Despite arriving 1-then-0, they surface in sequence order.
145
+ expect(api.components().map((c) => c.id)).toEqual(['c0', 'c1'])
146
+ })
147
+
148
+ it('surfaces a non-recoverable error and does not reconnect', async () => {
149
+ const onError = vi.fn()
150
+ const fetchStub = makeFetchStub([
151
+ { chunks: [sse('error', { error: 'fatal', message: 'boom', recoverable: false })] },
152
+ ])
153
+ vi.stubGlobal('fetch', fetchStub)
154
+
155
+ const api = renderHook({ query: 'q', onError })
156
+
157
+ await vi.waitFor(() => expect(api.error()).not.toBeNull())
158
+ expect(api.error()?.message).toBe('boom')
159
+ expect(api.isStreaming()).toBe(false)
160
+ expect(api.isLoading()).toBe(false)
161
+ expect(onError).toHaveBeenCalledTimes(1)
162
+
163
+ // A non-recoverable error schedules no retry — fetch stays at one call.
164
+ await new Promise((r) => setTimeout(r, 30))
165
+ expect(fetchStub).toHaveBeenCalledTimes(1)
166
+ })
167
+
168
+ it('reconnects with backoff after a recoverable error, then recovers', async () => {
169
+ const onError = vi.fn()
170
+ const fetchStub = makeFetchStub([
171
+ // 1st connection: recoverable error → schedules a reconnect (backoff 1000ms)
172
+ { chunks: [sse('error', { error: 'net', message: 'transient', recoverable: true })] },
173
+ // 2nd connection: succeeds
174
+ {
175
+ chunks: [
176
+ sse('status', { message: 'retry', timestamp: T }),
177
+ sse('component', comp(0, 'metric')),
178
+ sse('complete', completeMeta()),
179
+ ],
180
+ },
181
+ ])
182
+ vi.stubGlobal('fetch', fetchStub)
183
+
184
+ const api = renderHook({ query: 'q', onError })
185
+
186
+ await vi.waitFor(() => expect(onError).toHaveBeenCalledTimes(1))
187
+ // The reconnect fires a second fetch and the stream recovers.
188
+ await vi.waitFor(() => expect(fetchStub).toHaveBeenCalledTimes(2), { timeout: 2500 })
189
+ await vi.waitFor(() => expect(api.metadata()).not.toBeNull(), { timeout: 2500 })
190
+
191
+ expect(api.error()).toBeNull() // reconnect resets error state
192
+ expect(api.components().map((c) => c.id)).toEqual(['c0'])
193
+ })
194
+ })
package/src/index.ts CHANGED
@@ -292,6 +292,7 @@ export type {
292
292
  VideoComponentParams,
293
293
  // Code & Maps types (Sprint 6 + v3.1.0)
294
294
  CodeComponentParams,
295
+ LatLngPoint,
295
296
  MapMarker,
296
297
  MapComponentParams,
297
298
  MapPopupConfig,
@@ -3,9 +3,28 @@
3
3
  */
4
4
 
5
5
  import { describe, it, expect } from 'vitest'
6
- import { validateAgainstRegistry, getComponentEntry, ComponentRegistry } from './component-registry'
6
+ import {
7
+ validateAgainstRegistry,
8
+ getComponentEntry,
9
+ ComponentRegistry,
10
+ MapRegistry,
11
+ } from './component-registry'
7
12
  import type { ComponentType } from '../types'
8
- import { ComponentTypeSchema } from '@seed-ship/mcp-ui-spec'
13
+ import { ComponentTypeSchema, MapComponentParamsSchema } from '@seed-ship/mcp-ui-spec'
14
+
15
+ interface RegistrySchemaNode {
16
+ type?: string
17
+ enum?: string[]
18
+ oneOf?: RegistrySchemaNode[]
19
+ anyOf?: RegistrySchemaNode[]
20
+ properties?: Record<string, RegistrySchemaNode>
21
+ required?: string[]
22
+ items?: RegistrySchemaNode | RegistrySchemaNode[]
23
+ additionalItems?: boolean
24
+ minItems?: number
25
+ maxItems?: number
26
+ minLength?: number
27
+ }
9
28
 
10
29
  /** All 20 component types in the registry */
11
30
  const ALL_TYPES: ComponentType[] = [
@@ -41,6 +60,89 @@ describe('registry ↔ schema parity (P1.5)', () => {
41
60
  })
42
61
  })
43
62
 
63
+ describe('MapRegistry ↔ map spec parity (UI-MAP-0a)', () => {
64
+ const properties = MapRegistry.schema.properties as Record<string, RegistrySchemaNode>
65
+ const center = properties.center as { oneOf: unknown[] }
66
+ const markers = properties.markers as {
67
+ items: {
68
+ properties: Record<string, unknown> & { position: { oneOf: unknown[] } }
69
+ }
70
+ }
71
+ const markerProperties = markers.items.properties
72
+
73
+ it('advertises every root field accepted by MapComponentParamsSchema', () => {
74
+ expect(Object.keys(properties).sort()).toEqual(Object.keys(MapComponentParamsSchema.shape).sort())
75
+ })
76
+
77
+ it('uses the canonical marker names and keeps host trust out of the payload', () => {
78
+ expect(markerProperties).toHaveProperty('tooltip')
79
+ expect(markerProperties).not.toHaveProperty('title')
80
+ expect(properties).not.toHaveProperty('allowHtmlPopups')
81
+ })
82
+
83
+ it('advertises both tuple and object LatLng forms', () => {
84
+ expect(center.oneOf).toHaveLength(2)
85
+ expect(markerProperties.position.oneOf).toHaveLength(2)
86
+ })
87
+
88
+ it('advertises only the GeoJSON root shapes accepted by the runtime spec', () => {
89
+ const branches = properties.geojson.oneOf ?? []
90
+ const branchesByType = Object.fromEntries(
91
+ branches.map((branch) => {
92
+ const types = branch.properties?.type.enum ?? []
93
+ return [types.length === 1 ? types[0] : 'Geometry', branch]
94
+ })
95
+ ) as Record<string, RegistrySchemaNode>
96
+
97
+ expect(Object.keys(branchesByType).sort()).toEqual(['Feature', 'FeatureCollection', 'Geometry'])
98
+ expect(branchesByType.FeatureCollection.required).toEqual(['type', 'features'])
99
+ expect(branchesByType.Feature.required).toEqual(['type', 'geometry'])
100
+ expect(branchesByType.Geometry.required).toEqual(['type'])
101
+ expect(branchesByType.Geometry.properties?.type.enum).toEqual([
102
+ 'Point',
103
+ 'MultiPoint',
104
+ 'LineString',
105
+ 'MultiLineString',
106
+ 'Polygon',
107
+ 'MultiPolygon',
108
+ 'GeometryCollection',
109
+ ])
110
+ expect(branchesByType.Geometry.properties?.coordinates.anyOf).toHaveLength(4)
111
+ })
112
+
113
+ it('preserves choropleth tuple order and rejects empty layer names in the advertised schema', () => {
114
+ const scale = properties.geojsonStyle.properties?.choroplethScale
115
+ const tuple = scale?.items as RegistrySchemaNode
116
+ const tupleItems = tuple.items as RegistrySchemaNode[]
117
+ const layer = properties.layers.items as RegistrySchemaNode
118
+
119
+ expect(tupleItems).toEqual([{ type: 'number' }, { type: 'string' }])
120
+ expect(tuple.additionalItems).toBe(false)
121
+ expect(tuple.minItems).toBe(2)
122
+ expect(tuple.maxItems).toBe(2)
123
+ expect(tuple).not.toHaveProperty('prefixItems')
124
+ expect(layer.properties?.name.minLength).toBe(1)
125
+ })
126
+
127
+ it.each([
128
+ ['empty GeoJSON objects', { geojson: {} }],
129
+ ['reversed choropleth tuples', { geojsonStyle: { choroplethScale: [['red', 0]] } }],
130
+ [
131
+ 'empty layer names',
132
+ {
133
+ layers: [
134
+ {
135
+ name: '',
136
+ geojson: { type: 'FeatureCollection', features: [] },
137
+ },
138
+ ],
139
+ },
140
+ ],
141
+ ])('keeps runtime rejection coverage for %s', (_label, params) => {
142
+ expect(MapComponentParamsSchema.safeParse(params).success).toBe(false)
143
+ })
144
+ })
145
+
44
146
  describe('ComponentRegistry', () => {
45
147
  describe('registry completeness', () => {
46
148
  it('has exactly 20 registered types', () => {