@frontmcp/ui 0.6.1 → 0.6.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 (292) hide show
  1. package/bridge/core/bridge-factory.d.ts +1 -0
  2. package/bridge/core/bridge-factory.d.ts.map +1 -1
  3. package/bridge/index.d.ts +1 -1
  4. package/bridge/index.d.ts.map +1 -1
  5. package/bridge/index.js +39 -881
  6. package/bundler/browser-components.d.ts +42 -0
  7. package/bundler/browser-components.d.ts.map +1 -0
  8. package/bundler/bundler.d.ts +78 -4
  9. package/bundler/bundler.d.ts.map +1 -1
  10. package/bundler/index.d.ts +8 -8
  11. package/bundler/index.d.ts.map +1 -1
  12. package/bundler/index.js +1315 -1854
  13. package/bundler/types.d.ts +188 -7
  14. package/bundler/types.d.ts.map +1 -1
  15. package/esm/bridge/{index.js → index.mjs} +40 -877
  16. package/esm/bundler/{index.js → index.mjs} +1391 -1895
  17. package/esm/{index.js → index.mjs} +215 -3091
  18. package/esm/layouts/{index.js → index.mjs} +3 -3
  19. package/esm/package.json +9 -8
  20. package/esm/react/index.mjs +1183 -0
  21. package/esm/renderers/index.mjs +611 -0
  22. package/esm/universal/{index.js → index.mjs} +266 -70
  23. package/index.d.ts +1 -4
  24. package/index.d.ts.map +1 -1
  25. package/index.js +208 -3113
  26. package/layouts/base.d.ts.map +1 -1
  27. package/layouts/index.js +3 -3
  28. package/layouts/presets.d.ts.map +1 -1
  29. package/package.json +9 -8
  30. package/react/Badge.d.ts.map +1 -1
  31. package/react/hooks/context.d.ts.map +1 -1
  32. package/react/index.d.ts +0 -1
  33. package/react/index.d.ts.map +1 -1
  34. package/react/index.js +57 -2001
  35. package/react/types.d.ts.map +1 -1
  36. package/renderers/index.d.ts +9 -4
  37. package/renderers/index.d.ts.map +1 -1
  38. package/renderers/index.js +328 -88
  39. package/renderers/mdx.renderer.d.ts +99 -0
  40. package/renderers/mdx.renderer.d.ts.map +1 -0
  41. package/renderers/react.renderer.d.ts +22 -13
  42. package/renderers/react.renderer.d.ts.map +1 -1
  43. package/renderers/transpiler.d.ts +49 -0
  44. package/renderers/transpiler.d.ts.map +1 -0
  45. package/universal/cached-runtime.d.ts +25 -1
  46. package/universal/cached-runtime.d.ts.map +1 -1
  47. package/universal/index.js +266 -70
  48. package/universal/runtime-builder.d.ts.map +1 -1
  49. package/universal/types.d.ts.map +1 -1
  50. package/web-components/elements/fmcp-input.d.ts.map +1 -1
  51. package/web-components/elements/fmcp-select.d.ts.map +1 -1
  52. package/web-components/index.d.ts +0 -1
  53. package/web-components/index.d.ts.map +1 -1
  54. package/bundler/cache.d.ts +0 -173
  55. package/bundler/cache.d.ts.map +0 -1
  56. package/bundler/file-cache/component-builder.d.ts +0 -167
  57. package/bundler/file-cache/component-builder.d.ts.map +0 -1
  58. package/bundler/file-cache/hash-calculator.d.ts +0 -155
  59. package/bundler/file-cache/hash-calculator.d.ts.map +0 -1
  60. package/bundler/file-cache/index.d.ts +0 -12
  61. package/bundler/file-cache/index.d.ts.map +0 -1
  62. package/bundler/file-cache/storage/filesystem.d.ts +0 -149
  63. package/bundler/file-cache/storage/filesystem.d.ts.map +0 -1
  64. package/bundler/file-cache/storage/index.d.ts +0 -11
  65. package/bundler/file-cache/storage/index.d.ts.map +0 -1
  66. package/bundler/file-cache/storage/interface.d.ts +0 -152
  67. package/bundler/file-cache/storage/interface.d.ts.map +0 -1
  68. package/bundler/file-cache/storage/redis.d.ts +0 -139
  69. package/bundler/file-cache/storage/redis.d.ts.map +0 -1
  70. package/bundler/sandbox/enclave-adapter.d.ts +0 -121
  71. package/bundler/sandbox/enclave-adapter.d.ts.map +0 -1
  72. package/bundler/sandbox/executor.d.ts +0 -14
  73. package/bundler/sandbox/executor.d.ts.map +0 -1
  74. package/bundler/sandbox/policy.d.ts +0 -62
  75. package/bundler/sandbox/policy.d.ts.map +0 -1
  76. package/esm/bridge/adapters/base-adapter.d.ts +0 -104
  77. package/esm/bridge/adapters/base-adapter.d.ts.map +0 -1
  78. package/esm/bridge/adapters/claude.adapter.d.ts +0 -67
  79. package/esm/bridge/adapters/claude.adapter.d.ts.map +0 -1
  80. package/esm/bridge/adapters/ext-apps.adapter.d.ts +0 -143
  81. package/esm/bridge/adapters/ext-apps.adapter.d.ts.map +0 -1
  82. package/esm/bridge/adapters/gemini.adapter.d.ts +0 -64
  83. package/esm/bridge/adapters/gemini.adapter.d.ts.map +0 -1
  84. package/esm/bridge/adapters/generic.adapter.d.ts +0 -56
  85. package/esm/bridge/adapters/generic.adapter.d.ts.map +0 -1
  86. package/esm/bridge/adapters/index.d.ts +0 -26
  87. package/esm/bridge/adapters/index.d.ts.map +0 -1
  88. package/esm/bridge/adapters/openai.adapter.d.ts +0 -65
  89. package/esm/bridge/adapters/openai.adapter.d.ts.map +0 -1
  90. package/esm/bridge/core/adapter-registry.d.ts +0 -122
  91. package/esm/bridge/core/adapter-registry.d.ts.map +0 -1
  92. package/esm/bridge/core/bridge-factory.d.ts +0 -199
  93. package/esm/bridge/core/bridge-factory.d.ts.map +0 -1
  94. package/esm/bridge/core/index.d.ts +0 -10
  95. package/esm/bridge/core/index.d.ts.map +0 -1
  96. package/esm/bridge/index.d.ts +0 -62
  97. package/esm/bridge/index.d.ts.map +0 -1
  98. package/esm/bridge/runtime/iife-generator.d.ts +0 -62
  99. package/esm/bridge/runtime/iife-generator.d.ts.map +0 -1
  100. package/esm/bridge/runtime/index.d.ts +0 -10
  101. package/esm/bridge/runtime/index.d.ts.map +0 -1
  102. package/esm/bridge/types.d.ts +0 -386
  103. package/esm/bridge/types.d.ts.map +0 -1
  104. package/esm/bundler/bundler.d.ts +0 -208
  105. package/esm/bundler/bundler.d.ts.map +0 -1
  106. package/esm/bundler/cache.d.ts +0 -173
  107. package/esm/bundler/cache.d.ts.map +0 -1
  108. package/esm/bundler/file-cache/component-builder.d.ts +0 -167
  109. package/esm/bundler/file-cache/component-builder.d.ts.map +0 -1
  110. package/esm/bundler/file-cache/hash-calculator.d.ts +0 -155
  111. package/esm/bundler/file-cache/hash-calculator.d.ts.map +0 -1
  112. package/esm/bundler/file-cache/index.d.ts +0 -12
  113. package/esm/bundler/file-cache/index.d.ts.map +0 -1
  114. package/esm/bundler/file-cache/storage/filesystem.d.ts +0 -149
  115. package/esm/bundler/file-cache/storage/filesystem.d.ts.map +0 -1
  116. package/esm/bundler/file-cache/storage/index.d.ts +0 -11
  117. package/esm/bundler/file-cache/storage/index.d.ts.map +0 -1
  118. package/esm/bundler/file-cache/storage/interface.d.ts +0 -152
  119. package/esm/bundler/file-cache/storage/interface.d.ts.map +0 -1
  120. package/esm/bundler/file-cache/storage/redis.d.ts +0 -139
  121. package/esm/bundler/file-cache/storage/redis.d.ts.map +0 -1
  122. package/esm/bundler/index.d.ts +0 -43
  123. package/esm/bundler/index.d.ts.map +0 -1
  124. package/esm/bundler/sandbox/enclave-adapter.d.ts +0 -121
  125. package/esm/bundler/sandbox/enclave-adapter.d.ts.map +0 -1
  126. package/esm/bundler/sandbox/executor.d.ts +0 -14
  127. package/esm/bundler/sandbox/executor.d.ts.map +0 -1
  128. package/esm/bundler/sandbox/policy.d.ts +0 -62
  129. package/esm/bundler/sandbox/policy.d.ts.map +0 -1
  130. package/esm/bundler/types.d.ts +0 -702
  131. package/esm/bundler/types.d.ts.map +0 -1
  132. package/esm/components/alert.d.ts +0 -66
  133. package/esm/components/alert.d.ts.map +0 -1
  134. package/esm/components/alert.schema.d.ts +0 -98
  135. package/esm/components/alert.schema.d.ts.map +0 -1
  136. package/esm/components/avatar.d.ts +0 -77
  137. package/esm/components/avatar.d.ts.map +0 -1
  138. package/esm/components/avatar.schema.d.ts +0 -170
  139. package/esm/components/avatar.schema.d.ts.map +0 -1
  140. package/esm/components/badge.d.ts +0 -64
  141. package/esm/components/badge.d.ts.map +0 -1
  142. package/esm/components/badge.schema.d.ts +0 -91
  143. package/esm/components/badge.schema.d.ts.map +0 -1
  144. package/esm/components/button.d.ts +0 -100
  145. package/esm/components/button.d.ts.map +0 -1
  146. package/esm/components/button.schema.d.ts +0 -120
  147. package/esm/components/button.schema.d.ts.map +0 -1
  148. package/esm/components/card.d.ts +0 -53
  149. package/esm/components/card.d.ts.map +0 -1
  150. package/esm/components/card.schema.d.ts +0 -93
  151. package/esm/components/card.schema.d.ts.map +0 -1
  152. package/esm/components/form.d.ts +0 -212
  153. package/esm/components/form.d.ts.map +0 -1
  154. package/esm/components/form.schema.d.ts +0 -365
  155. package/esm/components/form.schema.d.ts.map +0 -1
  156. package/esm/components/index.d.ts +0 -29
  157. package/esm/components/index.d.ts.map +0 -1
  158. package/esm/components/list.d.ts +0 -121
  159. package/esm/components/list.d.ts.map +0 -1
  160. package/esm/components/list.schema.d.ts +0 -129
  161. package/esm/components/list.schema.d.ts.map +0 -1
  162. package/esm/components/modal.d.ts +0 -100
  163. package/esm/components/modal.d.ts.map +0 -1
  164. package/esm/components/modal.schema.d.ts +0 -151
  165. package/esm/components/modal.schema.d.ts.map +0 -1
  166. package/esm/components/table.d.ts +0 -91
  167. package/esm/components/table.d.ts.map +0 -1
  168. package/esm/components/table.schema.d.ts +0 -123
  169. package/esm/components/table.schema.d.ts.map +0 -1
  170. package/esm/index.d.ts +0 -40
  171. package/esm/index.d.ts.map +0 -1
  172. package/esm/layouts/base.d.ts +0 -86
  173. package/esm/layouts/base.d.ts.map +0 -1
  174. package/esm/layouts/index.d.ts +0 -8
  175. package/esm/layouts/index.d.ts.map +0 -1
  176. package/esm/layouts/presets.d.ts +0 -134
  177. package/esm/layouts/presets.d.ts.map +0 -1
  178. package/esm/pages/consent.d.ts +0 -117
  179. package/esm/pages/consent.d.ts.map +0 -1
  180. package/esm/pages/error.d.ts +0 -101
  181. package/esm/pages/error.d.ts.map +0 -1
  182. package/esm/pages/index.d.ts +0 -9
  183. package/esm/pages/index.d.ts.map +0 -1
  184. package/esm/pages/index.js +0 -1036
  185. package/esm/react/Alert.d.ts +0 -101
  186. package/esm/react/Alert.d.ts.map +0 -1
  187. package/esm/react/Badge.d.ts +0 -100
  188. package/esm/react/Badge.d.ts.map +0 -1
  189. package/esm/react/Button.d.ts +0 -108
  190. package/esm/react/Button.d.ts.map +0 -1
  191. package/esm/react/Card.d.ts +0 -103
  192. package/esm/react/Card.d.ts.map +0 -1
  193. package/esm/react/hooks/context.d.ts +0 -179
  194. package/esm/react/hooks/context.d.ts.map +0 -1
  195. package/esm/react/hooks/index.d.ts +0 -42
  196. package/esm/react/hooks/index.d.ts.map +0 -1
  197. package/esm/react/hooks/tools.d.ts +0 -284
  198. package/esm/react/hooks/tools.d.ts.map +0 -1
  199. package/esm/react/index.d.ts +0 -80
  200. package/esm/react/index.d.ts.map +0 -1
  201. package/esm/react/index.js +0 -3124
  202. package/esm/react/types.d.ts +0 -105
  203. package/esm/react/types.d.ts.map +0 -1
  204. package/esm/react/utils.d.ts +0 -43
  205. package/esm/react/utils.d.ts.map +0 -1
  206. package/esm/render/index.d.ts +0 -8
  207. package/esm/render/index.d.ts.map +0 -1
  208. package/esm/render/prerender.d.ts +0 -57
  209. package/esm/render/prerender.d.ts.map +0 -1
  210. package/esm/renderers/index.d.ts +0 -21
  211. package/esm/renderers/index.d.ts.map +0 -1
  212. package/esm/renderers/index.js +0 -381
  213. package/esm/renderers/react.adapter.d.ts +0 -70
  214. package/esm/renderers/react.adapter.d.ts.map +0 -1
  215. package/esm/renderers/react.renderer.d.ts +0 -96
  216. package/esm/renderers/react.renderer.d.ts.map +0 -1
  217. package/esm/universal/UniversalApp.d.ts +0 -108
  218. package/esm/universal/UniversalApp.d.ts.map +0 -1
  219. package/esm/universal/cached-runtime.d.ts +0 -115
  220. package/esm/universal/cached-runtime.d.ts.map +0 -1
  221. package/esm/universal/context.d.ts +0 -122
  222. package/esm/universal/context.d.ts.map +0 -1
  223. package/esm/universal/index.d.ts +0 -57
  224. package/esm/universal/index.d.ts.map +0 -1
  225. package/esm/universal/renderers/html.renderer.d.ts +0 -37
  226. package/esm/universal/renderers/html.renderer.d.ts.map +0 -1
  227. package/esm/universal/renderers/index.d.ts +0 -112
  228. package/esm/universal/renderers/index.d.ts.map +0 -1
  229. package/esm/universal/renderers/markdown.renderer.d.ts +0 -33
  230. package/esm/universal/renderers/markdown.renderer.d.ts.map +0 -1
  231. package/esm/universal/renderers/mdx.renderer.d.ts +0 -38
  232. package/esm/universal/renderers/mdx.renderer.d.ts.map +0 -1
  233. package/esm/universal/renderers/react.renderer.d.ts +0 -46
  234. package/esm/universal/renderers/react.renderer.d.ts.map +0 -1
  235. package/esm/universal/runtime-builder.d.ts +0 -33
  236. package/esm/universal/runtime-builder.d.ts.map +0 -1
  237. package/esm/universal/store.d.ts +0 -135
  238. package/esm/universal/store.d.ts.map +0 -1
  239. package/esm/universal/types.d.ts +0 -199
  240. package/esm/universal/types.d.ts.map +0 -1
  241. package/esm/web-components/core/attribute-parser.d.ts +0 -82
  242. package/esm/web-components/core/attribute-parser.d.ts.map +0 -1
  243. package/esm/web-components/core/base-element.d.ts +0 -197
  244. package/esm/web-components/core/base-element.d.ts.map +0 -1
  245. package/esm/web-components/core/index.d.ts +0 -9
  246. package/esm/web-components/core/index.d.ts.map +0 -1
  247. package/esm/web-components/elements/fmcp-alert.d.ts +0 -46
  248. package/esm/web-components/elements/fmcp-alert.d.ts.map +0 -1
  249. package/esm/web-components/elements/fmcp-badge.d.ts +0 -47
  250. package/esm/web-components/elements/fmcp-badge.d.ts.map +0 -1
  251. package/esm/web-components/elements/fmcp-button.d.ts +0 -117
  252. package/esm/web-components/elements/fmcp-button.d.ts.map +0 -1
  253. package/esm/web-components/elements/fmcp-card.d.ts +0 -53
  254. package/esm/web-components/elements/fmcp-card.d.ts.map +0 -1
  255. package/esm/web-components/elements/fmcp-input.d.ts +0 -96
  256. package/esm/web-components/elements/fmcp-input.d.ts.map +0 -1
  257. package/esm/web-components/elements/fmcp-select.d.ts +0 -100
  258. package/esm/web-components/elements/fmcp-select.d.ts.map +0 -1
  259. package/esm/web-components/elements/index.d.ts +0 -13
  260. package/esm/web-components/elements/index.d.ts.map +0 -1
  261. package/esm/web-components/index.d.ts +0 -50
  262. package/esm/web-components/index.d.ts.map +0 -1
  263. package/esm/web-components/register.d.ts +0 -57
  264. package/esm/web-components/register.d.ts.map +0 -1
  265. package/esm/web-components/types.d.ts +0 -122
  266. package/esm/web-components/types.d.ts.map +0 -1
  267. package/esm/widgets/index.d.ts +0 -8
  268. package/esm/widgets/index.d.ts.map +0 -1
  269. package/esm/widgets/index.js +0 -883
  270. package/esm/widgets/progress.d.ts +0 -133
  271. package/esm/widgets/progress.d.ts.map +0 -1
  272. package/esm/widgets/resource.d.ts +0 -163
  273. package/esm/widgets/resource.d.ts.map +0 -1
  274. package/pages/consent.d.ts +0 -117
  275. package/pages/consent.d.ts.map +0 -1
  276. package/pages/error.d.ts +0 -101
  277. package/pages/error.d.ts.map +0 -1
  278. package/pages/index.d.ts +0 -9
  279. package/pages/index.d.ts.map +0 -1
  280. package/pages/index.js +0 -1065
  281. package/react/utils.d.ts +0 -43
  282. package/react/utils.d.ts.map +0 -1
  283. package/widgets/index.d.ts +0 -8
  284. package/widgets/index.d.ts.map +0 -1
  285. package/widgets/index.js +0 -910
  286. package/widgets/progress.d.ts +0 -133
  287. package/widgets/progress.d.ts.map +0 -1
  288. package/widgets/resource.d.ts +0 -163
  289. package/widgets/resource.d.ts.map +0 -1
  290. /package/esm/components/{index.js → index.mjs} +0 -0
  291. /package/esm/render/{index.js → index.mjs} +0 -0
  292. /package/esm/web-components/{index.js → index.mjs} +0 -0
package/bundler/index.js CHANGED
@@ -5,9 +5,6 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __esm = (fn, res) => function __init() {
9
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
- };
11
8
  var __export = (target, all) => {
12
9
  for (var name in all)
13
10
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -30,678 +27,56 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
30
27
  ));
31
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
29
 
33
- // libs/ui/src/bundler/file-cache/storage/interface.ts
34
- function calculateManifestSize(manifest) {
35
- try {
36
- return Buffer.byteLength(JSON.stringify(manifest), "utf8");
37
- } catch {
38
- return 0;
39
- }
40
- }
41
- var DEFAULT_STORAGE_OPTIONS;
42
- var init_interface = __esm({
43
- "libs/ui/src/bundler/file-cache/storage/interface.ts"() {
44
- "use strict";
45
- DEFAULT_STORAGE_OPTIONS = {
46
- maxEntries: 1e3,
47
- maxSize: 100 * 1024 * 1024,
48
- // 100MB
49
- defaultTtl: 24 * 60 * 60,
50
- // 24 hours
51
- compress: false
52
- };
53
- }
54
- });
55
-
56
- // libs/ui/src/bundler/file-cache/storage/filesystem.ts
57
- var filesystem_exports = {};
58
- __export(filesystem_exports, {
59
- CacheInitializationError: () => CacheInitializationError,
60
- CacheOperationError: () => CacheOperationError,
61
- FilesystemStorage: () => FilesystemStorage,
62
- StorageNotInitializedError: () => StorageNotInitializedError,
63
- createFilesystemStorage: () => createFilesystemStorage
64
- });
65
- function createFilesystemStorage(options) {
66
- return new FilesystemStorage(options);
67
- }
68
- var import_promises, import_path, import_fs, import_crypto, CacheInitializationError, CacheOperationError, StorageNotInitializedError, DEFAULT_FS_OPTIONS, FilesystemStorage;
69
- var init_filesystem = __esm({
70
- "libs/ui/src/bundler/file-cache/storage/filesystem.ts"() {
71
- "use strict";
72
- import_promises = require("fs/promises");
73
- import_path = require("path");
74
- import_fs = require("fs");
75
- import_crypto = require("crypto");
76
- init_interface();
77
- CacheInitializationError = class extends Error {
78
- cause;
79
- constructor(message, cause) {
80
- super(message);
81
- this.name = "CacheInitializationError";
82
- this.cause = cause;
83
- }
84
- };
85
- CacheOperationError = class extends Error {
86
- cause;
87
- constructor(message, cause) {
88
- super(message);
89
- this.name = "CacheOperationError";
90
- this.cause = cause;
91
- }
92
- };
93
- StorageNotInitializedError = class extends Error {
94
- constructor() {
95
- super("Storage not initialized. Call initialize() first.");
96
- this.name = "StorageNotInitializedError";
97
- }
98
- };
99
- DEFAULT_FS_OPTIONS = {
100
- ...DEFAULT_STORAGE_OPTIONS,
101
- cacheDir: ".frontmcp-cache/builds",
102
- extension: ".json"
103
- };
104
- FilesystemStorage = class {
105
- type = "filesystem";
106
- options;
107
- initialized = false;
108
- stats = {
109
- entries: 0,
110
- totalSize: 0,
111
- hits: 0,
112
- misses: 0,
113
- hitRate: 0
114
- };
115
- constructor(options = {}) {
116
- this.options = {
117
- ...DEFAULT_FS_OPTIONS,
118
- ...options
119
- };
120
- }
121
- /**
122
- * Initialize the storage directory.
123
- */
124
- async initialize() {
125
- if (this.initialized) return;
126
- try {
127
- await (0, import_promises.mkdir)(this.options.cacheDir, { recursive: true });
128
- await this.loadStats();
129
- this.initialized = true;
130
- } catch (error) {
131
- throw new CacheInitializationError(`Failed to initialize cache directory: ${error}`, error);
132
- }
133
- }
134
- /**
135
- * Get a cached manifest.
136
- */
137
- async get(key) {
138
- this.ensureInitialized();
139
- const filePath = this.getFilePath(key);
140
- try {
141
- if (!(0, import_fs.existsSync)(filePath)) {
142
- this.stats.misses++;
143
- this.updateHitRate();
144
- return void 0;
145
- }
146
- const content = await (0, import_promises.readFile)(filePath, "utf8");
147
- const entry = JSON.parse(content);
148
- if (Date.now() > entry.metadata.expiresAt) {
149
- await this.delete(key);
150
- this.stats.misses++;
151
- this.updateHitRate();
152
- return void 0;
153
- }
154
- entry.metadata.lastAccessedAt = Date.now();
155
- entry.metadata.accessCount++;
156
- this.writeEntry(filePath, entry).catch((err) => {
157
- if (process.env["DEBUG"]) {
158
- console.debug(`[FilesystemStorage] Failed to update cache metadata for ${key}: ${err}`);
159
- }
160
- });
161
- this.stats.hits++;
162
- this.updateHitRate();
163
- return entry.data;
164
- } catch {
165
- this.stats.misses++;
166
- this.updateHitRate();
167
- return void 0;
168
- }
169
- }
170
- /**
171
- * Store a manifest in cache.
172
- */
173
- async set(key, manifest, ttl) {
174
- this.ensureInitialized();
175
- const filePath = this.getFilePath(key);
176
- const size = calculateManifestSize(manifest);
177
- const effectiveTtl = ttl ?? this.options.defaultTtl;
178
- await this.ensureCapacity(size);
179
- const entry = {
180
- data: manifest,
181
- metadata: {
182
- key,
183
- size,
184
- createdAt: Date.now(),
185
- expiresAt: Date.now() + effectiveTtl * 1e3,
186
- lastAccessedAt: Date.now(),
187
- accessCount: 0
188
- }
189
- };
190
- await this.writeEntry(filePath, entry);
191
- this.stats.entries++;
192
- this.stats.totalSize += size;
193
- }
194
- /**
195
- * Check if a key exists.
196
- */
197
- async has(key) {
198
- this.ensureInitialized();
199
- const filePath = this.getFilePath(key);
200
- try {
201
- if (!(0, import_fs.existsSync)(filePath)) return false;
202
- const content = await (0, import_promises.readFile)(filePath, "utf8");
203
- const entry = JSON.parse(content);
204
- if (Date.now() > entry.metadata.expiresAt) {
205
- await this.delete(key);
206
- return false;
207
- }
208
- return true;
209
- } catch {
210
- return false;
211
- }
212
- }
213
- /**
214
- * Delete a cached entry.
215
- */
216
- async delete(key) {
217
- this.ensureInitialized();
218
- const filePath = this.getFilePath(key);
219
- try {
220
- if (!(0, import_fs.existsSync)(filePath)) return false;
221
- const content = await (0, import_promises.readFile)(filePath, "utf8");
222
- const entry = JSON.parse(content);
223
- await (0, import_promises.unlink)(filePath);
224
- this.stats.entries = Math.max(0, this.stats.entries - 1);
225
- this.stats.totalSize = Math.max(0, this.stats.totalSize - entry.metadata.size);
226
- return true;
227
- } catch {
228
- return false;
229
- }
230
- }
231
- /**
232
- * Clear all cached entries.
233
- */
234
- async clear() {
235
- this.ensureInitialized();
236
- try {
237
- await (0, import_promises.rm)(this.options.cacheDir, { recursive: true, force: true });
238
- await (0, import_promises.mkdir)(this.options.cacheDir, { recursive: true });
239
- this.stats = {
240
- entries: 0,
241
- totalSize: 0,
242
- hits: 0,
243
- misses: 0,
244
- hitRate: 0
245
- };
246
- } catch (error) {
247
- throw new CacheOperationError(`Failed to clear cache: ${error}`, error);
248
- }
249
- }
250
- /**
251
- * Get cache statistics.
252
- */
253
- async getStats() {
254
- return { ...this.stats };
255
- }
256
- /**
257
- * Clean up expired entries.
258
- */
259
- async cleanup() {
260
- this.ensureInitialized();
261
- let removed = 0;
262
- try {
263
- const files = await (0, import_promises.readdir)(this.options.cacheDir);
264
- for (const file of files) {
265
- if (!file.endsWith(this.options.extension)) continue;
266
- const filePath = (0, import_path.join)(this.options.cacheDir, file);
267
- try {
268
- const content = await (0, import_promises.readFile)(filePath, "utf8");
269
- const entry = JSON.parse(content);
270
- if (Date.now() > entry.metadata.expiresAt) {
271
- await (0, import_promises.unlink)(filePath);
272
- this.stats.entries = Math.max(0, this.stats.entries - 1);
273
- this.stats.totalSize = Math.max(0, this.stats.totalSize - entry.metadata.size);
274
- removed++;
275
- }
276
- } catch {
277
- await (0, import_promises.unlink)(filePath).catch(() => {
278
- });
279
- removed++;
280
- }
281
- }
282
- } catch {
283
- }
284
- return removed;
285
- }
286
- /**
287
- * Close the storage (no-op for filesystem).
288
- */
289
- async close() {
290
- }
291
- /**
292
- * Get the file path for a cache key.
293
- * Uses SHA-256 hash to avoid collisions from key sanitization.
294
- */
295
- getFilePath(key) {
296
- const hash = (0, import_crypto.createHash)("sha256").update(key).digest("hex").slice(0, 16);
297
- return (0, import_path.join)(this.options.cacheDir, `${hash}${this.options.extension}`);
298
- }
299
- /**
300
- * Write a cache entry to disk.
301
- */
302
- async writeEntry(filePath, entry) {
303
- await (0, import_promises.mkdir)((0, import_path.dirname)(filePath), { recursive: true });
304
- await (0, import_promises.writeFile)(filePath, JSON.stringify(entry, null, 2), "utf8");
305
- }
306
- /**
307
- * Ensure the storage is initialized.
308
- */
309
- ensureInitialized() {
310
- if (!this.initialized) {
311
- throw new StorageNotInitializedError();
312
- }
313
- }
314
- /**
315
- * Load stats from existing cache files.
316
- * Reads entry metadata to get accurate manifest sizes.
317
- */
318
- async loadStats() {
319
- try {
320
- const files = await (0, import_promises.readdir)(this.options.cacheDir);
321
- let entries = 0;
322
- let totalSize = 0;
323
- for (const file of files) {
324
- if (!file.endsWith(this.options.extension)) continue;
325
- const filePath = (0, import_path.join)(this.options.cacheDir, file);
326
- try {
327
- const content = await (0, import_promises.readFile)(filePath, "utf8");
328
- const entry = JSON.parse(content);
329
- entries++;
330
- totalSize += entry.metadata.size;
331
- } catch {
332
- }
333
- }
334
- this.stats.entries = entries;
335
- this.stats.totalSize = totalSize;
336
- } catch {
337
- }
338
- }
339
- /**
340
- * Ensure there's capacity for a new entry.
341
- */
342
- async ensureCapacity(newEntrySize) {
343
- if (this.stats.entries >= this.options.maxEntries) {
344
- await this.evictLRU();
345
- }
346
- while (this.stats.totalSize + newEntrySize > this.options.maxSize) {
347
- const evicted = await this.evictLRU();
348
- if (!evicted) break;
349
- }
350
- }
351
- /**
352
- * Evict the least recently used entry.
353
- */
354
- async evictLRU() {
355
- try {
356
- const files = await (0, import_promises.readdir)(this.options.cacheDir);
357
- let oldestKey = null;
358
- let oldestTime = Infinity;
359
- let corruptedFile = null;
360
- for (const file of files) {
361
- if (!file.endsWith(this.options.extension)) continue;
362
- const filePath = (0, import_path.join)(this.options.cacheDir, file);
363
- try {
364
- const content = await (0, import_promises.readFile)(filePath, "utf8");
365
- const entry = JSON.parse(content);
366
- if (entry.metadata.lastAccessedAt < oldestTime) {
367
- oldestTime = entry.metadata.lastAccessedAt;
368
- oldestKey = entry.metadata.key;
369
- }
370
- } catch {
371
- corruptedFile = filePath;
372
- }
373
- }
374
- if (corruptedFile) {
375
- try {
376
- await (0, import_promises.unlink)(corruptedFile);
377
- this.stats.entries = Math.max(0, this.stats.entries - 1);
378
- return true;
379
- } catch {
380
- return false;
381
- }
382
- }
383
- if (oldestKey) {
384
- return await this.delete(oldestKey);
385
- }
386
- return false;
387
- } catch {
388
- return false;
389
- }
390
- }
391
- /**
392
- * Update hit rate statistic.
393
- */
394
- updateHitRate() {
395
- const total = this.stats.hits + this.stats.misses;
396
- this.stats.hitRate = total > 0 ? this.stats.hits / total : 0;
397
- }
398
- };
399
- }
400
- });
401
-
402
- // libs/ui/src/bundler/file-cache/storage/redis.ts
403
- var redis_exports = {};
404
- __export(redis_exports, {
405
- RedisStorage: () => RedisStorage,
406
- createRedisStorage: () => createRedisStorage
407
- });
408
- function createRedisStorage(options) {
409
- return new RedisStorage(options);
410
- }
411
- var STATS_KEY_SUFFIX, RedisStorage;
412
- var init_redis = __esm({
413
- "libs/ui/src/bundler/file-cache/storage/redis.ts"() {
414
- "use strict";
415
- init_interface();
416
- STATS_KEY_SUFFIX = ":__stats__";
417
- RedisStorage = class {
418
- type = "redis";
419
- options;
420
- initialized = false;
421
- localStats = {
422
- entries: 0,
423
- totalSize: 0,
424
- hits: 0,
425
- misses: 0,
426
- hitRate: 0
427
- };
428
- constructor(options) {
429
- if (!options.client) {
430
- throw new Error("Redis client is required");
431
- }
432
- this.options = {
433
- ...DEFAULT_STORAGE_OPTIONS,
434
- keyPrefix: "frontmcp:ui:build:",
435
- json: true,
436
- ...options
437
- };
438
- }
439
- /**
440
- * Initialize the Redis connection.
441
- */
442
- async initialize() {
443
- if (this.initialized) return;
444
- try {
445
- await this.options.client.ping();
446
- await this.loadStats();
447
- this.initialized = true;
448
- } catch (error) {
449
- throw new Error(`Failed to connect to Redis: ${error}`);
450
- }
451
- }
452
- /**
453
- * Get a cached manifest.
454
- */
455
- async get(key) {
456
- this.ensureInitialized();
457
- const redisKey = this.getRedisKey(key);
458
- try {
459
- const data = await this.options.client.get(redisKey);
460
- if (!data) {
461
- this.localStats.misses++;
462
- this.updateHitRate();
463
- await this.persistStats();
464
- return void 0;
465
- }
466
- const entry = JSON.parse(data);
467
- entry.metadata.lastAccessedAt = Date.now();
468
- entry.metadata.accessCount++;
469
- const ttl = await this.options.client.ttl(redisKey);
470
- if (ttl > 0) {
471
- const serialized = JSON.stringify(entry);
472
- await this.options.client.setex(redisKey, ttl, serialized);
473
- }
474
- this.localStats.hits++;
475
- this.updateHitRate();
476
- await this.persistStats();
477
- return entry.data;
478
- } catch (error) {
479
- console.warn?.(`Redis cache get failed for key "${key}": ${error}`);
480
- this.localStats.misses++;
481
- this.updateHitRate();
482
- await this.persistStats().catch(() => {
483
- });
484
- return void 0;
485
- }
486
- }
487
- /**
488
- * Store a manifest in cache.
489
- */
490
- async set(key, manifest, ttl) {
491
- this.ensureInitialized();
492
- const redisKey = this.getRedisKey(key);
493
- const size = calculateManifestSize(manifest);
494
- const effectiveTtl = ttl ?? this.options.defaultTtl;
495
- const entry = {
496
- data: manifest,
497
- metadata: {
498
- key,
499
- size,
500
- createdAt: Date.now(),
501
- expiresAt: Date.now() + effectiveTtl * 1e3,
502
- lastAccessedAt: Date.now(),
503
- accessCount: 0
504
- }
505
- };
506
- const serialized = JSON.stringify(entry);
507
- await this.options.client.setex(redisKey, effectiveTtl, serialized);
508
- this.localStats.entries++;
509
- this.localStats.totalSize += size;
510
- await this.persistStats();
511
- }
512
- /**
513
- * Check if a key exists.
514
- */
515
- async has(key) {
516
- this.ensureInitialized();
517
- const redisKey = this.getRedisKey(key);
518
- const exists = await this.options.client.exists(redisKey);
519
- return exists > 0;
520
- }
521
- /**
522
- * Delete a cached entry.
523
- */
524
- async delete(key) {
525
- this.ensureInitialized();
526
- const redisKey = this.getRedisKey(key);
527
- try {
528
- const data = await this.options.client.get(redisKey);
529
- if (data) {
530
- const entry = JSON.parse(data);
531
- this.localStats.totalSize = Math.max(0, this.localStats.totalSize - entry.metadata.size);
532
- }
533
- } catch {
534
- }
535
- const deleted = await this.options.client.del(redisKey);
536
- if (deleted > 0) {
537
- this.localStats.entries = Math.max(0, this.localStats.entries - 1);
538
- await this.persistStats();
539
- return true;
540
- }
541
- return false;
542
- }
543
- /**
544
- * Clear all cached entries.
545
- */
546
- async clear() {
547
- this.ensureInitialized();
548
- const pattern = `${this.options.keyPrefix}*`;
549
- const keys = await this.options.client.keys(pattern);
550
- if (keys.length > 0) {
551
- await this.options.client.del(keys);
552
- }
553
- this.localStats = {
554
- entries: 0,
555
- totalSize: 0,
556
- hits: 0,
557
- misses: 0,
558
- hitRate: 0
559
- };
560
- await this.persistStats();
561
- }
562
- /**
563
- * Get cache statistics.
564
- */
565
- async getStats() {
566
- await this.loadStats();
567
- return { ...this.localStats };
568
- }
569
- /**
570
- * Clean up expired entries.
571
- * Redis handles TTL expiration automatically, so this just refreshes stats.
572
- */
573
- async cleanup() {
574
- this.ensureInitialized();
575
- const pattern = `${this.options.keyPrefix}*`;
576
- const keys = await this.options.client.keys(pattern);
577
- const dataKeys = keys.filter((k) => !k.endsWith(STATS_KEY_SUFFIX));
578
- const previousCount = this.localStats.entries;
579
- this.localStats.entries = dataKeys.length;
580
- let totalSize = 0;
581
- for (const key of dataKeys) {
582
- try {
583
- const data = await this.options.client.get(key);
584
- if (data) {
585
- const entry = JSON.parse(data);
586
- totalSize += entry.metadata.size;
587
- }
588
- } catch {
589
- }
590
- }
591
- this.localStats.totalSize = totalSize;
592
- await this.persistStats();
593
- return Math.max(0, previousCount - this.localStats.entries);
594
- }
595
- /**
596
- * Close the Redis connection.
597
- */
598
- async close() {
599
- await this.options.client.quit();
600
- }
601
- /**
602
- * Get the Redis key for a cache key.
603
- */
604
- getRedisKey(key) {
605
- return `${this.options.keyPrefix}${key}`;
606
- }
607
- /**
608
- * Get the Redis key for stats.
609
- */
610
- getStatsKey() {
611
- return `${this.options.keyPrefix}${STATS_KEY_SUFFIX}`;
612
- }
613
- /**
614
- * Ensure the storage is initialized.
615
- */
616
- ensureInitialized() {
617
- if (!this.initialized) {
618
- throw new Error("Storage not initialized. Call initialize() first.");
619
- }
620
- }
621
- /**
622
- * Load stats from Redis.
623
- */
624
- async loadStats() {
625
- try {
626
- const statsKey = this.getStatsKey();
627
- const data = await this.options.client.get(statsKey);
628
- if (data) {
629
- const savedStats = JSON.parse(data);
630
- this.localStats = {
631
- ...this.localStats,
632
- ...savedStats
633
- };
634
- }
635
- } catch {
636
- }
637
- }
638
- /**
639
- * Persist stats to Redis.
640
- */
641
- async persistStats() {
642
- try {
643
- const statsKey = this.getStatsKey();
644
- const serialized = JSON.stringify(this.localStats);
645
- await this.options.client.set(statsKey, serialized);
646
- } catch {
647
- }
648
- }
649
- /**
650
- * Update hit rate statistic.
651
- */
652
- updateHitRate() {
653
- const total = this.localStats.hits + this.localStats.misses;
654
- this.localStats.hitRate = total > 0 ? this.localStats.hits / total : 0;
655
- }
656
- };
657
- }
658
- });
659
-
660
30
  // libs/ui/src/bundler/index.ts
661
31
  var bundler_exports = {};
662
32
  __export(bundler_exports, {
663
- BundlerCache: () => BundlerCache,
664
- ComponentBuilder: () => ComponentBuilder,
33
+ ALL_PLATFORMS: () => ALL_PLATFORMS,
34
+ BundlerCache: () => import_bundler4.BundlerCache,
35
+ ComponentBuilder: () => import_bundler7.ComponentBuilder,
665
36
  DEFAULT_BUNDLER_OPTIONS: () => DEFAULT_BUNDLER_OPTIONS,
666
37
  DEFAULT_BUNDLE_OPTIONS: () => DEFAULT_BUNDLE_OPTIONS,
667
38
  DEFAULT_SECURITY_POLICY: () => DEFAULT_SECURITY_POLICY,
668
39
  DEFAULT_STATIC_HTML_OPTIONS: () => DEFAULT_STATIC_HTML_OPTIONS,
669
- DEFAULT_STORAGE_OPTIONS: () => DEFAULT_STORAGE_OPTIONS,
670
- ExecutionError: () => ExecutionError,
671
- FilesystemStorage: () => FilesystemStorage,
40
+ DEFAULT_STORAGE_OPTIONS: () => import_bundler7.DEFAULT_STORAGE_OPTIONS,
41
+ ExecutionError: () => import_bundler2.ExecutionError,
42
+ FilesystemStorage: () => import_bundler7.FilesystemStorage,
43
+ HYBRID_DATA_PLACEHOLDER: () => HYBRID_DATA_PLACEHOLDER,
44
+ HYBRID_INPUT_PLACEHOLDER: () => HYBRID_INPUT_PLACEHOLDER,
672
45
  InMemoryBundler: () => InMemoryBundler,
673
- RedisStorage: () => RedisStorage,
46
+ RedisStorage: () => import_bundler7.RedisStorage,
674
47
  STATIC_HTML_CDN: () => STATIC_HTML_CDN,
675
- SecurityError: () => SecurityError,
676
- buildIdFromHash: () => buildIdFromHash,
677
- calculateComponentHash: () => calculateComponentHash,
678
- calculateManifestSize: () => calculateManifestSize,
679
- calculateQuickHash: () => calculateQuickHash,
48
+ SecurityError: () => import_bundler2.SecurityError,
49
+ buildIdFromHash: () => import_bundler7.buildIdFromHash,
50
+ calculateComponentHash: () => import_bundler7.calculateComponentHash,
51
+ calculateManifestSize: () => import_bundler7.calculateManifestSize,
52
+ calculateQuickHash: () => import_bundler7.calculateQuickHash,
680
53
  createBundler: () => createBundler,
681
- createCacheKey: () => createCacheKey,
682
- createFilesystemBuilder: () => createFilesystemBuilder,
683
- createFilesystemStorage: () => createFilesystemStorage,
684
- createRedisBuilder: () => createRedisBuilder,
685
- createRedisStorage: () => createRedisStorage,
686
- executeCode: () => executeCode,
687
- executeDefault: () => executeDefault,
688
- generateBuildId: () => generateBuildId,
54
+ createCacheKey: () => import_bundler4.createCacheKey,
55
+ createFilesystemBuilder: () => import_bundler7.createFilesystemBuilder,
56
+ createFilesystemStorage: () => import_bundler7.createFilesystemStorage,
57
+ createRedisBuilder: () => import_bundler7.createRedisBuilder,
58
+ createRedisStorage: () => import_bundler7.createRedisStorage,
59
+ executeCode: () => import_bundler6.executeCode,
60
+ executeDefault: () => import_bundler6.executeDefault,
61
+ generateBuildId: () => import_bundler7.generateBuildId,
689
62
  getCdnTypeForPlatform: () => getCdnTypeForPlatform,
690
- hashContent: () => hashContent,
691
- hashFile: () => hashFile,
692
- hashFiles: () => hashFiles,
693
- isExecutionError: () => isExecutionError,
694
- mergePolicy: () => mergePolicy,
695
- sha256: () => sha256,
696
- sha256Buffer: () => sha256Buffer,
697
- throwOnViolations: () => throwOnViolations,
698
- validateImports: () => validateImports,
699
- validateSize: () => validateSize,
700
- validateSource: () => validateSource
63
+ hashContent: () => import_bundler4.hashContent,
64
+ hashFile: () => import_bundler7.hashFile,
65
+ hashFiles: () => import_bundler7.hashFiles,
66
+ isExecutionError: () => import_bundler6.isExecutionError,
67
+ mergePolicy: () => import_bundler5.mergePolicy,
68
+ sha256: () => import_bundler7.sha256,
69
+ sha256Buffer: () => import_bundler7.sha256Buffer,
70
+ throwOnViolations: () => import_bundler5.throwOnViolations,
71
+ validateImports: () => import_bundler5.validateImports,
72
+ validateSize: () => import_bundler5.validateSize,
73
+ validateSource: () => import_bundler5.validateSource
701
74
  });
702
75
  module.exports = __toCommonJS(bundler_exports);
703
76
 
704
77
  // libs/ui/src/bundler/types.ts
78
+ var HYBRID_DATA_PLACEHOLDER = "__FRONTMCP_OUTPUT_PLACEHOLDER__";
79
+ var HYBRID_INPUT_PLACEHOLDER = "__FRONTMCP_INPUT_PLACEHOLDER__";
705
80
  var DEFAULT_SECURITY_POLICY = {
706
81
  allowedImports: [/^react$/, /^react-dom$/, /^react\/jsx-runtime$/, /^react\/jsx-dev-runtime$/, /^@frontmcp\/ui/],
707
82
  blockedImports: [
@@ -814,6 +189,13 @@ var DEFAULT_BUNDLER_OPTIONS = {
814
189
  verbose: false,
815
190
  esbuildOptions: {}
816
191
  };
192
+ var ALL_PLATFORMS = [
193
+ "openai",
194
+ "claude",
195
+ "cursor",
196
+ "ext-apps",
197
+ "generic"
198
+ ];
817
199
  var STATIC_HTML_CDN = {
818
200
  /**
819
201
  * ES modules from esm.sh (React 19, modern platforms)
@@ -829,635 +211,44 @@ var STATIC_HTML_CDN = {
829
211
  react: "https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js",
830
212
  reactDom: "https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"
831
213
  },
832
- /**
833
- * Tailwind CSS from cdnjs (cloudflare) - works on all platforms
834
- * Using CSS file instead of JS browser version to avoid style normalization issues
835
- */
836
- tailwind: "https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/3.4.1/tailwind.min.css",
837
214
  /**
838
215
  * Font CDN URLs
839
216
  */
840
217
  fonts: {
841
218
  preconnect: ["https://fonts.googleapis.com", "https://fonts.gstatic.com"],
842
- inter: "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
843
- }
844
- };
845
- function getCdnTypeForPlatform(platform) {
846
- if (platform === "claude") return "umd";
847
- return "esm";
848
- }
849
- var DEFAULT_STATIC_HTML_OPTIONS = {
850
- sourceType: "auto",
851
- targetPlatform: "auto",
852
- minify: true,
853
- skipCache: false,
854
- rootId: "frontmcp-widget-root",
855
- widgetAccessible: false,
856
- externals: {
857
- react: "cdn",
858
- reactDom: "cdn",
859
- tailwind: "cdn",
860
- frontmcpUi: "inline"
861
- },
862
- // Universal mode defaults
863
- universal: false,
864
- contentType: "auto",
865
- includeMarkdown: false,
866
- includeMdx: false
867
- };
868
-
869
- // libs/ui/src/bundler/cache.ts
870
- var BundlerCache = class {
871
- cache = /* @__PURE__ */ new Map();
872
- options;
873
- stats = {
874
- hits: 0,
875
- misses: 0,
876
- evictions: 0
877
- };
878
- constructor(options = {}) {
879
- this.options = {
880
- maxSize: options.maxSize ?? 100,
881
- ttl: options.ttl ?? 3e5
882
- };
883
- }
884
- /**
885
- * Get a cached bundle result.
886
- *
887
- * @param key - Cache key (typically content hash)
888
- * @returns Cached result or undefined if not found/expired
889
- */
890
- get(key) {
891
- const entry = this.cache.get(key);
892
- if (!entry) {
893
- this.stats.misses++;
894
- return void 0;
895
- }
896
- if (this.isExpired(entry)) {
897
- this.cache.delete(key);
898
- this.stats.misses++;
899
- this.stats.evictions++;
900
- return void 0;
901
- }
902
- entry.lastAccessedAt = Date.now();
903
- entry.accessCount++;
904
- this.stats.hits++;
905
- this.cache.delete(key);
906
- this.cache.set(key, entry);
907
- return entry.result;
908
- }
909
- /**
910
- * Store a bundle result in the cache.
911
- *
912
- * @param key - Cache key (typically content hash)
913
- * @param result - Bundle result to cache
914
- */
915
- set(key, result) {
916
- while (this.cache.size >= this.options.maxSize) {
917
- this.evictOldest();
918
- }
919
- const now = Date.now();
920
- const entry = {
921
- result,
922
- createdAt: now,
923
- lastAccessedAt: now,
924
- accessCount: 1
925
- };
926
- this.cache.set(key, entry);
927
- }
928
- /**
929
- * Check if a key exists in the cache (and is not expired).
930
- *
931
- * @param key - Cache key to check
932
- * @returns true if key exists and is not expired
933
- */
934
- has(key) {
935
- const entry = this.cache.get(key);
936
- if (!entry) return false;
937
- if (this.isExpired(entry)) {
938
- this.cache.delete(key);
939
- this.stats.evictions++;
940
- return false;
941
- }
942
- return true;
943
- }
944
- /**
945
- * Delete a specific entry from the cache.
946
- *
947
- * @param key - Cache key to delete
948
- * @returns true if the key was found and deleted
949
- */
950
- delete(key) {
951
- return this.cache.delete(key);
952
- }
953
- /**
954
- * Clear all entries from the cache.
955
- */
956
- clear() {
957
- this.cache.clear();
958
- this.stats = {
959
- hits: 0,
960
- misses: 0,
961
- evictions: 0
962
- };
963
- }
964
- /**
965
- * Get cache statistics.
966
- *
967
- * @returns Current cache statistics
968
- */
969
- getStats() {
970
- const total = this.stats.hits + this.stats.misses;
971
- const hitRate = total > 0 ? this.stats.hits / total : 0;
972
- let memoryUsage = 0;
973
- for (const entry of this.cache.values()) {
974
- memoryUsage += entry.result.size;
975
- if (entry.result.map) {
976
- memoryUsage += entry.result.map.length;
977
- }
978
- }
979
- return {
980
- size: this.cache.size,
981
- hits: this.stats.hits,
982
- misses: this.stats.misses,
983
- hitRate,
984
- evictions: this.stats.evictions,
985
- memoryUsage
986
- };
987
- }
988
- /**
989
- * Remove expired entries from the cache.
990
- *
991
- * @returns Number of entries removed
992
- */
993
- cleanup() {
994
- let removed = 0;
995
- for (const [key, entry] of this.cache.entries()) {
996
- if (this.isExpired(entry)) {
997
- this.cache.delete(key);
998
- removed++;
999
- }
1000
- }
1001
- this.stats.evictions += removed;
1002
- return removed;
1003
- }
1004
- /**
1005
- * Get all cache keys.
1006
- *
1007
- * @returns Array of cache keys
1008
- */
1009
- keys() {
1010
- return Array.from(this.cache.keys());
1011
- }
1012
- /**
1013
- * Get the number of entries in the cache.
1014
- */
1015
- get size() {
1016
- return this.cache.size;
1017
- }
1018
- /**
1019
- * Check if an entry is expired.
1020
- */
1021
- isExpired(entry) {
1022
- return Date.now() - entry.createdAt > this.options.ttl;
1023
- }
1024
- /**
1025
- * Evict the oldest (least recently used) entry.
1026
- */
1027
- evictOldest() {
1028
- const oldestKey = this.cache.keys().next().value;
1029
- if (oldestKey !== void 0) {
1030
- this.cache.delete(oldestKey);
1031
- this.stats.evictions++;
1032
- }
1033
- }
1034
- };
1035
- function hashContent(content) {
1036
- let hash = 2166136261;
1037
- for (let i = 0; i < content.length; i++) {
1038
- hash ^= content.charCodeAt(i);
1039
- hash = Math.imul(hash, 16777619);
1040
- }
1041
- return (hash >>> 0).toString(16).padStart(8, "0");
1042
- }
1043
- function createCacheKey(source, options) {
1044
- const sourceHash = hashContent(source);
1045
- const optionsHash = hashContent(
1046
- JSON.stringify({
1047
- sourceType: options.sourceType,
1048
- format: options.format,
1049
- minify: options.minify,
1050
- externals: options.externals?.sort(),
1051
- target: options.target
1052
- })
1053
- );
1054
- return `${sourceHash}-${optionsHash}`;
1055
- }
1056
-
1057
- // libs/ui/src/bundler/sandbox/policy.ts
1058
- var UNSAFE_PATTERNS = {
1059
- eval: /\beval\s*\(/g,
1060
- functionConstructor: /\bnew\s+Function\s*\(/g,
1061
- dynamicImport: /\bimport\s*\(/g,
1062
- require: /\brequire\s*\(/g,
1063
- processEnv: /\bprocess\.env\b/g,
1064
- globalThis: /\bglobalThis\b/g,
1065
- windowLocation: /\bwindow\.location\b/g,
1066
- documentCookie: /\bdocument\.cookie\b/g,
1067
- innerHTML: /\.innerHTML\s*=/g,
1068
- outerHTML: /\.outerHTML\s*=/g,
1069
- document_write: /\bdocument\.write\s*\(/g
1070
- };
1071
- function validateSource(source, policy = DEFAULT_SECURITY_POLICY) {
1072
- const violations = [];
1073
- if (policy.noEval !== false) {
1074
- const evalMatches = [...source.matchAll(UNSAFE_PATTERNS.eval)];
1075
- for (const match of evalMatches) {
1076
- violations.push({
1077
- type: "eval-usage",
1078
- message: "eval() is not allowed for security reasons",
1079
- location: getLocation(source, match.index ?? 0),
1080
- value: match[0]
1081
- });
1082
- }
1083
- const fnMatches = [...source.matchAll(UNSAFE_PATTERNS.functionConstructor)];
1084
- for (const match of fnMatches) {
1085
- violations.push({
1086
- type: "eval-usage",
1087
- message: "new Function() is not allowed for security reasons",
1088
- location: getLocation(source, match.index ?? 0),
1089
- value: match[0]
1090
- });
1091
- }
1092
- }
1093
- if (policy.noDynamicImports !== false) {
1094
- const matches = [...source.matchAll(UNSAFE_PATTERNS.dynamicImport)];
1095
- for (const match of matches) {
1096
- violations.push({
1097
- type: "dynamic-import",
1098
- message: "Dynamic imports are not allowed for security reasons",
1099
- location: getLocation(source, match.index ?? 0),
1100
- value: match[0]
1101
- });
1102
- }
1103
- }
1104
- if (policy.noRequire !== false) {
1105
- const matches = [...source.matchAll(UNSAFE_PATTERNS.require)];
1106
- for (const match of matches) {
1107
- violations.push({
1108
- type: "require-usage",
1109
- message: "require() is not allowed for security reasons",
1110
- location: getLocation(source, match.index ?? 0),
1111
- value: match[0]
1112
- });
1113
- }
1114
- }
1115
- const importViolations = validateImports(source, policy);
1116
- violations.push(...importViolations);
1117
- return violations;
1118
- }
1119
- function validateImports(source, policy = DEFAULT_SECURITY_POLICY) {
1120
- const violations = [];
1121
- const importPattern = /import\s+(?:(?:\{[^}]*\}|[\w*]+)\s+from\s+)?['"]([^'"]+)['"]/g;
1122
- const imports = [];
1123
- let match;
1124
- while ((match = importPattern.exec(source)) !== null) {
1125
- imports.push({ module: match[1], index: match.index });
1126
- }
1127
- const requirePattern = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
1128
- while ((match = requirePattern.exec(source)) !== null) {
1129
- imports.push({ module: match[1], index: match.index });
1130
- }
1131
- for (const imp of imports) {
1132
- if (policy.blockedImports) {
1133
- for (const blocked of policy.blockedImports) {
1134
- if (blocked.test(imp.module)) {
1135
- violations.push({
1136
- type: "blocked-import",
1137
- message: `Import '${imp.module}' is blocked by security policy`,
1138
- location: getLocation(source, imp.index),
1139
- value: imp.module
1140
- });
1141
- break;
1142
- }
1143
- }
1144
- }
1145
- if (policy.allowedImports && policy.allowedImports.length > 0) {
1146
- const isAllowed = policy.allowedImports.some((pattern) => pattern.test(imp.module));
1147
- if (!isAllowed) {
1148
- const alreadyBlocked = violations.some((v) => v.type === "blocked-import" && v.value === imp.module);
1149
- if (!alreadyBlocked) {
1150
- violations.push({
1151
- type: "disallowed-import",
1152
- message: `Import '${imp.module}' is not in the allowed imports list`,
1153
- location: getLocation(source, imp.index),
1154
- value: imp.module
1155
- });
1156
- }
1157
- }
1158
- }
1159
- }
1160
- return violations;
1161
- }
1162
- function validateSize(size, policy = DEFAULT_SECURITY_POLICY) {
1163
- const maxSize = policy.maxBundleSize ?? DEFAULT_SECURITY_POLICY.maxBundleSize ?? 512e3;
1164
- if (size > maxSize) {
1165
- return {
1166
- type: "size-exceeded",
1167
- message: `Bundle size (${formatBytes(size)}) exceeds maximum allowed (${formatBytes(maxSize)})`,
1168
- value: String(size)
1169
- };
1170
- }
1171
- return void 0;
1172
- }
1173
- function mergePolicy(userPolicy) {
1174
- if (!userPolicy) {
1175
- return { ...DEFAULT_SECURITY_POLICY };
1176
- }
1177
- return {
1178
- allowedImports: userPolicy.allowedImports ?? DEFAULT_SECURITY_POLICY.allowedImports,
1179
- blockedImports: userPolicy.blockedImports ?? DEFAULT_SECURITY_POLICY.blockedImports,
1180
- maxBundleSize: userPolicy.maxBundleSize ?? DEFAULT_SECURITY_POLICY.maxBundleSize,
1181
- maxTransformTime: userPolicy.maxTransformTime ?? DEFAULT_SECURITY_POLICY.maxTransformTime,
1182
- noEval: userPolicy.noEval ?? DEFAULT_SECURITY_POLICY.noEval,
1183
- noDynamicImports: userPolicy.noDynamicImports ?? DEFAULT_SECURITY_POLICY.noDynamicImports,
1184
- noRequire: userPolicy.noRequire ?? DEFAULT_SECURITY_POLICY.noRequire,
1185
- allowedGlobals: userPolicy.allowedGlobals ?? DEFAULT_SECURITY_POLICY.allowedGlobals
1186
- };
1187
- }
1188
- function getLocation(source, index) {
1189
- const lines = source.slice(0, index).split("\n");
1190
- return {
1191
- line: lines.length,
1192
- column: lines[lines.length - 1].length + 1
1193
- };
1194
- }
1195
- function formatBytes(bytes) {
1196
- if (bytes < 1024) return `${bytes} B`;
1197
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
1198
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1199
- }
1200
- var SecurityError = class extends Error {
1201
- violations;
1202
- constructor(message, violations) {
1203
- super(message);
1204
- this.name = "SecurityError";
1205
- this.violations = violations;
1206
- }
1207
- };
1208
- function throwOnViolations(violations) {
1209
- if (violations.length > 0) {
1210
- const message = violations.map((v) => v.message).join("; ");
1211
- throw new SecurityError(`Security policy violation: ${message}`, violations);
1212
- }
1213
- }
1214
-
1215
- // libs/ui/src/bundler/sandbox/enclave-adapter.ts
1216
- var import_enclave_vm = require("enclave-vm");
1217
- var DEFAULT_ENCLAVE_OPTIONS = {
1218
- securityLevel: "SECURE",
1219
- timeout: 5e3,
1220
- maxIterations: 1e4,
1221
- validate: true,
1222
- transform: true
1223
- };
1224
- var STRICT_SECURITY_BLOCKED_IMPORTS_THRESHOLD = 10;
1225
- function mapSecurityLevel(policy) {
1226
- if (policy?.blockedImports && policy.blockedImports.length > STRICT_SECURITY_BLOCKED_IMPORTS_THRESHOLD) {
1227
- return "STRICT";
1228
- }
1229
- return "SECURE";
1230
- }
1231
- function createJSXRuntime(React) {
1232
- const R = React;
1233
- return {
1234
- jsx: (type, props, key) => {
1235
- const { children, ...rest } = props;
1236
- return R.createElement(type, key ? { ...rest, key } : rest, children);
1237
- },
1238
- jsxs: (type, props, key) => {
1239
- const { children, ...rest } = props;
1240
- return R.createElement(type, key ? { ...rest, key } : rest, children);
1241
- },
1242
- jsxDEV: (type, props, key, _isStaticChildren, _source, _self) => {
1243
- const { children, ...rest } = props;
1244
- return R.createElement(type, key ? { ...rest, key } : rest, children);
1245
- },
1246
- Fragment: R.Fragment
1247
- };
1248
- }
1249
- var DANGEROUS_GLOBAL_KEYS = /* @__PURE__ */ new Set([
1250
- "process",
1251
- "require",
1252
- "__dirname",
1253
- "__filename",
1254
- "Buffer",
1255
- "eval",
1256
- "Function",
1257
- "constructor",
1258
- "global",
1259
- "globalThis",
1260
- "module",
1261
- "exports",
1262
- "__proto__"
1263
- ]);
1264
- function sanitizeGlobalKey(key) {
1265
- return key.replace(/[^a-zA-Z0-9_$]/g, "_");
1266
- }
1267
- function buildGlobals(context) {
1268
- const globals = {};
1269
- if (context.React) {
1270
- globals["React"] = context.React;
1271
- }
1272
- if (context.ReactDOM) {
1273
- globals["ReactDOM"] = context.ReactDOM;
1274
- }
1275
- if (context.React) {
1276
- const jsxRuntime = createJSXRuntime(context.React);
1277
- globals["__jsx"] = jsxRuntime["jsx"];
1278
- globals["__jsxs"] = jsxRuntime["jsxs"];
1279
- globals["__jsxDEV"] = jsxRuntime["jsxDEV"];
1280
- globals["Fragment"] = jsxRuntime["Fragment"];
1281
- }
1282
- if (context.modules) {
1283
- for (const [key, value] of Object.entries(context.modules)) {
1284
- const sanitizedKey = sanitizeGlobalKey(key);
1285
- if (DANGEROUS_GLOBAL_KEYS.has(sanitizedKey)) {
1286
- throw new ExecutionError(
1287
- `Dangerous module key '${key}' (sanitized: '${sanitizedKey}') is not allowed in execution context`,
1288
- { code: "SECURITY_VIOLATION" }
1289
- );
1290
- }
1291
- globals[sanitizedKey] = value;
1292
- }
1293
- }
1294
- if (context.globals) {
1295
- for (const [key, value] of Object.entries(context.globals)) {
1296
- if (DANGEROUS_GLOBAL_KEYS.has(key)) {
1297
- throw new ExecutionError(`Dangerous global key '${key}' is not allowed in execution context`, {
1298
- code: "SECURITY_VIOLATION"
1299
- });
1300
- }
1301
- const sanitizedKey = sanitizeGlobalKey(key);
1302
- if (DANGEROUS_GLOBAL_KEYS.has(sanitizedKey)) {
1303
- throw new ExecutionError(
1304
- `Dangerous global key '${key}' (sanitized: '${sanitizedKey}') is not allowed in execution context`,
1305
- { code: "SECURITY_VIOLATION" }
1306
- );
1307
- }
1308
- globals[sanitizedKey] = value;
1309
- }
1310
- }
1311
- return globals;
1312
- }
1313
- function buildRequireFunction(context) {
1314
- const normalizedContextModules = {};
1315
- if (context.modules) {
1316
- for (const [key, value] of Object.entries(context.modules)) {
1317
- normalizedContextModules[key.toLowerCase()] = value;
1318
- }
1319
- }
1320
- const modules = {
1321
- react: context.React,
1322
- "react-dom": context.ReactDOM,
1323
- "react/jsx-runtime": context.React ? createJSXRuntime(context.React) : void 0,
1324
- "react/jsx-dev-runtime": context.React ? createJSXRuntime(context.React) : void 0,
1325
- ...normalizedContextModules
1326
- };
1327
- return (id) => {
1328
- const normalizedId = id.toLowerCase();
1329
- if (normalizedId in modules) {
1330
- const mod = modules[normalizedId];
1331
- if (mod === void 0) {
1332
- throw new Error(`Module '${id}' is not available. Did you forget to provide it in the context?`);
1333
- }
1334
- return mod;
1335
- }
1336
- throw new Error(`Module '${id}' is not available in the sandbox environment`);
1337
- };
1338
- }
1339
- async function executeCode(code, context = {}) {
1340
- const consoleOutput = [];
1341
- const globals = buildGlobals(context);
1342
- globals["console"] = {
1343
- log: (...args) => {
1344
- consoleOutput.push(args.map(String).join(" "));
1345
- },
1346
- info: (...args) => {
1347
- consoleOutput.push(`[INFO] ${args.map(String).join(" ")}`);
1348
- },
1349
- warn: (...args) => {
1350
- consoleOutput.push(`[WARN] ${args.map(String).join(" ")}`);
1351
- },
1352
- error: (...args) => {
1353
- consoleOutput.push(`[ERROR] ${args.map(String).join(" ")}`);
1354
- },
1355
- debug: (...args) => {
1356
- consoleOutput.push(`[DEBUG] ${args.map(String).join(" ")}`);
1357
- },
1358
- trace: () => {
1359
- },
1360
- dir: () => {
1361
- },
1362
- table: () => {
1363
- },
1364
- group: () => {
1365
- },
1366
- groupEnd: () => {
1367
- },
1368
- time: () => {
1369
- },
1370
- timeEnd: () => {
1371
- },
1372
- assert: () => {
1373
- },
1374
- clear: () => {
1375
- },
1376
- count: () => {
1377
- },
1378
- countReset: () => {
1379
- }
1380
- };
1381
- globals["require"] = buildRequireFunction(context);
1382
- const enclave = new import_enclave_vm.Enclave({
1383
- ...DEFAULT_ENCLAVE_OPTIONS,
1384
- timeout: context.timeout ?? DEFAULT_ENCLAVE_OPTIONS.timeout,
1385
- maxIterations: context.maxIterations ?? DEFAULT_ENCLAVE_OPTIONS.maxIterations,
1386
- securityLevel: mapSecurityLevel(context.security),
1387
- globals,
1388
- allowFunctionsInGlobals: true
1389
- // Required for React components
1390
- });
1391
- try {
1392
- const wrappedCode = `
1393
- const module = { exports: {} };
1394
- const exports = module.exports;
1395
- const __filename = 'widget.js';
1396
- const __dirname = '/';
1397
- ${code}
1398
- return module.exports;
1399
- `;
1400
- const result = await enclave.run(wrappedCode);
1401
- if (!result.success) {
1402
- const errorMessage = result.error?.message ?? "Execution failed";
1403
- const errorCode = result.error?.code;
1404
- if (errorCode === "TIMEOUT") {
1405
- throw new ExecutionError(`Execution timed out after ${context.timeout ?? DEFAULT_ENCLAVE_OPTIONS.timeout}ms`, {
1406
- code: "TIMEOUT"
1407
- });
1408
- }
1409
- if (errorCode === "MAX_ITERATIONS") {
1410
- throw new ExecutionError(
1411
- `Maximum iterations exceeded (${context.maxIterations ?? DEFAULT_ENCLAVE_OPTIONS.maxIterations})`,
1412
- {
1413
- code: "MAX_ITERATIONS"
1414
- }
1415
- );
1416
- }
1417
- if (errorCode === "VALIDATION_ERROR") {
1418
- throw new ExecutionError(`Security validation failed: ${errorMessage}`, { code: "SECURITY_VIOLATION" });
1419
- }
1420
- throw new ExecutionError(errorMessage, result.error);
1421
- }
1422
- return {
1423
- exports: result.value,
1424
- executionTime: result.stats.duration,
1425
- consoleOutput: consoleOutput.length > 0 ? consoleOutput : void 0
1426
- };
1427
- } finally {
1428
- enclave.dispose();
1429
- }
1430
- }
1431
- async function executeDefault(code, context = {}) {
1432
- const result = await executeCode(code, context);
1433
- if ("default" in result.exports) {
1434
- return result.exports.default;
1435
- }
1436
- const exportKeys = Object.keys(result.exports);
1437
- if (exportKeys.length === 0) {
1438
- throw new ExecutionError("Code did not export any values");
1439
- }
1440
- if (exportKeys.length === 1) {
1441
- return result.exports[exportKeys[0]];
1442
- }
1443
- return result.exports;
1444
- }
1445
- var ExecutionError = class extends Error {
1446
- /** Error code for categorization */
1447
- code;
1448
- constructor(message, cause) {
1449
- super(message, { cause });
1450
- this.name = "ExecutionError";
1451
- if (cause && typeof cause === "object" && "code" in cause) {
1452
- this.code = cause.code;
1453
- }
219
+ inter: "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
1454
220
  }
1455
221
  };
1456
- function isExecutionError(error) {
1457
- return error instanceof ExecutionError;
222
+ function getCdnTypeForPlatform(platform) {
223
+ if (platform === "claude") return "umd";
224
+ return "esm";
1458
225
  }
226
+ var DEFAULT_STATIC_HTML_OPTIONS = {
227
+ sourceType: "auto",
228
+ targetPlatform: "auto",
229
+ minify: true,
230
+ skipCache: false,
231
+ rootId: "frontmcp-widget-root",
232
+ widgetAccessible: false,
233
+ externals: {
234
+ react: "cdn",
235
+ reactDom: "cdn",
236
+ tailwind: "cdn",
237
+ frontmcpUi: "inline"
238
+ },
239
+ // Universal mode defaults
240
+ universal: false,
241
+ contentType: "auto",
242
+ includeMarkdown: false,
243
+ includeMdx: false,
244
+ // Build mode defaults
245
+ buildMode: "static"
246
+ };
1459
247
 
1460
248
  // libs/ui/src/bundler/bundler.ts
249
+ var import_adapters = require("@frontmcp/uipack/adapters");
250
+ var import_theme = require("@frontmcp/uipack/theme");
251
+ var import_bundler = require("@frontmcp/uipack/bundler");
1461
252
  var import_utils = require("@frontmcp/uipack/utils");
1462
253
 
1463
254
  // libs/ui/src/universal/types.ts
@@ -1497,6 +288,8 @@ function detectContentType(source) {
1497
288
  }
1498
289
 
1499
290
  // libs/ui/src/universal/cached-runtime.ts
291
+ var import_runtime = require("@frontmcp/uipack/runtime");
292
+ var import_build = require("@frontmcp/uipack/build");
1500
293
  var RUNTIME_PLACEHOLDERS = {
1501
294
  /** Placeholder for transpiled component code */
1502
295
  COMPONENT_CODE: "/*__FRONTMCP_COMPONENT_CODE__*/",
@@ -1522,6 +315,7 @@ function generateCacheKey(options) {
1522
315
  options.includeMarkdown ? "md" : "",
1523
316
  options.includeMdx ? "mdx" : "",
1524
317
  options.minify ? "min" : "",
318
+ options.includeBridge ? "bridge" : "",
1525
319
  securityKey
1526
320
  ].filter(Boolean).join(":");
1527
321
  }
@@ -1565,6 +359,17 @@ function buildStoreRuntime() {
1565
359
  context: state,
1566
360
  setContext: function(ctx) {
1567
361
  this.setState(ctx);
362
+ },
363
+ // Dynamic mode: update output and re-render
364
+ updateOutput: function(output) {
365
+ this.setState({ output: output, loading: false });
366
+ // Also update the global window variable for compatibility
367
+ window.__mcpToolOutput = output;
368
+ },
369
+ // Dynamic mode: update input and re-render
370
+ updateInput: function(input) {
371
+ this.setState({ input: input });
372
+ window.__mcpToolInput = input;
1568
373
  }
1569
374
  };
1570
375
 
@@ -1589,6 +394,32 @@ function buildStoreRuntime() {
1589
394
  window.useContent = function() {
1590
395
  return window.useFrontMCPStore().content;
1591
396
  };
397
+
398
+ // Connect to MCP Bridge for platform data detection
399
+ function initFromBridge() {
400
+ // Check for data from mcpBridge (handles OpenAI, ext-apps, etc.)
401
+ if (window.mcpBridge && window.mcpBridge.toolOutput != null) {
402
+ window.__frontmcp.setState({
403
+ output: window.mcpBridge.toolOutput,
404
+ loading: false
405
+ });
406
+ }
407
+
408
+ // Subscribe to bridge updates via onToolResult
409
+ if (window.mcpBridge && window.mcpBridge.onToolResult) {
410
+ window.mcpBridge.onToolResult(function(result) {
411
+ window.__frontmcp.updateOutput(result);
412
+ });
413
+ }
414
+ }
415
+
416
+ // Initialize from bridge when ready
417
+ if (window.mcpBridge) {
418
+ initFromBridge();
419
+ } else {
420
+ // Wait for bridge to be ready
421
+ window.addEventListener('mcp:bridge-ready', initFromBridge);
422
+ }
1592
423
  })();
1593
424
  `;
1594
425
  }
@@ -1808,71 +639,7 @@ function buildRenderersRuntime(options) {
1808
639
  `;
1809
640
  }
1810
641
  function buildUIComponentsRuntime() {
1811
- return `
1812
- // UI Components (Vendor)
1813
- (function() {
1814
- window.Card = function(props) {
1815
- var children = props.children;
1816
- var title = props.title;
1817
- var className = props.className || '';
1818
- return React.createElement('div', {
1819
- className: 'bg-white rounded-lg shadow border border-gray-200 overflow-hidden ' + className
1820
- }, [
1821
- title && React.createElement('div', {
1822
- key: 'header',
1823
- className: 'px-4 py-3 border-b border-gray-200 bg-gray-50'
1824
- }, React.createElement('h3', { className: 'text-sm font-medium text-gray-900' }, title)),
1825
- React.createElement('div', { key: 'body', className: 'p-4' }, children)
1826
- ]);
1827
- };
1828
-
1829
- window.Badge = function(props) {
1830
- var children = props.children;
1831
- var variant = props.variant || 'default';
1832
- var variantClasses = {
1833
- default: 'bg-gray-100 text-gray-800',
1834
- success: 'bg-green-100 text-green-800',
1835
- warning: 'bg-yellow-100 text-yellow-800',
1836
- error: 'bg-red-100 text-red-800',
1837
- info: 'bg-blue-100 text-blue-800'
1838
- };
1839
- return React.createElement('span', {
1840
- className: 'inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ' + (variantClasses[variant] || variantClasses.default)
1841
- }, children);
1842
- };
1843
-
1844
- window.Button = function(props) {
1845
- var children = props.children;
1846
- var variant = props.variant || 'primary';
1847
- var onClick = props.onClick;
1848
- var disabled = props.disabled;
1849
- var variantClasses = {
1850
- primary: 'bg-blue-600 text-white hover:bg-blue-700',
1851
- secondary: 'bg-gray-100 text-gray-900 hover:bg-gray-200',
1852
- outline: 'border border-gray-300 text-gray-700 hover:bg-gray-50',
1853
- danger: 'bg-red-600 text-white hover:bg-red-700'
1854
- };
1855
- return React.createElement('button', {
1856
- className: 'px-4 py-2 rounded-md text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 ' +
1857
- (disabled ? 'opacity-50 cursor-not-allowed ' : '') +
1858
- (variantClasses[variant] || variantClasses.primary),
1859
- onClick: onClick,
1860
- disabled: disabled
1861
- }, children);
1862
- };
1863
-
1864
- // Export to namespace
1865
- window.frontmcp_ui_namespaceObject = Object.assign({}, window.React || {}, {
1866
- useToolOutput: window.useToolOutput,
1867
- useToolInput: window.useToolInput,
1868
- useMcpBridgeContext: function() { return window.__frontmcp.context; },
1869
- useCallTool: function() { return function() { return Promise.resolve(null); }; },
1870
- Card: window.Card,
1871
- Badge: window.Badge,
1872
- Button: window.Button
1873
- });
1874
- })();
1875
- `;
642
+ return (0, import_build.buildUIComponentsRuntime)();
1876
643
  }
1877
644
  function buildUniversalAppRuntime() {
1878
645
  return `
@@ -1997,7 +764,11 @@ function getCachedRuntime(options, config = {}) {
1997
764
  }
1998
765
  runtimeCache.delete(cacheKey);
1999
766
  }
2000
- const vendorParts = [buildStoreRuntime(), buildRequireShim()];
767
+ const vendorParts = [];
768
+ if (options.includeBridge) {
769
+ vendorParts.push((0, import_runtime.getMCPBridgeScript)());
770
+ }
771
+ vendorParts.push(buildStoreRuntime(), buildRequireShim());
2001
772
  if (options.cdnType === "umd" || options.includeMarkdown) {
2002
773
  vendorParts.push(buildInlineMarkdownParser(options));
2003
774
  }
@@ -2036,12 +807,51 @@ function buildAppTemplate() {
2036
807
  return [buildCustomComponentsWrapper(), buildComponentWrapper(), buildDataInjectionWrapper()].join("\n");
2037
808
  }
2038
809
  function minifyScript(script) {
2039
- return script.replace(/\/\/[^\n]*/g, "").replace(/\/\*[\s\S]*?\*\//g, "").replace(/\n\s*\n/g, "\n").replace(/^\s+/gm, "").trim();
810
+ return script.replace(/\/\*[\s\S]*?\*\//g, "").replace(/^\s*\/\/[^\n]*$/gm, "").replace(/\n\s*\n/g, "\n").replace(/^\s+/gm, "").trim();
2040
811
  }
2041
812
  function buildAppScript(appTemplate, componentCode, dataInjection, customComponents = "") {
2042
813
  return appTemplate.replace(RUNTIME_PLACEHOLDERS.CUSTOM_COMPONENTS, customComponents || "// No custom components").replace(RUNTIME_PLACEHOLDERS.COMPONENT_CODE, componentCode || "// No component code").replace(RUNTIME_PLACEHOLDERS.DATA_INJECTION, dataInjection);
2043
814
  }
2044
- function buildDataInjectionCode(toolName, input, output, structuredContent, contentType, source, hasComponent) {
815
+ var DEFAULT_OUTPUT_PLACEHOLDER = "__FRONTMCP_OUTPUT_PLACEHOLDER__";
816
+ var DEFAULT_INPUT_PLACEHOLDER = "__FRONTMCP_INPUT_PLACEHOLDER__";
817
+ function buildDataInjectionCode(toolName, input, output, structuredContent, contentType, source, hasComponent, options) {
818
+ const buildMode = options?.buildMode ?? "static";
819
+ const cdnType = options?.cdnType ?? "esm";
820
+ switch (buildMode) {
821
+ case "dynamic":
822
+ return buildDynamicDataInjectionCode(
823
+ toolName,
824
+ input,
825
+ output,
826
+ structuredContent,
827
+ contentType,
828
+ source,
829
+ hasComponent,
830
+ cdnType,
831
+ options?.dynamicOptions
832
+ );
833
+ case "hybrid":
834
+ return buildHybridDataInjectionCode(
835
+ toolName,
836
+ structuredContent,
837
+ contentType,
838
+ source,
839
+ hasComponent,
840
+ options?.hybridOptions
841
+ );
842
+ default:
843
+ return buildStaticDataInjectionCode(
844
+ toolName,
845
+ input,
846
+ output,
847
+ structuredContent,
848
+ contentType,
849
+ source,
850
+ hasComponent
851
+ );
852
+ }
853
+ }
854
+ function buildStaticDataInjectionCode(toolName, input, output, structuredContent, contentType, source, hasComponent) {
2045
855
  const safeJson = (value) => {
2046
856
  try {
2047
857
  return JSON.stringify(value);
@@ -2051,6 +861,7 @@ function buildDataInjectionCode(toolName, input, output, structuredContent, cont
2051
861
  };
2052
862
  if (hasComponent) {
2053
863
  return `
864
+ // Static Mode - Data baked at build time
2054
865
  window.__frontmcp.setState({
2055
866
  toolName: ${safeJson(toolName)},
2056
867
  input: ${safeJson(input ?? null)},
@@ -2065,6 +876,7 @@ function buildDataInjectionCode(toolName, input, output, structuredContent, cont
2065
876
  });`;
2066
877
  }
2067
878
  return `
879
+ // Static Mode - Data baked at build time
2068
880
  window.__frontmcp.setState({
2069
881
  toolName: ${safeJson(toolName)},
2070
882
  input: ${safeJson(input ?? null)},
@@ -2078,6 +890,181 @@ function buildDataInjectionCode(toolName, input, output, structuredContent, cont
2078
890
  error: null
2079
891
  });`;
2080
892
  }
893
+ function buildDynamicDataInjectionCode(toolName, input, output, structuredContent, contentType, source, hasComponent, cdnType, dynamicOptions) {
894
+ if (cdnType === "umd") {
895
+ return buildDynamicWithPlaceholdersCode(
896
+ toolName,
897
+ structuredContent,
898
+ contentType,
899
+ source,
900
+ hasComponent,
901
+ dynamicOptions
902
+ );
903
+ }
904
+ return buildDynamicWithSubscriptionCode(
905
+ toolName,
906
+ input,
907
+ output,
908
+ structuredContent,
909
+ contentType,
910
+ source,
911
+ hasComponent,
912
+ dynamicOptions
913
+ );
914
+ }
915
+ function buildDynamicWithPlaceholdersCode(toolName, structuredContent, contentType, source, hasComponent, dynamicOptions) {
916
+ const safeJson = (value) => {
917
+ try {
918
+ return JSON.stringify(value);
919
+ } catch {
920
+ return "null";
921
+ }
922
+ };
923
+ const outputPlaceholder = DEFAULT_OUTPUT_PLACEHOLDER;
924
+ const inputPlaceholder = DEFAULT_INPUT_PLACEHOLDER;
925
+ const includeInitialData = dynamicOptions?.includeInitialData ?? true;
926
+ const contentBlock = hasComponent ? `content: { type: 'react', source: window.__frontmcp_component }` : `content: { type: ${safeJson(contentType)}, source: ${safeJson(source)} }`;
927
+ return `
928
+ // Dynamic Mode - Placeholder-based for non-OpenAI platforms
929
+ var __outputRaw = "${outputPlaceholder}";
930
+ var __inputRaw = "${inputPlaceholder}";
931
+ var __output = null;
932
+ var __input = null;
933
+ var __error = null;
934
+ var __outputNotReplaced = false;
935
+ var __includeInitialData = ${includeInitialData};
936
+
937
+ // Parse output placeholder
938
+ if (typeof __outputRaw === 'string' && __outputRaw !== "${outputPlaceholder}") {
939
+ try { __output = JSON.parse(__outputRaw); } catch (e) {
940
+ console.warn('[FrontMCP] Failed to parse output:', e);
941
+ __error = 'Failed to parse output data';
942
+ }
943
+ } else if (__outputRaw === "${outputPlaceholder}") {
944
+ __outputNotReplaced = true;
945
+ }
946
+
947
+ // Parse input placeholder
948
+ if (typeof __inputRaw === 'string' && __inputRaw !== "${inputPlaceholder}") {
949
+ try { __input = JSON.parse(__inputRaw); } catch (e) { console.warn('[FrontMCP] Failed to parse input:', e); }
950
+ }
951
+
952
+ // Handle placeholder not replaced - show error if expecting initial data
953
+ if (__outputNotReplaced && __includeInitialData) {
954
+ __error = 'No data provided. The output placeholder was not replaced.';
955
+ }
956
+
957
+ window.__frontmcp.setState({
958
+ toolName: ${safeJson(toolName)},
959
+ input: __input,
960
+ output: __output,
961
+ structuredContent: ${safeJson(structuredContent ?? null)},
962
+ ${contentBlock},
963
+ loading: !__includeInitialData && __output === null && !__error,
964
+ error: __error
965
+ });`;
966
+ }
967
+ function buildDynamicWithSubscriptionCode(toolName, input, output, structuredContent, contentType, source, hasComponent, dynamicOptions) {
968
+ const safeJson = (value) => {
969
+ try {
970
+ return JSON.stringify(value);
971
+ } catch {
972
+ return "null";
973
+ }
974
+ };
975
+ const includeInitialData = dynamicOptions?.includeInitialData ?? true;
976
+ const subscribeToUpdates = dynamicOptions?.subscribeToUpdates ?? true;
977
+ const contentBlock = hasComponent ? `content: { type: 'react', source: window.__frontmcp_component }` : `content: { type: ${safeJson(contentType)}, source: ${safeJson(source)} }`;
978
+ const initialState = includeInitialData ? `{
979
+ toolName: ${safeJson(toolName)},
980
+ input: ${safeJson(input ?? null)},
981
+ output: ${safeJson(output ?? null)},
982
+ structuredContent: ${safeJson(structuredContent ?? null)},
983
+ ${contentBlock},
984
+ loading: false,
985
+ error: null
986
+ }` : `{
987
+ toolName: ${safeJson(toolName)},
988
+ input: ${safeJson(input ?? null)},
989
+ output: null,
990
+ structuredContent: ${safeJson(structuredContent ?? null)},
991
+ ${contentBlock},
992
+ loading: true,
993
+ error: null
994
+ }`;
995
+ const subscriptionBlock = subscribeToUpdates ? `
996
+ // Subscribe to platform tool result events
997
+ (function() {
998
+ function subscribeToUpdates() {
999
+ if (window.openai && window.openai.canvas && window.openai.canvas.onToolResult) {
1000
+ window.openai.canvas.onToolResult(function(result) {
1001
+ window.__frontmcp.updateOutput(result);
1002
+ window.dispatchEvent(new CustomEvent('frontmcp:toolResult', { detail: result }));
1003
+ });
1004
+ }
1005
+ }
1006
+ if (document.readyState === 'loading') {
1007
+ document.addEventListener('DOMContentLoaded', subscribeToUpdates);
1008
+ } else {
1009
+ subscribeToUpdates();
1010
+ }
1011
+ })();` : "";
1012
+ return `
1013
+ // Dynamic Mode - OpenAI Subscription
1014
+ window.__frontmcp.setState(${initialState});
1015
+ ${subscriptionBlock}`;
1016
+ }
1017
+ function buildHybridDataInjectionCode(toolName, structuredContent, contentType, source, hasComponent, hybridOptions) {
1018
+ const safeJson = (value) => {
1019
+ try {
1020
+ return JSON.stringify(value);
1021
+ } catch {
1022
+ return "null";
1023
+ }
1024
+ };
1025
+ const outputPlaceholder = hybridOptions?.placeholder ?? DEFAULT_OUTPUT_PLACEHOLDER;
1026
+ const inputPlaceholder = hybridOptions?.inputPlaceholder ?? DEFAULT_INPUT_PLACEHOLDER;
1027
+ const contentBlock = hasComponent ? `content: { type: 'react', source: window.__frontmcp_component }` : `content: { type: ${safeJson(contentType)}, source: ${safeJson(source)} }`;
1028
+ return `
1029
+ // Hybrid Mode - Placeholders replaced at runtime
1030
+ var __outputRaw = "${outputPlaceholder}";
1031
+ var __inputRaw = "${inputPlaceholder}";
1032
+ var __output = null;
1033
+ var __input = null;
1034
+ var __error = null;
1035
+ var __outputNotReplaced = false;
1036
+
1037
+ // Parse output placeholder
1038
+ if (typeof __outputRaw === 'string' && __outputRaw !== "${outputPlaceholder}") {
1039
+ try { __output = JSON.parse(__outputRaw); } catch (e) {
1040
+ console.warn('[FrontMCP] Failed to parse output:', e);
1041
+ __error = 'Failed to parse output data';
1042
+ }
1043
+ } else if (__outputRaw === "${outputPlaceholder}") {
1044
+ // Placeholder not replaced - no data was injected
1045
+ __outputNotReplaced = true;
1046
+ }
1047
+
1048
+ // Parse input placeholder
1049
+ if (typeof __inputRaw === 'string' && __inputRaw !== "${inputPlaceholder}") {
1050
+ try { __input = JSON.parse(__inputRaw); } catch (e) { console.warn('[FrontMCP] Failed to parse input:', e); }
1051
+ }
1052
+
1053
+ // Set error if output placeholder was not replaced (no data provided)
1054
+ if (__outputNotReplaced) {
1055
+ __error = 'No data provided. The output placeholder was not replaced.';
1056
+ }
1057
+
1058
+ window.__frontmcp.setState({
1059
+ toolName: ${safeJson(toolName)},
1060
+ input: __input,
1061
+ output: __output,
1062
+ structuredContent: ${safeJson(structuredContent ?? null)},
1063
+ ${contentBlock},
1064
+ loading: false,
1065
+ error: __error
1066
+ });`;
1067
+ }
2081
1068
  function buildComponentCode(transpiledCode) {
2082
1069
  return `
2083
1070
  // CommonJS module shim
@@ -2092,6 +1079,377 @@ function buildComponentCode(transpiledCode) {
2092
1079
  }
2093
1080
 
2094
1081
  // libs/ui/src/bundler/bundler.ts
1082
+ var import_build2 = require("@frontmcp/uipack/build");
1083
+
1084
+ // libs/ui/src/bundler/browser-components.ts
1085
+ var path = __toESM(require("path"));
1086
+ var cachedBrowserComponents = null;
1087
+ var buildingPromise = null;
1088
+ function getComponentsEntrySource() {
1089
+ return `
1090
+ // Browser Components Entry Point
1091
+ // This gets transpiled by esbuild to create browser-compatible code
1092
+
1093
+ import {
1094
+ // Card styles
1095
+ CARD_VARIANTS,
1096
+ CARD_SIZES,
1097
+ // Button styles
1098
+ BUTTON_VARIANTS,
1099
+ BUTTON_SIZES,
1100
+ BUTTON_ICON_SIZES,
1101
+ BUTTON_BASE_CLASSES,
1102
+ LOADING_SPINNER,
1103
+ // Badge styles
1104
+ BADGE_VARIANTS,
1105
+ BADGE_SIZES,
1106
+ BADGE_DOT_SIZES,
1107
+ BADGE_DOT_VARIANTS,
1108
+ // Alert styles
1109
+ ALERT_VARIANTS,
1110
+ ALERT_BASE_CLASSES,
1111
+ ALERT_ICONS,
1112
+ CLOSE_ICON,
1113
+ // Utility
1114
+ cn,
1115
+ } from '@frontmcp/uipack/styles';
1116
+
1117
+ // Re-export for the IIFE
1118
+ export {
1119
+ CARD_VARIANTS,
1120
+ CARD_SIZES,
1121
+ BUTTON_VARIANTS,
1122
+ BUTTON_SIZES,
1123
+ BUTTON_ICON_SIZES,
1124
+ BUTTON_BASE_CLASSES,
1125
+ LOADING_SPINNER,
1126
+ BADGE_VARIANTS,
1127
+ BADGE_SIZES,
1128
+ BADGE_DOT_SIZES,
1129
+ BADGE_DOT_VARIANTS,
1130
+ ALERT_VARIANTS,
1131
+ ALERT_BASE_CLASSES,
1132
+ ALERT_ICONS,
1133
+ CLOSE_ICON,
1134
+ cn,
1135
+ };
1136
+
1137
+ // Card Component
1138
+ export function Card(props: any) {
1139
+ const {
1140
+ title,
1141
+ subtitle,
1142
+ headerActions,
1143
+ footer,
1144
+ variant = 'default',
1145
+ size = 'md',
1146
+ className,
1147
+ id,
1148
+ clickable,
1149
+ href,
1150
+ children,
1151
+ } = props;
1152
+
1153
+ const variantClasses = CARD_VARIANTS[variant] || CARD_VARIANTS.default;
1154
+ const sizeClasses = CARD_SIZES[size] || CARD_SIZES.md;
1155
+ const clickableClasses = clickable ? 'cursor-pointer hover:shadow-md transition-shadow' : '';
1156
+ const allClasses = cn(variantClasses, sizeClasses, clickableClasses, className);
1157
+
1158
+ const hasHeader = title || subtitle || headerActions;
1159
+
1160
+ const headerElement = hasHeader ? React.createElement('div', {
1161
+ className: 'flex items-start justify-between mb-4'
1162
+ }, [
1163
+ React.createElement('div', { key: 'titles' }, [
1164
+ title && React.createElement('h3', {
1165
+ key: 'title',
1166
+ className: 'text-lg font-semibold text-text-primary'
1167
+ }, title),
1168
+ subtitle && React.createElement('p', {
1169
+ key: 'subtitle',
1170
+ className: 'text-sm text-text-secondary mt-1'
1171
+ }, subtitle)
1172
+ ]),
1173
+ headerActions && React.createElement('div', {
1174
+ key: 'actions',
1175
+ className: 'flex items-center gap-2'
1176
+ }, headerActions)
1177
+ ]) : null;
1178
+
1179
+ const footerElement = footer ? React.createElement('div', {
1180
+ className: 'mt-4 pt-4 border-t border-divider'
1181
+ }, footer) : null;
1182
+
1183
+ const content = React.createElement(React.Fragment, null, headerElement, children, footerElement);
1184
+
1185
+ if (href) {
1186
+ return React.createElement('a', { href, className: allClasses, id }, content);
1187
+ }
1188
+
1189
+ return React.createElement('div', { className: allClasses, id }, content);
1190
+ }
1191
+
1192
+ // Button Component
1193
+ export function Button(props: any) {
1194
+ const {
1195
+ variant = 'primary',
1196
+ size = 'md',
1197
+ disabled = false,
1198
+ loading = false,
1199
+ fullWidth = false,
1200
+ iconPosition = 'left',
1201
+ icon,
1202
+ iconOnly = false,
1203
+ type = 'button',
1204
+ className,
1205
+ onClick,
1206
+ children,
1207
+ } = props;
1208
+
1209
+ const variantClasses = BUTTON_VARIANTS[variant] || BUTTON_VARIANTS.primary;
1210
+ const sizeClasses = iconOnly
1211
+ ? (BUTTON_ICON_SIZES[size] || BUTTON_ICON_SIZES.md)
1212
+ : (BUTTON_SIZES[size] || BUTTON_SIZES.md);
1213
+
1214
+ const disabledClasses = (disabled || loading) ? 'opacity-50 cursor-not-allowed' : '';
1215
+ const widthClasses = fullWidth ? 'w-full' : '';
1216
+
1217
+ const allClasses = cn(BUTTON_BASE_CLASSES, variantClasses, sizeClasses, disabledClasses, widthClasses, className);
1218
+
1219
+ const iconElement = icon ? React.createElement('span', {
1220
+ className: iconPosition === 'left' ? 'mr-2' : 'ml-2'
1221
+ }, icon) : null;
1222
+
1223
+ const loadingSpinner = loading ? React.createElement('span', {
1224
+ className: 'mr-2',
1225
+ dangerouslySetInnerHTML: { __html: LOADING_SPINNER }
1226
+ }) : null;
1227
+
1228
+ return React.createElement('button', {
1229
+ type,
1230
+ className: allClasses,
1231
+ disabled: disabled || loading,
1232
+ onClick
1233
+ },
1234
+ loadingSpinner,
1235
+ !loading && icon && iconPosition === 'left' ? iconElement : null,
1236
+ !iconOnly ? children : null,
1237
+ !loading && icon && iconPosition === 'right' ? iconElement : null
1238
+ );
1239
+ }
1240
+
1241
+ // Badge Component
1242
+ export function Badge(props: any) {
1243
+ const {
1244
+ variant = 'default',
1245
+ size = 'md',
1246
+ pill = false,
1247
+ icon,
1248
+ dot = false,
1249
+ className,
1250
+ removable = false,
1251
+ onRemove,
1252
+ children,
1253
+ } = props;
1254
+
1255
+ // Handle dot badge
1256
+ if (dot) {
1257
+ const dotSizeClasses = BADGE_DOT_SIZES[size] || BADGE_DOT_SIZES.md;
1258
+ const dotVariantClasses = BADGE_DOT_VARIANTS[variant] || BADGE_DOT_VARIANTS.default;
1259
+ const dotClasses = cn('inline-block rounded-full', dotSizeClasses, dotVariantClasses, className);
1260
+ const label = typeof children === 'string' ? children : undefined;
1261
+ return React.createElement('span', {
1262
+ className: dotClasses,
1263
+ 'aria-label': label,
1264
+ title: label
1265
+ });
1266
+ }
1267
+
1268
+ const variantClasses = BADGE_VARIANTS[variant] || BADGE_VARIANTS.default;
1269
+ const sizeClasses = BADGE_SIZES[size] || BADGE_SIZES.md;
1270
+
1271
+ const baseClasses = cn(
1272
+ 'inline-flex items-center font-medium',
1273
+ pill ? 'rounded-full' : 'rounded-md',
1274
+ variantClasses,
1275
+ sizeClasses,
1276
+ className
1277
+ );
1278
+
1279
+ const closeButton = removable ? React.createElement('button', {
1280
+ type: 'button',
1281
+ className: 'ml-1.5 -mr-1 hover:opacity-70 transition-opacity',
1282
+ 'aria-label': 'Remove',
1283
+ onClick: onRemove
1284
+ }, React.createElement('svg', {
1285
+ className: 'w-3 h-3',
1286
+ fill: 'none',
1287
+ stroke: 'currentColor',
1288
+ viewBox: '0 0 24 24'
1289
+ }, React.createElement('path', {
1290
+ strokeLinecap: 'round',
1291
+ strokeLinejoin: 'round',
1292
+ strokeWidth: '2',
1293
+ d: 'M6 18L18 6M6 6l12 12'
1294
+ }))) : null;
1295
+
1296
+ return React.createElement('span', { className: baseClasses },
1297
+ icon ? React.createElement('span', { className: 'mr-1' }, icon) : null,
1298
+ children,
1299
+ closeButton
1300
+ );
1301
+ }
1302
+
1303
+ // Alert Component
1304
+ export function Alert(props: any) {
1305
+ const {
1306
+ variant = 'info',
1307
+ title,
1308
+ icon,
1309
+ showIcon = true,
1310
+ dismissible = false,
1311
+ onDismiss,
1312
+ className,
1313
+ children,
1314
+ } = props;
1315
+
1316
+ const variantStyles = ALERT_VARIANTS[variant] || ALERT_VARIANTS.info;
1317
+ const allClasses = cn(ALERT_BASE_CLASSES, variantStyles.container, className);
1318
+
1319
+ const iconContent = icon || (showIcon ? React.createElement('span', {
1320
+ className: cn('flex-shrink-0', variantStyles.icon),
1321
+ dangerouslySetInnerHTML: { __html: ALERT_ICONS[variant] || ALERT_ICONS.info }
1322
+ }) : null);
1323
+
1324
+ const dismissButton = dismissible ? React.createElement('button', {
1325
+ type: 'button',
1326
+ className: 'flex-shrink-0 ml-3 hover:opacity-70 transition-opacity',
1327
+ 'aria-label': 'Dismiss',
1328
+ onClick: onDismiss
1329
+ }, React.createElement('span', {
1330
+ dangerouslySetInnerHTML: { __html: CLOSE_ICON }
1331
+ })) : null;
1332
+
1333
+ return React.createElement('div', { className: allClasses, role: 'alert' },
1334
+ React.createElement('div', { className: 'flex' },
1335
+ iconContent ? React.createElement('div', { className: 'flex-shrink-0 mr-3' }, iconContent) : null,
1336
+ React.createElement('div', { className: 'flex-1' },
1337
+ title ? React.createElement('h4', { className: 'font-semibold mb-1' }, title) : null,
1338
+ React.createElement('div', { className: 'text-sm' }, children)
1339
+ ),
1340
+ dismissButton
1341
+ )
1342
+ );
1343
+ }
1344
+ `;
1345
+ }
1346
+ function getBrowserRuntimeWrapper() {
1347
+ return `
1348
+ // Assign components to window for require() shim
1349
+ window.Card = Card;
1350
+ window.Button = Button;
1351
+ window.Badge = Badge;
1352
+ window.Alert = Alert;
1353
+
1354
+ // Build the namespace object for @frontmcp/ui/react imports
1355
+ window.frontmcp_ui_namespaceObject = Object.assign({}, window.React || {}, {
1356
+ // Hooks
1357
+ useToolOutput: window.useToolOutput,
1358
+ useToolInput: window.useToolInput,
1359
+ useMcpBridgeContext: function() { return window.__frontmcp.context; },
1360
+ useMcpBridge: function() { return window.__frontmcp.context; },
1361
+ useCallTool: function() {
1362
+ return function(name, args) {
1363
+ if (window.__frontmcp.context && window.__frontmcp.context.callTool) {
1364
+ return window.__frontmcp.context.callTool(name, args);
1365
+ }
1366
+ console.warn('[FrontMCP] callTool not available');
1367
+ return Promise.resolve(null);
1368
+ };
1369
+ },
1370
+ useTheme: function() { return window.__frontmcp.theme || 'light'; },
1371
+ useDisplayMode: function() { return window.__frontmcp.displayMode || 'embedded'; },
1372
+ useHostContext: function() { return window.__frontmcp.hostContext || {}; },
1373
+ useCapability: function(cap) { return window.__frontmcp.capabilities && window.__frontmcp.capabilities[cap] || false; },
1374
+ useStructuredContent: function() { return window.__frontmcp.getState().structuredContent; },
1375
+ useToolCalls: function() { return []; },
1376
+ useSendMessage: function() { return function() { return Promise.resolve(); }; },
1377
+ useOpenLink: function() { return function() {}; },
1378
+
1379
+ // Components
1380
+ Card: window.Card,
1381
+ Badge: window.Badge,
1382
+ Button: window.Button,
1383
+ Alert: window.Alert,
1384
+
1385
+ // Re-export React for convenience
1386
+ createElement: React.createElement,
1387
+ Fragment: React.Fragment,
1388
+ useState: React.useState,
1389
+ useEffect: React.useEffect,
1390
+ useCallback: React.useCallback,
1391
+ useMemo: React.useMemo,
1392
+ useRef: React.useRef,
1393
+ useContext: React.useContext
1394
+ });
1395
+ `;
1396
+ }
1397
+ async function buildWithEsbuild() {
1398
+ try {
1399
+ const esbuild = await import("esbuild");
1400
+ const stylesPath = require.resolve("@frontmcp/uipack/styles");
1401
+ const entrySource = getComponentsEntrySource();
1402
+ const result = await esbuild.build({
1403
+ stdin: {
1404
+ contents: entrySource,
1405
+ loader: "tsx",
1406
+ resolveDir: path.dirname(stylesPath)
1407
+ },
1408
+ bundle: true,
1409
+ format: "iife",
1410
+ globalName: "__frontmcp_components",
1411
+ target: "es2020",
1412
+ minify: false,
1413
+ write: false,
1414
+ external: ["react", "react-dom"],
1415
+ define: {
1416
+ React: "window.React"
1417
+ },
1418
+ platform: "browser"
1419
+ });
1420
+ if (result.outputFiles && result.outputFiles.length > 0) {
1421
+ let code = result.outputFiles[0].text;
1422
+ code += "\n" + getBrowserRuntimeWrapper();
1423
+ return code;
1424
+ }
1425
+ throw new Error("No output from esbuild");
1426
+ } catch (error) {
1427
+ console.warn(
1428
+ `[FrontMCP] esbuild bundle failed, falling back to manual components: ${error instanceof Error ? error.message : String(error)}`
1429
+ );
1430
+ throw error;
1431
+ }
1432
+ }
1433
+ async function getBrowserComponents() {
1434
+ if (cachedBrowserComponents !== null) {
1435
+ return cachedBrowserComponents;
1436
+ }
1437
+ if (buildingPromise !== null) {
1438
+ return buildingPromise;
1439
+ }
1440
+ buildingPromise = buildWithEsbuild().then((code) => {
1441
+ cachedBrowserComponents = code;
1442
+ buildingPromise = null;
1443
+ return code;
1444
+ }).catch((error) => {
1445
+ buildingPromise = null;
1446
+ throw error;
1447
+ });
1448
+ return buildingPromise;
1449
+ }
1450
+
1451
+ // libs/ui/src/bundler/bundler.ts
1452
+ var import_bundler2 = require("@frontmcp/uipack/bundler");
2095
1453
  var esbuildTransform = null;
2096
1454
  async function loadEsbuild() {
2097
1455
  if (esbuildTransform !== null) {
@@ -2160,11 +1518,11 @@ var InMemoryBundler = class {
2160
1518
  ...options.cache
2161
1519
  }
2162
1520
  };
2163
- this.cache = new BundlerCache({
1521
+ this.cache = new import_bundler.BundlerCache({
2164
1522
  maxSize: this.options.cache.maxSize,
2165
1523
  ttl: this.options.cache.ttl
2166
1524
  });
2167
- this.defaultSecurity = mergePolicy(options.defaultSecurity);
1525
+ this.defaultSecurity = (0, import_bundler.mergePolicy)(options.defaultSecurity);
2168
1526
  }
2169
1527
  /**
2170
1528
  * Bundle source code.
@@ -2176,7 +1534,7 @@ var InMemoryBundler = class {
2176
1534
  const startTime = performance.now();
2177
1535
  const opts = this.mergeOptions(options);
2178
1536
  if (!opts.skipCache && !this.options.cache.disabled) {
2179
- const cacheKey = options.cacheKey ?? createCacheKey(options.source, opts);
1537
+ const cacheKey = options.cacheKey ?? (0, import_bundler.createCacheKey)(options.source, opts);
2180
1538
  const cached = this.cache.get(cacheKey);
2181
1539
  if (cached) {
2182
1540
  return {
@@ -2189,18 +1547,18 @@ var InMemoryBundler = class {
2189
1547
  };
2190
1548
  }
2191
1549
  }
2192
- const security = mergePolicy(options.security ?? this.defaultSecurity);
2193
- const violations = validateSource(options.source, security);
2194
- throwOnViolations(violations);
1550
+ const security = (0, import_bundler.mergePolicy)(options.security ?? this.defaultSecurity);
1551
+ const violations = (0, import_bundler.validateSource)(options.source, security);
1552
+ (0, import_bundler.throwOnViolations)(violations);
2195
1553
  const sourceType = opts.sourceType === "auto" ? this.detectSourceType(options.source) : opts.sourceType;
2196
1554
  const transformStart = performance.now();
2197
1555
  const transformed = await this.transform(options.source, sourceType, opts);
2198
1556
  const transformTime = performance.now() - transformStart;
2199
- const sizeViolation = validateSize(transformed.code.length, security);
1557
+ const sizeViolation = (0, import_bundler.validateSize)(transformed.code.length, security);
2200
1558
  if (sizeViolation) {
2201
- throwOnViolations([sizeViolation]);
1559
+ (0, import_bundler.throwOnViolations)([sizeViolation]);
2202
1560
  }
2203
- const hash = hashContent(transformed.code);
1561
+ const hash = (0, import_bundler.hashContent)(transformed.code);
2204
1562
  const result = {
2205
1563
  code: transformed.code,
2206
1564
  hash,
@@ -2217,7 +1575,7 @@ var InMemoryBundler = class {
2217
1575
  format: opts.format
2218
1576
  };
2219
1577
  if (!this.options.cache.disabled) {
2220
- const cacheKey = options.cacheKey ?? createCacheKey(options.source, opts);
1578
+ const cacheKey = options.cacheKey ?? (0, import_bundler.createCacheKey)(options.source, opts);
2221
1579
  this.cache.set(cacheKey, result);
2222
1580
  }
2223
1581
  return result;
@@ -2244,16 +1602,16 @@ var InMemoryBundler = class {
2244
1602
  throw new Error("React and react-dom/server are required for SSR. Install them: npm install react react-dom");
2245
1603
  }
2246
1604
  const renderStart = performance.now();
2247
- const Component = await executeDefault(bundleResult.code, {
1605
+ const Component = await (0, import_bundler.executeDefault)(bundleResult.code, {
2248
1606
  React,
2249
- security: mergePolicy(options.security ?? this.defaultSecurity)
1607
+ security: (0, import_bundler.mergePolicy)(options.security ?? this.defaultSecurity)
2250
1608
  });
2251
1609
  let html;
2252
1610
  try {
2253
1611
  const element = React.createElement(Component, options.context ?? {});
2254
1612
  html = ReactDOMServer.renderToString(element);
2255
1613
  } catch (error) {
2256
- throw new ExecutionError(
1614
+ throw new import_bundler.ExecutionError(
2257
1615
  `SSR rendering failed: ${error instanceof Error ? error.message : String(error)}`,
2258
1616
  error
2259
1617
  );
@@ -2294,10 +1652,10 @@ var InMemoryBundler = class {
2294
1652
  React = await import("react");
2295
1653
  } catch {
2296
1654
  }
2297
- return executeDefault(result.code, {
1655
+ return (0, import_bundler.executeDefault)(result.code, {
2298
1656
  React,
2299
1657
  globals: context,
2300
- security: mergePolicy(options.security ?? this.defaultSecurity)
1658
+ security: (0, import_bundler.mergePolicy)(options.security ?? this.defaultSecurity)
2301
1659
  });
2302
1660
  }
2303
1661
  /**
@@ -2348,10 +1706,19 @@ var InMemoryBundler = class {
2348
1706
  security: opts.security,
2349
1707
  skipCache: opts.skipCache
2350
1708
  });
2351
- const head = this.buildStaticHTMLHead({ externals: opts.externals, customCss: opts.customCss });
1709
+ const head = this.buildStaticHTMLHead({ externals: opts.externals, customCss: opts.customCss, theme: opts.theme });
2352
1710
  const reactRuntime = this.buildReactRuntimeScripts(opts.externals, platform, cdnType);
2353
- const frontmcpRuntime = this.buildFrontMCPRuntime();
2354
- const dataScript = this.buildDataInjectionScript(opts.toolName, opts.input, opts.output, opts.structuredContent);
1711
+ const frontmcpRuntime = await this.buildFrontMCPRuntime();
1712
+ const dataScript = this.buildDataInjectionScript(
1713
+ opts.toolName,
1714
+ opts.input,
1715
+ opts.output,
1716
+ opts.structuredContent,
1717
+ opts.buildMode,
1718
+ cdnType,
1719
+ opts.dynamicOptions,
1720
+ opts.hybridOptions
1721
+ );
2355
1722
  const componentScript = this.buildComponentRenderScript(bundleResult.code, opts.rootId, cdnType);
2356
1723
  const html = this.assembleStaticHTML({
2357
1724
  title: opts.title || `${opts.toolName} - Widget`,
@@ -2363,7 +1730,9 @@ var InMemoryBundler = class {
2363
1730
  rootId: opts.rootId,
2364
1731
  cdnType
2365
1732
  });
2366
- const hash = hashContent(html);
1733
+ const hash = (0, import_bundler.hashContent)(html);
1734
+ const dataPlaceholder = opts.buildMode === "hybrid" ? opts.hybridOptions?.placeholder ?? HYBRID_DATA_PLACEHOLDER : void 0;
1735
+ const inputPlaceholder = opts.buildMode === "hybrid" ? opts.hybridOptions?.inputPlaceholder ?? HYBRID_INPUT_PLACEHOLDER : void 0;
2367
1736
  return {
2368
1737
  html,
2369
1738
  componentCode: bundleResult.code,
@@ -2375,9 +1744,240 @@ var InMemoryBundler = class {
2375
1744
  size: html.length,
2376
1745
  cached: bundleResult.cached,
2377
1746
  sourceType: bundleResult.sourceType,
2378
- targetPlatform: platform
1747
+ targetPlatform: platform,
1748
+ buildMode: opts.buildMode,
1749
+ dataPlaceholder,
1750
+ inputPlaceholder
1751
+ };
1752
+ }
1753
+ /**
1754
+ * Bundle a component to static HTML for all target platforms at once.
1755
+ *
1756
+ * This method is optimized for efficiency:
1757
+ * - Transpiles the component source code only once
1758
+ * - Generates platform-specific HTML variations from the shared transpiled code
1759
+ * - Returns complete platform metadata ready for MCP responses
1760
+ *
1761
+ * @param options - Multi-platform build options
1762
+ * @returns Multi-platform build result with all platforms
1763
+ *
1764
+ * @example
1765
+ * ```typescript
1766
+ * const result = await bundler.bundleToStaticHTMLAll({
1767
+ * source: `
1768
+ * import { Card, useToolOutput } from '@frontmcp/ui/react';
1769
+ * export default function Weather() {
1770
+ * const output = useToolOutput();
1771
+ * return <Card title="Weather">{output?.temperature}°F</Card>;
1772
+ * }
1773
+ * `,
1774
+ * toolName: 'get_weather',
1775
+ * output: { temperature: 72 },
1776
+ * });
1777
+ *
1778
+ * // Access platform-specific results
1779
+ * const openaiHtml = result.platforms.openai.html;
1780
+ * const claudeHtml = result.platforms.claude.html;
1781
+ *
1782
+ * // Get metadata for MCP response
1783
+ * const openaiMeta = result.platforms.openai.meta;
1784
+ * ```
1785
+ */
1786
+ async bundleToStaticHTMLAll(options) {
1787
+ const startTime = performance.now();
1788
+ const opts = this.mergeStaticHTMLOptions(options);
1789
+ const platforms = options.platforms ?? [...ALL_PLATFORMS];
1790
+ const transpileStart = performance.now();
1791
+ let transpiledCode = null;
1792
+ let bundleResult = null;
1793
+ const isUniversal = opts.universal;
1794
+ const rawContentType = options.contentType ?? "auto";
1795
+ const contentType = isUniversal ? rawContentType === "auto" ? detectContentType(options.source) : rawContentType : "react";
1796
+ if (contentType === "react" || !isUniversal) {
1797
+ bundleResult = await this.bundle({
1798
+ source: options.source,
1799
+ sourceType: opts.sourceType,
1800
+ format: "cjs",
1801
+ minify: opts.minify,
1802
+ sourceMaps: false,
1803
+ externals: ["react", "react-dom", "react/jsx-runtime", "@frontmcp/ui", "@frontmcp/ui/react"],
1804
+ security: opts.security,
1805
+ skipCache: opts.skipCache
1806
+ });
1807
+ transpiledCode = bundleResult.code;
1808
+ }
1809
+ const transpileTime = performance.now() - transpileStart;
1810
+ const generationStart = performance.now();
1811
+ const platformResults = {};
1812
+ for (const platform of platforms) {
1813
+ const platformResult = await this.buildForPlatform({
1814
+ options,
1815
+ opts,
1816
+ platform,
1817
+ transpiledCode,
1818
+ bundleResult,
1819
+ contentType,
1820
+ isUniversal
1821
+ });
1822
+ platformResults[platform] = platformResult;
1823
+ }
1824
+ const generationTime = performance.now() - generationStart;
1825
+ return {
1826
+ platforms: platformResults,
1827
+ sharedComponentCode: transpiledCode ?? "",
1828
+ metrics: {
1829
+ transpileTime,
1830
+ generationTime,
1831
+ totalTime: performance.now() - startTime
1832
+ },
1833
+ cached: bundleResult?.cached ?? false
1834
+ };
1835
+ }
1836
+ /**
1837
+ * Build for a specific platform with pre-transpiled code.
1838
+ * Internal helper for bundleToStaticHTMLAll.
1839
+ */
1840
+ async buildForPlatform(params) {
1841
+ const { options, opts, platform, transpiledCode, bundleResult, contentType, isUniversal } = params;
1842
+ const cdnType = getCdnTypeForPlatform(platform);
1843
+ const buildStart = performance.now();
1844
+ let html;
1845
+ let componentCode;
1846
+ if (isUniversal) {
1847
+ const shouldIncludeBridge = opts.buildMode === "dynamic" || opts.buildMode === "hybrid";
1848
+ const cachedRuntime = getCachedRuntime({
1849
+ cdnType,
1850
+ includeMarkdown: opts.includeMarkdown || contentType === "markdown",
1851
+ includeMdx: opts.includeMdx || contentType === "mdx",
1852
+ minify: opts.minify,
1853
+ includeBridge: shouldIncludeBridge
1854
+ });
1855
+ const componentCodeStr = transpiledCode ? buildComponentCode(transpiledCode) : "";
1856
+ const dataInjectionStr = buildDataInjectionCode(
1857
+ opts.toolName,
1858
+ opts.input,
1859
+ opts.output,
1860
+ opts.structuredContent,
1861
+ contentType,
1862
+ transpiledCode ? null : options.source,
1863
+ transpiledCode !== null,
1864
+ {
1865
+ buildMode: opts.buildMode,
1866
+ cdnType,
1867
+ dynamicOptions: opts.dynamicOptions,
1868
+ hybridOptions: opts.hybridOptions
1869
+ }
1870
+ );
1871
+ const appScript = buildAppScript(
1872
+ cachedRuntime.appTemplate,
1873
+ componentCodeStr,
1874
+ dataInjectionStr,
1875
+ opts.customComponents ?? ""
1876
+ );
1877
+ const head = this.buildStaticHTMLHead({
1878
+ externals: opts.externals,
1879
+ customCss: opts.customCss,
1880
+ theme: opts.theme
1881
+ });
1882
+ const reactRuntime = this.buildReactRuntimeScripts(opts.externals, platform, cdnType);
1883
+ const renderScript = this.buildUniversalRenderScript(opts.rootId, cdnType);
1884
+ html = this.assembleUniversalStaticHTMLCached({
1885
+ title: opts.title || `${opts.toolName} - Widget`,
1886
+ head,
1887
+ reactRuntime,
1888
+ cdnImports: cachedRuntime.cdnImports,
1889
+ vendorScript: cachedRuntime.vendorScript,
1890
+ appScript,
1891
+ renderScript,
1892
+ rootId: opts.rootId,
1893
+ cdnType
1894
+ });
1895
+ componentCode = transpiledCode ?? appScript;
1896
+ } else {
1897
+ if (!transpiledCode) {
1898
+ throw new Error("Failed to transpile component source");
1899
+ }
1900
+ const head = this.buildStaticHTMLHead({
1901
+ externals: opts.externals,
1902
+ customCss: opts.customCss,
1903
+ theme: opts.theme
1904
+ });
1905
+ const reactRuntime = this.buildReactRuntimeScripts(opts.externals, platform, cdnType);
1906
+ const frontmcpRuntime = await this.buildFrontMCPRuntime();
1907
+ const dataScript = this.buildDataInjectionScript(
1908
+ opts.toolName,
1909
+ opts.input,
1910
+ opts.output,
1911
+ opts.structuredContent,
1912
+ opts.buildMode,
1913
+ cdnType,
1914
+ opts.dynamicOptions,
1915
+ opts.hybridOptions
1916
+ );
1917
+ const componentScript = this.buildComponentRenderScript(transpiledCode, opts.rootId, cdnType);
1918
+ html = this.assembleStaticHTML({
1919
+ title: opts.title || `${opts.toolName} - Widget`,
1920
+ head,
1921
+ reactRuntime,
1922
+ frontmcpRuntime,
1923
+ dataScript,
1924
+ componentScript,
1925
+ rootId: opts.rootId,
1926
+ cdnType
1927
+ });
1928
+ componentCode = transpiledCode;
1929
+ }
1930
+ const hash = (0, import_bundler.hashContent)(html);
1931
+ const meta = (0, import_adapters.buildUIMeta)({
1932
+ uiConfig: {
1933
+ template: () => html,
1934
+ widgetAccessible: opts.widgetAccessible
1935
+ },
1936
+ platformType: this.mapTargetPlatformToAIPlatform(platform),
1937
+ html
1938
+ });
1939
+ const dataPlaceholder = opts.buildMode === "hybrid" ? opts.hybridOptions?.placeholder ?? HYBRID_DATA_PLACEHOLDER : void 0;
1940
+ const inputPlaceholder = opts.buildMode === "hybrid" ? opts.hybridOptions?.inputPlaceholder ?? HYBRID_INPUT_PLACEHOLDER : void 0;
1941
+ return {
1942
+ html,
1943
+ componentCode,
1944
+ metrics: bundleResult?.metrics ?? {
1945
+ transformTime: 0,
1946
+ bundleTime: 0,
1947
+ totalTime: performance.now() - buildStart
1948
+ },
1949
+ hash,
1950
+ size: html.length,
1951
+ cached: bundleResult?.cached ?? false,
1952
+ sourceType: bundleResult?.sourceType ?? opts.sourceType,
1953
+ targetPlatform: platform,
1954
+ universal: isUniversal,
1955
+ contentType: isUniversal ? contentType : void 0,
1956
+ buildMode: opts.buildMode,
1957
+ dataPlaceholder,
1958
+ inputPlaceholder,
1959
+ meta
2379
1960
  };
2380
1961
  }
1962
+ /**
1963
+ * Map TargetPlatform to AIPlatformType for metadata generation.
1964
+ */
1965
+ mapTargetPlatformToAIPlatform(platform) {
1966
+ switch (platform) {
1967
+ case "openai":
1968
+ return "openai";
1969
+ case "claude":
1970
+ return "claude";
1971
+ case "cursor":
1972
+ return "cursor";
1973
+ case "ext-apps":
1974
+ return "ext-apps";
1975
+ case "generic":
1976
+ return "generic-mcp";
1977
+ default:
1978
+ return "generic-mcp";
1979
+ }
1980
+ }
2381
1981
  /**
2382
1982
  * Bundle to static HTML with universal rendering mode.
2383
1983
  * Uses the universal renderer that can handle multiple content types.
@@ -2409,11 +2009,13 @@ var InMemoryBundler = class {
2409
2009
  transpiledCode = bundleResult.code;
2410
2010
  transformTime = bundleResult.metrics.transformTime;
2411
2011
  }
2012
+ const shouldIncludeBridge = opts.buildMode === "dynamic" || opts.buildMode === "hybrid";
2412
2013
  const cachedRuntime = getCachedRuntime({
2413
2014
  cdnType,
2414
2015
  includeMarkdown: opts.includeMarkdown || contentType === "markdown",
2415
2016
  includeMdx: opts.includeMdx || contentType === "mdx",
2416
- minify: opts.minify
2017
+ minify: opts.minify,
2018
+ includeBridge: shouldIncludeBridge
2417
2019
  });
2418
2020
  const componentCodeStr = transpiledCode ? buildComponentCode(transpiledCode) : "";
2419
2021
  const dataInjectionStr = buildDataInjectionCode(
@@ -2424,7 +2026,13 @@ var InMemoryBundler = class {
2424
2026
  contentType,
2425
2027
  transpiledCode ? null : options.source,
2426
2028
  // Pass source only if not a component
2427
- transpiledCode !== null
2029
+ transpiledCode !== null,
2030
+ {
2031
+ buildMode: opts.buildMode,
2032
+ cdnType,
2033
+ dynamicOptions: opts.dynamicOptions,
2034
+ hybridOptions: opts.hybridOptions
2035
+ }
2428
2036
  );
2429
2037
  const appScript = buildAppScript(
2430
2038
  cachedRuntime.appTemplate,
@@ -2432,7 +2040,7 @@ var InMemoryBundler = class {
2432
2040
  dataInjectionStr,
2433
2041
  opts.customComponents ?? ""
2434
2042
  );
2435
- const head = this.buildStaticHTMLHead({ externals: opts.externals, customCss: opts.customCss });
2043
+ const head = this.buildStaticHTMLHead({ externals: opts.externals, customCss: opts.customCss, theme: opts.theme });
2436
2044
  const reactRuntime = this.buildReactRuntimeScripts(opts.externals, platform, cdnType);
2437
2045
  const renderScript = this.buildUniversalRenderScript(opts.rootId, cdnType);
2438
2046
  const html = this.assembleUniversalStaticHTMLCached({
@@ -2446,7 +2054,7 @@ var InMemoryBundler = class {
2446
2054
  rootId: opts.rootId,
2447
2055
  cdnType
2448
2056
  });
2449
- const hash = hashContent(html);
2057
+ const hash = (0, import_bundler.hashContent)(html);
2450
2058
  return {
2451
2059
  html,
2452
2060
  componentCode: transpiledCode ?? appScript,
@@ -2861,6 +2469,10 @@ ${parts.appScript}
2861
2469
  contentType: options.contentType ?? DEFAULT_STATIC_HTML_OPTIONS.contentType,
2862
2470
  includeMarkdown: options.includeMarkdown ?? DEFAULT_STATIC_HTML_OPTIONS.includeMarkdown,
2863
2471
  includeMdx: options.includeMdx ?? DEFAULT_STATIC_HTML_OPTIONS.includeMdx,
2472
+ // Build mode options
2473
+ buildMode: options.buildMode ?? DEFAULT_STATIC_HTML_OPTIONS.buildMode,
2474
+ dynamicOptions: options.dynamicOptions,
2475
+ hybridOptions: options.hybridOptions,
2864
2476
  // Pass-through options
2865
2477
  toolName: options.toolName,
2866
2478
  input: options.input,
@@ -2869,7 +2481,8 @@ ${parts.appScript}
2869
2481
  title: options.title,
2870
2482
  security: options.security,
2871
2483
  customCss: options.customCss,
2872
- customComponents: options.customComponents
2484
+ customComponents: options.customComponents,
2485
+ theme: options.theme
2873
2486
  };
2874
2487
  }
2875
2488
  /**
@@ -2883,12 +2496,8 @@ ${parts.appScript}
2883
2496
  parts.push(`<link rel="preconnect" href="${url}" crossorigin>`);
2884
2497
  }
2885
2498
  parts.push(`<link rel="stylesheet" href="${STATIC_HTML_CDN.fonts.inter}">`);
2886
- const tailwindConfig = opts.externals.tailwind ?? "cdn";
2887
- if (tailwindConfig === "cdn") {
2888
- parts.push(`<link rel="stylesheet" href="${STATIC_HTML_CDN.tailwind}">`);
2889
- } else if (tailwindConfig !== "inline" && tailwindConfig) {
2890
- parts.push(`<link rel="stylesheet" href="${tailwindConfig}">`);
2891
- }
2499
+ parts.push((0, import_build2.buildCDNScriptTag)(import_build2.CLOUDFLARE_CDN.tailwindCss));
2500
+ parts.push(this.buildThemeStyleBlock(opts.theme));
2892
2501
  parts.push(`<style>
2893
2502
  body { margin: 0; font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
2894
2503
  .frontmcp-loading { display: flex; align-items: center; justify-content: center; min-height: 200px; }
@@ -2902,6 +2511,18 @@ ${sanitizeCss(opts.customCss)}
2902
2511
  }
2903
2512
  return parts.join("\n ");
2904
2513
  }
2514
+ /**
2515
+ * Build theme CSS variables as a :root style block.
2516
+ * Uses DEFAULT_THEME if no theme is provided.
2517
+ */
2518
+ buildThemeStyleBlock(theme = import_theme.DEFAULT_THEME) {
2519
+ const cssVars = (0, import_theme.buildThemeCss)(theme);
2520
+ return `<style>
2521
+ :root {
2522
+ ${cssVars}
2523
+ }
2524
+ </style>`;
2525
+ }
2905
2526
  /**
2906
2527
  * Build React runtime scripts for static HTML.
2907
2528
  */
@@ -2966,9 +2587,17 @@ ${sanitizeCss(opts.customCss)}
2966
2587
  }
2967
2588
  /**
2968
2589
  * Build FrontMCP runtime (hooks and UI components).
2590
+ * Uses esbuild to transpile real React components at first use, then caches.
2591
+ * Falls back to manual implementation if esbuild fails.
2969
2592
  * Always inlined for reliability across platforms.
2970
2593
  */
2971
- buildFrontMCPRuntime() {
2594
+ async buildFrontMCPRuntime() {
2595
+ let uiComponents;
2596
+ try {
2597
+ uiComponents = await getBrowserComponents();
2598
+ } catch {
2599
+ uiComponents = (0, import_build2.buildUIComponentsRuntime)();
2600
+ }
2972
2601
  return `
2973
2602
  <!-- FrontMCP Runtime (always inline) -->
2974
2603
  <script>
@@ -2988,8 +2617,8 @@ ${sanitizeCss(opts.customCss)}
2988
2617
  'react-dom/client': function() { return window.ReactDOM; },
2989
2618
  'react/jsx-runtime': function() { return window.jsx_runtime_namespaceObject; },
2990
2619
  'react/jsx-dev-runtime': function() { return window.jsx_runtime_namespaceObject; },
2991
- '@frontmcp/ui': function() { return window.react_namespaceObject; },
2992
- '@frontmcp/ui/react': function() { return window.react_namespaceObject; },
2620
+ '@frontmcp/ui': function() { return window.frontmcp_ui_namespaceObject; },
2621
+ '@frontmcp/ui/react': function() { return window.frontmcp_ui_namespaceObject; },
2993
2622
  };
2994
2623
 
2995
2624
  var resolver = moduleMap[moduleName];
@@ -3020,7 +2649,7 @@ ${sanitizeCss(opts.customCss)}
3020
2649
  });
3021
2650
  };
3022
2651
 
3023
- // FrontMCP Hook implementations
2652
+ // FrontMCP Hook implementations and state
3024
2653
  window.__frontmcp = {
3025
2654
  // Context for MCP bridge
3026
2655
  context: {
@@ -3031,10 +2660,35 @@ ${sanitizeCss(opts.customCss)}
3031
2660
  callTool: null,
3032
2661
  },
3033
2662
 
2663
+ // Theme and display settings
2664
+ theme: 'light',
2665
+ displayMode: 'embedded',
2666
+ hostContext: {},
2667
+ capabilities: {},
2668
+
3034
2669
  // Set context from data injection
3035
2670
  setContext: function(ctx) {
3036
2671
  Object.assign(this.context, ctx);
3037
2672
  },
2673
+
2674
+ // State management (for universal mode compatibility)
2675
+ getState: function() {
2676
+ return {
2677
+ toolName: this.context.toolName,
2678
+ input: this.context.toolInput,
2679
+ output: this.context.toolOutput,
2680
+ structuredContent: this.context.structuredContent,
2681
+ loading: false,
2682
+ error: null
2683
+ };
2684
+ },
2685
+
2686
+ setState: function(partial) {
2687
+ if (partial.toolName !== undefined) this.context.toolName = partial.toolName;
2688
+ if (partial.input !== undefined) this.context.toolInput = partial.input;
2689
+ if (partial.output !== undefined) this.context.toolOutput = partial.output;
2690
+ if (partial.structuredContent !== undefined) this.context.structuredContent = partial.structuredContent;
2691
+ }
3038
2692
  };
3039
2693
 
3040
2694
  // Hook: useToolOutput - returns the tool output data
@@ -3062,74 +2716,31 @@ ${sanitizeCss(opts.customCss)}
3062
2716
  return Promise.resolve(null);
3063
2717
  };
3064
2718
  };
3065
-
3066
- // UI Components (simplified inline versions)
3067
- window.Card = function(props) {
3068
- var children = props.children;
3069
- var title = props.title;
3070
- var className = props.className || '';
3071
- return React.createElement('div', {
3072
- className: 'bg-white rounded-lg shadow border border-gray-200 overflow-hidden ' + className
3073
- }, [
3074
- title && React.createElement('div', {
3075
- key: 'header',
3076
- className: 'px-4 py-3 border-b border-gray-200 bg-gray-50'
3077
- }, React.createElement('h3', { className: 'text-sm font-medium text-gray-900' }, title)),
3078
- React.createElement('div', { key: 'body', className: 'p-4' }, children)
3079
- ]);
3080
- };
3081
-
3082
- window.Badge = function(props) {
3083
- var children = props.children;
3084
- var variant = props.variant || 'default';
3085
- var variantClasses = {
3086
- default: 'bg-gray-100 text-gray-800',
3087
- success: 'bg-green-100 text-green-800',
3088
- warning: 'bg-yellow-100 text-yellow-800',
3089
- error: 'bg-red-100 text-red-800',
3090
- info: 'bg-blue-100 text-blue-800',
3091
- };
3092
- return React.createElement('span', {
3093
- className: 'inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ' + (variantClasses[variant] || variantClasses.default)
3094
- }, children);
3095
- };
3096
-
3097
- window.Button = function(props) {
3098
- var children = props.children;
3099
- var variant = props.variant || 'primary';
3100
- var onClick = props.onClick;
3101
- var disabled = props.disabled;
3102
- var variantClasses = {
3103
- primary: 'bg-blue-600 text-white hover:bg-blue-700',
3104
- secondary: 'bg-gray-100 text-gray-900 hover:bg-gray-200',
3105
- outline: 'border border-gray-300 text-gray-700 hover:bg-gray-50',
3106
- danger: 'bg-red-600 text-white hover:bg-red-700',
3107
- };
3108
- return React.createElement('button', {
3109
- className: 'px-4 py-2 rounded-md text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 ' +
3110
- (disabled ? 'opacity-50 cursor-not-allowed ' : '') +
3111
- (variantClasses[variant] || variantClasses.primary),
3112
- onClick: onClick,
3113
- disabled: disabled,
3114
- }, children);
3115
- };
3116
-
3117
- // Make hooks available on react_namespaceObject for bundled imports
3118
- window.react_namespaceObject = Object.assign({}, window.React || {}, {
3119
- useToolOutput: window.useToolOutput,
3120
- useToolInput: window.useToolInput,
3121
- useMcpBridgeContext: window.useMcpBridgeContext,
3122
- useCallTool: window.useCallTool,
3123
- Card: window.Card,
3124
- Badge: window.Badge,
3125
- Button: window.Button,
3126
- });
2719
+ </script>
2720
+ <!-- UI Components (Full-Featured, Browser-Compatible) -->
2721
+ <script>
2722
+ ${uiComponents}
3127
2723
  </script>`;
3128
2724
  }
3129
2725
  /**
3130
2726
  * Build data injection script for tool input/output.
2727
+ * Dispatches to mode-specific builders based on buildMode.
2728
+ */
2729
+ buildDataInjectionScript(toolName, input, output, structuredContent, buildMode = "static", cdnType = "esm", dynamicOptions, hybridOptions) {
2730
+ switch (buildMode) {
2731
+ case "dynamic":
2732
+ return this.buildDynamicDataScript(toolName, input, output, structuredContent, cdnType, dynamicOptions);
2733
+ case "hybrid":
2734
+ return this.buildHybridDataScript(toolName, input, structuredContent, hybridOptions);
2735
+ case "static":
2736
+ default:
2737
+ return this.buildStaticDataScript(toolName, input, output, structuredContent);
2738
+ }
2739
+ }
2740
+ /**
2741
+ * Build static data injection - data baked in at build time (current default).
3131
2742
  */
3132
- buildDataInjectionScript(toolName, input, output, structuredContent) {
2743
+ buildStaticDataScript(toolName, input, output, structuredContent) {
3133
2744
  const safeJson = (value) => {
3134
2745
  try {
3135
2746
  return JSON.stringify(value);
@@ -3138,7 +2749,7 @@ ${sanitizeCss(opts.customCss)}
3138
2749
  }
3139
2750
  };
3140
2751
  return `
3141
- <!-- Tool Data Injection -->
2752
+ <!-- Tool Data Injection (Static Mode) -->
3142
2753
  <script>
3143
2754
  window.__mcpToolName = ${safeJson(toolName)};
3144
2755
  window.__mcpToolInput = ${safeJson(input ?? null)};
@@ -3154,6 +2765,256 @@ ${sanitizeCss(opts.customCss)}
3154
2765
  });
3155
2766
  </script>`;
3156
2767
  }
2768
+ /**
2769
+ * Build dynamic data injection - platform-aware.
2770
+ * For OpenAI (ESM): subscribes to platform events for updates.
2771
+ * For non-OpenAI (UMD/Claude): uses placeholders for data injection.
2772
+ */
2773
+ buildDynamicDataScript(toolName, input, output, structuredContent, cdnType = "esm", options) {
2774
+ if (cdnType === "umd") {
2775
+ return this.buildDynamicWithPlaceholdersScript(toolName, structuredContent, options);
2776
+ }
2777
+ return this.buildDynamicWithSubscriptionScript(toolName, input, output, structuredContent, options);
2778
+ }
2779
+ /**
2780
+ * Build dynamic data injection for non-OpenAI platforms using placeholders.
2781
+ * Similar to hybrid mode but with platform-appropriate loading/error states.
2782
+ */
2783
+ buildDynamicWithPlaceholdersScript(toolName, structuredContent, options) {
2784
+ const safeJson = (value) => {
2785
+ try {
2786
+ return JSON.stringify(value);
2787
+ } catch {
2788
+ return "null";
2789
+ }
2790
+ };
2791
+ const outputPlaceholder = HYBRID_DATA_PLACEHOLDER;
2792
+ const inputPlaceholder = HYBRID_INPUT_PLACEHOLDER;
2793
+ const includeInitialData = options?.includeInitialData !== false;
2794
+ return `
2795
+ <!-- Tool Data Injection (Dynamic Mode - Placeholder-based for non-OpenAI) -->
2796
+ <script>
2797
+ window.__mcpToolName = ${safeJson(toolName)};
2798
+ window.__mcpToolInput = "${inputPlaceholder}";
2799
+ window.__mcpToolOutput = "${outputPlaceholder}";
2800
+ window.__mcpStructuredContent = ${safeJson(structuredContent ?? null)};
2801
+ window.__mcpHybridError = null;
2802
+
2803
+ (function() {
2804
+ var outputNotReplaced = false;
2805
+ var includeInitialData = ${includeInitialData};
2806
+
2807
+ // Parse output placeholder
2808
+ var rawOutput = window.__mcpToolOutput;
2809
+ if (typeof rawOutput === 'string' && rawOutput !== "${outputPlaceholder}") {
2810
+ try {
2811
+ window.__mcpToolOutput = JSON.parse(rawOutput);
2812
+ } catch (e) {
2813
+ console.warn('[FrontMCP] Failed to parse injected output data:', e);
2814
+ window.__mcpToolOutput = null;
2815
+ window.__mcpHybridError = 'Failed to parse output data';
2816
+ }
2817
+ } else if (rawOutput === "${outputPlaceholder}") {
2818
+ window.__mcpToolOutput = null;
2819
+ outputNotReplaced = true;
2820
+ }
2821
+
2822
+ // Parse input placeholder
2823
+ var rawInput = window.__mcpToolInput;
2824
+ if (typeof rawInput === 'string' && rawInput !== "${inputPlaceholder}") {
2825
+ try {
2826
+ window.__mcpToolInput = JSON.parse(rawInput);
2827
+ } catch (e) {
2828
+ console.warn('[FrontMCP] Failed to parse injected input data:', e);
2829
+ window.__mcpToolInput = null;
2830
+ }
2831
+ } else if (rawInput === "${inputPlaceholder}") {
2832
+ window.__mcpToolInput = null;
2833
+ }
2834
+
2835
+ // Handle placeholder not replaced - show error if expecting initial data
2836
+ if (outputNotReplaced && includeInitialData) {
2837
+ window.__mcpHybridError = 'No data provided. The output placeholder was not replaced.';
2838
+ }
2839
+ })();
2840
+
2841
+ // Initialize FrontMCP context with appropriate loading/error state
2842
+ if (window.__frontmcp && window.__frontmcp.setContext) {
2843
+ window.__frontmcp.setContext({
2844
+ toolName: window.__mcpToolName,
2845
+ toolInput: window.__mcpToolInput,
2846
+ toolOutput: window.__mcpToolOutput,
2847
+ structuredContent: window.__mcpStructuredContent,
2848
+ loading: ${!includeInitialData} && window.__mcpToolOutput === null && !window.__mcpHybridError,
2849
+ error: window.__mcpHybridError,
2850
+ });
2851
+ }
2852
+ </script>`;
2853
+ }
2854
+ /**
2855
+ * Build dynamic data injection for OpenAI using subscription pattern.
2856
+ */
2857
+ buildDynamicWithSubscriptionScript(toolName, input, output, structuredContent, options) {
2858
+ const safeJson = (value) => {
2859
+ try {
2860
+ return JSON.stringify(value);
2861
+ } catch {
2862
+ return "null";
2863
+ }
2864
+ };
2865
+ const includeInitial = options?.includeInitialData !== false;
2866
+ const subscribeToUpdates = options?.subscribeToUpdates !== false;
2867
+ const initialDataBlock = includeInitial ? `
2868
+ window.__mcpToolOutput = ${safeJson(output ?? null)};
2869
+ if (window.__frontmcp && window.__frontmcp.setState) {
2870
+ window.__frontmcp.setState({
2871
+ output: window.__mcpToolOutput,
2872
+ loading: false,
2873
+ });
2874
+ }` : `
2875
+ window.__mcpToolOutput = null;
2876
+ if (window.__frontmcp && window.__frontmcp.setState) {
2877
+ window.__frontmcp.setState({
2878
+ output: null,
2879
+ loading: true,
2880
+ });
2881
+ }`;
2882
+ const subscriptionBlock = subscribeToUpdates ? `
2883
+ // Subscribe to platform tool result updates
2884
+ (function() {
2885
+ function subscribeToUpdates() {
2886
+ // OpenAI Apps SDK
2887
+ if (window.openai && window.openai.canvas && window.openai.canvas.onToolResult) {
2888
+ window.openai.canvas.onToolResult(function(result) {
2889
+ window.__mcpToolOutput = result;
2890
+ if (window.__frontmcp && window.__frontmcp.setState) {
2891
+ window.__frontmcp.setState({
2892
+ output: result,
2893
+ loading: false,
2894
+ });
2895
+ }
2896
+ // Dispatch custom event for React hooks
2897
+ window.dispatchEvent(new CustomEvent('frontmcp:toolResult', { detail: result }));
2898
+ });
2899
+ return;
2900
+ }
2901
+
2902
+ // Fallback: listen for custom events (for testing/other platforms)
2903
+ window.addEventListener('frontmcp:injectData', function(e) {
2904
+ if (e.detail && e.detail.output !== undefined) {
2905
+ window.__mcpToolOutput = e.detail.output;
2906
+ if (window.__frontmcp && window.__frontmcp.setState) {
2907
+ window.__frontmcp.setState({
2908
+ output: e.detail.output,
2909
+ loading: false,
2910
+ });
2911
+ }
2912
+ }
2913
+ });
2914
+ }
2915
+
2916
+ // Subscribe when DOM is ready
2917
+ if (document.readyState === 'loading') {
2918
+ document.addEventListener('DOMContentLoaded', subscribeToUpdates);
2919
+ } else {
2920
+ subscribeToUpdates();
2921
+ }
2922
+ })();` : "";
2923
+ return `
2924
+ <!-- Tool Data Injection (Dynamic Mode - OpenAI Subscription) -->
2925
+ <script>
2926
+ window.__mcpToolName = ${safeJson(toolName)};
2927
+ window.__mcpToolInput = ${safeJson(input ?? null)};
2928
+ window.__mcpStructuredContent = ${safeJson(structuredContent ?? null)};
2929
+ ${initialDataBlock}
2930
+
2931
+ // Initialize FrontMCP context
2932
+ if (window.__frontmcp && window.__frontmcp.setContext) {
2933
+ window.__frontmcp.setContext({
2934
+ toolName: window.__mcpToolName,
2935
+ toolInput: window.__mcpToolInput,
2936
+ toolOutput: window.__mcpToolOutput,
2937
+ structuredContent: window.__mcpStructuredContent,
2938
+ });
2939
+ }
2940
+ ${subscriptionBlock}
2941
+ </script>`;
2942
+ }
2943
+ /**
2944
+ * Build hybrid data injection - shell with placeholders for runtime injection.
2945
+ * Use injectHybridData() or injectHybridDataFull() from @frontmcp/uipack to replace the placeholders.
2946
+ */
2947
+ buildHybridDataScript(toolName, _input, structuredContent, options) {
2948
+ const safeJson = (value) => {
2949
+ try {
2950
+ return JSON.stringify(value);
2951
+ } catch {
2952
+ return "null";
2953
+ }
2954
+ };
2955
+ const outputPlaceholder = options?.placeholder ?? HYBRID_DATA_PLACEHOLDER;
2956
+ const inputPlaceholder = options?.inputPlaceholder ?? HYBRID_INPUT_PLACEHOLDER;
2957
+ return `
2958
+ <!-- Tool Data Injection (Hybrid Mode - Replace placeholders with JSON) -->
2959
+ <script>
2960
+ window.__mcpToolName = ${safeJson(toolName)};
2961
+ window.__mcpToolInput = "${inputPlaceholder}";
2962
+ window.__mcpToolOutput = "${outputPlaceholder}";
2963
+ window.__mcpStructuredContent = ${safeJson(structuredContent ?? null)};
2964
+ window.__mcpHybridError = null;
2965
+
2966
+ // Parse placeholders if they've been replaced with actual JSON
2967
+ (function() {
2968
+ var outputNotReplaced = false;
2969
+
2970
+ // Parse output placeholder
2971
+ var rawOutput = window.__mcpToolOutput;
2972
+ if (typeof rawOutput === 'string' && rawOutput !== "${outputPlaceholder}") {
2973
+ try {
2974
+ window.__mcpToolOutput = JSON.parse(rawOutput);
2975
+ } catch (e) {
2976
+ console.warn('[FrontMCP] Failed to parse injected output data:', e);
2977
+ window.__mcpToolOutput = null;
2978
+ window.__mcpHybridError = 'Failed to parse output data';
2979
+ }
2980
+ } else if (rawOutput === "${outputPlaceholder}") {
2981
+ // Placeholder not replaced - no data was injected
2982
+ window.__mcpToolOutput = null;
2983
+ outputNotReplaced = true;
2984
+ }
2985
+
2986
+ // Parse input placeholder
2987
+ var rawInput = window.__mcpToolInput;
2988
+ if (typeof rawInput === 'string' && rawInput !== "${inputPlaceholder}") {
2989
+ try {
2990
+ window.__mcpToolInput = JSON.parse(rawInput);
2991
+ } catch (e) {
2992
+ console.warn('[FrontMCP] Failed to parse injected input data:', e);
2993
+ window.__mcpToolInput = null;
2994
+ }
2995
+ } else if (rawInput === "${inputPlaceholder}") {
2996
+ window.__mcpToolInput = null;
2997
+ }
2998
+
2999
+ // Set error if output placeholder was not replaced (no data provided)
3000
+ if (outputNotReplaced) {
3001
+ window.__mcpHybridError = 'No data provided. The output placeholder was not replaced.';
3002
+ }
3003
+ })();
3004
+
3005
+ // Initialize FrontMCP context with appropriate loading/error state
3006
+ if (window.__frontmcp && window.__frontmcp.setContext) {
3007
+ window.__frontmcp.setContext({
3008
+ toolName: window.__mcpToolName,
3009
+ toolInput: window.__mcpToolInput,
3010
+ toolOutput: window.__mcpToolOutput,
3011
+ structuredContent: window.__mcpStructuredContent,
3012
+ loading: false,
3013
+ error: window.__mcpHybridError,
3014
+ });
3015
+ }
3016
+ </script>`;
3017
+ }
3157
3018
  /**
3158
3019
  * Build component render script.
3159
3020
  * Wraps CommonJS code with module/exports shim to capture the component.
@@ -3256,416 +3117,14 @@ function createBundler(options) {
3256
3117
  return new InMemoryBundler(options);
3257
3118
  }
3258
3119
 
3259
- // libs/ui/src/bundler/file-cache/storage/index.ts
3260
- init_interface();
3261
- init_filesystem();
3262
- init_redis();
3263
-
3264
- // libs/ui/src/bundler/file-cache/hash-calculator.ts
3265
- var import_crypto2 = require("crypto");
3266
- var import_promises2 = require("fs/promises");
3267
- var import_fs2 = require("fs");
3268
- var import_path2 = require("path");
3269
- function sha256(content) {
3270
- return (0, import_crypto2.createHash)("sha256").update(content, "utf8").digest("hex");
3271
- }
3272
- function sha256Buffer(buffer) {
3273
- return (0, import_crypto2.createHash)("sha256").update(buffer).digest("hex");
3274
- }
3275
- async function hashFile(filePath) {
3276
- try {
3277
- const content = await (0, import_promises2.readFile)(filePath);
3278
- return sha256Buffer(content);
3279
- } catch {
3280
- return void 0;
3281
- }
3282
- }
3283
- async function hashFiles(filePaths) {
3284
- const hashes = [];
3285
- for (const filePath of filePaths.sort()) {
3286
- const hash = await hashFile(filePath);
3287
- if (hash) {
3288
- hashes.push(`${filePath}:${hash}`);
3289
- }
3290
- }
3291
- return sha256(hashes.join("\n"));
3292
- }
3293
- async function calculateComponentHash(options) {
3294
- const {
3295
- entryPath,
3296
- baseDir = (0, import_path2.dirname)(entryPath),
3297
- externals = [],
3298
- dependencies = {},
3299
- bundleOptions = {},
3300
- maxDepth = 10
3301
- } = options;
3302
- const absoluteEntryPath = (0, import_path2.resolve)(entryPath);
3303
- const files = /* @__PURE__ */ new Set();
3304
- const fileHashes = {};
3305
- await collectLocalDependencies(absoluteEntryPath, baseDir, files, maxDepth, 0);
3306
- for (const file of files) {
3307
- const hash = await hashFile(file);
3308
- if (hash) {
3309
- fileHashes[file] = hash;
3310
- }
3311
- }
3312
- const sortedFiles = Array.from(files).sort();
3313
- const fileHashContent = sortedFiles.map((f) => `${f}:${fileHashes[f] || "missing"}`).join("\n");
3314
- const filesHash = sha256(fileHashContent);
3315
- const optionsHash = sha256(JSON.stringify(sortedObject(bundleOptions)));
3316
- const dependenciesHash = sha256(JSON.stringify(sortedObject(dependencies)));
3317
- const combinedHash = sha256([filesHash, optionsHash, dependenciesHash].join(":"));
3318
- return {
3319
- hash: combinedHash,
3320
- entryHash: fileHashes[absoluteEntryPath] || "",
3321
- files: sortedFiles,
3322
- fileHashes,
3323
- optionsHash,
3324
- dependenciesHash
3325
- };
3326
- }
3327
- async function calculateQuickHash(entryPath, bundleOptions) {
3328
- const entryHash = await hashFile(entryPath);
3329
- const optionsHash = bundleOptions ? sha256(JSON.stringify(sortedObject(bundleOptions))) : "";
3330
- return sha256(`${entryHash || "missing"}:${optionsHash}`);
3331
- }
3332
- async function collectLocalDependencies(filePath, baseDir, collected, maxDepth, currentDepth) {
3333
- if (currentDepth >= maxDepth) return;
3334
- if (collected.has(filePath)) return;
3335
- if (!(0, import_fs2.existsSync)(filePath)) return;
3336
- collected.add(filePath);
3337
- try {
3338
- const content = await (0, import_promises2.readFile)(filePath, "utf8");
3339
- const imports = extractImportPaths(content);
3340
- for (const importPath of imports) {
3341
- if (!importPath.startsWith(".") && !importPath.startsWith("/")) {
3342
- continue;
3343
- }
3344
- const resolvedPath = resolveImportPath(importPath, (0, import_path2.dirname)(filePath));
3345
- if (resolvedPath && (0, import_fs2.existsSync)(resolvedPath)) {
3346
- await collectLocalDependencies(resolvedPath, baseDir, collected, maxDepth, currentDepth + 1);
3347
- }
3348
- }
3349
- } catch {
3350
- }
3351
- }
3352
- function extractImportPaths(source) {
3353
- const paths = [];
3354
- const importRegex = /import\s+(?:[^'"]+\s+from\s+)?['"]([^'"]+)['"]/g;
3355
- let match;
3356
- while ((match = importRegex.exec(source)) !== null) {
3357
- paths.push(match[1]);
3358
- }
3359
- const exportRegex = /export\s+(?:\*|{[^}]+})\s+from\s+['"]([^'"]+)['"]/g;
3360
- while ((match = exportRegex.exec(source)) !== null) {
3361
- paths.push(match[1]);
3362
- }
3363
- const requireRegex = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
3364
- while ((match = requireRegex.exec(source)) !== null) {
3365
- paths.push(match[1]);
3366
- }
3367
- const dynamicRegex = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
3368
- while ((match = dynamicRegex.exec(source)) !== null) {
3369
- paths.push(match[1]);
3370
- }
3371
- return [...new Set(paths)];
3372
- }
3373
- function resolveImportPath(importPath, fromDir) {
3374
- const extensions = ["", ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
3375
- for (const ext of extensions) {
3376
- const fullPath = (0, import_path2.join)(fromDir, importPath + ext);
3377
- if ((0, import_fs2.existsSync)(fullPath)) {
3378
- return fullPath;
3379
- }
3380
- }
3381
- for (const ext of extensions) {
3382
- const indexPath = (0, import_path2.join)(fromDir, importPath, `index${ext}`);
3383
- if ((0, import_fs2.existsSync)(indexPath)) {
3384
- return indexPath;
3385
- }
3386
- }
3387
- return void 0;
3388
- }
3389
- function sortedObject(obj) {
3390
- const sorted = {};
3391
- const keys = Object.keys(obj).sort();
3392
- for (const key of keys) {
3393
- const value = obj[key];
3394
- if (value && typeof value === "object" && !Array.isArray(value)) {
3395
- sorted[key] = sortedObject(value);
3396
- } else {
3397
- sorted[key] = value;
3398
- }
3399
- }
3400
- return sorted;
3401
- }
3402
- function generateBuildId() {
3403
- const timestamp = Date.now().toString(36);
3404
- const random = Math.random().toString(36).substring(2, 10);
3405
- return `${timestamp}-${random}`;
3406
- }
3407
- function buildIdFromHash(hash) {
3408
- return hash.substring(0, 12);
3409
- }
3410
-
3411
- // libs/ui/src/bundler/file-cache/component-builder.ts
3412
- var import_promises3 = require("fs/promises");
3413
- var import_fs3 = require("fs");
3414
- var import_path3 = require("path");
3415
- var import_crypto3 = require("crypto");
3416
- var import_dependency = require("@frontmcp/uipack/dependency");
3417
- var ComponentBuilder = class {
3418
- storage;
3419
- esbuild = null;
3420
- constructor(storage) {
3421
- this.storage = storage;
3422
- }
3423
- /**
3424
- * Build a component from a file path.
3425
- */
3426
- async build(options) {
3427
- const startTime = performance.now();
3428
- const {
3429
- entryPath,
3430
- toolName,
3431
- externals = [],
3432
- dependencies = {},
3433
- bundleOptions = {},
3434
- platform = "unknown",
3435
- skipCache = false,
3436
- ssr = false,
3437
- ssrContext = {},
3438
- executeCode: executeCode3
3439
- } = options;
3440
- const absoluteEntryPath = (0, import_path3.resolve)(entryPath);
3441
- if (!(0, import_fs3.existsSync)(absoluteEntryPath)) {
3442
- throw new Error(`Entry file not found: ${absoluteEntryPath}`);
3443
- }
3444
- const hashResult = await calculateComponentHash({
3445
- entryPath: absoluteEntryPath,
3446
- externals,
3447
- dependencies,
3448
- bundleOptions
3449
- });
3450
- if (!skipCache) {
3451
- const cached = await this.storage.get(hashResult.hash);
3452
- if (cached) {
3453
- return {
3454
- manifest: cached,
3455
- cached: true,
3456
- buildTimeMs: performance.now() - startTime
3457
- };
3458
- }
3459
- }
3460
- const source = await (0, import_promises3.readFile)(absoluteEntryPath, "utf8");
3461
- const resolver = new import_dependency.DependencyResolver({ platform });
3462
- const resolvedDeps = [];
3463
- for (const pkg of externals) {
3464
- try {
3465
- const override = dependencies[pkg];
3466
- const resolved = resolver.resolve(pkg, override);
3467
- if (resolved) {
3468
- resolvedDeps.push(resolved);
3469
- }
3470
- } catch (error) {
3471
- console.warn(`Failed to resolve external "${pkg}": ${error}`);
3472
- }
3473
- }
3474
- const allExternals = new Set(externals);
3475
- for (const dep of resolvedDeps) {
3476
- const entry = resolver.getRegistry()[dep.packageName];
3477
- if (entry?.providers) {
3478
- const providerConfig = Object.values(entry.providers)[0];
3479
- if (providerConfig?.peerDependencies) {
3480
- for (const peer of providerConfig.peerDependencies) {
3481
- if (!allExternals.has(peer)) {
3482
- allExternals.add(peer);
3483
- try {
3484
- const peerOverride = dependencies[peer];
3485
- const resolved = resolver.resolve(peer, peerOverride);
3486
- if (resolved) {
3487
- resolvedDeps.push(resolved);
3488
- }
3489
- } catch {
3490
- }
3491
- }
3492
- }
3493
- }
3494
- }
3495
- }
3496
- const importMap = (0, import_dependency.createImportMap)(resolvedDeps);
3497
- const bundleResult = await this.bundleComponent({
3498
- source,
3499
- entryPath: absoluteEntryPath,
3500
- externals: Array.from(allExternals),
3501
- bundleOptions
3502
- });
3503
- let ssrHtml;
3504
- if (ssr) {
3505
- ssrHtml = await this.renderSSR(bundleResult.code, ssrContext, resolvedDeps, executeCode3);
3506
- }
3507
- const manifest = {
3508
- version: "1.0",
3509
- buildId: (0, import_crypto3.randomUUID)(),
3510
- toolName,
3511
- entryPath: absoluteEntryPath,
3512
- contentHash: hashResult.hash,
3513
- dependencies: resolvedDeps,
3514
- outputs: {
3515
- code: bundleResult.code,
3516
- sourceMap: bundleResult.map,
3517
- ssrHtml
3518
- },
3519
- importMap,
3520
- metadata: {
3521
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3522
- buildTimeMs: performance.now() - startTime,
3523
- totalSize: Buffer.byteLength(bundleResult.code, "utf8"),
3524
- bundlerVersion: bundleResult.bundlerVersion
3525
- }
3526
- };
3527
- await this.storage.set(hashResult.hash, manifest);
3528
- return {
3529
- manifest,
3530
- cached: false,
3531
- buildTimeMs: performance.now() - startTime
3532
- };
3533
- }
3534
- /**
3535
- * Build multiple components.
3536
- */
3537
- async buildMany(options) {
3538
- return Promise.all(options.map((opt) => this.build(opt)));
3539
- }
3540
- /**
3541
- * Check if a component needs rebuilding.
3542
- */
3543
- async needsRebuild(options) {
3544
- const absoluteEntryPath = (0, import_path3.resolve)(options.entryPath);
3545
- const hashResult = await calculateComponentHash({
3546
- entryPath: absoluteEntryPath,
3547
- externals: options.externals,
3548
- dependencies: options.dependencies,
3549
- bundleOptions: options.bundleOptions
3550
- });
3551
- const cached = await this.storage.has(hashResult.hash);
3552
- return !cached;
3553
- }
3554
- /**
3555
- * Get a cached build if it exists.
3556
- */
3557
- async getCached(options) {
3558
- const absoluteEntryPath = (0, import_path3.resolve)(options.entryPath);
3559
- const hashResult = await calculateComponentHash({
3560
- entryPath: absoluteEntryPath,
3561
- externals: options.externals,
3562
- dependencies: options.dependencies,
3563
- bundleOptions: options.bundleOptions
3564
- });
3565
- return this.storage.get(hashResult.hash);
3566
- }
3567
- /**
3568
- * Invalidate a cached build.
3569
- */
3570
- async invalidate(contentHash) {
3571
- return this.storage.delete(contentHash);
3572
- }
3573
- /**
3574
- * Generate complete HTML for a built component.
3575
- */
3576
- generateHTML(manifest, minify = false) {
3577
- const parts = [];
3578
- const dependencyHtml = (0, import_dependency.generateDependencyHTML)(manifest.dependencies, { minify });
3579
- parts.push(dependencyHtml);
3580
- parts.push(`<script type="module">${manifest.outputs.code}</script>`);
3581
- return parts.join(minify ? "" : "\n");
3582
- }
3583
- /**
3584
- * Bundle a component using esbuild.
3585
- */
3586
- async bundleComponent(options) {
3587
- const { source, entryPath, externals, bundleOptions } = options;
3588
- if (!this.esbuild) {
3589
- try {
3590
- this.esbuild = await import("esbuild");
3591
- } catch {
3592
- throw new Error("esbuild is required for component building. Install with: npm install esbuild");
3593
- }
3594
- }
3595
- const ext = (0, import_path3.extname)(entryPath).toLowerCase();
3596
- const loader = ext === ".tsx" ? "tsx" : ext === ".ts" ? "ts" : ext === ".jsx" ? "jsx" : "js";
3597
- try {
3598
- const result = await this.esbuild.transform(source, {
3599
- loader,
3600
- format: "esm",
3601
- minify: bundleOptions.minify ?? process.env["NODE_ENV"] === "production",
3602
- sourcemap: bundleOptions.sourceMaps ? "inline" : false,
3603
- target: bundleOptions.target ?? "es2020",
3604
- treeShaking: bundleOptions.treeShake ?? true,
3605
- jsx: "automatic",
3606
- jsxImportSource: bundleOptions.jsxImportSource ?? "react",
3607
- // Mark externals for later import map resolution
3608
- banner: externals.length > 0 ? `/* externals: ${externals.join(", ")} */` : void 0
3609
- });
3610
- return {
3611
- code: result.code,
3612
- map: result.map || void 0,
3613
- bundlerVersion: this.esbuild.version
3614
- };
3615
- } catch (error) {
3616
- throw new Error(`Bundle failed for ${entryPath}: ${error}`);
3617
- }
3618
- }
3619
- /**
3620
- * Perform server-side rendering.
3621
- */
3622
- async renderSSR(code, context, dependencies, executeCode3) {
3623
- const hasReact = dependencies.some((d) => d.packageName === "react");
3624
- if (!hasReact) {
3625
- console.warn("SSR requires React as an external dependency");
3626
- return void 0;
3627
- }
3628
- try {
3629
- const React = await import("react");
3630
- const ReactDOMServer = await import("react-dom/server");
3631
- const exports2 = {};
3632
- const module2 = { exports: exports2 };
3633
- if (executeCode3) {
3634
- executeCode3(code, exports2, module2, React);
3635
- } else {
3636
- const fn = new Function("exports", "module", "React", code);
3637
- fn(exports2, module2, React);
3638
- }
3639
- const Component = module2.exports.default || module2.exports;
3640
- if (typeof Component !== "function") {
3641
- console.warn("SSR: No default component export found");
3642
- return void 0;
3643
- }
3644
- const element = React.createElement(Component, context);
3645
- return ReactDOMServer.renderToString(element);
3646
- } catch (error) {
3647
- console.warn(`SSR failed: ${error}`);
3648
- return void 0;
3649
- }
3650
- }
3651
- };
3652
- async function createFilesystemBuilder(cacheDir = ".frontmcp-cache/builds") {
3653
- const { FilesystemStorage: FilesystemStorage2 } = await Promise.resolve().then(() => (init_filesystem(), filesystem_exports));
3654
- const storage = new FilesystemStorage2({ cacheDir });
3655
- await storage.initialize();
3656
- return new ComponentBuilder(storage);
3657
- }
3658
- async function createRedisBuilder(redisClient, keyPrefix = "frontmcp:ui:build:") {
3659
- const { RedisStorage: RedisStorage2 } = await Promise.resolve().then(() => (init_redis(), redis_exports));
3660
- const storage = new RedisStorage2({
3661
- client: redisClient,
3662
- keyPrefix
3663
- });
3664
- await storage.initialize();
3665
- return new ComponentBuilder(storage);
3666
- }
3120
+ // libs/ui/src/bundler/index.ts
3121
+ var import_bundler4 = require("@frontmcp/uipack/bundler");
3122
+ var import_bundler5 = require("@frontmcp/uipack/bundler");
3123
+ var import_bundler6 = require("@frontmcp/uipack/bundler");
3124
+ var import_bundler7 = require("@frontmcp/uipack/bundler");
3667
3125
  // Annotate the CommonJS export names for ESM import in node:
3668
3126
  0 && (module.exports = {
3127
+ ALL_PLATFORMS,
3669
3128
  BundlerCache,
3670
3129
  ComponentBuilder,
3671
3130
  DEFAULT_BUNDLER_OPTIONS,
@@ -3675,6 +3134,8 @@ async function createRedisBuilder(redisClient, keyPrefix = "frontmcp:ui:build:")
3675
3134
  DEFAULT_STORAGE_OPTIONS,
3676
3135
  ExecutionError,
3677
3136
  FilesystemStorage,
3137
+ HYBRID_DATA_PLACEHOLDER,
3138
+ HYBRID_INPUT_PLACEHOLDER,
3678
3139
  InMemoryBundler,
3679
3140
  RedisStorage,
3680
3141
  STATIC_HTML_CDN,