@frontmcp/ui 0.6.0 → 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.
- package/README.md +140 -362
- package/bridge/core/bridge-factory.d.ts +1 -0
- package/bridge/core/bridge-factory.d.ts.map +1 -1
- package/bridge/index.d.ts +1 -1
- package/bridge/index.d.ts.map +1 -1
- package/bridge/index.js +39 -881
- package/bridge/runtime/index.d.ts +2 -1
- package/bridge/runtime/index.d.ts.map +1 -1
- package/bundler/browser-components.d.ts +42 -0
- package/bundler/browser-components.d.ts.map +1 -0
- package/bundler/bundler.d.ts +78 -4
- package/bundler/bundler.d.ts.map +1 -1
- package/bundler/index.d.ts +8 -8
- package/bundler/index.d.ts.map +1 -1
- package/bundler/index.js +1411 -2997
- package/bundler/types.d.ts +188 -7
- package/bundler/types.d.ts.map +1 -1
- package/components/alert.schema.d.ts +6 -6
- package/components/avatar.schema.d.ts +9 -9
- package/components/badge.schema.d.ts +9 -9
- package/components/button.schema.d.ts +9 -9
- package/components/card.schema.d.ts +7 -7
- package/components/form.schema.d.ts +24 -24
- package/components/index.js +128 -198
- package/components/modal.schema.d.ts +8 -8
- package/components/table.schema.d.ts +6 -6
- package/esm/bridge/{index.js → index.mjs} +40 -877
- package/esm/bundler/index.mjs +3136 -0
- package/esm/components/{index.js → index.mjs} +136 -196
- package/esm/index.mjs +5450 -0
- package/esm/layouts/index.mjs +409 -0
- package/esm/package.json +15 -32
- package/esm/react/{index.js → index.mjs} +71 -260
- package/esm/renderers/index.mjs +611 -0
- package/esm/universal/index.mjs +1951 -0
- package/esm/web-components/{index.js → index.mjs} +232 -287
- package/index.d.ts +22 -41
- package/index.d.ts.map +1 -1
- package/index.js +4286 -19607
- package/layouts/base.d.ts +2 -2
- package/layouts/base.d.ts.map +1 -1
- package/layouts/index.js +46 -539
- package/layouts/presets.d.ts.map +1 -1
- package/package.json +15 -32
- package/react/Alert.d.ts +1 -2
- package/react/Alert.d.ts.map +1 -1
- package/react/Badge.d.ts +1 -2
- package/react/Badge.d.ts.map +1 -1
- package/react/Button.d.ts +1 -2
- package/react/Button.d.ts.map +1 -1
- package/react/Card.d.ts +1 -2
- package/react/Card.d.ts.map +1 -1
- package/react/hooks/context.d.ts +1 -1
- package/react/hooks/context.d.ts.map +1 -1
- package/react/index.d.ts +5 -7
- package/react/index.d.ts.map +1 -1
- package/react/index.js +55 -269
- package/react/types.d.ts +1 -2
- package/react/types.d.ts.map +1 -1
- package/renderers/index.d.ts +15 -25
- package/renderers/index.d.ts.map +1 -1
- package/renderers/index.js +393 -1619
- package/renderers/mdx.renderer.d.ts +13 -34
- package/renderers/mdx.renderer.d.ts.map +1 -1
- package/{esm/runtime/adapters → renderers}/react.adapter.d.ts +2 -2
- package/renderers/react.adapter.d.ts.map +1 -0
- package/renderers/react.renderer.d.ts +25 -16
- package/renderers/react.renderer.d.ts.map +1 -1
- package/renderers/transpiler.d.ts +49 -0
- package/renderers/transpiler.d.ts.map +1 -0
- package/universal/cached-runtime.d.ts +25 -1
- package/universal/cached-runtime.d.ts.map +1 -1
- package/universal/index.js +2037 -0
- package/universal/runtime-builder.d.ts.map +1 -1
- package/universal/types.d.ts.map +1 -1
- package/web-components/elements/fmcp-input.d.ts.map +1 -1
- package/web-components/elements/fmcp-select.d.ts.map +1 -1
- package/web-components/index.d.ts +0 -1
- package/web-components/index.d.ts.map +1 -1
- package/web-components/index.js +224 -289
- package/adapters/index.d.ts +0 -13
- package/adapters/index.d.ts.map +0 -1
- package/adapters/index.js +0 -462
- package/adapters/platform-meta.d.ts +0 -166
- package/adapters/platform-meta.d.ts.map +0 -1
- package/adapters/response-builder.d.ts +0 -108
- package/adapters/response-builder.d.ts.map +0 -1
- package/adapters/serving-mode.d.ts +0 -107
- package/adapters/serving-mode.d.ts.map +0 -1
- package/base-template/bridge.d.ts +0 -90
- package/base-template/bridge.d.ts.map +0 -1
- package/base-template/default-base-template.d.ts +0 -92
- package/base-template/default-base-template.d.ts.map +0 -1
- package/base-template/index.d.ts +0 -15
- package/base-template/index.d.ts.map +0 -1
- package/base-template/index.js +0 -1398
- package/base-template/polyfills.d.ts +0 -31
- package/base-template/polyfills.d.ts.map +0 -1
- package/base-template/theme-styles.d.ts +0 -74
- package/base-template/theme-styles.d.ts.map +0 -1
- package/build/cdn-resources.d.ts +0 -243
- package/build/cdn-resources.d.ts.map +0 -1
- package/build/index.d.ts +0 -295
- package/build/index.d.ts.map +0 -1
- package/build/index.js +0 -7096
- package/build/widget-manifest.d.ts +0 -362
- package/build/widget-manifest.d.ts.map +0 -1
- package/bundler/cache.d.ts +0 -173
- package/bundler/cache.d.ts.map +0 -1
- package/bundler/file-cache/component-builder.d.ts +0 -167
- package/bundler/file-cache/component-builder.d.ts.map +0 -1
- package/bundler/file-cache/hash-calculator.d.ts +0 -155
- package/bundler/file-cache/hash-calculator.d.ts.map +0 -1
- package/bundler/file-cache/index.d.ts +0 -12
- package/bundler/file-cache/index.d.ts.map +0 -1
- package/bundler/file-cache/storage/filesystem.d.ts +0 -149
- package/bundler/file-cache/storage/filesystem.d.ts.map +0 -1
- package/bundler/file-cache/storage/index.d.ts +0 -11
- package/bundler/file-cache/storage/index.d.ts.map +0 -1
- package/bundler/file-cache/storage/interface.d.ts +0 -152
- package/bundler/file-cache/storage/interface.d.ts.map +0 -1
- package/bundler/file-cache/storage/redis.d.ts +0 -139
- package/bundler/file-cache/storage/redis.d.ts.map +0 -1
- package/bundler/sandbox/enclave-adapter.d.ts +0 -121
- package/bundler/sandbox/enclave-adapter.d.ts.map +0 -1
- package/bundler/sandbox/executor.d.ts +0 -14
- package/bundler/sandbox/executor.d.ts.map +0 -1
- package/bundler/sandbox/policy.d.ts +0 -62
- package/bundler/sandbox/policy.d.ts.map +0 -1
- package/dependency/cdn-registry.d.ts +0 -98
- package/dependency/cdn-registry.d.ts.map +0 -1
- package/dependency/import-map.d.ts +0 -186
- package/dependency/import-map.d.ts.map +0 -1
- package/dependency/import-parser.d.ts +0 -82
- package/dependency/import-parser.d.ts.map +0 -1
- package/dependency/index.d.ts +0 -17
- package/dependency/index.d.ts.map +0 -1
- package/dependency/resolver.d.ts +0 -164
- package/dependency/resolver.d.ts.map +0 -1
- package/dependency/schemas.d.ts +0 -486
- package/dependency/schemas.d.ts.map +0 -1
- package/dependency/template-loader.d.ts +0 -204
- package/dependency/template-loader.d.ts.map +0 -1
- package/dependency/template-processor.d.ts +0 -118
- package/dependency/template-processor.d.ts.map +0 -1
- package/dependency/types.d.ts +0 -739
- package/dependency/types.d.ts.map +0 -1
- package/esm/adapters/index.d.ts +0 -13
- package/esm/adapters/index.d.ts.map +0 -1
- package/esm/adapters/index.js +0 -427
- package/esm/adapters/platform-meta.d.ts +0 -166
- package/esm/adapters/platform-meta.d.ts.map +0 -1
- package/esm/adapters/response-builder.d.ts +0 -108
- package/esm/adapters/response-builder.d.ts.map +0 -1
- package/esm/adapters/serving-mode.d.ts +0 -107
- package/esm/adapters/serving-mode.d.ts.map +0 -1
- package/esm/base-template/bridge.d.ts +0 -90
- package/esm/base-template/bridge.d.ts.map +0 -1
- package/esm/base-template/default-base-template.d.ts +0 -92
- package/esm/base-template/default-base-template.d.ts.map +0 -1
- package/esm/base-template/index.d.ts +0 -15
- package/esm/base-template/index.d.ts.map +0 -1
- package/esm/base-template/index.js +0 -1364
- package/esm/base-template/polyfills.d.ts +0 -31
- package/esm/base-template/polyfills.d.ts.map +0 -1
- package/esm/base-template/theme-styles.d.ts +0 -74
- package/esm/base-template/theme-styles.d.ts.map +0 -1
- package/esm/bridge/adapters/base-adapter.d.ts +0 -104
- package/esm/bridge/adapters/base-adapter.d.ts.map +0 -1
- package/esm/bridge/adapters/claude.adapter.d.ts +0 -67
- package/esm/bridge/adapters/claude.adapter.d.ts.map +0 -1
- package/esm/bridge/adapters/ext-apps.adapter.d.ts +0 -143
- package/esm/bridge/adapters/ext-apps.adapter.d.ts.map +0 -1
- package/esm/bridge/adapters/gemini.adapter.d.ts +0 -64
- package/esm/bridge/adapters/gemini.adapter.d.ts.map +0 -1
- package/esm/bridge/adapters/generic.adapter.d.ts +0 -56
- package/esm/bridge/adapters/generic.adapter.d.ts.map +0 -1
- package/esm/bridge/adapters/index.d.ts +0 -26
- package/esm/bridge/adapters/index.d.ts.map +0 -1
- package/esm/bridge/adapters/openai.adapter.d.ts +0 -65
- package/esm/bridge/adapters/openai.adapter.d.ts.map +0 -1
- package/esm/bridge/core/adapter-registry.d.ts +0 -122
- package/esm/bridge/core/adapter-registry.d.ts.map +0 -1
- package/esm/bridge/core/bridge-factory.d.ts +0 -199
- package/esm/bridge/core/bridge-factory.d.ts.map +0 -1
- package/esm/bridge/core/index.d.ts +0 -10
- package/esm/bridge/core/index.d.ts.map +0 -1
- package/esm/bridge/index.d.ts +0 -62
- package/esm/bridge/index.d.ts.map +0 -1
- package/esm/bridge/runtime/iife-generator.d.ts +0 -62
- package/esm/bridge/runtime/iife-generator.d.ts.map +0 -1
- package/esm/bridge/runtime/index.d.ts +0 -9
- package/esm/bridge/runtime/index.d.ts.map +0 -1
- package/esm/bridge/types.d.ts +0 -386
- package/esm/bridge/types.d.ts.map +0 -1
- package/esm/build/cdn-resources.d.ts +0 -243
- package/esm/build/cdn-resources.d.ts.map +0 -1
- package/esm/build/index.d.ts +0 -295
- package/esm/build/index.d.ts.map +0 -1
- package/esm/build/index.js +0 -7021
- package/esm/build/widget-manifest.d.ts +0 -362
- package/esm/build/widget-manifest.d.ts.map +0 -1
- package/esm/bundler/bundler.d.ts +0 -208
- package/esm/bundler/bundler.d.ts.map +0 -1
- package/esm/bundler/cache.d.ts +0 -173
- package/esm/bundler/cache.d.ts.map +0 -1
- package/esm/bundler/file-cache/component-builder.d.ts +0 -167
- package/esm/bundler/file-cache/component-builder.d.ts.map +0 -1
- package/esm/bundler/file-cache/hash-calculator.d.ts +0 -155
- package/esm/bundler/file-cache/hash-calculator.d.ts.map +0 -1
- package/esm/bundler/file-cache/index.d.ts +0 -12
- package/esm/bundler/file-cache/index.d.ts.map +0 -1
- package/esm/bundler/file-cache/storage/filesystem.d.ts +0 -149
- package/esm/bundler/file-cache/storage/filesystem.d.ts.map +0 -1
- package/esm/bundler/file-cache/storage/index.d.ts +0 -11
- package/esm/bundler/file-cache/storage/index.d.ts.map +0 -1
- package/esm/bundler/file-cache/storage/interface.d.ts +0 -152
- package/esm/bundler/file-cache/storage/interface.d.ts.map +0 -1
- package/esm/bundler/file-cache/storage/redis.d.ts +0 -139
- package/esm/bundler/file-cache/storage/redis.d.ts.map +0 -1
- package/esm/bundler/index.d.ts +0 -43
- package/esm/bundler/index.d.ts.map +0 -1
- package/esm/bundler/index.js +0 -4687
- package/esm/bundler/sandbox/enclave-adapter.d.ts +0 -121
- package/esm/bundler/sandbox/enclave-adapter.d.ts.map +0 -1
- package/esm/bundler/sandbox/executor.d.ts +0 -14
- package/esm/bundler/sandbox/executor.d.ts.map +0 -1
- package/esm/bundler/sandbox/policy.d.ts +0 -62
- package/esm/bundler/sandbox/policy.d.ts.map +0 -1
- package/esm/bundler/types.d.ts +0 -702
- package/esm/bundler/types.d.ts.map +0 -1
- package/esm/components/alert.d.ts +0 -66
- package/esm/components/alert.d.ts.map +0 -1
- package/esm/components/alert.schema.d.ts +0 -98
- package/esm/components/alert.schema.d.ts.map +0 -1
- package/esm/components/avatar.d.ts +0 -77
- package/esm/components/avatar.d.ts.map +0 -1
- package/esm/components/avatar.schema.d.ts +0 -170
- package/esm/components/avatar.schema.d.ts.map +0 -1
- package/esm/components/badge.d.ts +0 -64
- package/esm/components/badge.d.ts.map +0 -1
- package/esm/components/badge.schema.d.ts +0 -91
- package/esm/components/badge.schema.d.ts.map +0 -1
- package/esm/components/button.d.ts +0 -100
- package/esm/components/button.d.ts.map +0 -1
- package/esm/components/button.schema.d.ts +0 -120
- package/esm/components/button.schema.d.ts.map +0 -1
- package/esm/components/card.d.ts +0 -53
- package/esm/components/card.d.ts.map +0 -1
- package/esm/components/card.schema.d.ts +0 -93
- package/esm/components/card.schema.d.ts.map +0 -1
- package/esm/components/form.d.ts +0 -212
- package/esm/components/form.d.ts.map +0 -1
- package/esm/components/form.schema.d.ts +0 -365
- package/esm/components/form.schema.d.ts.map +0 -1
- package/esm/components/index.d.ts +0 -29
- package/esm/components/index.d.ts.map +0 -1
- package/esm/components/list.d.ts +0 -121
- package/esm/components/list.d.ts.map +0 -1
- package/esm/components/list.schema.d.ts +0 -129
- package/esm/components/list.schema.d.ts.map +0 -1
- package/esm/components/modal.d.ts +0 -100
- package/esm/components/modal.d.ts.map +0 -1
- package/esm/components/modal.schema.d.ts +0 -151
- package/esm/components/modal.schema.d.ts.map +0 -1
- package/esm/components/table.d.ts +0 -91
- package/esm/components/table.d.ts.map +0 -1
- package/esm/components/table.schema.d.ts +0 -123
- package/esm/components/table.schema.d.ts.map +0 -1
- package/esm/dependency/cdn-registry.d.ts +0 -98
- package/esm/dependency/cdn-registry.d.ts.map +0 -1
- package/esm/dependency/import-map.d.ts +0 -186
- package/esm/dependency/import-map.d.ts.map +0 -1
- package/esm/dependency/import-parser.d.ts +0 -82
- package/esm/dependency/import-parser.d.ts.map +0 -1
- package/esm/dependency/index.d.ts +0 -17
- package/esm/dependency/index.d.ts.map +0 -1
- package/esm/dependency/resolver.d.ts +0 -164
- package/esm/dependency/resolver.d.ts.map +0 -1
- package/esm/dependency/schemas.d.ts +0 -486
- package/esm/dependency/schemas.d.ts.map +0 -1
- package/esm/dependency/template-loader.d.ts +0 -204
- package/esm/dependency/template-loader.d.ts.map +0 -1
- package/esm/dependency/template-processor.d.ts +0 -118
- package/esm/dependency/template-processor.d.ts.map +0 -1
- package/esm/dependency/types.d.ts +0 -739
- package/esm/dependency/types.d.ts.map +0 -1
- package/esm/handlebars/expression-extractor.d.ts +0 -147
- package/esm/handlebars/expression-extractor.d.ts.map +0 -1
- package/esm/handlebars/helpers.d.ts +0 -339
- package/esm/handlebars/helpers.d.ts.map +0 -1
- package/esm/handlebars/index.d.ts +0 -195
- package/esm/handlebars/index.d.ts.map +0 -1
- package/esm/handlebars/index.js +0 -587
- package/esm/index.d.ts +0 -56
- package/esm/index.d.ts.map +0 -1
- package/esm/index.js +0 -20511
- package/esm/layouts/base.d.ts +0 -86
- package/esm/layouts/base.d.ts.map +0 -1
- package/esm/layouts/index.d.ts +0 -8
- package/esm/layouts/index.d.ts.map +0 -1
- package/esm/layouts/index.js +0 -892
- package/esm/layouts/presets.d.ts +0 -134
- package/esm/layouts/presets.d.ts.map +0 -1
- package/esm/pages/consent.d.ts +0 -117
- package/esm/pages/consent.d.ts.map +0 -1
- package/esm/pages/error.d.ts +0 -101
- package/esm/pages/error.d.ts.map +0 -1
- package/esm/pages/index.d.ts +0 -9
- package/esm/pages/index.d.ts.map +0 -1
- package/esm/pages/index.js +0 -1563
- package/esm/react/Alert.d.ts +0 -102
- package/esm/react/Alert.d.ts.map +0 -1
- package/esm/react/Badge.d.ts +0 -101
- package/esm/react/Badge.d.ts.map +0 -1
- package/esm/react/Button.d.ts +0 -109
- package/esm/react/Button.d.ts.map +0 -1
- package/esm/react/Card.d.ts +0 -104
- package/esm/react/Card.d.ts.map +0 -1
- package/esm/react/hooks/context.d.ts +0 -179
- package/esm/react/hooks/context.d.ts.map +0 -1
- package/esm/react/hooks/index.d.ts +0 -42
- package/esm/react/hooks/index.d.ts.map +0 -1
- package/esm/react/hooks/tools.d.ts +0 -284
- package/esm/react/hooks/tools.d.ts.map +0 -1
- package/esm/react/index.d.ts +0 -81
- package/esm/react/index.d.ts.map +0 -1
- package/esm/react/types.d.ts +0 -106
- package/esm/react/types.d.ts.map +0 -1
- package/esm/react/utils.d.ts +0 -43
- package/esm/react/utils.d.ts.map +0 -1
- package/esm/registry/index.d.ts +0 -46
- package/esm/registry/index.d.ts.map +0 -1
- package/esm/registry/index.js +0 -6422
- package/esm/registry/render-template.d.ts +0 -91
- package/esm/registry/render-template.d.ts.map +0 -1
- package/esm/registry/tool-ui.registry.d.ts +0 -294
- package/esm/registry/tool-ui.registry.d.ts.map +0 -1
- package/esm/registry/uri-utils.d.ts +0 -56
- package/esm/registry/uri-utils.d.ts.map +0 -1
- package/esm/render/index.d.ts +0 -8
- package/esm/render/index.d.ts.map +0 -1
- package/esm/render/prerender.d.ts +0 -57
- package/esm/render/prerender.d.ts.map +0 -1
- package/esm/renderers/cache.d.ts +0 -145
- package/esm/renderers/cache.d.ts.map +0 -1
- package/esm/renderers/html.renderer.d.ts +0 -123
- package/esm/renderers/html.renderer.d.ts.map +0 -1
- package/esm/renderers/index.d.ts +0 -36
- package/esm/renderers/index.d.ts.map +0 -1
- package/esm/renderers/index.js +0 -1827
- package/esm/renderers/mdx.renderer.d.ts +0 -120
- package/esm/renderers/mdx.renderer.d.ts.map +0 -1
- package/esm/renderers/react.renderer.d.ts +0 -96
- package/esm/renderers/react.renderer.d.ts.map +0 -1
- package/esm/renderers/registry.d.ts +0 -134
- package/esm/renderers/registry.d.ts.map +0 -1
- package/esm/renderers/types.d.ts +0 -342
- package/esm/renderers/types.d.ts.map +0 -1
- package/esm/renderers/utils/detect.d.ts +0 -107
- package/esm/renderers/utils/detect.d.ts.map +0 -1
- package/esm/renderers/utils/hash.d.ts +0 -40
- package/esm/renderers/utils/hash.d.ts.map +0 -1
- package/esm/renderers/utils/index.d.ts +0 -9
- package/esm/renderers/utils/index.d.ts.map +0 -1
- package/esm/renderers/utils/transpiler.d.ts +0 -89
- package/esm/renderers/utils/transpiler.d.ts.map +0 -1
- package/esm/runtime/adapters/html.adapter.d.ts +0 -59
- package/esm/runtime/adapters/html.adapter.d.ts.map +0 -1
- package/esm/runtime/adapters/index.d.ts +0 -26
- package/esm/runtime/adapters/index.d.ts.map +0 -1
- package/esm/runtime/adapters/mdx.adapter.d.ts +0 -73
- package/esm/runtime/adapters/mdx.adapter.d.ts.map +0 -1
- package/esm/runtime/adapters/react.adapter.d.ts.map +0 -1
- package/esm/runtime/adapters/types.d.ts +0 -95
- package/esm/runtime/adapters/types.d.ts.map +0 -1
- package/esm/runtime/csp.d.ts +0 -48
- package/esm/runtime/csp.d.ts.map +0 -1
- package/esm/runtime/index.d.ts +0 -17
- package/esm/runtime/index.d.ts.map +0 -1
- package/esm/runtime/index.js +0 -5186
- package/esm/runtime/mcp-bridge.d.ts +0 -101
- package/esm/runtime/mcp-bridge.d.ts.map +0 -1
- package/esm/runtime/renderer-runtime.d.ts +0 -133
- package/esm/runtime/renderer-runtime.d.ts.map +0 -1
- package/esm/runtime/sanitizer.d.ts +0 -172
- package/esm/runtime/sanitizer.d.ts.map +0 -1
- package/esm/runtime/types.d.ts +0 -415
- package/esm/runtime/types.d.ts.map +0 -1
- package/esm/runtime/wrapper.d.ts +0 -421
- package/esm/runtime/wrapper.d.ts.map +0 -1
- package/esm/styles/index.d.ts +0 -8
- package/esm/styles/index.d.ts.map +0 -1
- package/esm/styles/index.js +0 -171
- package/esm/styles/variants.d.ts +0 -51
- package/esm/styles/variants.d.ts.map +0 -1
- package/esm/theme/cdn.d.ts +0 -195
- package/esm/theme/cdn.d.ts.map +0 -1
- package/esm/theme/index.d.ts +0 -18
- package/esm/theme/index.d.ts.map +0 -1
- package/esm/theme/index.js +0 -700
- package/esm/theme/platforms.d.ts +0 -107
- package/esm/theme/platforms.d.ts.map +0 -1
- package/esm/theme/presets/github-openai.d.ts +0 -50
- package/esm/theme/presets/github-openai.d.ts.map +0 -1
- package/esm/theme/presets/index.d.ts +0 -11
- package/esm/theme/presets/index.d.ts.map +0 -1
- package/esm/theme/theme.d.ts +0 -396
- package/esm/theme/theme.d.ts.map +0 -1
- package/esm/tool-template/builder.d.ts +0 -213
- package/esm/tool-template/builder.d.ts.map +0 -1
- package/esm/tool-template/index.d.ts +0 -16
- package/esm/tool-template/index.d.ts.map +0 -1
- package/esm/tool-template/index.js +0 -3515
- package/esm/types/index.d.ts +0 -14
- package/esm/types/index.d.ts.map +0 -1
- package/esm/types/index.js +0 -75
- package/esm/types/ui-config.d.ts +0 -639
- package/esm/types/ui-config.d.ts.map +0 -1
- package/esm/types/ui-runtime.d.ts +0 -1007
- package/esm/types/ui-runtime.d.ts.map +0 -1
- package/esm/typings/cache/cache-adapter.d.ts +0 -125
- package/esm/typings/cache/cache-adapter.d.ts.map +0 -1
- package/esm/typings/cache/index.d.ts +0 -10
- package/esm/typings/cache/index.d.ts.map +0 -1
- package/esm/typings/cache/memory-cache.d.ts +0 -92
- package/esm/typings/cache/memory-cache.d.ts.map +0 -1
- package/esm/typings/dts-parser.d.ts +0 -90
- package/esm/typings/dts-parser.d.ts.map +0 -1
- package/esm/typings/index.d.ts +0 -48
- package/esm/typings/index.d.ts.map +0 -1
- package/esm/typings/schemas.d.ts +0 -232
- package/esm/typings/schemas.d.ts.map +0 -1
- package/esm/typings/type-fetcher.d.ts +0 -89
- package/esm/typings/type-fetcher.d.ts.map +0 -1
- package/esm/typings/types.d.ts +0 -320
- package/esm/typings/types.d.ts.map +0 -1
- package/esm/universal/UniversalApp.d.ts +0 -108
- package/esm/universal/UniversalApp.d.ts.map +0 -1
- package/esm/universal/cached-runtime.d.ts +0 -115
- package/esm/universal/cached-runtime.d.ts.map +0 -1
- package/esm/universal/context.d.ts +0 -122
- package/esm/universal/context.d.ts.map +0 -1
- package/esm/universal/index.d.ts +0 -57
- package/esm/universal/index.d.ts.map +0 -1
- package/esm/universal/renderers/html.renderer.d.ts +0 -37
- package/esm/universal/renderers/html.renderer.d.ts.map +0 -1
- package/esm/universal/renderers/index.d.ts +0 -112
- package/esm/universal/renderers/index.d.ts.map +0 -1
- package/esm/universal/renderers/markdown.renderer.d.ts +0 -33
- package/esm/universal/renderers/markdown.renderer.d.ts.map +0 -1
- package/esm/universal/renderers/mdx.renderer.d.ts +0 -38
- package/esm/universal/renderers/mdx.renderer.d.ts.map +0 -1
- package/esm/universal/renderers/react.renderer.d.ts +0 -46
- package/esm/universal/renderers/react.renderer.d.ts.map +0 -1
- package/esm/universal/runtime-builder.d.ts +0 -33
- package/esm/universal/runtime-builder.d.ts.map +0 -1
- package/esm/universal/store.d.ts +0 -135
- package/esm/universal/store.d.ts.map +0 -1
- package/esm/universal/types.d.ts +0 -199
- package/esm/universal/types.d.ts.map +0 -1
- package/esm/utils/escape-html.d.ts +0 -58
- package/esm/utils/escape-html.d.ts.map +0 -1
- package/esm/utils/index.d.ts +0 -10
- package/esm/utils/index.d.ts.map +0 -1
- package/esm/utils/index.js +0 -40
- package/esm/utils/safe-stringify.d.ts +0 -30
- package/esm/utils/safe-stringify.d.ts.map +0 -1
- package/esm/validation/error-box.d.ts +0 -56
- package/esm/validation/error-box.d.ts.map +0 -1
- package/esm/validation/index.d.ts +0 -13
- package/esm/validation/index.d.ts.map +0 -1
- package/esm/validation/index.js +0 -562
- package/esm/validation/schema-paths.d.ts +0 -118
- package/esm/validation/schema-paths.d.ts.map +0 -1
- package/esm/validation/template-validator.d.ts +0 -143
- package/esm/validation/template-validator.d.ts.map +0 -1
- package/esm/validation/wrapper.d.ts +0 -97
- package/esm/validation/wrapper.d.ts.map +0 -1
- package/esm/web-components/core/attribute-parser.d.ts +0 -82
- package/esm/web-components/core/attribute-parser.d.ts.map +0 -1
- package/esm/web-components/core/base-element.d.ts +0 -197
- package/esm/web-components/core/base-element.d.ts.map +0 -1
- package/esm/web-components/core/index.d.ts +0 -9
- package/esm/web-components/core/index.d.ts.map +0 -1
- package/esm/web-components/elements/fmcp-alert.d.ts +0 -46
- package/esm/web-components/elements/fmcp-alert.d.ts.map +0 -1
- package/esm/web-components/elements/fmcp-badge.d.ts +0 -47
- package/esm/web-components/elements/fmcp-badge.d.ts.map +0 -1
- package/esm/web-components/elements/fmcp-button.d.ts +0 -117
- package/esm/web-components/elements/fmcp-button.d.ts.map +0 -1
- package/esm/web-components/elements/fmcp-card.d.ts +0 -53
- package/esm/web-components/elements/fmcp-card.d.ts.map +0 -1
- package/esm/web-components/elements/fmcp-input.d.ts +0 -96
- package/esm/web-components/elements/fmcp-input.d.ts.map +0 -1
- package/esm/web-components/elements/fmcp-select.d.ts +0 -100
- package/esm/web-components/elements/fmcp-select.d.ts.map +0 -1
- package/esm/web-components/elements/index.d.ts +0 -13
- package/esm/web-components/elements/index.d.ts.map +0 -1
- package/esm/web-components/index.d.ts +0 -50
- package/esm/web-components/index.d.ts.map +0 -1
- package/esm/web-components/register.d.ts +0 -57
- package/esm/web-components/register.d.ts.map +0 -1
- package/esm/web-components/types.d.ts +0 -122
- package/esm/web-components/types.d.ts.map +0 -1
- package/esm/widgets/index.d.ts +0 -8
- package/esm/widgets/index.d.ts.map +0 -1
- package/esm/widgets/index.js +0 -941
- package/esm/widgets/progress.d.ts +0 -133
- package/esm/widgets/progress.d.ts.map +0 -1
- package/esm/widgets/resource.d.ts +0 -163
- package/esm/widgets/resource.d.ts.map +0 -1
- package/handlebars/expression-extractor.d.ts +0 -147
- package/handlebars/expression-extractor.d.ts.map +0 -1
- package/handlebars/helpers.d.ts +0 -339
- package/handlebars/helpers.d.ts.map +0 -1
- package/handlebars/index.d.ts +0 -195
- package/handlebars/index.d.ts.map +0 -1
- package/handlebars/index.js +0 -666
- package/pages/consent.d.ts +0 -117
- package/pages/consent.d.ts.map +0 -1
- package/pages/error.d.ts +0 -101
- package/pages/error.d.ts.map +0 -1
- package/pages/index.d.ts +0 -9
- package/pages/index.d.ts.map +0 -1
- package/pages/index.js +0 -1602
- package/react/utils.d.ts +0 -43
- package/react/utils.d.ts.map +0 -1
- package/registry/index.d.ts +0 -46
- package/registry/index.d.ts.map +0 -1
- package/registry/index.js +0 -6465
- package/registry/render-template.d.ts +0 -91
- package/registry/render-template.d.ts.map +0 -1
- package/registry/tool-ui.registry.d.ts +0 -294
- package/registry/tool-ui.registry.d.ts.map +0 -1
- package/registry/uri-utils.d.ts +0 -56
- package/registry/uri-utils.d.ts.map +0 -1
- package/renderers/cache.d.ts +0 -145
- package/renderers/cache.d.ts.map +0 -1
- package/renderers/html.renderer.d.ts +0 -123
- package/renderers/html.renderer.d.ts.map +0 -1
- package/renderers/registry.d.ts +0 -134
- package/renderers/registry.d.ts.map +0 -1
- package/renderers/types.d.ts +0 -342
- package/renderers/types.d.ts.map +0 -1
- package/renderers/utils/detect.d.ts +0 -107
- package/renderers/utils/detect.d.ts.map +0 -1
- package/renderers/utils/hash.d.ts +0 -40
- package/renderers/utils/hash.d.ts.map +0 -1
- package/renderers/utils/index.d.ts +0 -9
- package/renderers/utils/index.d.ts.map +0 -1
- package/renderers/utils/transpiler.d.ts +0 -89
- package/renderers/utils/transpiler.d.ts.map +0 -1
- package/runtime/adapters/html.adapter.d.ts +0 -59
- package/runtime/adapters/html.adapter.d.ts.map +0 -1
- package/runtime/adapters/index.d.ts +0 -26
- package/runtime/adapters/index.d.ts.map +0 -1
- package/runtime/adapters/mdx.adapter.d.ts +0 -73
- package/runtime/adapters/mdx.adapter.d.ts.map +0 -1
- package/runtime/adapters/react.adapter.d.ts +0 -70
- package/runtime/adapters/react.adapter.d.ts.map +0 -1
- package/runtime/adapters/types.d.ts +0 -95
- package/runtime/adapters/types.d.ts.map +0 -1
- package/runtime/csp.d.ts +0 -48
- package/runtime/csp.d.ts.map +0 -1
- package/runtime/index.d.ts +0 -17
- package/runtime/index.d.ts.map +0 -1
- package/runtime/index.js +0 -5264
- package/runtime/mcp-bridge.d.ts +0 -101
- package/runtime/mcp-bridge.d.ts.map +0 -1
- package/runtime/renderer-runtime.d.ts +0 -133
- package/runtime/renderer-runtime.d.ts.map +0 -1
- package/runtime/sanitizer.d.ts +0 -172
- package/runtime/sanitizer.d.ts.map +0 -1
- package/runtime/types.d.ts +0 -415
- package/runtime/types.d.ts.map +0 -1
- package/runtime/wrapper.d.ts +0 -421
- package/runtime/wrapper.d.ts.map +0 -1
- package/styles/index.d.ts +0 -8
- package/styles/index.d.ts.map +0 -1
- package/styles/index.js +0 -222
- package/styles/variants.d.ts +0 -51
- package/styles/variants.d.ts.map +0 -1
- package/theme/cdn.d.ts +0 -195
- package/theme/cdn.d.ts.map +0 -1
- package/theme/index.d.ts +0 -18
- package/theme/index.d.ts.map +0 -1
- package/theme/index.js +0 -757
- package/theme/platforms.d.ts +0 -107
- package/theme/platforms.d.ts.map +0 -1
- package/theme/presets/github-openai.d.ts +0 -50
- package/theme/presets/github-openai.d.ts.map +0 -1
- package/theme/presets/index.d.ts +0 -11
- package/theme/presets/index.d.ts.map +0 -1
- package/theme/theme.d.ts +0 -396
- package/theme/theme.d.ts.map +0 -1
- package/tool-template/builder.d.ts +0 -213
- package/tool-template/builder.d.ts.map +0 -1
- package/tool-template/index.d.ts +0 -16
- package/tool-template/index.d.ts.map +0 -1
- package/tool-template/index.js +0 -3559
- package/types/index.d.ts +0 -14
- package/types/index.d.ts.map +0 -1
- package/types/index.js +0 -108
- package/types/ui-config.d.ts +0 -639
- package/types/ui-config.d.ts.map +0 -1
- package/types/ui-runtime.d.ts +0 -1007
- package/types/ui-runtime.d.ts.map +0 -1
- package/typings/cache/cache-adapter.d.ts +0 -125
- package/typings/cache/cache-adapter.d.ts.map +0 -1
- package/typings/cache/index.d.ts +0 -10
- package/typings/cache/index.d.ts.map +0 -1
- package/typings/cache/memory-cache.d.ts +0 -92
- package/typings/cache/memory-cache.d.ts.map +0 -1
- package/typings/dts-parser.d.ts +0 -90
- package/typings/dts-parser.d.ts.map +0 -1
- package/typings/index.d.ts +0 -48
- package/typings/index.d.ts.map +0 -1
- package/typings/schemas.d.ts +0 -232
- package/typings/schemas.d.ts.map +0 -1
- package/typings/type-fetcher.d.ts +0 -89
- package/typings/type-fetcher.d.ts.map +0 -1
- package/typings/types.d.ts +0 -320
- package/typings/types.d.ts.map +0 -1
- package/utils/escape-html.d.ts +0 -58
- package/utils/escape-html.d.ts.map +0 -1
- package/utils/index.d.ts +0 -10
- package/utils/index.d.ts.map +0 -1
- package/utils/index.js +0 -70
- package/utils/safe-stringify.d.ts +0 -30
- package/utils/safe-stringify.d.ts.map +0 -1
- package/validation/error-box.d.ts +0 -56
- package/validation/error-box.d.ts.map +0 -1
- package/validation/index.d.ts +0 -13
- package/validation/index.d.ts.map +0 -1
- package/validation/index.js +0 -603
- package/validation/schema-paths.d.ts +0 -118
- package/validation/schema-paths.d.ts.map +0 -1
- package/validation/template-validator.d.ts +0 -143
- package/validation/template-validator.d.ts.map +0 -1
- package/validation/wrapper.d.ts +0 -97
- package/validation/wrapper.d.ts.map +0 -1
- package/widgets/index.d.ts +0 -8
- package/widgets/index.d.ts.map +0 -1
- package/widgets/index.js +0 -978
- package/widgets/progress.d.ts +0 -133
- package/widgets/progress.d.ts.map +0 -1
- package/widgets/resource.d.ts +0 -163
- package/widgets/resource.d.ts.map +0 -1
- /package/esm/render/{index.js → index.mjs} +0 -0
|
@@ -0,0 +1,3136 @@
|
|
|
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');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
// libs/ui/src/bundler/types.ts
|
|
9
|
+
var HYBRID_DATA_PLACEHOLDER = "__FRONTMCP_OUTPUT_PLACEHOLDER__";
|
|
10
|
+
var HYBRID_INPUT_PLACEHOLDER = "__FRONTMCP_INPUT_PLACEHOLDER__";
|
|
11
|
+
var DEFAULT_SECURITY_POLICY = {
|
|
12
|
+
allowedImports: [/^react$/, /^react-dom$/, /^react\/jsx-runtime$/, /^react\/jsx-dev-runtime$/, /^@frontmcp\/ui/],
|
|
13
|
+
blockedImports: [
|
|
14
|
+
/^fs$/,
|
|
15
|
+
/^fs\//,
|
|
16
|
+
/^net$/,
|
|
17
|
+
/^child_process$/,
|
|
18
|
+
/^os$/,
|
|
19
|
+
/^path$/,
|
|
20
|
+
/^crypto$/,
|
|
21
|
+
/^http$/,
|
|
22
|
+
/^https$/,
|
|
23
|
+
/^dgram$/,
|
|
24
|
+
/^dns$/,
|
|
25
|
+
/^cluster$/,
|
|
26
|
+
/^readline$/,
|
|
27
|
+
/^repl$/,
|
|
28
|
+
/^tls$/,
|
|
29
|
+
/^vm$/,
|
|
30
|
+
/^worker_threads$/
|
|
31
|
+
],
|
|
32
|
+
maxBundleSize: 512e3,
|
|
33
|
+
// 500KB
|
|
34
|
+
maxTransformTime: 5e3,
|
|
35
|
+
// 5s
|
|
36
|
+
noEval: true,
|
|
37
|
+
noDynamicImports: true,
|
|
38
|
+
noRequire: true,
|
|
39
|
+
allowedGlobals: [
|
|
40
|
+
"console",
|
|
41
|
+
"Math",
|
|
42
|
+
"JSON",
|
|
43
|
+
"Date",
|
|
44
|
+
"Array",
|
|
45
|
+
"Object",
|
|
46
|
+
"String",
|
|
47
|
+
"Number",
|
|
48
|
+
"Boolean",
|
|
49
|
+
"Promise",
|
|
50
|
+
"Map",
|
|
51
|
+
"Set",
|
|
52
|
+
"WeakMap",
|
|
53
|
+
"WeakSet",
|
|
54
|
+
"Symbol",
|
|
55
|
+
"Reflect",
|
|
56
|
+
"Proxy",
|
|
57
|
+
"Error",
|
|
58
|
+
"TypeError",
|
|
59
|
+
"RangeError",
|
|
60
|
+
"SyntaxError",
|
|
61
|
+
"ReferenceError",
|
|
62
|
+
"parseInt",
|
|
63
|
+
"parseFloat",
|
|
64
|
+
"isNaN",
|
|
65
|
+
"isFinite",
|
|
66
|
+
"encodeURI",
|
|
67
|
+
"encodeURIComponent",
|
|
68
|
+
"decodeURI",
|
|
69
|
+
"decodeURIComponent",
|
|
70
|
+
"setTimeout",
|
|
71
|
+
"clearTimeout",
|
|
72
|
+
"setInterval",
|
|
73
|
+
"clearInterval",
|
|
74
|
+
"atob",
|
|
75
|
+
"btoa",
|
|
76
|
+
"Intl",
|
|
77
|
+
"TextEncoder",
|
|
78
|
+
"TextDecoder",
|
|
79
|
+
"URL",
|
|
80
|
+
"URLSearchParams",
|
|
81
|
+
"Uint8Array",
|
|
82
|
+
"Int8Array",
|
|
83
|
+
"Uint16Array",
|
|
84
|
+
"Int16Array",
|
|
85
|
+
"Uint32Array",
|
|
86
|
+
"Int32Array",
|
|
87
|
+
"Float32Array",
|
|
88
|
+
"Float64Array",
|
|
89
|
+
"BigInt",
|
|
90
|
+
"BigInt64Array",
|
|
91
|
+
"BigUint64Array",
|
|
92
|
+
"ArrayBuffer",
|
|
93
|
+
"SharedArrayBuffer",
|
|
94
|
+
"DataView",
|
|
95
|
+
"queueMicrotask"
|
|
96
|
+
]
|
|
97
|
+
};
|
|
98
|
+
var DEFAULT_BUNDLE_OPTIONS = {
|
|
99
|
+
sourceType: "auto",
|
|
100
|
+
format: "iife",
|
|
101
|
+
minify: false,
|
|
102
|
+
sourceMaps: false,
|
|
103
|
+
externals: ["react", "react-dom"],
|
|
104
|
+
jsx: {
|
|
105
|
+
runtime: "automatic",
|
|
106
|
+
importSource: "react"
|
|
107
|
+
},
|
|
108
|
+
target: "es2020",
|
|
109
|
+
globalName: "Widget",
|
|
110
|
+
skipCache: false
|
|
111
|
+
};
|
|
112
|
+
var DEFAULT_BUNDLER_OPTIONS = {
|
|
113
|
+
defaultSecurity: DEFAULT_SECURITY_POLICY,
|
|
114
|
+
cache: {
|
|
115
|
+
maxSize: 100,
|
|
116
|
+
ttl: 3e5,
|
|
117
|
+
// 5 minutes
|
|
118
|
+
disabled: false
|
|
119
|
+
},
|
|
120
|
+
verbose: false,
|
|
121
|
+
esbuildOptions: {}
|
|
122
|
+
};
|
|
123
|
+
var ALL_PLATFORMS = [
|
|
124
|
+
"openai",
|
|
125
|
+
"claude",
|
|
126
|
+
"cursor",
|
|
127
|
+
"ext-apps",
|
|
128
|
+
"generic"
|
|
129
|
+
];
|
|
130
|
+
var STATIC_HTML_CDN = {
|
|
131
|
+
/**
|
|
132
|
+
* ES modules from esm.sh (React 19, modern platforms)
|
|
133
|
+
*/
|
|
134
|
+
esm: {
|
|
135
|
+
react: "https://esm.sh/react@19",
|
|
136
|
+
reactDom: "https://esm.sh/react-dom@19/client"
|
|
137
|
+
},
|
|
138
|
+
/**
|
|
139
|
+
* UMD builds from cdnjs (React 18.2, Claude only trusts cloudflare)
|
|
140
|
+
*/
|
|
141
|
+
umd: {
|
|
142
|
+
react: "https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js",
|
|
143
|
+
reactDom: "https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"
|
|
144
|
+
},
|
|
145
|
+
/**
|
|
146
|
+
* Font CDN URLs
|
|
147
|
+
*/
|
|
148
|
+
fonts: {
|
|
149
|
+
preconnect: ["https://fonts.googleapis.com", "https://fonts.gstatic.com"],
|
|
150
|
+
inter: "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
function getCdnTypeForPlatform(platform) {
|
|
154
|
+
if (platform === "claude") return "umd";
|
|
155
|
+
return "esm";
|
|
156
|
+
}
|
|
157
|
+
var DEFAULT_STATIC_HTML_OPTIONS = {
|
|
158
|
+
sourceType: "auto",
|
|
159
|
+
targetPlatform: "auto",
|
|
160
|
+
minify: true,
|
|
161
|
+
skipCache: false,
|
|
162
|
+
rootId: "frontmcp-widget-root",
|
|
163
|
+
widgetAccessible: false,
|
|
164
|
+
externals: {
|
|
165
|
+
react: "cdn",
|
|
166
|
+
reactDom: "cdn",
|
|
167
|
+
tailwind: "cdn",
|
|
168
|
+
frontmcpUi: "inline"
|
|
169
|
+
},
|
|
170
|
+
// Universal mode defaults
|
|
171
|
+
universal: false,
|
|
172
|
+
contentType: "auto",
|
|
173
|
+
includeMarkdown: false,
|
|
174
|
+
includeMdx: false,
|
|
175
|
+
// Build mode defaults
|
|
176
|
+
buildMode: "static"
|
|
177
|
+
};
|
|
178
|
+
|
|
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"
|
|
201
|
+
}
|
|
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";
|
|
208
|
+
}
|
|
209
|
+
if (typeof source !== "string") {
|
|
210
|
+
return "html";
|
|
211
|
+
}
|
|
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";
|
|
218
|
+
}
|
|
219
|
+
if (hasJsxTags && hasMarkdown && !hasModuleSyntax) {
|
|
220
|
+
return "mdx";
|
|
221
|
+
}
|
|
222
|
+
if (hasMarkdown || /\*\*[^*]+\*\*/.test(source) || /\[[^\]]+\]\([^)]+\)/.test(source)) {
|
|
223
|
+
return "markdown";
|
|
224
|
+
}
|
|
225
|
+
if (hasJsxTags && !hasModuleSyntax) {
|
|
226
|
+
return "mdx";
|
|
227
|
+
}
|
|
228
|
+
return "html";
|
|
229
|
+
}
|
|
230
|
+
|
|
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__*/"
|
|
241
|
+
};
|
|
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(":");
|
|
262
|
+
}
|
|
263
|
+
function buildStoreRuntime() {
|
|
264
|
+
return `
|
|
265
|
+
// FrontMCP Store (Vendor)
|
|
266
|
+
(function() {
|
|
267
|
+
var state = {
|
|
268
|
+
toolName: null,
|
|
269
|
+
input: null,
|
|
270
|
+
output: null,
|
|
271
|
+
content: null,
|
|
272
|
+
structuredContent: null,
|
|
273
|
+
loading: false,
|
|
274
|
+
error: null
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
var listeners = new Set();
|
|
278
|
+
|
|
279
|
+
window.__frontmcp = {
|
|
280
|
+
getState: function() { return state; },
|
|
281
|
+
setState: function(partial) {
|
|
282
|
+
state = Object.assign({}, state, partial);
|
|
283
|
+
listeners.forEach(function(fn) { fn(); });
|
|
284
|
+
},
|
|
285
|
+
subscribe: function(fn) {
|
|
286
|
+
listeners.add(fn);
|
|
287
|
+
return function() { listeners.delete(fn); };
|
|
288
|
+
},
|
|
289
|
+
reset: function() {
|
|
290
|
+
state = {
|
|
291
|
+
toolName: null,
|
|
292
|
+
input: null,
|
|
293
|
+
output: null,
|
|
294
|
+
content: null,
|
|
295
|
+
structuredContent: null,
|
|
296
|
+
loading: false,
|
|
297
|
+
error: null
|
|
298
|
+
};
|
|
299
|
+
},
|
|
300
|
+
context: state,
|
|
301
|
+
setContext: function(ctx) {
|
|
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;
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
// React hooks
|
|
318
|
+
window.useFrontMCPStore = function() {
|
|
319
|
+
var store = window.__frontmcp;
|
|
320
|
+
return React.useSyncExternalStore(
|
|
321
|
+
store.subscribe,
|
|
322
|
+
store.getState,
|
|
323
|
+
store.getState
|
|
324
|
+
);
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
window.useToolOutput = function() {
|
|
328
|
+
return window.useFrontMCPStore().output;
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
window.useToolInput = function() {
|
|
332
|
+
return window.useFrontMCPStore().input;
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
window.useContent = function() {
|
|
336
|
+
return window.useFrontMCPStore().content;
|
|
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
|
+
}
|
|
364
|
+
})();
|
|
365
|
+
`;
|
|
366
|
+
}
|
|
367
|
+
function buildRequireShim() {
|
|
368
|
+
return `
|
|
369
|
+
// Module Require Shim (Vendor)
|
|
370
|
+
(function() {
|
|
371
|
+
window.__moduleCache = {};
|
|
372
|
+
window.require = function(moduleName) {
|
|
373
|
+
if (window.__moduleCache[moduleName]) {
|
|
374
|
+
return window.__moduleCache[moduleName];
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
var moduleMap = {
|
|
378
|
+
'react': function() { return window.React; },
|
|
379
|
+
'react-dom': function() { return window.ReactDOM; },
|
|
380
|
+
'react-dom/client': function() { return window.ReactDOM; },
|
|
381
|
+
'react/jsx-runtime': function() { return window.jsx_runtime_namespaceObject; },
|
|
382
|
+
'react/jsx-dev-runtime': function() { return window.jsx_runtime_namespaceObject; },
|
|
383
|
+
'@frontmcp/ui': function() { return window.frontmcp_ui_namespaceObject; },
|
|
384
|
+
'@frontmcp/ui/react': function() { return window.frontmcp_ui_namespaceObject; }
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
var resolver = moduleMap[moduleName];
|
|
388
|
+
if (resolver) {
|
|
389
|
+
var mod = resolver();
|
|
390
|
+
window.__moduleCache[moduleName] = mod;
|
|
391
|
+
return mod;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
console.warn('[FrontMCP] Unknown module:', moduleName);
|
|
395
|
+
return {};
|
|
396
|
+
};
|
|
397
|
+
})();
|
|
398
|
+
`;
|
|
399
|
+
}
|
|
400
|
+
function buildInlineMarkdownParser(options) {
|
|
401
|
+
const allowUnsafeLinks = options?.contentSecurity?.bypassSanitization || options?.contentSecurity?.allowUnsafeLinks;
|
|
402
|
+
return `
|
|
403
|
+
// Inline Markdown Parser (Vendor)
|
|
404
|
+
(function() {
|
|
405
|
+
// XSS protection settings (configured at build time)
|
|
406
|
+
// Set to true if contentSecurity.allowUnsafeLinks or bypassSanitization is enabled
|
|
407
|
+
var __allowUnsafeLinks = ${allowUnsafeLinks ? "true" : "false"};
|
|
408
|
+
|
|
409
|
+
// URL scheme validation to prevent XSS via javascript: URLs
|
|
410
|
+
function isSafeUrl(url) {
|
|
411
|
+
// If unsafe links are explicitly allowed, skip validation
|
|
412
|
+
if (__allowUnsafeLinks) return true;
|
|
413
|
+
if (!url) return false;
|
|
414
|
+
var lower = url.toLowerCase().trim();
|
|
415
|
+
return lower.startsWith('http://') ||
|
|
416
|
+
lower.startsWith('https://') ||
|
|
417
|
+
lower.startsWith('/') ||
|
|
418
|
+
lower.startsWith('#') ||
|
|
419
|
+
lower.startsWith('mailto:');
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function parseMarkdown(md) {
|
|
423
|
+
var html = md;
|
|
424
|
+
html = html.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
425
|
+
html = html.replace(/^######\\s+(.*)$/gm, '<h6>$1</h6>');
|
|
426
|
+
html = html.replace(/^#####\\s+(.*)$/gm, '<h5>$1</h5>');
|
|
427
|
+
html = html.replace(/^####\\s+(.*)$/gm, '<h4>$1</h4>');
|
|
428
|
+
html = html.replace(/^###\\s+(.*)$/gm, '<h3>$1</h3>');
|
|
429
|
+
html = html.replace(/^##\\s+(.*)$/gm, '<h2>$1</h2>');
|
|
430
|
+
html = html.replace(/^#\\s+(.*)$/gm, '<h1>$1</h1>');
|
|
431
|
+
html = html.replace(/\\*\\*\\*(.+?)\\*\\*\\*/g, '<strong><em>$1</em></strong>');
|
|
432
|
+
html = html.replace(/\\*\\*(.+?)\\*\\*/g, '<strong>$1</strong>');
|
|
433
|
+
html = html.replace(/\\*(.+?)\\*/g, '<em>$1</em>');
|
|
434
|
+
html = html.replace(/\`([^\`]+)\`/g, '<code>$1</code>');
|
|
435
|
+
// Links - validate URL scheme to prevent XSS (unless bypassed)
|
|
436
|
+
html = html.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, function(match, text, url) {
|
|
437
|
+
return isSafeUrl(url) ? '<a href="' + url + '">' + text + '</a>' : text;
|
|
438
|
+
});
|
|
439
|
+
html = html.replace(/^[-*]\\s+(.*)$/gm, '<li>$1</li>');
|
|
440
|
+
html = html.replace(/\\n\\n+/g, '</p><p>');
|
|
441
|
+
html = '<p>' + html + '</p>';
|
|
442
|
+
return html;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
window.__frontmcp.parseMarkdown = parseMarkdown;
|
|
446
|
+
|
|
447
|
+
window.ReactMarkdown = function(props) {
|
|
448
|
+
var html = parseMarkdown(props.children || '');
|
|
449
|
+
return React.createElement('div', {
|
|
450
|
+
className: 'frontmcp-markdown prose',
|
|
451
|
+
dangerouslySetInnerHTML: { __html: html }
|
|
452
|
+
});
|
|
453
|
+
};
|
|
454
|
+
})();
|
|
455
|
+
`;
|
|
456
|
+
}
|
|
457
|
+
function buildRenderersRuntime(options) {
|
|
458
|
+
const bypassSanitization = options?.contentSecurity?.bypassSanitization;
|
|
459
|
+
const allowInlineScripts = bypassSanitization || options?.contentSecurity?.allowInlineScripts;
|
|
460
|
+
return `
|
|
461
|
+
// Universal Renderers (Vendor)
|
|
462
|
+
(function() {
|
|
463
|
+
var renderers = {};
|
|
464
|
+
|
|
465
|
+
// XSS protection settings (configured at build time)
|
|
466
|
+
// Set to true if contentSecurity.allowInlineScripts or bypassSanitization is enabled
|
|
467
|
+
var __allowInlineScripts = ${allowInlineScripts ? "true" : "false"};
|
|
468
|
+
|
|
469
|
+
renderers.html = {
|
|
470
|
+
type: 'html',
|
|
471
|
+
priority: 0,
|
|
472
|
+
canHandle: function(c) { return c.type === 'html'; },
|
|
473
|
+
render: function(c, ctx) {
|
|
474
|
+
var html = c.source;
|
|
475
|
+
// Apply XSS protection unless bypassed
|
|
476
|
+
if (!__allowInlineScripts) {
|
|
477
|
+
// Remove script tags and event handlers to prevent XSS
|
|
478
|
+
html = html.replace(/<script[^>]*>[\\s\\S]*?<\\/script>/gi, '');
|
|
479
|
+
html = html.replace(/\\s+on\\w+\\s*=/gi, ' data-removed-handler=');
|
|
480
|
+
}
|
|
481
|
+
return React.createElement('div', {
|
|
482
|
+
className: 'frontmcp-html-content',
|
|
483
|
+
dangerouslySetInnerHTML: { __html: html }
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
};
|
|
487
|
+
|
|
488
|
+
renderers.markdown = {
|
|
489
|
+
type: 'markdown',
|
|
490
|
+
priority: 10,
|
|
491
|
+
canHandle: function(c) {
|
|
492
|
+
if (c.type === 'markdown') return true;
|
|
493
|
+
if (typeof c.source !== 'string') return false;
|
|
494
|
+
var s = c.source;
|
|
495
|
+
return /^#{1,6}\\s/m.test(s) || /^[-*]\\s/m.test(s) || /\\*\\*[^*]+\\*\\*/.test(s);
|
|
496
|
+
},
|
|
497
|
+
render: function(c, ctx) {
|
|
498
|
+
if (window.ReactMarkdown) {
|
|
499
|
+
return React.createElement(window.ReactMarkdown, {
|
|
500
|
+
children: c.source,
|
|
501
|
+
components: Object.assign({}, ctx.components, c.components)
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
var html = window.__frontmcp.parseMarkdown ? window.__frontmcp.parseMarkdown(c.source) : c.source;
|
|
505
|
+
return React.createElement('div', {
|
|
506
|
+
className: 'frontmcp-markdown prose',
|
|
507
|
+
dangerouslySetInnerHTML: { __html: html }
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
|
|
512
|
+
renderers.react = {
|
|
513
|
+
type: 'react',
|
|
514
|
+
priority: 30,
|
|
515
|
+
canHandle: function(c) { return c.type === 'react' || typeof c.source === 'function'; },
|
|
516
|
+
render: function(c, ctx) {
|
|
517
|
+
var Component = c.source;
|
|
518
|
+
var props = Object.assign({
|
|
519
|
+
output: ctx.output,
|
|
520
|
+
input: ctx.input,
|
|
521
|
+
state: ctx.state,
|
|
522
|
+
data: ctx.output // Alias for convenience
|
|
523
|
+
}, c.props);
|
|
524
|
+
return React.createElement(Component, props);
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
renderers.mdx = {
|
|
529
|
+
type: 'mdx',
|
|
530
|
+
priority: 20,
|
|
531
|
+
canHandle: function(c) {
|
|
532
|
+
if (c.type === 'mdx') return true;
|
|
533
|
+
if (typeof c.source !== 'string') return false;
|
|
534
|
+
var s = c.source;
|
|
535
|
+
return /<[A-Z][a-zA-Z]*/.test(s) && /^#{1,6}\\s/m.test(s);
|
|
536
|
+
},
|
|
537
|
+
render: function(c, ctx) {
|
|
538
|
+
if (typeof c.compiledContent === 'function') {
|
|
539
|
+
var MDXContent = c.compiledContent;
|
|
540
|
+
return React.createElement(MDXContent, {
|
|
541
|
+
output: ctx.output,
|
|
542
|
+
input: ctx.input,
|
|
543
|
+
components: Object.assign({}, ctx.components, c.components)
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
return React.createElement('div', { className: 'frontmcp-mdx-fallback' }, [
|
|
547
|
+
React.createElement('div', {
|
|
548
|
+
key: 'warn',
|
|
549
|
+
className: 'bg-yellow-50 border border-yellow-200 rounded p-2 mb-2 text-sm text-yellow-800'
|
|
550
|
+
}, 'MDX requires pre-compilation. Showing raw content.'),
|
|
551
|
+
React.createElement('pre', {
|
|
552
|
+
key: 'pre',
|
|
553
|
+
className: 'bg-gray-100 p-4 rounded text-sm overflow-auto'
|
|
554
|
+
}, c.source)
|
|
555
|
+
]);
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
var sortedRenderers = [renderers.react, renderers.mdx, renderers.markdown, renderers.html];
|
|
560
|
+
|
|
561
|
+
window.__frontmcp.detectRenderer = function(content) {
|
|
562
|
+
if (content.type && renderers[content.type]) {
|
|
563
|
+
return renderers[content.type];
|
|
564
|
+
}
|
|
565
|
+
for (var i = 0; i < sortedRenderers.length; i++) {
|
|
566
|
+
if (sortedRenderers[i].canHandle(content)) {
|
|
567
|
+
return sortedRenderers[i];
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
return renderers.html;
|
|
571
|
+
};
|
|
572
|
+
|
|
573
|
+
window.__frontmcp.renderContent = function(content, context) {
|
|
574
|
+
var renderer = window.__frontmcp.detectRenderer(content);
|
|
575
|
+
return renderer.render(content, context);
|
|
576
|
+
};
|
|
577
|
+
|
|
578
|
+
window.__frontmcp.renderers = renderers;
|
|
579
|
+
})();
|
|
580
|
+
`;
|
|
581
|
+
}
|
|
582
|
+
function buildUIComponentsRuntime() {
|
|
583
|
+
return buildBrowserUIComponents();
|
|
584
|
+
}
|
|
585
|
+
function buildUniversalAppRuntime() {
|
|
586
|
+
return `
|
|
587
|
+
// Universal App (Vendor)
|
|
588
|
+
(function() {
|
|
589
|
+
var LoadingSpinner = function() {
|
|
590
|
+
return React.createElement('div', {
|
|
591
|
+
className: 'frontmcp-loading flex items-center justify-center min-h-[200px]'
|
|
592
|
+
}, React.createElement('div', {
|
|
593
|
+
className: 'frontmcp-spinner w-6 h-6 border-2 border-gray-200 border-t-blue-500 rounded-full animate-spin'
|
|
594
|
+
}));
|
|
595
|
+
};
|
|
596
|
+
|
|
597
|
+
var ErrorDisplay = function(props) {
|
|
598
|
+
return React.createElement('div', {
|
|
599
|
+
className: 'frontmcp-error bg-red-50 border border-red-200 rounded-lg p-4 text-red-800'
|
|
600
|
+
}, [
|
|
601
|
+
React.createElement('div', { key: 'title', className: 'font-medium' }, 'Error'),
|
|
602
|
+
React.createElement('div', { key: 'msg', className: 'text-sm mt-1' }, props.error)
|
|
603
|
+
]);
|
|
604
|
+
};
|
|
605
|
+
|
|
606
|
+
var EmptyState = function() {
|
|
607
|
+
return React.createElement('div', {
|
|
608
|
+
className: 'frontmcp-empty text-gray-500 text-center py-8'
|
|
609
|
+
}, 'No content to display');
|
|
610
|
+
};
|
|
611
|
+
|
|
612
|
+
window.__frontmcp.UniversalApp = function(props) {
|
|
613
|
+
var state = window.useFrontMCPStore();
|
|
614
|
+
|
|
615
|
+
if (state.loading) {
|
|
616
|
+
return props.fallback || React.createElement(LoadingSpinner);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
if (state.error) {
|
|
620
|
+
var ErrorComp = props.errorFallback || ErrorDisplay;
|
|
621
|
+
return React.createElement(ErrorComp, { error: state.error });
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
var content = props.content || state.content;
|
|
625
|
+
|
|
626
|
+
if (!content) {
|
|
627
|
+
return React.createElement(EmptyState);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
var context = {
|
|
631
|
+
output: state.output,
|
|
632
|
+
input: state.input,
|
|
633
|
+
state: state,
|
|
634
|
+
components: props.components || {}
|
|
635
|
+
};
|
|
636
|
+
|
|
637
|
+
var rendered = window.__frontmcp.renderContent(content, context);
|
|
638
|
+
return React.createElement('div', { className: 'frontmcp-content' }, rendered);
|
|
639
|
+
};
|
|
640
|
+
|
|
641
|
+
window.__frontmcp.LoadingSpinner = LoadingSpinner;
|
|
642
|
+
window.__frontmcp.ErrorDisplay = ErrorDisplay;
|
|
643
|
+
window.__frontmcp.EmptyState = EmptyState;
|
|
644
|
+
})();
|
|
645
|
+
`;
|
|
646
|
+
}
|
|
647
|
+
function buildComponentWrapper() {
|
|
648
|
+
return `
|
|
649
|
+
// Component Execution (App Chunk)
|
|
650
|
+
(function() {
|
|
651
|
+
${RUNTIME_PLACEHOLDERS.COMPONENT_CODE}
|
|
652
|
+
})();
|
|
653
|
+
`;
|
|
654
|
+
}
|
|
655
|
+
function buildDataInjectionWrapper() {
|
|
656
|
+
return `
|
|
657
|
+
// Data Injection (App Chunk)
|
|
658
|
+
(function() {
|
|
659
|
+
${RUNTIME_PLACEHOLDERS.DATA_INJECTION}
|
|
660
|
+
})();
|
|
661
|
+
`;
|
|
662
|
+
}
|
|
663
|
+
function buildCustomComponentsWrapper() {
|
|
664
|
+
return `
|
|
665
|
+
// Custom Components (App Chunk)
|
|
666
|
+
(function() {
|
|
667
|
+
${RUNTIME_PLACEHOLDERS.CUSTOM_COMPONENTS}
|
|
668
|
+
})();
|
|
669
|
+
`;
|
|
670
|
+
}
|
|
671
|
+
function buildCdnImports(options) {
|
|
672
|
+
const parts = [];
|
|
673
|
+
if (options.cdnType === "esm") {
|
|
674
|
+
if (options.includeMarkdown) {
|
|
675
|
+
parts.push(`
|
|
676
|
+
<script type="module">
|
|
677
|
+
import ReactMarkdown from '${UNIVERSAL_CDN.esm.reactMarkdown}';
|
|
678
|
+
window.ReactMarkdown = ReactMarkdown;
|
|
679
|
+
</script>`);
|
|
680
|
+
}
|
|
681
|
+
if (options.includeMdx) {
|
|
682
|
+
parts.push(`
|
|
683
|
+
<script type="module">
|
|
684
|
+
import { MDXProvider } from '${UNIVERSAL_CDN.esm.mdxReact}';
|
|
685
|
+
window.MDXProvider = MDXProvider;
|
|
686
|
+
</script>`);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
return parts.join("\n");
|
|
690
|
+
}
|
|
691
|
+
function getCachedRuntime(options, config = {}) {
|
|
692
|
+
const cacheKey = generateCacheKey(options);
|
|
693
|
+
const cacheConfig = { ...DEFAULT_CACHE_CONFIG, ...config };
|
|
694
|
+
const cached = runtimeCache.get(cacheKey);
|
|
695
|
+
if (cached) {
|
|
696
|
+
if (cacheConfig.ttl === 0 || Date.now() - cached.cachedAt < cacheConfig.ttl) {
|
|
697
|
+
return {
|
|
698
|
+
vendorScript: cached.script,
|
|
699
|
+
appTemplate: buildAppTemplate(),
|
|
700
|
+
cdnImports: cached.cdnImports,
|
|
701
|
+
vendorSize: cached.size,
|
|
702
|
+
cached: true,
|
|
703
|
+
cacheKey
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
runtimeCache.delete(cacheKey);
|
|
707
|
+
}
|
|
708
|
+
const vendorParts = [];
|
|
709
|
+
if (options.includeBridge) {
|
|
710
|
+
vendorParts.push(getMCPBridgeScript());
|
|
711
|
+
}
|
|
712
|
+
vendorParts.push(buildStoreRuntime(), buildRequireShim());
|
|
713
|
+
if (options.cdnType === "umd" || options.includeMarkdown) {
|
|
714
|
+
vendorParts.push(buildInlineMarkdownParser(options));
|
|
715
|
+
}
|
|
716
|
+
vendorParts.push(buildRenderersRuntime(options));
|
|
717
|
+
vendorParts.push(buildUIComponentsRuntime());
|
|
718
|
+
vendorParts.push(buildUniversalAppRuntime());
|
|
719
|
+
let vendorScript = vendorParts.join("\n");
|
|
720
|
+
if (options.minify) {
|
|
721
|
+
vendorScript = minifyScript(vendorScript);
|
|
722
|
+
}
|
|
723
|
+
const cdnImports = buildCdnImports(options);
|
|
724
|
+
const entry = {
|
|
725
|
+
script: vendorScript,
|
|
726
|
+
cdnImports,
|
|
727
|
+
size: vendorScript.length,
|
|
728
|
+
cacheKey,
|
|
729
|
+
cachedAt: Date.now()
|
|
730
|
+
};
|
|
731
|
+
if (runtimeCache.size >= cacheConfig.maxEntries) {
|
|
732
|
+
const oldestKey = runtimeCache.keys().next().value;
|
|
733
|
+
if (oldestKey) {
|
|
734
|
+
runtimeCache.delete(oldestKey);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
runtimeCache.set(cacheKey, entry);
|
|
738
|
+
return {
|
|
739
|
+
vendorScript,
|
|
740
|
+
appTemplate: buildAppTemplate(),
|
|
741
|
+
cdnImports,
|
|
742
|
+
vendorSize: vendorScript.length,
|
|
743
|
+
cached: false,
|
|
744
|
+
cacheKey
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
function buildAppTemplate() {
|
|
748
|
+
return [buildCustomComponentsWrapper(), buildComponentWrapper(), buildDataInjectionWrapper()].join("\n");
|
|
749
|
+
}
|
|
750
|
+
function minifyScript(script) {
|
|
751
|
+
return script.replace(/\/\*[\s\S]*?\*\//g, "").replace(/^\s*\/\/[^\n]*$/gm, "").replace(/\n\s*\n/g, "\n").replace(/^\s+/gm, "").trim();
|
|
752
|
+
}
|
|
753
|
+
function buildAppScript(appTemplate, componentCode, dataInjection, customComponents = "") {
|
|
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);
|
|
755
|
+
}
|
|
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) {
|
|
796
|
+
const safeJson = (value) => {
|
|
797
|
+
try {
|
|
798
|
+
return JSON.stringify(value);
|
|
799
|
+
} catch {
|
|
800
|
+
return "null";
|
|
801
|
+
}
|
|
802
|
+
};
|
|
803
|
+
if (hasComponent) {
|
|
804
|
+
return `
|
|
805
|
+
// Static Mode - Data baked at build time
|
|
806
|
+
window.__frontmcp.setState({
|
|
807
|
+
toolName: ${safeJson(toolName)},
|
|
808
|
+
input: ${safeJson(input ?? null)},
|
|
809
|
+
output: ${safeJson(output ?? null)},
|
|
810
|
+
structuredContent: ${safeJson(structuredContent ?? null)},
|
|
811
|
+
content: {
|
|
812
|
+
type: 'react',
|
|
813
|
+
source: window.__frontmcp_component
|
|
814
|
+
},
|
|
815
|
+
loading: false,
|
|
816
|
+
error: null
|
|
817
|
+
});`;
|
|
818
|
+
}
|
|
819
|
+
return `
|
|
820
|
+
// Static Mode - Data baked at build time
|
|
821
|
+
window.__frontmcp.setState({
|
|
822
|
+
toolName: ${safeJson(toolName)},
|
|
823
|
+
input: ${safeJson(input ?? null)},
|
|
824
|
+
output: ${safeJson(output ?? null)},
|
|
825
|
+
structuredContent: ${safeJson(structuredContent ?? null)},
|
|
826
|
+
content: {
|
|
827
|
+
type: ${safeJson(contentType)},
|
|
828
|
+
source: ${safeJson(source)}
|
|
829
|
+
},
|
|
830
|
+
loading: false,
|
|
831
|
+
error: null
|
|
832
|
+
});`;
|
|
833
|
+
}
|
|
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)} }`;
|
|
868
|
+
return `
|
|
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};
|
|
877
|
+
|
|
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
|
+
}
|
|
887
|
+
|
|
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
|
+
}
|
|
892
|
+
|
|
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.';
|
|
896
|
+
}
|
|
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) => {
|
|
910
|
+
try {
|
|
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"
|
|
1429
|
+
},
|
|
1430
|
+
sourceMaps: opts.sourcemap ? true : false
|
|
1431
|
+
});
|
|
1432
|
+
return { code: result.code, map: result.map };
|
|
1433
|
+
};
|
|
1434
|
+
return esbuildTransform;
|
|
1435
|
+
} catch {
|
|
1436
|
+
console.warn(
|
|
1437
|
+
"[@frontmcp/ui/bundler] Neither esbuild nor @swc/core available. Install esbuild for best performance: npm install esbuild"
|
|
1438
|
+
);
|
|
1439
|
+
return null;
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
function sanitizeRootId(rootId) {
|
|
1444
|
+
const safeId = rootId.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
1445
|
+
if (safeId !== rootId) {
|
|
1446
|
+
console.warn("[FrontMCP] rootId sanitized:", { original: rootId, sanitized: safeId });
|
|
1447
|
+
}
|
|
1448
|
+
return safeId || "frontmcp-root";
|
|
1449
|
+
}
|
|
1450
|
+
function sanitizeCss(css) {
|
|
1451
|
+
return css.replace(/<\/style>/gi, "\\3c/style\\3e");
|
|
1452
|
+
}
|
|
1453
|
+
var InMemoryBundler = class {
|
|
1454
|
+
cache;
|
|
1455
|
+
options;
|
|
1456
|
+
defaultSecurity;
|
|
1457
|
+
constructor(options = {}) {
|
|
1458
|
+
this.options = {
|
|
1459
|
+
...DEFAULT_BUNDLER_OPTIONS,
|
|
1460
|
+
...options,
|
|
1461
|
+
cache: {
|
|
1462
|
+
...DEFAULT_BUNDLER_OPTIONS.cache,
|
|
1463
|
+
...options.cache
|
|
1464
|
+
}
|
|
1465
|
+
};
|
|
1466
|
+
this.cache = new BundlerCache({
|
|
1467
|
+
maxSize: this.options.cache.maxSize,
|
|
1468
|
+
ttl: this.options.cache.ttl
|
|
1469
|
+
});
|
|
1470
|
+
this.defaultSecurity = mergePolicy(options.defaultSecurity);
|
|
1471
|
+
}
|
|
1472
|
+
/**
|
|
1473
|
+
* Bundle source code.
|
|
1474
|
+
*
|
|
1475
|
+
* @param options - Bundle options
|
|
1476
|
+
* @returns Bundle result
|
|
1477
|
+
*/
|
|
1478
|
+
async bundle(options) {
|
|
1479
|
+
const startTime = performance.now();
|
|
1480
|
+
const opts = this.mergeOptions(options);
|
|
1481
|
+
if (!opts.skipCache && !this.options.cache.disabled) {
|
|
1482
|
+
const cacheKey = options.cacheKey ?? createCacheKey(options.source, opts);
|
|
1483
|
+
const cached = this.cache.get(cacheKey);
|
|
1484
|
+
if (cached) {
|
|
1485
|
+
return {
|
|
1486
|
+
...cached,
|
|
1487
|
+
cached: true,
|
|
1488
|
+
metrics: {
|
|
1489
|
+
...cached.metrics,
|
|
1490
|
+
cacheTime: performance.now() - startTime
|
|
1491
|
+
}
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
const security = mergePolicy(options.security ?? this.defaultSecurity);
|
|
1496
|
+
const violations = validateSource(options.source, security);
|
|
1497
|
+
throwOnViolations(violations);
|
|
1498
|
+
const sourceType = opts.sourceType === "auto" ? this.detectSourceType(options.source) : opts.sourceType;
|
|
1499
|
+
const transformStart = performance.now();
|
|
1500
|
+
const transformed = await this.transform(options.source, sourceType, opts);
|
|
1501
|
+
const transformTime = performance.now() - transformStart;
|
|
1502
|
+
const sizeViolation = validateSize(transformed.code.length, security);
|
|
1503
|
+
if (sizeViolation) {
|
|
1504
|
+
throwOnViolations([sizeViolation]);
|
|
1505
|
+
}
|
|
1506
|
+
const hash = hashContent(transformed.code);
|
|
1507
|
+
const result = {
|
|
1508
|
+
code: transformed.code,
|
|
1509
|
+
hash,
|
|
1510
|
+
cached: false,
|
|
1511
|
+
size: transformed.code.length,
|
|
1512
|
+
map: transformed.map,
|
|
1513
|
+
metrics: {
|
|
1514
|
+
transformTime,
|
|
1515
|
+
bundleTime: 0,
|
|
1516
|
+
// No separate bundle step for transform-only
|
|
1517
|
+
totalTime: performance.now() - startTime
|
|
1518
|
+
},
|
|
1519
|
+
sourceType,
|
|
1520
|
+
format: opts.format
|
|
1521
|
+
};
|
|
1522
|
+
if (!this.options.cache.disabled) {
|
|
1523
|
+
const cacheKey = options.cacheKey ?? createCacheKey(options.source, opts);
|
|
1524
|
+
this.cache.set(cacheKey, result);
|
|
1525
|
+
}
|
|
1526
|
+
return result;
|
|
1527
|
+
}
|
|
1528
|
+
/**
|
|
1529
|
+
* Bundle and execute for SSR.
|
|
1530
|
+
*
|
|
1531
|
+
* @param options - SSR bundle options
|
|
1532
|
+
* @returns SSR result with rendered HTML
|
|
1533
|
+
*/
|
|
1534
|
+
async bundleSSR(options) {
|
|
1535
|
+
const startTime = performance.now();
|
|
1536
|
+
const bundleResult = await this.bundle({
|
|
1537
|
+
...options,
|
|
1538
|
+
format: "cjs"
|
|
1539
|
+
// CommonJS for execution
|
|
1540
|
+
});
|
|
1541
|
+
let React;
|
|
1542
|
+
let ReactDOMServer;
|
|
1543
|
+
try {
|
|
1544
|
+
React = await import("react");
|
|
1545
|
+
ReactDOMServer = await import("react-dom/server");
|
|
1546
|
+
} catch {
|
|
1547
|
+
throw new Error("React and react-dom/server are required for SSR. Install them: npm install react react-dom");
|
|
1548
|
+
}
|
|
1549
|
+
const renderStart = performance.now();
|
|
1550
|
+
const Component = await executeDefault(bundleResult.code, {
|
|
1551
|
+
React,
|
|
1552
|
+
security: mergePolicy(options.security ?? this.defaultSecurity)
|
|
1553
|
+
});
|
|
1554
|
+
let html;
|
|
1555
|
+
try {
|
|
1556
|
+
const element = React.createElement(Component, options.context ?? {});
|
|
1557
|
+
html = ReactDOMServer.renderToString(element);
|
|
1558
|
+
} catch (error) {
|
|
1559
|
+
throw new ExecutionError(
|
|
1560
|
+
`SSR rendering failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
1561
|
+
error
|
|
1562
|
+
);
|
|
1563
|
+
}
|
|
1564
|
+
const renderTime = performance.now() - renderStart;
|
|
1565
|
+
let hydrationScript;
|
|
1566
|
+
if (options.includeHydration) {
|
|
1567
|
+
hydrationScript = this.buildHydrationScript(bundleResult.code, options.context);
|
|
1568
|
+
}
|
|
1569
|
+
return {
|
|
1570
|
+
...bundleResult,
|
|
1571
|
+
html,
|
|
1572
|
+
hydrationScript,
|
|
1573
|
+
metrics: {
|
|
1574
|
+
...bundleResult.metrics,
|
|
1575
|
+
totalTime: performance.now() - startTime
|
|
1576
|
+
},
|
|
1577
|
+
ssrMetrics: {
|
|
1578
|
+
renderTime
|
|
1579
|
+
}
|
|
1580
|
+
};
|
|
1581
|
+
}
|
|
1582
|
+
/**
|
|
1583
|
+
* Bundle and execute code, returning the exports.
|
|
1584
|
+
*
|
|
1585
|
+
* @param options - Bundle options
|
|
1586
|
+
* @param context - Execution context
|
|
1587
|
+
* @returns Exported value
|
|
1588
|
+
*/
|
|
1589
|
+
async bundleAndExecute(options, context) {
|
|
1590
|
+
const result = await this.bundle({
|
|
1591
|
+
...options,
|
|
1592
|
+
format: "cjs"
|
|
1593
|
+
// CommonJS for execution
|
|
1594
|
+
});
|
|
1595
|
+
let React;
|
|
1596
|
+
try {
|
|
1597
|
+
React = await import("react");
|
|
1598
|
+
} catch {
|
|
1599
|
+
}
|
|
1600
|
+
return executeDefault(result.code, {
|
|
1601
|
+
React,
|
|
1602
|
+
globals: context,
|
|
1603
|
+
security: mergePolicy(options.security ?? this.defaultSecurity)
|
|
1604
|
+
});
|
|
1605
|
+
}
|
|
1606
|
+
/**
|
|
1607
|
+
* Bundle a component to a self-contained static HTML document.
|
|
1608
|
+
*
|
|
1609
|
+
* Creates a complete HTML page with:
|
|
1610
|
+
* - React runtime (CDN or inline based on platform)
|
|
1611
|
+
* - FrontMCP UI hooks and components (always inline)
|
|
1612
|
+
* - Tool data injection (input/output)
|
|
1613
|
+
* - Transpiled component code
|
|
1614
|
+
* - Client-side rendering via createRoot
|
|
1615
|
+
*
|
|
1616
|
+
* @param options - Static HTML options
|
|
1617
|
+
* @returns Static HTML result with complete document
|
|
1618
|
+
*
|
|
1619
|
+
* @example
|
|
1620
|
+
* ```typescript
|
|
1621
|
+
* const result = await bundler.bundleToStaticHTML({
|
|
1622
|
+
* source: `
|
|
1623
|
+
* import { Card, useToolOutput } from '@frontmcp/ui/react';
|
|
1624
|
+
* export default function Weather() {
|
|
1625
|
+
* const output = useToolOutput();
|
|
1626
|
+
* return <Card title="Weather">{output?.temperature}°F</Card>;
|
|
1627
|
+
* }
|
|
1628
|
+
* `,
|
|
1629
|
+
* toolName: 'get_weather',
|
|
1630
|
+
* output: { temperature: 72 },
|
|
1631
|
+
* });
|
|
1632
|
+
*
|
|
1633
|
+
* // result.html contains the complete HTML document
|
|
1634
|
+
* ```
|
|
1635
|
+
*/
|
|
1636
|
+
async bundleToStaticHTML(options) {
|
|
1637
|
+
const startTime = performance.now();
|
|
1638
|
+
const opts = this.mergeStaticHTMLOptions(options);
|
|
1639
|
+
const platform = opts.targetPlatform === "auto" ? "generic" : opts.targetPlatform;
|
|
1640
|
+
const cdnType = getCdnTypeForPlatform(platform);
|
|
1641
|
+
if (opts.universal) {
|
|
1642
|
+
return this.bundleToStaticHTMLUniversal(options, opts, platform, cdnType, startTime);
|
|
1643
|
+
}
|
|
1644
|
+
const bundleResult = await this.bundle({
|
|
1645
|
+
source: options.source,
|
|
1646
|
+
sourceType: opts.sourceType,
|
|
1647
|
+
format: "cjs",
|
|
1648
|
+
minify: opts.minify,
|
|
1649
|
+
sourceMaps: false,
|
|
1650
|
+
externals: ["react", "react-dom", "react/jsx-runtime", "@frontmcp/ui", "@frontmcp/ui/react"],
|
|
1651
|
+
security: opts.security,
|
|
1652
|
+
skipCache: opts.skipCache
|
|
1653
|
+
});
|
|
1654
|
+
const head = this.buildStaticHTMLHead({ externals: opts.externals, customCss: opts.customCss, theme: opts.theme });
|
|
1655
|
+
const reactRuntime = this.buildReactRuntimeScripts(opts.externals, platform, cdnType);
|
|
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
|
+
);
|
|
1667
|
+
const componentScript = this.buildComponentRenderScript(bundleResult.code, opts.rootId, cdnType);
|
|
1668
|
+
const html = this.assembleStaticHTML({
|
|
1669
|
+
title: opts.title || `${opts.toolName} - Widget`,
|
|
1670
|
+
head,
|
|
1671
|
+
reactRuntime,
|
|
1672
|
+
frontmcpRuntime,
|
|
1673
|
+
dataScript,
|
|
1674
|
+
componentScript,
|
|
1675
|
+
rootId: opts.rootId,
|
|
1676
|
+
cdnType
|
|
1677
|
+
});
|
|
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;
|
|
1681
|
+
return {
|
|
1682
|
+
html,
|
|
1683
|
+
componentCode: bundleResult.code,
|
|
1684
|
+
metrics: {
|
|
1685
|
+
...bundleResult.metrics,
|
|
1686
|
+
totalTime: performance.now() - startTime
|
|
1687
|
+
},
|
|
1688
|
+
hash,
|
|
1689
|
+
size: html.length,
|
|
1690
|
+
cached: bundleResult.cached,
|
|
1691
|
+
sourceType: bundleResult.sourceType,
|
|
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
|
|
1779
|
+
};
|
|
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
|
+
}
|
|
1926
|
+
/**
|
|
1927
|
+
* Bundle to static HTML with universal rendering mode.
|
|
1928
|
+
* Uses the universal renderer that can handle multiple content types.
|
|
1929
|
+
*
|
|
1930
|
+
* Optimization: Uses cached runtime (vendor chunk) to avoid rebuilding
|
|
1931
|
+
* static code on every request. Only the user's component is transpiled.
|
|
1932
|
+
*/
|
|
1933
|
+
async bundleToStaticHTMLUniversal(options, opts, platform, cdnType, startTime) {
|
|
1934
|
+
let contentType;
|
|
1935
|
+
const rawContentType = options.contentType ?? "auto";
|
|
1936
|
+
if (rawContentType === "auto") {
|
|
1937
|
+
contentType = detectContentType(options.source);
|
|
1938
|
+
} else {
|
|
1939
|
+
contentType = rawContentType;
|
|
1940
|
+
}
|
|
1941
|
+
let transpiledCode = null;
|
|
1942
|
+
let transformTime = 0;
|
|
1943
|
+
if (contentType === "react") {
|
|
1944
|
+
const bundleResult = await this.bundle({
|
|
1945
|
+
source: options.source,
|
|
1946
|
+
sourceType: opts.sourceType,
|
|
1947
|
+
format: "cjs",
|
|
1948
|
+
minify: opts.minify,
|
|
1949
|
+
sourceMaps: false,
|
|
1950
|
+
externals: ["react", "react-dom", "react/jsx-runtime", "@frontmcp/ui", "@frontmcp/ui/react"],
|
|
1951
|
+
security: opts.security,
|
|
1952
|
+
skipCache: opts.skipCache
|
|
1953
|
+
});
|
|
1954
|
+
transpiledCode = bundleResult.code;
|
|
1955
|
+
transformTime = bundleResult.metrics.transformTime;
|
|
1956
|
+
}
|
|
1957
|
+
const shouldIncludeBridge = opts.buildMode === "dynamic" || opts.buildMode === "hybrid";
|
|
1958
|
+
const cachedRuntime = getCachedRuntime({
|
|
1959
|
+
cdnType,
|
|
1960
|
+
includeMarkdown: opts.includeMarkdown || contentType === "markdown",
|
|
1961
|
+
includeMdx: opts.includeMdx || contentType === "mdx",
|
|
1962
|
+
minify: opts.minify,
|
|
1963
|
+
includeBridge: shouldIncludeBridge
|
|
1964
|
+
});
|
|
1965
|
+
const componentCodeStr = transpiledCode ? buildComponentCode(transpiledCode) : "";
|
|
1966
|
+
const dataInjectionStr = buildDataInjectionCode(
|
|
1967
|
+
opts.toolName,
|
|
1968
|
+
opts.input,
|
|
1969
|
+
opts.output,
|
|
1970
|
+
opts.structuredContent,
|
|
1971
|
+
contentType,
|
|
1972
|
+
transpiledCode ? null : options.source,
|
|
1973
|
+
// Pass source only if not a component
|
|
1974
|
+
transpiledCode !== null,
|
|
1975
|
+
{
|
|
1976
|
+
buildMode: opts.buildMode,
|
|
1977
|
+
cdnType,
|
|
1978
|
+
dynamicOptions: opts.dynamicOptions,
|
|
1979
|
+
hybridOptions: opts.hybridOptions
|
|
1980
|
+
}
|
|
1981
|
+
);
|
|
1982
|
+
const appScript = buildAppScript(
|
|
1983
|
+
cachedRuntime.appTemplate,
|
|
1984
|
+
componentCodeStr,
|
|
1985
|
+
dataInjectionStr,
|
|
1986
|
+
opts.customComponents ?? ""
|
|
1987
|
+
);
|
|
1988
|
+
const head = this.buildStaticHTMLHead({ externals: opts.externals, customCss: opts.customCss, theme: opts.theme });
|
|
1989
|
+
const reactRuntime = this.buildReactRuntimeScripts(opts.externals, platform, cdnType);
|
|
1990
|
+
const renderScript = this.buildUniversalRenderScript(opts.rootId, cdnType);
|
|
1991
|
+
const html = this.assembleUniversalStaticHTMLCached({
|
|
1992
|
+
title: opts.title || `${opts.toolName} - Widget`,
|
|
1993
|
+
head,
|
|
1994
|
+
reactRuntime,
|
|
1995
|
+
cdnImports: cachedRuntime.cdnImports,
|
|
1996
|
+
vendorScript: cachedRuntime.vendorScript,
|
|
1997
|
+
appScript,
|
|
1998
|
+
renderScript,
|
|
1999
|
+
rootId: opts.rootId,
|
|
2000
|
+
cdnType
|
|
2001
|
+
});
|
|
2002
|
+
const hash = hashContent(html);
|
|
2003
|
+
return {
|
|
2004
|
+
html,
|
|
2005
|
+
componentCode: transpiledCode ?? appScript,
|
|
2006
|
+
metrics: {
|
|
2007
|
+
transformTime,
|
|
2008
|
+
bundleTime: 0,
|
|
2009
|
+
totalTime: performance.now() - startTime,
|
|
2010
|
+
cacheTime: cachedRuntime.cached ? 0 : void 0
|
|
2011
|
+
},
|
|
2012
|
+
hash,
|
|
2013
|
+
size: html.length,
|
|
2014
|
+
cached: cachedRuntime.cached,
|
|
2015
|
+
sourceType: opts.sourceType === "auto" ? this.detectSourceType(options.source) : opts.sourceType,
|
|
2016
|
+
targetPlatform: platform,
|
|
2017
|
+
universal: true,
|
|
2018
|
+
contentType
|
|
2019
|
+
};
|
|
2020
|
+
}
|
|
2021
|
+
/**
|
|
2022
|
+
* Assemble the complete universal static HTML document using cached runtime.
|
|
2023
|
+
*
|
|
2024
|
+
* For ESM mode (OpenAI), scripts must wait for React to load asynchronously.
|
|
2025
|
+
* For UMD mode (Claude), scripts can execute synchronously.
|
|
2026
|
+
*/
|
|
2027
|
+
assembleUniversalStaticHTMLCached(parts) {
|
|
2028
|
+
if (parts.cdnType === "umd") {
|
|
2029
|
+
return `<!DOCTYPE html>
|
|
2030
|
+
<html lang="en">
|
|
2031
|
+
<head>
|
|
2032
|
+
<title>${escapeHtml(parts.title)}</title>
|
|
2033
|
+
${parts.head}
|
|
2034
|
+
${parts.reactRuntime}
|
|
2035
|
+
${parts.cdnImports}
|
|
2036
|
+
<!-- Vendor Runtime (Cached) -->
|
|
2037
|
+
<script>
|
|
2038
|
+
${parts.vendorScript}
|
|
2039
|
+
</script>
|
|
2040
|
+
<!-- App Script (User Component) -->
|
|
2041
|
+
<script>
|
|
2042
|
+
${parts.appScript}
|
|
2043
|
+
</script>
|
|
2044
|
+
</head>
|
|
2045
|
+
<body>
|
|
2046
|
+
<div id="${parts.rootId}" class="frontmcp-loading">
|
|
2047
|
+
<div class="frontmcp-spinner"></div>
|
|
2048
|
+
</div>
|
|
2049
|
+
${parts.renderScript}
|
|
2050
|
+
</body>
|
|
2051
|
+
</html>`;
|
|
2052
|
+
} else {
|
|
2053
|
+
return `<!DOCTYPE html>
|
|
2054
|
+
<html lang="en">
|
|
2055
|
+
<head>
|
|
2056
|
+
<title>${escapeHtml(parts.title)}</title>
|
|
2057
|
+
${parts.head}
|
|
2058
|
+
${parts.reactRuntime}
|
|
2059
|
+
${parts.cdnImports}
|
|
2060
|
+
</head>
|
|
2061
|
+
<body>
|
|
2062
|
+
<div id="${parts.rootId}" class="frontmcp-loading">
|
|
2063
|
+
<div class="frontmcp-spinner"></div>
|
|
2064
|
+
</div>
|
|
2065
|
+
<!-- Scripts wait for React to load (ESM is async) -->
|
|
2066
|
+
<script type="module">
|
|
2067
|
+
// Wait for React to be ready
|
|
2068
|
+
function initFrontMCP() {
|
|
2069
|
+
// Vendor Runtime (Cached)
|
|
2070
|
+
${parts.vendorScript}
|
|
2071
|
+
|
|
2072
|
+
// App Script (User Component)
|
|
2073
|
+
${parts.appScript}
|
|
2074
|
+
|
|
2075
|
+
// Render the app
|
|
2076
|
+
var container = document.getElementById('${parts.rootId}');
|
|
2077
|
+
if (container && window.ReactDOM && window.ReactDOM.createRoot && window.__frontmcp.UniversalApp) {
|
|
2078
|
+
var root = window.ReactDOM.createRoot(container);
|
|
2079
|
+
root.render(React.createElement(window.__frontmcp.UniversalApp));
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
|
|
2083
|
+
if (window.__reactReady) {
|
|
2084
|
+
initFrontMCP();
|
|
2085
|
+
} else {
|
|
2086
|
+
window.addEventListener('react:ready', initFrontMCP);
|
|
2087
|
+
}
|
|
2088
|
+
</script>
|
|
2089
|
+
</body>
|
|
2090
|
+
</html>`;
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
/**
|
|
2094
|
+
* Build the component script for transpiled React/MDX content.
|
|
2095
|
+
* Wraps CommonJS code with module/exports shim to capture the component.
|
|
2096
|
+
*/
|
|
2097
|
+
buildUniversalComponentScript(transpiledCode, cdnType) {
|
|
2098
|
+
const wrappedCode = `
|
|
2099
|
+
// CommonJS module shim
|
|
2100
|
+
var module = { exports: {} };
|
|
2101
|
+
var exports = module.exports;
|
|
2102
|
+
|
|
2103
|
+
// Execute transpiled component code (CommonJS format)
|
|
2104
|
+
${transpiledCode}
|
|
2105
|
+
|
|
2106
|
+
// Capture the component export
|
|
2107
|
+
window.__frontmcp_component = module.exports.default || module.exports;
|
|
2108
|
+
`;
|
|
2109
|
+
if (cdnType === "umd") {
|
|
2110
|
+
return `
|
|
2111
|
+
<!-- Universal Component Script (transpiled) -->
|
|
2112
|
+
<script>
|
|
2113
|
+
(function() {
|
|
2114
|
+
${wrappedCode}
|
|
2115
|
+
})();
|
|
2116
|
+
</script>`;
|
|
2117
|
+
} else {
|
|
2118
|
+
return `
|
|
2119
|
+
<!-- Universal Component Script (transpiled, ESM) -->
|
|
2120
|
+
<script type="module">
|
|
2121
|
+
function loadComponent() {
|
|
2122
|
+
${wrappedCode}
|
|
2123
|
+
}
|
|
2124
|
+
|
|
2125
|
+
if (window.__reactReady) {
|
|
2126
|
+
loadComponent();
|
|
2127
|
+
} else {
|
|
2128
|
+
window.addEventListener('react:ready', loadComponent);
|
|
2129
|
+
}
|
|
2130
|
+
</script>`;
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
/**
|
|
2134
|
+
* Build the universal runtime script section.
|
|
2135
|
+
*/
|
|
2136
|
+
buildUniversalRuntimeScript(runtimeScript) {
|
|
2137
|
+
return `
|
|
2138
|
+
<!-- Universal Runtime -->
|
|
2139
|
+
<script>
|
|
2140
|
+
${runtimeScript}
|
|
2141
|
+
</script>`;
|
|
2142
|
+
}
|
|
2143
|
+
/**
|
|
2144
|
+
* Build data injection script for universal mode.
|
|
2145
|
+
*/
|
|
2146
|
+
buildUniversalDataScript(toolName, input, output, structuredContent, contentType, source, hasTranspiledComponent = false) {
|
|
2147
|
+
const safeJson = (value) => {
|
|
2148
|
+
try {
|
|
2149
|
+
return JSON.stringify(value);
|
|
2150
|
+
} catch {
|
|
2151
|
+
return "null";
|
|
2152
|
+
}
|
|
2153
|
+
};
|
|
2154
|
+
if (hasTranspiledComponent) {
|
|
2155
|
+
return `
|
|
2156
|
+
<!-- Universal Data Injection (React Component) -->
|
|
2157
|
+
<script>
|
|
2158
|
+
window.__frontmcp.setState({
|
|
2159
|
+
toolName: ${safeJson(toolName)},
|
|
2160
|
+
input: ${safeJson(input ?? null)},
|
|
2161
|
+
output: ${safeJson(output ?? null)},
|
|
2162
|
+
structuredContent: ${safeJson(structuredContent ?? null)},
|
|
2163
|
+
content: {
|
|
2164
|
+
type: 'react',
|
|
2165
|
+
source: window.__frontmcp_component
|
|
2166
|
+
},
|
|
2167
|
+
loading: false,
|
|
2168
|
+
error: null
|
|
2169
|
+
});
|
|
2170
|
+
</script>`;
|
|
2171
|
+
}
|
|
2172
|
+
const escapedSource = JSON.stringify(source);
|
|
2173
|
+
return `
|
|
2174
|
+
<!-- Universal Data Injection -->
|
|
2175
|
+
<script>
|
|
2176
|
+
window.__frontmcp.setState({
|
|
2177
|
+
toolName: ${safeJson(toolName)},
|
|
2178
|
+
input: ${safeJson(input ?? null)},
|
|
2179
|
+
output: ${safeJson(output ?? null)},
|
|
2180
|
+
structuredContent: ${safeJson(structuredContent ?? null)},
|
|
2181
|
+
content: {
|
|
2182
|
+
type: ${safeJson(contentType)},
|
|
2183
|
+
source: ${escapedSource}
|
|
2184
|
+
},
|
|
2185
|
+
loading: false,
|
|
2186
|
+
error: null
|
|
2187
|
+
});
|
|
2188
|
+
</script>`;
|
|
2189
|
+
}
|
|
2190
|
+
/**
|
|
2191
|
+
* Build the universal render script.
|
|
2192
|
+
*/
|
|
2193
|
+
buildUniversalRenderScript(rootId, cdnType) {
|
|
2194
|
+
if (cdnType === "umd") {
|
|
2195
|
+
return `
|
|
2196
|
+
<!-- Universal Render Script (UMD - synchronous) -->
|
|
2197
|
+
<script>
|
|
2198
|
+
(function() {
|
|
2199
|
+
var container = document.getElementById('${rootId}');
|
|
2200
|
+
if (container && window.ReactDOM && window.ReactDOM.createRoot && window.__frontmcp.UniversalApp) {
|
|
2201
|
+
var root = window.ReactDOM.createRoot(container);
|
|
2202
|
+
root.render(React.createElement(window.__frontmcp.UniversalApp));
|
|
2203
|
+
} else if (container && window.ReactDOM && window.ReactDOM.render && window.__frontmcp.UniversalApp) {
|
|
2204
|
+
window.ReactDOM.render(
|
|
2205
|
+
React.createElement(window.__frontmcp.UniversalApp),
|
|
2206
|
+
container
|
|
2207
|
+
);
|
|
2208
|
+
}
|
|
2209
|
+
})();
|
|
2210
|
+
</script>`;
|
|
2211
|
+
} else {
|
|
2212
|
+
return `
|
|
2213
|
+
<!-- Universal Render Script (ESM - waits for React) -->
|
|
2214
|
+
<script type="module">
|
|
2215
|
+
function renderUniversalApp() {
|
|
2216
|
+
var container = document.getElementById('${rootId}');
|
|
2217
|
+
if (container && window.ReactDOM && window.ReactDOM.createRoot && window.__frontmcp.UniversalApp) {
|
|
2218
|
+
var root = window.ReactDOM.createRoot(container);
|
|
2219
|
+
root.render(React.createElement(window.__frontmcp.UniversalApp));
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
|
|
2223
|
+
if (window.__reactReady) {
|
|
2224
|
+
renderUniversalApp();
|
|
2225
|
+
} else {
|
|
2226
|
+
window.addEventListener('react:ready', renderUniversalApp);
|
|
2227
|
+
}
|
|
2228
|
+
</script>`;
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
/**
|
|
2232
|
+
* Assemble the complete universal static HTML document.
|
|
2233
|
+
*/
|
|
2234
|
+
assembleUniversalStaticHTML(parts) {
|
|
2235
|
+
return `<!DOCTYPE html>
|
|
2236
|
+
<html lang="en">
|
|
2237
|
+
<head>
|
|
2238
|
+
<title>${escapeHtml(parts.title)}</title>
|
|
2239
|
+
${parts.head}
|
|
2240
|
+
${parts.reactRuntime}
|
|
2241
|
+
${parts.frontmcpRuntime ?? ""}
|
|
2242
|
+
${parts.cdnImports}
|
|
2243
|
+
${parts.universalRuntimeScript}
|
|
2244
|
+
${parts.componentScript ?? ""}
|
|
2245
|
+
${parts.dataScript}
|
|
2246
|
+
</head>
|
|
2247
|
+
<body>
|
|
2248
|
+
<div id="${parts.rootId}" class="frontmcp-loading">
|
|
2249
|
+
<div class="frontmcp-spinner"></div>
|
|
2250
|
+
</div>
|
|
2251
|
+
${parts.renderScript}
|
|
2252
|
+
</body>
|
|
2253
|
+
</html>`;
|
|
2254
|
+
}
|
|
2255
|
+
/**
|
|
2256
|
+
* Get cache statistics.
|
|
2257
|
+
*/
|
|
2258
|
+
getCacheStats() {
|
|
2259
|
+
return this.cache.getStats();
|
|
2260
|
+
}
|
|
2261
|
+
/**
|
|
2262
|
+
* Clear the cache.
|
|
2263
|
+
*/
|
|
2264
|
+
clearCache() {
|
|
2265
|
+
this.cache.clear();
|
|
2266
|
+
}
|
|
2267
|
+
/**
|
|
2268
|
+
* Clean up expired cache entries.
|
|
2269
|
+
*/
|
|
2270
|
+
cleanupCache() {
|
|
2271
|
+
return this.cache.cleanup();
|
|
2272
|
+
}
|
|
2273
|
+
/**
|
|
2274
|
+
* Transform source code using esbuild/SWC.
|
|
2275
|
+
*/
|
|
2276
|
+
async transform(source, sourceType, options) {
|
|
2277
|
+
const transform = await loadEsbuild();
|
|
2278
|
+
if (!transform) {
|
|
2279
|
+
throw new Error("No bundler available. Install esbuild or @swc/core: npm install esbuild");
|
|
2280
|
+
}
|
|
2281
|
+
const loader = this.getLoader(sourceType);
|
|
2282
|
+
const esbuildOptions = {
|
|
2283
|
+
loader,
|
|
2284
|
+
minify: options.minify,
|
|
2285
|
+
sourcemap: options.sourceMaps,
|
|
2286
|
+
target: options.target,
|
|
2287
|
+
format: options.format === "cjs" ? "cjs" : options.format === "esm" ? "esm" : "iife",
|
|
2288
|
+
jsx: "automatic",
|
|
2289
|
+
jsxImportSource: options.jsx.importSource
|
|
2290
|
+
};
|
|
2291
|
+
try {
|
|
2292
|
+
const result = await transform(source, esbuildOptions);
|
|
2293
|
+
return {
|
|
2294
|
+
code: result.code,
|
|
2295
|
+
map: result.map
|
|
2296
|
+
};
|
|
2297
|
+
} catch (error) {
|
|
2298
|
+
throw new Error(`Transform failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
/**
|
|
2302
|
+
* Get esbuild loader for source type.
|
|
2303
|
+
*/
|
|
2304
|
+
getLoader(sourceType) {
|
|
2305
|
+
switch (sourceType) {
|
|
2306
|
+
case "jsx":
|
|
2307
|
+
return "jsx";
|
|
2308
|
+
case "tsx":
|
|
2309
|
+
return "tsx";
|
|
2310
|
+
case "mdx":
|
|
2311
|
+
return "tsx";
|
|
2312
|
+
// MDX compiles to JSX/TSX
|
|
2313
|
+
case "html":
|
|
2314
|
+
return "text";
|
|
2315
|
+
default:
|
|
2316
|
+
return "tsx";
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
/**
|
|
2320
|
+
* Detect source type from content.
|
|
2321
|
+
*/
|
|
2322
|
+
detectSourceType(source) {
|
|
2323
|
+
const hasTypeScript = /:\s*(string|number|boolean|any|unknown|void|never|object)\b/.test(source) || /interface\s+\w+/.test(source) || /type\s+\w+\s*=/.test(source) || /<\w+>/.test(source);
|
|
2324
|
+
const hasJSX = /<[A-Z][a-zA-Z]*/.test(source) || // Component tags
|
|
2325
|
+
/<[a-z]+\s/.test(source) || // HTML tags with attributes
|
|
2326
|
+
/<[a-z]+>/.test(source) || // Self-closing HTML tags
|
|
2327
|
+
/<\/[a-z]+>/.test(source);
|
|
2328
|
+
const hasMDX = /^#\s+/.test(source) || // Markdown heading
|
|
2329
|
+
/^-\s+/.test(source) || // Markdown list
|
|
2330
|
+
/\*\*\w+\*\*/.test(source) || // Bold
|
|
2331
|
+
/```\w*\n/.test(source);
|
|
2332
|
+
if (hasMDX && hasJSX) {
|
|
2333
|
+
return "mdx";
|
|
2334
|
+
}
|
|
2335
|
+
if (hasTypeScript && hasJSX) {
|
|
2336
|
+
return "tsx";
|
|
2337
|
+
}
|
|
2338
|
+
if (hasJSX) {
|
|
2339
|
+
return "jsx";
|
|
2340
|
+
}
|
|
2341
|
+
if (hasTypeScript) {
|
|
2342
|
+
return "tsx";
|
|
2343
|
+
}
|
|
2344
|
+
return "jsx";
|
|
2345
|
+
}
|
|
2346
|
+
/**
|
|
2347
|
+
* Merge bundle options with defaults.
|
|
2348
|
+
*/
|
|
2349
|
+
mergeOptions(options) {
|
|
2350
|
+
return {
|
|
2351
|
+
sourceType: options.sourceType ?? DEFAULT_BUNDLE_OPTIONS.sourceType,
|
|
2352
|
+
format: options.format ?? DEFAULT_BUNDLE_OPTIONS.format,
|
|
2353
|
+
minify: options.minify ?? DEFAULT_BUNDLE_OPTIONS.minify,
|
|
2354
|
+
sourceMaps: options.sourceMaps ?? DEFAULT_BUNDLE_OPTIONS.sourceMaps,
|
|
2355
|
+
externals: options.externals ?? DEFAULT_BUNDLE_OPTIONS.externals,
|
|
2356
|
+
jsx: {
|
|
2357
|
+
...DEFAULT_BUNDLE_OPTIONS.jsx,
|
|
2358
|
+
...options.jsx
|
|
2359
|
+
},
|
|
2360
|
+
target: options.target ?? DEFAULT_BUNDLE_OPTIONS.target,
|
|
2361
|
+
globalName: options.globalName ?? DEFAULT_BUNDLE_OPTIONS.globalName,
|
|
2362
|
+
skipCache: options.skipCache ?? DEFAULT_BUNDLE_OPTIONS.skipCache
|
|
2363
|
+
};
|
|
2364
|
+
}
|
|
2365
|
+
/**
|
|
2366
|
+
* Build hydration script for client-side React.
|
|
2367
|
+
*/
|
|
2368
|
+
buildHydrationScript(bundledCode, context) {
|
|
2369
|
+
const contextJson = context ? JSON.stringify(context) : "{}";
|
|
2370
|
+
return `
|
|
2371
|
+
(function() {
|
|
2372
|
+
var context = ${contextJson};
|
|
2373
|
+
var exports = {};
|
|
2374
|
+
var module = { exports: exports };
|
|
2375
|
+
|
|
2376
|
+
// Execute bundled code
|
|
2377
|
+
(function(exports, module) {
|
|
2378
|
+
${bundledCode}
|
|
2379
|
+
})(exports, module);
|
|
2380
|
+
|
|
2381
|
+
// Get component
|
|
2382
|
+
var Component = module.exports.default || module.exports;
|
|
2383
|
+
|
|
2384
|
+
// Hydrate
|
|
2385
|
+
if (typeof ReactDOM !== 'undefined' && ReactDOM.hydrateRoot) {
|
|
2386
|
+
var container = document.getElementById('root') || document.body.firstElementChild;
|
|
2387
|
+
if (container) {
|
|
2388
|
+
ReactDOM.hydrateRoot(container, React.createElement(Component, context));
|
|
2389
|
+
}
|
|
2390
|
+
}
|
|
2391
|
+
})();
|
|
2392
|
+
`.trim();
|
|
2393
|
+
}
|
|
2394
|
+
// ============================================
|
|
2395
|
+
// Static HTML Helper Methods
|
|
2396
|
+
// ============================================
|
|
2397
|
+
/**
|
|
2398
|
+
* Merge static HTML options with defaults.
|
|
2399
|
+
*/
|
|
2400
|
+
mergeStaticHTMLOptions(options) {
|
|
2401
|
+
return {
|
|
2402
|
+
sourceType: options.sourceType ?? DEFAULT_STATIC_HTML_OPTIONS.sourceType,
|
|
2403
|
+
targetPlatform: options.targetPlatform ?? DEFAULT_STATIC_HTML_OPTIONS.targetPlatform,
|
|
2404
|
+
minify: options.minify ?? DEFAULT_STATIC_HTML_OPTIONS.minify,
|
|
2405
|
+
skipCache: options.skipCache ?? DEFAULT_STATIC_HTML_OPTIONS.skipCache,
|
|
2406
|
+
rootId: sanitizeRootId(options.rootId ?? DEFAULT_STATIC_HTML_OPTIONS.rootId),
|
|
2407
|
+
widgetAccessible: options.widgetAccessible ?? DEFAULT_STATIC_HTML_OPTIONS.widgetAccessible,
|
|
2408
|
+
externals: {
|
|
2409
|
+
...DEFAULT_STATIC_HTML_OPTIONS.externals,
|
|
2410
|
+
...options.externals
|
|
2411
|
+
},
|
|
2412
|
+
// Universal mode options
|
|
2413
|
+
universal: options.universal ?? DEFAULT_STATIC_HTML_OPTIONS.universal,
|
|
2414
|
+
contentType: options.contentType ?? DEFAULT_STATIC_HTML_OPTIONS.contentType,
|
|
2415
|
+
includeMarkdown: options.includeMarkdown ?? DEFAULT_STATIC_HTML_OPTIONS.includeMarkdown,
|
|
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,
|
|
2421
|
+
// Pass-through options
|
|
2422
|
+
toolName: options.toolName,
|
|
2423
|
+
input: options.input,
|
|
2424
|
+
output: options.output,
|
|
2425
|
+
structuredContent: options.structuredContent,
|
|
2426
|
+
title: options.title,
|
|
2427
|
+
security: options.security,
|
|
2428
|
+
customCss: options.customCss,
|
|
2429
|
+
customComponents: options.customComponents,
|
|
2430
|
+
theme: options.theme
|
|
2431
|
+
};
|
|
2432
|
+
}
|
|
2433
|
+
/**
|
|
2434
|
+
* Build the <head> section for static HTML.
|
|
2435
|
+
*/
|
|
2436
|
+
buildStaticHTMLHead(opts) {
|
|
2437
|
+
const parts = [];
|
|
2438
|
+
parts.push(`<meta charset="UTF-8">`);
|
|
2439
|
+
parts.push(`<meta name="viewport" content="width=device-width, initial-scale=1.0">`);
|
|
2440
|
+
for (const url of STATIC_HTML_CDN.fonts.preconnect) {
|
|
2441
|
+
parts.push(`<link rel="preconnect" href="${url}" crossorigin>`);
|
|
2442
|
+
}
|
|
2443
|
+
parts.push(`<link rel="stylesheet" href="${STATIC_HTML_CDN.fonts.inter}">`);
|
|
2444
|
+
parts.push(buildCDNScriptTag(CLOUDFLARE_CDN.tailwindCss));
|
|
2445
|
+
parts.push(this.buildThemeStyleBlock(opts.theme));
|
|
2446
|
+
parts.push(`<style>
|
|
2447
|
+
body { margin: 0; font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
|
2448
|
+
.frontmcp-loading { display: flex; align-items: center; justify-content: center; min-height: 200px; }
|
|
2449
|
+
.frontmcp-spinner { width: 24px; height: 24px; border: 2px solid #e5e7eb; border-top-color: #3b82f6; border-radius: 50%; animation: spin 1s linear infinite; }
|
|
2450
|
+
@keyframes spin { to { transform: rotate(360deg); } }
|
|
2451
|
+
</style>`);
|
|
2452
|
+
if (opts.customCss) {
|
|
2453
|
+
parts.push(`<style>
|
|
2454
|
+
${sanitizeCss(opts.customCss)}
|
|
2455
|
+
</style>`);
|
|
2456
|
+
}
|
|
2457
|
+
return parts.join("\n ");
|
|
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
|
+
}
|
|
2471
|
+
/**
|
|
2472
|
+
* Build React runtime scripts for static HTML.
|
|
2473
|
+
*/
|
|
2474
|
+
buildReactRuntimeScripts(externals, platform, cdnType) {
|
|
2475
|
+
const reactConfig = externals.react ?? "cdn";
|
|
2476
|
+
const reactDomConfig = externals.reactDom ?? "cdn";
|
|
2477
|
+
if (cdnType === "umd") {
|
|
2478
|
+
const reactUrl = reactConfig === "cdn" ? STATIC_HTML_CDN.umd.react : reactConfig;
|
|
2479
|
+
const reactDomUrl = reactDomConfig === "cdn" ? STATIC_HTML_CDN.umd.reactDom : reactDomConfig;
|
|
2480
|
+
return `
|
|
2481
|
+
<!-- React Runtime (UMD from cdnjs - Claude compatible) -->
|
|
2482
|
+
<script src="${reactUrl}"></script>
|
|
2483
|
+
<script src="${reactDomUrl}"></script>
|
|
2484
|
+
<script>
|
|
2485
|
+
// Webpack/esbuild polyfills for transpiled code (UMD globals)
|
|
2486
|
+
window.external_react_namespaceObject = window.React;
|
|
2487
|
+
window.jsx_runtime_namespaceObject = {
|
|
2488
|
+
jsx: function(type, props, key) {
|
|
2489
|
+
if (key !== undefined) props = Object.assign({}, props, { key: key });
|
|
2490
|
+
return React.createElement(type, props);
|
|
2491
|
+
},
|
|
2492
|
+
jsxs: function(type, props, key) {
|
|
2493
|
+
if (key !== undefined) props = Object.assign({}, props, { key: key });
|
|
2494
|
+
return React.createElement(type, props);
|
|
2495
|
+
},
|
|
2496
|
+
Fragment: React.Fragment
|
|
2497
|
+
};
|
|
2498
|
+
window.__reactReady = true;
|
|
2499
|
+
</script>`;
|
|
2500
|
+
} else {
|
|
2501
|
+
const reactUrl = reactConfig === "cdn" ? STATIC_HTML_CDN.esm.react : reactConfig;
|
|
2502
|
+
const reactDomUrl = reactDomConfig === "cdn" ? STATIC_HTML_CDN.esm.reactDom : reactDomConfig;
|
|
2503
|
+
return `
|
|
2504
|
+
<!-- React Runtime (ES modules from esm.sh) -->
|
|
2505
|
+
<script type="module">
|
|
2506
|
+
import React from '${reactUrl}';
|
|
2507
|
+
import { createRoot } from '${reactDomUrl}';
|
|
2508
|
+
|
|
2509
|
+
// Make React available globally
|
|
2510
|
+
window.React = React;
|
|
2511
|
+
window.ReactDOM = { createRoot };
|
|
2512
|
+
|
|
2513
|
+
// Webpack/esbuild polyfills for transpiled code
|
|
2514
|
+
window.external_react_namespaceObject = React;
|
|
2515
|
+
window.jsx_runtime_namespaceObject = {
|
|
2516
|
+
jsx: function(type, props, key) {
|
|
2517
|
+
if (key !== undefined) props = Object.assign({}, props, { key: key });
|
|
2518
|
+
return React.createElement(type, props);
|
|
2519
|
+
},
|
|
2520
|
+
jsxs: function(type, props, key) {
|
|
2521
|
+
if (key !== undefined) props = Object.assign({}, props, { key: key });
|
|
2522
|
+
return React.createElement(type, props);
|
|
2523
|
+
},
|
|
2524
|
+
Fragment: React.Fragment
|
|
2525
|
+
};
|
|
2526
|
+
|
|
2527
|
+
// Signal React is ready
|
|
2528
|
+
window.__reactReady = true;
|
|
2529
|
+
window.dispatchEvent(new CustomEvent('react:ready'));
|
|
2530
|
+
</script>`;
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2533
|
+
/**
|
|
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.
|
|
2537
|
+
* Always inlined for reliability across platforms.
|
|
2538
|
+
*/
|
|
2539
|
+
async buildFrontMCPRuntime() {
|
|
2540
|
+
let uiComponents;
|
|
2541
|
+
try {
|
|
2542
|
+
uiComponents = await getBrowserComponents();
|
|
2543
|
+
} catch {
|
|
2544
|
+
uiComponents = buildFallbackUIComponents();
|
|
2545
|
+
}
|
|
2546
|
+
return `
|
|
2547
|
+
<!-- FrontMCP Runtime (always inline) -->
|
|
2548
|
+
<script>
|
|
2549
|
+
// Custom require() shim for browser - maps module names to globals
|
|
2550
|
+
// This allows esbuild-transpiled code to work in browsers
|
|
2551
|
+
window.__moduleCache = {};
|
|
2552
|
+
window.require = function(moduleName) {
|
|
2553
|
+
// Check cache first
|
|
2554
|
+
if (window.__moduleCache[moduleName]) {
|
|
2555
|
+
return window.__moduleCache[moduleName];
|
|
2556
|
+
}
|
|
2557
|
+
|
|
2558
|
+
// Map module names to browser globals
|
|
2559
|
+
var moduleMap = {
|
|
2560
|
+
'react': function() { return window.React; },
|
|
2561
|
+
'react-dom': function() { return window.ReactDOM; },
|
|
2562
|
+
'react-dom/client': function() { return window.ReactDOM; },
|
|
2563
|
+
'react/jsx-runtime': function() { return window.jsx_runtime_namespaceObject; },
|
|
2564
|
+
'react/jsx-dev-runtime': function() { return window.jsx_runtime_namespaceObject; },
|
|
2565
|
+
'@frontmcp/ui': function() { return window.frontmcp_ui_namespaceObject; },
|
|
2566
|
+
'@frontmcp/ui/react': function() { return window.frontmcp_ui_namespaceObject; },
|
|
2567
|
+
};
|
|
2568
|
+
|
|
2569
|
+
var resolver = moduleMap[moduleName];
|
|
2570
|
+
if (resolver) {
|
|
2571
|
+
var mod = resolver();
|
|
2572
|
+
window.__moduleCache[moduleName] = mod;
|
|
2573
|
+
return mod;
|
|
2574
|
+
}
|
|
2575
|
+
|
|
2576
|
+
console.warn('[FrontMCP] Unknown module requested:', moduleName);
|
|
2577
|
+
return {};
|
|
2578
|
+
};
|
|
2579
|
+
|
|
2580
|
+
// Async require for dynamic imports (returns Promise)
|
|
2581
|
+
window.requireAsync = function(moduleName) {
|
|
2582
|
+
return new Promise(function(resolve, reject) {
|
|
2583
|
+
// If module is already loaded, resolve immediately
|
|
2584
|
+
var mod = window.require(moduleName);
|
|
2585
|
+
if (mod && Object.keys(mod).length > 0) {
|
|
2586
|
+
resolve(mod);
|
|
2587
|
+
return;
|
|
2588
|
+
}
|
|
2589
|
+
|
|
2590
|
+
// For now, we don't support dynamic CDN loading
|
|
2591
|
+
// All required modules should be pre-loaded
|
|
2592
|
+
console.warn('[FrontMCP] Async module not available:', moduleName);
|
|
2593
|
+
resolve({});
|
|
2594
|
+
});
|
|
2595
|
+
};
|
|
2596
|
+
|
|
2597
|
+
// FrontMCP Hook implementations and state
|
|
2598
|
+
window.__frontmcp = {
|
|
2599
|
+
// Context for MCP bridge
|
|
2600
|
+
context: {
|
|
2601
|
+
toolName: null,
|
|
2602
|
+
toolInput: null,
|
|
2603
|
+
toolOutput: null,
|
|
2604
|
+
structuredContent: null,
|
|
2605
|
+
callTool: null,
|
|
2606
|
+
},
|
|
2607
|
+
|
|
2608
|
+
// Theme and display settings
|
|
2609
|
+
theme: 'light',
|
|
2610
|
+
displayMode: 'embedded',
|
|
2611
|
+
hostContext: {},
|
|
2612
|
+
capabilities: {},
|
|
2613
|
+
|
|
2614
|
+
// Set context from data injection
|
|
2615
|
+
setContext: function(ctx) {
|
|
2616
|
+
Object.assign(this.context, ctx);
|
|
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
|
+
}
|
|
2637
|
+
};
|
|
2638
|
+
|
|
2639
|
+
// Hook: useToolOutput - returns the tool output data
|
|
2640
|
+
window.useToolOutput = function() {
|
|
2641
|
+
return window.__frontmcp.context.toolOutput;
|
|
2642
|
+
};
|
|
2643
|
+
|
|
2644
|
+
// Hook: useToolInput - returns the tool input arguments
|
|
2645
|
+
window.useToolInput = function() {
|
|
2646
|
+
return window.__frontmcp.context.toolInput;
|
|
2647
|
+
};
|
|
2648
|
+
|
|
2649
|
+
// Hook: useMcpBridgeContext - returns full bridge context
|
|
2650
|
+
window.useMcpBridgeContext = function() {
|
|
2651
|
+
return window.__frontmcp.context;
|
|
2652
|
+
};
|
|
2653
|
+
|
|
2654
|
+
// Hook: useCallTool - returns function to call other tools
|
|
2655
|
+
window.useCallTool = function() {
|
|
2656
|
+
return function(name, args) {
|
|
2657
|
+
if (window.__frontmcp.context.callTool) {
|
|
2658
|
+
return window.__frontmcp.context.callTool(name, args);
|
|
2659
|
+
}
|
|
2660
|
+
console.warn('[FrontMCP] callTool not available - widget may not have tool access');
|
|
2661
|
+
return Promise.resolve(null);
|
|
2662
|
+
};
|
|
2663
|
+
};
|
|
2664
|
+
</script>
|
|
2665
|
+
<!-- UI Components (Full-Featured, Browser-Compatible) -->
|
|
2666
|
+
<script>
|
|
2667
|
+
${uiComponents}
|
|
2668
|
+
</script>`;
|
|
2669
|
+
}
|
|
2670
|
+
/**
|
|
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).
|
|
2687
|
+
*/
|
|
2688
|
+
buildStaticDataScript(toolName, input, output, structuredContent) {
|
|
2689
|
+
const safeJson = (value) => {
|
|
2690
|
+
try {
|
|
2691
|
+
return JSON.stringify(value);
|
|
2692
|
+
} catch {
|
|
2693
|
+
return "null";
|
|
2694
|
+
}
|
|
2695
|
+
};
|
|
2696
|
+
return `
|
|
2697
|
+
<!-- Tool Data Injection (Static Mode) -->
|
|
2698
|
+
<script>
|
|
2699
|
+
window.__mcpToolName = ${safeJson(toolName)};
|
|
2700
|
+
window.__mcpToolInput = ${safeJson(input ?? null)};
|
|
2701
|
+
window.__mcpToolOutput = ${safeJson(output ?? null)};
|
|
2702
|
+
window.__mcpStructuredContent = ${safeJson(structuredContent ?? null)};
|
|
2703
|
+
|
|
2704
|
+
// Initialize FrontMCP context
|
|
2705
|
+
window.__frontmcp.setContext({
|
|
2706
|
+
toolName: window.__mcpToolName,
|
|
2707
|
+
toolInput: window.__mcpToolInput,
|
|
2708
|
+
toolOutput: window.__mcpToolOutput,
|
|
2709
|
+
structuredContent: window.__mcpStructuredContent,
|
|
2710
|
+
});
|
|
2711
|
+
</script>`;
|
|
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
|
+
}
|
|
2963
|
+
/**
|
|
2964
|
+
* Build component render script.
|
|
2965
|
+
* Wraps CommonJS code with module/exports shim to capture the component.
|
|
2966
|
+
*/
|
|
2967
|
+
buildComponentRenderScript(componentCode, rootId, cdnType) {
|
|
2968
|
+
const wrappedCode = `
|
|
2969
|
+
// CommonJS module shim
|
|
2970
|
+
var module = { exports: {} };
|
|
2971
|
+
var exports = module.exports;
|
|
2972
|
+
|
|
2973
|
+
// Execute transpiled component code (CommonJS format)
|
|
2974
|
+
${componentCode}
|
|
2975
|
+
|
|
2976
|
+
// Capture the component export
|
|
2977
|
+
window.__frontmcp_component = module.exports;
|
|
2978
|
+
`;
|
|
2979
|
+
if (cdnType === "umd") {
|
|
2980
|
+
return `
|
|
2981
|
+
<!-- Component Render Script (UMD - synchronous) -->
|
|
2982
|
+
<script>
|
|
2983
|
+
(function() {
|
|
2984
|
+
${wrappedCode}
|
|
2985
|
+
|
|
2986
|
+
// Get the component
|
|
2987
|
+
var Component = window.__frontmcp_component.default || window.__frontmcp_component;
|
|
2988
|
+
|
|
2989
|
+
// Render the component
|
|
2990
|
+
var container = document.getElementById('${rootId}');
|
|
2991
|
+
if (container && window.ReactDOM && window.ReactDOM.createRoot) {
|
|
2992
|
+
var root = window.ReactDOM.createRoot(container);
|
|
2993
|
+
root.render(React.createElement(Component, {
|
|
2994
|
+
output: window.__mcpToolOutput,
|
|
2995
|
+
input: window.__mcpToolInput,
|
|
2996
|
+
}));
|
|
2997
|
+
} else if (container && window.ReactDOM && window.ReactDOM.render) {
|
|
2998
|
+
// Fallback for React 17
|
|
2999
|
+
window.ReactDOM.render(
|
|
3000
|
+
React.createElement(Component, {
|
|
3001
|
+
output: window.__mcpToolOutput,
|
|
3002
|
+
input: window.__mcpToolInput,
|
|
3003
|
+
}),
|
|
3004
|
+
container
|
|
3005
|
+
);
|
|
3006
|
+
}
|
|
3007
|
+
})();
|
|
3008
|
+
</script>`;
|
|
3009
|
+
} else {
|
|
3010
|
+
return `
|
|
3011
|
+
<!-- Component Render Script (ESM - waits for React) -->
|
|
3012
|
+
<script type="module">
|
|
3013
|
+
function renderComponent() {
|
|
3014
|
+
${wrappedCode}
|
|
3015
|
+
|
|
3016
|
+
// Get the component
|
|
3017
|
+
var Component = window.__frontmcp_component.default || window.__frontmcp_component;
|
|
3018
|
+
|
|
3019
|
+
// Render the component
|
|
3020
|
+
var container = document.getElementById('${rootId}');
|
|
3021
|
+
if (container && window.ReactDOM && window.ReactDOM.createRoot) {
|
|
3022
|
+
var root = window.ReactDOM.createRoot(container);
|
|
3023
|
+
root.render(React.createElement(Component, {
|
|
3024
|
+
output: window.__mcpToolOutput,
|
|
3025
|
+
input: window.__mcpToolInput,
|
|
3026
|
+
}));
|
|
3027
|
+
}
|
|
3028
|
+
}
|
|
3029
|
+
|
|
3030
|
+
// Wait for React to be ready
|
|
3031
|
+
if (window.__reactReady) {
|
|
3032
|
+
renderComponent();
|
|
3033
|
+
} else {
|
|
3034
|
+
window.addEventListener('react:ready', renderComponent);
|
|
3035
|
+
}
|
|
3036
|
+
</script>`;
|
|
3037
|
+
}
|
|
3038
|
+
}
|
|
3039
|
+
/**
|
|
3040
|
+
* Assemble the complete static HTML document.
|
|
3041
|
+
*/
|
|
3042
|
+
assembleStaticHTML(parts) {
|
|
3043
|
+
return `<!DOCTYPE html>
|
|
3044
|
+
<html lang="en">
|
|
3045
|
+
<head>
|
|
3046
|
+
<title>${escapeHtml(parts.title)}</title>
|
|
3047
|
+
${parts.head}
|
|
3048
|
+
${parts.reactRuntime}
|
|
3049
|
+
${parts.frontmcpRuntime}
|
|
3050
|
+
${parts.dataScript}
|
|
3051
|
+
</head>
|
|
3052
|
+
<body>
|
|
3053
|
+
<div id="${parts.rootId}" class="frontmcp-loading">
|
|
3054
|
+
<div class="frontmcp-spinner"></div>
|
|
3055
|
+
</div>
|
|
3056
|
+
${parts.componentScript}
|
|
3057
|
+
</body>
|
|
3058
|
+
</html>`;
|
|
3059
|
+
}
|
|
3060
|
+
};
|
|
3061
|
+
function createBundler(options) {
|
|
3062
|
+
return new InMemoryBundler(options);
|
|
3063
|
+
}
|
|
3064
|
+
|
|
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";
|
|
3094
|
+
export {
|
|
3095
|
+
ALL_PLATFORMS,
|
|
3096
|
+
BundlerCache2 as BundlerCache,
|
|
3097
|
+
ComponentBuilder,
|
|
3098
|
+
DEFAULT_BUNDLER_OPTIONS,
|
|
3099
|
+
DEFAULT_BUNDLE_OPTIONS,
|
|
3100
|
+
DEFAULT_SECURITY_POLICY,
|
|
3101
|
+
DEFAULT_STATIC_HTML_OPTIONS,
|
|
3102
|
+
DEFAULT_STORAGE_OPTIONS,
|
|
3103
|
+
ExecutionError2 as ExecutionError,
|
|
3104
|
+
FilesystemStorage,
|
|
3105
|
+
HYBRID_DATA_PLACEHOLDER,
|
|
3106
|
+
HYBRID_INPUT_PLACEHOLDER,
|
|
3107
|
+
InMemoryBundler,
|
|
3108
|
+
RedisStorage,
|
|
3109
|
+
STATIC_HTML_CDN,
|
|
3110
|
+
SecurityError,
|
|
3111
|
+
buildIdFromHash,
|
|
3112
|
+
calculateComponentHash,
|
|
3113
|
+
calculateManifestSize,
|
|
3114
|
+
calculateQuickHash,
|
|
3115
|
+
createBundler,
|
|
3116
|
+
createCacheKey2 as createCacheKey,
|
|
3117
|
+
createFilesystemBuilder,
|
|
3118
|
+
createFilesystemStorage,
|
|
3119
|
+
createRedisBuilder,
|
|
3120
|
+
createRedisStorage,
|
|
3121
|
+
executeCode,
|
|
3122
|
+
executeDefault2 as executeDefault,
|
|
3123
|
+
generateBuildId,
|
|
3124
|
+
getCdnTypeForPlatform,
|
|
3125
|
+
hashContent2 as hashContent,
|
|
3126
|
+
hashFile,
|
|
3127
|
+
hashFiles,
|
|
3128
|
+
isExecutionError,
|
|
3129
|
+
mergePolicy2 as mergePolicy,
|
|
3130
|
+
sha256,
|
|
3131
|
+
sha256Buffer,
|
|
3132
|
+
throwOnViolations2 as throwOnViolations,
|
|
3133
|
+
validateImports,
|
|
3134
|
+
validateSize2 as validateSize,
|
|
3135
|
+
validateSource2 as validateSource
|
|
3136
|
+
};
|