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