@frontmcp/ui 0.5.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 (393) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +438 -0
  3. package/package.json +147 -0
  4. package/src/adapters/index.d.ts +10 -0
  5. package/src/adapters/index.js +18 -0
  6. package/src/adapters/index.js.map +1 -0
  7. package/src/adapters/platform-meta.d.ts +165 -0
  8. package/src/adapters/platform-meta.js +310 -0
  9. package/src/adapters/platform-meta.js.map +1 -0
  10. package/src/base-template/bridge.d.ts +89 -0
  11. package/src/base-template/bridge.js +452 -0
  12. package/src/base-template/bridge.js.map +1 -0
  13. package/src/base-template/default-base-template.d.ts +91 -0
  14. package/src/base-template/default-base-template.js +435 -0
  15. package/src/base-template/default-base-template.js.map +1 -0
  16. package/src/base-template/index.d.ts +14 -0
  17. package/src/base-template/index.js +30 -0
  18. package/src/base-template/index.js.map +1 -0
  19. package/src/base-template/polyfills.d.ts +30 -0
  20. package/src/base-template/polyfills.js +190 -0
  21. package/src/base-template/polyfills.js.map +1 -0
  22. package/src/base-template/theme-styles.d.ts +73 -0
  23. package/src/base-template/theme-styles.js +95 -0
  24. package/src/base-template/theme-styles.js.map +1 -0
  25. package/src/bridge/adapters/base-adapter.d.ts +103 -0
  26. package/src/bridge/adapters/base-adapter.js +314 -0
  27. package/src/bridge/adapters/base-adapter.js.map +1 -0
  28. package/src/bridge/adapters/claude.adapter.d.ts +66 -0
  29. package/src/bridge/adapters/claude.adapter.js +145 -0
  30. package/src/bridge/adapters/claude.adapter.js.map +1 -0
  31. package/src/bridge/adapters/ext-apps.adapter.d.ts +142 -0
  32. package/src/bridge/adapters/ext-apps.adapter.js +416 -0
  33. package/src/bridge/adapters/ext-apps.adapter.js.map +1 -0
  34. package/src/bridge/adapters/gemini.adapter.d.ts +63 -0
  35. package/src/bridge/adapters/gemini.adapter.js +160 -0
  36. package/src/bridge/adapters/gemini.adapter.js.map +1 -0
  37. package/src/bridge/adapters/generic.adapter.d.ts +55 -0
  38. package/src/bridge/adapters/generic.adapter.js +108 -0
  39. package/src/bridge/adapters/generic.adapter.js.map +1 -0
  40. package/src/bridge/adapters/index.d.ts +25 -0
  41. package/src/bridge/adapters/index.js +65 -0
  42. package/src/bridge/adapters/index.js.map +1 -0
  43. package/src/bridge/adapters/openai.adapter.d.ts +64 -0
  44. package/src/bridge/adapters/openai.adapter.js +194 -0
  45. package/src/bridge/adapters/openai.adapter.js.map +1 -0
  46. package/src/bridge/core/adapter-registry.d.ts +121 -0
  47. package/src/bridge/core/adapter-registry.js +271 -0
  48. package/src/bridge/core/adapter-registry.js.map +1 -0
  49. package/src/bridge/core/bridge-factory.d.ts +198 -0
  50. package/src/bridge/core/bridge-factory.js +428 -0
  51. package/src/bridge/core/bridge-factory.js.map +1 -0
  52. package/src/bridge/core/index.d.ts +9 -0
  53. package/src/bridge/core/index.js +22 -0
  54. package/src/bridge/core/index.js.map +1 -0
  55. package/src/bridge/index.d.ts +61 -0
  56. package/src/bridge/index.js +94 -0
  57. package/src/bridge/index.js.map +1 -0
  58. package/src/bridge/runtime/iife-generator.d.ts +61 -0
  59. package/src/bridge/runtime/iife-generator.js +940 -0
  60. package/src/bridge/runtime/iife-generator.js.map +1 -0
  61. package/src/bridge/runtime/index.d.ts +8 -0
  62. package/src/bridge/runtime/index.js +16 -0
  63. package/src/bridge/runtime/index.js.map +1 -0
  64. package/src/bridge/types.d.ts +385 -0
  65. package/src/bridge/types.js +11 -0
  66. package/src/bridge/types.js.map +1 -0
  67. package/src/build/cdn-resources.d.ts +140 -0
  68. package/src/build/cdn-resources.js +314 -0
  69. package/src/build/cdn-resources.js.map +1 -0
  70. package/src/build/index.d.ts +294 -0
  71. package/src/build/index.js +325 -0
  72. package/src/build/index.js.map +1 -0
  73. package/src/build/widget-manifest.d.ts +212 -0
  74. package/src/build/widget-manifest.js +652 -0
  75. package/src/build/widget-manifest.js.map +1 -0
  76. package/src/bundler/bundler.d.ts +110 -0
  77. package/src/bundler/bundler.js +432 -0
  78. package/src/bundler/bundler.js.map +1 -0
  79. package/src/bundler/cache.d.ts +172 -0
  80. package/src/bundler/cache.js +250 -0
  81. package/src/bundler/cache.js.map +1 -0
  82. package/src/bundler/index.d.ts +41 -0
  83. package/src/bundler/index.js +73 -0
  84. package/src/bundler/index.js.map +1 -0
  85. package/src/bundler/sandbox/enclave-adapter.d.ts +120 -0
  86. package/src/bundler/sandbox/enclave-adapter.js +339 -0
  87. package/src/bundler/sandbox/enclave-adapter.js.map +1 -0
  88. package/src/bundler/sandbox/executor.d.ts +13 -0
  89. package/src/bundler/sandbox/executor.js +22 -0
  90. package/src/bundler/sandbox/executor.js.map +1 -0
  91. package/src/bundler/sandbox/policy.d.ts +61 -0
  92. package/src/bundler/sandbox/policy.js +238 -0
  93. package/src/bundler/sandbox/policy.js.map +1 -0
  94. package/src/bundler/types.d.ts +347 -0
  95. package/src/bundler/types.js +132 -0
  96. package/src/bundler/types.js.map +1 -0
  97. package/src/components/alert.d.ts +71 -0
  98. package/src/components/alert.js +189 -0
  99. package/src/components/alert.js.map +1 -0
  100. package/src/components/alert.schema.d.ts +114 -0
  101. package/src/components/alert.schema.js +105 -0
  102. package/src/components/alert.schema.js.map +1 -0
  103. package/src/components/avatar.d.ts +76 -0
  104. package/src/components/avatar.js +176 -0
  105. package/src/components/avatar.js.map +1 -0
  106. package/src/components/avatar.schema.d.ts +169 -0
  107. package/src/components/avatar.schema.js +103 -0
  108. package/src/components/avatar.schema.js.map +1 -0
  109. package/src/components/badge.d.ts +70 -0
  110. package/src/components/badge.js +149 -0
  111. package/src/components/badge.js.map +1 -0
  112. package/src/components/badge.schema.d.ts +109 -0
  113. package/src/components/badge.schema.js +96 -0
  114. package/src/components/badge.schema.js.map +1 -0
  115. package/src/components/button.d.ts +111 -0
  116. package/src/components/button.js +336 -0
  117. package/src/components/button.js.map +1 -0
  118. package/src/components/button.schema.d.ts +148 -0
  119. package/src/components/button.schema.js +121 -0
  120. package/src/components/button.schema.js.map +1 -0
  121. package/src/components/card.d.ts +60 -0
  122. package/src/components/card.js +117 -0
  123. package/src/components/card.js.map +1 -0
  124. package/src/components/card.schema.d.ts +113 -0
  125. package/src/components/card.schema.js +98 -0
  126. package/src/components/card.schema.js.map +1 -0
  127. package/src/components/form.d.ts +239 -0
  128. package/src/components/form.js +420 -0
  129. package/src/components/form.js.map +1 -0
  130. package/src/components/form.schema.d.ts +441 -0
  131. package/src/components/form.schema.js +406 -0
  132. package/src/components/form.schema.js.map +1 -0
  133. package/src/components/index.d.ts +29 -0
  134. package/src/components/index.js +98 -0
  135. package/src/components/index.js.map +1 -0
  136. package/src/components/list.d.ts +127 -0
  137. package/src/components/list.js +279 -0
  138. package/src/components/list.js.map +1 -0
  139. package/src/components/list.schema.d.ts +134 -0
  140. package/src/components/list.schema.js +168 -0
  141. package/src/components/list.schema.js.map +1 -0
  142. package/src/components/modal.d.ts +111 -0
  143. package/src/components/modal.js +260 -0
  144. package/src/components/modal.js.map +1 -0
  145. package/src/components/modal.schema.d.ts +186 -0
  146. package/src/components/modal.schema.js +167 -0
  147. package/src/components/modal.schema.js.map +1 -0
  148. package/src/components/table.d.ts +105 -0
  149. package/src/components/table.js +283 -0
  150. package/src/components/table.js.map +1 -0
  151. package/src/components/table.schema.d.ts +159 -0
  152. package/src/components/table.schema.js +173 -0
  153. package/src/components/table.schema.js.map +1 -0
  154. package/src/handlebars/helpers.d.ts +348 -0
  155. package/src/handlebars/helpers.js +605 -0
  156. package/src/handlebars/helpers.js.map +1 -0
  157. package/src/handlebars/index.d.ts +193 -0
  158. package/src/handlebars/index.js +350 -0
  159. package/src/handlebars/index.js.map +1 -0
  160. package/src/index.d.ts +50 -0
  161. package/src/index.js +192 -0
  162. package/src/index.js.map +1 -0
  163. package/src/layouts/base.d.ts +88 -0
  164. package/src/layouts/base.js +227 -0
  165. package/src/layouts/base.js.map +1 -0
  166. package/src/layouts/index.d.ts +7 -0
  167. package/src/layouts/index.js +25 -0
  168. package/src/layouts/index.js.map +1 -0
  169. package/src/layouts/presets.d.ts +133 -0
  170. package/src/layouts/presets.js +277 -0
  171. package/src/layouts/presets.js.map +1 -0
  172. package/src/pages/consent.d.ts +116 -0
  173. package/src/pages/consent.js +218 -0
  174. package/src/pages/consent.js.map +1 -0
  175. package/src/pages/error.d.ts +100 -0
  176. package/src/pages/error.js +263 -0
  177. package/src/pages/error.js.map +1 -0
  178. package/src/pages/index.d.ts +8 -0
  179. package/src/pages/index.js +27 -0
  180. package/src/pages/index.js.map +1 -0
  181. package/src/react/Alert.d.ts +101 -0
  182. package/src/react/Alert.js +51 -0
  183. package/src/react/Alert.js.map +1 -0
  184. package/src/react/Badge.d.ts +100 -0
  185. package/src/react/Badge.js +55 -0
  186. package/src/react/Badge.js.map +1 -0
  187. package/src/react/Button.d.ts +108 -0
  188. package/src/react/Button.js +52 -0
  189. package/src/react/Button.js.map +1 -0
  190. package/src/react/Card.d.ts +103 -0
  191. package/src/react/Card.js +55 -0
  192. package/src/react/Card.js.map +1 -0
  193. package/src/react/hooks/context.d.ts +178 -0
  194. package/src/react/hooks/context.js +287 -0
  195. package/src/react/hooks/context.js.map +1 -0
  196. package/src/react/hooks/index.d.ts +41 -0
  197. package/src/react/hooks/index.js +61 -0
  198. package/src/react/hooks/index.js.map +1 -0
  199. package/src/react/hooks/tools.d.ts +283 -0
  200. package/src/react/hooks/tools.js +465 -0
  201. package/src/react/hooks/tools.js.map +1 -0
  202. package/src/react/index.d.ts +80 -0
  203. package/src/react/index.js +113 -0
  204. package/src/react/index.js.map +1 -0
  205. package/src/react/types.d.ts +105 -0
  206. package/src/react/types.js +12 -0
  207. package/src/react/types.js.map +1 -0
  208. package/src/react/utils.d.ts +42 -0
  209. package/src/react/utils.js +99 -0
  210. package/src/react/utils.js.map +1 -0
  211. package/src/registry/index.d.ts +45 -0
  212. package/src/registry/index.js +67 -0
  213. package/src/registry/index.js.map +1 -0
  214. package/src/registry/render-template.d.ts +86 -0
  215. package/src/registry/render-template.js +239 -0
  216. package/src/registry/render-template.js.map +1 -0
  217. package/src/registry/tool-ui.registry.d.ts +260 -0
  218. package/src/registry/tool-ui.registry.js +438 -0
  219. package/src/registry/tool-ui.registry.js.map +1 -0
  220. package/src/registry/uri-utils.d.ts +55 -0
  221. package/src/registry/uri-utils.js +97 -0
  222. package/src/registry/uri-utils.js.map +1 -0
  223. package/src/render/index.d.ts +7 -0
  224. package/src/render/index.js +14 -0
  225. package/src/render/index.js.map +1 -0
  226. package/src/render/prerender.d.ts +56 -0
  227. package/src/render/prerender.js +98 -0
  228. package/src/render/prerender.js.map +1 -0
  229. package/src/renderers/cache.d.ts +144 -0
  230. package/src/renderers/cache.js +240 -0
  231. package/src/renderers/cache.js.map +1 -0
  232. package/src/renderers/html.renderer.d.ts +122 -0
  233. package/src/renderers/html.renderer.js +204 -0
  234. package/src/renderers/html.renderer.js.map +1 -0
  235. package/src/renderers/index.d.ts +35 -0
  236. package/src/renderers/index.js +70 -0
  237. package/src/renderers/index.js.map +1 -0
  238. package/src/renderers/mdx.renderer.d.ts +119 -0
  239. package/src/renderers/mdx.renderer.js +305 -0
  240. package/src/renderers/mdx.renderer.js.map +1 -0
  241. package/src/renderers/react.renderer.d.ts +95 -0
  242. package/src/renderers/react.renderer.js +260 -0
  243. package/src/renderers/react.renderer.js.map +1 -0
  244. package/src/renderers/registry.d.ts +133 -0
  245. package/src/renderers/registry.js +232 -0
  246. package/src/renderers/registry.js.map +1 -0
  247. package/src/renderers/types.d.ts +341 -0
  248. package/src/renderers/types.js +9 -0
  249. package/src/renderers/types.js.map +1 -0
  250. package/src/renderers/utils/detect.d.ts +106 -0
  251. package/src/renderers/utils/detect.js +267 -0
  252. package/src/renderers/utils/detect.js.map +1 -0
  253. package/src/renderers/utils/hash.d.ts +39 -0
  254. package/src/renderers/utils/hash.js +75 -0
  255. package/src/renderers/utils/hash.js.map +1 -0
  256. package/src/renderers/utils/index.d.ts +8 -0
  257. package/src/renderers/utils/index.js +28 -0
  258. package/src/renderers/utils/index.js.map +1 -0
  259. package/src/renderers/utils/transpiler.d.ts +88 -0
  260. package/src/renderers/utils/transpiler.js +215 -0
  261. package/src/renderers/utils/transpiler.js.map +1 -0
  262. package/src/runtime/adapters/html.adapter.d.ts +58 -0
  263. package/src/runtime/adapters/html.adapter.js +131 -0
  264. package/src/runtime/adapters/html.adapter.js.map +1 -0
  265. package/src/runtime/adapters/index.d.ts +25 -0
  266. package/src/runtime/adapters/index.js +54 -0
  267. package/src/runtime/adapters/index.js.map +1 -0
  268. package/src/runtime/adapters/mdx.adapter.d.ts +72 -0
  269. package/src/runtime/adapters/mdx.adapter.js +241 -0
  270. package/src/runtime/adapters/mdx.adapter.js.map +1 -0
  271. package/src/runtime/adapters/react.adapter.d.ts +69 -0
  272. package/src/runtime/adapters/react.adapter.js +245 -0
  273. package/src/runtime/adapters/react.adapter.js.map +1 -0
  274. package/src/runtime/adapters/types.d.ts +94 -0
  275. package/src/runtime/adapters/types.js +11 -0
  276. package/src/runtime/adapters/types.js.map +1 -0
  277. package/src/runtime/csp.d.ts +37 -0
  278. package/src/runtime/csp.js +140 -0
  279. package/src/runtime/csp.js.map +1 -0
  280. package/src/runtime/index.d.ts +16 -0
  281. package/src/runtime/index.js +72 -0
  282. package/src/runtime/index.js.map +1 -0
  283. package/src/runtime/mcp-bridge.d.ts +100 -0
  284. package/src/runtime/mcp-bridge.js +581 -0
  285. package/src/runtime/mcp-bridge.js.map +1 -0
  286. package/src/runtime/renderer-runtime.d.ts +132 -0
  287. package/src/runtime/renderer-runtime.js +389 -0
  288. package/src/runtime/renderer-runtime.js.map +1 -0
  289. package/src/runtime/sanitizer.d.ts +171 -0
  290. package/src/runtime/sanitizer.js +318 -0
  291. package/src/runtime/sanitizer.js.map +1 -0
  292. package/src/runtime/types.d.ts +414 -0
  293. package/src/runtime/types.js +12 -0
  294. package/src/runtime/types.js.map +1 -0
  295. package/src/runtime/wrapper.d.ts +375 -0
  296. package/src/runtime/wrapper.js +1793 -0
  297. package/src/runtime/wrapper.js.map +1 -0
  298. package/src/styles/index.d.ts +7 -0
  299. package/src/styles/index.js +11 -0
  300. package/src/styles/index.js.map +1 -0
  301. package/src/styles/variants.d.ts +50 -0
  302. package/src/styles/variants.js +175 -0
  303. package/src/styles/variants.js.map +1 -0
  304. package/src/theme/cdn.d.ts +194 -0
  305. package/src/theme/cdn.js +375 -0
  306. package/src/theme/cdn.js.map +1 -0
  307. package/src/theme/index.d.ts +17 -0
  308. package/src/theme/index.js +57 -0
  309. package/src/theme/index.js.map +1 -0
  310. package/src/theme/platforms.d.ts +106 -0
  311. package/src/theme/platforms.js +161 -0
  312. package/src/theme/platforms.js.map +1 -0
  313. package/src/theme/presets/github-openai.d.ts +49 -0
  314. package/src/theme/presets/github-openai.js +189 -0
  315. package/src/theme/presets/github-openai.js.map +1 -0
  316. package/src/theme/presets/index.d.ts +10 -0
  317. package/src/theme/presets/index.js +17 -0
  318. package/src/theme/presets/index.js.map +1 -0
  319. package/src/theme/theme.d.ts +395 -0
  320. package/src/theme/theme.js +332 -0
  321. package/src/theme/theme.js.map +1 -0
  322. package/src/tool-template/builder.d.ts +212 -0
  323. package/src/tool-template/builder.js +397 -0
  324. package/src/tool-template/builder.js.map +1 -0
  325. package/src/tool-template/index.d.ts +15 -0
  326. package/src/tool-template/index.js +38 -0
  327. package/src/tool-template/index.js.map +1 -0
  328. package/src/types/index.d.ts +13 -0
  329. package/src/types/index.js +26 -0
  330. package/src/types/index.js.map +1 -0
  331. package/src/types/ui-config.d.ts +357 -0
  332. package/src/types/ui-config.js +12 -0
  333. package/src/types/ui-config.js.map +1 -0
  334. package/src/types/ui-runtime.d.ts +965 -0
  335. package/src/types/ui-runtime.js +117 -0
  336. package/src/types/ui-runtime.js.map +1 -0
  337. package/src/validation/error-box.d.ts +55 -0
  338. package/src/validation/error-box.js +75 -0
  339. package/src/validation/error-box.js.map +1 -0
  340. package/src/validation/index.d.ts +12 -0
  341. package/src/validation/index.js +21 -0
  342. package/src/validation/index.js.map +1 -0
  343. package/src/validation/wrapper.d.ts +96 -0
  344. package/src/validation/wrapper.js +117 -0
  345. package/src/validation/wrapper.js.map +1 -0
  346. package/src/web-components/core/attribute-parser.d.ts +85 -0
  347. package/src/web-components/core/attribute-parser.js +189 -0
  348. package/src/web-components/core/attribute-parser.js.map +1 -0
  349. package/src/web-components/core/base-element.d.ts +197 -0
  350. package/src/web-components/core/base-element.js +289 -0
  351. package/src/web-components/core/base-element.js.map +1 -0
  352. package/src/web-components/core/index.d.ts +8 -0
  353. package/src/web-components/core/index.js +18 -0
  354. package/src/web-components/core/index.js.map +1 -0
  355. package/src/web-components/elements/fmcp-alert.d.ts +45 -0
  356. package/src/web-components/elements/fmcp-alert.js +93 -0
  357. package/src/web-components/elements/fmcp-alert.js.map +1 -0
  358. package/src/web-components/elements/fmcp-badge.d.ts +46 -0
  359. package/src/web-components/elements/fmcp-badge.js +99 -0
  360. package/src/web-components/elements/fmcp-badge.js.map +1 -0
  361. package/src/web-components/elements/fmcp-button.d.ts +124 -0
  362. package/src/web-components/elements/fmcp-button.js +233 -0
  363. package/src/web-components/elements/fmcp-button.js.map +1 -0
  364. package/src/web-components/elements/fmcp-card.d.ts +52 -0
  365. package/src/web-components/elements/fmcp-card.js +115 -0
  366. package/src/web-components/elements/fmcp-card.js.map +1 -0
  367. package/src/web-components/elements/fmcp-input.d.ts +95 -0
  368. package/src/web-components/elements/fmcp-input.js +248 -0
  369. package/src/web-components/elements/fmcp-input.js.map +1 -0
  370. package/src/web-components/elements/fmcp-select.d.ts +99 -0
  371. package/src/web-components/elements/fmcp-select.js +243 -0
  372. package/src/web-components/elements/fmcp-select.js.map +1 -0
  373. package/src/web-components/elements/index.d.ts +12 -0
  374. package/src/web-components/elements/index.js +34 -0
  375. package/src/web-components/elements/index.js.map +1 -0
  376. package/src/web-components/index.d.ts +49 -0
  377. package/src/web-components/index.js +75 -0
  378. package/src/web-components/index.js.map +1 -0
  379. package/src/web-components/register.d.ts +56 -0
  380. package/src/web-components/register.js +80 -0
  381. package/src/web-components/register.js.map +1 -0
  382. package/src/web-components/types.d.ts +121 -0
  383. package/src/web-components/types.js +25 -0
  384. package/src/web-components/types.js.map +1 -0
  385. package/src/widgets/index.d.ts +7 -0
  386. package/src/widgets/index.js +24 -0
  387. package/src/widgets/index.js.map +1 -0
  388. package/src/widgets/progress.d.ts +132 -0
  389. package/src/widgets/progress.js +303 -0
  390. package/src/widgets/progress.js.map +1 -0
  391. package/src/widgets/resource.d.ts +162 -0
  392. package/src/widgets/resource.js +340 -0
  393. package/src/widgets/resource.js.map +1 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform-meta.js","sourceRoot":"","sources":["../../../src/adapters/platform-meta.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Platform Metadata Adapters\n *\n * Build platform-specific _meta fields for tool UI responses.\n * Adapts the UI configuration to the format expected by each\n * AI platform (OpenAI, Claude, Gemini, etc.).\n *\n * This module is SDK-independent and can be used by external systems\n * like AgentLink without requiring @frontmcp/sdk.\n *\n * @packageDocumentation\n */\n\nimport type { UITemplateConfig, UIContentSecurityPolicy } from '../types';\n\n// ============================================\n// Platform Types\n// ============================================\n\n/**\n * Supported AI platform types.\n * Used to determine which metadata format to generate.\n */\nexport type AIPlatformType =\n | 'openai'\n | 'claude'\n | 'gemini'\n | 'cursor'\n | 'continue'\n | 'cody'\n | 'generic-mcp'\n | 'ext-apps'\n | 'unknown';\n\n// ============================================\n// UI Metadata\n// ============================================\n\n/**\n * UI metadata to include in tool response _meta field.\n * Contains both universal fields and platform-specific annotations.\n */\nexport interface UIMetadata {\n // Universal fields\n /** Inline rendered HTML (universal) */\n 'ui/html'?: string;\n /** MIME type for the HTML content */\n 'ui/mimeType'?: string;\n /** Widget token for authenticated operations */\n 'ui/widgetToken'?: string;\n /** Direct URL to widget (for direct-url serving mode) */\n 'ui/directUrl'?: string;\n\n // Widget manifest fields (new)\n /** Renderer type for the widget (html, react, mdx, markdown, auto) */\n 'ui/type'?: string;\n /** Manifest URI for accessing widget configuration */\n 'ui/manifestUri'?: string;\n /** Hash of the widget content for cache validation */\n 'ui/contentHash'?: string;\n /** Required renderer assets for lazy loading */\n 'ui/requiredRenderers'?: string[];\n\n // OpenAI-specific fields\n /** OpenAI: Resource URI for widget template */\n 'openai/outputTemplate'?: string;\n /** OpenAI: Whether widget can invoke tools */\n 'openai/widgetAccessible'?: boolean;\n /** OpenAI: Whether tool result can produce a widget (CRITICAL for ChatGPT) */\n 'openai/resultCanProduceWidget'?: boolean;\n /** OpenAI: CSP configuration */\n 'openai/widgetCSP'?: {\n connect_domains?: string[];\n resource_domains?: string[];\n };\n /** OpenAI: Display mode preference */\n 'openai/displayMode'?: string;\n /** OpenAI: Widget description */\n 'openai/widgetDescription'?: string;\n /** OpenAI: Status text while tool is executing */\n 'openai/toolInvocation/invoking'?: string;\n /** OpenAI: Status text after tool execution completes */\n 'openai/toolInvocation/invoked'?: string;\n\n // Claude-specific fields\n /** Claude: Widget description */\n 'claude/widgetDescription'?: string;\n\n // Gemini-specific fields\n /** Gemini: Widget description */\n 'gemini/widgetDescription'?: string;\n\n // IDE-specific fields (Cursor, Continue, Cody)\n /** IDE: Resource URI for widget template */\n 'ide/outputTemplate'?: string;\n /** IDE: Widget description */\n 'ide/widgetDescription'?: string;\n\n // MCP Apps (ext-apps) fields - per specification\n /** MCP Apps: Resource URI for UI template */\n 'ui/resourceUri'?: string;\n /** MCP Apps: CSP configuration */\n 'ui/csp'?: {\n connectDomains?: string[];\n resourceDomains?: string[];\n };\n /** MCP Apps: Dedicated sandbox domain */\n 'ui/domain'?: string;\n /** MCP Apps: Whether to show border around UI */\n 'ui/prefersBorder'?: boolean;\n /** MCP Apps: Display mode */\n 'ui/displayMode'?: 'inline' | 'fullscreen' | 'pip';\n\n /** Allow additional platform-specific fields */\n [key: string]: unknown;\n}\n\n// ============================================\n// Build Options\n// ============================================\n\n/**\n * Options for building UI metadata.\n */\nexport interface BuildUIMetaOptions<In = unknown, Out = unknown> {\n /** Tool UI configuration */\n uiConfig: UITemplateConfig<In, Out>;\n /** Detected platform type */\n platformType: AIPlatformType;\n /** Rendered HTML content */\n html: string;\n /** Widget access token */\n token?: string;\n /** Direct URL for widget serving */\n directUrl?: string;\n /** Renderer type for the widget (html, react, mdx, markdown, auto) */\n rendererType?: string;\n /** Hash of the widget content for cache validation */\n contentHash?: string;\n /** Manifest URI for accessing widget configuration */\n manifestUri?: string;\n}\n\n// ============================================\n// Main Builder Function\n// ============================================\n\n/**\n * Build platform-specific UI metadata for tool response.\n *\n * For inline serving mode (default), HTML is embedded directly in `_meta['ui/html']`.\n * For static mode, the static widget URI is provided in tools/list\n * and the tool response contains only structured data.\n *\n * @example\n * ```typescript\n * import { buildUIMeta } from '@frontmcp/ui/adapters';\n *\n * const meta = buildUIMeta({\n * uiConfig: { template: (ctx) => `<div>${ctx.output.value}</div>` },\n * platformType: 'openai',\n * html: '<div>Hello World</div>',\n * });\n * ```\n */\nexport function buildUIMeta<In = unknown, Out = unknown>(options: BuildUIMetaOptions<In, Out>): UIMetadata {\n const { uiConfig, platformType, html, token, directUrl, rendererType, contentHash, manifestUri } = options;\n\n const meta: UIMetadata = {};\n\n // Add manifest-related fields if provided\n if (rendererType) {\n meta['ui/type'] = rendererType;\n }\n if (contentHash) {\n meta['ui/contentHash'] = contentHash;\n }\n if (manifestUri) {\n meta['ui/manifestUri'] = manifestUri;\n }\n\n // For inline mode, embed HTML directly in response\n // This is the only serving mode that uses buildUIMeta\n // (static mode returns structured data only, no UI _meta)\n meta['ui/html'] = html;\n\n // Always include MIME type for platforms that need it\n meta['ui/mimeType'] = getMimeType(platformType);\n\n // Include token if provided\n if (token) {\n meta['ui/widgetToken'] = token;\n }\n\n // Include direct URL if provided\n if (directUrl) {\n meta['ui/directUrl'] = directUrl;\n }\n\n // Platform-specific fields\n switch (platformType) {\n case 'openai':\n return buildOpenAIMeta(meta, uiConfig);\n\n case 'claude':\n return buildClaudeMeta(meta, uiConfig);\n\n case 'gemini':\n return buildGeminiMeta(meta, uiConfig);\n\n case 'cursor':\n case 'continue':\n case 'cody':\n return buildIDEMeta(meta, uiConfig);\n\n case 'generic-mcp':\n return buildGenericMeta(meta, uiConfig);\n\n case 'ext-apps':\n return buildExtAppsMeta(meta, uiConfig);\n\n default:\n // Unknown platform - just return universal fields\n return meta;\n }\n}\n\n// ============================================\n// Helper Functions\n// ============================================\n\n/**\n * Get MIME type based on platform.\n */\nfunction getMimeType(platformType: AIPlatformType): string {\n switch (platformType) {\n case 'openai':\n return 'text/html+skybridge';\n default:\n return 'text/html+mcp';\n }\n}\n\n/**\n * Build OpenAI-specific metadata for tool CALL response (inline mode).\n *\n * NOTE: Per OpenAI's pizzaz example, the call response should only include\n * invocation status in _meta. The outputTemplate, resultCanProduceWidget, etc.\n * are discovery-time fields that belong in tools/list _meta, NOT in call response.\n *\n * For static mode: OpenAI fetches the widget HTML from the outputTemplate URI\n * (set in tools/list) and injects structuredContent as window.openai.toolOutput.\n * For inline mode: HTML is embedded directly in _meta['ui/html'].\n */\nfunction buildOpenAIMeta<In, Out>(meta: UIMetadata, uiConfig: UITemplateConfig<In, Out>): UIMetadata {\n // Only include invocation status in call response _meta\n // (per pizzaz example - they don't include outputTemplate in call response)\n if (uiConfig.invocationStatus?.invoking) {\n meta['openai/toolInvocation/invoking'] = uiConfig.invocationStatus.invoking;\n }\n if (uiConfig.invocationStatus?.invoked) {\n meta['openai/toolInvocation/invoked'] = uiConfig.invocationStatus.invoked;\n }\n\n return meta;\n}\n\n/**\n * Build OpenAI CSP format.\n */\nexport function buildOpenAICSP(csp: UIContentSecurityPolicy): {\n connect_domains?: string[];\n resource_domains?: string[];\n} {\n const result: { connect_domains?: string[]; resource_domains?: string[] } = {};\n\n if (csp.connectDomains?.length) {\n result.connect_domains = csp.connectDomains;\n }\n\n if (csp.resourceDomains?.length) {\n result.resource_domains = csp.resourceDomains;\n }\n\n return result;\n}\n\n/**\n * Build Claude-specific metadata.\n * Claude widgets are network-blocked, so we don't include URI references.\n */\nfunction buildClaudeMeta<In, Out>(meta: UIMetadata, uiConfig: UITemplateConfig<In, Out>): UIMetadata {\n // Claude uses inline HTML only (network-blocked)\n // Don't include resource URI since Claude can't fetch it\n\n if (uiConfig.widgetDescription) {\n meta['claude/widgetDescription'] = uiConfig.widgetDescription;\n }\n\n return meta;\n}\n\n/**\n * Build Gemini-specific metadata.\n */\nfunction buildGeminiMeta<In, Out>(meta: UIMetadata, uiConfig: UITemplateConfig<In, Out>): UIMetadata {\n // Gemini support is limited - include inline HTML\n // Future: Add Gemini-specific fields when they're defined\n\n if (uiConfig.widgetDescription) {\n meta['gemini/widgetDescription'] = uiConfig.widgetDescription;\n }\n\n return meta;\n}\n\n/**\n * Build IDE-specific metadata (Cursor, Continue, Cody).\n * For inline mode, HTML is embedded directly in _meta['ui/html'].\n */\nfunction buildIDEMeta<In, Out>(meta: UIMetadata, uiConfig: UITemplateConfig<In, Out>): UIMetadata {\n if (uiConfig.widgetDescription) {\n meta['ide/widgetDescription'] = uiConfig.widgetDescription;\n }\n\n return meta;\n}\n\n/**\n * Build generic MCP client metadata.\n * For inline mode, HTML is embedded directly in _meta['ui/html'].\n */\nfunction buildGenericMeta<In, Out>(meta: UIMetadata, uiConfig: UITemplateConfig<In, Out>): UIMetadata {\n if (uiConfig.widgetAccessible) {\n meta['openai/widgetAccessible'] = true;\n }\n\n if (uiConfig.csp) {\n meta['openai/widgetCSP'] = buildOpenAICSP(uiConfig.csp);\n }\n\n return meta;\n}\n\n/**\n * Build MCP Apps (ext-apps) metadata per specification.\n * For inline mode, HTML is embedded directly in _meta['ui/html'].\n *\n * Per MCP Apps spec: https://github.com/modelcontextprotocol/ext-apps\n * - ui/csp: Content security policy for sandboxed iframe\n * - ui/displayMode: How the UI should be displayed\n * - ui/prefersBorder: Whether to show border around UI\n * - ui/domain: Optional dedicated sandbox domain\n */\nfunction buildExtAppsMeta<In, Out>(meta: UIMetadata, uiConfig: UITemplateConfig<In, Out>): UIMetadata {\n // MCP Apps uses text/html+mcp MIME type\n meta['ui/mimeType'] = 'text/html+mcp';\n\n // CSP configuration (uses camelCase per MCP Apps spec)\n if (uiConfig.csp) {\n const csp: { connectDomains?: string[]; resourceDomains?: string[] } = {};\n\n if (uiConfig.csp.connectDomains?.length) {\n csp.connectDomains = uiConfig.csp.connectDomains;\n }\n\n if (uiConfig.csp.resourceDomains?.length) {\n csp.resourceDomains = uiConfig.csp.resourceDomains;\n }\n\n if (Object.keys(csp).length > 0) {\n meta['ui/csp'] = csp;\n }\n }\n\n // Display mode preference\n if (uiConfig.displayMode) {\n // Map generic display modes to MCP Apps specific values\n const displayModeMap: Record<string, 'inline' | 'fullscreen' | 'pip'> = {\n inline: 'inline',\n fullscreen: 'fullscreen',\n pip: 'pip',\n // Map OpenAI-style values\n widget: 'inline',\n panel: 'fullscreen',\n };\n const mappedMode = displayModeMap[uiConfig.displayMode];\n if (mappedMode) {\n meta['ui/displayMode'] = mappedMode;\n }\n }\n\n // Border preference (default: true for visual clarity in sandbox)\n if (uiConfig.prefersBorder !== undefined) {\n meta['ui/prefersBorder'] = uiConfig.prefersBorder;\n }\n\n // Dedicated sandbox domain (optional)\n if (uiConfig.sandboxDomain) {\n meta['ui/domain'] = uiConfig.sandboxDomain;\n }\n\n return meta;\n}\n\n// ============================================\n// Tool Discovery Metadata\n// ============================================\n\n/**\n * Build metadata for tool discovery (tools/list response).\n * This includes fields that should be present at discovery time,\n * not in individual tool call responses.\n *\n * @example\n * ```typescript\n * import { buildToolDiscoveryMeta } from '@frontmcp/ui/adapters';\n *\n * const toolMeta = buildToolDiscoveryMeta({\n * uiConfig: { template: MyWidget, widgetAccessible: true },\n * platformType: 'openai',\n * staticWidgetUri: 'ui://widget/my_tool.html',\n * });\n *\n * // Use in tools/list response\n * const tool = {\n * name: 'my_tool',\n * description: '...',\n * inputSchema: {...},\n * _meta: toolMeta,\n * };\n * ```\n */\nexport interface BuildToolDiscoveryMetaOptions<In = unknown, Out = unknown> {\n /** Tool UI configuration */\n uiConfig: UITemplateConfig<In, Out>;\n /** Detected platform type */\n platformType: AIPlatformType;\n /** Static widget URI (e.g., ui://widget/my_tool.html) */\n staticWidgetUri: string;\n}\n\n/**\n * Build tool discovery metadata (for tools/list response).\n */\nexport function buildToolDiscoveryMeta<In = unknown, Out = unknown>(\n options: BuildToolDiscoveryMetaOptions<In, Out>,\n): UIMetadata {\n const { uiConfig, platformType, staticWidgetUri } = options;\n\n const meta: UIMetadata = {};\n\n switch (platformType) {\n case 'openai':\n // OpenAI-specific discovery fields\n meta['openai/outputTemplate'] = staticWidgetUri;\n meta['openai/resultCanProduceWidget'] = true;\n\n if (uiConfig.widgetAccessible) {\n meta['openai/widgetAccessible'] = true;\n }\n\n if (uiConfig.csp) {\n meta['openai/widgetCSP'] = buildOpenAICSP(uiConfig.csp);\n }\n\n if (uiConfig.displayMode) {\n meta['openai/displayMode'] = uiConfig.displayMode;\n }\n\n if (uiConfig.widgetDescription) {\n meta['openai/widgetDescription'] = uiConfig.widgetDescription;\n }\n break;\n\n case 'generic-mcp':\n // Generic MCP uses OpenAI-like format\n meta['openai/outputTemplate'] = staticWidgetUri;\n meta['openai/resultCanProduceWidget'] = true;\n\n if (uiConfig.widgetAccessible) {\n meta['openai/widgetAccessible'] = true;\n }\n\n if (uiConfig.csp) {\n meta['openai/widgetCSP'] = buildOpenAICSP(uiConfig.csp);\n }\n break;\n\n case 'ext-apps':\n // MCP Apps discovery metadata per specification\n meta['ui/resourceUri'] = staticWidgetUri;\n meta['ui/mimeType'] = 'text/html+mcp';\n\n if (uiConfig.csp) {\n const csp: { connectDomains?: string[]; resourceDomains?: string[] } = {};\n if (uiConfig.csp.connectDomains?.length) {\n csp.connectDomains = uiConfig.csp.connectDomains;\n }\n if (uiConfig.csp.resourceDomains?.length) {\n csp.resourceDomains = uiConfig.csp.resourceDomains;\n }\n if (Object.keys(csp).length > 0) {\n meta['ui/csp'] = csp;\n }\n }\n\n if (uiConfig.displayMode) {\n const displayModeMap: Record<string, 'inline' | 'fullscreen' | 'pip'> = {\n inline: 'inline',\n fullscreen: 'fullscreen',\n pip: 'pip',\n };\n const mappedMode = displayModeMap[uiConfig.displayMode];\n if (mappedMode) {\n meta['ui/displayMode'] = mappedMode;\n }\n }\n\n if (uiConfig.prefersBorder !== undefined) {\n meta['ui/prefersBorder'] = uiConfig.prefersBorder;\n }\n\n if (uiConfig.sandboxDomain) {\n meta['ui/domain'] = uiConfig.sandboxDomain;\n }\n break;\n\n // Claude, Gemini, IDEs don't need discovery metadata\n // They use inline HTML at call time\n default:\n break;\n }\n\n return meta;\n}\n"]}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Platform Bridge - Reactive Data Store for Tool UI
3
+ *
4
+ * Provides a reactive interface for tool output data that works across:
5
+ * - React (via useSyncExternalStore)
6
+ * - HTMX (via custom events)
7
+ * - Vanilla JS (via subscribe callbacks)
8
+ *
9
+ * Uses Object.defineProperty to intercept when platforms inject data,
10
+ * automatically detecting and notifying subscribers when data changes.
11
+ *
12
+ * @example React integration
13
+ * ```tsx
14
+ * import { useSyncExternalStore } from 'react';
15
+ *
16
+ * function useToolOutput() {
17
+ * return useSyncExternalStore(
18
+ * window.__frontmcp.bridge.subscribe,
19
+ * window.__frontmcp.bridge.getSnapshot,
20
+ * window.__frontmcp.bridge.getServerSnapshot
21
+ * );
22
+ * }
23
+ * ```
24
+ *
25
+ * @example Vanilla JS
26
+ * ```javascript
27
+ * const unsubscribe = window.__frontmcp.bridge.subscribe(() => {
28
+ * const state = window.__frontmcp.bridge.getState();
29
+ * if (!state.loading) {
30
+ * renderWidget(state.data);
31
+ * }
32
+ * });
33
+ * ```
34
+ *
35
+ * @example HTMX
36
+ * ```html
37
+ * <div hx-trigger="frontmcp:change from:document" hx-get="/render">
38
+ * Loading...
39
+ * </div>
40
+ * ```
41
+ */
42
+ /**
43
+ * Bridge state containing data, loading status, and error information.
44
+ */
45
+ export interface BridgeState<T = unknown> {
46
+ /** Current data (null when loading or no data) */
47
+ data: T | null;
48
+ /** Whether the bridge is waiting for data */
49
+ loading: boolean;
50
+ /** Error message if data loading failed */
51
+ error: string | null;
52
+ }
53
+ /**
54
+ * Platform Bridge interface for reactive data access.
55
+ */
56
+ export interface PlatformBridge<T = unknown> {
57
+ /** Get current state snapshot */
58
+ getState(): BridgeState<T>;
59
+ /** Subscribe to state changes */
60
+ subscribe(callback: () => void): () => void;
61
+ /** React useSyncExternalStore compatible getSnapshot */
62
+ getSnapshot(): BridgeState<T>;
63
+ /** React SSR compatible getServerSnapshot */
64
+ getServerSnapshot(): BridgeState<T>;
65
+ /** Check if bridge has received data */
66
+ hasData(): boolean;
67
+ /** Manually set data (for testing or custom injection) */
68
+ setData(data: T): void;
69
+ /** Manually set error */
70
+ setError(error: string): void;
71
+ /** Reset to loading state */
72
+ reset(): void;
73
+ }
74
+ /**
75
+ * Render the Platform Bridge inline script.
76
+ *
77
+ * This script creates a reactive data store that:
78
+ * 1. Installs interceptors on window.openai to detect data injection
79
+ * 2. Provides subscribe/getState API for React/HTMX/vanilla integration
80
+ * 3. Dispatches custom events for HTMX compatibility
81
+ * 4. Handles the race condition between iframe load and data injection
82
+ *
83
+ * @returns Script tag with bridge implementation
84
+ */
85
+ export declare function renderBridgeScript(): string;
86
+ /**
87
+ * Generate TypeScript types for the bridge (for documentation/IDE support).
88
+ */
89
+ export declare const BRIDGE_TYPES = "\ninterface BridgeState<T = unknown> {\n data: T | null;\n loading: boolean;\n error: string | null;\n}\n\ninterface PlatformBridge<T = unknown> {\n getState(): BridgeState<T>;\n subscribe(callback: () => void): () => void;\n getSnapshot(): BridgeState<T>;\n getServerSnapshot(): BridgeState<T>;\n hasData(): boolean;\n setData(data: T): void;\n setError(error: string): void;\n reset(): void;\n}\n\ndeclare global {\n interface Window {\n __frontmcp: {\n bridge: PlatformBridge;\n // ... other __frontmcp methods\n };\n }\n}\n";
@@ -0,0 +1,452 @@
1
+ "use strict";
2
+ /**
3
+ * Platform Bridge - Reactive Data Store for Tool UI
4
+ *
5
+ * Provides a reactive interface for tool output data that works across:
6
+ * - React (via useSyncExternalStore)
7
+ * - HTMX (via custom events)
8
+ * - Vanilla JS (via subscribe callbacks)
9
+ *
10
+ * Uses Object.defineProperty to intercept when platforms inject data,
11
+ * automatically detecting and notifying subscribers when data changes.
12
+ *
13
+ * @example React integration
14
+ * ```tsx
15
+ * import { useSyncExternalStore } from 'react';
16
+ *
17
+ * function useToolOutput() {
18
+ * return useSyncExternalStore(
19
+ * window.__frontmcp.bridge.subscribe,
20
+ * window.__frontmcp.bridge.getSnapshot,
21
+ * window.__frontmcp.bridge.getServerSnapshot
22
+ * );
23
+ * }
24
+ * ```
25
+ *
26
+ * @example Vanilla JS
27
+ * ```javascript
28
+ * const unsubscribe = window.__frontmcp.bridge.subscribe(() => {
29
+ * const state = window.__frontmcp.bridge.getState();
30
+ * if (!state.loading) {
31
+ * renderWidget(state.data);
32
+ * }
33
+ * });
34
+ * ```
35
+ *
36
+ * @example HTMX
37
+ * ```html
38
+ * <div hx-trigger="frontmcp:change from:document" hx-get="/render">
39
+ * Loading...
40
+ * </div>
41
+ * ```
42
+ */
43
+ Object.defineProperty(exports, "__esModule", { value: true });
44
+ exports.BRIDGE_TYPES = void 0;
45
+ exports.renderBridgeScript = renderBridgeScript;
46
+ /**
47
+ * Render the Platform Bridge inline script.
48
+ *
49
+ * This script creates a reactive data store that:
50
+ * 1. Installs interceptors on window.openai to detect data injection
51
+ * 2. Provides subscribe/getState API for React/HTMX/vanilla integration
52
+ * 3. Dispatches custom events for HTMX compatibility
53
+ * 4. Handles the race condition between iframe load and data injection
54
+ *
55
+ * @returns Script tag with bridge implementation
56
+ */
57
+ function renderBridgeScript() {
58
+ return `<script>
59
+ (function() {
60
+ 'use strict';
61
+
62
+ // ============================================
63
+ // Debug Mode
64
+ // ============================================
65
+
66
+ var DEBUG = window.location.search.indexOf('frontmcp_debug=1') > -1 ||
67
+ window.localStorage.getItem('frontmcp_debug') === '1';
68
+ var debugLog = [];
69
+
70
+ function log(level, message, data) {
71
+ var entry = {
72
+ ts: new Date().toISOString(),
73
+ level: level,
74
+ message: message,
75
+ data: data
76
+ };
77
+ debugLog.push(entry);
78
+ if (DEBUG) {
79
+ console[level === 'error' ? 'error' : level === 'warn' ? 'warn' : 'log'](
80
+ '[frontmcp] ' + message,
81
+ data || ''
82
+ );
83
+ }
84
+ }
85
+
86
+ // ============================================
87
+ // Bridge State Management
88
+ // ============================================
89
+
90
+ var state = { data: null, loading: true, error: null };
91
+ var subscribers = [];
92
+ var stateVersion = 0; // For React useSyncExternalStore stability
93
+
94
+ function notify() {
95
+ stateVersion++;
96
+ for (var i = 0; i < subscribers.length; i++) {
97
+ try { subscribers[i](); } catch(e) {
98
+ log('error', 'Subscriber error:', { error: e.message, stack: e.stack });
99
+ }
100
+ }
101
+ // Dispatch custom event for HTMX
102
+ if (typeof CustomEvent !== 'undefined') {
103
+ document.dispatchEvent(new CustomEvent('frontmcp:change', { detail: state }));
104
+ }
105
+ }
106
+
107
+ function setData(data) {
108
+ // Skip if data hasn't actually changed (prevents unnecessary re-renders)
109
+ if (!state.loading && state.data === data) return;
110
+ log('info', 'setData called', {
111
+ dataType: typeof data,
112
+ dataLength: typeof data === 'string' ? data.length : 'N/A',
113
+ isNull: data === null,
114
+ isUndefined: data === undefined
115
+ });
116
+ state = { data: data, loading: false, error: null };
117
+ notify();
118
+ }
119
+
120
+ function setError(error) {
121
+ log('error', 'setError called', { error: error });
122
+ state = { data: null, loading: false, error: error };
123
+ notify();
124
+ }
125
+
126
+ function reset() {
127
+ log('info', 'reset called');
128
+ state = { data: null, loading: true, error: null };
129
+ notify();
130
+ }
131
+
132
+ // ============================================
133
+ // Data Validation
134
+ // ============================================
135
+
136
+ function validateToolOutput(data, source) {
137
+ var errors = [];
138
+
139
+ if (data === undefined) {
140
+ errors.push('toolOutput is undefined');
141
+ } else if (data === null) {
142
+ // null is valid initial state from OpenAI
143
+ log('info', 'toolOutput is null (OpenAI initial state)', { source: source });
144
+ return { valid: false, errors: ['initial_state'], isInitial: true };
145
+ }
146
+
147
+ if (typeof data === 'object' && data !== null) {
148
+ if (data.isError === true) {
149
+ errors.push('Tool returned error: ' + JSON.stringify(data));
150
+ }
151
+ // Check for MCP error structure
152
+ if (data.error && data.error.code) {
153
+ errors.push('MCP error: code=' + data.error.code + ', message=' + (data.error.message || 'unknown'));
154
+ }
155
+ }
156
+
157
+ if (errors.length > 0) {
158
+ log('warn', 'Validation issues', { source: source, errors: errors });
159
+ }
160
+
161
+ return { valid: errors.length === 0, errors: errors, isInitial: false };
162
+ }
163
+
164
+ function validateMetadata(meta, source) {
165
+ var errors = [];
166
+
167
+ if (!meta) {
168
+ errors.push('toolResponseMetadata is null/undefined');
169
+ return { valid: false, errors: errors };
170
+ }
171
+
172
+ if (typeof meta !== 'object') {
173
+ errors.push('toolResponseMetadata is not an object: ' + typeof meta);
174
+ return { valid: false, errors: errors };
175
+ }
176
+
177
+ var html = meta['ui/html'];
178
+ if (html !== undefined) {
179
+ if (typeof html !== 'string') {
180
+ errors.push('ui/html is not a string: ' + typeof html);
181
+ } else if (html.length === 0) {
182
+ errors.push('ui/html is empty string');
183
+ } else if (html.indexOf('validation-error') > -1) {
184
+ errors.push('ui/html contains validation error');
185
+ log('warn', 'HTML contains validation error', { htmlPreview: html.substring(0, 500) });
186
+ }
187
+ }
188
+
189
+ if (errors.length > 0) {
190
+ log('warn', 'Metadata validation issues', { source: source, errors: errors, keys: Object.keys(meta) });
191
+ }
192
+
193
+ return { valid: errors.length === 0, errors: errors };
194
+ }
195
+
196
+ // ============================================
197
+ // OpenAI Platform Interceptors
198
+ // ============================================
199
+
200
+ var openaiIntercepted = false;
201
+
202
+ function checkOpenAIData(openai) {
203
+ if (!openai) {
204
+ log('warn', 'checkOpenAIData: openai object is null');
205
+ return false;
206
+ }
207
+
208
+ log('info', 'checkOpenAIData called', {
209
+ hasToolOutput: openai.toolOutput !== undefined,
210
+ hasToolResponseMetadata: openai.toolResponseMetadata !== undefined,
211
+ toolOutputType: typeof openai.toolOutput,
212
+ metadataKeys: openai.toolResponseMetadata ? Object.keys(openai.toolResponseMetadata) : []
213
+ });
214
+
215
+ // Priority 1: Pre-rendered HTML from toolResponseMetadata
216
+ if (openai.toolResponseMetadata) {
217
+ var metaValidation = validateMetadata(openai.toolResponseMetadata, 'openai.toolResponseMetadata');
218
+ var html = openai.toolResponseMetadata['ui/html'];
219
+ if (html && typeof html === 'string') {
220
+ log('info', 'Using ui/html from metadata', { htmlLength: html.length });
221
+ setData(html);
222
+ return true;
223
+ }
224
+ }
225
+
226
+ // Priority 2: Tool output (skip null - that's OpenAI's initial state)
227
+ if (openai.toolOutput !== undefined && openai.toolOutput !== null) {
228
+ var outputValidation = validateToolOutput(openai.toolOutput, 'openai.toolOutput');
229
+ if (!outputValidation.isInitial) {
230
+ log('info', 'Using toolOutput', {
231
+ valid: outputValidation.valid,
232
+ errors: outputValidation.errors
233
+ });
234
+ setData(openai.toolOutput);
235
+ return true;
236
+ }
237
+ }
238
+
239
+ log('info', 'checkOpenAIData: no data found yet');
240
+ return false;
241
+ }
242
+
243
+ function installPropertyInterceptor(obj, prop, onSet) {
244
+ var current = obj[prop];
245
+ var descriptor = Object.getOwnPropertyDescriptor(obj, prop);
246
+
247
+ // Don't re-intercept
248
+ if (descriptor && descriptor.get && descriptor.set) return;
249
+
250
+ Object.defineProperty(obj, prop, {
251
+ get: function() { return current; },
252
+ set: function(val) {
253
+ current = val;
254
+ onSet(val);
255
+ },
256
+ enumerable: true,
257
+ configurable: true
258
+ });
259
+ }
260
+
261
+ function installOpenAIInterceptors(openai) {
262
+ if (!openai || openaiIntercepted) return;
263
+ openaiIntercepted = true;
264
+
265
+ // Check existing data first
266
+ if (checkOpenAIData(openai)) return;
267
+
268
+ // Intercept toolOutput
269
+ installPropertyInterceptor(openai, 'toolOutput', function(val) {
270
+ checkOpenAIData(openai);
271
+ });
272
+
273
+ // Intercept toolResponseMetadata
274
+ installPropertyInterceptor(openai, 'toolResponseMetadata', function(val) {
275
+ checkOpenAIData(openai);
276
+ });
277
+ }
278
+
279
+ // Install interceptor on window.openai
280
+ if (typeof window.openai !== 'undefined') {
281
+ installOpenAIInterceptors(window.openai);
282
+ } else {
283
+ // OpenAI object doesn't exist yet - wait for it
284
+ var pendingOpenai = undefined;
285
+ Object.defineProperty(window, 'openai', {
286
+ get: function() { return pendingOpenai; },
287
+ set: function(val) {
288
+ pendingOpenai = val;
289
+ if (val) installOpenAIInterceptors(val);
290
+ },
291
+ enumerable: true,
292
+ configurable: true
293
+ });
294
+ }
295
+
296
+ // ============================================
297
+ // MCP/Generic Platform Support
298
+ // ============================================
299
+
300
+ // Check for existing __frontmcp.toolOutput
301
+ if (window.__frontmcp && window.__frontmcp.toolOutput !== undefined) {
302
+ setData(window.__frontmcp.toolOutput);
303
+ }
304
+
305
+ // Check for __mcpToolOutput
306
+ if (window.__mcpToolOutput !== undefined && window.__mcpToolOutput !== null) {
307
+ setData(window.__mcpToolOutput);
308
+ }
309
+
310
+ // Check for __mcpResponseMeta
311
+ if (window.__mcpResponseMeta && window.__mcpResponseMeta['ui/html']) {
312
+ setData(window.__mcpResponseMeta['ui/html']);
313
+ }
314
+
315
+ // ============================================
316
+ // Bridge API
317
+ // ============================================
318
+
319
+ window.__frontmcp = window.__frontmcp || {};
320
+
321
+ window.__frontmcp.bridge = {
322
+ getState: function() { return state; },
323
+
324
+ // React useSyncExternalStore compatible
325
+ getSnapshot: function() { return state; },
326
+ getServerSnapshot: function() { return { data: null, loading: true, error: null }; },
327
+
328
+ subscribe: function(callback) {
329
+ subscribers.push(callback);
330
+ return function() {
331
+ var idx = subscribers.indexOf(callback);
332
+ if (idx > -1) subscribers.splice(idx, 1);
333
+ };
334
+ },
335
+
336
+ hasData: function() { return !state.loading && state.data !== null; },
337
+
338
+ // Manual control (for testing/custom injection)
339
+ setData: setData,
340
+ setError: setError,
341
+ reset: reset,
342
+
343
+ // Debug API
344
+ debug: {
345
+ getLogs: function() { return debugLog.slice(); },
346
+ getLastErrors: function() {
347
+ return debugLog.filter(function(e) { return e.level === 'error' || e.level === 'warn'; });
348
+ },
349
+ enableDebug: function() {
350
+ DEBUG = true;
351
+ window.localStorage.setItem('frontmcp_debug', '1');
352
+ console.log('[frontmcp] Debug mode enabled. Reload page to see all logs.');
353
+ },
354
+ disableDebug: function() {
355
+ DEBUG = false;
356
+ window.localStorage.removeItem('frontmcp_debug');
357
+ },
358
+ isDebugEnabled: function() { return DEBUG; },
359
+ getStateHistory: function() {
360
+ return {
361
+ current: state,
362
+ version: stateVersion,
363
+ subscriberCount: subscribers.length,
364
+ openaiIntercepted: openaiIntercepted,
365
+ pollCount: pollCount
366
+ };
367
+ },
368
+ dumpAll: function() {
369
+ console.group('[frontmcp] Debug Dump');
370
+ console.log('State:', state);
371
+ console.log('Version:', stateVersion);
372
+ console.log('Subscribers:', subscribers.length);
373
+ console.log('OpenAI Intercepted:', openaiIntercepted);
374
+ console.log('Poll Count:', pollCount);
375
+ console.log('Debug Logs:', debugLog);
376
+ console.groupEnd();
377
+ return {
378
+ state: state,
379
+ logs: debugLog,
380
+ version: stateVersion,
381
+ openaiIntercepted: openaiIntercepted
382
+ };
383
+ }
384
+ }
385
+ };
386
+
387
+ // ============================================
388
+ // Fallback Polling (for platforms that don't trigger setters)
389
+ // ============================================
390
+
391
+ var pollCount = 0;
392
+ var maxPolls = 30; // ~3 seconds with exponential backoff
393
+
394
+ function pollForData() {
395
+ if (state.data !== null || pollCount >= maxPolls) return;
396
+ pollCount++;
397
+
398
+ // Check all sources
399
+ if (window.openai) {
400
+ if (checkOpenAIData(window.openai)) return;
401
+ }
402
+
403
+ // Use getToolOutput if available
404
+ if (window.__frontmcp.getToolOutput) {
405
+ var data = window.__frontmcp.getToolOutput();
406
+ if (data !== undefined && data !== null) {
407
+ setData(data);
408
+ return;
409
+ }
410
+ }
411
+
412
+ // Exponential backoff: 50ms, 75ms, 112ms, ...
413
+ var delay = Math.min(50 * Math.pow(1.5, Math.min(pollCount, 10)), 500);
414
+ setTimeout(pollForData, delay);
415
+ }
416
+
417
+ // Start polling after a brief delay (give interceptors time to fire)
418
+ setTimeout(pollForData, 50);
419
+ })();
420
+ </script>`;
421
+ }
422
+ /**
423
+ * Generate TypeScript types for the bridge (for documentation/IDE support).
424
+ */
425
+ exports.BRIDGE_TYPES = `
426
+ interface BridgeState<T = unknown> {
427
+ data: T | null;
428
+ loading: boolean;
429
+ error: string | null;
430
+ }
431
+
432
+ interface PlatformBridge<T = unknown> {
433
+ getState(): BridgeState<T>;
434
+ subscribe(callback: () => void): () => void;
435
+ getSnapshot(): BridgeState<T>;
436
+ getServerSnapshot(): BridgeState<T>;
437
+ hasData(): boolean;
438
+ setData(data: T): void;
439
+ setError(error: string): void;
440
+ reset(): void;
441
+ }
442
+
443
+ declare global {
444
+ interface Window {
445
+ __frontmcp: {
446
+ bridge: PlatformBridge;
447
+ // ... other __frontmcp methods
448
+ };
449
+ }
450
+ }
451
+ `;
452
+ //# sourceMappingURL=bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.js","sourceRoot":"","sources":["../../../src/base-template/bridge.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;;;AA+CH,gDA4WC;AAvXD;;;;;;;;;;GAUG;AACH,SAAgB,kBAAkB;IAChC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA0WC,CAAC;AACX,CAAC;AAED;;GAEG;AACU,QAAA,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;CA0B3B,CAAC","sourcesContent":["/**\n * Platform Bridge - Reactive Data Store for Tool UI\n *\n * Provides a reactive interface for tool output data that works across:\n * - React (via useSyncExternalStore)\n * - HTMX (via custom events)\n * - Vanilla JS (via subscribe callbacks)\n *\n * Uses Object.defineProperty to intercept when platforms inject data,\n * automatically detecting and notifying subscribers when data changes.\n *\n * @example React integration\n * ```tsx\n * import { useSyncExternalStore } from 'react';\n *\n * function useToolOutput() {\n * return useSyncExternalStore(\n * window.__frontmcp.bridge.subscribe,\n * window.__frontmcp.bridge.getSnapshot,\n * window.__frontmcp.bridge.getServerSnapshot\n * );\n * }\n * ```\n *\n * @example Vanilla JS\n * ```javascript\n * const unsubscribe = window.__frontmcp.bridge.subscribe(() => {\n * const state = window.__frontmcp.bridge.getState();\n * if (!state.loading) {\n * renderWidget(state.data);\n * }\n * });\n * ```\n *\n * @example HTMX\n * ```html\n * <div hx-trigger=\"frontmcp:change from:document\" hx-get=\"/render\">\n * Loading...\n * </div>\n * ```\n */\n\n/**\n * Bridge state containing data, loading status, and error information.\n */\nexport interface BridgeState<T = unknown> {\n /** Current data (null when loading or no data) */\n data: T | null;\n /** Whether the bridge is waiting for data */\n loading: boolean;\n /** Error message if data loading failed */\n error: string | null;\n}\n\n/**\n * Platform Bridge interface for reactive data access.\n */\nexport interface PlatformBridge<T = unknown> {\n /** Get current state snapshot */\n getState(): BridgeState<T>;\n /** Subscribe to state changes */\n subscribe(callback: () => void): () => void;\n /** React useSyncExternalStore compatible getSnapshot */\n getSnapshot(): BridgeState<T>;\n /** React SSR compatible getServerSnapshot */\n getServerSnapshot(): BridgeState<T>;\n /** Check if bridge has received data */\n hasData(): boolean;\n /** Manually set data (for testing or custom injection) */\n setData(data: T): void;\n /** Manually set error */\n setError(error: string): void;\n /** Reset to loading state */\n reset(): void;\n}\n\n/**\n * Render the Platform Bridge inline script.\n *\n * This script creates a reactive data store that:\n * 1. Installs interceptors on window.openai to detect data injection\n * 2. Provides subscribe/getState API for React/HTMX/vanilla integration\n * 3. Dispatches custom events for HTMX compatibility\n * 4. Handles the race condition between iframe load and data injection\n *\n * @returns Script tag with bridge implementation\n */\nexport function renderBridgeScript(): string {\n return `<script>\n(function() {\n 'use strict';\n\n // ============================================\n // Debug Mode\n // ============================================\n\n var DEBUG = window.location.search.indexOf('frontmcp_debug=1') > -1 ||\n window.localStorage.getItem('frontmcp_debug') === '1';\n var debugLog = [];\n\n function log(level, message, data) {\n var entry = {\n ts: new Date().toISOString(),\n level: level,\n message: message,\n data: data\n };\n debugLog.push(entry);\n if (DEBUG) {\n console[level === 'error' ? 'error' : level === 'warn' ? 'warn' : 'log'](\n '[frontmcp] ' + message,\n data || ''\n );\n }\n }\n\n // ============================================\n // Bridge State Management\n // ============================================\n\n var state = { data: null, loading: true, error: null };\n var subscribers = [];\n var stateVersion = 0; // For React useSyncExternalStore stability\n\n function notify() {\n stateVersion++;\n for (var i = 0; i < subscribers.length; i++) {\n try { subscribers[i](); } catch(e) {\n log('error', 'Subscriber error:', { error: e.message, stack: e.stack });\n }\n }\n // Dispatch custom event for HTMX\n if (typeof CustomEvent !== 'undefined') {\n document.dispatchEvent(new CustomEvent('frontmcp:change', { detail: state }));\n }\n }\n\n function setData(data) {\n // Skip if data hasn't actually changed (prevents unnecessary re-renders)\n if (!state.loading && state.data === data) return;\n log('info', 'setData called', {\n dataType: typeof data,\n dataLength: typeof data === 'string' ? data.length : 'N/A',\n isNull: data === null,\n isUndefined: data === undefined\n });\n state = { data: data, loading: false, error: null };\n notify();\n }\n\n function setError(error) {\n log('error', 'setError called', { error: error });\n state = { data: null, loading: false, error: error };\n notify();\n }\n\n function reset() {\n log('info', 'reset called');\n state = { data: null, loading: true, error: null };\n notify();\n }\n\n // ============================================\n // Data Validation\n // ============================================\n\n function validateToolOutput(data, source) {\n var errors = [];\n\n if (data === undefined) {\n errors.push('toolOutput is undefined');\n } else if (data === null) {\n // null is valid initial state from OpenAI\n log('info', 'toolOutput is null (OpenAI initial state)', { source: source });\n return { valid: false, errors: ['initial_state'], isInitial: true };\n }\n\n if (typeof data === 'object' && data !== null) {\n if (data.isError === true) {\n errors.push('Tool returned error: ' + JSON.stringify(data));\n }\n // Check for MCP error structure\n if (data.error && data.error.code) {\n errors.push('MCP error: code=' + data.error.code + ', message=' + (data.error.message || 'unknown'));\n }\n }\n\n if (errors.length > 0) {\n log('warn', 'Validation issues', { source: source, errors: errors });\n }\n\n return { valid: errors.length === 0, errors: errors, isInitial: false };\n }\n\n function validateMetadata(meta, source) {\n var errors = [];\n\n if (!meta) {\n errors.push('toolResponseMetadata is null/undefined');\n return { valid: false, errors: errors };\n }\n\n if (typeof meta !== 'object') {\n errors.push('toolResponseMetadata is not an object: ' + typeof meta);\n return { valid: false, errors: errors };\n }\n\n var html = meta['ui/html'];\n if (html !== undefined) {\n if (typeof html !== 'string') {\n errors.push('ui/html is not a string: ' + typeof html);\n } else if (html.length === 0) {\n errors.push('ui/html is empty string');\n } else if (html.indexOf('validation-error') > -1) {\n errors.push('ui/html contains validation error');\n log('warn', 'HTML contains validation error', { htmlPreview: html.substring(0, 500) });\n }\n }\n\n if (errors.length > 0) {\n log('warn', 'Metadata validation issues', { source: source, errors: errors, keys: Object.keys(meta) });\n }\n\n return { valid: errors.length === 0, errors: errors };\n }\n\n // ============================================\n // OpenAI Platform Interceptors\n // ============================================\n\n var openaiIntercepted = false;\n\n function checkOpenAIData(openai) {\n if (!openai) {\n log('warn', 'checkOpenAIData: openai object is null');\n return false;\n }\n\n log('info', 'checkOpenAIData called', {\n hasToolOutput: openai.toolOutput !== undefined,\n hasToolResponseMetadata: openai.toolResponseMetadata !== undefined,\n toolOutputType: typeof openai.toolOutput,\n metadataKeys: openai.toolResponseMetadata ? Object.keys(openai.toolResponseMetadata) : []\n });\n\n // Priority 1: Pre-rendered HTML from toolResponseMetadata\n if (openai.toolResponseMetadata) {\n var metaValidation = validateMetadata(openai.toolResponseMetadata, 'openai.toolResponseMetadata');\n var html = openai.toolResponseMetadata['ui/html'];\n if (html && typeof html === 'string') {\n log('info', 'Using ui/html from metadata', { htmlLength: html.length });\n setData(html);\n return true;\n }\n }\n\n // Priority 2: Tool output (skip null - that's OpenAI's initial state)\n if (openai.toolOutput !== undefined && openai.toolOutput !== null) {\n var outputValidation = validateToolOutput(openai.toolOutput, 'openai.toolOutput');\n if (!outputValidation.isInitial) {\n log('info', 'Using toolOutput', {\n valid: outputValidation.valid,\n errors: outputValidation.errors\n });\n setData(openai.toolOutput);\n return true;\n }\n }\n\n log('info', 'checkOpenAIData: no data found yet');\n return false;\n }\n\n function installPropertyInterceptor(obj, prop, onSet) {\n var current = obj[prop];\n var descriptor = Object.getOwnPropertyDescriptor(obj, prop);\n\n // Don't re-intercept\n if (descriptor && descriptor.get && descriptor.set) return;\n\n Object.defineProperty(obj, prop, {\n get: function() { return current; },\n set: function(val) {\n current = val;\n onSet(val);\n },\n enumerable: true,\n configurable: true\n });\n }\n\n function installOpenAIInterceptors(openai) {\n if (!openai || openaiIntercepted) return;\n openaiIntercepted = true;\n\n // Check existing data first\n if (checkOpenAIData(openai)) return;\n\n // Intercept toolOutput\n installPropertyInterceptor(openai, 'toolOutput', function(val) {\n checkOpenAIData(openai);\n });\n\n // Intercept toolResponseMetadata\n installPropertyInterceptor(openai, 'toolResponseMetadata', function(val) {\n checkOpenAIData(openai);\n });\n }\n\n // Install interceptor on window.openai\n if (typeof window.openai !== 'undefined') {\n installOpenAIInterceptors(window.openai);\n } else {\n // OpenAI object doesn't exist yet - wait for it\n var pendingOpenai = undefined;\n Object.defineProperty(window, 'openai', {\n get: function() { return pendingOpenai; },\n set: function(val) {\n pendingOpenai = val;\n if (val) installOpenAIInterceptors(val);\n },\n enumerable: true,\n configurable: true\n });\n }\n\n // ============================================\n // MCP/Generic Platform Support\n // ============================================\n\n // Check for existing __frontmcp.toolOutput\n if (window.__frontmcp && window.__frontmcp.toolOutput !== undefined) {\n setData(window.__frontmcp.toolOutput);\n }\n\n // Check for __mcpToolOutput\n if (window.__mcpToolOutput !== undefined && window.__mcpToolOutput !== null) {\n setData(window.__mcpToolOutput);\n }\n\n // Check for __mcpResponseMeta\n if (window.__mcpResponseMeta && window.__mcpResponseMeta['ui/html']) {\n setData(window.__mcpResponseMeta['ui/html']);\n }\n\n // ============================================\n // Bridge API\n // ============================================\n\n window.__frontmcp = window.__frontmcp || {};\n\n window.__frontmcp.bridge = {\n getState: function() { return state; },\n\n // React useSyncExternalStore compatible\n getSnapshot: function() { return state; },\n getServerSnapshot: function() { return { data: null, loading: true, error: null }; },\n\n subscribe: function(callback) {\n subscribers.push(callback);\n return function() {\n var idx = subscribers.indexOf(callback);\n if (idx > -1) subscribers.splice(idx, 1);\n };\n },\n\n hasData: function() { return !state.loading && state.data !== null; },\n\n // Manual control (for testing/custom injection)\n setData: setData,\n setError: setError,\n reset: reset,\n\n // Debug API\n debug: {\n getLogs: function() { return debugLog.slice(); },\n getLastErrors: function() {\n return debugLog.filter(function(e) { return e.level === 'error' || e.level === 'warn'; });\n },\n enableDebug: function() {\n DEBUG = true;\n window.localStorage.setItem('frontmcp_debug', '1');\n console.log('[frontmcp] Debug mode enabled. Reload page to see all logs.');\n },\n disableDebug: function() {\n DEBUG = false;\n window.localStorage.removeItem('frontmcp_debug');\n },\n isDebugEnabled: function() { return DEBUG; },\n getStateHistory: function() {\n return {\n current: state,\n version: stateVersion,\n subscriberCount: subscribers.length,\n openaiIntercepted: openaiIntercepted,\n pollCount: pollCount\n };\n },\n dumpAll: function() {\n console.group('[frontmcp] Debug Dump');\n console.log('State:', state);\n console.log('Version:', stateVersion);\n console.log('Subscribers:', subscribers.length);\n console.log('OpenAI Intercepted:', openaiIntercepted);\n console.log('Poll Count:', pollCount);\n console.log('Debug Logs:', debugLog);\n console.groupEnd();\n return {\n state: state,\n logs: debugLog,\n version: stateVersion,\n openaiIntercepted: openaiIntercepted\n };\n }\n }\n };\n\n // ============================================\n // Fallback Polling (for platforms that don't trigger setters)\n // ============================================\n\n var pollCount = 0;\n var maxPolls = 30; // ~3 seconds with exponential backoff\n\n function pollForData() {\n if (state.data !== null || pollCount >= maxPolls) return;\n pollCount++;\n\n // Check all sources\n if (window.openai) {\n if (checkOpenAIData(window.openai)) return;\n }\n\n // Use getToolOutput if available\n if (window.__frontmcp.getToolOutput) {\n var data = window.__frontmcp.getToolOutput();\n if (data !== undefined && data !== null) {\n setData(data);\n return;\n }\n }\n\n // Exponential backoff: 50ms, 75ms, 112ms, ...\n var delay = Math.min(50 * Math.pow(1.5, Math.min(pollCount, 10)), 500);\n setTimeout(pollForData, delay);\n }\n\n // Start polling after a brief delay (give interceptors time to fire)\n setTimeout(pollForData, 50);\n})();\n</script>`;\n}\n\n/**\n * Generate TypeScript types for the bridge (for documentation/IDE support).\n */\nexport const BRIDGE_TYPES = `\ninterface BridgeState<T = unknown> {\n data: T | null;\n loading: boolean;\n error: string | null;\n}\n\ninterface PlatformBridge<T = unknown> {\n getState(): BridgeState<T>;\n subscribe(callback: () => void): () => void;\n getSnapshot(): BridgeState<T>;\n getServerSnapshot(): BridgeState<T>;\n hasData(): boolean;\n setData(data: T): void;\n setError(error: string): void;\n reset(): void;\n}\n\ndeclare global {\n interface Window {\n __frontmcp: {\n bridge: PlatformBridge;\n // ... other __frontmcp methods\n };\n }\n}\n`;\n"]}