@xemahq/ui-kernel 0.9.0 → 0.9.2

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 (444) hide show
  1. package/README.md +2 -2
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +1 -0
  5. package/dist/index.js.map +1 -1
  6. package/dist/lib/biome-host/biome-builders.d.ts.map +1 -1
  7. package/dist/lib/biome-host/biome-builders.js +3 -1
  8. package/dist/lib/biome-host/biome-builders.js.map +1 -1
  9. package/dist/lib/biome-host/biome-registry.d.ts.map +1 -1
  10. package/dist/lib/biome-host/biome-registry.js +14 -9
  11. package/dist/lib/biome-host/biome-registry.js.map +1 -1
  12. package/dist/lib/biome-host/biome-scope-hooks.d.ts +11 -0
  13. package/dist/lib/biome-host/biome-scope-hooks.d.ts.map +1 -0
  14. package/dist/lib/biome-host/biome-scope-hooks.js +25 -0
  15. package/dist/lib/biome-host/biome-scope-hooks.js.map +1 -0
  16. package/dist/lib/biome-host/biome-scope.d.ts +1 -10
  17. package/dist/lib/biome-host/biome-scope.d.ts.map +1 -1
  18. package/dist/lib/biome-host/biome-scope.js +1 -24
  19. package/dist/lib/biome-host/biome-scope.js.map +1 -1
  20. package/dist/lib/biome-host/biome-scoped-query.d.ts +1 -1
  21. package/dist/lib/biome-host/biome-scoped-query.d.ts.map +1 -1
  22. package/dist/lib/biome-host/biome-scoped-query.js +1 -1
  23. package/dist/lib/biome-host/biome-scoped-query.js.map +1 -1
  24. package/dist/lib/biome-host/create-biome-orval-config.js +2 -2
  25. package/dist/lib/biome-host/create-biome-orval-config.js.map +1 -1
  26. package/dist/lib/biome-host/errors.js.map +1 -1
  27. package/dist/lib/biome-host/index.d.ts +1 -1
  28. package/dist/lib/biome-host/index.d.ts.map +1 -1
  29. package/dist/lib/biome-host/index.js +1 -1
  30. package/dist/lib/biome-host/index.js.map +1 -1
  31. package/dist/lib/biome-host/session-profiles.d.ts.map +1 -1
  32. package/dist/lib/biome-host/session-profiles.js +4 -1
  33. package/dist/lib/biome-host/session-profiles.js.map +1 -1
  34. package/dist/lib/capabilities/capability-context.d.ts +9 -0
  35. package/dist/lib/capabilities/capability-context.d.ts.map +1 -0
  36. package/dist/lib/capabilities/capability-context.js +13 -0
  37. package/dist/lib/capabilities/capability-context.js.map +1 -0
  38. package/dist/lib/capabilities/capability-provider.d.ts +2 -8
  39. package/dist/lib/capabilities/capability-provider.d.ts.map +1 -1
  40. package/dist/lib/capabilities/capability-provider.js +1 -12
  41. package/dist/lib/capabilities/capability-provider.js.map +1 -1
  42. package/dist/lib/capabilities/index.d.ts +1 -0
  43. package/dist/lib/capabilities/index.d.ts.map +1 -1
  44. package/dist/lib/capabilities/index.js +1 -0
  45. package/dist/lib/capabilities/index.js.map +1 -1
  46. package/dist/lib/capabilities/use-capability.d.ts.map +1 -1
  47. package/dist/lib/capabilities/use-capability.js +1 -1
  48. package/dist/lib/capabilities/use-capability.js.map +1 -1
  49. package/dist/lib/contribution-registry/contribution-registry.d.ts +24 -0
  50. package/dist/lib/contribution-registry/contribution-registry.d.ts.map +1 -0
  51. package/dist/lib/contribution-registry/contribution-registry.js +67 -0
  52. package/dist/lib/contribution-registry/contribution-registry.js.map +1 -0
  53. package/dist/lib/contribution-registry/index.d.ts +3 -0
  54. package/dist/lib/contribution-registry/index.d.ts.map +1 -0
  55. package/dist/lib/contribution-registry/index.js +3 -0
  56. package/dist/lib/contribution-registry/index.js.map +1 -0
  57. package/dist/lib/contribution-registry/renderable-registry.d.ts +33 -0
  58. package/dist/lib/contribution-registry/renderable-registry.d.ts.map +1 -0
  59. package/dist/lib/contribution-registry/renderable-registry.js +51 -0
  60. package/dist/lib/contribution-registry/renderable-registry.js.map +1 -0
  61. package/dist/lib/system-bus/deeplink.js.map +1 -1
  62. package/dist/lib/system-bus/host-ports.d.ts +2 -1
  63. package/dist/lib/system-bus/host-ports.d.ts.map +1 -1
  64. package/dist/lib/system-bus/intent-registry.d.ts.map +1 -1
  65. package/dist/lib/system-bus/intent-registry.js.map +1 -1
  66. package/dist/offline/index.d.ts +2 -1
  67. package/dist/offline/index.d.ts.map +1 -1
  68. package/dist/offline/index.js +2 -1
  69. package/dist/offline/index.js.map +1 -1
  70. package/dist/offline/offline-status-badge.js +1 -1
  71. package/dist/offline/offline-status-badge.js.map +1 -1
  72. package/dist/offline/outbox.js.map +1 -1
  73. package/dist/offline/react-hooks.d.ts +7 -0
  74. package/dist/offline/react-hooks.d.ts.map +1 -0
  75. package/dist/offline/react-hooks.js +25 -0
  76. package/dist/offline/react-hooks.js.map +1 -0
  77. package/dist/offline/react.d.ts +0 -4
  78. package/dist/offline/react.d.ts.map +1 -1
  79. package/dist/offline/react.js +2 -24
  80. package/dist/offline/react.js.map +1 -1
  81. package/dist/offline/storage.d.ts.map +1 -1
  82. package/dist/offline/storage.js +8 -5
  83. package/dist/offline/storage.js.map +1 -1
  84. package/dist/registry/index.d.ts +1 -0
  85. package/dist/registry/index.d.ts.map +1 -1
  86. package/dist/registry/index.js +1 -0
  87. package/dist/registry/index.js.map +1 -1
  88. package/dist/registry/lib/primitives/SessionEventCard.d.ts.map +1 -1
  89. package/dist/registry/lib/primitives/SessionEventCard.js +1 -1
  90. package/dist/registry/lib/primitives/SessionEventCard.js.map +1 -1
  91. package/dist/registry/lib/session-context-provider.d.ts +1 -3
  92. package/dist/registry/lib/session-context-provider.d.ts.map +1 -1
  93. package/dist/registry/lib/session-context-provider.js +2 -15
  94. package/dist/registry/lib/session-context-provider.js.map +1 -1
  95. package/dist/registry/lib/session-context.d.ts +5 -0
  96. package/dist/registry/lib/session-context.d.ts.map +1 -0
  97. package/dist/registry/lib/session-context.js +15 -0
  98. package/dist/registry/lib/session-context.js.map +1 -0
  99. package/dist/registry/lib/session-selectors.d.ts.map +1 -1
  100. package/dist/registry/lib/session-selectors.js +3 -1
  101. package/dist/registry/lib/session-selectors.js.map +1 -1
  102. package/dist/ui/branding/app-branding-context.d.ts +9 -0
  103. package/dist/ui/branding/app-branding-context.d.ts.map +1 -0
  104. package/dist/ui/branding/app-branding-context.js +10 -0
  105. package/dist/ui/branding/app-branding-context.js.map +1 -0
  106. package/dist/ui/branding/app-branding-provider.d.ts +0 -6
  107. package/dist/ui/branding/app-branding-provider.d.ts.map +1 -1
  108. package/dist/ui/branding/app-branding-provider.js +1 -8
  109. package/dist/ui/branding/app-branding-provider.js.map +1 -1
  110. package/dist/ui/capture/field-spec-to-zod.d.ts +1 -0
  111. package/dist/ui/capture/field-spec-to-zod.d.ts.map +1 -1
  112. package/dist/ui/capture/field-spec-to-zod.js +19 -0
  113. package/dist/ui/capture/field-spec-to-zod.js.map +1 -1
  114. package/dist/ui/capture/field-spec-validate.d.ts +4 -0
  115. package/dist/ui/capture/field-spec-validate.d.ts.map +1 -0
  116. package/dist/ui/capture/field-spec-validate.js +129 -0
  117. package/dist/ui/capture/field-spec-validate.js.map +1 -0
  118. package/dist/ui/capture/field-spec.d.ts +13 -1
  119. package/dist/ui/capture/field-spec.d.ts.map +1 -1
  120. package/dist/ui/capture/field-spec.js +25 -0
  121. package/dist/ui/capture/field-spec.js.map +1 -1
  122. package/dist/ui/capture/index.d.ts +4 -2
  123. package/dist/ui/capture/index.d.ts.map +1 -1
  124. package/dist/ui/capture/index.js +4 -2
  125. package/dist/ui/capture/index.js.map +1 -1
  126. package/dist/ui/capture/schema-field-input.d.ts.map +1 -1
  127. package/dist/ui/capture/schema-field-input.js +40 -13
  128. package/dist/ui/capture/schema-field-input.js.map +1 -1
  129. package/dist/ui/capture/submit-values.d.ts +6 -0
  130. package/dist/ui/capture/submit-values.d.ts.map +1 -0
  131. package/dist/ui/capture/submit-values.js +28 -0
  132. package/dist/ui/capture/submit-values.js.map +1 -0
  133. package/dist/ui/capture/use-prefill.d.ts.map +1 -1
  134. package/dist/ui/capture/use-prefill.js +3 -1
  135. package/dist/ui/capture/use-prefill.js.map +1 -1
  136. package/dist/ui/capture/use-wizard.d.ts +1 -1
  137. package/dist/ui/capture/use-wizard.d.ts.map +1 -1
  138. package/dist/ui/capture/use-wizard.js +1 -1
  139. package/dist/ui/capture/use-wizard.js.map +1 -1
  140. package/dist/ui/capture/wizard.d.ts.map +1 -1
  141. package/dist/ui/capture/wizard.js.map +1 -1
  142. package/dist/ui/index.d.ts +6 -1
  143. package/dist/ui/index.d.ts.map +1 -1
  144. package/dist/ui/index.js +6 -1
  145. package/dist/ui/index.js.map +1 -1
  146. package/dist/ui/primitives/alert-dialog.js +1 -1
  147. package/dist/ui/primitives/alert-dialog.js.map +1 -1
  148. package/dist/ui/primitives/badge-variants.d.ts +4 -0
  149. package/dist/ui/primitives/badge-variants.d.ts.map +1 -0
  150. package/dist/ui/primitives/badge-variants.js +15 -0
  151. package/dist/ui/primitives/badge-variants.js.map +1 -0
  152. package/dist/ui/primitives/badge.d.ts +3 -5
  153. package/dist/ui/primitives/badge.d.ts.map +1 -1
  154. package/dist/ui/primitives/badge.js +2 -15
  155. package/dist/ui/primitives/badge.js.map +1 -1
  156. package/dist/ui/primitives/button-variants.d.ts +5 -0
  157. package/dist/ui/primitives/button-variants.d.ts.map +1 -0
  158. package/dist/ui/primitives/button-variants.js +24 -0
  159. package/dist/ui/primitives/button-variants.js.map +1 -0
  160. package/dist/ui/primitives/button.d.ts +3 -6
  161. package/dist/ui/primitives/button.d.ts.map +1 -1
  162. package/dist/ui/primitives/button.js +2 -24
  163. package/dist/ui/primitives/button.js.map +1 -1
  164. package/dist/ui/primitives/form-stepper.d.ts +1 -13
  165. package/dist/ui/primitives/form-stepper.d.ts.map +1 -1
  166. package/dist/ui/primitives/form-stepper.js +1 -6
  167. package/dist/ui/primitives/form-stepper.js.map +1 -1
  168. package/dist/ui/primitives/form-stepper.types.d.ts +14 -0
  169. package/dist/ui/primitives/form-stepper.types.d.ts.map +1 -0
  170. package/dist/ui/primitives/form-stepper.types.js +7 -0
  171. package/dist/ui/primitives/form-stepper.types.js.map +1 -0
  172. package/dist/ui/primitives/overflow-tabs.d.ts +5 -0
  173. package/dist/ui/primitives/overflow-tabs.d.ts.map +1 -1
  174. package/dist/ui/primitives/overflow-tabs.js +4 -4
  175. package/dist/ui/primitives/overflow-tabs.js.map +1 -1
  176. package/dist/ui/primitives/preview-device-frame.js +1 -1
  177. package/dist/ui/primitives/preview-device-frame.js.map +1 -1
  178. package/dist/ui/primitives/tag-multi-select.d.ts.map +1 -1
  179. package/dist/ui/primitives/tag-multi-select.js +1 -1
  180. package/dist/ui/primitives/tag-multi-select.js.map +1 -1
  181. package/dist/ui/primitives/url-preview-chrome.d.ts +8 -0
  182. package/dist/ui/primitives/url-preview-chrome.d.ts.map +1 -0
  183. package/dist/ui/primitives/url-preview-chrome.js +40 -0
  184. package/dist/ui/primitives/url-preview-chrome.js.map +1 -0
  185. package/dist/vista/GenericCatalogueSurface.d.ts +10 -0
  186. package/dist/vista/GenericCatalogueSurface.d.ts.map +1 -0
  187. package/dist/vista/GenericCatalogueSurface.js +80 -0
  188. package/dist/vista/GenericCatalogueSurface.js.map +1 -0
  189. package/dist/vista/SurfaceFallbackCard.d.ts +9 -0
  190. package/dist/vista/SurfaceFallbackCard.d.ts.map +1 -0
  191. package/dist/vista/SurfaceFallbackCard.js +44 -0
  192. package/dist/vista/SurfaceFallbackCard.js.map +1 -0
  193. package/dist/vista/VistaPanel.d.ts +21 -0
  194. package/dist/vista/VistaPanel.d.ts.map +1 -0
  195. package/dist/vista/VistaPanel.js +31 -0
  196. package/dist/vista/VistaPanel.js.map +1 -0
  197. package/dist/vista/html/CustomUiFrame.d.ts +20 -0
  198. package/dist/vista/html/CustomUiFrame.d.ts.map +1 -0
  199. package/dist/vista/html/CustomUiFrame.js +69 -0
  200. package/dist/vista/html/CustomUiFrame.js.map +1 -0
  201. package/dist/vista/html/custom-ui-frame-bridge.d.ts +13 -0
  202. package/dist/vista/html/custom-ui-frame-bridge.d.ts.map +1 -0
  203. package/dist/vista/html/custom-ui-frame-bridge.js +30 -0
  204. package/dist/vista/html/custom-ui-frame-bridge.js.map +1 -0
  205. package/dist/vista/html/custom-ui-frame-contract.d.ts +29 -0
  206. package/dist/vista/html/custom-ui-frame-contract.d.ts.map +1 -0
  207. package/dist/vista/html/custom-ui-frame-contract.js +16 -0
  208. package/dist/vista/html/custom-ui-frame-contract.js.map +1 -0
  209. package/dist/vista/html/custom-ui-intents.d.ts +20 -0
  210. package/dist/vista/html/custom-ui-intents.d.ts.map +1 -0
  211. package/dist/vista/html/custom-ui-intents.js +57 -0
  212. package/dist/vista/html/custom-ui-intents.js.map +1 -0
  213. package/dist/vista/html/custom-ui-srcdoc.d.ts +17 -0
  214. package/dist/vista/html/custom-ui-srcdoc.d.ts.map +1 -0
  215. package/dist/vista/html/custom-ui-srcdoc.js +70 -0
  216. package/dist/vista/html/custom-ui-srcdoc.js.map +1 -0
  217. package/dist/vista/html/index.d.ts +6 -0
  218. package/dist/vista/html/index.d.ts.map +1 -0
  219. package/dist/vista/html/index.js +6 -0
  220. package/dist/vista/html/index.js.map +1 -0
  221. package/dist/vista/index.d.ts +22 -0
  222. package/dist/vista/index.d.ts.map +1 -0
  223. package/dist/vista/index.js +22 -0
  224. package/dist/vista/index.js.map +1 -0
  225. package/dist/vista/surface-catalogue.d.ts +5 -0
  226. package/dist/vista/surface-catalogue.d.ts.map +1 -0
  227. package/dist/vista/surface-catalogue.js +30 -0
  228. package/dist/vista/surface-catalogue.js.map +1 -0
  229. package/dist/vista/surface-icons.d.ts +7 -0
  230. package/dist/vista/surface-icons.d.ts.map +1 -0
  231. package/dist/vista/surface-icons.js +26 -0
  232. package/dist/vista/surface-icons.js.map +1 -0
  233. package/dist/vista/surface-registry.d.ts +7 -0
  234. package/dist/vista/surface-registry.d.ts.map +1 -0
  235. package/dist/vista/surface-registry.js +7 -0
  236. package/dist/vista/surface-registry.js.map +1 -0
  237. package/dist/vista/surfaces/ChartSurface.d.ts +4 -0
  238. package/dist/vista/surfaces/ChartSurface.d.ts.map +1 -0
  239. package/dist/vista/surfaces/ChartSurface.js +17 -0
  240. package/dist/vista/surfaces/ChartSurface.js.map +1 -0
  241. package/dist/vista/surfaces/FilesSurface.d.ts +4 -0
  242. package/dist/vista/surfaces/FilesSurface.d.ts.map +1 -0
  243. package/dist/vista/surfaces/FilesSurface.js +77 -0
  244. package/dist/vista/surfaces/FilesSurface.js.map +1 -0
  245. package/dist/vista/surfaces/FormSurface.d.ts +4 -0
  246. package/dist/vista/surfaces/FormSurface.d.ts.map +1 -0
  247. package/dist/vista/surfaces/FormSurface.js +124 -0
  248. package/dist/vista/surfaces/FormSurface.js.map +1 -0
  249. package/dist/vista/surfaces/HtmlSurface.d.ts +4 -0
  250. package/dist/vista/surfaces/HtmlSurface.d.ts.map +1 -0
  251. package/dist/vista/surfaces/HtmlSurface.js +11 -0
  252. package/dist/vista/surfaces/HtmlSurface.js.map +1 -0
  253. package/dist/vista/surfaces/TableSurface.d.ts +4 -0
  254. package/dist/vista/surfaces/TableSurface.d.ts.map +1 -0
  255. package/dist/vista/surfaces/TableSurface.js +42 -0
  256. package/dist/vista/surfaces/TableSurface.js.map +1 -0
  257. package/dist/vista/surfaces/UrlSurface.d.ts +4 -0
  258. package/dist/vista/surfaces/UrlSurface.d.ts.map +1 -0
  259. package/dist/vista/surfaces/UrlSurface.js +7 -0
  260. package/dist/vista/surfaces/UrlSurface.js.map +1 -0
  261. package/dist/vista/surfaces/blob-surfaces.d.ts +6 -0
  262. package/dist/vista/surfaces/blob-surfaces.d.ts.map +1 -0
  263. package/dist/vista/surfaces/blob-surfaces.js +38 -0
  264. package/dist/vista/surfaces/blob-surfaces.js.map +1 -0
  265. package/dist/vista/surfaces/elicitation-transport.d.ts +27 -0
  266. package/dist/vista/surfaces/elicitation-transport.d.ts.map +1 -0
  267. package/dist/vista/surfaces/elicitation-transport.js +193 -0
  268. package/dist/vista/surfaces/elicitation-transport.js.map +1 -0
  269. package/dist/vista/surfaces/file-surface-utils.d.ts +13 -0
  270. package/dist/vista/surfaces/file-surface-utils.d.ts.map +1 -0
  271. package/dist/vista/surfaces/file-surface-utils.js +46 -0
  272. package/dist/vista/surfaces/file-surface-utils.js.map +1 -0
  273. package/dist/vista/surfaces/index.d.ts +12 -0
  274. package/dist/vista/surfaces/index.d.ts.map +1 -0
  275. package/dist/vista/surfaces/index.js +12 -0
  276. package/dist/vista/surfaces/index.js.map +1 -0
  277. package/dist/vista/surfaces/register-surfaces.d.ts +2 -0
  278. package/dist/vista/surfaces/register-surfaces.d.ts.map +1 -0
  279. package/dist/vista/surfaces/register-surfaces.js +80 -0
  280. package/dist/vista/surfaces/register-surfaces.js.map +1 -0
  281. package/dist/vista/surfaces/text-surfaces.d.ts +8 -0
  282. package/dist/vista/surfaces/text-surfaces.d.ts.map +1 -0
  283. package/dist/vista/surfaces/text-surfaces.js +54 -0
  284. package/dist/vista/surfaces/text-surfaces.js.map +1 -0
  285. package/dist/vista/use-vista-tabs.d.ts +30 -0
  286. package/dist/vista/use-vista-tabs.d.ts.map +1 -0
  287. package/dist/vista/use-vista-tabs.js +77 -0
  288. package/dist/vista/use-vista-tabs.js.map +1 -0
  289. package/dist/vista/vista-code-renderer-context.d.ts +9 -0
  290. package/dist/vista/vista-code-renderer-context.d.ts.map +1 -0
  291. package/dist/vista/vista-code-renderer-context.js +6 -0
  292. package/dist/vista/vista-code-renderer-context.js.map +1 -0
  293. package/dist/vista/vista-code-renderer.d.ts +7 -0
  294. package/dist/vista/vista-code-renderer.d.ts.map +1 -0
  295. package/dist/vista/vista-code-renderer.js +6 -0
  296. package/dist/vista/vista-code-renderer.js.map +1 -0
  297. package/dist/vista/vista-file-context.d.ts +8 -0
  298. package/dist/vista/vista-file-context.d.ts.map +1 -0
  299. package/dist/vista/vista-file-context.js +6 -0
  300. package/dist/vista/vista-file-context.js.map +1 -0
  301. package/dist/vista/vista-file-source.d.ts +15 -0
  302. package/dist/vista/vista-file-source.d.ts.map +1 -0
  303. package/dist/vista/vista-file-source.js +66 -0
  304. package/dist/vista/vista-file-source.js.map +1 -0
  305. package/dist/vista/vista-form-context.d.ts +13 -0
  306. package/dist/vista/vista-form-context.d.ts.map +1 -0
  307. package/dist/vista/vista-form-context.js +6 -0
  308. package/dist/vista/vista-form-context.js.map +1 -0
  309. package/dist/vista/vista-form-host.d.ts +7 -0
  310. package/dist/vista/vista-form-host.d.ts.map +1 -0
  311. package/dist/vista/vista-form-host.js +6 -0
  312. package/dist/vista/vista-form-host.js.map +1 -0
  313. package/dist/vista/vista-model.d.ts +12 -0
  314. package/dist/vista/vista-model.d.ts.map +1 -0
  315. package/dist/vista/vista-model.js +72 -0
  316. package/dist/vista/vista-model.js.map +1 -0
  317. package/dist/vista/vista-open-context.d.ts +7 -0
  318. package/dist/vista/vista-open-context.d.ts.map +1 -0
  319. package/dist/vista/vista-open-context.js +6 -0
  320. package/dist/vista/vista-open-context.js.map +1 -0
  321. package/dist/vista/vista-open-state.d.ts +5 -0
  322. package/dist/vista/vista-open-state.d.ts.map +1 -0
  323. package/dist/vista/vista-open-state.js +6 -0
  324. package/dist/vista/vista-open-state.js.map +1 -0
  325. package/dist/vista/vista-payloads.d.ts +106 -0
  326. package/dist/vista/vista-payloads.d.ts.map +1 -0
  327. package/dist/vista/vista-payloads.js +302 -0
  328. package/dist/vista/vista-payloads.js.map +1 -0
  329. package/dist/vista/vista-tabs-manifest.d.ts +17 -0
  330. package/dist/vista/vista-tabs-manifest.d.ts.map +1 -0
  331. package/dist/vista/vista-tabs-manifest.js +60 -0
  332. package/dist/vista/vista-tabs-manifest.js.map +1 -0
  333. package/dist/vista/vista-tools.d.ts +41 -0
  334. package/dist/vista/vista-tools.d.ts.map +1 -0
  335. package/dist/vista/vista-tools.js +113 -0
  336. package/dist/vista/vista-tools.js.map +1 -0
  337. package/package.json +4 -4
  338. package/src/index.ts +5 -0
  339. package/src/lib/biome-host/biome-builders.ts +4 -1
  340. package/src/lib/biome-host/biome-registry.ts +31 -12
  341. package/src/lib/biome-host/biome-scope-hooks.ts +39 -0
  342. package/src/lib/biome-host/biome-scope.tsx +1 -68
  343. package/src/lib/biome-host/biome-scoped-query.ts +4 -1
  344. package/src/lib/biome-host/create-biome-orval-config.ts +2 -2
  345. package/src/lib/biome-host/errors.ts +1 -1
  346. package/src/lib/biome-host/index.ts +1 -1
  347. package/src/lib/biome-host/session-profiles.ts +7 -1
  348. package/src/lib/capabilities/capability-context.ts +25 -0
  349. package/src/lib/capabilities/capability-provider.tsx +5 -34
  350. package/src/lib/capabilities/index.ts +1 -0
  351. package/src/lib/capabilities/use-capability.ts +4 -1
  352. package/src/lib/contribution-registry/contribution-registry.spec.ts +141 -0
  353. package/src/lib/contribution-registry/contribution-registry.ts +202 -0
  354. package/src/lib/contribution-registry/index.ts +9 -0
  355. package/src/lib/contribution-registry/renderable-registry.ts +142 -0
  356. package/src/lib/system-bus/deeplink.ts +1 -1
  357. package/src/lib/system-bus/host-ports.ts +2 -1
  358. package/src/lib/system-bus/intent-registry.ts +1 -2
  359. package/src/offline/index.ts +4 -2
  360. package/src/offline/offline-status-badge.tsx +1 -1
  361. package/src/offline/outbox.test.ts +23 -16
  362. package/src/offline/outbox.ts +1 -1
  363. package/src/offline/react-hooks.ts +37 -0
  364. package/src/offline/react.tsx +1 -40
  365. package/src/offline/storage.ts +11 -6
  366. package/src/registry/index.ts +1 -0
  367. package/src/registry/lib/primitives/SessionEventCard.tsx +4 -2
  368. package/src/registry/lib/session-context-provider.tsx +8 -25
  369. package/src/registry/lib/session-context.ts +21 -0
  370. package/src/registry/lib/session-selectors.ts +3 -1
  371. package/src/ui/branding/app-branding-context.ts +20 -0
  372. package/src/ui/branding/app-branding-provider.tsx +5 -27
  373. package/src/ui/capture/field-spec-to-zod.ts +22 -0
  374. package/src/ui/capture/field-spec-validate.ts +147 -0
  375. package/src/ui/capture/field-spec.ts +62 -0
  376. package/src/ui/capture/index.ts +9 -1
  377. package/src/ui/capture/schema-field-input.tsx +141 -15
  378. package/src/ui/capture/submit-values.test.ts +70 -0
  379. package/src/ui/capture/submit-values.ts +63 -0
  380. package/src/ui/capture/use-prefill.ts +4 -1
  381. package/src/ui/capture/use-wizard.ts +2 -2
  382. package/src/ui/capture/wizard.tsx +2 -1
  383. package/src/ui/index.ts +11 -2
  384. package/src/ui/primitives/alert-dialog.tsx +1 -1
  385. package/src/ui/primitives/badge-variants.ts +21 -0
  386. package/src/ui/primitives/badge.tsx +3 -19
  387. package/src/ui/primitives/button-variants.ts +31 -0
  388. package/src/ui/primitives/button.tsx +3 -28
  389. package/src/ui/primitives/form-stepper.tsx +1 -16
  390. package/src/ui/primitives/form-stepper.types.ts +15 -0
  391. package/src/ui/primitives/overflow-tabs.tsx +24 -4
  392. package/src/ui/primitives/preview-device-frame.tsx +1 -1
  393. package/src/ui/primitives/tag-multi-select.tsx +3 -4
  394. package/src/ui/primitives/url-preview-chrome.tsx +102 -0
  395. package/src/vista/GenericCatalogueSurface.tsx +177 -0
  396. package/src/vista/SurfaceFallbackCard.tsx +109 -0
  397. package/src/vista/VistaPanel.tsx +155 -0
  398. package/src/vista/form-payload.spec.ts +139 -0
  399. package/src/vista/html/CustomUiFrame.spec.tsx +125 -0
  400. package/src/vista/html/CustomUiFrame.tsx +204 -0
  401. package/src/vista/html/custom-ui-frame-bridge.spec.ts +191 -0
  402. package/src/vista/html/custom-ui-frame-bridge.ts +92 -0
  403. package/src/vista/html/custom-ui-frame-contract.ts +88 -0
  404. package/src/vista/html/custom-ui-intents.spec.ts +133 -0
  405. package/src/vista/html/custom-ui-intents.ts +151 -0
  406. package/src/vista/html/custom-ui-srcdoc.spec.ts +151 -0
  407. package/src/vista/html/custom-ui-srcdoc.ts +193 -0
  408. package/src/vista/html/index.ts +13 -0
  409. package/src/vista/index.ts +52 -0
  410. package/src/vista/surface-catalogue.spec.ts +140 -0
  411. package/src/vista/surface-catalogue.ts +84 -0
  412. package/src/vista/surface-icons.ts +57 -0
  413. package/src/vista/surface-registry.ts +56 -0
  414. package/src/vista/surfaces/ChartSurface.tsx +34 -0
  415. package/src/vista/surfaces/FilesSurface.tsx +202 -0
  416. package/src/vista/surfaces/FormSurface.tsx +363 -0
  417. package/src/vista/surfaces/HtmlSurface.tsx +43 -0
  418. package/src/vista/surfaces/TableSurface.tsx +99 -0
  419. package/src/vista/surfaces/UrlSurface.tsx +40 -0
  420. package/src/vista/surfaces/blob-surfaces.tsx +129 -0
  421. package/src/vista/surfaces/elicitation-transport.spec.ts +369 -0
  422. package/src/vista/surfaces/elicitation-transport.ts +353 -0
  423. package/src/vista/surfaces/file-surface-utils.ts +62 -0
  424. package/src/vista/surfaces/index.ts +24 -0
  425. package/src/vista/surfaces/register-surfaces.tsx +137 -0
  426. package/src/vista/surfaces/text-surfaces.tsx +119 -0
  427. package/src/vista/use-vista-tabs.ts +188 -0
  428. package/src/vista/vista-code-renderer-context.ts +16 -0
  429. package/src/vista/vista-code-renderer.tsx +34 -0
  430. package/src/vista/vista-file-context.ts +14 -0
  431. package/src/vista/vista-file-source.tsx +140 -0
  432. package/src/vista/vista-form-context.ts +20 -0
  433. package/src/vista/vista-form-host.tsx +53 -0
  434. package/src/vista/vista-model.ts +130 -0
  435. package/src/vista/vista-open-context.tsx +32 -0
  436. package/src/vista/vista-open-state.ts +11 -0
  437. package/src/vista/vista-payloads.ts +512 -0
  438. package/src/vista/vista-tabs-manifest.ts +123 -0
  439. package/src/vista/vista-tools.ts +220 -0
  440. package/dist/lib/biome-host/response-envelope.d.ts +0 -3
  441. package/dist/lib/biome-host/response-envelope.d.ts.map +0 -1
  442. package/dist/lib/biome-host/response-envelope.js +0 -21
  443. package/dist/lib/biome-host/response-envelope.js.map +0 -1
  444. package/src/lib/biome-host/response-envelope.ts +0 -69
@@ -1,6 +1,11 @@
1
1
  import { Registry } from '@xemahq/registry';
2
2
  import { useSyncExternalStore } from 'react';
3
3
 
4
+ import {
5
+ DuplicateContributionPolicy,
6
+ createContributionRegistry,
7
+ } from '../contribution-registry';
8
+
4
9
  import type {
5
10
  FrontendBiome,
6
11
  FrontendBiomeFactory,
@@ -81,7 +86,6 @@ class FrontendBiomeRegistry extends Registry<string, FrontendBiome & { key: stri
81
86
  // because the biome is being torn down anyway.
82
87
  void Promise.resolve(entry.dispose()).catch((error: unknown) => {
83
88
  // Surface for devtools without crashing the unregister flow.
84
- // eslint-disable-next-line no-console
85
89
  console.error(`[xema-ui-kernel] dispose() failed for '${id}':`, error);
86
90
  });
87
91
  }
@@ -153,23 +157,38 @@ class FrontendBiomeRegistry extends Registry<string, FrontendBiome & { key: stri
153
157
  * light up the moment the biome's frontend loads. Duplicate kinds
154
158
  * across biomes are surfaced as a console error and the first
155
159
  * registration wins (deterministic + fail-loud, not silent).
160
+ *
161
+ * Built on the shared `createContributionRegistry` core — the same
162
+ * closed+open two-layer bookkeeping the ChatWidget, reply-variant and
163
+ * Vista-surface registries use. This overlay is rebuilt on every call
164
+ * (it is read during render), so it takes the `FirstWins` duplicate
165
+ * policy rather than `Throw`: a duplicate must not take down a render.
166
+ * `FirstWins` still REQUIRES the `onDuplicate` reporter below — the
167
+ * rejected contribution is never dropped silently.
156
168
  */
157
169
  outputRenderers(): ReadonlyMap<string, OutputRendererContribution> {
158
- const out = new Map<string, OutputRendererContribution>();
159
- if (!this.enabled) return out;
170
+ if (!this.enabled) return new Map<string, OutputRendererContribution>();
171
+ // `onDuplicate` fires SYNCHRONOUSLY from inside `register()`, so this
172
+ // cursor always names the biome whose contribution was rejected — the
173
+ // contribution object itself does not carry its owner.
174
+ let contributingBiomeId = '';
175
+ const overlay = createContributionRegistry<OutputRendererContribution>({
176
+ name: 'biome outputRenderer',
177
+ duplicatePolicy: DuplicateContributionPolicy.FirstWins,
178
+ onDuplicate: (outputKind) => {
179
+ console.error(
180
+ `[xema-ui-kernel] duplicate outputRenderer for kind '${outputKind}' ` +
181
+ `from biome '${contributingBiomeId}'; first registration wins.`,
182
+ );
183
+ },
184
+ });
160
185
  for (const biome of this.list()) {
186
+ contributingBiomeId = biome.id;
161
187
  for (const entry of biome.outputRenderers ?? []) {
162
- if (out.has(entry.outputKind)) {
163
- console.error(
164
- `[xema-ui-kernel] duplicate outputRenderer for kind '${entry.outputKind}' ` +
165
- `from biome '${biome.id}'; first registration wins.`,
166
- );
167
- continue;
168
- }
169
- out.set(entry.outputKind, entry);
188
+ overlay.register(entry.outputKind, entry);
170
189
  }
171
190
  }
172
- return out;
191
+ return overlay.entries();
173
192
  }
174
193
 
175
194
  private notify(): void {
@@ -0,0 +1,39 @@
1
+ import { useHostBridge } from './host-bridge';
2
+
3
+ export interface ProjectScope {
4
+ readonly orgId: string | null;
5
+ readonly projectId: string | null;
6
+ }
7
+
8
+ export interface RequiredProjectScope {
9
+ readonly orgId: string;
10
+ readonly projectId: string;
11
+ }
12
+
13
+ function normalize(value: string | null | undefined): string | null {
14
+ if (typeof value !== 'string') return null;
15
+ const trimmed = value.trim();
16
+ return trimmed.length > 0 ? trimmed : null;
17
+ }
18
+
19
+ export function useProjectScope(): ProjectScope {
20
+ const bridge = useHostBridge();
21
+ const params = bridge.navigation.useRouteParams<{ projectId?: string }>();
22
+ const orgId = normalize(bridge.auth.getOrgId());
23
+ const projectId =
24
+ normalize(params.projectId) ?? normalize(bridge.auth.getProjectId());
25
+ return { orgId, projectId };
26
+ }
27
+
28
+ export function useRequiredScope(): RequiredProjectScope {
29
+ const { orgId, projectId } = useProjectScope();
30
+ if (!orgId || !projectId) {
31
+ throw new Error(
32
+ `[xema-ui-kernel] useRequiredScope() requires both orgId and projectId, ` +
33
+ `got orgId=${orgId ?? 'null'} projectId=${projectId ?? 'null'}. ` +
34
+ `Render this component under <ScopeGuard> or use useProjectScope() for ` +
35
+ `org-scoped surfaces.`,
36
+ );
37
+ }
38
+ return { orgId, projectId };
39
+ }
@@ -13,74 +13,7 @@
13
13
  */
14
14
  import { Fragment, type ReactElement, type ReactNode } from 'react';
15
15
 
16
- import { useHostBridge } from './host-bridge';
17
-
18
- /**
19
- * Normalized active scope. Both fields are `null` when absent (never
20
- * `undefined` and never empty-string) so consumers branch on a single shape.
21
- */
22
- export interface ProjectScope {
23
- readonly orgId: string | null;
24
- readonly projectId: string | null;
25
- }
26
-
27
- /**
28
- * Like {@link ProjectScope} but with both fields guaranteed present — the
29
- * resolved shape {@link useRequiredScope} and {@link ScopeGuard} hand to
30
- * consumers once the scope is known to be complete.
31
- */
32
- export interface RequiredProjectScope {
33
- readonly orgId: string;
34
- readonly projectId: string;
35
- }
36
-
37
- /** Collapse `undefined` / `''` to `null`; trim incidental whitespace. */
38
- function normalize(value: string | null | undefined): string | null {
39
- if (typeof value !== 'string') return null;
40
- const trimmed = value.trim();
41
- return trimmed.length > 0 ? trimmed : null;
42
- }
43
-
44
- /**
45
- * The active `{ orgId, projectId }` for the current biome render.
46
- *
47
- * - `orgId` comes from the host (`bridge.auth.getOrgId()`) — the org is owned
48
- * by the host shell, never carried in a biome URL.
49
- * - `projectId` is read from the matcher-bound route params
50
- * (`bridge.navigation.useRouteParams().projectId`, set on project-scoped
51
- * routes) and falls back to `bridge.auth.getProjectId()` for org-scoped
52
- * surfaces that still have an active project selection.
53
- *
54
- * Both values are null-normalized. This is the single sanctioned scope read —
55
- * biomes MUST NOT re-derive org/project from JWT claims or ad-hoc params.
56
- */
57
- export function useProjectScope(): ProjectScope {
58
- const bridge = useHostBridge();
59
- const params = bridge.navigation.useRouteParams<{ projectId?: string }>();
60
- const orgId = normalize(bridge.auth.getOrgId());
61
- const projectId = normalize(params.projectId) ?? normalize(bridge.auth.getProjectId());
62
- return { orgId, projectId };
63
- }
64
-
65
- /**
66
- * Like {@link useProjectScope} but fail-fast: throws if either `orgId` or
67
- * `projectId` is absent. Use it in project-scoped components that cannot
68
- * render without both — wrap them in {@link ScopeGuard} to render a fallback
69
- * instead of throwing. Returns the {@link RequiredProjectScope} narrowed
70
- * shape so callers skip null checks.
71
- */
72
- export function useRequiredScope(): RequiredProjectScope {
73
- const { orgId, projectId } = useProjectScope();
74
- if (!orgId || !projectId) {
75
- throw new Error(
76
- `[xema-ui-kernel] useRequiredScope() requires both orgId and projectId, ` +
77
- `got orgId=${orgId ?? 'null'} projectId=${projectId ?? 'null'}. ` +
78
- `Render this component under <ScopeGuard> or use useProjectScope() for ` +
79
- `org-scoped surfaces.`,
80
- );
81
- }
82
- return { orgId, projectId };
83
- }
16
+ import { useProjectScope, type ProjectScope } from './biome-scope-hooks';
84
17
 
85
18
  export interface ScopeGuardProps {
86
19
  /**
@@ -18,7 +18,10 @@ import {
18
18
  type UseQueryResult,
19
19
  } from '@tanstack/react-query';
20
20
 
21
- import { useProjectScope, type ProjectScope } from './biome-scope';
21
+ import {
22
+ useProjectScope,
23
+ type ProjectScope,
24
+ } from './biome-scope-hooks';
22
25
 
23
26
  /**
24
27
  * The canonical Xema-platform tenant header. Every platform biome-api reads it
@@ -66,7 +66,7 @@ export function createBiomeOrvalConfig(
66
66
  baseUrl: opts.baseUrl,
67
67
  maxRetries: opts.maxRetries ?? 1,
68
68
  getAuthToken: () => bridge.auth.ensureFreshToken(),
69
- getHeaders: async () => {
69
+ getHeaders: () => {
70
70
  const headers: Record<string, string> = {
71
71
  'X-Correlation-Id': crypto.randomUUID(),
72
72
  };
@@ -75,7 +75,7 @@ export function createBiomeOrvalConfig(
75
75
  // Canonical header read by every Xema-platform service's RequestContext.
76
76
  headers['X-Xema-Org-Id'] = orgId;
77
77
  }
78
- return headers;
78
+ return Promise.resolve(headers);
79
79
  },
80
80
  onUnauthorized: () => bridge.auth.onUnauthorized(),
81
81
  };
@@ -166,7 +166,7 @@ function getOrvalClientErrorMessage(error: OrvalClientErrorLike, fallback: strin
166
166
  const payload: WorkflowErrorEnvelope = (() => {
167
167
  const message = record['message'];
168
168
  if (message && typeof message === 'object') {
169
- return message as WorkflowErrorEnvelope;
169
+ return message;
170
170
  }
171
171
  return {
172
172
  code: typeof record['code'] === 'string' ? record['code'] : undefined,
@@ -16,6 +16,7 @@ export * from './frontend-biome';
16
16
  export * from './host-bridge';
17
17
  export * from './biome-navigation';
18
18
  export * from './biome-scope';
19
+ export * from './biome-scope-hooks';
19
20
  export * from './biome-scoped-query';
20
21
  export * from './biome-builders';
21
22
  export * from './define-web-biome';
@@ -31,6 +32,5 @@ export * from './realtime-hooks';
31
32
  export * from './i18n-port';
32
33
  export * from './i18n-hooks';
33
34
  export * from './errors';
34
- export * from './response-envelope';
35
35
  export * from './use-mutation-with-error-toast';
36
36
  export * from './use-page-state';
@@ -89,7 +89,13 @@ export function useSessionProfileDescriptor(
89
89
  for (const biome of biomeRegistry.list()) {
90
90
  for (const profile of biome.session?.profiles ?? []) {
91
91
  if (profile.key !== profileKey) continue;
92
- if (biomeId != null && profile.biomeId === biomeId) return profile;
92
+ if (
93
+ biomeId !== null &&
94
+ biomeId !== undefined &&
95
+ profile.biomeId === biomeId
96
+ ) {
97
+ return profile;
98
+ }
93
99
  crossPluginMatch ??= profile;
94
100
  }
95
101
  }
@@ -0,0 +1,25 @@
1
+ import { createContext, useContext } from 'react';
2
+
3
+ import type { MeCapabilitiesSnapshot } from './types';
4
+
5
+ export interface CapabilityContextValue {
6
+ readonly snapshot: MeCapabilitiesSnapshot | undefined;
7
+ readonly isLoading: boolean;
8
+ readonly error: unknown;
9
+ }
10
+
11
+ export const CapabilityContext =
12
+ createContext<CapabilityContextValue | null>(null);
13
+ CapabilityContext.displayName = 'CapabilityContext';
14
+
15
+ export function useCapabilityContext(): CapabilityContextValue {
16
+ const ctx = useContext(CapabilityContext);
17
+ if (!ctx) {
18
+ throw new Error(
19
+ '[xema-ui-kernel] useCapability()/useCapabilities() called outside ' +
20
+ '<CapabilityProvider>. The host (or biome shell) must mount the ' +
21
+ 'capability provider above any capability-gated subtree.',
22
+ );
23
+ }
24
+ return ctx;
25
+ }
@@ -1,26 +1,14 @@
1
1
  import { useQuery } from '@tanstack/react-query';
2
- import { createContext, useContext, type ReactNode } from 'react';
2
+ import type { ReactNode } from 'react';
3
3
 
4
4
  import { useHostBridge } from '../biome-host/host-bridge';
5
+ import {
6
+ CapabilityContext,
7
+ type CapabilityContextValue,
8
+ } from './capability-context';
5
9
 
6
10
  import type { CapabilityPort, MeCapabilitiesSnapshot } from './types';
7
11
 
8
- /**
9
- * The value exposed by {@link CapabilityProvider} through React context.
10
- * `snapshot` is `undefined` until the first successful fetch; `isLoading`
11
- * and `error` let callers distinguish "still unknown" and "fetch failed"
12
- * from "explicitly denied" — the gate hook must NOT treat an errored fetch
13
- * as "no capabilities".
14
- */
15
- export interface CapabilityContextValue {
16
- readonly snapshot: MeCapabilitiesSnapshot | undefined;
17
- readonly isLoading: boolean;
18
- readonly error: unknown;
19
- }
20
-
21
- const CapabilityContext = createContext<CapabilityContextValue | null>(null);
22
- CapabilityContext.displayName = 'CapabilityContext';
23
-
24
12
  /**
25
13
  * Stable query-key root for the capability snapshot. Folded with the active
26
14
  * org so each org caches independently and a re-fetch fires when the org
@@ -76,20 +64,3 @@ export function CapabilityProvider(props: CapabilityProviderProps): JSX.Element
76
64
 
77
65
  return <CapabilityContext.Provider value={value}>{props.children}</CapabilityContext.Provider>;
78
66
  }
79
-
80
- /**
81
- * Internal accessor for the capability context. Throws fail-fast if used
82
- * outside {@link CapabilityProvider} — a missing provider is a wiring bug,
83
- * not a "deny everything" condition.
84
- */
85
- export function useCapabilityContext(): CapabilityContextValue {
86
- const ctx = useContext(CapabilityContext);
87
- if (!ctx) {
88
- throw new Error(
89
- '[xema-ui-kernel] useCapability()/useCapabilities() called outside ' +
90
- '<CapabilityProvider>. The host (or biome shell) must mount the ' +
91
- 'capability provider above any capability-gated subtree.',
92
- );
93
- }
94
- return ctx;
95
- }
@@ -12,5 +12,6 @@
12
12
  * import { CapabilityProvider, useCapability } from '@xemahq/ui-kernel';
13
13
  */
14
14
  export * from './types';
15
+ export * from './capability-context';
15
16
  export * from './capability-provider';
16
17
  export * from './use-capability';
@@ -1,6 +1,9 @@
1
1
  import type { ExecutionEnvironmentKind } from '@xemahq/kernel-contracts/execution-environment';
2
2
 
3
- import { useCapabilityContext, type CapabilityContextValue } from './capability-provider';
3
+ import {
4
+ useCapabilityContext,
5
+ type CapabilityContextValue,
6
+ } from './capability-context';
4
7
  import type { MeCapabilitiesSnapshot } from './types';
5
8
 
6
9
  export interface UseCapabilityOptions {
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Guards the shared closed+open contribution registry core — the ONE
3
+ * implementation behind the ChatWidget, biome output-renderer, reply-variant
4
+ * and Vista-surface planes. Every property the hand-written copies had must
5
+ * survive the extraction.
6
+ *
7
+ * Runs under the host-web vitest mirror (this package carries no vitest dep).
8
+ */
9
+ import { describe, expect, it, vi } from 'vitest';
10
+
11
+ import {
12
+ ContributionFallbackReason,
13
+ DuplicateContributionPolicy,
14
+ createContributionRegistry,
15
+ createRenderableContributionRegistry,
16
+ } from './index';
17
+
18
+ function makeRegistry(): ReturnType<typeof createContributionRegistry<string>> {
19
+ return createContributionRegistry<string>({
20
+ name: 'test',
21
+ reservedKinds: ['plan', 'diff'],
22
+ });
23
+ }
24
+
25
+ describe('createContributionRegistry', () => {
26
+ it('resolves first-party before open, and lists both', () => {
27
+ const registry = makeRegistry();
28
+ registry.registerFirstParty('plan', 'builtin-plan');
29
+ registry.register('acme:gantt', 'acme-gantt');
30
+ expect(registry.get('plan')).toBe('builtin-plan');
31
+ expect(registry.get('acme:gantt')).toBe('acme-gantt');
32
+ expect(registry.has('nope')).toBe(false);
33
+ expect(registry.list()).toEqual(['acme:gantt', 'plan']);
34
+ expect(registry.listOpen()).toEqual(['acme:gantt']);
35
+ expect([...registry.entries().keys()].sort()).toEqual(['acme:gantt', 'plan']);
36
+ });
37
+
38
+ it('refuses to let the open set claim a reserved first-party kind', () => {
39
+ const registry = makeRegistry();
40
+ expect(registry.isReserved('diff')).toBe(true);
41
+ expect(() => registry.register('diff', 'hijack')).toThrow(/reserved/);
42
+ // The namespaced form is fine.
43
+ expect(() => registry.register('acme:diff', 'ok')).not.toThrow();
44
+ });
45
+
46
+ it('is idempotent for the identical entry and throws for a different one', () => {
47
+ const registry = makeRegistry();
48
+ registry.register('acme:gantt', 'entry');
49
+ expect(() => registry.register('acme:gantt', 'entry')).not.toThrow();
50
+ expect(() => registry.register('acme:gantt', 'other')).toThrow(
51
+ /already registered/,
52
+ );
53
+ registry.registerFirstParty('plan', 'builtin');
54
+ expect(() => registry.registerFirstParty('plan', 'builtin')).not.toThrow();
55
+ expect(() => registry.registerFirstParty('plan', 'other')).toThrow(
56
+ /already registered/,
57
+ );
58
+ });
59
+
60
+ it('resets only the OPEN set', () => {
61
+ const registry = makeRegistry();
62
+ registry.registerFirstParty('plan', 'builtin');
63
+ registry.register('acme:gantt', 'entry');
64
+ registry.__resetOpenForTests();
65
+ expect(registry.list()).toEqual(['plan']);
66
+ });
67
+
68
+ it('reports — never swallows — a duplicate under FirstWins', () => {
69
+ const onDuplicate = vi.fn();
70
+ const registry = createContributionRegistry<string>({
71
+ name: 'overlay',
72
+ duplicatePolicy: DuplicateContributionPolicy.FirstWins,
73
+ onDuplicate,
74
+ });
75
+ registry.register('k', 'first');
76
+ registry.register('k', 'second');
77
+ expect(registry.get('k')).toBe('first');
78
+ expect(onDuplicate).toHaveBeenCalledWith('k', 'first', 'second');
79
+ });
80
+
81
+ it('refuses FirstWins without a reporter', () => {
82
+ expect(() =>
83
+ createContributionRegistry<string>({
84
+ name: 'overlay',
85
+ duplicatePolicy: DuplicateContributionPolicy.FirstWins,
86
+ }),
87
+ ).toThrow(/onDuplicate/);
88
+ });
89
+ });
90
+
91
+ describe('createRenderableContributionRegistry', () => {
92
+ const registry = createRenderableContributionRegistry<string>({
93
+ name: 'renderable',
94
+ reservedKinds: ['plan'],
95
+ });
96
+ registry.registerFirstParty<{ label: string }>('plan', {
97
+ version: 2,
98
+ validate: (payload) =>
99
+ typeof payload === 'object' &&
100
+ payload !== null &&
101
+ typeof (payload as { label?: unknown }).label === 'string'
102
+ ? (payload as { label: string })
103
+ : null,
104
+ render: (payload) => `plan:${payload.label}`,
105
+ });
106
+
107
+ it('renders a matching envelope', () => {
108
+ const resolution = registry.resolve({
109
+ kind: 'plan',
110
+ version: 1,
111
+ payload: { label: 'ship it' },
112
+ });
113
+ expect(resolution).toEqual({
114
+ matched: true,
115
+ kind: 'plan',
116
+ version: 2,
117
+ rendered: 'plan:ship it',
118
+ });
119
+ });
120
+
121
+ it('never throws — it returns a typed reason instead', () => {
122
+ expect(
123
+ registry.resolve({ kind: 'nope', version: 1, payload: {} }),
124
+ ).toMatchObject({
125
+ matched: false,
126
+ reason: ContributionFallbackReason.UnknownKind,
127
+ });
128
+ expect(
129
+ registry.resolve({ kind: 'plan', version: 99, payload: {} }),
130
+ ).toMatchObject({
131
+ matched: false,
132
+ reason: ContributionFallbackReason.UnsupportedVersion,
133
+ });
134
+ expect(
135
+ registry.resolve({ kind: 'plan', version: 1, payload: { label: 7 } }),
136
+ ).toMatchObject({
137
+ matched: false,
138
+ reason: ContributionFallbackReason.InvalidPayload,
139
+ });
140
+ });
141
+ });