@frontmcp/uipack 1.10.8 → 1.10.9-rc.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/adapters/index.js +29 -2040
- package/bridge-runtime/index.js +11 -217
- package/component/index.js +24 -1469
- package/esm/adapters/index.mjs +29 -2006
- package/esm/bridge-runtime/index.mjs +11 -185
- package/esm/component/index.mjs +24 -1439
- package/esm/index.mjs +150 -3483
- package/esm/package.json +2 -2
- package/esm/resolver/index.mjs +2 -660
- package/esm/shell/index.mjs +16 -750
- package/esm/types/index.mjs +1 -75
- package/esm/typings/index.mjs +3 -971
- package/esm/utils/index.mjs +1 -66
- package/index.js +150 -3605
- package/package.json +2 -2
- package/resolver/index.js +2 -707
- package/shell/index.js +16 -803
- package/types/index.js +1 -108
- package/typings/index.js +3 -1032
- package/utils/index.js +1 -96
package/esm/index.mjs
CHANGED
|
@@ -1,220 +1,7 @@
|
|
|
1
1
|
var __frontmcpModule = typeof process !== "undefined" && typeof process.getBuiltinModule === "function" ? process.getBuiltinModule("module") : undefined;
|
|
2
2
|
const require = __frontmcpModule ? __frontmcpModule.createRequire(import.meta.url || 'file:///') : undefined;
|
|
3
3
|
// __frontmcpCreateRequire
|
|
4
|
-
var __require
|
|
5
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
6
|
-
}) : x)(function(x) {
|
|
7
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
8
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
// libs/uipack/src/types/ui-runtime.ts
|
|
12
|
-
function isUIType(value) {
|
|
13
|
-
return typeof value === "string" && ["html", "react", "mdx", "markdown", "auto"].includes(value);
|
|
14
|
-
}
|
|
15
|
-
function isBundlingMode(value) {
|
|
16
|
-
return typeof value === "string" && ["static", "dynamic"].includes(value);
|
|
17
|
-
}
|
|
18
|
-
function isResourceMode(value) {
|
|
19
|
-
return typeof value === "string" && ["cdn", "inline"].includes(value);
|
|
20
|
-
}
|
|
21
|
-
function isDisplayMode(value) {
|
|
22
|
-
return typeof value === "string" && ["inline", "fullscreen", "pip"].includes(value);
|
|
23
|
-
}
|
|
24
|
-
function isOutputMode(value) {
|
|
25
|
-
return typeof value === "string" && ["code-only", "full-ssr"].includes(value);
|
|
26
|
-
}
|
|
27
|
-
var DEFAULT_CSP_BY_TYPE = {
|
|
28
|
-
html: {
|
|
29
|
-
scriptSrc: ["'self'", "'unsafe-inline'"],
|
|
30
|
-
styleSrc: ["'self'", "'unsafe-inline'"],
|
|
31
|
-
connectSrc: []
|
|
32
|
-
},
|
|
33
|
-
react: {
|
|
34
|
-
scriptSrc: ["'self'", "'unsafe-inline'", "https://unpkg.com"],
|
|
35
|
-
styleSrc: ["'self'", "'unsafe-inline'"],
|
|
36
|
-
connectSrc: []
|
|
37
|
-
},
|
|
38
|
-
mdx: {
|
|
39
|
-
scriptSrc: ["'self'", "'unsafe-inline'", "https://unpkg.com"],
|
|
40
|
-
styleSrc: ["'self'", "'unsafe-inline'"],
|
|
41
|
-
connectSrc: []
|
|
42
|
-
},
|
|
43
|
-
markdown: {
|
|
44
|
-
scriptSrc: ["'self'", "'unsafe-inline'"],
|
|
45
|
-
styleSrc: ["'self'", "'unsafe-inline'"],
|
|
46
|
-
connectSrc: []
|
|
47
|
-
},
|
|
48
|
-
auto: {
|
|
49
|
-
scriptSrc: ["'self'", "'unsafe-inline'", "https://unpkg.com"],
|
|
50
|
-
styleSrc: ["'self'", "'unsafe-inline'"],
|
|
51
|
-
connectSrc: []
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
var DEFAULT_RENDERER_ASSETS = {
|
|
55
|
-
html: {
|
|
56
|
-
handlebars: { url: "https://unpkg.com/handlebars@latest/dist/handlebars.runtime.min.js" }
|
|
57
|
-
},
|
|
58
|
-
react: {
|
|
59
|
-
react: { url: "https://unpkg.com/react@18/umd/react.production.min.js" },
|
|
60
|
-
reactDom: { url: "https://unpkg.com/react-dom@18/umd/react-dom.production.min.js" }
|
|
61
|
-
},
|
|
62
|
-
mdx: {
|
|
63
|
-
react: { url: "https://unpkg.com/react@18/umd/react.production.min.js" },
|
|
64
|
-
reactDom: { url: "https://unpkg.com/react-dom@18/umd/react-dom.production.min.js" },
|
|
65
|
-
markdown: { url: "https://unpkg.com/marked@latest/marked.min.js" }
|
|
66
|
-
},
|
|
67
|
-
markdown: {
|
|
68
|
-
markdown: { url: "https://unpkg.com/marked@latest/marked.min.js" }
|
|
69
|
-
},
|
|
70
|
-
auto: {
|
|
71
|
-
react: { url: "https://unpkg.com/react@18/umd/react.production.min.js" },
|
|
72
|
-
reactDom: { url: "https://unpkg.com/react-dom@18/umd/react-dom.production.min.js" },
|
|
73
|
-
markdown: { url: "https://unpkg.com/marked@latest/marked.min.js" },
|
|
74
|
-
handlebars: { url: "https://unpkg.com/handlebars@latest/dist/handlebars.runtime.min.js" }
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
// libs/uipack/src/utils/index.ts
|
|
79
|
-
function safeStringify(value, space) {
|
|
80
|
-
const seen = /* @__PURE__ */ new WeakSet();
|
|
81
|
-
try {
|
|
82
|
-
return JSON.stringify(
|
|
83
|
-
value,
|
|
84
|
-
(_key, val) => {
|
|
85
|
-
if (typeof val === "object" && val !== null) {
|
|
86
|
-
if (seen.has(val)) return "[Circular]";
|
|
87
|
-
seen.add(val);
|
|
88
|
-
}
|
|
89
|
-
return val;
|
|
90
|
-
},
|
|
91
|
-
space
|
|
92
|
-
);
|
|
93
|
-
} catch {
|
|
94
|
-
return JSON.stringify({ error: "Output could not be serialized" });
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
function escapeHtml(str) {
|
|
98
|
-
if (str === null || str === void 0) {
|
|
99
|
-
return "";
|
|
100
|
-
}
|
|
101
|
-
const s = String(str);
|
|
102
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
103
|
-
}
|
|
104
|
-
function escapeHtmlAttr(str) {
|
|
105
|
-
return str.replace(/&/g, "&").replace(/"/g, """);
|
|
106
|
-
}
|
|
107
|
-
function escapeJsString(str) {
|
|
108
|
-
return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
109
|
-
}
|
|
110
|
-
function escapeScriptClose(jsonString) {
|
|
111
|
-
return jsonString.replace(/<\//g, "<\\/");
|
|
112
|
-
}
|
|
113
|
-
var SCRIPT_UNSAFE_CHARACTERS = /[<>&\u2028\u2029]/g;
|
|
114
|
-
function toUnicodeEscape(character) {
|
|
115
|
-
return `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`;
|
|
116
|
-
}
|
|
117
|
-
function safeJsonForScript(value) {
|
|
118
|
-
if (value === void 0) {
|
|
119
|
-
return "null";
|
|
120
|
-
}
|
|
121
|
-
try {
|
|
122
|
-
const jsonString = JSON.stringify(value, (_key, val) => {
|
|
123
|
-
if (typeof val === "bigint") {
|
|
124
|
-
return val.toString();
|
|
125
|
-
}
|
|
126
|
-
return val;
|
|
127
|
-
});
|
|
128
|
-
if (jsonString === void 0) {
|
|
129
|
-
return "null";
|
|
130
|
-
}
|
|
131
|
-
return jsonString.replace(SCRIPT_UNSAFE_CHARACTERS, toUnicodeEscape);
|
|
132
|
-
} catch {
|
|
133
|
-
return '{"error":"Value could not be serialized"}';
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// libs/uipack/src/bridge-runtime/iife-generator.ts
|
|
138
|
-
var WIDGET_CALL_META_KEY = "frontmcp/widgetCall";
|
|
139
|
-
var MCP_APPS_DISPLAY_MODES = ["inline", "fullscreen", "pip"];
|
|
140
|
-
function generateBridgeIIFE(options = {}) {
|
|
141
|
-
const { debug = false, trustedOrigins = [], minify = false } = options;
|
|
142
|
-
const adapters = options.adapters || ["openai", "ext-apps", "claude", "gemini", "generic"];
|
|
143
|
-
const parts = [];
|
|
144
|
-
parts.push("(function() {");
|
|
145
|
-
parts.push('"use strict";');
|
|
146
|
-
parts.push("");
|
|
147
|
-
if (debug) {
|
|
148
|
-
parts.push('function log(msg) { console.log("[FrontMcpBridge] " + msg); }');
|
|
149
|
-
} else {
|
|
150
|
-
parts.push("function log() {}");
|
|
151
|
-
}
|
|
152
|
-
parts.push("");
|
|
153
|
-
parts.push("var DEFAULT_SAFE_AREA = { top: 0, bottom: 0, left: 0, right: 0 };");
|
|
154
|
-
parts.push("");
|
|
155
|
-
parts.push(generateContextDetection());
|
|
156
|
-
parts.push("");
|
|
157
|
-
parts.push(generateBaseCapabilities());
|
|
158
|
-
parts.push("");
|
|
159
|
-
if (adapters.includes("openai")) {
|
|
160
|
-
parts.push(generateOpenAIAdapter());
|
|
161
|
-
parts.push("");
|
|
162
|
-
}
|
|
163
|
-
if (adapters.includes("ext-apps")) {
|
|
164
|
-
parts.push(generateExtAppsAdapter(trustedOrigins));
|
|
165
|
-
parts.push("");
|
|
166
|
-
}
|
|
167
|
-
if (adapters.includes("claude")) {
|
|
168
|
-
parts.push(generateClaudeAdapter());
|
|
169
|
-
parts.push("");
|
|
170
|
-
}
|
|
171
|
-
if (adapters.includes("gemini")) {
|
|
172
|
-
parts.push(generateGeminiAdapter());
|
|
173
|
-
parts.push("");
|
|
174
|
-
}
|
|
175
|
-
if (adapters.includes("generic")) {
|
|
176
|
-
parts.push(generateGenericAdapter());
|
|
177
|
-
parts.push("");
|
|
178
|
-
}
|
|
179
|
-
parts.push(generatePlatformDetection(adapters));
|
|
180
|
-
parts.push("");
|
|
181
|
-
parts.push(generateBridgeClass());
|
|
182
|
-
parts.push("");
|
|
183
|
-
parts.push("var bridge = new FrontMcpBridge();");
|
|
184
|
-
parts.push("bridge.initialize().then(function() {");
|
|
185
|
-
parts.push(' log("Bridge initialized with adapter: " + bridge.adapterId);');
|
|
186
|
-
parts.push(' window.dispatchEvent(new CustomEvent("bridge:ready", { detail: { adapter: bridge.adapterId } }));');
|
|
187
|
-
parts.push(" if (window.__mcpDisplayMode) bridge.requestDisplayMode(window.__mcpDisplayMode).catch(function() {});");
|
|
188
|
-
parts.push("}).catch(function(err) {");
|
|
189
|
-
parts.push(' console.error("[FrontMcpBridge] Init failed:", err);');
|
|
190
|
-
parts.push(' window.dispatchEvent(new CustomEvent("bridge:error", { detail: { error: err } }));');
|
|
191
|
-
parts.push("});");
|
|
192
|
-
parts.push("");
|
|
193
|
-
parts.push("window.FrontMcpBridge = bridge;");
|
|
194
|
-
parts.push("");
|
|
195
|
-
parts.push(generateAutoResize());
|
|
196
|
-
parts.push("function __showLoading() {");
|
|
197
|
-
parts.push(' var root = document.getElementById("root");');
|
|
198
|
-
parts.push(" if (root && !root.hasChildNodes()) {");
|
|
199
|
-
parts.push(
|
|
200
|
-
` root.innerHTML = '<div style="display:flex;align-items:center;justify-content:center;padding:2rem;color:#6b7280;font-family:system-ui,sans-serif"><div style="text-align:center"><div style="width:24px;height:24px;border:2px solid #e5e7eb;border-top-color:#3b82f6;border-radius:50%;animation:__spin 0.6s linear infinite;margin:0 auto 12px"></div><div style="font-size:0.875rem">Loading widget...</div></div></div><style>@keyframes __spin{to{transform:rotate(360deg)}}</style>';`
|
|
201
|
-
);
|
|
202
|
-
parts.push(" }");
|
|
203
|
-
parts.push("}");
|
|
204
|
-
parts.push('if (document.readyState === "loading") {');
|
|
205
|
-
parts.push(' document.addEventListener("DOMContentLoaded", __showLoading);');
|
|
206
|
-
parts.push("} else {");
|
|
207
|
-
parts.push(" __showLoading();");
|
|
208
|
-
parts.push("}");
|
|
209
|
-
parts.push("})();");
|
|
210
|
-
const code = parts.join("\n");
|
|
211
|
-
if (minify) {
|
|
212
|
-
return minifyJS(code);
|
|
213
|
-
}
|
|
214
|
-
return code;
|
|
215
|
-
}
|
|
216
|
-
function generateAutoResize() {
|
|
217
|
-
return `
|
|
4
|
+
var __require=(x=>typeof require!=="undefined"?require:typeof Proxy!=="undefined"?new Proxy(x,{get:(a,b)=>(typeof require!=="undefined"?require:a)[b]}):x)(function(x){if(typeof require!=="undefined")return require.apply(this,arguments);throw Error('Dynamic require of "'+x+'" is not supported')});function isUIType(value){return typeof value==="string"&&["html","react","mdx","markdown","auto"].includes(value)}function isBundlingMode(value){return typeof value==="string"&&["static","dynamic"].includes(value)}function isResourceMode(value){return typeof value==="string"&&["cdn","inline"].includes(value)}function isDisplayMode(value){return typeof value==="string"&&["inline","fullscreen","pip"].includes(value)}function isOutputMode(value){return typeof value==="string"&&["code-only","full-ssr"].includes(value)}var DEFAULT_CSP_BY_TYPE={html:{scriptSrc:["'self'","'unsafe-inline'"],styleSrc:["'self'","'unsafe-inline'"],connectSrc:[]},react:{scriptSrc:["'self'","'unsafe-inline'","https://unpkg.com"],styleSrc:["'self'","'unsafe-inline'"],connectSrc:[]},mdx:{scriptSrc:["'self'","'unsafe-inline'","https://unpkg.com"],styleSrc:["'self'","'unsafe-inline'"],connectSrc:[]},markdown:{scriptSrc:["'self'","'unsafe-inline'"],styleSrc:["'self'","'unsafe-inline'"],connectSrc:[]},auto:{scriptSrc:["'self'","'unsafe-inline'","https://unpkg.com"],styleSrc:["'self'","'unsafe-inline'"],connectSrc:[]}};var DEFAULT_RENDERER_ASSETS={html:{handlebars:{url:"https://unpkg.com/handlebars@latest/dist/handlebars.runtime.min.js"}},react:{react:{url:"https://unpkg.com/react@18/umd/react.production.min.js"},reactDom:{url:"https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"}},mdx:{react:{url:"https://unpkg.com/react@18/umd/react.production.min.js"},reactDom:{url:"https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"},markdown:{url:"https://unpkg.com/marked@latest/marked.min.js"}},markdown:{markdown:{url:"https://unpkg.com/marked@latest/marked.min.js"}},auto:{react:{url:"https://unpkg.com/react@18/umd/react.production.min.js"},reactDom:{url:"https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"},markdown:{url:"https://unpkg.com/marked@latest/marked.min.js"},handlebars:{url:"https://unpkg.com/handlebars@latest/dist/handlebars.runtime.min.js"}}};function safeStringify(value,space){const seen=new WeakSet;try{return JSON.stringify(value,(_key,val)=>{if(typeof val==="object"&&val!==null){if(seen.has(val))return"[Circular]";seen.add(val)}return val},space)}catch{return JSON.stringify({error:"Output could not be serialized"})}}function escapeHtml(str){if(str===null||str===void 0){return""}const s=String(str);return s.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\u2028/g,"\\u2028").replace(/\u2029/g,"\\u2029")}function escapeHtmlAttr(str){return str.replace(/&/g,"&").replace(/"/g,""")}function escapeJsString(str){return str.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/\t/g,"\\t").replace(/\u2028/g,"\\u2028").replace(/\u2029/g,"\\u2029")}function escapeScriptClose(jsonString){return jsonString.replace(/<\//g,"<\\/")}var SCRIPT_UNSAFE_CHARACTERS=/[<>&\u2028\u2029]/g;function toUnicodeEscape(character){return`\\u${character.charCodeAt(0).toString(16).padStart(4,"0")}`}function safeJsonForScript(value){if(value===void 0){return"null"}try{const jsonString=JSON.stringify(value,(_key,val)=>{if(typeof val==="bigint"){return val.toString()}return val});if(jsonString===void 0){return"null"}return jsonString.replace(SCRIPT_UNSAFE_CHARACTERS,toUnicodeEscape)}catch{return'{"error":"Value could not be serialized"}'}}var WIDGET_CALL_META_KEY="frontmcp/widgetCall";var MCP_APPS_DISPLAY_MODES=["inline","fullscreen","pip"];function generateBridgeIIFE(options={}){const{debug=false,trustedOrigins=[],minify=false}=options;const adapters=options.adapters||["openai","ext-apps","claude","gemini","generic"];const parts=[];parts.push("(function() {");parts.push('"use strict";');parts.push("");if(debug){parts.push('function log(msg) { console.log("[FrontMcpBridge] " + msg); }')}else{parts.push("function log() {}")}parts.push("");parts.push("var DEFAULT_SAFE_AREA = { top: 0, bottom: 0, left: 0, right: 0 };");parts.push("");parts.push(generateContextDetection());parts.push("");parts.push(generateBaseCapabilities());parts.push("");if(adapters.includes("openai")){parts.push(generateOpenAIAdapter());parts.push("")}if(adapters.includes("ext-apps")){parts.push(generateExtAppsAdapter(trustedOrigins));parts.push("")}if(adapters.includes("claude")){parts.push(generateClaudeAdapter());parts.push("")}if(adapters.includes("gemini")){parts.push(generateGeminiAdapter());parts.push("")}if(adapters.includes("generic")){parts.push(generateGenericAdapter());parts.push("")}parts.push(generatePlatformDetection(adapters));parts.push("");parts.push(generateBridgeClass());parts.push("");parts.push("var bridge = new FrontMcpBridge();");parts.push("bridge.initialize().then(function() {");parts.push(' log("Bridge initialized with adapter: " + bridge.adapterId);');parts.push(' window.dispatchEvent(new CustomEvent("bridge:ready", { detail: { adapter: bridge.adapterId } }));');parts.push(" if (window.__mcpDisplayMode) bridge.requestDisplayMode(window.__mcpDisplayMode).catch(function() {});");parts.push("}).catch(function(err) {");parts.push(' console.error("[FrontMcpBridge] Init failed:", err);');parts.push(' window.dispatchEvent(new CustomEvent("bridge:error", { detail: { error: err } }));');parts.push("});");parts.push("");parts.push("window.FrontMcpBridge = bridge;");parts.push("");parts.push(generateAutoResize());parts.push("function __showLoading() {");parts.push(' var root = document.getElementById("root");');parts.push(" if (root && !root.hasChildNodes()) {");parts.push(` root.innerHTML = '<div style="display:flex;align-items:center;justify-content:center;padding:2rem;color:#6b7280;font-family:system-ui,sans-serif"><div style="text-align:center"><div style="width:24px;height:24px;border:2px solid #e5e7eb;border-top-color:#3b82f6;border-radius:50%;animation:__spin 0.6s linear infinite;margin:0 auto 12px"></div><div style="font-size:0.875rem">Loading widget...</div></div></div><style>@keyframes __spin{to{transform:rotate(360deg)}}</style>';`);parts.push(" }");parts.push("}");parts.push('if (document.readyState === "loading") {');parts.push(' document.addEventListener("DOMContentLoaded", __showLoading);');parts.push("} else {");parts.push(" __showLoading();");parts.push("}");parts.push("})();");const code=parts.join("\n");if(minify){return minifyJS(code)}return code}function generateAutoResize(){return`
|
|
218
5
|
function __applySizingCss(sizing) {
|
|
219
6
|
if (typeof document === 'undefined' || !document.documentElement) return;
|
|
220
7
|
function toLen(v) { return typeof v === 'number' ? v + 'px' : v; }
|
|
@@ -349,10 +136,7 @@ function __initAutoResize() {
|
|
|
349
136
|
}
|
|
350
137
|
}
|
|
351
138
|
__initAutoResize();
|
|
352
|
-
`.trim()
|
|
353
|
-
}
|
|
354
|
-
function generateContextDetection() {
|
|
355
|
-
return `
|
|
139
|
+
`.trim()}function generateContextDetection(){return`
|
|
356
140
|
function detectTheme() {
|
|
357
141
|
if (typeof window !== 'undefined' && window.matchMedia) {
|
|
358
142
|
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
@@ -439,10 +223,7 @@ function readInjectedData() {
|
|
|
439
223
|
}
|
|
440
224
|
return data;
|
|
441
225
|
}
|
|
442
|
-
`.trim()
|
|
443
|
-
}
|
|
444
|
-
function generateBaseCapabilities() {
|
|
445
|
-
return `
|
|
226
|
+
`.trim()}function generateBaseCapabilities(){return`
|
|
446
227
|
var DEFAULT_CAPABILITIES = {
|
|
447
228
|
canCallTools: false,
|
|
448
229
|
canSendMessages: false,
|
|
@@ -452,10 +233,7 @@ var DEFAULT_CAPABILITIES = {
|
|
|
452
233
|
supportsDisplayModes: false,
|
|
453
234
|
supportsTheme: true
|
|
454
235
|
};
|
|
455
|
-
`.trim()
|
|
456
|
-
}
|
|
457
|
-
function generateOpenAIAdapter() {
|
|
458
|
-
return `
|
|
236
|
+
`.trim()}function generateOpenAIAdapter(){return`
|
|
459
237
|
var OpenAIAdapter = {
|
|
460
238
|
id: 'openai',
|
|
461
239
|
name: 'OpenAI ChatGPT',
|
|
@@ -540,11 +318,7 @@ var OpenAIAdapter = {
|
|
|
540
318
|
return Promise.resolve();
|
|
541
319
|
}
|
|
542
320
|
};
|
|
543
|
-
`.trim();
|
|
544
|
-
}
|
|
545
|
-
function generateExtAppsAdapter(trustedOrigins) {
|
|
546
|
-
const originsArray = trustedOrigins.length > 0 ? JSON.stringify(trustedOrigins) : "[]";
|
|
547
|
-
return `
|
|
321
|
+
`.trim()}function generateExtAppsAdapter(trustedOrigins){const originsArray=trustedOrigins.length>0?JSON.stringify(trustedOrigins):"[]";return`
|
|
548
322
|
var ExtAppsAdapter = {
|
|
549
323
|
id: 'ext-apps',
|
|
550
324
|
name: 'ext-apps (SEP-1865)',
|
|
@@ -887,10 +661,7 @@ var ExtAppsAdapter = {
|
|
|
887
661
|
return this.sendRequest('ui/unregisterTool', { name: name });
|
|
888
662
|
}
|
|
889
663
|
};
|
|
890
|
-
`.trim()
|
|
891
|
-
}
|
|
892
|
-
function generateClaudeAdapter() {
|
|
893
|
-
return `
|
|
664
|
+
`.trim()}function generateClaudeAdapter(){return`
|
|
894
665
|
var ClaudeAdapter = {
|
|
895
666
|
id: 'claude',
|
|
896
667
|
name: 'Claude (Anthropic)',
|
|
@@ -951,10 +722,7 @@ var ClaudeAdapter = {
|
|
|
951
722
|
return Promise.resolve();
|
|
952
723
|
}
|
|
953
724
|
};
|
|
954
|
-
`.trim()
|
|
955
|
-
}
|
|
956
|
-
function generateGeminiAdapter() {
|
|
957
|
-
return `
|
|
725
|
+
`.trim()}function generateGeminiAdapter(){return`
|
|
958
726
|
var GeminiAdapter = {
|
|
959
727
|
id: 'gemini',
|
|
960
728
|
name: 'Google Gemini',
|
|
@@ -1005,10 +773,7 @@ var GeminiAdapter = {
|
|
|
1005
773
|
return Promise.resolve();
|
|
1006
774
|
}
|
|
1007
775
|
};
|
|
1008
|
-
`.trim()
|
|
1009
|
-
}
|
|
1010
|
-
function generateGenericAdapter() {
|
|
1011
|
-
return `
|
|
776
|
+
`.trim()}function generateGenericAdapter(){return`
|
|
1012
777
|
var GenericAdapter = {
|
|
1013
778
|
id: 'generic',
|
|
1014
779
|
name: 'Generic Web',
|
|
@@ -1043,26 +808,7 @@ var GenericAdapter = {
|
|
|
1043
808
|
return Promise.resolve();
|
|
1044
809
|
}
|
|
1045
810
|
};
|
|
1046
|
-
`.trim();
|
|
1047
|
-
}
|
|
1048
|
-
function generatePlatformDetection(adapters) {
|
|
1049
|
-
const adapterVars = adapters.map((a) => {
|
|
1050
|
-
switch (a) {
|
|
1051
|
-
case "openai":
|
|
1052
|
-
return "OpenAIAdapter";
|
|
1053
|
-
case "ext-apps":
|
|
1054
|
-
return "ExtAppsAdapter";
|
|
1055
|
-
case "claude":
|
|
1056
|
-
return "ClaudeAdapter";
|
|
1057
|
-
case "gemini":
|
|
1058
|
-
return "GeminiAdapter";
|
|
1059
|
-
case "generic":
|
|
1060
|
-
return "GenericAdapter";
|
|
1061
|
-
default:
|
|
1062
|
-
return "";
|
|
1063
|
-
}
|
|
1064
|
-
}).filter(Boolean);
|
|
1065
|
-
return `
|
|
811
|
+
`.trim()}function generatePlatformDetection(adapters){const adapterVars=adapters.map(a=>{switch(a){case"openai":return"OpenAIAdapter";case"ext-apps":return"ExtAppsAdapter";case"claude":return"ClaudeAdapter";case"gemini":return"GeminiAdapter";case"generic":return"GenericAdapter";default:return""}}).filter(Boolean);return`
|
|
1066
812
|
var ADAPTERS = [${adapterVars.join(", ")}].sort(function(a, b) { return b.priority - a.priority; });
|
|
1067
813
|
|
|
1068
814
|
function detectPlatform() {
|
|
@@ -1075,10 +821,7 @@ function detectPlatform() {
|
|
|
1075
821
|
log('No platform detected, using generic');
|
|
1076
822
|
return GenericAdapter;
|
|
1077
823
|
}
|
|
1078
|
-
`.trim()
|
|
1079
|
-
}
|
|
1080
|
-
function generateBridgeClass() {
|
|
1081
|
-
return `
|
|
824
|
+
`.trim()}function generateBridgeClass(){return`
|
|
1082
825
|
function FrontMcpBridge() {
|
|
1083
826
|
this._adapter = null;
|
|
1084
827
|
this._initialized = false;
|
|
@@ -1454,3228 +1197,152 @@ FrontMcpBridge.prototype._setupDataToolCallHandler = function() {
|
|
|
1454
1197
|
}
|
|
1455
1198
|
}, true); // Use capture phase to handle before React handlers
|
|
1456
1199
|
};
|
|
1457
|
-
`.trim();
|
|
1458
|
-
}
|
|
1459
|
-
|
|
1460
|
-
function minifyJS(code) {
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
...options,
|
|
1490
|
-
adapters: platformAdapters[platform]
|
|
1491
|
-
});
|
|
1492
|
-
}
|
|
1493
|
-
var UNIVERSAL_BRIDGE_SCRIPT = generateBridgeIIFE();
|
|
1494
|
-
var BRIDGE_SCRIPT_TAGS = {
|
|
1495
|
-
universal: `<script>${UNIVERSAL_BRIDGE_SCRIPT}</script>`,
|
|
1496
|
-
chatgpt: `<script>${generatePlatformBundle("chatgpt")}</script>`,
|
|
1497
|
-
claude: `<script>${generatePlatformBundle("claude")}</script>`,
|
|
1498
|
-
gemini: `<script>${generatePlatformBundle("gemini")}</script>`
|
|
1499
|
-
};
|
|
1500
|
-
|
|
1501
|
-
// libs/uipack/src/typings/types.ts
|
|
1502
|
-
var DEFAULT_ALLOWED_PACKAGES = ["react", "react-dom", "react/jsx-runtime", "zod", "@frontmcp/*"];
|
|
1503
|
-
var DEFAULT_TYPE_FETCHER_OPTIONS = {
|
|
1504
|
-
allowedPackages: [...DEFAULT_ALLOWED_PACKAGES],
|
|
1505
|
-
maxDepth: 4,
|
|
1506
|
-
timeout: 1e4,
|
|
1507
|
-
maxConcurrency: 5,
|
|
1508
|
-
cdnBaseUrl: "https://esm.sh"
|
|
1509
|
-
};
|
|
1510
|
-
var TYPE_CACHE_PREFIX = "types:";
|
|
1511
|
-
var DEFAULT_TYPE_CACHE_TTL = 60 * 60 * 1e3;
|
|
1512
|
-
|
|
1513
|
-
// libs/uipack/src/typings/cache/cache-adapter.ts
|
|
1514
|
-
var DEFAULT_CACHE_OPTIONS = {
|
|
1515
|
-
maxSize: 500,
|
|
1516
|
-
defaultTtl: 60 * 60 * 1e3,
|
|
1517
|
-
// 1 hour
|
|
1518
|
-
trackStats: true
|
|
1519
|
-
};
|
|
1520
|
-
|
|
1521
|
-
// libs/uipack/src/typings/cache/memory-cache.ts
|
|
1522
|
-
var MemoryTypeCache = class {
|
|
1523
|
-
cache = /* @__PURE__ */ new Map();
|
|
1524
|
-
maxSize;
|
|
1525
|
-
defaultTtl;
|
|
1526
|
-
trackStats;
|
|
1527
|
-
/** Cache statistics */
|
|
1528
|
-
stats = {
|
|
1529
|
-
hits: 0,
|
|
1530
|
-
misses: 0,
|
|
1531
|
-
evictions: 0
|
|
1532
|
-
};
|
|
1533
|
-
constructor(options = {}) {
|
|
1534
|
-
const opts = { ...DEFAULT_CACHE_OPTIONS, ...options };
|
|
1535
|
-
this.maxSize = opts.maxSize;
|
|
1536
|
-
this.defaultTtl = opts.defaultTtl;
|
|
1537
|
-
this.trackStats = opts.trackStats;
|
|
1538
|
-
}
|
|
1539
|
-
/**
|
|
1540
|
-
* Get a cached entry by key.
|
|
1541
|
-
*/
|
|
1542
|
-
async get(key) {
|
|
1543
|
-
const internal = this.cache.get(key);
|
|
1544
|
-
if (!internal) {
|
|
1545
|
-
if (this.trackStats) this.stats.misses++;
|
|
1546
|
-
return void 0;
|
|
1547
|
-
}
|
|
1548
|
-
if (internal.expiresAt > 0 && Date.now() > internal.expiresAt) {
|
|
1549
|
-
this.cache.delete(key);
|
|
1550
|
-
if (this.trackStats) this.stats.misses++;
|
|
1551
|
-
return void 0;
|
|
1552
|
-
}
|
|
1553
|
-
this.cache.delete(key);
|
|
1554
|
-
internal.entry.accessCount++;
|
|
1555
|
-
this.cache.set(key, internal);
|
|
1556
|
-
if (this.trackStats) this.stats.hits++;
|
|
1557
|
-
return internal.entry;
|
|
1558
|
-
}
|
|
1559
|
-
/**
|
|
1560
|
-
* Store an entry in the cache.
|
|
1561
|
-
*/
|
|
1562
|
-
async set(key, entry, ttl) {
|
|
1563
|
-
if (this.cache.size >= this.maxSize && !this.cache.has(key)) {
|
|
1564
|
-
const oldestKey = this.cache.keys().next().value;
|
|
1565
|
-
if (oldestKey) {
|
|
1566
|
-
this.cache.delete(oldestKey);
|
|
1567
|
-
if (this.trackStats) this.stats.evictions++;
|
|
1568
|
-
}
|
|
1569
|
-
}
|
|
1570
|
-
const effectiveTtl = ttl ?? this.defaultTtl;
|
|
1571
|
-
const expiresAt = effectiveTtl > 0 ? Date.now() + effectiveTtl : 0;
|
|
1572
|
-
this.cache.set(key, { entry, expiresAt });
|
|
1573
|
-
}
|
|
1574
|
-
/**
|
|
1575
|
-
* Check if a key exists in the cache.
|
|
1576
|
-
*/
|
|
1577
|
-
async has(key) {
|
|
1578
|
-
const internal = this.cache.get(key);
|
|
1579
|
-
if (!internal) return false;
|
|
1580
|
-
if (internal.expiresAt > 0 && Date.now() > internal.expiresAt) {
|
|
1581
|
-
this.cache.delete(key);
|
|
1582
|
-
return false;
|
|
1583
|
-
}
|
|
1584
|
-
return true;
|
|
1585
|
-
}
|
|
1586
|
-
/**
|
|
1587
|
-
* Delete a cached entry.
|
|
1588
|
-
*/
|
|
1589
|
-
async delete(key) {
|
|
1590
|
-
return this.cache.delete(key);
|
|
1591
|
-
}
|
|
1592
|
-
/**
|
|
1593
|
-
* Clear all cached entries.
|
|
1594
|
-
*/
|
|
1595
|
-
async clear() {
|
|
1596
|
-
this.cache.clear();
|
|
1597
|
-
this.stats = { hits: 0, misses: 0, evictions: 0 };
|
|
1598
|
-
}
|
|
1599
|
-
/**
|
|
1600
|
-
* Get cache statistics.
|
|
1601
|
-
*/
|
|
1602
|
-
async getStats() {
|
|
1603
|
-
let totalSize = 0;
|
|
1604
|
-
for (const { entry } of this.cache.values()) {
|
|
1605
|
-
totalSize += entry.size;
|
|
1606
|
-
}
|
|
1607
|
-
const total = this.stats.hits + this.stats.misses;
|
|
1608
|
-
return {
|
|
1609
|
-
entries: this.cache.size,
|
|
1610
|
-
totalSize,
|
|
1611
|
-
hits: this.stats.hits,
|
|
1612
|
-
misses: this.stats.misses,
|
|
1613
|
-
hitRate: total > 0 ? this.stats.hits / total : 0
|
|
1614
|
-
};
|
|
1615
|
-
}
|
|
1616
|
-
/**
|
|
1617
|
-
* Get current cache size.
|
|
1618
|
-
*/
|
|
1619
|
-
get size() {
|
|
1620
|
-
return this.cache.size;
|
|
1621
|
-
}
|
|
1622
|
-
/**
|
|
1623
|
-
* Get number of evictions.
|
|
1624
|
-
*/
|
|
1625
|
-
get evictions() {
|
|
1626
|
-
return this.stats.evictions;
|
|
1627
|
-
}
|
|
1628
|
-
/**
|
|
1629
|
-
* Clean up expired entries.
|
|
1630
|
-
* Call periodically to free memory from expired entries.
|
|
1631
|
-
*
|
|
1632
|
-
* @returns Number of entries cleaned up
|
|
1633
|
-
*/
|
|
1634
|
-
cleanup() {
|
|
1635
|
-
const now = Date.now();
|
|
1636
|
-
let cleaned = 0;
|
|
1637
|
-
for (const [key, internal] of this.cache.entries()) {
|
|
1638
|
-
if (internal.expiresAt > 0 && now > internal.expiresAt) {
|
|
1639
|
-
this.cache.delete(key);
|
|
1640
|
-
cleaned++;
|
|
1641
|
-
}
|
|
1642
|
-
}
|
|
1643
|
-
return cleaned;
|
|
1200
|
+
`.trim()}var MAX_MINIFY_CODE_LENGTH=5e5;function minifyJS(code){if(code.length>MAX_MINIFY_CODE_LENGTH){return code}const out=[];let inBlockComment=false;for(const raw of code.split("\n")){const line=raw.trim();if(inBlockComment){if(line.includes("*/"))inBlockComment=false;continue}if(line===""||line.startsWith("//"))continue;if(line.startsWith("/*")){if(!line.includes("*/"))inBlockComment=true;continue}out.push(line)}return out.join("\n")}function generatePlatformBundle(platform,options={}){const platformAdapters={chatgpt:["openai","generic"],claude:["claude","generic"],gemini:["gemini","generic"],universal:["openai","ext-apps","claude","gemini","generic"]};return generateBridgeIIFE({...options,adapters:platformAdapters[platform]})}var UNIVERSAL_BRIDGE_SCRIPT=generateBridgeIIFE();var BRIDGE_SCRIPT_TAGS={universal:`<script>${UNIVERSAL_BRIDGE_SCRIPT}</script>`,chatgpt:`<script>${generatePlatformBundle("chatgpt")}</script>`,claude:`<script>${generatePlatformBundle("claude")}</script>`,gemini:`<script>${generatePlatformBundle("gemini")}</script>`};var DEFAULT_ALLOWED_PACKAGES=["react","react-dom","react/jsx-runtime","zod","@frontmcp/*"];var DEFAULT_TYPE_FETCHER_OPTIONS={allowedPackages:[...DEFAULT_ALLOWED_PACKAGES],maxDepth:4,timeout:1e4,maxConcurrency:5,cdnBaseUrl:"https://esm.sh"};var TYPE_CACHE_PREFIX="types:";var DEFAULT_TYPE_CACHE_TTL=60*60*1e3;var DEFAULT_CACHE_OPTIONS={maxSize:500,defaultTtl:60*60*1e3,trackStats:true};var MemoryTypeCache=class{cache=new Map;maxSize;defaultTtl;trackStats;stats={hits:0,misses:0,evictions:0};constructor(options={}){const opts={...DEFAULT_CACHE_OPTIONS,...options};this.maxSize=opts.maxSize;this.defaultTtl=opts.defaultTtl;this.trackStats=opts.trackStats}async get(key){const internal=this.cache.get(key);if(!internal){if(this.trackStats)this.stats.misses++;return void 0}if(internal.expiresAt>0&&Date.now()>internal.expiresAt){this.cache.delete(key);if(this.trackStats)this.stats.misses++;return void 0}this.cache.delete(key);internal.entry.accessCount++;this.cache.set(key,internal);if(this.trackStats)this.stats.hits++;return internal.entry}async set(key,entry,ttl){if(this.cache.size>=this.maxSize&&!this.cache.has(key)){const oldestKey=this.cache.keys().next().value;if(oldestKey){this.cache.delete(oldestKey);if(this.trackStats)this.stats.evictions++}}const effectiveTtl=ttl??this.defaultTtl;const expiresAt=effectiveTtl>0?Date.now()+effectiveTtl:0;this.cache.set(key,{entry,expiresAt})}async has(key){const internal=this.cache.get(key);if(!internal)return false;if(internal.expiresAt>0&&Date.now()>internal.expiresAt){this.cache.delete(key);return false}return true}async delete(key){return this.cache.delete(key)}async clear(){this.cache.clear();this.stats={hits:0,misses:0,evictions:0}}async getStats(){let totalSize=0;for(const{entry}of this.cache.values()){totalSize+=entry.size}const total=this.stats.hits+this.stats.misses;return{entries:this.cache.size,totalSize,hits:this.stats.hits,misses:this.stats.misses,hitRate:total>0?this.stats.hits/total:0}}get size(){return this.cache.size}get evictions(){return this.stats.evictions}cleanup(){const now=Date.now();let cleaned=0;for(const[key,internal]of this.cache.entries()){if(internal.expiresAt>0&&now>internal.expiresAt){this.cache.delete(key);cleaned++}}return cleaned}};var globalTypeCache=new MemoryTypeCache({maxSize:500,defaultTtl:60*60*1e3});var IMPORT_PATTERN=/^import\s+(?:type\s+)?(?:(?:\{[^}]*\}|\*\s+as\s+\w+|\w+)\s+from\s+)?['"]([^'"]+)['"]/gm;var EXPORT_FROM_PATTERN=/^export\s+(?:\{[^}]*\}|\*)\s+from\s+['"]([^'"]+)['"]/gm;var REFERENCE_TYPES_PATTERN=/^\/\/\/\s*<reference\s+types\s*=\s*['"]([^'"]+)['"]\s*\/>/gm;var REFERENCE_PATH_PATTERN=/^\/\/\/\s*<reference\s+path\s*=\s*['"]([^'"]+)['"]\s*\/>/gm;var DECLARE_MODULE_PATTERN=/^declare\s+module\s+['"]([^'"]+)['"]/gm;function parseDtsImports(content){const imports=[];const externalPackages=new Set;const relativeImports=new Set;const lines=content.split("\n");for(let i=0;i<lines.length;i++){const line=lines[i];const lineNum=i+1;IMPORT_PATTERN.lastIndex=0;let match=IMPORT_PATTERN.exec(line);if(match){const specifier=match[1];imports.push({type:"import",specifier,statement:line.trim(),line:lineNum});categorizeSpecifier(specifier,externalPackages,relativeImports)}EXPORT_FROM_PATTERN.lastIndex=0;match=EXPORT_FROM_PATTERN.exec(line);if(match){const specifier=match[1];imports.push({type:"export",specifier,statement:line.trim(),line:lineNum});categorizeSpecifier(specifier,externalPackages,relativeImports)}REFERENCE_TYPES_PATTERN.lastIndex=0;match=REFERENCE_TYPES_PATTERN.exec(line);if(match){const specifier=match[1];imports.push({type:"reference",specifier,statement:line.trim(),line:lineNum});externalPackages.add(getPackageFromSpecifier(specifier))}REFERENCE_PATH_PATTERN.lastIndex=0;match=REFERENCE_PATH_PATTERN.exec(line);if(match){const specifier=match[1];imports.push({type:"reference",specifier,statement:line.trim(),line:lineNum});relativeImports.add(specifier)}DECLARE_MODULE_PATTERN.lastIndex=0;match=DECLARE_MODULE_PATTERN.exec(line);if(match){const specifier=match[1];imports.push({type:"declare-module",specifier,statement:line.trim(),line:lineNum})}}return{imports,externalPackages:Array.from(externalPackages),relativeImports:Array.from(relativeImports)}}function categorizeSpecifier(specifier,externalPackages,relativeImports){if(isRelativeImport(specifier)){relativeImports.add(specifier)}else{externalPackages.add(getPackageFromSpecifier(specifier))}}function isRelativeImport(specifier){return specifier.startsWith("./")||specifier.startsWith("../")||specifier.startsWith("/")||/^[A-Za-z]:/.test(specifier)}function getPackageFromSpecifier(specifier){if(specifier.startsWith("@")){const parts=specifier.split("/");if(parts.length>=2){return`${parts[0]}/${parts[1]}`}return specifier}const slashIndex=specifier.indexOf("/");if(slashIndex>0){return specifier.substring(0,slashIndex)}return specifier}function getSubpathFromSpecifier(specifier){const packageName=getPackageFromSpecifier(specifier);if(specifier.length>packageName.length+1){return specifier.substring(packageName.length+1)}return void 0}var MAX_IMPORT_STATEMENT_LENGTH=2e3;function parseImportStatement(statement){if(statement.length>MAX_IMPORT_STATEMENT_LENGTH){return null}const namedMatch=/import\s+(?:\{[^}]*\}|\*\s+as\s+\w+|\w+)\s+from\s+['"]([^'"]+)['"]/.exec(statement);if(namedMatch){return namedMatch[1]}const sideEffectMatch=/import\s+['"]([^'"]+)['"]/.exec(statement);if(sideEffectMatch){return sideEffectMatch[1]}return null}function combineDtsContents(contents){const seenReferences=new Set;const references=[];const declarations=[];for(const[url,content]of contents.entries()){const lines=content.split("\n");let inReferences=true;for(const line of lines){const trimmed=line.trim();if(trimmed.startsWith("///")){if(!seenReferences.has(trimmed)){seenReferences.add(trimmed);references.push(trimmed)}continue}if(inReferences&&trimmed===""){continue}inReferences=false;if(declarations.length===0||declarations[declarations.length-1]!==""){if(trimmed!==""){if(!declarations.some(d=>d.includes(`// Source: ${url}`))){declarations.push(`
|
|
1201
|
+
// Source: ${url}`)}}}declarations.push(line)}}const parts=[];if(references.length>0){parts.push(references.join("\n"));parts.push("")}parts.push(declarations.join("\n").trim());return parts.join("\n")}var Semaphore=class{permits;waiting=[];constructor(permits){this.permits=permits}async acquire(){if(this.permits>0){this.permits--;return}return new Promise(resolve=>{this.waiting.push(resolve)})}release(){if(this.waiting.length>0){const next=this.waiting.shift();next?.()}else{this.permits++}}};var TypeFetcher=class{maxDepth;timeout;maxConcurrency;cdnBaseUrl;fetchFn;cache;allowedPatterns;allowlistEnabled;_initialized=false;constructor(options={},cache){this.maxDepth=options.maxDepth??2;this.timeout=options.timeout??1e4;this.maxConcurrency=options.maxConcurrency??5;this.cdnBaseUrl=options.cdnBaseUrl??"https://esm.sh";this.fetchFn=options.fetch??globalThis.fetch.bind(globalThis);this.cache=cache??globalTypeCache;if(options.allowedPackages===false){this.allowlistEnabled=false;this.allowedPatterns=[]}else{this.allowlistEnabled=true;this.allowedPatterns=[...DEFAULT_ALLOWED_PACKAGES,...options.allowedPackages??[]]}}isPackageAllowed(packageName){if(!this.allowlistEnabled){return true}return this.allowedPatterns.some(pattern=>{if(pattern.includes("*")){const escaped=pattern.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*");const regex=new RegExp(`^${escaped}$`);return regex.test(packageName)}return packageName===pattern||packageName.startsWith(pattern+"/")})}async initialize(options){if(this._initialized){const stats2=await this.cache.getStats();return{loaded:[],failed:[],cached:stats2.entries}}const loaded=[];const failed=[];const packagesToLoad=this.allowedPatterns.filter(p=>!p.includes("*"));for(const pkg of packagesToLoad){const result=await this.fetchBatch({imports:[`import * from "${pkg}"`],timeout:options?.timeout??this.timeout});if(result.results.length>0){loaded.push(pkg)}else if(result.errors.length>0){failed.push(pkg)}}this._initialized=true;const stats=await this.cache.getStats();return{loaded,failed,cached:stats.entries}}get initialized(){return this._initialized}get allowedPackagePatterns(){return this.allowedPatterns}async fetchBatch(request){const startTime=Date.now();const results=[];const errors=[];let cacheHits=0;let networkRequests=0;const maxDepth=request.maxDepth??this.maxDepth;const timeout=request.timeout??this.timeout;const maxConcurrency=request.maxConcurrency??this.maxConcurrency;const skipCache=request.skipCache??false;const versionOverrides=request.versionOverrides??{};const semaphore=new Semaphore(maxConcurrency);const promises=request.imports.map(async importStatement=>{await semaphore.acquire();try{const specifier=parseImportStatement(importStatement);if(!specifier){errors.push({specifier:importStatement,code:"INVALID_SPECIFIER",message:`Could not parse import statement: ${importStatement}`});return}const packageName=getPackageFromSpecifier(specifier);if(!this.isPackageAllowed(packageName)){errors.push({specifier,code:"PACKAGE_NOT_ALLOWED",message:`Package "${packageName}" is not in the allowlist`});return}const version=versionOverrides[packageName]??"latest";const cacheKey=`${TYPE_CACHE_PREFIX}${packageName}@${version}`;if(!skipCache){const cached=await this.cache.get(cacheKey);if(cached){cacheHits++;results.push(cached.result);return}}const result=await this.fetchTypesForSpecifier(specifier,{maxDepth,timeout,version});if(result.success){results.push(result.data);networkRequests+=result.fetchCount;const entry={result:result.data,cachedAt:Date.now(),size:result.data.content.length,accessCount:1};await this.cache.set(cacheKey,entry)}else{errors.push(result.error);networkRequests+=result.fetchCount}}finally{semaphore.release()}});await Promise.all(promises);return{results,errors,totalTimeMs:Date.now()-startTime,cacheHits,networkRequests}}async fetchTypesForSpecifier(specifier,options){let fetchCount=0;const fetchedUrls=[];const visitedUrls=new Set;const contents=new Map;try{const resolution=await this.resolvePackage(specifier,options.version,options.timeout);if(!resolution){return{success:false,error:{specifier,code:"PACKAGE_NOT_FOUND",message:`Could not resolve package: ${specifier}`},fetchCount}}fetchCount++;const fetchResult=await this.fetchRecursive(resolution.typesUrl,options.maxDepth,options.timeout,visitedUrls,contents);fetchCount+=fetchResult.fetchCount;fetchedUrls.push(...fetchResult.fetchedUrls);if(!fetchResult.success){return{success:false,error:{specifier,code:fetchResult.errorCode,message:fetchResult.errorMessage??"Unknown error",url:fetchResult.errorUrl},fetchCount}}const files=buildTypeFiles(contents,resolution.packageName,resolution.version,resolution.subpath);const combinedContent=combineDtsContents(contents);const result={specifier,resolvedPackage:resolution.packageName,subpath:resolution.subpath,version:resolution.version,content:combinedContent,files,fetchedUrls,fetchedAt:new Date().toISOString()};return{success:true,data:result,fetchCount}}catch(error){return{success:false,error:{specifier,code:"NETWORK_ERROR",message:error instanceof Error?error.message:String(error)},fetchCount}}}async resolvePackage(specifier,version,timeout){const packageName=getPackageFromSpecifier(specifier);const subpath=getSubpathFromSpecifier(specifier);const versionSuffix=version==="latest"?"":`@${version}`;const baseUrl=`${this.cdnBaseUrl}/${packageName}${versionSuffix}`;const urlsToTry=subpath?[`${baseUrl}/${subpath}`,baseUrl]:[baseUrl];for(const url of urlsToTry){try{const response=await this.fetchWithTimeout(url,timeout,"HEAD");if(response.ok){const typesHeader=response.headers.get("X-TypeScript-Types");if(typesHeader){const typesUrl=typesHeader.startsWith("/")?`${this.cdnBaseUrl}${typesHeader}`:typesHeader;const versionMatch=/@(\d+\.\d+\.\d+[^/]*)/.exec(typesUrl);const resolvedVersion=versionMatch?versionMatch[1]:version;return{packageName,subpath,version:resolvedVersion,typesUrl}}}}catch{continue}}return null}async fetchRecursive(url,depth,timeout,visited,contents){if(visited.has(url)){return{success:true,fetchCount:0,fetchedUrls:[]}}visited.add(url);let fetchCount=1;const fetchedUrls=[url];try{const response=await this.fetchWithTimeout(url,timeout,"GET");if(!response.ok){return{success:false,fetchCount,fetchedUrls,errorCode:"NETWORK_ERROR",errorMessage:`HTTP ${response.status}: ${response.statusText}`,errorUrl:url}}const content=await response.text();contents.set(url,content);if(depth<=0){return{success:true,fetchCount,fetchedUrls}}const parsed=parseDtsImports(content);for(const pkg of parsed.externalPackages){if(pkg.startsWith("@types/")||isBuiltinModule(pkg)){continue}const resolution=await this.resolvePackage(pkg,"latest",timeout);fetchCount++;if(resolution&&!visited.has(resolution.typesUrl)){const result=await this.fetchRecursive(resolution.typesUrl,depth-1,timeout,visited,contents);fetchCount+=result.fetchCount;fetchedUrls.push(...result.fetchedUrls)}}for(const relativePath of parsed.relativeImports){const resolvedUrl=resolveRelativeUrl(url,relativePath);if(resolvedUrl&&!visited.has(resolvedUrl)){const result=await this.fetchRecursive(resolvedUrl,depth-1,timeout,visited,contents);fetchCount+=result.fetchCount;fetchedUrls.push(...result.fetchedUrls)}}return{success:true,fetchCount,fetchedUrls}}catch(error){const isTimeout=error instanceof Error&&error.message.includes("timeout");return{success:false,fetchCount,fetchedUrls,errorCode:isTimeout?"TIMEOUT":"NETWORK_ERROR",errorMessage:error instanceof Error?error.message:String(error),errorUrl:url}}}async fetchWithTimeout(url,timeout,method){const controller=new AbortController;const timeoutId=setTimeout(()=>controller.abort(),timeout);try{const response=await this.fetchFn(url,{method,signal:controller.signal,headers:{Accept:"application/typescript, text/plain, */*"}});return response}finally{clearTimeout(timeoutId)}}};function isBuiltinModule(name){const builtins=new Set(["assert","buffer","child_process","cluster","console","constants","crypto","dgram","dns","domain","events","fs","http","https","module","net","os","path","punycode","querystring","readline","repl","stream","string_decoder","sys","timers","tls","tty","url","util","v8","vm","zlib","node:assert","node:buffer","node:child_process","node:cluster","node:console","node:constants","node:crypto","node:dgram","node:dns","node:domain","node:events","node:fs","node:http","node:https","node:module","node:net","node:os","node:path","node:punycode","node:querystring","node:readline","node:repl","node:stream","node:string_decoder","node:sys","node:timers","node:tls","node:tty","node:url","node:util","node:v8","node:vm","node:zlib"]);return builtins.has(name)}function resolveRelativeUrl(base,relative){try{let path=relative;if(!path.endsWith(".d.ts")&&!path.endsWith(".ts")){path=`${path}.d.ts`}const resolved=new URL(path,base);return resolved.toString()}catch{return null}}function buildTypeFiles(contents,packageName,version,subpath){const files=[];for(const[url,content]of contents.entries()){const virtualPath=urlToVirtualPath(url,packageName,version);files.push({path:virtualPath,url,content})}if(subpath){const aliasPath=`node_modules/${packageName}/${subpath}/index.d.ts`;const aliasExists=files.some(f=>f.path===aliasPath);if(!aliasExists){const aliasContent=`// Auto-generated alias for ${packageName}/${subpath}
|
|
1202
|
+
export * from '${getRelativeImportPath(subpath)}';
|
|
1203
|
+
`;files.push({path:aliasPath,url:"",content:aliasContent})}}return files}function getRelativeImportPath(subpath){const depth=subpath.split("/").length;const prefix="../".repeat(depth);return`${prefix}index`}function urlToVirtualPath(url,packageName,version){try{const urlObj=new URL(url);const pathname=urlObj.pathname;const cleanPath=pathname.replace(/^\/v\d+\//,"/");const versionedPackage=`${packageName}@${version}`;const packageIndex=cleanPath.indexOf(versionedPackage);if(packageIndex!==-1){const afterPackageVersion=cleanPath.substring(packageIndex+versionedPackage.length);const relativePath=afterPackageVersion.startsWith("/")?afterPackageVersion.substring(1):afterPackageVersion;return`node_modules/${packageName}/${relativePath||"index.d.ts"}`}const packagePattern=new RegExp(`/${escapeRegExp(packageName)}@[^/]+(/.*)?$`);const match=pathname.match(packagePattern);if(match){const filePath=match[1]?match[1].substring(1):"index.d.ts";return`node_modules/${packageName}/${filePath}`}return`node_modules/${packageName}/${pathname.split("/").pop()||"index.d.ts"}`}catch{return`node_modules/${packageName}/index.d.ts`}}function escapeRegExp(string){return string.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function createTypeFetcher(options,cache){return new TypeFetcher(options,cache)}var DEFAULT_CDN_REGISTRY={react:{packageName:"react",defaultVersion:"19.2.4",providers:{cloudflare:{url:"https://cdnjs.cloudflare.com/ajax/libs/react/19.2.4/umd/react.production.min.js",global:"React",crossorigin:"anonymous"},jsdelivr:{url:"https://cdn.jsdelivr.net/npm/react@19.2.4/umd/react.production.min.js",global:"React",crossorigin:"anonymous"},unpkg:{url:"https://unpkg.com/react@19.2.4/umd/react.production.min.js",global:"React",crossorigin:"anonymous"},"esm.sh":{url:"https://esm.sh/react@19.2.4",esm:true,crossorigin:"anonymous"}},preferredProviders:["esm.sh","cloudflare","jsdelivr","unpkg"],metadata:{description:"A JavaScript library for building user interfaces",homepage:"https://react.dev",license:"MIT"}},"react-dom":{packageName:"react-dom",defaultVersion:"19.2.4",providers:{cloudflare:{url:"https://cdnjs.cloudflare.com/ajax/libs/react-dom/19.2.4/umd/react-dom.production.min.js",global:"ReactDOM",crossorigin:"anonymous",peerDependencies:["react"]},jsdelivr:{url:"https://cdn.jsdelivr.net/npm/react-dom@19.2.4/umd/react-dom.production.min.js",global:"ReactDOM",crossorigin:"anonymous",peerDependencies:["react"]},unpkg:{url:"https://unpkg.com/react-dom@19.2.4/umd/react-dom.production.min.js",global:"ReactDOM",crossorigin:"anonymous",peerDependencies:["react"]},"esm.sh":{url:"https://esm.sh/react-dom@19.2.4",esm:true,crossorigin:"anonymous",peerDependencies:["react"]}},preferredProviders:["esm.sh","cloudflare","jsdelivr","unpkg"],metadata:{description:"React package for working with the DOM",homepage:"https://react.dev",license:"MIT"}},"chart.js":{packageName:"chart.js",defaultVersion:"4.5.1",providers:{cloudflare:{url:"https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.5.1/chart.umd.min.js",global:"Chart",crossorigin:"anonymous"},jsdelivr:{url:"https://cdn.jsdelivr.net/npm/chart.js@4.5.1/dist/chart.umd.min.js",global:"Chart",crossorigin:"anonymous"},unpkg:{url:"https://unpkg.com/chart.js@4.5.1/dist/chart.umd.min.js",global:"Chart",crossorigin:"anonymous"}},preferredProviders:["esm.sh","cloudflare","jsdelivr","unpkg"],metadata:{description:"Simple yet flexible JavaScript charting library",homepage:"https://www.chartjs.org",license:"MIT"}},d3:{packageName:"d3",defaultVersion:"7.9.0",providers:{cloudflare:{url:"https://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.min.js",integrity:"sha512-vc58qvvBdrDR4etbxMdlTt4GBQk1qjvyORR2nrsPsFPyrs+/u5c3+1Ct6upOgdZoIl7eq6k3a1UPDSNAQi/32A==",global:"d3",crossorigin:"anonymous"},jsdelivr:{url:"https://cdn.jsdelivr.net/npm/d3@7.9.0/dist/d3.min.js",global:"d3",crossorigin:"anonymous"},unpkg:{url:"https://unpkg.com/d3@7.9.0/dist/d3.min.js",global:"d3",crossorigin:"anonymous"}},preferredProviders:["esm.sh","cloudflare","jsdelivr","unpkg"],metadata:{description:"Data-Driven Documents",homepage:"https://d3js.org",license:"ISC"}},lodash:{packageName:"lodash",defaultVersion:"4.17.21",providers:{cloudflare:{url:"https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js",integrity:"sha512-WFN04846sdKMIP5LKNphMaWzU7YpMyCU245etK3g/2ARYbPK9Ub18eG+ljU96qKRCWh+quCY7yefSmlkQw1ANQ==",global:"_",crossorigin:"anonymous"},jsdelivr:{url:"https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js",global:"_",crossorigin:"anonymous"},unpkg:{url:"https://unpkg.com/lodash@4.17.21/lodash.min.js",global:"_",crossorigin:"anonymous"}},preferredProviders:["esm.sh","cloudflare","jsdelivr","unpkg"],metadata:{description:"A modern JavaScript utility library",homepage:"https://lodash.com",license:"MIT"}}};function lookupPackage(packageName,registry=DEFAULT_CDN_REGISTRY){return registry[packageName]}function getPackageCDNUrl(packageName,providerOrder=["esm.sh","cloudflare","jsdelivr","unpkg"],registry=DEFAULT_CDN_REGISTRY){const entry=lookupPackage(packageName,registry);if(!entry)return void 0;for(const provider of providerOrder){const config=entry.providers[provider];if(config?.url){return config.url}}for(const provider of Object.keys(entry.providers)){const config=entry.providers[provider];if(config?.url){return config.url}}return void 0}function getRegisteredPackages(registry=DEFAULT_CDN_REGISTRY){return Object.keys(registry)}function isPackageRegistered(packageName,registry=DEFAULT_CDN_REGISTRY){return packageName in registry}function mergeRegistries(customRegistry){return{...DEFAULT_CDN_REGISTRY,...customRegistry}}function getPackagePeerDependencies(packageName,registry=DEFAULT_CDN_REGISTRY){const entry=lookupPackage(packageName,registry);if(!entry)return[];for(const provider of Object.keys(entry.providers)){const config=entry.providers[provider];if(config?.peerDependencies){return config.peerDependencies}}return[]}var IMPORT_PATTERNS={named:/import\s*\{([^}]+)\}\s*from\s*['"]([^'"]+)['"]/g,default:/import\s+(\w+)\s+from\s*['"]([^'"]+)['"]/g,namespace:/import\s*\*\s*as\s+(\w+)\s+from\s*['"]([^'"]+)['"]/g,sideEffect:/import\s*['"]([^'"]+)['"]/g,dynamic:/(?:await\s+)?import\s*\(\s*['"]([^'"]+)['"]\s*\)/g,defaultAndNamed:/import\s+(\w+)\s*,\s*\{([^}]+)\}\s*from\s*['"]([^'"]+)['"]/g,defaultAndNamespace:/import\s+(\w+)\s*,\s*\*\s*as\s+(\w+)\s+from\s*['"]([^'"]+)['"]/g,reExport:/export\s*\{[^}]+\}\s*from\s*['"]([^'"]+)['"]/g,reExportAll:/export\s*\*\s*from\s*['"]([^'"]+)['"]/g};function parseNamedImports(namedString){return namedString.split(",").map(s=>s.trim()).filter(s=>s.length>0).map(s=>{const asMatch=s.match(/^(\w+)\s+as\s+\w+$/);return asMatch?asMatch[1]:s})}function isRelativeImport2(specifier){return specifier.startsWith("./")||specifier.startsWith("../")||specifier.startsWith("/")}function isExternalImport(specifier){return!isRelativeImport2(specifier)&&!specifier.startsWith("#")}function getPackageName(specifier){if(specifier.startsWith("@")){const parts=specifier.split("/");if(parts.length>=2){return`${parts[0]}/${parts[1]}`}return specifier}const firstSlash=specifier.indexOf("/");return firstSlash===-1?specifier:specifier.slice(0,firstSlash)}function getLineNumber(source,index){let line=1;for(let i=0;i<index&&i<source.length;i++){if(source[i]==="\n"){line++}}return line}function getColumnNumber(source,index){let column=0;for(let i=index-1;i>=0&&source[i]!=="\n";i--){column++}return column}function parseImports(source){const imports=[];const seenStatements=new Set;const addImport=imp=>{const key=`${imp.type}:${imp.specifier}:${imp.statement}`;if(!seenStatements.has(key)){seenStatements.add(key);imports.push(imp)}};let match;const defaultAndNamedRegex=new RegExp(IMPORT_PATTERNS.defaultAndNamed.source,"g");while((match=defaultAndNamedRegex.exec(source))!==null){const[statement,defaultName,namedString,specifier]=match;addImport({statement,specifier,type:"default",defaultImport:defaultName,namedImports:parseNamedImports(namedString),line:getLineNumber(source,match.index),column:getColumnNumber(source,match.index)})}const defaultAndNamespaceRegex=new RegExp(IMPORT_PATTERNS.defaultAndNamespace.source,"g");while((match=defaultAndNamespaceRegex.exec(source))!==null){const[statement,defaultName,namespaceName,specifier]=match;addImport({statement,specifier,type:"default",defaultImport:defaultName,namespaceImport:namespaceName,line:getLineNumber(source,match.index),column:getColumnNumber(source,match.index)})}const namedRegex=new RegExp(IMPORT_PATTERNS.named.source,"g");while((match=namedRegex.exec(source))!==null){const[statement,namedString,specifier]=match;addImport({statement,specifier,type:"named",namedImports:parseNamedImports(namedString),line:getLineNumber(source,match.index),column:getColumnNumber(source,match.index)})}const defaultRegex=new RegExp(IMPORT_PATTERNS.default.source,"g");while((match=defaultRegex.exec(source))!==null){const[statement,defaultName,specifier]=match;const afterMatch=source.slice(match.index+match[0].length-specifier.length-2);if(afterMatch.startsWith(","))continue;addImport({statement,specifier,type:"default",defaultImport:defaultName,line:getLineNumber(source,match.index),column:getColumnNumber(source,match.index)})}const namespaceRegex=new RegExp(IMPORT_PATTERNS.namespace.source,"g");while((match=namespaceRegex.exec(source))!==null){const[statement,namespaceName,specifier]=match;addImport({statement,specifier,type:"namespace",namespaceImport:namespaceName,line:getLineNumber(source,match.index),column:getColumnNumber(source,match.index)})}const sideEffectRegex=new RegExp(IMPORT_PATTERNS.sideEffect.source,"g");while((match=sideEffectRegex.exec(source))!==null){const[statement,specifier]=match;const beforeMatch=source.slice(Math.max(0,match.index-50),match.index);if(beforeMatch.includes("from"))continue;addImport({statement,specifier,type:"side-effect",line:getLineNumber(source,match.index),column:getColumnNumber(source,match.index)})}const dynamicRegex=new RegExp(IMPORT_PATTERNS.dynamic.source,"g");while((match=dynamicRegex.exec(source))!==null){const[statement,specifier]=match;addImport({statement,specifier,type:"dynamic",line:getLineNumber(source,match.index),column:getColumnNumber(source,match.index)})}const reExportRegex=new RegExp(IMPORT_PATTERNS.reExport.source,"g");while((match=reExportRegex.exec(source))!==null){const[statement,specifier]=match;addImport({statement,specifier,type:"named",line:getLineNumber(source,match.index),column:getColumnNumber(source,match.index)})}const reExportAllRegex=new RegExp(IMPORT_PATTERNS.reExportAll.source,"g");while((match=reExportAllRegex.exec(source))!==null){const[statement,specifier]=match;addImport({statement,specifier,type:"namespace",line:getLineNumber(source,match.index),column:getColumnNumber(source,match.index)})}const externalImports=imports.filter(imp=>isExternalImport(imp.specifier));const relativeImports=imports.filter(imp=>isRelativeImport2(imp.specifier));const externalPackages=[...new Set(externalImports.map(imp=>getPackageName(imp.specifier)))];return{imports,externalImports,relativeImports,externalPackages}}function extractExternalPackages(source){return parseImports(source).externalPackages}var DEFAULT_FALLBACK_CDN="https://esm.sh";function createResolverWithOverrides(overrides,baseOptions){const base=createEsmShResolver(baseOptions);return{overrides,resolve(specifier,context){if(overrides[specifier]){return{value:overrides[specifier],type:"url"}}for(const[prefix,url]of Object.entries(overrides)){if(specifier.startsWith(prefix+"/")){const subpath=specifier.slice(prefix.length);return{value:url+subpath,type:"url"}}}return base.resolve(specifier,context)}}}function createEsmShResolver(options={}){const{fallbackCdnBase=DEFAULT_FALLBACK_CDN,registry=DEFAULT_CDN_REGISTRY,providerOrder=["esm.sh","cloudflare","jsdelivr","unpkg"]}=options;return{resolve(specifier,_context){if(specifier.startsWith("./")||specifier.startsWith("../")||specifier.startsWith("/")){return null}if(specifier.startsWith("node:")||specifier.startsWith("#")){return null}const pkgName=getPackageName(specifier);const entry=lookupPackage(pkgName,registry);if(entry){for(const provider of providerOrder){const config=entry.providers[provider];if(config?.url){const subpath=specifier.slice(pkgName.length);if(subpath&&config.url.includes("esm.sh/")){return{value:config.url+subpath,type:"url",integrity:config.integrity}}if(subpath){return{value:`${fallbackCdnBase}/${specifier}`,type:"url"}}if(config.global&&!config.esm){return{value:config.url,type:"url",integrity:config.integrity}}return{value:config.url,type:"url",integrity:config.integrity}}}}return{value:`${fallbackCdnBase}/${specifier}`,type:"url"}}}}function createImportMapFromResolved(resolved){const imports={};const integrity={};for(const[specifier,res]of Object.entries(resolved)){if(res.type==="url"){imports[specifier]=res.value;if(res.integrity){integrity[res.value]=res.integrity}}}return{imports,integrity:Object.keys(integrity).length>0?integrity:void 0}}function createImportMap(dependencies){const imports={};const integrity={};for(const dep of dependencies){imports[dep.packageName]=dep.cdnUrl;if(dep.integrity){integrity[dep.cdnUrl]=dep.integrity}}return{imports,integrity:Object.keys(integrity).length>0?integrity:void 0}}function mergeImportMaps(...maps){const imports={};const integrity={};const scopes={};for(const map of maps){Object.assign(imports,map.imports);if(map.integrity){Object.assign(integrity,map.integrity)}if(map.scopes){for(const[scope,mappings]of Object.entries(map.scopes)){scopes[scope]={...scopes[scope],...mappings}}}}return{imports,integrity:Object.keys(integrity).length>0?integrity:void 0,scopes:Object.keys(scopes).length>0?scopes:void 0}}function generateImportMapScriptTag(map){const json=JSON.stringify(map,null,2).replace(/<\//g,"<\\/");return`<script type="importmap">
|
|
1204
|
+
${json}
|
|
1205
|
+
</script>`}function generateImportMapScriptTagMinified(map){const json=JSON.stringify(map).replace(/<\//g,"<\\/");return`<script type="importmap">${json}</script>`}function generateCDNScriptTags(dependencies){return dependencies.filter(dep=>!dep.esm).map(dep=>{const attrs=[`src="${escapeHtmlAttr(dep.cdnUrl)}"`];if(dep.integrity){attrs.push(`integrity="${escapeHtmlAttr(dep.integrity)}"`)}attrs.push('crossorigin="anonymous"');return`<script ${attrs.join(" ")}></script>`})}function generateDependencyHTML(dependencies,options={}){const{minify=false}=options;const parts=[];const importMap=createImportMap(dependencies);const importMapTag=minify?generateImportMapScriptTagMinified(importMap):generateImportMapScriptTag(importMap);parts.push(importMapTag);const umdTags=generateCDNScriptTags(dependencies);parts.push(...umdTags);return parts.join(minify?"":"\n")}function validateImportMap(map){const errors=[];if(typeof map!=="object"||map===null){return{valid:false,errors:["Import map must be an object"]}}const obj=map;if(!obj["imports"]){errors.push('Import map must have an "imports" field')}else if(typeof obj["imports"]!=="object"||obj["imports"]===null){errors.push('"imports" must be an object')}else{const imports=obj["imports"];for(const[key,value]of Object.entries(imports)){if(typeof value!=="string"){errors.push(`Import "${key}" must map to a string URL`)}else if(!value.startsWith("https://")){errors.push(`Import "${key}" URL must use HTTPS: ${value}`)}}}if(obj["integrity"]!==void 0){if(typeof obj["integrity"]!=="object"||obj["integrity"]===null){errors.push('"integrity" must be an object')}else{const integrity=obj["integrity"];for(const[url,hash]of Object.entries(integrity)){if(typeof hash!=="string"){errors.push(`Integrity for "${url}" must be a string`)}else if(!hash.match(/^sha(256|384|512)-/)){errors.push(`Invalid integrity hash format for "${url}": ${hash}`)}}}}return{valid:errors.length===0,errors}}function rewriteImports(source,options={}){const{resolver=createEsmShResolver(),skipPackages=[],overrides={}}=options;const skipSet=new Set(skipPackages);const rewrites=new Map;const fallbackPackages=[];const parsed=parseImports(source);const specifierMap=new Map;for(const imp of parsed.externalImports){const specifier=imp.specifier;if(specifierMap.has(specifier))continue;if(specifier.startsWith("node:"))continue;const pkgName=getPackageName(specifier);if(skipSet.has(pkgName))continue;if(overrides[specifier]){specifierMap.set(specifier,overrides[specifier]);continue}if(overrides[pkgName]){const subpath=specifier.slice(pkgName.length);specifierMap.set(specifier,overrides[pkgName]+subpath);continue}const resolved=resolver.resolve(specifier);if(resolved&&resolved.type==="url"){specifierMap.set(specifier,resolved.value)}}let code=source;for(const[specifier,cdnUrl]of specifierMap){const escaped=escapeRegExp2(specifier);const singleQuotePattern=new RegExp(`((?:import|export)\\s+(?:[^'"]*\\s+)?from\\s+)'${escaped}'`,"g");const doubleQuotePattern=new RegExp(`((?:import|export)\\s+(?:[^'"]*\\s+)?from\\s+)"${escaped}"`,"g");const dynamicSinglePattern=new RegExp(`(import\\s*\\(\\s*)'${escaped}'`,"g");const dynamicDoublePattern=new RegExp(`(import\\s*\\(\\s*)"${escaped}"`,"g");const sideEffectSinglePattern=new RegExp(`(import\\s+)'${escaped}'`,"g");const sideEffectDoublePattern=new RegExp(`(import\\s+)"${escaped}"`,"g");const beforeLength=code.length;code=code.replace(singleQuotePattern,`$1'${cdnUrl}'`).replace(doubleQuotePattern,`$1"${cdnUrl}"`).replace(dynamicSinglePattern,`$1'${cdnUrl}'`).replace(dynamicDoublePattern,`$1"${cdnUrl}"`).replace(sideEffectSinglePattern,`$1'${cdnUrl}'`).replace(sideEffectDoublePattern,`$1"${cdnUrl}"`);if(code.length!==beforeLength||code!==source){rewrites.set(specifier,cdnUrl)}}return{code,rewrittenCount:rewrites.size,rewrites,fallbackPackages}}function escapeRegExp2(str){return str.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}var DEFAULT_CDN_DOMAINS=["https://cdn.jsdelivr.net","https://cdnjs.cloudflare.com","https://fonts.googleapis.com","https://fonts.gstatic.com","https://esm.sh"];var DEFAULT_CSP_DIRECTIVES=["default-src 'none'",`script-src 'self' 'unsafe-inline' ${DEFAULT_CDN_DOMAINS.join(" ")}`,`style-src 'self' 'unsafe-inline' ${DEFAULT_CDN_DOMAINS.join(" ")}`,`img-src 'self' data: ${DEFAULT_CDN_DOMAINS.join(" ")}`,`font-src 'self' data: ${DEFAULT_CDN_DOMAINS.join(" ")}`,`connect-src ${DEFAULT_CDN_DOMAINS.join(" ")}`,"object-src 'self' data:"];var RESTRICTIVE_CSP_DIRECTIVES=["default-src 'none'","script-src 'self' 'unsafe-inline'","style-src 'self' 'unsafe-inline'","img-src 'self' data:","font-src 'self' data:","connect-src 'none'","object-src 'self' data:"];function buildCSPDirectives(csp){if(!csp){return[...DEFAULT_CSP_DIRECTIVES]}const validResourceDomains=sanitizeCSPDomains(csp.resourceDomains);const validConnectDomains=sanitizeCSPDomains(csp.connectDomains);const allResourceDomains=[...new Set([...DEFAULT_CDN_DOMAINS,...validResourceDomains])];const directives=["default-src 'none'",`script-src 'self' 'unsafe-inline' ${allResourceDomains.join(" ")}`,`style-src 'self' 'unsafe-inline' ${allResourceDomains.join(" ")}`];const imgSources=["'self'","data:",...allResourceDomains];directives.push(`img-src ${imgSources.join(" ")}`);const fontSources=["'self'","data:",...allResourceDomains];directives.push(`font-src ${fontSources.join(" ")}`);const connectSources=[...new Set([...allResourceDomains,...validConnectDomains])];directives.push(`connect-src ${connectSources.join(" ")}`);directives.push("object-src 'self' data:");return directives}function buildCSPMetaTag(csp){const directives=buildCSPDirectives(csp);const content=directives.join("; ");return`<meta http-equiv="Content-Security-Policy" content="${escapeAttribute(content)}">`}var LOOPBACK_HOSTS=new Set(["localhost","127.0.0.1","[::1]"]);function validateCSPDomain(domain){if(typeof domain!=="string"||/[\s;,'"]/.test(domain))return false;if(/[?#]/.test(domain))return false;if(domain.includes("*")){const wildcard=/^(?:https|wss):\/\/\*\.(.*)$/.exec(domain);return wildcard!==null&&/^([a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/.test(wildcard[1])}try{const url=new URL(domain);if(url.protocol==="https:"||url.protocol==="wss:")return true;return(url.protocol==="http:"||url.protocol==="ws:")&&LOOPBACK_HOSTS.has(url.hostname)}catch{return false}}var reportedInvalidDomains=new Set;function sanitizeCSPDomains(domains){if(!domains)return[];const valid=[];for(const domain of domains){if(validateCSPDomain(domain)){valid.push(domain)}else if(!reportedInvalidDomains.has(domain)){reportedInvalidDomains.add(domain);console.warn(`Invalid CSP domain ignored: ${domain} (expected an https:// or wss:// origin, or http:// / ws:// on localhost)`)}}return valid}function escapeAttribute(str){return str.replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(/</g,"<").replace(/>/g,">")}var SHELL_PLACEHOLDER_NAMES=["CSP","DATA","BRIDGE","CONTENT","TITLE"];var SHELL_PLACEHOLDERS={CSP:"{{CSP}}",DATA:"{{DATA}}",BRIDGE:"{{BRIDGE}}",CONTENT:"{{CONTENT}}",TITLE:"{{TITLE}}"};var REQUIRED_PLACEHOLDERS=["CONTENT"];var OPTIONAL_PLACEHOLDERS=["CSP","DATA","BRIDGE","TITLE"];function isInlineShellSource(source){return typeof source==="object"&&source!==null&&"inline"in source}function isUrlShellSource(source){return typeof source==="object"&&source!==null&&"url"in source}function isNpmShellSource(source){return typeof source==="object"&&source!==null&&"npm"in source}var PLACEHOLDER_PATTERN=new RegExp(`\\{\\{(${SHELL_PLACEHOLDER_NAMES.join("|")})\\}\\}`,"g");var VALUE_KEYS={CSP:"csp",DATA:"data",BRIDGE:"bridge",CONTENT:"content",TITLE:"title"};function applyShellTemplate(template,values){return template.replace(PLACEHOLDER_PATTERN,(_token,name)=>values[VALUE_KEYS[name]])}function validateShellTemplate(template){const found={};for(const name of SHELL_PLACEHOLDER_NAMES){found[name]=template.includes(SHELL_PLACEHOLDERS[name])}const missingRequired=REQUIRED_PLACEHOLDERS.filter(name=>!found[name]);const missingOptional=OPTIONAL_PLACEHOLDERS.filter(name=>!found[name]);return{valid:missingRequired.length===0,found,missingRequired,missingOptional}}var TRUSTED_HTML=Symbol.for("@frontmcp/uipack/trusted-html");function trustedHtml(markup){const value=String(markup);return Object.freeze({[TRUSTED_HTML]:value,toString:()=>value})}function isTrustedHtml(value){return typeof value==="object"&&value!==null&&typeof value[TRUSTED_HTML]==="string"}function interpolate(value){if(isTrustedHtml(value))return value[TRUSTED_HTML];if(Array.isArray(value))return value.map(interpolate).join("");if(value===false)return"";return escapeHtml(value)}function html(strings,...values){let markup=strings[0];for(let index=0;index<values.length;index++){markup+=interpolate(values[index])+strings[index+1]}return trustedHtml(markup)}function hasSizing(sizing){if(!sizing)return false;return sizing.preferredHeight!==void 0||sizing.minHeight!==void 0||sizing.maxHeight!==void 0||sizing.aspectRatio!==void 0||sizing.autoResize!==void 0}function buildDataInjectionScript(options){const{toolName,input,output,structuredContent,sizing,displayMode}=options;const lines=[`window.__mcpAppsEnabled = true;`,`window.__mcpToolName = ${safeJsonForScript(toolName)};`,`window.__mcpToolInput = ${safeJsonForScript(input??null)};`,`window.__mcpToolOutput = ${safeJsonForScript(output??null)};`,`window.__mcpStructuredContent = ${safeJsonForScript(structuredContent??null)};`];if(hasSizing(sizing)){lines.push(`window.__mcpWidgetSizing = ${safeJsonForScript(sizing)};`)}if(displayMode&&displayMode!=="inline"){lines.push(`window.__mcpDisplayMode = ${safeJsonForScript(displayMode)};`)}return`<script>
|
|
1206
|
+
${lines.join("\n")}
|
|
1207
|
+
</script>`}function buildCustomDataInjectionScript(descriptor){if(descriptor.script!==void 0){return descriptor.script}if(descriptor.globalKey!==void 0){return`<script>window[${safeJsonForScript(descriptor.globalKey)}] = ${safeJsonForScript(descriptor.value??null)};</script>`}return""}var _uniqueIdCounter=0;function createTemplateHelpers(){return{escapeHtml:str=>escapeHtml(str),formatDate:(date,format)=>{const d=date instanceof Date?date:new Date(date);if(isNaN(d.getTime()))return String(date);if(format==="iso")return d.toISOString();if(format==="date")return d.toLocaleDateString();if(format==="time")return d.toLocaleTimeString();return d.toLocaleString()},formatCurrency:(amount,currency="USD")=>{return new Intl.NumberFormat("en-US",{style:"currency",currency}).format(amount)},uniqueId:(prefix="mcp")=>{return`${prefix}-${++_uniqueIdCounter}`},jsonEmbed:data=>safeJsonForScript(data),html,trustedHtml}}function sanitizeCssValue(value){return value.replace(/[<>{};]/g,"").trim()}function toCssLength(value){return typeof value==="number"?`${value}px`:sanitizeCssValue(value)}function buildSizingStyleTag(sizing){if(!hasSizing(sizing))return"";const hasCssSizing=sizing.preferredHeight!==void 0||sizing.minHeight!==void 0||sizing.maxHeight!==void 0||sizing.aspectRatio!==void 0;if(!hasCssSizing)return"";const rootRules=[];const docRules=["margin: 0;"];if(sizing.preferredHeight!==void 0){const h=toCssLength(sizing.preferredHeight);docRules.push(`height: ${h};`);rootRules.push(`min-height: ${h};`)}if(sizing.minHeight!==void 0){const mh=toCssLength(sizing.minHeight);docRules.push(`min-height: ${mh};`);rootRules.push(`min-height: ${mh};`)}if(sizing.maxHeight!==void 0){const mx=toCssLength(sizing.maxHeight);docRules.push(`max-height: ${mx};`);rootRules.push(`max-height: ${mx};`)}if(sizing.aspectRatio!==void 0){const ar=typeof sizing.aspectRatio==="number"?String(sizing.aspectRatio):sanitizeCssValue(sizing.aspectRatio);if(ar)rootRules.push(`aspect-ratio: ${ar};`)}const parts=[`html, body { ${docRules.join(" ")} }`];if(rootRules.length>0){parts.push(`#root { ${rootRules.join(" ")} }`)}return`<style>${parts.join("\n")}</style>`}function resolveDataInjectionScript(args){if(args.dataInjection){return buildCustomDataInjectionScript(args.dataInjection)}return buildDataInjectionScript({toolName:args.toolName,input:args.input,output:args.output,structuredContent:args.structuredContent,sizing:args.sizing,displayMode:args.displayMode})}function buildShell(content,config){const{toolName,csp,withShell=true,input,output,structuredContent,includeBridge=true,title,sizing,displayMode}=config;const{customShell,dataInjection}=config;const dataScript=resolveDataInjectionScript({dataInjection,toolName,input,output,structuredContent,sizing,displayMode});if(!withShell){const html3=`${dataScript}
|
|
1208
|
+
${content}`;return{html:html3,hash:simpleHash(html3),size:Buffer.byteLength(html3,"utf-8")}}if(customShell){return buildCustomShell(content,customShell,{csp,toolName,input,output,structuredContent,includeBridge,title,sizing,displayMode,dataInjection})}const headParts=['<meta charset="UTF-8">','<meta name="viewport" content="width=device-width, initial-scale=1.0">'];if(title){headParts.push(`<title>${escapeHtmlForTag(title)}</title>`)}headParts.push(buildCSPMetaTag(csp));headParts.push(dataScript);const sizingStyle=buildSizingStyleTag(sizing);if(sizingStyle){headParts.push(sizingStyle)}if(includeBridge){const bridgeScript=generateBridgeIIFE({minify:true});headParts.push(`<script>${bridgeScript}</script>`)}const html2=`<!DOCTYPE html>
|
|
1209
|
+
<html lang="en">
|
|
1210
|
+
<head>
|
|
1211
|
+
${headParts.map(p=>` ${p}`).join("\n")}
|
|
1212
|
+
</head>
|
|
1213
|
+
<body>
|
|
1214
|
+
${content}
|
|
1215
|
+
</body>
|
|
1216
|
+
</html>`;return{html:html2,hash:simpleHash(html2),size:Buffer.byteLength(html2,"utf-8")}}function buildCustomShell(content,customShell,ctx){let template;if(typeof customShell==="string"){const validation=validateShellTemplate(customShell);if(!validation.valid){throw new Error(`Custom shell template is missing required placeholder(s): ${validation.missingRequired.map(n=>`{{${n}}}`).join(", ")}`)}template=customShell}else{template=customShell.template}const cspTag=buildCSPMetaTag(ctx.csp);const dataScript=resolveDataInjectionScript({dataInjection:ctx.dataInjection,toolName:ctx.toolName,input:ctx.input,output:ctx.output,structuredContent:ctx.structuredContent,sizing:ctx.sizing,displayMode:ctx.displayMode});const sizingStyle=buildSizingStyleTag(ctx.sizing);const dataWithSizing=sizingStyle?`${dataScript}
|
|
1217
|
+
${sizingStyle}`:dataScript;let bridgeHtml="";if(ctx.includeBridge){const bridgeScript=generateBridgeIIFE({minify:true});bridgeHtml=`<script>${bridgeScript}</script>`}const html2=applyShellTemplate(template,{csp:cspTag,data:dataWithSizing,bridge:bridgeHtml,content,title:ctx.title?escapeHtmlForTag(ctx.title):""});return{html:html2,hash:simpleHash(html2),size:Buffer.byteLength(html2,"utf-8")}}function simpleHash(str){let hash=0;for(let i=0;i<str.length;i++){const char=str.charCodeAt(i);hash=(hash<<5)-hash+char|0}return Math.abs(hash).toString(16).padStart(8,"0")}function escapeHtmlForTag(str){return str.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}var DEFAULT_TIMEOUT_MS=1e4;var templateCache=new Map;async function resolveShellTemplate(source,options){if(isInlineShellSource(source)){return resolveInline(source.inline)}if(isUrlShellSource(source)){const cacheKey=`url:${source.url}`;if(!options?.noCache){const cached=templateCache.get(cacheKey);if(cached)return cached}const template=await fetchWithTimeout(source.url,source.timeout??DEFAULT_TIMEOUT_MS);const resolved=buildResolved(template,"url");if(!options?.noCache){templateCache.set(cacheKey,resolved)}return resolved}if(isNpmShellSource(source)){const specifier=source.version?`${source.npm}@${source.version}`:source.npm;const cacheKey=`npm:${specifier}`;if(!options?.noCache){const cached=templateCache.get(cacheKey);if(cached)return cached}const template=await resolveNpmTemplate(source,specifier,options?.resolver);const resolved=buildResolved(template,"npm");if(!options?.noCache){templateCache.set(cacheKey,resolved)}return resolved}throw new Error('Invalid custom shell source: must have "inline", "url", or "npm" property')}function clearShellTemplateCache(){templateCache.clear()}function resolveInline(template){return buildResolved(template,"inline")}function buildResolved(template,sourceType){const validation=validateShellTemplate(template);if(!validation.valid){throw new Error(`Custom shell template is missing required placeholder(s): ${validation.missingRequired.map(n=>`{{${n}}}`).join(", ")}`)}return{template,sourceType,validation}}async function fetchWithTimeout(url,timeoutMs){const controller=new AbortController;const timer=setTimeout(()=>controller.abort(),timeoutMs);try{const response=await fetch(url,{signal:controller.signal});if(!response.ok){throw new Error(`Failed to fetch shell template from ${url}: HTTP ${response.status}`)}return await response.text()}catch(err){if(err instanceof Error&&err.name==="AbortError"){throw new Error(`Shell template fetch timed out after ${timeoutMs}ms: ${url}`)}throw err}finally{clearTimeout(timer)}}async function resolveNpmTemplate(source,specifier,resolver){const cdnUrl=resolveNpmToCdnUrl(specifier,resolver);const moduleText=await fetchWithTimeout(cdnUrl,DEFAULT_TIMEOUT_MS);return extractTemplateFromModule(moduleText,source.exportName)}function resolveNpmToCdnUrl(specifier,resolver){if(resolver){const resolved=resolver.resolve(specifier);if(resolved){return resolved.value}}return`https://esm.sh/${specifier}`}function extractTemplateFromModule(moduleText,exportName){if(exportName&&exportName!=="default"){const namedPattern=new RegExp(`export\\s+(?:const|let|var)\\s+${escapeRegExp3(exportName)}\\s*=\\s*\`([\\s\\S]*?)\``);const namedMatch=moduleText.match(namedPattern);if(namedMatch){return namedMatch[1]}throw new Error(`Could not extract named export "${exportName}" from npm shell package. The package must export a template literal string.`)}const defaultPattern=/export\s+default\s+`([\s\S]*?)`/;const defaultMatch=moduleText.match(defaultPattern);if(defaultMatch){return defaultMatch[1]}throw new Error("Could not extract default export from npm shell package. The package must export a template literal string (e.g., export default `<html>...</html>`).")}function escapeRegExp3(str){return str.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function isNpmSource(source){return typeof source==="object"&&source!==null&&"npm"in source}function isFileSource(source){return typeof source==="object"&&source!==null&&"file"in source}function isImportSource(source){return typeof source==="object"&&source!==null&&"import"in source}function isFunctionSource(source){return typeof source==="function"}var FRONTMCP_META_KEY="__frontmcp_meta";function isFrontmcpUiResolvable(...candidatePaths){try{const nodeFs=__require("fs");const nodePath=__require("path");const ORG="@frontmcp";const PKG="ui";for(const candidate of candidatePaths){let dir=candidate;while(true){const pkgJson=nodePath.join(dir,"node_modules",ORG,PKG,"package.json");if(nodeFs.existsSync(pkgJson))return true;const parent=nodePath.dirname(dir);if(parent===dir)break;dir=parent}}return false}catch{return false}}function loadEsbuild(filename){try{return __require("esbuild")}catch(err){if(!isMissingEsbuild(err))throw err;const subject=filename?`FileSource widget "${filename}"`:"A FileSource widget";throw new Error(`${subject} needs esbuild, which is not installed. @frontmcp/uipack loads esbuild on demand to bundle FileSource widgets (.tsx/.jsx) when the tool is called, so it must be installed where the server runs: \`npm install esbuild\` (in "dependencies", not "devDependencies"). Projects created with \`frontmcp create\` already get it through the \`frontmcp\` package.`,{cause:err})}}function isMissingEsbuild(err){if(!(err instanceof Error))return false;return err.code==="MODULE_NOT_FOUND"&&/Cannot find module ['"]esbuild['"]/.test(err.message)}function transpileReactSource(source,filename){const esbuild=loadEsbuild(filename);const loader=filename?.endsWith(".tsx")?"tsx":"jsx";const result=esbuild.transformSync(source,{loader,jsx:"transform",jsxFactory:"React.createElement",jsxFragment:"React.Fragment",format:"esm",target:"es2020",define:{"process.env.NODE_ENV":'"production"'}});return result.code}function bundleFileSource(source,filename,resolveDir,componentName,options={}){if(!isFrontmcpUiResolvable(resolveDir,process.cwd())){throw new Error(`FileSource widget "${filename}" requires the @frontmcp/ui package, which provides the React bridge mount that's injected at bundle time. Install it (e.g. \`npm install @frontmcp/ui\` or \`yarn add @frontmcp/ui\`) and try again.`)}const esbuild=loadEsbuild(filename);const mountCode=`
|
|
1218
|
+
// --- Auto-generated mount ---
|
|
1219
|
+
import { createElement as __h } from 'react';
|
|
1220
|
+
import { createRoot } from 'react-dom/client';
|
|
1221
|
+
import { McpBridgeProvider } from '@frontmcp/ui/react';
|
|
1222
|
+
var __root = document.getElementById('root');
|
|
1223
|
+
if (__root) {
|
|
1224
|
+
var __reactRoot = createRoot(__root);
|
|
1225
|
+
function __hasData(v) { return v !== undefined; }
|
|
1226
|
+
function __render(output) {
|
|
1227
|
+
__reactRoot.render(
|
|
1228
|
+
__h(McpBridgeProvider, null,
|
|
1229
|
+
__h(${componentName}, { output: output !== undefined ? output : null, input: window.__mcpToolInput, loading: !__hasData(output) })
|
|
1230
|
+
)
|
|
1231
|
+
);
|
|
1644
1232
|
}
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
var DECLARE_MODULE_PATTERN = /^declare\s+module\s+['"]([^'"]+)['"]/gm;
|
|
1658
|
-
function parseDtsImports(content) {
|
|
1659
|
-
const imports = [];
|
|
1660
|
-
const externalPackages = /* @__PURE__ */ new Set();
|
|
1661
|
-
const relativeImports = /* @__PURE__ */ new Set();
|
|
1662
|
-
const lines = content.split("\n");
|
|
1663
|
-
for (let i = 0; i < lines.length; i++) {
|
|
1664
|
-
const line = lines[i];
|
|
1665
|
-
const lineNum = i + 1;
|
|
1666
|
-
IMPORT_PATTERN.lastIndex = 0;
|
|
1667
|
-
let match = IMPORT_PATTERN.exec(line);
|
|
1668
|
-
if (match) {
|
|
1669
|
-
const specifier = match[1];
|
|
1670
|
-
imports.push({
|
|
1671
|
-
type: "import",
|
|
1672
|
-
specifier,
|
|
1673
|
-
statement: line.trim(),
|
|
1674
|
-
line: lineNum
|
|
1675
|
-
});
|
|
1676
|
-
categorizeSpecifier(specifier, externalPackages, relativeImports);
|
|
1677
|
-
}
|
|
1678
|
-
EXPORT_FROM_PATTERN.lastIndex = 0;
|
|
1679
|
-
match = EXPORT_FROM_PATTERN.exec(line);
|
|
1680
|
-
if (match) {
|
|
1681
|
-
const specifier = match[1];
|
|
1682
|
-
imports.push({
|
|
1683
|
-
type: "export",
|
|
1684
|
-
specifier,
|
|
1685
|
-
statement: line.trim(),
|
|
1686
|
-
line: lineNum
|
|
1687
|
-
});
|
|
1688
|
-
categorizeSpecifier(specifier, externalPackages, relativeImports);
|
|
1689
|
-
}
|
|
1690
|
-
REFERENCE_TYPES_PATTERN.lastIndex = 0;
|
|
1691
|
-
match = REFERENCE_TYPES_PATTERN.exec(line);
|
|
1692
|
-
if (match) {
|
|
1693
|
-
const specifier = match[1];
|
|
1694
|
-
imports.push({
|
|
1695
|
-
type: "reference",
|
|
1696
|
-
specifier,
|
|
1697
|
-
statement: line.trim(),
|
|
1698
|
-
line: lineNum
|
|
1699
|
-
});
|
|
1700
|
-
externalPackages.add(getPackageFromSpecifier(specifier));
|
|
1701
|
-
}
|
|
1702
|
-
REFERENCE_PATH_PATTERN.lastIndex = 0;
|
|
1703
|
-
match = REFERENCE_PATH_PATTERN.exec(line);
|
|
1704
|
-
if (match) {
|
|
1705
|
-
const specifier = match[1];
|
|
1706
|
-
imports.push({
|
|
1707
|
-
type: "reference",
|
|
1708
|
-
specifier,
|
|
1709
|
-
statement: line.trim(),
|
|
1710
|
-
line: lineNum
|
|
1711
|
-
});
|
|
1712
|
-
relativeImports.add(specifier);
|
|
1713
|
-
}
|
|
1714
|
-
DECLARE_MODULE_PATTERN.lastIndex = 0;
|
|
1715
|
-
match = DECLARE_MODULE_PATTERN.exec(line);
|
|
1716
|
-
if (match) {
|
|
1717
|
-
const specifier = match[1];
|
|
1718
|
-
imports.push({
|
|
1719
|
-
type: "declare-module",
|
|
1720
|
-
specifier,
|
|
1721
|
-
statement: line.trim(),
|
|
1722
|
-
line: lineNum
|
|
1723
|
-
});
|
|
1724
|
-
}
|
|
1233
|
+
// Render immediately (component shows loading state until data arrives)
|
|
1234
|
+
__render(undefined);
|
|
1235
|
+
// 1. Try OpenAI SDK (toolOutput set synchronously or after load)
|
|
1236
|
+
if (typeof window !== 'undefined') {
|
|
1237
|
+
if (!window.openai) window.openai = {};
|
|
1238
|
+
var __cur = window.openai.toolOutput;
|
|
1239
|
+
if (__hasData(__cur)) { __render(__cur); }
|
|
1240
|
+
Object.defineProperty(window.openai, 'toolOutput', {
|
|
1241
|
+
get: function() { return __cur; },
|
|
1242
|
+
set: function(v) { __cur = v; __render(v); },
|
|
1243
|
+
configurable: true, enumerable: true
|
|
1244
|
+
});
|
|
1725
1245
|
}
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
}
|
|
1732
|
-
function categorizeSpecifier(specifier, externalPackages, relativeImports) {
|
|
1733
|
-
if (isRelativeImport(specifier)) {
|
|
1734
|
-
relativeImports.add(specifier);
|
|
1246
|
+
// 2. Try injected data globals (null: the page carries no call's data, e.g. compiled at startup)
|
|
1247
|
+
if (__hasData(window.__mcpToolOutput) && window.__mcpToolOutput !== null) { __render(window.__mcpToolOutput); }
|
|
1248
|
+
// 3. Listen for bridge tool-result (ext-apps / MCP Inspector)
|
|
1249
|
+
var __bridge = window.FrontMcpBridge;
|
|
1250
|
+
if (__bridge && typeof __bridge.onToolResult === 'function') {
|
|
1251
|
+
__bridge.onToolResult(function(data) { __render(data); });
|
|
1735
1252
|
} else {
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
/^[A-Za-z]:/.test(specifier);
|
|
1742
|
-
}
|
|
1743
|
-
function getPackageFromSpecifier(specifier) {
|
|
1744
|
-
if (specifier.startsWith("@")) {
|
|
1745
|
-
const parts = specifier.split("/");
|
|
1746
|
-
if (parts.length >= 2) {
|
|
1747
|
-
return `${parts[0]}/${parts[1]}`;
|
|
1748
|
-
}
|
|
1749
|
-
return specifier;
|
|
1750
|
-
}
|
|
1751
|
-
const slashIndex = specifier.indexOf("/");
|
|
1752
|
-
if (slashIndex > 0) {
|
|
1753
|
-
return specifier.substring(0, slashIndex);
|
|
1754
|
-
}
|
|
1755
|
-
return specifier;
|
|
1756
|
-
}
|
|
1757
|
-
function getSubpathFromSpecifier(specifier) {
|
|
1758
|
-
const packageName = getPackageFromSpecifier(specifier);
|
|
1759
|
-
if (specifier.length > packageName.length + 1) {
|
|
1760
|
-
return specifier.substring(packageName.length + 1);
|
|
1761
|
-
}
|
|
1762
|
-
return void 0;
|
|
1763
|
-
}
|
|
1764
|
-
var MAX_IMPORT_STATEMENT_LENGTH = 2e3;
|
|
1765
|
-
function parseImportStatement(statement) {
|
|
1766
|
-
if (statement.length > MAX_IMPORT_STATEMENT_LENGTH) {
|
|
1767
|
-
return null;
|
|
1768
|
-
}
|
|
1769
|
-
const namedMatch = /import\s+(?:\{[^}]*\}|\*\s+as\s+\w+|\w+)\s+from\s+['"]([^'"]+)['"]/.exec(statement);
|
|
1770
|
-
if (namedMatch) {
|
|
1771
|
-
return namedMatch[1];
|
|
1772
|
-
}
|
|
1773
|
-
const sideEffectMatch = /import\s+['"]([^'"]+)['"]/.exec(statement);
|
|
1774
|
-
if (sideEffectMatch) {
|
|
1775
|
-
return sideEffectMatch[1];
|
|
1253
|
+
// 4. Fallback: listen for tool:result CustomEvent (standalone / MCP Inspector)
|
|
1254
|
+
window.addEventListener('tool:result', function(e) {
|
|
1255
|
+
var d = e.detail;
|
|
1256
|
+
if (d) __render(d.structuredContent !== undefined ? d.structuredContent : d.content !== undefined ? d.content : d);
|
|
1257
|
+
});
|
|
1776
1258
|
}
|
|
1777
|
-
|
|
1259
|
+
}`;const loader={".tsx":"tsx",".jsx":"jsx"};const stdinLoader=filename.endsWith(".tsx")?"tsx":"jsx";const alias={};try{const nodePath=__require("path");const nodeFs=__require("fs");const candidates=[nodePath.join(process.cwd(),"node_modules","@frontmcp","ui","dist","esm"),nodePath.join(resolveDir,"node_modules","@frontmcp","ui","dist","esm")];for(const uiEsmBase of candidates){if(!nodeFs.existsSync(uiEsmBase))continue;const subpaths=["components","react","theme","bridge","runtime"];for(const sub of subpaths){const mjs=nodePath.join(uiEsmBase,sub,"index.mjs");if(nodeFs.existsSync(mjs))alias[`@frontmcp/ui/${sub}`]=mjs}if(Object.keys(alias).length>0)break}}catch{}try{const result=esbuild.buildSync({stdin:{contents:source+"\n"+mountCode,loader:stdinLoader,resolveDir,sourcefile:filename},bundle:true,write:false,format:"esm",target:"es2020",jsx:"automatic",external:options.bundleReact?[]:["react","react-dom","react/jsx-runtime","react/jsx-dev-runtime"],alias,define:{"process.env.NODE_ENV":'"production"'},platform:"browser",treeShaking:true,logLevel:"warning",loader});return{code:result.outputFiles[0].text}}catch(err){const message=err instanceof Error?err.message:String(err);throw new Error(`Failed to bundle FileSource "${filename}": ${message}. If the error mentions @frontmcp/ui or @frontmcp/uipack, ensure both packages are installed in the consuming project.`,{cause:err})}}function extractDefaultExportName(code){let match=code.match(/export\s+default\s+(?:function|class)\s+(\w+)/);if(match)return match[1];match=code.match(/export\s+default\s+(\w+)\s*;/);if(match)return match[1];return null}var DEFAULT_META={mcpAware:false,renderer:"auto"};function resolveUISource(source,options){if(options?.inlineReact&&options?.transformOnly){throw new Error("resolveUISource: `inlineReact` and `transformOnly` are mutually exclusive \u2014 set at most one.")}const resolver=options?.resolver??createEsmShResolver();if(isNpmSource(source)){return resolveNpmSource(source,resolver)}if(isImportSource(source)){return resolveImportSource(source)}if(isFileSource(source)){return resolveFileSource(source,{inlineReact:options?.inlineReact,transformOnly:options?.transformOnly})}if(isFunctionSource(source)){return resolveFunctionSource(source,options?.input,options?.output)}throw new Error("Unknown UISource type")}function resolveNpmSource(source,resolver){const specifier=source.version?`${source.npm}@${source.version}`:source.npm;const pkgName=getPackageName(source.npm);const resolved=resolver.resolve(specifier);const url=resolved?.value??`https://esm.sh/${specifier}`;const peerDeps=[];if(pkgName!=="react"&&pkgName!=="react-dom"){peerDeps.push("react","react-dom")}return{mode:"module",url,exportName:source.exportName??"default",meta:{...DEFAULT_META,renderer:"react"},peerDependencies:peerDeps}}function resolveImportSource(source){return{mode:"module",url:source.import,exportName:source.exportName??"default",meta:{...DEFAULT_META},peerDependencies:[]}}var BUILD_DIR_SEGMENTS=new Set(["dist","build","out"]);function missingWidgetError(filePath,cause){const sourceWidget=findSourceWidget(filePath);const error=new Error(`FileSource widget not found (ENOENT): no file at "${filePath}". The widget source is read from this path when the tool is called, so it has to exist where the compiled tool runs \u2014 a path built from \`__dirname\` or \`import.meta.url\` points into the build output once the tool is compiled. tsc does not copy \`*.widget.tsx\` / \`*.widget.jsx\` files into its output; \`frontmcp build\` does, and any other build needs a step that copies them next to the compiled tool.`+(sourceWidget?` The source widget exists at "${sourceWidget}" \u2014 copy it to "${filePath}", or build with \`frontmcp build\`.`:""),{cause});error.code="ENOENT";error.path=filePath;return error}function findSourceWidget(filePath){const path=__require("path");const fs=__require("fs");const parts=filePath.split(path.sep);for(let i=parts.length-2;i>=0;i--){if(!BUILD_DIR_SEGMENTS.has(parts[i]))continue;const candidates=[[...parts.slice(0,i),"src",...parts.slice(i+1)]];if(parts.length-i>2)candidates.push([...parts.slice(0,i),"src",...parts.slice(i+2)]);candidates.push([...parts.slice(0,i),...parts.slice(i+1)]);for(const candidate of candidates){const candidatePath=candidate.join(path.sep);try{if(fs.existsSync(candidatePath))return candidatePath}catch{}}return void 0}return void 0}function resolveFileSource(source,options={}){const path=__require("path");const ext=path.extname(source.file).toLowerCase();if(ext===".tsx"||ext===".jsx"){const fs=__require("fs");const wasRelative=!path.isAbsolute(source.file);const filePath=wasRelative?path.resolve(process.cwd(),source.file):source.file;let rawSource;try{rawSource=fs.readFileSync(filePath,"utf-8")}catch(err){const isNotFound=err?.code==="ENOENT";if(isNotFound&&wasRelative){throw new Error(`FileSource widget "${source.file}" not found at "${filePath}". Relative paths are resolved against process.cwd() ("${process.cwd()}"), not the tool file's directory. To anchor the path to the tool file, pass an absolute path \u2014 e.g. \`{ file: fileURLToPath(new URL('./widget.tsx', import.meta.url)) }\` from \`node:url\` (see issue #444).`,{cause:err})}if(isNotFound)throw missingWidgetError(filePath,err);throw err}const componentName=source.exportName||extractDefaultExportName(rawSource)||"Component";if(options.transformOnly===true){const code=transpileReactSource(rawSource,path.basename(filePath));const parsed2=parseImports(code);return{mode:"module",code,imports:[...new Set(parsed2.externalImports.map(i=>i.specifier))],exportName:componentName,meta:{mcpAware:true,renderer:"react"},peerDependencies:source.peerDependencies||["react","react-dom"],bundled:false}}const bundled=bundleFileSource(rawSource,source.file,path.dirname(filePath),componentName,{bundleReact:options.inlineReact===true});const parsed=parseImports(bundled.code);return{mode:"module",code:bundled.code,imports:[...new Set(parsed.externalImports.map(i=>i.specifier))],exportName:componentName,meta:{mcpAware:true,renderer:"react"},peerDependencies:source.peerDependencies||["react","react-dom"],bundled:true}}if(source.inline){return{mode:"inline",html:`<!-- File source: ${source.file} (inline mode - content to be resolved at build time) -->`,exportName:"default",meta:{...DEFAULT_META,renderer:"html"},peerDependencies:[]}}return{mode:"module",url:source.file,exportName:"default",meta:{...DEFAULT_META},peerDependencies:[]}}function resolveFunctionSource(source,input,output){const html2=source(input,output);return{mode:"inline",html:html2,exportName:"default",meta:{mcpAware:false,renderer:"html"},peerDependencies:[]}}function generateMountScript(resolved,propsMapping){if(resolved.mode!=="module"||!resolved.url){return""}const componentImport=resolved.exportName==="default"?`import Component from '${resolved.url}';`:`import { ${resolved.exportName} as Component } from '${resolved.url}';`;if(resolved.meta.mcpAware){return`${componentImport}
|
|
1260
|
+
import React from 'react';
|
|
1261
|
+
import { createRoot } from 'react-dom/client';
|
|
1262
|
+
createRoot(document.getElementById('root'))
|
|
1263
|
+
.render(React.createElement(Component));`}const propsCode=propsMapping?generateMappedPropsCode(propsMapping):"window.__mcpToolOutput";return`${componentImport}
|
|
1264
|
+
import React from 'react';
|
|
1265
|
+
import { createRoot } from 'react-dom/client';
|
|
1266
|
+
const output = window.__mcpToolOutput || {};
|
|
1267
|
+
const props = ${propsCode};
|
|
1268
|
+
createRoot(document.getElementById('root'))
|
|
1269
|
+
.render(React.createElement(Component, props));`}function generateMappedPropsCode(mapping){const entries=Object.entries(mapping).map(([prop,path])=>{const accessPath=path.split(".").map(part=>`[${safeJsonForScript(part)}]`).join("");return`${safeJsonForScript(prop)}: output${accessPath}`}).join(", ");return`({ ${entries} })`}var DEFAULT_WIDGET_MOUNT={moduleSpecifier:"@frontmcp/ui/react",wrapperImportName:"McpBridgeProvider"};function renderComponent(config,shellConfig){const resolver=shellConfig.resolver??createEsmShResolver();const resolved=resolveUISource(config.source,{resolver,input:shellConfig.input,output:shellConfig.output,inlineReact:config.inlineReact===true,transformOnly:config.transformOnly===true});const mergedShellConfig={...shellConfig,withShell:config.withShell??shellConfig.withShell,includeBridge:config.includeBridge??shellConfig.includeBridge,customShell:config.customShell??shellConfig.customShell};if(resolved.mode==="inline"){return buildShell(resolved.html??"",mergedShellConfig)}const content=buildModuleContent(resolved,resolver,config.props,shellConfig.mount);return buildShell(content,mergedShellConfig)}function buildModuleContent(resolved,resolver,propsMapping,mount=DEFAULT_WIDGET_MOUNT){const parts=[];if(resolved.code&&resolved.bundled){const importEntries={};for(const spec of resolved.imports||[]){const depResolved=resolver.resolve(spec);if(depResolved){importEntries[spec]=depResolved}}if(Object.keys(importEntries).length>0){const importMap=createImportMapFromResolved(importEntries);parts.push(generateImportMapScriptTag(importMap))}parts.push('<div id="root"></div>');parts.push(`<script type="module">
|
|
1270
|
+
${resolved.code}
|
|
1271
|
+
</script>`)}else if(resolved.code){const importEntries={};const allSpecifiers=new Set([...resolved.imports||[],"react-dom/client",mount.moduleSpecifier,"react/jsx-runtime","react/jsx-dev-runtime"]);if(mount===DEFAULT_WIDGET_MOUNT){allSpecifiers.add("react-dom/server");allSpecifiers.add("react-dom/static")}const coreDeps=new Set(["react","react-dom","react-dom/client","react/jsx-runtime","react/jsx-dev-runtime","react-dom/server","react-dom/static"]);for(const spec of allSpecifiers){const depResolved=resolver.resolve(spec);if(depResolved){if(!coreDeps.has(spec)&&depResolved.value?.includes("esm.sh")){importEntries[spec]={...depResolved,value:addExternalParam(depResolved.value,["react","react-dom"])}}else{importEntries[spec]=depResolved}}}if(importEntries["react-dom"]&&!importEntries["react-dom/client"]){const rdc=resolver.resolve("react-dom/client");if(rdc)importEntries["react-dom/client"]=rdc}if(Object.keys(importEntries).length>0){const importMap=createImportMapFromResolved(importEntries);parts.push(generateImportMapScriptTag(importMap))}const mountNodeId=mount.mountNodeId??"root";parts.push(`<div id="${mountNodeId}">${mount.mountNodeInnerHtml??""}</div>`);const mountCode=generateInlineMountCode(resolved.exportName,mount);parts.push(`<script type="module">
|
|
1272
|
+
${resolved.code}
|
|
1273
|
+
${mountCode}
|
|
1274
|
+
</script>`)}else{const importEntries={};for(const dep of resolved.peerDependencies){const depResolved=resolver.resolve(dep);if(depResolved){importEntries[dep]=depResolved}}if(importEntries["react-dom"]){const reactDomClient=resolver.resolve("react-dom/client");if(reactDomClient){importEntries["react-dom/client"]=reactDomClient}}if(Object.keys(importEntries).length>0){const importMap=createImportMapFromResolved(importEntries);parts.push(generateImportMapScriptTag(importMap))}parts.push('<div id="root"></div>');const mountScript=generateMountScript(resolved,propsMapping);if(mountScript){parts.push(`<script type="module">
|
|
1275
|
+
${mountScript}
|
|
1276
|
+
</script>`)}}return parts.join("\n")}function addExternalParam(url,externals){const sep=url.includes("?")?"&":"?";return`${url}${sep}external=${externals.join(",")}`}function generateInlineMountCode(componentName,mount){if(mount.generate){return mount.generate(componentName)}const wrapperName=mount.wrapperImportName??"McpBridgeProvider";const wrapperAlias=`__${wrapperName}`;const mountNodeId=mount.mountNodeId??"root";return`
|
|
1277
|
+
// --- Mount ---
|
|
1278
|
+
import { createRoot as __createRoot } from 'react-dom/client';
|
|
1279
|
+
import { ${wrapperName} as ${wrapperAlias} } from '${mount.moduleSpecifier}';
|
|
1280
|
+
const __root = document.getElementById('${mountNodeId}');
|
|
1281
|
+
if (__root) {
|
|
1282
|
+
__createRoot(__root).render(
|
|
1283
|
+
React.createElement(${wrapperAlias}, null,
|
|
1284
|
+
React.createElement(${componentName})
|
|
1285
|
+
)
|
|
1286
|
+
);
|
|
1287
|
+
}`}var MCP_APPS_MIME_TYPE="text/html;profile=mcp-app";var MCP_APPS_EXTENSION_ID="io.modelcontextprotocol/ui";function resolveServingMode(options){const{configuredMode,platformType}=options;if(platformType==="gemini"){return{mode:configuredMode,supportsUI:false,effectiveMode:null,useStructuredContent:false,reason:"Gemini does not support MCP Apps UI"}}const effectiveMode=configuredMode==="auto"?"inline":configuredMode;if(effectiveMode==="hybrid"){const hybridCapable=platformType==="openai"||platformType==="ext-apps"||platformType==="cursor";if(!hybridCapable){return{mode:configuredMode,supportsUI:false,effectiveMode:null,useStructuredContent:false,reason:`Platform ${platformType} does not support hybrid serving mode`}}}const useStructuredContent=platformType==="openai"||platformType==="ext-apps"||platformType==="generic-mcp"||platformType==="unknown";return{mode:configuredMode,supportsUI:true,effectiveMode,useStructuredContent,reason:`Platform ${platformType} supports UI, mode: ${effectiveMode}`}}function buildToolResponseContent(options){const{rawOutput,htmlContent,servingMode,useStructuredContent,platformType}=options;const textSummary=typeof rawOutput==="string"?rawOutput:rawOutput!==null&&rawOutput!==void 0?JSON.stringify(rawOutput):"";const result={content:[{type:"text",text:textSummary}]};if(useStructuredContent&&rawOutput!==null&&rawOutput!==void 0){const structured=typeof rawOutput==="object"&&!Array.isArray(rawOutput)?rawOutput:{value:rawOutput};result.structuredContent=structured}if(htmlContent&&servingMode==="inline"){const htmlKey="ui/html";result._meta={[htmlKey]:htmlContent};result.format="html"}return result}function isUIRenderFailure(result){if(typeof result!=="object"||result===null){return false}const rec=result;return typeof rec["reason"]==="string"&&!("meta"in rec)}var CHART_TYPES=new Set(["bar","line","pie","area","scatter","doughnut","radar","polarArea","bubble"]);var MERMAID_PREFIXES=["flowchart","graph","sequenceDiagram","classDiagram","stateDiagram","erDiagram","gantt","pie","gitGraph","journey","mindmap","timeline","quadrantChart","sankey","xychart"];var PDF_MAGIC="JVBERi";var BASE64_PATTERN=/^[A-Za-z0-9+/=\r\n]+$/;function detectContentType(value){if(value===null||value===void 0){return"text"}if(typeof value==="object"&&!Array.isArray(value)){const obj=value;if(typeof obj["type"]==="string"&&CHART_TYPES.has(obj["type"])){return"chart"}return"json"}if(typeof value==="string"){const trimmed=value.trimStart();if(trimmed.startsWith(PDF_MAGIC)){return BASE64_PATTERN.test(trimmed.trimEnd())?"pdf":"text"}for(const prefix of MERMAID_PREFIXES){if(trimmed.startsWith(prefix)){return"mermaid"}}if(trimmed.includes("<")&&trimmed.includes(">")){return"html"}return"text"}return"text"}var CHART_JS_CDN="https://esm.sh/chart.js@4/auto";var MERMAID_CDN="https://esm.sh/mermaid@11/dist/mermaid.esm.min.mjs";function buildChartHtml(chartJson){const chartData=safeJsonForScript(chartJson);const content=`
|
|
1288
|
+
<canvas id="chart" style="max-width:100%;max-height:80vh;"></canvas>
|
|
1289
|
+
<script type="module">
|
|
1290
|
+
import Chart from '${CHART_JS_CDN}';
|
|
1291
|
+
const ctx = document.getElementById('chart').getContext('2d');
|
|
1292
|
+
new Chart(ctx, ${chartData});
|
|
1293
|
+
</script>`;const result=buildShell(content,{toolName:"chart",csp:{resourceDomains:["https://esm.sh"],connectDomains:["https://esm.sh"]},includeBridge:false});return result.html}function buildMermaidHtml(mermaidCode){const escaped=escapeHtml(mermaidCode);const content=`
|
|
1294
|
+
<pre class="mermaid">${escaped}</pre>
|
|
1295
|
+
<script type="module">
|
|
1296
|
+
import mermaid from '${MERMAID_CDN}';
|
|
1297
|
+
mermaid.initialize({ startOnLoad: true, theme: 'default' });
|
|
1298
|
+
</script>`;const result=buildShell(content,{toolName:"mermaid",csp:{resourceDomains:["https://esm.sh"],connectDomains:["https://esm.sh"]},includeBridge:false});return result.html}function buildPdfHtml(base64){const content=`
|
|
1299
|
+
<div id="pdf-container" style="width:100%;overflow:auto;text-align:center;"></div>
|
|
1300
|
+
<script type="module">
|
|
1301
|
+
import * as pdfjsLib from 'https://esm.sh/pdfjs-dist@4/build/pdf.min.mjs';
|
|
1302
|
+
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://esm.sh/pdfjs-dist@4/build/pdf.worker.min.mjs';
|
|
1303
|
+
|
|
1304
|
+
const base64 = ${safeJsonForScript(base64)};
|
|
1305
|
+
const binaryStr = atob(base64);
|
|
1306
|
+
const bytes = new Uint8Array(binaryStr.length);
|
|
1307
|
+
for (let i = 0; i < binaryStr.length; i++) bytes[i] = binaryStr.charCodeAt(i);
|
|
1308
|
+
|
|
1309
|
+
const pdf = await pdfjsLib.getDocument({ data: bytes }).promise;
|
|
1310
|
+
const container = document.getElementById('pdf-container');
|
|
1311
|
+
for (let i = 1; i <= pdf.numPages; i++) {
|
|
1312
|
+
const page = await pdf.getPage(i);
|
|
1313
|
+
const viewport = page.getViewport({ scale: 1.5 });
|
|
1314
|
+
const canvas = document.createElement('canvas');
|
|
1315
|
+
canvas.width = viewport.width;
|
|
1316
|
+
canvas.height = viewport.height;
|
|
1317
|
+
canvas.style.marginBottom = '8px';
|
|
1318
|
+
container.appendChild(canvas);
|
|
1319
|
+
await page.render({ canvasContext: canvas.getContext('2d'), viewport }).promise;
|
|
1778
1320
|
}
|
|
1779
|
-
function
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
declarations.push(`
|
|
1803
|
-
// Source: ${url}`);
|
|
1804
|
-
}
|
|
1805
|
-
}
|
|
1806
|
-
}
|
|
1807
|
-
declarations.push(line);
|
|
1808
|
-
}
|
|
1809
|
-
}
|
|
1810
|
-
const parts = [];
|
|
1811
|
-
if (references.length > 0) {
|
|
1812
|
-
parts.push(references.join("\n"));
|
|
1813
|
-
parts.push("");
|
|
1814
|
-
}
|
|
1815
|
-
parts.push(declarations.join("\n").trim());
|
|
1816
|
-
return parts.join("\n");
|
|
1817
|
-
}
|
|
1818
|
-
|
|
1819
|
-
// libs/uipack/src/typings/type-fetcher.ts
|
|
1820
|
-
var Semaphore = class {
|
|
1821
|
-
permits;
|
|
1822
|
-
waiting = [];
|
|
1823
|
-
constructor(permits) {
|
|
1824
|
-
this.permits = permits;
|
|
1825
|
-
}
|
|
1826
|
-
async acquire() {
|
|
1827
|
-
if (this.permits > 0) {
|
|
1828
|
-
this.permits--;
|
|
1829
|
-
return;
|
|
1830
|
-
}
|
|
1831
|
-
return new Promise((resolve) => {
|
|
1832
|
-
this.waiting.push(resolve);
|
|
1833
|
-
});
|
|
1834
|
-
}
|
|
1835
|
-
release() {
|
|
1836
|
-
if (this.waiting.length > 0) {
|
|
1837
|
-
const next = this.waiting.shift();
|
|
1838
|
-
next?.();
|
|
1839
|
-
} else {
|
|
1840
|
-
this.permits++;
|
|
1841
|
-
}
|
|
1842
|
-
}
|
|
1843
|
-
};
|
|
1844
|
-
var TypeFetcher = class {
|
|
1845
|
-
maxDepth;
|
|
1846
|
-
timeout;
|
|
1847
|
-
maxConcurrency;
|
|
1848
|
-
cdnBaseUrl;
|
|
1849
|
-
fetchFn;
|
|
1850
|
-
cache;
|
|
1851
|
-
allowedPatterns;
|
|
1852
|
-
allowlistEnabled;
|
|
1853
|
-
_initialized = false;
|
|
1854
|
-
constructor(options = {}, cache) {
|
|
1855
|
-
this.maxDepth = options.maxDepth ?? 2;
|
|
1856
|
-
this.timeout = options.timeout ?? 1e4;
|
|
1857
|
-
this.maxConcurrency = options.maxConcurrency ?? 5;
|
|
1858
|
-
this.cdnBaseUrl = options.cdnBaseUrl ?? "https://esm.sh";
|
|
1859
|
-
this.fetchFn = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
1860
|
-
this.cache = cache ?? globalTypeCache;
|
|
1861
|
-
if (options.allowedPackages === false) {
|
|
1862
|
-
this.allowlistEnabled = false;
|
|
1863
|
-
this.allowedPatterns = [];
|
|
1864
|
-
} else {
|
|
1865
|
-
this.allowlistEnabled = true;
|
|
1866
|
-
this.allowedPatterns = [...DEFAULT_ALLOWED_PACKAGES, ...options.allowedPackages ?? []];
|
|
1867
|
-
}
|
|
1868
|
-
}
|
|
1869
|
-
/**
|
|
1870
|
-
* Check if a package is allowed by the allowlist.
|
|
1871
|
-
* Supports glob patterns (e.g., '@frontmcp/*' matches '@frontmcp/ui').
|
|
1872
|
-
*
|
|
1873
|
-
* @param packageName - The package name to check
|
|
1874
|
-
* @returns true if the package is allowed
|
|
1875
|
-
*/
|
|
1876
|
-
isPackageAllowed(packageName) {
|
|
1877
|
-
if (!this.allowlistEnabled) {
|
|
1878
|
-
return true;
|
|
1879
|
-
}
|
|
1880
|
-
return this.allowedPatterns.some((pattern) => {
|
|
1881
|
-
if (pattern.includes("*")) {
|
|
1882
|
-
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
1883
|
-
const regex = new RegExp(`^${escaped}$`);
|
|
1884
|
-
return regex.test(packageName);
|
|
1885
|
-
}
|
|
1886
|
-
return packageName === pattern || packageName.startsWith(pattern + "/");
|
|
1887
|
-
});
|
|
1888
|
-
}
|
|
1889
|
-
/**
|
|
1890
|
-
* Initialize the TypeFetcher by pre-loading allowed packages.
|
|
1891
|
-
* This fetches types for all allowed packages and caches them.
|
|
1892
|
-
*
|
|
1893
|
-
* @param options - Options for initialization
|
|
1894
|
-
* @returns Summary of initialization results
|
|
1895
|
-
*/
|
|
1896
|
-
async initialize(options) {
|
|
1897
|
-
if (this._initialized) {
|
|
1898
|
-
const stats2 = await this.cache.getStats();
|
|
1899
|
-
return { loaded: [], failed: [], cached: stats2.entries };
|
|
1900
|
-
}
|
|
1901
|
-
const loaded = [];
|
|
1902
|
-
const failed = [];
|
|
1903
|
-
const packagesToLoad = this.allowedPatterns.filter((p) => !p.includes("*"));
|
|
1904
|
-
for (const pkg of packagesToLoad) {
|
|
1905
|
-
const result = await this.fetchBatch({
|
|
1906
|
-
imports: [`import * from "${pkg}"`],
|
|
1907
|
-
timeout: options?.timeout ?? this.timeout
|
|
1908
|
-
});
|
|
1909
|
-
if (result.results.length > 0) {
|
|
1910
|
-
loaded.push(pkg);
|
|
1911
|
-
} else if (result.errors.length > 0) {
|
|
1912
|
-
failed.push(pkg);
|
|
1913
|
-
}
|
|
1914
|
-
}
|
|
1915
|
-
this._initialized = true;
|
|
1916
|
-
const stats = await this.cache.getStats();
|
|
1917
|
-
return { loaded, failed, cached: stats.entries };
|
|
1918
|
-
}
|
|
1919
|
-
/**
|
|
1920
|
-
* Whether the fetcher has been initialized.
|
|
1921
|
-
*/
|
|
1922
|
-
get initialized() {
|
|
1923
|
-
return this._initialized;
|
|
1924
|
-
}
|
|
1925
|
-
/**
|
|
1926
|
-
* Get the list of allowed package patterns.
|
|
1927
|
-
*/
|
|
1928
|
-
get allowedPackagePatterns() {
|
|
1929
|
-
return this.allowedPatterns;
|
|
1930
|
-
}
|
|
1931
|
-
/**
|
|
1932
|
-
* Fetch types for a batch of import statements.
|
|
1933
|
-
*
|
|
1934
|
-
* @param request - Batch request configuration
|
|
1935
|
-
* @returns Batch result with results and errors
|
|
1936
|
-
*/
|
|
1937
|
-
async fetchBatch(request) {
|
|
1938
|
-
const startTime = Date.now();
|
|
1939
|
-
const results = [];
|
|
1940
|
-
const errors = [];
|
|
1941
|
-
let cacheHits = 0;
|
|
1942
|
-
let networkRequests = 0;
|
|
1943
|
-
const maxDepth = request.maxDepth ?? this.maxDepth;
|
|
1944
|
-
const timeout = request.timeout ?? this.timeout;
|
|
1945
|
-
const maxConcurrency = request.maxConcurrency ?? this.maxConcurrency;
|
|
1946
|
-
const skipCache = request.skipCache ?? false;
|
|
1947
|
-
const versionOverrides = request.versionOverrides ?? {};
|
|
1948
|
-
const semaphore = new Semaphore(maxConcurrency);
|
|
1949
|
-
const promises = request.imports.map(async (importStatement) => {
|
|
1950
|
-
await semaphore.acquire();
|
|
1951
|
-
try {
|
|
1952
|
-
const specifier = parseImportStatement(importStatement);
|
|
1953
|
-
if (!specifier) {
|
|
1954
|
-
errors.push({
|
|
1955
|
-
specifier: importStatement,
|
|
1956
|
-
code: "INVALID_SPECIFIER",
|
|
1957
|
-
message: `Could not parse import statement: ${importStatement}`
|
|
1958
|
-
});
|
|
1959
|
-
return;
|
|
1960
|
-
}
|
|
1961
|
-
const packageName = getPackageFromSpecifier(specifier);
|
|
1962
|
-
if (!this.isPackageAllowed(packageName)) {
|
|
1963
|
-
errors.push({
|
|
1964
|
-
specifier,
|
|
1965
|
-
code: "PACKAGE_NOT_ALLOWED",
|
|
1966
|
-
message: `Package "${packageName}" is not in the allowlist`
|
|
1967
|
-
});
|
|
1968
|
-
return;
|
|
1969
|
-
}
|
|
1970
|
-
const version = versionOverrides[packageName] ?? "latest";
|
|
1971
|
-
const cacheKey = `${TYPE_CACHE_PREFIX}${packageName}@${version}`;
|
|
1972
|
-
if (!skipCache) {
|
|
1973
|
-
const cached = await this.cache.get(cacheKey);
|
|
1974
|
-
if (cached) {
|
|
1975
|
-
cacheHits++;
|
|
1976
|
-
results.push(cached.result);
|
|
1977
|
-
return;
|
|
1978
|
-
}
|
|
1979
|
-
}
|
|
1980
|
-
const result = await this.fetchTypesForSpecifier(specifier, {
|
|
1981
|
-
maxDepth,
|
|
1982
|
-
timeout,
|
|
1983
|
-
version
|
|
1984
|
-
});
|
|
1985
|
-
if (result.success) {
|
|
1986
|
-
results.push(result.data);
|
|
1987
|
-
networkRequests += result.fetchCount;
|
|
1988
|
-
const entry = {
|
|
1989
|
-
result: result.data,
|
|
1990
|
-
cachedAt: Date.now(),
|
|
1991
|
-
size: result.data.content.length,
|
|
1992
|
-
accessCount: 1
|
|
1993
|
-
};
|
|
1994
|
-
await this.cache.set(cacheKey, entry);
|
|
1995
|
-
} else {
|
|
1996
|
-
errors.push(result.error);
|
|
1997
|
-
networkRequests += result.fetchCount;
|
|
1998
|
-
}
|
|
1999
|
-
} finally {
|
|
2000
|
-
semaphore.release();
|
|
2001
|
-
}
|
|
2002
|
-
});
|
|
2003
|
-
await Promise.all(promises);
|
|
2004
|
-
return {
|
|
2005
|
-
results,
|
|
2006
|
-
errors,
|
|
2007
|
-
totalTimeMs: Date.now() - startTime,
|
|
2008
|
-
cacheHits,
|
|
2009
|
-
networkRequests
|
|
2010
|
-
};
|
|
2011
|
-
}
|
|
2012
|
-
/**
|
|
2013
|
-
* Fetch types for a single import specifier.
|
|
2014
|
-
*/
|
|
2015
|
-
async fetchTypesForSpecifier(specifier, options) {
|
|
2016
|
-
let fetchCount = 0;
|
|
2017
|
-
const fetchedUrls = [];
|
|
2018
|
-
const visitedUrls = /* @__PURE__ */ new Set();
|
|
2019
|
-
const contents = /* @__PURE__ */ new Map();
|
|
2020
|
-
try {
|
|
2021
|
-
const resolution = await this.resolvePackage(specifier, options.version, options.timeout);
|
|
2022
|
-
if (!resolution) {
|
|
2023
|
-
return {
|
|
2024
|
-
success: false,
|
|
2025
|
-
error: {
|
|
2026
|
-
specifier,
|
|
2027
|
-
code: "PACKAGE_NOT_FOUND",
|
|
2028
|
-
message: `Could not resolve package: ${specifier}`
|
|
2029
|
-
},
|
|
2030
|
-
fetchCount
|
|
2031
|
-
};
|
|
2032
|
-
}
|
|
2033
|
-
fetchCount++;
|
|
2034
|
-
const fetchResult = await this.fetchRecursive(
|
|
2035
|
-
resolution.typesUrl,
|
|
2036
|
-
options.maxDepth,
|
|
2037
|
-
options.timeout,
|
|
2038
|
-
visitedUrls,
|
|
2039
|
-
contents
|
|
2040
|
-
);
|
|
2041
|
-
fetchCount += fetchResult.fetchCount;
|
|
2042
|
-
fetchedUrls.push(...fetchResult.fetchedUrls);
|
|
2043
|
-
if (!fetchResult.success) {
|
|
2044
|
-
return {
|
|
2045
|
-
success: false,
|
|
2046
|
-
error: {
|
|
2047
|
-
specifier,
|
|
2048
|
-
code: fetchResult.errorCode,
|
|
2049
|
-
message: fetchResult.errorMessage ?? "Unknown error",
|
|
2050
|
-
url: fetchResult.errorUrl
|
|
2051
|
-
},
|
|
2052
|
-
fetchCount
|
|
2053
|
-
};
|
|
2054
|
-
}
|
|
2055
|
-
const files = buildTypeFiles(contents, resolution.packageName, resolution.version, resolution.subpath);
|
|
2056
|
-
const combinedContent = combineDtsContents(contents);
|
|
2057
|
-
const result = {
|
|
2058
|
-
specifier,
|
|
2059
|
-
resolvedPackage: resolution.packageName,
|
|
2060
|
-
subpath: resolution.subpath,
|
|
2061
|
-
version: resolution.version,
|
|
2062
|
-
content: combinedContent,
|
|
2063
|
-
files,
|
|
2064
|
-
fetchedUrls,
|
|
2065
|
-
fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2066
|
-
};
|
|
2067
|
-
return { success: true, data: result, fetchCount };
|
|
2068
|
-
} catch (error) {
|
|
2069
|
-
return {
|
|
2070
|
-
success: false,
|
|
2071
|
-
error: {
|
|
2072
|
-
specifier,
|
|
2073
|
-
code: "NETWORK_ERROR",
|
|
2074
|
-
message: error instanceof Error ? error.message : String(error)
|
|
2075
|
-
},
|
|
2076
|
-
fetchCount
|
|
2077
|
-
};
|
|
2078
|
-
}
|
|
2079
|
-
}
|
|
2080
|
-
/**
|
|
2081
|
-
* Resolve a package specifier to a types URL.
|
|
2082
|
-
* Uses path fallback: try full path, then remove last segment until found.
|
|
2083
|
-
*/
|
|
2084
|
-
async resolvePackage(specifier, version, timeout) {
|
|
2085
|
-
const packageName = getPackageFromSpecifier(specifier);
|
|
2086
|
-
const subpath = getSubpathFromSpecifier(specifier);
|
|
2087
|
-
const versionSuffix = version === "latest" ? "" : `@${version}`;
|
|
2088
|
-
const baseUrl = `${this.cdnBaseUrl}/${packageName}${versionSuffix}`;
|
|
2089
|
-
const urlsToTry = subpath ? [`${baseUrl}/${subpath}`, baseUrl] : [baseUrl];
|
|
2090
|
-
for (const url of urlsToTry) {
|
|
2091
|
-
try {
|
|
2092
|
-
const response = await this.fetchWithTimeout(url, timeout, "HEAD");
|
|
2093
|
-
if (response.ok) {
|
|
2094
|
-
const typesHeader = response.headers.get("X-TypeScript-Types");
|
|
2095
|
-
if (typesHeader) {
|
|
2096
|
-
const typesUrl = typesHeader.startsWith("/") ? `${this.cdnBaseUrl}${typesHeader}` : typesHeader;
|
|
2097
|
-
const versionMatch = /@(\d+\.\d+\.\d+[^/]*)/.exec(typesUrl);
|
|
2098
|
-
const resolvedVersion = versionMatch ? versionMatch[1] : version;
|
|
2099
|
-
return {
|
|
2100
|
-
packageName,
|
|
2101
|
-
subpath,
|
|
2102
|
-
version: resolvedVersion,
|
|
2103
|
-
typesUrl
|
|
2104
|
-
};
|
|
2105
|
-
}
|
|
2106
|
-
}
|
|
2107
|
-
} catch {
|
|
2108
|
-
continue;
|
|
2109
|
-
}
|
|
2110
|
-
}
|
|
2111
|
-
return null;
|
|
2112
|
-
}
|
|
2113
|
-
/**
|
|
2114
|
-
* Recursively fetch .d.ts files.
|
|
2115
|
-
*/
|
|
2116
|
-
async fetchRecursive(url, depth, timeout, visited, contents) {
|
|
2117
|
-
if (visited.has(url)) {
|
|
2118
|
-
return { success: true, fetchCount: 0, fetchedUrls: [] };
|
|
2119
|
-
}
|
|
2120
|
-
visited.add(url);
|
|
2121
|
-
let fetchCount = 1;
|
|
2122
|
-
const fetchedUrls = [url];
|
|
2123
|
-
try {
|
|
2124
|
-
const response = await this.fetchWithTimeout(url, timeout, "GET");
|
|
2125
|
-
if (!response.ok) {
|
|
2126
|
-
return {
|
|
2127
|
-
success: false,
|
|
2128
|
-
fetchCount,
|
|
2129
|
-
fetchedUrls,
|
|
2130
|
-
errorCode: "NETWORK_ERROR",
|
|
2131
|
-
errorMessage: `HTTP ${response.status}: ${response.statusText}`,
|
|
2132
|
-
errorUrl: url
|
|
2133
|
-
};
|
|
2134
|
-
}
|
|
2135
|
-
const content = await response.text();
|
|
2136
|
-
contents.set(url, content);
|
|
2137
|
-
if (depth <= 0) {
|
|
2138
|
-
return { success: true, fetchCount, fetchedUrls };
|
|
2139
|
-
}
|
|
2140
|
-
const parsed = parseDtsImports(content);
|
|
2141
|
-
for (const pkg of parsed.externalPackages) {
|
|
2142
|
-
if (pkg.startsWith("@types/") || isBuiltinModule(pkg)) {
|
|
2143
|
-
continue;
|
|
2144
|
-
}
|
|
2145
|
-
const resolution = await this.resolvePackage(pkg, "latest", timeout);
|
|
2146
|
-
fetchCount++;
|
|
2147
|
-
if (resolution && !visited.has(resolution.typesUrl)) {
|
|
2148
|
-
const result = await this.fetchRecursive(resolution.typesUrl, depth - 1, timeout, visited, contents);
|
|
2149
|
-
fetchCount += result.fetchCount;
|
|
2150
|
-
fetchedUrls.push(...result.fetchedUrls);
|
|
2151
|
-
}
|
|
2152
|
-
}
|
|
2153
|
-
for (const relativePath of parsed.relativeImports) {
|
|
2154
|
-
const resolvedUrl = resolveRelativeUrl(url, relativePath);
|
|
2155
|
-
if (resolvedUrl && !visited.has(resolvedUrl)) {
|
|
2156
|
-
const result = await this.fetchRecursive(resolvedUrl, depth - 1, timeout, visited, contents);
|
|
2157
|
-
fetchCount += result.fetchCount;
|
|
2158
|
-
fetchedUrls.push(...result.fetchedUrls);
|
|
2159
|
-
}
|
|
2160
|
-
}
|
|
2161
|
-
return { success: true, fetchCount, fetchedUrls };
|
|
2162
|
-
} catch (error) {
|
|
2163
|
-
const isTimeout = error instanceof Error && error.message.includes("timeout");
|
|
2164
|
-
return {
|
|
2165
|
-
success: false,
|
|
2166
|
-
fetchCount,
|
|
2167
|
-
fetchedUrls,
|
|
2168
|
-
errorCode: isTimeout ? "TIMEOUT" : "NETWORK_ERROR",
|
|
2169
|
-
errorMessage: error instanceof Error ? error.message : String(error),
|
|
2170
|
-
errorUrl: url
|
|
2171
|
-
};
|
|
2172
|
-
}
|
|
2173
|
-
}
|
|
2174
|
-
/**
|
|
2175
|
-
* Fetch with timeout support.
|
|
2176
|
-
*/
|
|
2177
|
-
async fetchWithTimeout(url, timeout, method) {
|
|
2178
|
-
const controller = new AbortController();
|
|
2179
|
-
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
2180
|
-
try {
|
|
2181
|
-
const response = await this.fetchFn(url, {
|
|
2182
|
-
method,
|
|
2183
|
-
signal: controller.signal,
|
|
2184
|
-
headers: {
|
|
2185
|
-
Accept: "application/typescript, text/plain, */*"
|
|
2186
|
-
}
|
|
2187
|
-
});
|
|
2188
|
-
return response;
|
|
2189
|
-
} finally {
|
|
2190
|
-
clearTimeout(timeoutId);
|
|
2191
|
-
}
|
|
2192
|
-
}
|
|
2193
|
-
};
|
|
2194
|
-
function isBuiltinModule(name) {
|
|
2195
|
-
const builtins = /* @__PURE__ */ new Set([
|
|
2196
|
-
"assert",
|
|
2197
|
-
"buffer",
|
|
2198
|
-
"child_process",
|
|
2199
|
-
"cluster",
|
|
2200
|
-
"console",
|
|
2201
|
-
"constants",
|
|
2202
|
-
"crypto",
|
|
2203
|
-
"dgram",
|
|
2204
|
-
"dns",
|
|
2205
|
-
"domain",
|
|
2206
|
-
"events",
|
|
2207
|
-
"fs",
|
|
2208
|
-
"http",
|
|
2209
|
-
"https",
|
|
2210
|
-
"module",
|
|
2211
|
-
"net",
|
|
2212
|
-
"os",
|
|
2213
|
-
"path",
|
|
2214
|
-
"punycode",
|
|
2215
|
-
"querystring",
|
|
2216
|
-
"readline",
|
|
2217
|
-
"repl",
|
|
2218
|
-
"stream",
|
|
2219
|
-
"string_decoder",
|
|
2220
|
-
"sys",
|
|
2221
|
-
"timers",
|
|
2222
|
-
"tls",
|
|
2223
|
-
"tty",
|
|
2224
|
-
"url",
|
|
2225
|
-
"util",
|
|
2226
|
-
"v8",
|
|
2227
|
-
"vm",
|
|
2228
|
-
"zlib",
|
|
2229
|
-
// Node.js prefixed modules
|
|
2230
|
-
"node:assert",
|
|
2231
|
-
"node:buffer",
|
|
2232
|
-
"node:child_process",
|
|
2233
|
-
"node:cluster",
|
|
2234
|
-
"node:console",
|
|
2235
|
-
"node:constants",
|
|
2236
|
-
"node:crypto",
|
|
2237
|
-
"node:dgram",
|
|
2238
|
-
"node:dns",
|
|
2239
|
-
"node:domain",
|
|
2240
|
-
"node:events",
|
|
2241
|
-
"node:fs",
|
|
2242
|
-
"node:http",
|
|
2243
|
-
"node:https",
|
|
2244
|
-
"node:module",
|
|
2245
|
-
"node:net",
|
|
2246
|
-
"node:os",
|
|
2247
|
-
"node:path",
|
|
2248
|
-
"node:punycode",
|
|
2249
|
-
"node:querystring",
|
|
2250
|
-
"node:readline",
|
|
2251
|
-
"node:repl",
|
|
2252
|
-
"node:stream",
|
|
2253
|
-
"node:string_decoder",
|
|
2254
|
-
"node:sys",
|
|
2255
|
-
"node:timers",
|
|
2256
|
-
"node:tls",
|
|
2257
|
-
"node:tty",
|
|
2258
|
-
"node:url",
|
|
2259
|
-
"node:util",
|
|
2260
|
-
"node:v8",
|
|
2261
|
-
"node:vm",
|
|
2262
|
-
"node:zlib"
|
|
2263
|
-
]);
|
|
2264
|
-
return builtins.has(name);
|
|
2265
|
-
}
|
|
2266
|
-
function resolveRelativeUrl(base, relative) {
|
|
2267
|
-
try {
|
|
2268
|
-
let path = relative;
|
|
2269
|
-
if (!path.endsWith(".d.ts") && !path.endsWith(".ts")) {
|
|
2270
|
-
path = `${path}.d.ts`;
|
|
2271
|
-
}
|
|
2272
|
-
const resolved = new URL(path, base);
|
|
2273
|
-
return resolved.toString();
|
|
2274
|
-
} catch {
|
|
2275
|
-
return null;
|
|
2276
|
-
}
|
|
2277
|
-
}
|
|
2278
|
-
function buildTypeFiles(contents, packageName, version, subpath) {
|
|
2279
|
-
const files = [];
|
|
2280
|
-
for (const [url, content] of contents.entries()) {
|
|
2281
|
-
const virtualPath = urlToVirtualPath(url, packageName, version);
|
|
2282
|
-
files.push({
|
|
2283
|
-
path: virtualPath,
|
|
2284
|
-
url,
|
|
2285
|
-
content
|
|
2286
|
-
});
|
|
2287
|
-
}
|
|
2288
|
-
if (subpath) {
|
|
2289
|
-
const aliasPath = `node_modules/${packageName}/${subpath}/index.d.ts`;
|
|
2290
|
-
const aliasExists = files.some((f) => f.path === aliasPath);
|
|
2291
|
-
if (!aliasExists) {
|
|
2292
|
-
const aliasContent = `// Auto-generated alias for ${packageName}/${subpath}
|
|
2293
|
-
export * from '${getRelativeImportPath(subpath)}';
|
|
2294
|
-
`;
|
|
2295
|
-
files.push({
|
|
2296
|
-
path: aliasPath,
|
|
2297
|
-
url: "",
|
|
2298
|
-
// No actual URL - this is synthesized
|
|
2299
|
-
content: aliasContent
|
|
2300
|
-
});
|
|
2301
|
-
}
|
|
2302
|
-
}
|
|
2303
|
-
return files;
|
|
2304
|
-
}
|
|
2305
|
-
function getRelativeImportPath(subpath) {
|
|
2306
|
-
const depth = subpath.split("/").length;
|
|
2307
|
-
const prefix = "../".repeat(depth);
|
|
2308
|
-
return `${prefix}index`;
|
|
2309
|
-
}
|
|
2310
|
-
function urlToVirtualPath(url, packageName, version) {
|
|
2311
|
-
try {
|
|
2312
|
-
const urlObj = new URL(url);
|
|
2313
|
-
const pathname = urlObj.pathname;
|
|
2314
|
-
const cleanPath = pathname.replace(/^\/v\d+\//, "/");
|
|
2315
|
-
const versionedPackage = `${packageName}@${version}`;
|
|
2316
|
-
const packageIndex = cleanPath.indexOf(versionedPackage);
|
|
2317
|
-
if (packageIndex !== -1) {
|
|
2318
|
-
const afterPackageVersion = cleanPath.substring(packageIndex + versionedPackage.length);
|
|
2319
|
-
const relativePath = afterPackageVersion.startsWith("/") ? afterPackageVersion.substring(1) : afterPackageVersion;
|
|
2320
|
-
return `node_modules/${packageName}/${relativePath || "index.d.ts"}`;
|
|
2321
|
-
}
|
|
2322
|
-
const packagePattern = new RegExp(`/${escapeRegExp(packageName)}@[^/]+(/.*)?$`);
|
|
2323
|
-
const match = pathname.match(packagePattern);
|
|
2324
|
-
if (match) {
|
|
2325
|
-
const filePath = match[1] ? match[1].substring(1) : "index.d.ts";
|
|
2326
|
-
return `node_modules/${packageName}/${filePath}`;
|
|
2327
|
-
}
|
|
2328
|
-
return `node_modules/${packageName}/${pathname.split("/").pop() || "index.d.ts"}`;
|
|
2329
|
-
} catch {
|
|
2330
|
-
return `node_modules/${packageName}/index.d.ts`;
|
|
2331
|
-
}
|
|
2332
|
-
}
|
|
2333
|
-
function escapeRegExp(string) {
|
|
2334
|
-
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2335
|
-
}
|
|
2336
|
-
function createTypeFetcher(options, cache) {
|
|
2337
|
-
return new TypeFetcher(options, cache);
|
|
2338
|
-
}
|
|
2339
|
-
|
|
2340
|
-
// libs/uipack/src/resolver/cdn-registry.ts
|
|
2341
|
-
var DEFAULT_CDN_REGISTRY = {
|
|
2342
|
-
react: {
|
|
2343
|
-
packageName: "react",
|
|
2344
|
-
defaultVersion: "19.2.4",
|
|
2345
|
-
providers: {
|
|
2346
|
-
cloudflare: {
|
|
2347
|
-
url: "https://cdnjs.cloudflare.com/ajax/libs/react/19.2.4/umd/react.production.min.js",
|
|
2348
|
-
global: "React",
|
|
2349
|
-
crossorigin: "anonymous"
|
|
2350
|
-
},
|
|
2351
|
-
jsdelivr: {
|
|
2352
|
-
url: "https://cdn.jsdelivr.net/npm/react@19.2.4/umd/react.production.min.js",
|
|
2353
|
-
global: "React",
|
|
2354
|
-
crossorigin: "anonymous"
|
|
2355
|
-
},
|
|
2356
|
-
unpkg: {
|
|
2357
|
-
url: "https://unpkg.com/react@19.2.4/umd/react.production.min.js",
|
|
2358
|
-
global: "React",
|
|
2359
|
-
crossorigin: "anonymous"
|
|
2360
|
-
},
|
|
2361
|
-
"esm.sh": {
|
|
2362
|
-
url: "https://esm.sh/react@19.2.4",
|
|
2363
|
-
esm: true,
|
|
2364
|
-
crossorigin: "anonymous"
|
|
2365
|
-
}
|
|
2366
|
-
},
|
|
2367
|
-
preferredProviders: ["esm.sh", "cloudflare", "jsdelivr", "unpkg"],
|
|
2368
|
-
metadata: {
|
|
2369
|
-
description: "A JavaScript library for building user interfaces",
|
|
2370
|
-
homepage: "https://react.dev",
|
|
2371
|
-
license: "MIT"
|
|
2372
|
-
}
|
|
2373
|
-
},
|
|
2374
|
-
"react-dom": {
|
|
2375
|
-
packageName: "react-dom",
|
|
2376
|
-
defaultVersion: "19.2.4",
|
|
2377
|
-
providers: {
|
|
2378
|
-
cloudflare: {
|
|
2379
|
-
url: "https://cdnjs.cloudflare.com/ajax/libs/react-dom/19.2.4/umd/react-dom.production.min.js",
|
|
2380
|
-
global: "ReactDOM",
|
|
2381
|
-
crossorigin: "anonymous",
|
|
2382
|
-
peerDependencies: ["react"]
|
|
2383
|
-
},
|
|
2384
|
-
jsdelivr: {
|
|
2385
|
-
url: "https://cdn.jsdelivr.net/npm/react-dom@19.2.4/umd/react-dom.production.min.js",
|
|
2386
|
-
global: "ReactDOM",
|
|
2387
|
-
crossorigin: "anonymous",
|
|
2388
|
-
peerDependencies: ["react"]
|
|
2389
|
-
},
|
|
2390
|
-
unpkg: {
|
|
2391
|
-
url: "https://unpkg.com/react-dom@19.2.4/umd/react-dom.production.min.js",
|
|
2392
|
-
global: "ReactDOM",
|
|
2393
|
-
crossorigin: "anonymous",
|
|
2394
|
-
peerDependencies: ["react"]
|
|
2395
|
-
},
|
|
2396
|
-
"esm.sh": {
|
|
2397
|
-
url: "https://esm.sh/react-dom@19.2.4",
|
|
2398
|
-
esm: true,
|
|
2399
|
-
crossorigin: "anonymous",
|
|
2400
|
-
peerDependencies: ["react"]
|
|
2401
|
-
}
|
|
2402
|
-
},
|
|
2403
|
-
preferredProviders: ["esm.sh", "cloudflare", "jsdelivr", "unpkg"],
|
|
2404
|
-
metadata: {
|
|
2405
|
-
description: "React package for working with the DOM",
|
|
2406
|
-
homepage: "https://react.dev",
|
|
2407
|
-
license: "MIT"
|
|
2408
|
-
}
|
|
2409
|
-
},
|
|
2410
|
-
"chart.js": {
|
|
2411
|
-
packageName: "chart.js",
|
|
2412
|
-
defaultVersion: "4.5.1",
|
|
2413
|
-
providers: {
|
|
2414
|
-
cloudflare: {
|
|
2415
|
-
url: "https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.5.1/chart.umd.min.js",
|
|
2416
|
-
global: "Chart",
|
|
2417
|
-
crossorigin: "anonymous"
|
|
2418
|
-
},
|
|
2419
|
-
jsdelivr: {
|
|
2420
|
-
url: "https://cdn.jsdelivr.net/npm/chart.js@4.5.1/dist/chart.umd.min.js",
|
|
2421
|
-
global: "Chart",
|
|
2422
|
-
crossorigin: "anonymous"
|
|
2423
|
-
},
|
|
2424
|
-
unpkg: {
|
|
2425
|
-
url: "https://unpkg.com/chart.js@4.5.1/dist/chart.umd.min.js",
|
|
2426
|
-
global: "Chart",
|
|
2427
|
-
crossorigin: "anonymous"
|
|
2428
|
-
}
|
|
2429
|
-
},
|
|
2430
|
-
preferredProviders: ["esm.sh", "cloudflare", "jsdelivr", "unpkg"],
|
|
2431
|
-
metadata: {
|
|
2432
|
-
description: "Simple yet flexible JavaScript charting library",
|
|
2433
|
-
homepage: "https://www.chartjs.org",
|
|
2434
|
-
license: "MIT"
|
|
2435
|
-
}
|
|
2436
|
-
},
|
|
2437
|
-
d3: {
|
|
2438
|
-
packageName: "d3",
|
|
2439
|
-
defaultVersion: "7.9.0",
|
|
2440
|
-
providers: {
|
|
2441
|
-
cloudflare: {
|
|
2442
|
-
url: "https://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.min.js",
|
|
2443
|
-
integrity: "sha512-vc58qvvBdrDR4etbxMdlTt4GBQk1qjvyORR2nrsPsFPyrs+/u5c3+1Ct6upOgdZoIl7eq6k3a1UPDSNAQi/32A==",
|
|
2444
|
-
global: "d3",
|
|
2445
|
-
crossorigin: "anonymous"
|
|
2446
|
-
},
|
|
2447
|
-
jsdelivr: {
|
|
2448
|
-
url: "https://cdn.jsdelivr.net/npm/d3@7.9.0/dist/d3.min.js",
|
|
2449
|
-
global: "d3",
|
|
2450
|
-
crossorigin: "anonymous"
|
|
2451
|
-
},
|
|
2452
|
-
unpkg: {
|
|
2453
|
-
url: "https://unpkg.com/d3@7.9.0/dist/d3.min.js",
|
|
2454
|
-
global: "d3",
|
|
2455
|
-
crossorigin: "anonymous"
|
|
2456
|
-
}
|
|
2457
|
-
},
|
|
2458
|
-
preferredProviders: ["esm.sh", "cloudflare", "jsdelivr", "unpkg"],
|
|
2459
|
-
metadata: {
|
|
2460
|
-
description: "Data-Driven Documents",
|
|
2461
|
-
homepage: "https://d3js.org",
|
|
2462
|
-
license: "ISC"
|
|
2463
|
-
}
|
|
2464
|
-
},
|
|
2465
|
-
lodash: {
|
|
2466
|
-
packageName: "lodash",
|
|
2467
|
-
defaultVersion: "4.17.21",
|
|
2468
|
-
providers: {
|
|
2469
|
-
cloudflare: {
|
|
2470
|
-
url: "https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js",
|
|
2471
|
-
integrity: "sha512-WFN04846sdKMIP5LKNphMaWzU7YpMyCU245etK3g/2ARYbPK9Ub18eG+ljU96qKRCWh+quCY7yefSmlkQw1ANQ==",
|
|
2472
|
-
global: "_",
|
|
2473
|
-
crossorigin: "anonymous"
|
|
2474
|
-
},
|
|
2475
|
-
jsdelivr: {
|
|
2476
|
-
url: "https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js",
|
|
2477
|
-
global: "_",
|
|
2478
|
-
crossorigin: "anonymous"
|
|
2479
|
-
},
|
|
2480
|
-
unpkg: {
|
|
2481
|
-
url: "https://unpkg.com/lodash@4.17.21/lodash.min.js",
|
|
2482
|
-
global: "_",
|
|
2483
|
-
crossorigin: "anonymous"
|
|
2484
|
-
}
|
|
2485
|
-
},
|
|
2486
|
-
preferredProviders: ["esm.sh", "cloudflare", "jsdelivr", "unpkg"],
|
|
2487
|
-
metadata: {
|
|
2488
|
-
description: "A modern JavaScript utility library",
|
|
2489
|
-
homepage: "https://lodash.com",
|
|
2490
|
-
license: "MIT"
|
|
2491
|
-
}
|
|
2492
|
-
}
|
|
2493
|
-
};
|
|
2494
|
-
function lookupPackage(packageName, registry = DEFAULT_CDN_REGISTRY) {
|
|
2495
|
-
return registry[packageName];
|
|
2496
|
-
}
|
|
2497
|
-
function getPackageCDNUrl(packageName, providerOrder = ["esm.sh", "cloudflare", "jsdelivr", "unpkg"], registry = DEFAULT_CDN_REGISTRY) {
|
|
2498
|
-
const entry = lookupPackage(packageName, registry);
|
|
2499
|
-
if (!entry) return void 0;
|
|
2500
|
-
for (const provider of providerOrder) {
|
|
2501
|
-
const config = entry.providers[provider];
|
|
2502
|
-
if (config?.url) {
|
|
2503
|
-
return config.url;
|
|
2504
|
-
}
|
|
2505
|
-
}
|
|
2506
|
-
for (const provider of Object.keys(entry.providers)) {
|
|
2507
|
-
const config = entry.providers[provider];
|
|
2508
|
-
if (config?.url) {
|
|
2509
|
-
return config.url;
|
|
2510
|
-
}
|
|
2511
|
-
}
|
|
2512
|
-
return void 0;
|
|
2513
|
-
}
|
|
2514
|
-
function getRegisteredPackages(registry = DEFAULT_CDN_REGISTRY) {
|
|
2515
|
-
return Object.keys(registry);
|
|
2516
|
-
}
|
|
2517
|
-
function isPackageRegistered(packageName, registry = DEFAULT_CDN_REGISTRY) {
|
|
2518
|
-
return packageName in registry;
|
|
2519
|
-
}
|
|
2520
|
-
function mergeRegistries(customRegistry) {
|
|
2521
|
-
return {
|
|
2522
|
-
...DEFAULT_CDN_REGISTRY,
|
|
2523
|
-
...customRegistry
|
|
2524
|
-
};
|
|
2525
|
-
}
|
|
2526
|
-
function getPackagePeerDependencies(packageName, registry = DEFAULT_CDN_REGISTRY) {
|
|
2527
|
-
const entry = lookupPackage(packageName, registry);
|
|
2528
|
-
if (!entry) return [];
|
|
2529
|
-
for (const provider of Object.keys(entry.providers)) {
|
|
2530
|
-
const config = entry.providers[provider];
|
|
2531
|
-
if (config?.peerDependencies) {
|
|
2532
|
-
return config.peerDependencies;
|
|
2533
|
-
}
|
|
2534
|
-
}
|
|
2535
|
-
return [];
|
|
2536
|
-
}
|
|
2537
|
-
|
|
2538
|
-
// libs/uipack/src/resolver/import-parser.ts
|
|
2539
|
-
var IMPORT_PATTERNS = {
|
|
2540
|
-
named: /import\s*\{([^}]+)\}\s*from\s*['"]([^'"]+)['"]/g,
|
|
2541
|
-
default: /import\s+(\w+)\s+from\s*['"]([^'"]+)['"]/g,
|
|
2542
|
-
namespace: /import\s*\*\s*as\s+(\w+)\s+from\s*['"]([^'"]+)['"]/g,
|
|
2543
|
-
sideEffect: /import\s*['"]([^'"]+)['"]/g,
|
|
2544
|
-
dynamic: /(?:await\s+)?import\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
2545
|
-
defaultAndNamed: /import\s+(\w+)\s*,\s*\{([^}]+)\}\s*from\s*['"]([^'"]+)['"]/g,
|
|
2546
|
-
defaultAndNamespace: /import\s+(\w+)\s*,\s*\*\s*as\s+(\w+)\s+from\s*['"]([^'"]+)['"]/g,
|
|
2547
|
-
reExport: /export\s*\{[^}]+\}\s*from\s*['"]([^'"]+)['"]/g,
|
|
2548
|
-
reExportAll: /export\s*\*\s*from\s*['"]([^'"]+)['"]/g
|
|
2549
|
-
};
|
|
2550
|
-
function parseNamedImports(namedString) {
|
|
2551
|
-
return namedString.split(",").map((s) => s.trim()).filter((s) => s.length > 0).map((s) => {
|
|
2552
|
-
const asMatch = s.match(/^(\w+)\s+as\s+\w+$/);
|
|
2553
|
-
return asMatch ? asMatch[1] : s;
|
|
2554
|
-
});
|
|
2555
|
-
}
|
|
2556
|
-
function isRelativeImport2(specifier) {
|
|
2557
|
-
return specifier.startsWith("./") || specifier.startsWith("../") || specifier.startsWith("/");
|
|
2558
|
-
}
|
|
2559
|
-
function isExternalImport(specifier) {
|
|
2560
|
-
return !isRelativeImport2(specifier) && !specifier.startsWith("#");
|
|
2561
|
-
}
|
|
2562
|
-
function getPackageName(specifier) {
|
|
2563
|
-
if (specifier.startsWith("@")) {
|
|
2564
|
-
const parts = specifier.split("/");
|
|
2565
|
-
if (parts.length >= 2) {
|
|
2566
|
-
return `${parts[0]}/${parts[1]}`;
|
|
2567
|
-
}
|
|
2568
|
-
return specifier;
|
|
2569
|
-
}
|
|
2570
|
-
const firstSlash = specifier.indexOf("/");
|
|
2571
|
-
return firstSlash === -1 ? specifier : specifier.slice(0, firstSlash);
|
|
2572
|
-
}
|
|
2573
|
-
function getLineNumber(source, index) {
|
|
2574
|
-
let line = 1;
|
|
2575
|
-
for (let i = 0; i < index && i < source.length; i++) {
|
|
2576
|
-
if (source[i] === "\n") {
|
|
2577
|
-
line++;
|
|
2578
|
-
}
|
|
2579
|
-
}
|
|
2580
|
-
return line;
|
|
2581
|
-
}
|
|
2582
|
-
function getColumnNumber(source, index) {
|
|
2583
|
-
let column = 0;
|
|
2584
|
-
for (let i = index - 1; i >= 0 && source[i] !== "\n"; i--) {
|
|
2585
|
-
column++;
|
|
2586
|
-
}
|
|
2587
|
-
return column;
|
|
2588
|
-
}
|
|
2589
|
-
function parseImports(source) {
|
|
2590
|
-
const imports = [];
|
|
2591
|
-
const seenStatements = /* @__PURE__ */ new Set();
|
|
2592
|
-
const addImport = (imp) => {
|
|
2593
|
-
const key = `${imp.type}:${imp.specifier}:${imp.statement}`;
|
|
2594
|
-
if (!seenStatements.has(key)) {
|
|
2595
|
-
seenStatements.add(key);
|
|
2596
|
-
imports.push(imp);
|
|
2597
|
-
}
|
|
2598
|
-
};
|
|
2599
|
-
let match;
|
|
2600
|
-
const defaultAndNamedRegex = new RegExp(IMPORT_PATTERNS.defaultAndNamed.source, "g");
|
|
2601
|
-
while ((match = defaultAndNamedRegex.exec(source)) !== null) {
|
|
2602
|
-
const [statement, defaultName, namedString, specifier] = match;
|
|
2603
|
-
addImport({
|
|
2604
|
-
statement,
|
|
2605
|
-
specifier,
|
|
2606
|
-
type: "default",
|
|
2607
|
-
defaultImport: defaultName,
|
|
2608
|
-
namedImports: parseNamedImports(namedString),
|
|
2609
|
-
line: getLineNumber(source, match.index),
|
|
2610
|
-
column: getColumnNumber(source, match.index)
|
|
2611
|
-
});
|
|
2612
|
-
}
|
|
2613
|
-
const defaultAndNamespaceRegex = new RegExp(IMPORT_PATTERNS.defaultAndNamespace.source, "g");
|
|
2614
|
-
while ((match = defaultAndNamespaceRegex.exec(source)) !== null) {
|
|
2615
|
-
const [statement, defaultName, namespaceName, specifier] = match;
|
|
2616
|
-
addImport({
|
|
2617
|
-
statement,
|
|
2618
|
-
specifier,
|
|
2619
|
-
type: "default",
|
|
2620
|
-
defaultImport: defaultName,
|
|
2621
|
-
namespaceImport: namespaceName,
|
|
2622
|
-
line: getLineNumber(source, match.index),
|
|
2623
|
-
column: getColumnNumber(source, match.index)
|
|
2624
|
-
});
|
|
2625
|
-
}
|
|
2626
|
-
const namedRegex = new RegExp(IMPORT_PATTERNS.named.source, "g");
|
|
2627
|
-
while ((match = namedRegex.exec(source)) !== null) {
|
|
2628
|
-
const [statement, namedString, specifier] = match;
|
|
2629
|
-
addImport({
|
|
2630
|
-
statement,
|
|
2631
|
-
specifier,
|
|
2632
|
-
type: "named",
|
|
2633
|
-
namedImports: parseNamedImports(namedString),
|
|
2634
|
-
line: getLineNumber(source, match.index),
|
|
2635
|
-
column: getColumnNumber(source, match.index)
|
|
2636
|
-
});
|
|
2637
|
-
}
|
|
2638
|
-
const defaultRegex = new RegExp(IMPORT_PATTERNS.default.source, "g");
|
|
2639
|
-
while ((match = defaultRegex.exec(source)) !== null) {
|
|
2640
|
-
const [statement, defaultName, specifier] = match;
|
|
2641
|
-
const afterMatch = source.slice(match.index + match[0].length - specifier.length - 2);
|
|
2642
|
-
if (afterMatch.startsWith(",")) continue;
|
|
2643
|
-
addImport({
|
|
2644
|
-
statement,
|
|
2645
|
-
specifier,
|
|
2646
|
-
type: "default",
|
|
2647
|
-
defaultImport: defaultName,
|
|
2648
|
-
line: getLineNumber(source, match.index),
|
|
2649
|
-
column: getColumnNumber(source, match.index)
|
|
2650
|
-
});
|
|
2651
|
-
}
|
|
2652
|
-
const namespaceRegex = new RegExp(IMPORT_PATTERNS.namespace.source, "g");
|
|
2653
|
-
while ((match = namespaceRegex.exec(source)) !== null) {
|
|
2654
|
-
const [statement, namespaceName, specifier] = match;
|
|
2655
|
-
addImport({
|
|
2656
|
-
statement,
|
|
2657
|
-
specifier,
|
|
2658
|
-
type: "namespace",
|
|
2659
|
-
namespaceImport: namespaceName,
|
|
2660
|
-
line: getLineNumber(source, match.index),
|
|
2661
|
-
column: getColumnNumber(source, match.index)
|
|
2662
|
-
});
|
|
2663
|
-
}
|
|
2664
|
-
const sideEffectRegex = new RegExp(IMPORT_PATTERNS.sideEffect.source, "g");
|
|
2665
|
-
while ((match = sideEffectRegex.exec(source)) !== null) {
|
|
2666
|
-
const [statement, specifier] = match;
|
|
2667
|
-
const beforeMatch = source.slice(Math.max(0, match.index - 50), match.index);
|
|
2668
|
-
if (beforeMatch.includes("from")) continue;
|
|
2669
|
-
addImport({
|
|
2670
|
-
statement,
|
|
2671
|
-
specifier,
|
|
2672
|
-
type: "side-effect",
|
|
2673
|
-
line: getLineNumber(source, match.index),
|
|
2674
|
-
column: getColumnNumber(source, match.index)
|
|
2675
|
-
});
|
|
2676
|
-
}
|
|
2677
|
-
const dynamicRegex = new RegExp(IMPORT_PATTERNS.dynamic.source, "g");
|
|
2678
|
-
while ((match = dynamicRegex.exec(source)) !== null) {
|
|
2679
|
-
const [statement, specifier] = match;
|
|
2680
|
-
addImport({
|
|
2681
|
-
statement,
|
|
2682
|
-
specifier,
|
|
2683
|
-
type: "dynamic",
|
|
2684
|
-
line: getLineNumber(source, match.index),
|
|
2685
|
-
column: getColumnNumber(source, match.index)
|
|
2686
|
-
});
|
|
2687
|
-
}
|
|
2688
|
-
const reExportRegex = new RegExp(IMPORT_PATTERNS.reExport.source, "g");
|
|
2689
|
-
while ((match = reExportRegex.exec(source)) !== null) {
|
|
2690
|
-
const [statement, specifier] = match;
|
|
2691
|
-
addImport({
|
|
2692
|
-
statement,
|
|
2693
|
-
specifier,
|
|
2694
|
-
type: "named",
|
|
2695
|
-
line: getLineNumber(source, match.index),
|
|
2696
|
-
column: getColumnNumber(source, match.index)
|
|
2697
|
-
});
|
|
2698
|
-
}
|
|
2699
|
-
const reExportAllRegex = new RegExp(IMPORT_PATTERNS.reExportAll.source, "g");
|
|
2700
|
-
while ((match = reExportAllRegex.exec(source)) !== null) {
|
|
2701
|
-
const [statement, specifier] = match;
|
|
2702
|
-
addImport({
|
|
2703
|
-
statement,
|
|
2704
|
-
specifier,
|
|
2705
|
-
type: "namespace",
|
|
2706
|
-
line: getLineNumber(source, match.index),
|
|
2707
|
-
column: getColumnNumber(source, match.index)
|
|
2708
|
-
});
|
|
2709
|
-
}
|
|
2710
|
-
const externalImports = imports.filter((imp) => isExternalImport(imp.specifier));
|
|
2711
|
-
const relativeImports = imports.filter((imp) => isRelativeImport2(imp.specifier));
|
|
2712
|
-
const externalPackages = [...new Set(externalImports.map((imp) => getPackageName(imp.specifier)))];
|
|
2713
|
-
return {
|
|
2714
|
-
imports,
|
|
2715
|
-
externalImports,
|
|
2716
|
-
relativeImports,
|
|
2717
|
-
externalPackages
|
|
2718
|
-
};
|
|
2719
|
-
}
|
|
2720
|
-
function extractExternalPackages(source) {
|
|
2721
|
-
return parseImports(source).externalPackages;
|
|
2722
|
-
}
|
|
2723
|
-
|
|
2724
|
-
// libs/uipack/src/resolver/esm-sh.resolver.ts
|
|
2725
|
-
var DEFAULT_FALLBACK_CDN = "https://esm.sh";
|
|
2726
|
-
function createResolverWithOverrides(overrides, baseOptions) {
|
|
2727
|
-
const base = createEsmShResolver(baseOptions);
|
|
2728
|
-
return {
|
|
2729
|
-
overrides,
|
|
2730
|
-
resolve(specifier, context) {
|
|
2731
|
-
if (overrides[specifier]) {
|
|
2732
|
-
return { value: overrides[specifier], type: "url" };
|
|
2733
|
-
}
|
|
2734
|
-
for (const [prefix, url] of Object.entries(overrides)) {
|
|
2735
|
-
if (specifier.startsWith(prefix + "/")) {
|
|
2736
|
-
const subpath = specifier.slice(prefix.length);
|
|
2737
|
-
return { value: url + subpath, type: "url" };
|
|
2738
|
-
}
|
|
2739
|
-
}
|
|
2740
|
-
return base.resolve(specifier, context);
|
|
2741
|
-
}
|
|
2742
|
-
};
|
|
2743
|
-
}
|
|
2744
|
-
function createEsmShResolver(options = {}) {
|
|
2745
|
-
const {
|
|
2746
|
-
fallbackCdnBase = DEFAULT_FALLBACK_CDN,
|
|
2747
|
-
registry = DEFAULT_CDN_REGISTRY,
|
|
2748
|
-
providerOrder = ["esm.sh", "cloudflare", "jsdelivr", "unpkg"]
|
|
2749
|
-
} = options;
|
|
2750
|
-
return {
|
|
2751
|
-
resolve(specifier, _context) {
|
|
2752
|
-
if (specifier.startsWith("./") || specifier.startsWith("../") || specifier.startsWith("/")) {
|
|
2753
|
-
return null;
|
|
2754
|
-
}
|
|
2755
|
-
if (specifier.startsWith("node:") || specifier.startsWith("#")) {
|
|
2756
|
-
return null;
|
|
2757
|
-
}
|
|
2758
|
-
const pkgName = getPackageName(specifier);
|
|
2759
|
-
const entry = lookupPackage(pkgName, registry);
|
|
2760
|
-
if (entry) {
|
|
2761
|
-
for (const provider of providerOrder) {
|
|
2762
|
-
const config = entry.providers[provider];
|
|
2763
|
-
if (config?.url) {
|
|
2764
|
-
const subpath = specifier.slice(pkgName.length);
|
|
2765
|
-
if (subpath && config.url.includes("esm.sh/")) {
|
|
2766
|
-
return {
|
|
2767
|
-
value: config.url + subpath,
|
|
2768
|
-
type: "url",
|
|
2769
|
-
integrity: config.integrity
|
|
2770
|
-
};
|
|
2771
|
-
}
|
|
2772
|
-
if (subpath) {
|
|
2773
|
-
return {
|
|
2774
|
-
value: `${fallbackCdnBase}/${specifier}`,
|
|
2775
|
-
type: "url"
|
|
2776
|
-
};
|
|
2777
|
-
}
|
|
2778
|
-
if (config.global && !config.esm) {
|
|
2779
|
-
return {
|
|
2780
|
-
value: config.url,
|
|
2781
|
-
type: "url",
|
|
2782
|
-
integrity: config.integrity
|
|
2783
|
-
};
|
|
2784
|
-
}
|
|
2785
|
-
return {
|
|
2786
|
-
value: config.url,
|
|
2787
|
-
type: "url",
|
|
2788
|
-
integrity: config.integrity
|
|
2789
|
-
};
|
|
2790
|
-
}
|
|
2791
|
-
}
|
|
2792
|
-
}
|
|
2793
|
-
return {
|
|
2794
|
-
value: `${fallbackCdnBase}/${specifier}`,
|
|
2795
|
-
type: "url"
|
|
2796
|
-
};
|
|
2797
|
-
}
|
|
2798
|
-
};
|
|
2799
|
-
}
|
|
2800
|
-
|
|
2801
|
-
// libs/uipack/src/resolver/import-map.ts
|
|
2802
|
-
function createImportMapFromResolved(resolved) {
|
|
2803
|
-
const imports = {};
|
|
2804
|
-
const integrity = {};
|
|
2805
|
-
for (const [specifier, res] of Object.entries(resolved)) {
|
|
2806
|
-
if (res.type === "url") {
|
|
2807
|
-
imports[specifier] = res.value;
|
|
2808
|
-
if (res.integrity) {
|
|
2809
|
-
integrity[res.value] = res.integrity;
|
|
2810
|
-
}
|
|
2811
|
-
}
|
|
2812
|
-
}
|
|
2813
|
-
return {
|
|
2814
|
-
imports,
|
|
2815
|
-
integrity: Object.keys(integrity).length > 0 ? integrity : void 0
|
|
2816
|
-
};
|
|
2817
|
-
}
|
|
2818
|
-
function createImportMap(dependencies) {
|
|
2819
|
-
const imports = {};
|
|
2820
|
-
const integrity = {};
|
|
2821
|
-
for (const dep of dependencies) {
|
|
2822
|
-
imports[dep.packageName] = dep.cdnUrl;
|
|
2823
|
-
if (dep.integrity) {
|
|
2824
|
-
integrity[dep.cdnUrl] = dep.integrity;
|
|
2825
|
-
}
|
|
2826
|
-
}
|
|
2827
|
-
return {
|
|
2828
|
-
imports,
|
|
2829
|
-
integrity: Object.keys(integrity).length > 0 ? integrity : void 0
|
|
2830
|
-
};
|
|
2831
|
-
}
|
|
2832
|
-
function mergeImportMaps(...maps) {
|
|
2833
|
-
const imports = {};
|
|
2834
|
-
const integrity = {};
|
|
2835
|
-
const scopes = {};
|
|
2836
|
-
for (const map of maps) {
|
|
2837
|
-
Object.assign(imports, map.imports);
|
|
2838
|
-
if (map.integrity) {
|
|
2839
|
-
Object.assign(integrity, map.integrity);
|
|
2840
|
-
}
|
|
2841
|
-
if (map.scopes) {
|
|
2842
|
-
for (const [scope, mappings] of Object.entries(map.scopes)) {
|
|
2843
|
-
scopes[scope] = { ...scopes[scope], ...mappings };
|
|
2844
|
-
}
|
|
2845
|
-
}
|
|
2846
|
-
}
|
|
2847
|
-
return {
|
|
2848
|
-
imports,
|
|
2849
|
-
integrity: Object.keys(integrity).length > 0 ? integrity : void 0,
|
|
2850
|
-
scopes: Object.keys(scopes).length > 0 ? scopes : void 0
|
|
2851
|
-
};
|
|
2852
|
-
}
|
|
2853
|
-
function generateImportMapScriptTag(map) {
|
|
2854
|
-
const json = JSON.stringify(map, null, 2).replace(/<\//g, "<\\/");
|
|
2855
|
-
return `<script type="importmap">
|
|
2856
|
-
${json}
|
|
2857
|
-
</script>`;
|
|
2858
|
-
}
|
|
2859
|
-
function generateImportMapScriptTagMinified(map) {
|
|
2860
|
-
const json = JSON.stringify(map).replace(/<\//g, "<\\/");
|
|
2861
|
-
return `<script type="importmap">${json}</script>`;
|
|
2862
|
-
}
|
|
2863
|
-
function generateCDNScriptTags(dependencies) {
|
|
2864
|
-
return dependencies.filter((dep) => !dep.esm).map((dep) => {
|
|
2865
|
-
const attrs = [`src="${escapeHtmlAttr(dep.cdnUrl)}"`];
|
|
2866
|
-
if (dep.integrity) {
|
|
2867
|
-
attrs.push(`integrity="${escapeHtmlAttr(dep.integrity)}"`);
|
|
2868
|
-
}
|
|
2869
|
-
attrs.push('crossorigin="anonymous"');
|
|
2870
|
-
return `<script ${attrs.join(" ")}></script>`;
|
|
2871
|
-
});
|
|
2872
|
-
}
|
|
2873
|
-
function generateDependencyHTML(dependencies, options = {}) {
|
|
2874
|
-
const { minify = false } = options;
|
|
2875
|
-
const parts = [];
|
|
2876
|
-
const importMap = createImportMap(dependencies);
|
|
2877
|
-
const importMapTag = minify ? generateImportMapScriptTagMinified(importMap) : generateImportMapScriptTag(importMap);
|
|
2878
|
-
parts.push(importMapTag);
|
|
2879
|
-
const umdTags = generateCDNScriptTags(dependencies);
|
|
2880
|
-
parts.push(...umdTags);
|
|
2881
|
-
return parts.join(minify ? "" : "\n");
|
|
2882
|
-
}
|
|
2883
|
-
function validateImportMap(map) {
|
|
2884
|
-
const errors = [];
|
|
2885
|
-
if (typeof map !== "object" || map === null) {
|
|
2886
|
-
return { valid: false, errors: ["Import map must be an object"] };
|
|
2887
|
-
}
|
|
2888
|
-
const obj = map;
|
|
2889
|
-
if (!obj["imports"]) {
|
|
2890
|
-
errors.push('Import map must have an "imports" field');
|
|
2891
|
-
} else if (typeof obj["imports"] !== "object" || obj["imports"] === null) {
|
|
2892
|
-
errors.push('"imports" must be an object');
|
|
2893
|
-
} else {
|
|
2894
|
-
const imports = obj["imports"];
|
|
2895
|
-
for (const [key, value] of Object.entries(imports)) {
|
|
2896
|
-
if (typeof value !== "string") {
|
|
2897
|
-
errors.push(`Import "${key}" must map to a string URL`);
|
|
2898
|
-
} else if (!value.startsWith("https://")) {
|
|
2899
|
-
errors.push(`Import "${key}" URL must use HTTPS: ${value}`);
|
|
2900
|
-
}
|
|
2901
|
-
}
|
|
2902
|
-
}
|
|
2903
|
-
if (obj["integrity"] !== void 0) {
|
|
2904
|
-
if (typeof obj["integrity"] !== "object" || obj["integrity"] === null) {
|
|
2905
|
-
errors.push('"integrity" must be an object');
|
|
2906
|
-
} else {
|
|
2907
|
-
const integrity = obj["integrity"];
|
|
2908
|
-
for (const [url, hash] of Object.entries(integrity)) {
|
|
2909
|
-
if (typeof hash !== "string") {
|
|
2910
|
-
errors.push(`Integrity for "${url}" must be a string`);
|
|
2911
|
-
} else if (!hash.match(/^sha(256|384|512)-/)) {
|
|
2912
|
-
errors.push(`Invalid integrity hash format for "${url}": ${hash}`);
|
|
2913
|
-
}
|
|
2914
|
-
}
|
|
2915
|
-
}
|
|
2916
|
-
}
|
|
2917
|
-
return { valid: errors.length === 0, errors };
|
|
2918
|
-
}
|
|
2919
|
-
|
|
2920
|
-
// libs/uipack/src/resolver/import-rewriter.ts
|
|
2921
|
-
function rewriteImports(source, options = {}) {
|
|
2922
|
-
const { resolver = createEsmShResolver(), skipPackages = [], overrides = {} } = options;
|
|
2923
|
-
const skipSet = new Set(skipPackages);
|
|
2924
|
-
const rewrites = /* @__PURE__ */ new Map();
|
|
2925
|
-
const fallbackPackages = [];
|
|
2926
|
-
const parsed = parseImports(source);
|
|
2927
|
-
const specifierMap = /* @__PURE__ */ new Map();
|
|
2928
|
-
for (const imp of parsed.externalImports) {
|
|
2929
|
-
const specifier = imp.specifier;
|
|
2930
|
-
if (specifierMap.has(specifier)) continue;
|
|
2931
|
-
if (specifier.startsWith("node:")) continue;
|
|
2932
|
-
const pkgName = getPackageName(specifier);
|
|
2933
|
-
if (skipSet.has(pkgName)) continue;
|
|
2934
|
-
if (overrides[specifier]) {
|
|
2935
|
-
specifierMap.set(specifier, overrides[specifier]);
|
|
2936
|
-
continue;
|
|
2937
|
-
}
|
|
2938
|
-
if (overrides[pkgName]) {
|
|
2939
|
-
const subpath = specifier.slice(pkgName.length);
|
|
2940
|
-
specifierMap.set(specifier, overrides[pkgName] + subpath);
|
|
2941
|
-
continue;
|
|
2942
|
-
}
|
|
2943
|
-
const resolved = resolver.resolve(specifier);
|
|
2944
|
-
if (resolved && resolved.type === "url") {
|
|
2945
|
-
specifierMap.set(specifier, resolved.value);
|
|
2946
|
-
}
|
|
2947
|
-
}
|
|
2948
|
-
let code = source;
|
|
2949
|
-
for (const [specifier, cdnUrl] of specifierMap) {
|
|
2950
|
-
const escaped = escapeRegExp2(specifier);
|
|
2951
|
-
const singleQuotePattern = new RegExp(`((?:import|export)\\s+(?:[^'"]*\\s+)?from\\s+)'${escaped}'`, "g");
|
|
2952
|
-
const doubleQuotePattern = new RegExp(`((?:import|export)\\s+(?:[^'"]*\\s+)?from\\s+)"${escaped}"`, "g");
|
|
2953
|
-
const dynamicSinglePattern = new RegExp(`(import\\s*\\(\\s*)'${escaped}'`, "g");
|
|
2954
|
-
const dynamicDoublePattern = new RegExp(`(import\\s*\\(\\s*)"${escaped}"`, "g");
|
|
2955
|
-
const sideEffectSinglePattern = new RegExp(`(import\\s+)'${escaped}'`, "g");
|
|
2956
|
-
const sideEffectDoublePattern = new RegExp(`(import\\s+)"${escaped}"`, "g");
|
|
2957
|
-
const beforeLength = code.length;
|
|
2958
|
-
code = code.replace(singleQuotePattern, `$1'${cdnUrl}'`).replace(doubleQuotePattern, `$1"${cdnUrl}"`).replace(dynamicSinglePattern, `$1'${cdnUrl}'`).replace(dynamicDoublePattern, `$1"${cdnUrl}"`).replace(sideEffectSinglePattern, `$1'${cdnUrl}'`).replace(sideEffectDoublePattern, `$1"${cdnUrl}"`);
|
|
2959
|
-
if (code.length !== beforeLength || code !== source) {
|
|
2960
|
-
rewrites.set(specifier, cdnUrl);
|
|
2961
|
-
}
|
|
2962
|
-
}
|
|
2963
|
-
return {
|
|
2964
|
-
code,
|
|
2965
|
-
rewrittenCount: rewrites.size,
|
|
2966
|
-
rewrites,
|
|
2967
|
-
fallbackPackages
|
|
2968
|
-
};
|
|
2969
|
-
}
|
|
2970
|
-
function escapeRegExp2(str) {
|
|
2971
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2972
|
-
}
|
|
2973
|
-
|
|
2974
|
-
// libs/uipack/src/shell/csp.ts
|
|
2975
|
-
var DEFAULT_CDN_DOMAINS = [
|
|
2976
|
-
"https://cdn.jsdelivr.net",
|
|
2977
|
-
"https://cdnjs.cloudflare.com",
|
|
2978
|
-
"https://fonts.googleapis.com",
|
|
2979
|
-
"https://fonts.gstatic.com",
|
|
2980
|
-
"https://esm.sh"
|
|
2981
|
-
];
|
|
2982
|
-
var DEFAULT_CSP_DIRECTIVES = [
|
|
2983
|
-
"default-src 'none'",
|
|
2984
|
-
`script-src 'self' 'unsafe-inline' ${DEFAULT_CDN_DOMAINS.join(" ")}`,
|
|
2985
|
-
`style-src 'self' 'unsafe-inline' ${DEFAULT_CDN_DOMAINS.join(" ")}`,
|
|
2986
|
-
`img-src 'self' data: ${DEFAULT_CDN_DOMAINS.join(" ")}`,
|
|
2987
|
-
`font-src 'self' data: ${DEFAULT_CDN_DOMAINS.join(" ")}`,
|
|
2988
|
-
`connect-src ${DEFAULT_CDN_DOMAINS.join(" ")}`,
|
|
2989
|
-
"object-src 'self' data:"
|
|
2990
|
-
];
|
|
2991
|
-
var RESTRICTIVE_CSP_DIRECTIVES = [
|
|
2992
|
-
"default-src 'none'",
|
|
2993
|
-
"script-src 'self' 'unsafe-inline'",
|
|
2994
|
-
"style-src 'self' 'unsafe-inline'",
|
|
2995
|
-
"img-src 'self' data:",
|
|
2996
|
-
"font-src 'self' data:",
|
|
2997
|
-
"connect-src 'none'",
|
|
2998
|
-
"object-src 'self' data:"
|
|
2999
|
-
];
|
|
3000
|
-
function buildCSPDirectives(csp) {
|
|
3001
|
-
if (!csp) {
|
|
3002
|
-
return [...DEFAULT_CSP_DIRECTIVES];
|
|
3003
|
-
}
|
|
3004
|
-
const validResourceDomains = sanitizeCSPDomains(csp.resourceDomains);
|
|
3005
|
-
const validConnectDomains = sanitizeCSPDomains(csp.connectDomains);
|
|
3006
|
-
const allResourceDomains = [.../* @__PURE__ */ new Set([...DEFAULT_CDN_DOMAINS, ...validResourceDomains])];
|
|
3007
|
-
const directives = [
|
|
3008
|
-
"default-src 'none'",
|
|
3009
|
-
`script-src 'self' 'unsafe-inline' ${allResourceDomains.join(" ")}`,
|
|
3010
|
-
`style-src 'self' 'unsafe-inline' ${allResourceDomains.join(" ")}`
|
|
3011
|
-
];
|
|
3012
|
-
const imgSources = ["'self'", "data:", ...allResourceDomains];
|
|
3013
|
-
directives.push(`img-src ${imgSources.join(" ")}`);
|
|
3014
|
-
const fontSources = ["'self'", "data:", ...allResourceDomains];
|
|
3015
|
-
directives.push(`font-src ${fontSources.join(" ")}`);
|
|
3016
|
-
const connectSources = [.../* @__PURE__ */ new Set([...allResourceDomains, ...validConnectDomains])];
|
|
3017
|
-
directives.push(`connect-src ${connectSources.join(" ")}`);
|
|
3018
|
-
directives.push("object-src 'self' data:");
|
|
3019
|
-
return directives;
|
|
3020
|
-
}
|
|
3021
|
-
function buildCSPMetaTag(csp) {
|
|
3022
|
-
const directives = buildCSPDirectives(csp);
|
|
3023
|
-
const content = directives.join("; ");
|
|
3024
|
-
return `<meta http-equiv="Content-Security-Policy" content="${escapeAttribute(content)}">`;
|
|
3025
|
-
}
|
|
3026
|
-
var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]"]);
|
|
3027
|
-
function validateCSPDomain(domain) {
|
|
3028
|
-
if (typeof domain !== "string" || /[\s;,'"]/.test(domain)) return false;
|
|
3029
|
-
if (/[?#]/.test(domain)) return false;
|
|
3030
|
-
if (domain.includes("*")) {
|
|
3031
|
-
const wildcard = /^(?:https|wss):\/\/\*\.(.*)$/.exec(domain);
|
|
3032
|
-
return wildcard !== null && /^([a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/.test(wildcard[1]);
|
|
3033
|
-
}
|
|
3034
|
-
try {
|
|
3035
|
-
const url = new URL(domain);
|
|
3036
|
-
if (url.protocol === "https:" || url.protocol === "wss:") return true;
|
|
3037
|
-
return (url.protocol === "http:" || url.protocol === "ws:") && LOOPBACK_HOSTS.has(url.hostname);
|
|
3038
|
-
} catch {
|
|
3039
|
-
return false;
|
|
3040
|
-
}
|
|
3041
|
-
}
|
|
3042
|
-
var reportedInvalidDomains = /* @__PURE__ */ new Set();
|
|
3043
|
-
function sanitizeCSPDomains(domains) {
|
|
3044
|
-
if (!domains) return [];
|
|
3045
|
-
const valid = [];
|
|
3046
|
-
for (const domain of domains) {
|
|
3047
|
-
if (validateCSPDomain(domain)) {
|
|
3048
|
-
valid.push(domain);
|
|
3049
|
-
} else if (!reportedInvalidDomains.has(domain)) {
|
|
3050
|
-
reportedInvalidDomains.add(domain);
|
|
3051
|
-
console.warn(
|
|
3052
|
-
`Invalid CSP domain ignored: ${domain} (expected an https:// or wss:// origin, or http:// / ws:// on localhost)`
|
|
3053
|
-
);
|
|
3054
|
-
}
|
|
3055
|
-
}
|
|
3056
|
-
return valid;
|
|
3057
|
-
}
|
|
3058
|
-
function escapeAttribute(str) {
|
|
3059
|
-
return str.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">");
|
|
3060
|
-
}
|
|
3061
|
-
|
|
3062
|
-
// libs/uipack/src/shell/custom-shell-types.ts
|
|
3063
|
-
var SHELL_PLACEHOLDER_NAMES = ["CSP", "DATA", "BRIDGE", "CONTENT", "TITLE"];
|
|
3064
|
-
var SHELL_PLACEHOLDERS = {
|
|
3065
|
-
CSP: "{{CSP}}",
|
|
3066
|
-
DATA: "{{DATA}}",
|
|
3067
|
-
BRIDGE: "{{BRIDGE}}",
|
|
3068
|
-
CONTENT: "{{CONTENT}}",
|
|
3069
|
-
TITLE: "{{TITLE}}"
|
|
3070
|
-
};
|
|
3071
|
-
var REQUIRED_PLACEHOLDERS = ["CONTENT"];
|
|
3072
|
-
var OPTIONAL_PLACEHOLDERS = ["CSP", "DATA", "BRIDGE", "TITLE"];
|
|
3073
|
-
function isInlineShellSource(source) {
|
|
3074
|
-
return typeof source === "object" && source !== null && "inline" in source;
|
|
3075
|
-
}
|
|
3076
|
-
function isUrlShellSource(source) {
|
|
3077
|
-
return typeof source === "object" && source !== null && "url" in source;
|
|
3078
|
-
}
|
|
3079
|
-
function isNpmShellSource(source) {
|
|
3080
|
-
return typeof source === "object" && source !== null && "npm" in source;
|
|
3081
|
-
}
|
|
3082
|
-
|
|
3083
|
-
// libs/uipack/src/shell/custom-shell-applier.ts
|
|
3084
|
-
var PLACEHOLDER_PATTERN = new RegExp(`\\{\\{(${SHELL_PLACEHOLDER_NAMES.join("|")})\\}\\}`, "g");
|
|
3085
|
-
var VALUE_KEYS = {
|
|
3086
|
-
CSP: "csp",
|
|
3087
|
-
DATA: "data",
|
|
3088
|
-
BRIDGE: "bridge",
|
|
3089
|
-
CONTENT: "content",
|
|
3090
|
-
TITLE: "title"
|
|
3091
|
-
};
|
|
3092
|
-
function applyShellTemplate(template, values) {
|
|
3093
|
-
return template.replace(PLACEHOLDER_PATTERN, (_token, name) => values[VALUE_KEYS[name]]);
|
|
3094
|
-
}
|
|
3095
|
-
|
|
3096
|
-
// libs/uipack/src/shell/custom-shell-validator.ts
|
|
3097
|
-
function validateShellTemplate(template) {
|
|
3098
|
-
const found = {};
|
|
3099
|
-
for (const name of SHELL_PLACEHOLDER_NAMES) {
|
|
3100
|
-
found[name] = template.includes(SHELL_PLACEHOLDERS[name]);
|
|
3101
|
-
}
|
|
3102
|
-
const missingRequired = REQUIRED_PLACEHOLDERS.filter((name) => !found[name]);
|
|
3103
|
-
const missingOptional = OPTIONAL_PLACEHOLDERS.filter((name) => !found[name]);
|
|
3104
|
-
return {
|
|
3105
|
-
valid: missingRequired.length === 0,
|
|
3106
|
-
found,
|
|
3107
|
-
missingRequired,
|
|
3108
|
-
missingOptional
|
|
3109
|
-
};
|
|
3110
|
-
}
|
|
3111
|
-
|
|
3112
|
-
// libs/uipack/src/shell/trusted-html.ts
|
|
3113
|
-
var TRUSTED_HTML = /* @__PURE__ */ Symbol.for("@frontmcp/uipack/trusted-html");
|
|
3114
|
-
function trustedHtml(markup) {
|
|
3115
|
-
const value = String(markup);
|
|
3116
|
-
return Object.freeze({ [TRUSTED_HTML]: value, toString: () => value });
|
|
3117
|
-
}
|
|
3118
|
-
function isTrustedHtml(value) {
|
|
3119
|
-
return typeof value === "object" && value !== null && typeof value[TRUSTED_HTML] === "string";
|
|
3120
|
-
}
|
|
3121
|
-
function interpolate(value) {
|
|
3122
|
-
if (isTrustedHtml(value)) return value[TRUSTED_HTML];
|
|
3123
|
-
if (Array.isArray(value)) return value.map(interpolate).join("");
|
|
3124
|
-
if (value === false) return "";
|
|
3125
|
-
return escapeHtml(value);
|
|
3126
|
-
}
|
|
3127
|
-
function html(strings, ...values) {
|
|
3128
|
-
let markup = strings[0];
|
|
3129
|
-
for (let index = 0; index < values.length; index++) {
|
|
3130
|
-
markup += interpolate(values[index]) + strings[index + 1];
|
|
3131
|
-
}
|
|
3132
|
-
return trustedHtml(markup);
|
|
3133
|
-
}
|
|
3134
|
-
|
|
3135
|
-
// libs/uipack/src/shell/data-injector.ts
|
|
3136
|
-
function hasSizing(sizing) {
|
|
3137
|
-
if (!sizing) return false;
|
|
3138
|
-
return sizing.preferredHeight !== void 0 || sizing.minHeight !== void 0 || sizing.maxHeight !== void 0 || sizing.aspectRatio !== void 0 || sizing.autoResize !== void 0;
|
|
3139
|
-
}
|
|
3140
|
-
function buildDataInjectionScript(options) {
|
|
3141
|
-
const { toolName, input, output, structuredContent, sizing, displayMode } = options;
|
|
3142
|
-
const lines = [
|
|
3143
|
-
`window.__mcpAppsEnabled = true;`,
|
|
3144
|
-
`window.__mcpToolName = ${safeJsonForScript(toolName)};`,
|
|
3145
|
-
`window.__mcpToolInput = ${safeJsonForScript(input ?? null)};`,
|
|
3146
|
-
`window.__mcpToolOutput = ${safeJsonForScript(output ?? null)};`,
|
|
3147
|
-
`window.__mcpStructuredContent = ${safeJsonForScript(structuredContent ?? null)};`
|
|
3148
|
-
];
|
|
3149
|
-
if (hasSizing(sizing)) {
|
|
3150
|
-
lines.push(`window.__mcpWidgetSizing = ${safeJsonForScript(sizing)};`);
|
|
3151
|
-
}
|
|
3152
|
-
if (displayMode && displayMode !== "inline") {
|
|
3153
|
-
lines.push(`window.__mcpDisplayMode = ${safeJsonForScript(displayMode)};`);
|
|
3154
|
-
}
|
|
3155
|
-
return `<script>
|
|
3156
|
-
${lines.join("\n")}
|
|
3157
|
-
</script>`;
|
|
3158
|
-
}
|
|
3159
|
-
function buildCustomDataInjectionScript(descriptor) {
|
|
3160
|
-
if (descriptor.script !== void 0) {
|
|
3161
|
-
return descriptor.script;
|
|
3162
|
-
}
|
|
3163
|
-
if (descriptor.globalKey !== void 0) {
|
|
3164
|
-
return `<script>window[${safeJsonForScript(descriptor.globalKey)}] = ${safeJsonForScript(
|
|
3165
|
-
descriptor.value ?? null
|
|
3166
|
-
)};</script>`;
|
|
3167
|
-
}
|
|
3168
|
-
return "";
|
|
3169
|
-
}
|
|
3170
|
-
var _uniqueIdCounter = 0;
|
|
3171
|
-
function createTemplateHelpers() {
|
|
3172
|
-
return {
|
|
3173
|
-
escapeHtml: (str) => escapeHtml(str),
|
|
3174
|
-
formatDate: (date, format) => {
|
|
3175
|
-
const d = date instanceof Date ? date : new Date(date);
|
|
3176
|
-
if (isNaN(d.getTime())) return String(date);
|
|
3177
|
-
if (format === "iso") return d.toISOString();
|
|
3178
|
-
if (format === "date") return d.toLocaleDateString();
|
|
3179
|
-
if (format === "time") return d.toLocaleTimeString();
|
|
3180
|
-
return d.toLocaleString();
|
|
3181
|
-
},
|
|
3182
|
-
formatCurrency: (amount, currency = "USD") => {
|
|
3183
|
-
return new Intl.NumberFormat("en-US", {
|
|
3184
|
-
style: "currency",
|
|
3185
|
-
currency
|
|
3186
|
-
}).format(amount);
|
|
3187
|
-
},
|
|
3188
|
-
uniqueId: (prefix = "mcp") => {
|
|
3189
|
-
return `${prefix}-${++_uniqueIdCounter}`;
|
|
3190
|
-
},
|
|
3191
|
-
jsonEmbed: (data) => safeJsonForScript(data),
|
|
3192
|
-
html,
|
|
3193
|
-
trustedHtml
|
|
3194
|
-
};
|
|
3195
|
-
}
|
|
3196
|
-
|
|
3197
|
-
// libs/uipack/src/shell/sizing-css.ts
|
|
3198
|
-
function sanitizeCssValue(value) {
|
|
3199
|
-
return value.replace(/[<>{};]/g, "").trim();
|
|
3200
|
-
}
|
|
3201
|
-
function toCssLength(value) {
|
|
3202
|
-
return typeof value === "number" ? `${value}px` : sanitizeCssValue(value);
|
|
3203
|
-
}
|
|
3204
|
-
function buildSizingStyleTag(sizing) {
|
|
3205
|
-
if (!hasSizing(sizing)) return "";
|
|
3206
|
-
const hasCssSizing = sizing.preferredHeight !== void 0 || sizing.minHeight !== void 0 || sizing.maxHeight !== void 0 || sizing.aspectRatio !== void 0;
|
|
3207
|
-
if (!hasCssSizing) return "";
|
|
3208
|
-
const rootRules = [];
|
|
3209
|
-
const docRules = ["margin: 0;"];
|
|
3210
|
-
if (sizing.preferredHeight !== void 0) {
|
|
3211
|
-
const h = toCssLength(sizing.preferredHeight);
|
|
3212
|
-
docRules.push(`height: ${h};`);
|
|
3213
|
-
rootRules.push(`min-height: ${h};`);
|
|
3214
|
-
}
|
|
3215
|
-
if (sizing.minHeight !== void 0) {
|
|
3216
|
-
const mh = toCssLength(sizing.minHeight);
|
|
3217
|
-
docRules.push(`min-height: ${mh};`);
|
|
3218
|
-
rootRules.push(`min-height: ${mh};`);
|
|
3219
|
-
}
|
|
3220
|
-
if (sizing.maxHeight !== void 0) {
|
|
3221
|
-
const mx = toCssLength(sizing.maxHeight);
|
|
3222
|
-
docRules.push(`max-height: ${mx};`);
|
|
3223
|
-
rootRules.push(`max-height: ${mx};`);
|
|
3224
|
-
}
|
|
3225
|
-
if (sizing.aspectRatio !== void 0) {
|
|
3226
|
-
const ar = typeof sizing.aspectRatio === "number" ? String(sizing.aspectRatio) : sanitizeCssValue(sizing.aspectRatio);
|
|
3227
|
-
if (ar) rootRules.push(`aspect-ratio: ${ar};`);
|
|
3228
|
-
}
|
|
3229
|
-
const parts = [`html, body { ${docRules.join(" ")} }`];
|
|
3230
|
-
if (rootRules.length > 0) {
|
|
3231
|
-
parts.push(`#root { ${rootRules.join(" ")} }`);
|
|
3232
|
-
}
|
|
3233
|
-
return `<style>${parts.join("\n")}</style>`;
|
|
3234
|
-
}
|
|
3235
|
-
|
|
3236
|
-
// libs/uipack/src/shell/builder.ts
|
|
3237
|
-
function resolveDataInjectionScript(args) {
|
|
3238
|
-
if (args.dataInjection) {
|
|
3239
|
-
return buildCustomDataInjectionScript(args.dataInjection);
|
|
3240
|
-
}
|
|
3241
|
-
return buildDataInjectionScript({
|
|
3242
|
-
toolName: args.toolName,
|
|
3243
|
-
input: args.input,
|
|
3244
|
-
output: args.output,
|
|
3245
|
-
structuredContent: args.structuredContent,
|
|
3246
|
-
sizing: args.sizing,
|
|
3247
|
-
displayMode: args.displayMode
|
|
3248
|
-
});
|
|
3249
|
-
}
|
|
3250
|
-
function buildShell(content, config) {
|
|
3251
|
-
const {
|
|
3252
|
-
toolName,
|
|
3253
|
-
csp,
|
|
3254
|
-
withShell = true,
|
|
3255
|
-
input,
|
|
3256
|
-
output,
|
|
3257
|
-
structuredContent,
|
|
3258
|
-
includeBridge = true,
|
|
3259
|
-
title,
|
|
3260
|
-
sizing,
|
|
3261
|
-
displayMode
|
|
3262
|
-
} = config;
|
|
3263
|
-
const { customShell, dataInjection } = config;
|
|
3264
|
-
const dataScript = resolveDataInjectionScript({
|
|
3265
|
-
dataInjection,
|
|
3266
|
-
toolName,
|
|
3267
|
-
input,
|
|
3268
|
-
output,
|
|
3269
|
-
structuredContent,
|
|
3270
|
-
sizing,
|
|
3271
|
-
displayMode
|
|
3272
|
-
});
|
|
3273
|
-
if (!withShell) {
|
|
3274
|
-
const html3 = `${dataScript}
|
|
3275
|
-
${content}`;
|
|
3276
|
-
return {
|
|
3277
|
-
html: html3,
|
|
3278
|
-
hash: simpleHash(html3),
|
|
3279
|
-
size: Buffer.byteLength(html3, "utf-8")
|
|
3280
|
-
};
|
|
3281
|
-
}
|
|
3282
|
-
if (customShell) {
|
|
3283
|
-
return buildCustomShell(content, customShell, {
|
|
3284
|
-
csp,
|
|
3285
|
-
toolName,
|
|
3286
|
-
input,
|
|
3287
|
-
output,
|
|
3288
|
-
structuredContent,
|
|
3289
|
-
includeBridge,
|
|
3290
|
-
title,
|
|
3291
|
-
sizing,
|
|
3292
|
-
displayMode,
|
|
3293
|
-
dataInjection
|
|
3294
|
-
});
|
|
3295
|
-
}
|
|
3296
|
-
const headParts = [
|
|
3297
|
-
'<meta charset="UTF-8">',
|
|
3298
|
-
'<meta name="viewport" content="width=device-width, initial-scale=1.0">'
|
|
3299
|
-
];
|
|
3300
|
-
if (title) {
|
|
3301
|
-
headParts.push(`<title>${escapeHtmlForTag(title)}</title>`);
|
|
3302
|
-
}
|
|
3303
|
-
headParts.push(buildCSPMetaTag(csp));
|
|
3304
|
-
headParts.push(dataScript);
|
|
3305
|
-
const sizingStyle = buildSizingStyleTag(sizing);
|
|
3306
|
-
if (sizingStyle) {
|
|
3307
|
-
headParts.push(sizingStyle);
|
|
3308
|
-
}
|
|
3309
|
-
if (includeBridge) {
|
|
3310
|
-
const bridgeScript = generateBridgeIIFE({ minify: true });
|
|
3311
|
-
headParts.push(`<script>${bridgeScript}</script>`);
|
|
3312
|
-
}
|
|
3313
|
-
const html2 = `<!DOCTYPE html>
|
|
3314
|
-
<html lang="en">
|
|
3315
|
-
<head>
|
|
3316
|
-
${headParts.map((p) => ` ${p}`).join("\n")}
|
|
3317
|
-
</head>
|
|
3318
|
-
<body>
|
|
3319
|
-
${content}
|
|
3320
|
-
</body>
|
|
3321
|
-
</html>`;
|
|
3322
|
-
return {
|
|
3323
|
-
html: html2,
|
|
3324
|
-
hash: simpleHash(html2),
|
|
3325
|
-
size: Buffer.byteLength(html2, "utf-8")
|
|
3326
|
-
};
|
|
3327
|
-
}
|
|
3328
|
-
function buildCustomShell(content, customShell, ctx) {
|
|
3329
|
-
let template;
|
|
3330
|
-
if (typeof customShell === "string") {
|
|
3331
|
-
const validation = validateShellTemplate(customShell);
|
|
3332
|
-
if (!validation.valid) {
|
|
3333
|
-
throw new Error(
|
|
3334
|
-
`Custom shell template is missing required placeholder(s): ${validation.missingRequired.map((n) => `{{${n}}}`).join(", ")}`
|
|
3335
|
-
);
|
|
3336
|
-
}
|
|
3337
|
-
template = customShell;
|
|
3338
|
-
} else {
|
|
3339
|
-
template = customShell.template;
|
|
3340
|
-
}
|
|
3341
|
-
const cspTag = buildCSPMetaTag(ctx.csp);
|
|
3342
|
-
const dataScript = resolveDataInjectionScript({
|
|
3343
|
-
dataInjection: ctx.dataInjection,
|
|
3344
|
-
toolName: ctx.toolName,
|
|
3345
|
-
input: ctx.input,
|
|
3346
|
-
output: ctx.output,
|
|
3347
|
-
structuredContent: ctx.structuredContent,
|
|
3348
|
-
sizing: ctx.sizing,
|
|
3349
|
-
displayMode: ctx.displayMode
|
|
3350
|
-
});
|
|
3351
|
-
const sizingStyle = buildSizingStyleTag(ctx.sizing);
|
|
3352
|
-
const dataWithSizing = sizingStyle ? `${dataScript}
|
|
3353
|
-
${sizingStyle}` : dataScript;
|
|
3354
|
-
let bridgeHtml = "";
|
|
3355
|
-
if (ctx.includeBridge) {
|
|
3356
|
-
const bridgeScript = generateBridgeIIFE({ minify: true });
|
|
3357
|
-
bridgeHtml = `<script>${bridgeScript}</script>`;
|
|
3358
|
-
}
|
|
3359
|
-
const html2 = applyShellTemplate(template, {
|
|
3360
|
-
csp: cspTag,
|
|
3361
|
-
data: dataWithSizing,
|
|
3362
|
-
bridge: bridgeHtml,
|
|
3363
|
-
content,
|
|
3364
|
-
title: ctx.title ? escapeHtmlForTag(ctx.title) : ""
|
|
3365
|
-
});
|
|
3366
|
-
return {
|
|
3367
|
-
html: html2,
|
|
3368
|
-
hash: simpleHash(html2),
|
|
3369
|
-
size: Buffer.byteLength(html2, "utf-8")
|
|
3370
|
-
};
|
|
3371
|
-
}
|
|
3372
|
-
function simpleHash(str) {
|
|
3373
|
-
let hash = 0;
|
|
3374
|
-
for (let i = 0; i < str.length; i++) {
|
|
3375
|
-
const char = str.charCodeAt(i);
|
|
3376
|
-
hash = (hash << 5) - hash + char | 0;
|
|
3377
|
-
}
|
|
3378
|
-
return Math.abs(hash).toString(16).padStart(8, "0");
|
|
3379
|
-
}
|
|
3380
|
-
function escapeHtmlForTag(str) {
|
|
3381
|
-
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
3382
|
-
}
|
|
3383
|
-
|
|
3384
|
-
// libs/uipack/src/shell/custom-shell-resolver.ts
|
|
3385
|
-
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
3386
|
-
var templateCache = /* @__PURE__ */ new Map();
|
|
3387
|
-
async function resolveShellTemplate(source, options) {
|
|
3388
|
-
if (isInlineShellSource(source)) {
|
|
3389
|
-
return resolveInline(source.inline);
|
|
3390
|
-
}
|
|
3391
|
-
if (isUrlShellSource(source)) {
|
|
3392
|
-
const cacheKey = `url:${source.url}`;
|
|
3393
|
-
if (!options?.noCache) {
|
|
3394
|
-
const cached = templateCache.get(cacheKey);
|
|
3395
|
-
if (cached) return cached;
|
|
3396
|
-
}
|
|
3397
|
-
const template = await fetchWithTimeout(source.url, source.timeout ?? DEFAULT_TIMEOUT_MS);
|
|
3398
|
-
const resolved = buildResolved(template, "url");
|
|
3399
|
-
if (!options?.noCache) {
|
|
3400
|
-
templateCache.set(cacheKey, resolved);
|
|
3401
|
-
}
|
|
3402
|
-
return resolved;
|
|
3403
|
-
}
|
|
3404
|
-
if (isNpmShellSource(source)) {
|
|
3405
|
-
const specifier = source.version ? `${source.npm}@${source.version}` : source.npm;
|
|
3406
|
-
const cacheKey = `npm:${specifier}`;
|
|
3407
|
-
if (!options?.noCache) {
|
|
3408
|
-
const cached = templateCache.get(cacheKey);
|
|
3409
|
-
if (cached) return cached;
|
|
3410
|
-
}
|
|
3411
|
-
const template = await resolveNpmTemplate(source, specifier, options?.resolver);
|
|
3412
|
-
const resolved = buildResolved(template, "npm");
|
|
3413
|
-
if (!options?.noCache) {
|
|
3414
|
-
templateCache.set(cacheKey, resolved);
|
|
3415
|
-
}
|
|
3416
|
-
return resolved;
|
|
3417
|
-
}
|
|
3418
|
-
throw new Error('Invalid custom shell source: must have "inline", "url", or "npm" property');
|
|
3419
|
-
}
|
|
3420
|
-
function clearShellTemplateCache() {
|
|
3421
|
-
templateCache.clear();
|
|
3422
|
-
}
|
|
3423
|
-
function resolveInline(template) {
|
|
3424
|
-
return buildResolved(template, "inline");
|
|
3425
|
-
}
|
|
3426
|
-
function buildResolved(template, sourceType) {
|
|
3427
|
-
const validation = validateShellTemplate(template);
|
|
3428
|
-
if (!validation.valid) {
|
|
3429
|
-
throw new Error(
|
|
3430
|
-
`Custom shell template is missing required placeholder(s): ${validation.missingRequired.map((n) => `{{${n}}}`).join(", ")}`
|
|
3431
|
-
);
|
|
3432
|
-
}
|
|
3433
|
-
return { template, sourceType, validation };
|
|
3434
|
-
}
|
|
3435
|
-
async function fetchWithTimeout(url, timeoutMs) {
|
|
3436
|
-
const controller = new AbortController();
|
|
3437
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
3438
|
-
try {
|
|
3439
|
-
const response = await fetch(url, { signal: controller.signal });
|
|
3440
|
-
if (!response.ok) {
|
|
3441
|
-
throw new Error(`Failed to fetch shell template from ${url}: HTTP ${response.status}`);
|
|
3442
|
-
}
|
|
3443
|
-
return await response.text();
|
|
3444
|
-
} catch (err) {
|
|
3445
|
-
if (err instanceof Error && err.name === "AbortError") {
|
|
3446
|
-
throw new Error(`Shell template fetch timed out after ${timeoutMs}ms: ${url}`);
|
|
3447
|
-
}
|
|
3448
|
-
throw err;
|
|
3449
|
-
} finally {
|
|
3450
|
-
clearTimeout(timer);
|
|
3451
|
-
}
|
|
3452
|
-
}
|
|
3453
|
-
async function resolveNpmTemplate(source, specifier, resolver) {
|
|
3454
|
-
const cdnUrl = resolveNpmToCdnUrl(specifier, resolver);
|
|
3455
|
-
const moduleText = await fetchWithTimeout(cdnUrl, DEFAULT_TIMEOUT_MS);
|
|
3456
|
-
return extractTemplateFromModule(moduleText, source.exportName);
|
|
3457
|
-
}
|
|
3458
|
-
function resolveNpmToCdnUrl(specifier, resolver) {
|
|
3459
|
-
if (resolver) {
|
|
3460
|
-
const resolved = resolver.resolve(specifier);
|
|
3461
|
-
if (resolved) {
|
|
3462
|
-
return resolved.value;
|
|
3463
|
-
}
|
|
3464
|
-
}
|
|
3465
|
-
return `https://esm.sh/${specifier}`;
|
|
3466
|
-
}
|
|
3467
|
-
function extractTemplateFromModule(moduleText, exportName) {
|
|
3468
|
-
if (exportName && exportName !== "default") {
|
|
3469
|
-
const namedPattern = new RegExp(
|
|
3470
|
-
`export\\s+(?:const|let|var)\\s+${escapeRegExp3(exportName)}\\s*=\\s*\`([\\s\\S]*?)\``
|
|
3471
|
-
);
|
|
3472
|
-
const namedMatch = moduleText.match(namedPattern);
|
|
3473
|
-
if (namedMatch) {
|
|
3474
|
-
return namedMatch[1];
|
|
3475
|
-
}
|
|
3476
|
-
throw new Error(
|
|
3477
|
-
`Could not extract named export "${exportName}" from npm shell package. The package must export a template literal string.`
|
|
3478
|
-
);
|
|
3479
|
-
}
|
|
3480
|
-
const defaultPattern = /export\s+default\s+`([\s\S]*?)`/;
|
|
3481
|
-
const defaultMatch = moduleText.match(defaultPattern);
|
|
3482
|
-
if (defaultMatch) {
|
|
3483
|
-
return defaultMatch[1];
|
|
3484
|
-
}
|
|
3485
|
-
throw new Error(
|
|
3486
|
-
"Could not extract default export from npm shell package. The package must export a template literal string (e.g., export default `<html>...</html>`)."
|
|
3487
|
-
);
|
|
3488
|
-
}
|
|
3489
|
-
function escapeRegExp3(str) {
|
|
3490
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3491
|
-
}
|
|
3492
|
-
|
|
3493
|
-
// libs/uipack/src/component/types.ts
|
|
3494
|
-
function isNpmSource(source) {
|
|
3495
|
-
return typeof source === "object" && source !== null && "npm" in source;
|
|
3496
|
-
}
|
|
3497
|
-
function isFileSource(source) {
|
|
3498
|
-
return typeof source === "object" && source !== null && "file" in source;
|
|
3499
|
-
}
|
|
3500
|
-
function isImportSource(source) {
|
|
3501
|
-
return typeof source === "object" && source !== null && "import" in source;
|
|
3502
|
-
}
|
|
3503
|
-
function isFunctionSource(source) {
|
|
3504
|
-
return typeof source === "function";
|
|
3505
|
-
}
|
|
3506
|
-
var FRONTMCP_META_KEY = "__frontmcp_meta";
|
|
3507
|
-
|
|
3508
|
-
// libs/uipack/src/component/ui-availability.ts
|
|
3509
|
-
function isFrontmcpUiResolvable(...candidatePaths) {
|
|
3510
|
-
try {
|
|
3511
|
-
const nodeFs = __require("fs");
|
|
3512
|
-
const nodePath = __require("path");
|
|
3513
|
-
const ORG = "@frontmcp";
|
|
3514
|
-
const PKG = "ui";
|
|
3515
|
-
for (const candidate of candidatePaths) {
|
|
3516
|
-
let dir = candidate;
|
|
3517
|
-
while (true) {
|
|
3518
|
-
const pkgJson = nodePath.join(dir, "node_modules", ORG, PKG, "package.json");
|
|
3519
|
-
if (nodeFs.existsSync(pkgJson)) return true;
|
|
3520
|
-
const parent = nodePath.dirname(dir);
|
|
3521
|
-
if (parent === dir) break;
|
|
3522
|
-
dir = parent;
|
|
3523
|
-
}
|
|
3524
|
-
}
|
|
3525
|
-
return false;
|
|
3526
|
-
} catch {
|
|
3527
|
-
return false;
|
|
3528
|
-
}
|
|
3529
|
-
}
|
|
3530
|
-
|
|
3531
|
-
// libs/uipack/src/component/transpiler.ts
|
|
3532
|
-
function loadEsbuild(filename) {
|
|
3533
|
-
try {
|
|
3534
|
-
return __require("esbuild");
|
|
3535
|
-
} catch (err) {
|
|
3536
|
-
if (!isMissingEsbuild(err)) throw err;
|
|
3537
|
-
const subject = filename ? `FileSource widget "${filename}"` : "A FileSource widget";
|
|
3538
|
-
throw new Error(
|
|
3539
|
-
`${subject} needs esbuild, which is not installed. @frontmcp/uipack loads esbuild on demand to bundle FileSource widgets (.tsx/.jsx) when the tool is called, so it must be installed where the server runs: \`npm install esbuild\` (in "dependencies", not "devDependencies"). Projects created with \`frontmcp create\` already get it through the \`frontmcp\` package.`,
|
|
3540
|
-
{ cause: err }
|
|
3541
|
-
);
|
|
3542
|
-
}
|
|
3543
|
-
}
|
|
3544
|
-
function isMissingEsbuild(err) {
|
|
3545
|
-
if (!(err instanceof Error)) return false;
|
|
3546
|
-
return err.code === "MODULE_NOT_FOUND" && /Cannot find module ['"]esbuild['"]/.test(err.message);
|
|
3547
|
-
}
|
|
3548
|
-
function transpileReactSource(source, filename) {
|
|
3549
|
-
const esbuild = loadEsbuild(filename);
|
|
3550
|
-
const loader = filename?.endsWith(".tsx") ? "tsx" : "jsx";
|
|
3551
|
-
const result = esbuild.transformSync(source, {
|
|
3552
|
-
loader,
|
|
3553
|
-
jsx: "transform",
|
|
3554
|
-
jsxFactory: "React.createElement",
|
|
3555
|
-
jsxFragment: "React.Fragment",
|
|
3556
|
-
format: "esm",
|
|
3557
|
-
target: "es2020",
|
|
3558
|
-
define: { "process.env.NODE_ENV": '"production"' }
|
|
3559
|
-
});
|
|
3560
|
-
return result.code;
|
|
3561
|
-
}
|
|
3562
|
-
function bundleFileSource(source, filename, resolveDir, componentName, options = {}) {
|
|
3563
|
-
if (!isFrontmcpUiResolvable(resolveDir, process.cwd())) {
|
|
3564
|
-
throw new Error(
|
|
3565
|
-
`FileSource widget "${filename}" requires the @frontmcp/ui package, which provides the React bridge mount that's injected at bundle time. Install it (e.g. \`npm install @frontmcp/ui\` or \`yarn add @frontmcp/ui\`) and try again.`
|
|
3566
|
-
);
|
|
3567
|
-
}
|
|
3568
|
-
const esbuild = loadEsbuild(filename);
|
|
3569
|
-
const mountCode = `
|
|
3570
|
-
// --- Auto-generated mount ---
|
|
3571
|
-
import { createElement as __h } from 'react';
|
|
3572
|
-
import { createRoot } from 'react-dom/client';
|
|
3573
|
-
import { McpBridgeProvider } from '@frontmcp/ui/react';
|
|
3574
|
-
var __root = document.getElementById('root');
|
|
3575
|
-
if (__root) {
|
|
3576
|
-
var __reactRoot = createRoot(__root);
|
|
3577
|
-
function __hasData(v) { return v !== undefined; }
|
|
3578
|
-
function __render(output) {
|
|
3579
|
-
__reactRoot.render(
|
|
3580
|
-
__h(McpBridgeProvider, null,
|
|
3581
|
-
__h(${componentName}, { output: output !== undefined ? output : null, input: window.__mcpToolInput, loading: !__hasData(output) })
|
|
3582
|
-
)
|
|
3583
|
-
);
|
|
3584
|
-
}
|
|
3585
|
-
// Render immediately (component shows loading state until data arrives)
|
|
3586
|
-
__render(undefined);
|
|
3587
|
-
// 1. Try OpenAI SDK (toolOutput set synchronously or after load)
|
|
3588
|
-
if (typeof window !== 'undefined') {
|
|
3589
|
-
if (!window.openai) window.openai = {};
|
|
3590
|
-
var __cur = window.openai.toolOutput;
|
|
3591
|
-
if (__hasData(__cur)) { __render(__cur); }
|
|
3592
|
-
Object.defineProperty(window.openai, 'toolOutput', {
|
|
3593
|
-
get: function() { return __cur; },
|
|
3594
|
-
set: function(v) { __cur = v; __render(v); },
|
|
3595
|
-
configurable: true, enumerable: true
|
|
3596
|
-
});
|
|
3597
|
-
}
|
|
3598
|
-
// 2. Try injected data globals (null: the page carries no call's data, e.g. compiled at startup)
|
|
3599
|
-
if (__hasData(window.__mcpToolOutput) && window.__mcpToolOutput !== null) { __render(window.__mcpToolOutput); }
|
|
3600
|
-
// 3. Listen for bridge tool-result (ext-apps / MCP Inspector)
|
|
3601
|
-
var __bridge = window.FrontMcpBridge;
|
|
3602
|
-
if (__bridge && typeof __bridge.onToolResult === 'function') {
|
|
3603
|
-
__bridge.onToolResult(function(data) { __render(data); });
|
|
3604
|
-
} else {
|
|
3605
|
-
// 4. Fallback: listen for tool:result CustomEvent (standalone / MCP Inspector)
|
|
3606
|
-
window.addEventListener('tool:result', function(e) {
|
|
3607
|
-
var d = e.detail;
|
|
3608
|
-
if (d) __render(d.structuredContent !== undefined ? d.structuredContent : d.content !== undefined ? d.content : d);
|
|
3609
|
-
});
|
|
3610
|
-
}
|
|
3611
|
-
}`;
|
|
3612
|
-
const loader = {
|
|
3613
|
-
".tsx": "tsx",
|
|
3614
|
-
".jsx": "jsx"
|
|
3615
|
-
};
|
|
3616
|
-
const stdinLoader = filename.endsWith(".tsx") ? "tsx" : "jsx";
|
|
3617
|
-
const alias = {};
|
|
3618
|
-
try {
|
|
3619
|
-
const nodePath = __require("path");
|
|
3620
|
-
const nodeFs = __require("fs");
|
|
3621
|
-
const candidates = [
|
|
3622
|
-
nodePath.join(process.cwd(), "node_modules", "@frontmcp", "ui", "dist", "esm"),
|
|
3623
|
-
nodePath.join(resolveDir, "node_modules", "@frontmcp", "ui", "dist", "esm")
|
|
3624
|
-
];
|
|
3625
|
-
for (const uiEsmBase of candidates) {
|
|
3626
|
-
if (!nodeFs.existsSync(uiEsmBase)) continue;
|
|
3627
|
-
const subpaths = ["components", "react", "theme", "bridge", "runtime"];
|
|
3628
|
-
for (const sub of subpaths) {
|
|
3629
|
-
const mjs = nodePath.join(uiEsmBase, sub, "index.mjs");
|
|
3630
|
-
if (nodeFs.existsSync(mjs)) alias[`@frontmcp/ui/${sub}`] = mjs;
|
|
3631
|
-
}
|
|
3632
|
-
if (Object.keys(alias).length > 0) break;
|
|
3633
|
-
}
|
|
3634
|
-
} catch {
|
|
3635
|
-
}
|
|
3636
|
-
try {
|
|
3637
|
-
const result = esbuild.buildSync({
|
|
3638
|
-
stdin: {
|
|
3639
|
-
contents: source + "\n" + mountCode,
|
|
3640
|
-
loader: stdinLoader,
|
|
3641
|
-
resolveDir,
|
|
3642
|
-
sourcefile: filename
|
|
3643
|
-
},
|
|
3644
|
-
bundle: true,
|
|
3645
|
-
write: false,
|
|
3646
|
-
format: "esm",
|
|
3647
|
-
target: "es2020",
|
|
3648
|
-
jsx: "automatic",
|
|
3649
|
-
// When `bundleReact` is set (resourceMode: 'inline'), React itself is
|
|
3650
|
-
// bundled — required for hosts that block external script execution
|
|
3651
|
-
// such as Claude (#454). Otherwise React stays external and is loaded
|
|
3652
|
-
// via the import map emitted by the renderer.
|
|
3653
|
-
external: options.bundleReact ? [] : ["react", "react-dom", "react/jsx-runtime", "react/jsx-dev-runtime"],
|
|
3654
|
-
alias,
|
|
3655
|
-
define: { "process.env.NODE_ENV": '"production"' },
|
|
3656
|
-
platform: "browser",
|
|
3657
|
-
treeShaking: true,
|
|
3658
|
-
logLevel: "warning",
|
|
3659
|
-
loader
|
|
3660
|
-
});
|
|
3661
|
-
return { code: result.outputFiles[0].text };
|
|
3662
|
-
} catch (err) {
|
|
3663
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
3664
|
-
throw new Error(
|
|
3665
|
-
`Failed to bundle FileSource "${filename}": ${message}. If the error mentions @frontmcp/ui or @frontmcp/uipack, ensure both packages are installed in the consuming project.`,
|
|
3666
|
-
{ cause: err }
|
|
3667
|
-
);
|
|
3668
|
-
}
|
|
3669
|
-
}
|
|
3670
|
-
function extractDefaultExportName(code) {
|
|
3671
|
-
let match = code.match(/export\s+default\s+(?:function|class)\s+(\w+)/);
|
|
3672
|
-
if (match) return match[1];
|
|
3673
|
-
match = code.match(/export\s+default\s+(\w+)\s*;/);
|
|
3674
|
-
if (match) return match[1];
|
|
3675
|
-
return null;
|
|
3676
|
-
}
|
|
3677
|
-
|
|
3678
|
-
// libs/uipack/src/component/loader.ts
|
|
3679
|
-
var DEFAULT_META = {
|
|
3680
|
-
mcpAware: false,
|
|
3681
|
-
renderer: "auto"
|
|
3682
|
-
};
|
|
3683
|
-
function resolveUISource(source, options) {
|
|
3684
|
-
if (options?.inlineReact && options?.transformOnly) {
|
|
3685
|
-
throw new Error("resolveUISource: `inlineReact` and `transformOnly` are mutually exclusive \u2014 set at most one.");
|
|
3686
|
-
}
|
|
3687
|
-
const resolver = options?.resolver ?? createEsmShResolver();
|
|
3688
|
-
if (isNpmSource(source)) {
|
|
3689
|
-
return resolveNpmSource(source, resolver);
|
|
3690
|
-
}
|
|
3691
|
-
if (isImportSource(source)) {
|
|
3692
|
-
return resolveImportSource(source);
|
|
3693
|
-
}
|
|
3694
|
-
if (isFileSource(source)) {
|
|
3695
|
-
return resolveFileSource(source, { inlineReact: options?.inlineReact, transformOnly: options?.transformOnly });
|
|
3696
|
-
}
|
|
3697
|
-
if (isFunctionSource(source)) {
|
|
3698
|
-
return resolveFunctionSource(source, options?.input, options?.output);
|
|
3699
|
-
}
|
|
3700
|
-
throw new Error("Unknown UISource type");
|
|
3701
|
-
}
|
|
3702
|
-
function resolveNpmSource(source, resolver) {
|
|
3703
|
-
const specifier = source.version ? `${source.npm}@${source.version}` : source.npm;
|
|
3704
|
-
const pkgName = getPackageName(source.npm);
|
|
3705
|
-
const resolved = resolver.resolve(specifier);
|
|
3706
|
-
const url = resolved?.value ?? `https://esm.sh/${specifier}`;
|
|
3707
|
-
const peerDeps = [];
|
|
3708
|
-
if (pkgName !== "react" && pkgName !== "react-dom") {
|
|
3709
|
-
peerDeps.push("react", "react-dom");
|
|
3710
|
-
}
|
|
3711
|
-
return {
|
|
3712
|
-
mode: "module",
|
|
3713
|
-
url,
|
|
3714
|
-
exportName: source.exportName ?? "default",
|
|
3715
|
-
meta: { ...DEFAULT_META, renderer: "react" },
|
|
3716
|
-
peerDependencies: peerDeps
|
|
3717
|
-
};
|
|
3718
|
-
}
|
|
3719
|
-
function resolveImportSource(source) {
|
|
3720
|
-
return {
|
|
3721
|
-
mode: "module",
|
|
3722
|
-
url: source.import,
|
|
3723
|
-
exportName: source.exportName ?? "default",
|
|
3724
|
-
meta: { ...DEFAULT_META },
|
|
3725
|
-
peerDependencies: []
|
|
3726
|
-
};
|
|
3727
|
-
}
|
|
3728
|
-
var BUILD_DIR_SEGMENTS = /* @__PURE__ */ new Set(["dist", "build", "out"]);
|
|
3729
|
-
function missingWidgetError(filePath, cause) {
|
|
3730
|
-
const sourceWidget = findSourceWidget(filePath);
|
|
3731
|
-
const error = new Error(
|
|
3732
|
-
`FileSource widget not found (ENOENT): no file at "${filePath}". The widget source is read from this path when the tool is called, so it has to exist where the compiled tool runs \u2014 a path built from \`__dirname\` or \`import.meta.url\` points into the build output once the tool is compiled. tsc does not copy \`*.widget.tsx\` / \`*.widget.jsx\` files into its output; \`frontmcp build\` does, and any other build needs a step that copies them next to the compiled tool.` + (sourceWidget ? ` The source widget exists at "${sourceWidget}" \u2014 copy it to "${filePath}", or build with \`frontmcp build\`.` : ""),
|
|
3733
|
-
{ cause }
|
|
3734
|
-
);
|
|
3735
|
-
error.code = "ENOENT";
|
|
3736
|
-
error.path = filePath;
|
|
3737
|
-
return error;
|
|
3738
|
-
}
|
|
3739
|
-
function findSourceWidget(filePath) {
|
|
3740
|
-
const path = __require("path");
|
|
3741
|
-
const fs = __require("fs");
|
|
3742
|
-
const parts = filePath.split(path.sep);
|
|
3743
|
-
for (let i = parts.length - 2; i >= 0; i--) {
|
|
3744
|
-
if (!BUILD_DIR_SEGMENTS.has(parts[i])) continue;
|
|
3745
|
-
const candidates = [[...parts.slice(0, i), "src", ...parts.slice(i + 1)]];
|
|
3746
|
-
if (parts.length - i > 2) candidates.push([...parts.slice(0, i), "src", ...parts.slice(i + 2)]);
|
|
3747
|
-
candidates.push([...parts.slice(0, i), ...parts.slice(i + 1)]);
|
|
3748
|
-
for (const candidate of candidates) {
|
|
3749
|
-
const candidatePath = candidate.join(path.sep);
|
|
3750
|
-
try {
|
|
3751
|
-
if (fs.existsSync(candidatePath)) return candidatePath;
|
|
3752
|
-
} catch {
|
|
3753
|
-
}
|
|
3754
|
-
}
|
|
3755
|
-
return void 0;
|
|
3756
|
-
}
|
|
3757
|
-
return void 0;
|
|
3758
|
-
}
|
|
3759
|
-
function resolveFileSource(source, options = {}) {
|
|
3760
|
-
const path = __require("path");
|
|
3761
|
-
const ext = path.extname(source.file).toLowerCase();
|
|
3762
|
-
if (ext === ".tsx" || ext === ".jsx") {
|
|
3763
|
-
const fs = __require("fs");
|
|
3764
|
-
const wasRelative = !path.isAbsolute(source.file);
|
|
3765
|
-
const filePath = wasRelative ? path.resolve(process.cwd(), source.file) : source.file;
|
|
3766
|
-
let rawSource;
|
|
3767
|
-
try {
|
|
3768
|
-
rawSource = fs.readFileSync(filePath, "utf-8");
|
|
3769
|
-
} catch (err) {
|
|
3770
|
-
const isNotFound = err?.code === "ENOENT";
|
|
3771
|
-
if (isNotFound && wasRelative) {
|
|
3772
|
-
throw new Error(
|
|
3773
|
-
`FileSource widget "${source.file}" not found at "${filePath}". Relative paths are resolved against process.cwd() ("${process.cwd()}"), not the tool file's directory. To anchor the path to the tool file, pass an absolute path \u2014 e.g. \`{ file: fileURLToPath(new URL('./widget.tsx', import.meta.url)) }\` from \`node:url\` (see issue #444).`,
|
|
3774
|
-
{ cause: err }
|
|
3775
|
-
);
|
|
3776
|
-
}
|
|
3777
|
-
if (isNotFound) throw missingWidgetError(filePath, err);
|
|
3778
|
-
throw err;
|
|
3779
|
-
}
|
|
3780
|
-
const componentName = source.exportName || extractDefaultExportName(rawSource) || "Component";
|
|
3781
|
-
if (options.transformOnly === true) {
|
|
3782
|
-
const code = transpileReactSource(rawSource, path.basename(filePath));
|
|
3783
|
-
const parsed2 = parseImports(code);
|
|
3784
|
-
return {
|
|
3785
|
-
mode: "module",
|
|
3786
|
-
code,
|
|
3787
|
-
imports: [...new Set(parsed2.externalImports.map((i) => i.specifier))],
|
|
3788
|
-
exportName: componentName,
|
|
3789
|
-
meta: { mcpAware: true, renderer: "react" },
|
|
3790
|
-
peerDependencies: source.peerDependencies || ["react", "react-dom"],
|
|
3791
|
-
bundled: false
|
|
3792
|
-
};
|
|
3793
|
-
}
|
|
3794
|
-
const bundled = bundleFileSource(rawSource, source.file, path.dirname(filePath), componentName, {
|
|
3795
|
-
bundleReact: options.inlineReact === true
|
|
3796
|
-
});
|
|
3797
|
-
const parsed = parseImports(bundled.code);
|
|
3798
|
-
return {
|
|
3799
|
-
mode: "module",
|
|
3800
|
-
code: bundled.code,
|
|
3801
|
-
imports: [...new Set(parsed.externalImports.map((i) => i.specifier))],
|
|
3802
|
-
exportName: componentName,
|
|
3803
|
-
meta: { mcpAware: true, renderer: "react" },
|
|
3804
|
-
peerDependencies: source.peerDependencies || ["react", "react-dom"],
|
|
3805
|
-
bundled: true
|
|
3806
|
-
};
|
|
3807
|
-
}
|
|
3808
|
-
if (source.inline) {
|
|
3809
|
-
return {
|
|
3810
|
-
mode: "inline",
|
|
3811
|
-
html: `<!-- File source: ${source.file} (inline mode - content to be resolved at build time) -->`,
|
|
3812
|
-
exportName: "default",
|
|
3813
|
-
meta: { ...DEFAULT_META, renderer: "html" },
|
|
3814
|
-
peerDependencies: []
|
|
3815
|
-
};
|
|
3816
|
-
}
|
|
3817
|
-
return {
|
|
3818
|
-
mode: "module",
|
|
3819
|
-
url: source.file,
|
|
3820
|
-
exportName: "default",
|
|
3821
|
-
meta: { ...DEFAULT_META },
|
|
3822
|
-
peerDependencies: []
|
|
3823
|
-
};
|
|
3824
|
-
}
|
|
3825
|
-
function resolveFunctionSource(source, input, output) {
|
|
3826
|
-
const html2 = source(input, output);
|
|
3827
|
-
return {
|
|
3828
|
-
mode: "inline",
|
|
3829
|
-
html: html2,
|
|
3830
|
-
exportName: "default",
|
|
3831
|
-
meta: { mcpAware: false, renderer: "html" },
|
|
3832
|
-
peerDependencies: []
|
|
3833
|
-
};
|
|
3834
|
-
}
|
|
3835
|
-
function generateMountScript(resolved, propsMapping) {
|
|
3836
|
-
if (resolved.mode !== "module" || !resolved.url) {
|
|
3837
|
-
return "";
|
|
3838
|
-
}
|
|
3839
|
-
const componentImport = resolved.exportName === "default" ? `import Component from '${resolved.url}';` : `import { ${resolved.exportName} as Component } from '${resolved.url}';`;
|
|
3840
|
-
if (resolved.meta.mcpAware) {
|
|
3841
|
-
return `${componentImport}
|
|
3842
|
-
import React from 'react';
|
|
3843
|
-
import { createRoot } from 'react-dom/client';
|
|
3844
|
-
createRoot(document.getElementById('root'))
|
|
3845
|
-
.render(React.createElement(Component));`;
|
|
3846
|
-
}
|
|
3847
|
-
const propsCode = propsMapping ? generateMappedPropsCode(propsMapping) : "window.__mcpToolOutput";
|
|
3848
|
-
return `${componentImport}
|
|
3849
|
-
import React from 'react';
|
|
3850
|
-
import { createRoot } from 'react-dom/client';
|
|
3851
|
-
const output = window.__mcpToolOutput || {};
|
|
3852
|
-
const props = ${propsCode};
|
|
3853
|
-
createRoot(document.getElementById('root'))
|
|
3854
|
-
.render(React.createElement(Component, props));`;
|
|
3855
|
-
}
|
|
3856
|
-
function generateMappedPropsCode(mapping) {
|
|
3857
|
-
const entries = Object.entries(mapping).map(([prop, path]) => {
|
|
3858
|
-
const accessPath = path.split(".").map((part) => `[${safeJsonForScript(part)}]`).join("");
|
|
3859
|
-
return `${safeJsonForScript(prop)}: output${accessPath}`;
|
|
3860
|
-
}).join(", ");
|
|
3861
|
-
return `({ ${entries} })`;
|
|
3862
|
-
}
|
|
3863
|
-
|
|
3864
|
-
// libs/uipack/src/component/renderer.ts
|
|
3865
|
-
var DEFAULT_WIDGET_MOUNT = {
|
|
3866
|
-
moduleSpecifier: "@frontmcp/ui/react",
|
|
3867
|
-
wrapperImportName: "McpBridgeProvider"
|
|
3868
|
-
};
|
|
3869
|
-
function renderComponent(config, shellConfig) {
|
|
3870
|
-
const resolver = shellConfig.resolver ?? createEsmShResolver();
|
|
3871
|
-
const resolved = resolveUISource(config.source, {
|
|
3872
|
-
resolver,
|
|
3873
|
-
input: shellConfig.input,
|
|
3874
|
-
output: shellConfig.output,
|
|
3875
|
-
inlineReact: config.inlineReact === true,
|
|
3876
|
-
transformOnly: config.transformOnly === true
|
|
3877
|
-
});
|
|
3878
|
-
const mergedShellConfig = {
|
|
3879
|
-
...shellConfig,
|
|
3880
|
-
withShell: config.withShell ?? shellConfig.withShell,
|
|
3881
|
-
includeBridge: config.includeBridge ?? shellConfig.includeBridge,
|
|
3882
|
-
customShell: config.customShell ?? shellConfig.customShell
|
|
3883
|
-
};
|
|
3884
|
-
if (resolved.mode === "inline") {
|
|
3885
|
-
return buildShell(resolved.html ?? "", mergedShellConfig);
|
|
3886
|
-
}
|
|
3887
|
-
const content = buildModuleContent(resolved, resolver, config.props, shellConfig.mount);
|
|
3888
|
-
return buildShell(content, mergedShellConfig);
|
|
3889
|
-
}
|
|
3890
|
-
function buildModuleContent(resolved, resolver, propsMapping, mount = DEFAULT_WIDGET_MOUNT) {
|
|
3891
|
-
const parts = [];
|
|
3892
|
-
if (resolved.code && resolved.bundled) {
|
|
3893
|
-
const importEntries = {};
|
|
3894
|
-
for (const spec of resolved.imports || []) {
|
|
3895
|
-
const depResolved = resolver.resolve(spec);
|
|
3896
|
-
if (depResolved) {
|
|
3897
|
-
importEntries[spec] = depResolved;
|
|
3898
|
-
}
|
|
3899
|
-
}
|
|
3900
|
-
if (Object.keys(importEntries).length > 0) {
|
|
3901
|
-
const importMap = createImportMapFromResolved(importEntries);
|
|
3902
|
-
parts.push(generateImportMapScriptTag(importMap));
|
|
3903
|
-
}
|
|
3904
|
-
parts.push('<div id="root"></div>');
|
|
3905
|
-
parts.push(`<script type="module">
|
|
3906
|
-
${resolved.code}
|
|
3907
|
-
</script>`);
|
|
3908
|
-
} else if (resolved.code) {
|
|
3909
|
-
const importEntries = {};
|
|
3910
|
-
const allSpecifiers = /* @__PURE__ */ new Set([
|
|
3911
|
-
...resolved.imports || [],
|
|
3912
|
-
"react-dom/client",
|
|
3913
|
-
// needed by mount script
|
|
3914
|
-
mount.moduleSpecifier,
|
|
3915
|
-
// mounter/provider package (e.g. @frontmcp/ui/react)
|
|
3916
|
-
// React subpath entries needed by esm.sh externalized modules:
|
|
3917
|
-
"react/jsx-runtime",
|
|
3918
|
-
"react/jsx-dev-runtime"
|
|
3919
|
-
]);
|
|
3920
|
-
if (mount === DEFAULT_WIDGET_MOUNT) {
|
|
3921
|
-
allSpecifiers.add("react-dom/server");
|
|
3922
|
-
allSpecifiers.add("react-dom/static");
|
|
3923
|
-
}
|
|
3924
|
-
const coreDeps = /* @__PURE__ */ new Set([
|
|
3925
|
-
"react",
|
|
3926
|
-
"react-dom",
|
|
3927
|
-
"react-dom/client",
|
|
3928
|
-
"react/jsx-runtime",
|
|
3929
|
-
"react/jsx-dev-runtime",
|
|
3930
|
-
"react-dom/server",
|
|
3931
|
-
"react-dom/static"
|
|
3932
|
-
]);
|
|
3933
|
-
for (const spec of allSpecifiers) {
|
|
3934
|
-
const depResolved = resolver.resolve(spec);
|
|
3935
|
-
if (depResolved) {
|
|
3936
|
-
if (!coreDeps.has(spec) && depResolved.value?.includes("esm.sh")) {
|
|
3937
|
-
importEntries[spec] = {
|
|
3938
|
-
...depResolved,
|
|
3939
|
-
value: addExternalParam(depResolved.value, ["react", "react-dom"])
|
|
3940
|
-
};
|
|
3941
|
-
} else {
|
|
3942
|
-
importEntries[spec] = depResolved;
|
|
3943
|
-
}
|
|
3944
|
-
}
|
|
3945
|
-
}
|
|
3946
|
-
if (importEntries["react-dom"] && !importEntries["react-dom/client"]) {
|
|
3947
|
-
const rdc = resolver.resolve("react-dom/client");
|
|
3948
|
-
if (rdc) importEntries["react-dom/client"] = rdc;
|
|
3949
|
-
}
|
|
3950
|
-
if (Object.keys(importEntries).length > 0) {
|
|
3951
|
-
const importMap = createImportMapFromResolved(importEntries);
|
|
3952
|
-
parts.push(generateImportMapScriptTag(importMap));
|
|
3953
|
-
}
|
|
3954
|
-
const mountNodeId = mount.mountNodeId ?? "root";
|
|
3955
|
-
parts.push(`<div id="${mountNodeId}">${mount.mountNodeInnerHtml ?? ""}</div>`);
|
|
3956
|
-
const mountCode = generateInlineMountCode(resolved.exportName, mount);
|
|
3957
|
-
parts.push(`<script type="module">
|
|
3958
|
-
${resolved.code}
|
|
3959
|
-
${mountCode}
|
|
3960
|
-
</script>`);
|
|
3961
|
-
} else {
|
|
3962
|
-
const importEntries = {};
|
|
3963
|
-
for (const dep of resolved.peerDependencies) {
|
|
3964
|
-
const depResolved = resolver.resolve(dep);
|
|
3965
|
-
if (depResolved) {
|
|
3966
|
-
importEntries[dep] = depResolved;
|
|
3967
|
-
}
|
|
3968
|
-
}
|
|
3969
|
-
if (importEntries["react-dom"]) {
|
|
3970
|
-
const reactDomClient = resolver.resolve("react-dom/client");
|
|
3971
|
-
if (reactDomClient) {
|
|
3972
|
-
importEntries["react-dom/client"] = reactDomClient;
|
|
3973
|
-
}
|
|
3974
|
-
}
|
|
3975
|
-
if (Object.keys(importEntries).length > 0) {
|
|
3976
|
-
const importMap = createImportMapFromResolved(importEntries);
|
|
3977
|
-
parts.push(generateImportMapScriptTag(importMap));
|
|
3978
|
-
}
|
|
3979
|
-
parts.push('<div id="root"></div>');
|
|
3980
|
-
const mountScript = generateMountScript(resolved, propsMapping);
|
|
3981
|
-
if (mountScript) {
|
|
3982
|
-
parts.push(`<script type="module">
|
|
3983
|
-
${mountScript}
|
|
3984
|
-
</script>`);
|
|
3985
|
-
}
|
|
3986
|
-
}
|
|
3987
|
-
return parts.join("\n");
|
|
3988
|
-
}
|
|
3989
|
-
function addExternalParam(url, externals) {
|
|
3990
|
-
const sep = url.includes("?") ? "&" : "?";
|
|
3991
|
-
return `${url}${sep}external=${externals.join(",")}`;
|
|
3992
|
-
}
|
|
3993
|
-
function generateInlineMountCode(componentName, mount) {
|
|
3994
|
-
if (mount.generate) {
|
|
3995
|
-
return mount.generate(componentName);
|
|
3996
|
-
}
|
|
3997
|
-
const wrapperName = mount.wrapperImportName ?? "McpBridgeProvider";
|
|
3998
|
-
const wrapperAlias = `__${wrapperName}`;
|
|
3999
|
-
const mountNodeId = mount.mountNodeId ?? "root";
|
|
4000
|
-
return `
|
|
4001
|
-
// --- Mount ---
|
|
4002
|
-
import { createRoot as __createRoot } from 'react-dom/client';
|
|
4003
|
-
import { ${wrapperName} as ${wrapperAlias} } from '${mount.moduleSpecifier}';
|
|
4004
|
-
const __root = document.getElementById('${mountNodeId}');
|
|
4005
|
-
if (__root) {
|
|
4006
|
-
__createRoot(__root).render(
|
|
4007
|
-
React.createElement(${wrapperAlias}, null,
|
|
4008
|
-
React.createElement(${componentName})
|
|
4009
|
-
)
|
|
4010
|
-
);
|
|
4011
|
-
}`;
|
|
4012
|
-
}
|
|
4013
|
-
|
|
4014
|
-
// libs/uipack/src/adapters/constants.ts
|
|
4015
|
-
var MCP_APPS_MIME_TYPE = "text/html;profile=mcp-app";
|
|
4016
|
-
var MCP_APPS_EXTENSION_ID = "io.modelcontextprotocol/ui";
|
|
4017
|
-
|
|
4018
|
-
// libs/uipack/src/adapters/serving-mode.ts
|
|
4019
|
-
function resolveServingMode(options) {
|
|
4020
|
-
const { configuredMode, platformType } = options;
|
|
4021
|
-
if (platformType === "gemini") {
|
|
4022
|
-
return {
|
|
4023
|
-
mode: configuredMode,
|
|
4024
|
-
supportsUI: false,
|
|
4025
|
-
effectiveMode: null,
|
|
4026
|
-
useStructuredContent: false,
|
|
4027
|
-
reason: "Gemini does not support MCP Apps UI"
|
|
4028
|
-
};
|
|
4029
|
-
}
|
|
4030
|
-
const effectiveMode = configuredMode === "auto" ? "inline" : configuredMode;
|
|
4031
|
-
if (effectiveMode === "hybrid") {
|
|
4032
|
-
const hybridCapable = platformType === "openai" || platformType === "ext-apps" || platformType === "cursor";
|
|
4033
|
-
if (!hybridCapable) {
|
|
4034
|
-
return {
|
|
4035
|
-
mode: configuredMode,
|
|
4036
|
-
supportsUI: false,
|
|
4037
|
-
effectiveMode: null,
|
|
4038
|
-
useStructuredContent: false,
|
|
4039
|
-
reason: `Platform ${platformType} does not support hybrid serving mode`
|
|
4040
|
-
};
|
|
4041
|
-
}
|
|
4042
|
-
}
|
|
4043
|
-
const useStructuredContent = platformType === "openai" || platformType === "ext-apps" || platformType === "generic-mcp" || platformType === "unknown";
|
|
4044
|
-
return {
|
|
4045
|
-
mode: configuredMode,
|
|
4046
|
-
supportsUI: true,
|
|
4047
|
-
effectiveMode,
|
|
4048
|
-
useStructuredContent,
|
|
4049
|
-
reason: `Platform ${platformType} supports UI, mode: ${effectiveMode}`
|
|
4050
|
-
};
|
|
4051
|
-
}
|
|
4052
|
-
|
|
4053
|
-
// libs/uipack/src/adapters/response-builder.ts
|
|
4054
|
-
function buildToolResponseContent(options) {
|
|
4055
|
-
const { rawOutput, htmlContent, servingMode, useStructuredContent, platformType } = options;
|
|
4056
|
-
const textSummary = typeof rawOutput === "string" ? rawOutput : rawOutput !== null && rawOutput !== void 0 ? JSON.stringify(rawOutput) : "";
|
|
4057
|
-
const result = {
|
|
4058
|
-
content: [{ type: "text", text: textSummary }]
|
|
4059
|
-
};
|
|
4060
|
-
if (useStructuredContent && rawOutput !== null && rawOutput !== void 0) {
|
|
4061
|
-
const structured = typeof rawOutput === "object" && !Array.isArray(rawOutput) ? rawOutput : { value: rawOutput };
|
|
4062
|
-
result.structuredContent = structured;
|
|
4063
|
-
}
|
|
4064
|
-
if (htmlContent && servingMode === "inline") {
|
|
4065
|
-
const htmlKey = "ui/html";
|
|
4066
|
-
result._meta = {
|
|
4067
|
-
[htmlKey]: htmlContent
|
|
4068
|
-
};
|
|
4069
|
-
result.format = "html";
|
|
4070
|
-
}
|
|
4071
|
-
return result;
|
|
4072
|
-
}
|
|
4073
|
-
|
|
4074
|
-
// libs/uipack/src/adapters/render-failure.ts
|
|
4075
|
-
function isUIRenderFailure(result) {
|
|
4076
|
-
if (typeof result !== "object" || result === null) {
|
|
4077
|
-
return false;
|
|
4078
|
-
}
|
|
4079
|
-
const rec = result;
|
|
4080
|
-
return typeof rec["reason"] === "string" && !("meta" in rec);
|
|
4081
|
-
}
|
|
4082
|
-
|
|
4083
|
-
// libs/uipack/src/adapters/content-detector.ts
|
|
4084
|
-
var CHART_TYPES = /* @__PURE__ */ new Set(["bar", "line", "pie", "area", "scatter", "doughnut", "radar", "polarArea", "bubble"]);
|
|
4085
|
-
var MERMAID_PREFIXES = [
|
|
4086
|
-
"flowchart",
|
|
4087
|
-
"graph",
|
|
4088
|
-
"sequenceDiagram",
|
|
4089
|
-
"classDiagram",
|
|
4090
|
-
"stateDiagram",
|
|
4091
|
-
"erDiagram",
|
|
4092
|
-
"gantt",
|
|
4093
|
-
"pie",
|
|
4094
|
-
"gitGraph",
|
|
4095
|
-
"journey",
|
|
4096
|
-
"mindmap",
|
|
4097
|
-
"timeline",
|
|
4098
|
-
"quadrantChart",
|
|
4099
|
-
"sankey",
|
|
4100
|
-
"xychart"
|
|
4101
|
-
];
|
|
4102
|
-
var PDF_MAGIC = "JVBERi";
|
|
4103
|
-
var BASE64_PATTERN = /^[A-Za-z0-9+/=\r\n]+$/;
|
|
4104
|
-
function detectContentType(value) {
|
|
4105
|
-
if (value === null || value === void 0) {
|
|
4106
|
-
return "text";
|
|
4107
|
-
}
|
|
4108
|
-
if (typeof value === "object" && !Array.isArray(value)) {
|
|
4109
|
-
const obj = value;
|
|
4110
|
-
if (typeof obj["type"] === "string" && CHART_TYPES.has(obj["type"])) {
|
|
4111
|
-
return "chart";
|
|
4112
|
-
}
|
|
4113
|
-
return "json";
|
|
4114
|
-
}
|
|
4115
|
-
if (typeof value === "string") {
|
|
4116
|
-
const trimmed = value.trimStart();
|
|
4117
|
-
if (trimmed.startsWith(PDF_MAGIC)) {
|
|
4118
|
-
return BASE64_PATTERN.test(trimmed.trimEnd()) ? "pdf" : "text";
|
|
4119
|
-
}
|
|
4120
|
-
for (const prefix of MERMAID_PREFIXES) {
|
|
4121
|
-
if (trimmed.startsWith(prefix)) {
|
|
4122
|
-
return "mermaid";
|
|
4123
|
-
}
|
|
4124
|
-
}
|
|
4125
|
-
if (trimmed.includes("<") && trimmed.includes(">")) {
|
|
4126
|
-
return "html";
|
|
4127
|
-
}
|
|
4128
|
-
return "text";
|
|
4129
|
-
}
|
|
4130
|
-
return "text";
|
|
4131
|
-
}
|
|
4132
|
-
|
|
4133
|
-
// libs/uipack/src/adapters/content-renderers.ts
|
|
4134
|
-
var CHART_JS_CDN = "https://esm.sh/chart.js@4/auto";
|
|
4135
|
-
var MERMAID_CDN = "https://esm.sh/mermaid@11/dist/mermaid.esm.min.mjs";
|
|
4136
|
-
function buildChartHtml(chartJson) {
|
|
4137
|
-
const chartData = safeJsonForScript(chartJson);
|
|
4138
|
-
const content = `
|
|
4139
|
-
<canvas id="chart" style="max-width:100%;max-height:80vh;"></canvas>
|
|
4140
|
-
<script type="module">
|
|
4141
|
-
import Chart from '${CHART_JS_CDN}';
|
|
4142
|
-
const ctx = document.getElementById('chart').getContext('2d');
|
|
4143
|
-
new Chart(ctx, ${chartData});
|
|
4144
|
-
</script>`;
|
|
4145
|
-
const result = buildShell(content, {
|
|
4146
|
-
toolName: "chart",
|
|
4147
|
-
csp: {
|
|
4148
|
-
resourceDomains: ["https://esm.sh"],
|
|
4149
|
-
connectDomains: ["https://esm.sh"]
|
|
4150
|
-
},
|
|
4151
|
-
includeBridge: false
|
|
4152
|
-
});
|
|
4153
|
-
return result.html;
|
|
4154
|
-
}
|
|
4155
|
-
function buildMermaidHtml(mermaidCode) {
|
|
4156
|
-
const escaped = escapeHtml(mermaidCode);
|
|
4157
|
-
const content = `
|
|
4158
|
-
<pre class="mermaid">${escaped}</pre>
|
|
4159
|
-
<script type="module">
|
|
4160
|
-
import mermaid from '${MERMAID_CDN}';
|
|
4161
|
-
mermaid.initialize({ startOnLoad: true, theme: 'default' });
|
|
4162
|
-
</script>`;
|
|
4163
|
-
const result = buildShell(content, {
|
|
4164
|
-
toolName: "mermaid",
|
|
4165
|
-
csp: {
|
|
4166
|
-
resourceDomains: ["https://esm.sh"],
|
|
4167
|
-
connectDomains: ["https://esm.sh"]
|
|
4168
|
-
},
|
|
4169
|
-
includeBridge: false
|
|
4170
|
-
});
|
|
4171
|
-
return result.html;
|
|
4172
|
-
}
|
|
4173
|
-
function buildPdfHtml(base64) {
|
|
4174
|
-
const content = `
|
|
4175
|
-
<div id="pdf-container" style="width:100%;overflow:auto;text-align:center;"></div>
|
|
4176
|
-
<script type="module">
|
|
4177
|
-
import * as pdfjsLib from 'https://esm.sh/pdfjs-dist@4/build/pdf.min.mjs';
|
|
4178
|
-
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://esm.sh/pdfjs-dist@4/build/pdf.worker.min.mjs';
|
|
4179
|
-
|
|
4180
|
-
const base64 = ${safeJsonForScript(base64)};
|
|
4181
|
-
const binaryStr = atob(base64);
|
|
4182
|
-
const bytes = new Uint8Array(binaryStr.length);
|
|
4183
|
-
for (let i = 0; i < binaryStr.length; i++) bytes[i] = binaryStr.charCodeAt(i);
|
|
4184
|
-
|
|
4185
|
-
const pdf = await pdfjsLib.getDocument({ data: bytes }).promise;
|
|
4186
|
-
const container = document.getElementById('pdf-container');
|
|
4187
|
-
for (let i = 1; i <= pdf.numPages; i++) {
|
|
4188
|
-
const page = await pdf.getPage(i);
|
|
4189
|
-
const viewport = page.getViewport({ scale: 1.5 });
|
|
4190
|
-
const canvas = document.createElement('canvas');
|
|
4191
|
-
canvas.width = viewport.width;
|
|
4192
|
-
canvas.height = viewport.height;
|
|
4193
|
-
canvas.style.marginBottom = '8px';
|
|
4194
|
-
container.appendChild(canvas);
|
|
4195
|
-
await page.render({ canvasContext: canvas.getContext('2d'), viewport }).promise;
|
|
4196
|
-
}
|
|
4197
|
-
</script>`;
|
|
4198
|
-
const result = buildShell(content, {
|
|
4199
|
-
toolName: "pdf",
|
|
4200
|
-
csp: {
|
|
4201
|
-
resourceDomains: ["https://esm.sh"],
|
|
4202
|
-
connectDomains: ["https://esm.sh"]
|
|
4203
|
-
},
|
|
4204
|
-
includeBridge: false
|
|
4205
|
-
});
|
|
4206
|
-
return result.html;
|
|
4207
|
-
}
|
|
4208
|
-
function wrapDetectedContent(value) {
|
|
4209
|
-
const contentType = detectContentType(value);
|
|
4210
|
-
switch (contentType) {
|
|
4211
|
-
case "chart":
|
|
4212
|
-
return buildChartHtml(value);
|
|
4213
|
-
case "mermaid":
|
|
4214
|
-
return buildMermaidHtml(value);
|
|
4215
|
-
case "pdf":
|
|
4216
|
-
return buildPdfHtml(value);
|
|
4217
|
-
case "html":
|
|
4218
|
-
return void 0;
|
|
4219
|
-
default:
|
|
4220
|
-
return void 0;
|
|
4221
|
-
}
|
|
4222
|
-
}
|
|
4223
|
-
|
|
4224
|
-
// libs/uipack/src/adapters/markdown.ts
|
|
4225
|
-
var SAFE_HREF = /^(https?:|mailto:|\/|#)/i;
|
|
4226
|
-
function emphasis(text) {
|
|
4227
|
-
return text.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>").replace(/\*([^*]+)\*/g, "<em>$1</em>");
|
|
4228
|
-
}
|
|
4229
|
-
function inline(text) {
|
|
4230
|
-
const stash = [];
|
|
4231
|
-
const hold = (html2) => `\uE000${stash.push(html2) - 1}\uE000`;
|
|
4232
|
-
let out = escapeHtml(text.replace(/\uE000/g, "")).replace(
|
|
4233
|
-
/`([^`]+)`/g,
|
|
4234
|
-
(_m, code) => hold(`<code>${code}</code>`)
|
|
4235
|
-
);
|
|
4236
|
-
out = out.replace(
|
|
4237
|
-
/\[([^\]]+)\]\(([^)\s]+)\)/g,
|
|
4238
|
-
(_m, label, href) => SAFE_HREF.test(href) ? hold(`<a href="${href}" rel="noopener noreferrer">${emphasis(label)}</a>`) : label
|
|
4239
|
-
);
|
|
4240
|
-
out = emphasis(out);
|
|
4241
|
-
for (let i = 0; i < 2; i++) out = out.replace(/\uE000(\d+)\uE000/g, (_m, n) => stash[Number(n)]);
|
|
4242
|
-
return out;
|
|
4243
|
-
}
|
|
4244
|
-
function markdownToHtml(source) {
|
|
4245
|
-
const lines = source.replace(/\r\n?/g, "\n").split("\n");
|
|
4246
|
-
const blocks = [];
|
|
4247
|
-
let paragraph = [];
|
|
4248
|
-
let list;
|
|
4249
|
-
const flushParagraph = () => {
|
|
4250
|
-
if (paragraph.length > 0) blocks.push(`<p>${inline(paragraph.join(" "))}</p>`);
|
|
4251
|
-
paragraph = [];
|
|
4252
|
-
};
|
|
4253
|
-
const flushList = () => {
|
|
4254
|
-
if (list)
|
|
4255
|
-
blocks.push(`<${list.tag}>${list.items.map((item) => `<li>${inline(item)}</li>`).join("")}</${list.tag}>`);
|
|
4256
|
-
list = void 0;
|
|
4257
|
-
};
|
|
4258
|
-
for (let i = 0; i < lines.length; i++) {
|
|
4259
|
-
const line = lines[i];
|
|
4260
|
-
const fence = /^\s*(`{3,})/.exec(line);
|
|
4261
|
-
if (fence) {
|
|
4262
|
-
flushParagraph();
|
|
4263
|
-
flushList();
|
|
4264
|
-
const fenceLength = fence[1].length;
|
|
4265
|
-
const isClosing = (l) => {
|
|
4266
|
-
const m = /^\s*(`{3,})\s*$/.exec(l);
|
|
4267
|
-
return m !== null && m[1].length >= fenceLength;
|
|
4268
|
-
};
|
|
4269
|
-
const code = [];
|
|
4270
|
-
for (i++; i < lines.length && !isClosing(lines[i]); i++) code.push(lines[i]);
|
|
4271
|
-
blocks.push(`<pre><code>${escapeHtml(code.join("\n"))}
|
|
4272
|
-
</code></pre>`);
|
|
4273
|
-
continue;
|
|
4274
|
-
}
|
|
4275
|
-
const heading = /^(#{1,6})\s+(.*)$/.exec(line);
|
|
4276
|
-
if (heading) {
|
|
4277
|
-
flushParagraph();
|
|
4278
|
-
flushList();
|
|
4279
|
-
blocks.push(`<h${heading[1].length}>${inline(heading[2].trim())}</h${heading[1].length}>`);
|
|
4280
|
-
continue;
|
|
4281
|
-
}
|
|
4282
|
-
const item = /^\s*([-*+]|\d+\.)\s+(.*)$/.exec(line);
|
|
4283
|
-
if (item) {
|
|
4284
|
-
flushParagraph();
|
|
4285
|
-
const tag = /\d/.test(item[1]) ? "ol" : "ul";
|
|
4286
|
-
if (list && list.tag !== tag) flushList();
|
|
4287
|
-
list ??= { tag, items: [] };
|
|
4288
|
-
list.items.push(item[2]);
|
|
4289
|
-
continue;
|
|
4290
|
-
}
|
|
4291
|
-
if (line.trim() === "") {
|
|
4292
|
-
flushParagraph();
|
|
4293
|
-
flushList();
|
|
4294
|
-
continue;
|
|
4295
|
-
}
|
|
4296
|
-
flushList();
|
|
4297
|
-
paragraph.push(line.trim());
|
|
4298
|
-
}
|
|
4299
|
-
flushParagraph();
|
|
4300
|
-
flushList();
|
|
4301
|
-
return blocks.join("\n");
|
|
4302
|
-
}
|
|
4303
|
-
|
|
4304
|
-
// libs/uipack/src/adapters/type-detector.ts
|
|
4305
|
-
function detectUIType(template) {
|
|
4306
|
-
if (template === null || template === void 0) {
|
|
4307
|
-
return "auto";
|
|
4308
|
-
}
|
|
4309
|
-
if (typeof template === "object" && template !== null && "file" in template) {
|
|
4310
|
-
const file = template.file;
|
|
4311
|
-
if (/\.(tsx|jsx)$/i.test(file)) return "react";
|
|
4312
|
-
}
|
|
4313
|
-
if (typeof template === "function") {
|
|
4314
|
-
const proto = template.prototype;
|
|
4315
|
-
if (proto && typeof proto.render === "function") {
|
|
4316
|
-
return "react";
|
|
4317
|
-
}
|
|
4318
|
-
const asRecord = template;
|
|
4319
|
-
if (asRecord["$$typeof"] !== void 0) {
|
|
4320
|
-
return "react";
|
|
4321
|
-
}
|
|
4322
|
-
if (template.name && /^[A-Z]/.test(template.name)) {
|
|
4323
|
-
return "react";
|
|
4324
|
-
}
|
|
4325
|
-
return "html";
|
|
4326
|
-
}
|
|
4327
|
-
if (typeof template === "string") {
|
|
4328
|
-
if (template.includes("<") && template.includes(">")) {
|
|
4329
|
-
return "html";
|
|
4330
|
-
}
|
|
4331
|
-
return "markdown";
|
|
4332
|
-
}
|
|
4333
|
-
return "auto";
|
|
4334
|
-
}
|
|
4335
|
-
|
|
4336
|
-
// libs/uipack/src/adapters/template-renderer.ts
|
|
4337
|
-
function buildCspConfig(resolver, extra) {
|
|
4338
|
-
const cspResourceDomains = ["https://esm.sh"];
|
|
4339
|
-
const cspConnectDomains = ["https://esm.sh"];
|
|
4340
|
-
for (const domain of extra?.resourceDomains ?? []) {
|
|
4341
|
-
if (!cspResourceDomains.includes(domain)) cspResourceDomains.push(domain);
|
|
4342
|
-
}
|
|
4343
|
-
for (const domain of extra?.connectDomains ?? []) {
|
|
4344
|
-
if (!cspConnectDomains.includes(domain)) cspConnectDomains.push(domain);
|
|
4345
|
-
}
|
|
4346
|
-
if (resolver && "overrides" in resolver) {
|
|
4347
|
-
const overrides = resolver.overrides;
|
|
4348
|
-
if (overrides) {
|
|
4349
|
-
for (const url of Object.values(overrides)) {
|
|
4350
|
-
try {
|
|
4351
|
-
const origin = new URL(url).origin;
|
|
4352
|
-
if (!cspResourceDomains.includes(origin)) cspResourceDomains.push(origin);
|
|
4353
|
-
if (!cspConnectDomains.includes(origin)) cspConnectDomains.push(origin);
|
|
4354
|
-
} catch {
|
|
4355
|
-
}
|
|
4356
|
-
}
|
|
4357
|
-
}
|
|
4358
|
-
}
|
|
4359
|
-
return { resourceDomains: cspResourceDomains, connectDomains: cspConnectDomains };
|
|
4360
|
-
}
|
|
4361
|
-
var STRING_RESULT_DOCS = "https://frontmcp.dev/learn/your-first-widget";
|
|
4362
|
-
var noticedStringResultTools = /* @__PURE__ */ new Set();
|
|
4363
|
-
function noticeStringResult(toolName, logger) {
|
|
4364
|
-
if (noticedStringResultTools.has(toolName)) return;
|
|
4365
|
-
noticedStringResultTools.add(toolName);
|
|
4366
|
-
logger.warn(
|
|
4367
|
-
`[frontmcp] The UI template of tool "${toolName}" returned a plain string containing markup. Since 1.9.2, FrontMCP HTML-escapes plain string results by default, so it is shown as text. Build the markup with ctx.helpers.html\`\u2026\` (interpolated values are escaped) or wrap safe markup with ctx.helpers.trustedHtml(). Set ui.escapeStringResults: false to render plain strings as markup, or true to silence this notice. See ${STRING_RESULT_DOCS}`
|
|
4368
|
-
);
|
|
4369
|
-
}
|
|
4370
|
-
function renderUnwrappedResult(rawResult, options) {
|
|
4371
|
-
if (isTrustedHtml(rawResult)) {
|
|
4372
|
-
return String(rawResult);
|
|
4373
|
-
}
|
|
4374
|
-
if (typeof rawResult !== "string") {
|
|
4375
|
-
return `<pre>${escapeHtml(JSON.stringify(rawResult, null, 2))}</pre>`;
|
|
4376
|
-
}
|
|
4377
|
-
const looksLikeMarkup = detectContentType(rawResult) === "html";
|
|
4378
|
-
if (looksLikeMarkup && options.escapeStringResults === false) {
|
|
4379
|
-
return rawResult;
|
|
4380
|
-
}
|
|
4381
|
-
if (looksLikeMarkup && options.escapeStringResults === void 0) {
|
|
4382
|
-
noticeStringResult(options.toolName, options.logger ?? console);
|
|
4383
|
-
}
|
|
4384
|
-
return escapeHtml(rawResult);
|
|
4385
|
-
}
|
|
4386
|
-
var warnedComponentReferences = /* @__PURE__ */ new WeakMap();
|
|
4387
|
-
function warnComponentReference(template, toolName, logger) {
|
|
4388
|
-
const warned = warnedComponentReferences.get(template) ?? /* @__PURE__ */ new Set();
|
|
4389
|
-
if (warned.has(toolName)) return;
|
|
4390
|
-
warned.add(toolName);
|
|
4391
|
-
warnedComponentReferences.set(template, warned);
|
|
4392
|
-
logger.warn(
|
|
4393
|
-
`[FrontMCP] Tool "${toolName}": \`ui.template\` is a React component reference, which cannot be bundled (there is no source file to compile), so the widget would render an empty page. Use \`template: { file: './widget.tsx' }\` instead.`
|
|
4394
|
-
);
|
|
4395
|
-
}
|
|
4396
|
-
var COMPILED_JSX_PATTERN = /\b(?:React\.createElement|_?jsx(?:s|DEV)?)\)?\s*\(/;
|
|
4397
|
-
function isComponentReference(template) {
|
|
4398
|
-
if (isReactTypedObject(template)) return true;
|
|
4399
|
-
if (typeof template !== "function" || detectUIType(template) !== "react") return false;
|
|
4400
|
-
return isDefinitelyReactComponent(template) || COMPILED_JSX_PATTERN.test(Function.prototype.toString.call(template));
|
|
4401
|
-
}
|
|
4402
|
-
function warnIfComponentReference(toolName, template, logger = console) {
|
|
4403
|
-
if (!isComponentReference(template)) return false;
|
|
4404
|
-
warnComponentReference(template, toolName, logger);
|
|
4405
|
-
return true;
|
|
4406
|
-
}
|
|
4407
|
-
function isReactTypedObject(value) {
|
|
4408
|
-
return typeof value === "object" && value !== null && "$$typeof" in value;
|
|
4409
|
-
}
|
|
4410
|
-
function isDefinitelyReactComponent(template) {
|
|
4411
|
-
if (typeof template !== "function") return false;
|
|
4412
|
-
const proto = template.prototype;
|
|
4413
|
-
return typeof proto?.render === "function" || template["$$typeof"] !== void 0;
|
|
4414
|
-
}
|
|
4415
|
-
function renderToolTemplate(options) {
|
|
4416
|
-
const { toolName, input, output, template, resolver, platformType, sizing } = options;
|
|
4417
|
-
const uiType = detectUIType(template);
|
|
4418
|
-
const resourceMode = options.resourceMode ?? (platformType === "claude" ? "inline" : "cdn");
|
|
4419
|
-
const shellConfig = {
|
|
4420
|
-
toolName,
|
|
4421
|
-
input,
|
|
4422
|
-
output,
|
|
4423
|
-
includeBridge: true,
|
|
4424
|
-
resolver,
|
|
4425
|
-
sizing,
|
|
4426
|
-
displayMode: options.displayMode,
|
|
4427
|
-
csp: options.csp ? buildCspConfig(resolver, options.csp) : void 0
|
|
4428
|
-
};
|
|
4429
|
-
let html2;
|
|
4430
|
-
let hash = "";
|
|
4431
|
-
let size = 0;
|
|
4432
|
-
if (typeof template === "object" && template !== null && "file" in template) {
|
|
4433
|
-
const result = renderComponent(
|
|
4434
|
-
{ source: template, inlineReact: resourceMode === "inline" },
|
|
4435
|
-
{ ...shellConfig, csp: buildCspConfig(resolver, options.csp) }
|
|
4436
|
-
);
|
|
4437
|
-
html2 = result.html;
|
|
4438
|
-
hash = result.hash;
|
|
4439
|
-
size = result.size;
|
|
4440
|
-
} else if (typeof template === "function") {
|
|
4441
|
-
const helpers = createTemplateHelpers();
|
|
4442
|
-
const ctx = { input: input ?? {}, output: output ?? {}, helpers };
|
|
4443
|
-
let rawResult;
|
|
4444
|
-
let isHtmlBuilder = uiType !== "react" || !isDefinitelyReactComponent(template);
|
|
4445
|
-
if (isHtmlBuilder) {
|
|
4446
|
-
try {
|
|
4447
|
-
rawResult = template(ctx);
|
|
4448
|
-
isHtmlBuilder = !isReactTypedObject(rawResult);
|
|
4449
|
-
} catch (error) {
|
|
4450
|
-
if (uiType !== "react") throw error;
|
|
4451
|
-
isHtmlBuilder = false;
|
|
4452
|
-
}
|
|
4453
|
-
}
|
|
4454
|
-
if (!isHtmlBuilder) {
|
|
4455
|
-
warnComponentReference(template, toolName, options.logger ?? console);
|
|
4456
|
-
const shellResult = buildShell('<div id="root"></div>', shellConfig);
|
|
4457
|
-
html2 = shellResult.html;
|
|
4458
|
-
hash = shellResult.hash;
|
|
4459
|
-
size = shellResult.size;
|
|
4460
|
-
} else {
|
|
4461
|
-
const wrapped = isTrustedHtml(rawResult) ? void 0 : wrapDetectedContent(rawResult);
|
|
4462
|
-
if (wrapped) {
|
|
4463
|
-
html2 = wrapped;
|
|
4464
|
-
} else {
|
|
4465
|
-
const shellResult = buildShell(renderUnwrappedResult(rawResult, options), shellConfig);
|
|
4466
|
-
html2 = shellResult.html;
|
|
4467
|
-
hash = shellResult.hash;
|
|
4468
|
-
size = shellResult.size;
|
|
4469
|
-
}
|
|
4470
|
-
}
|
|
4471
|
-
} else if (typeof template === "string") {
|
|
4472
|
-
const shellResult = buildShell(uiType === "markdown" ? markdownToHtml(template) : template, shellConfig);
|
|
4473
|
-
html2 = shellResult.html;
|
|
4474
|
-
hash = shellResult.hash;
|
|
4475
|
-
size = shellResult.size;
|
|
4476
|
-
} else {
|
|
4477
|
-
if (isReactTypedObject(template)) warnComponentReference(template, toolName, options.logger ?? console);
|
|
4478
|
-
const shellResult = buildShell('<div id="root"></div>', shellConfig);
|
|
4479
|
-
html2 = shellResult.html;
|
|
4480
|
-
hash = shellResult.hash;
|
|
4481
|
-
size = shellResult.size;
|
|
4482
|
-
}
|
|
4483
|
-
const htmlKey = "ui/html";
|
|
4484
|
-
const meta = {
|
|
4485
|
-
[htmlKey]: html2,
|
|
4486
|
-
"ui/type": uiType,
|
|
4487
|
-
"ui/mimeType": MCP_APPS_MIME_TYPE
|
|
4488
|
-
};
|
|
4489
|
-
if (hasSizing(sizing)) {
|
|
4490
|
-
if (sizing.preferredHeight !== void 0) meta["ui/preferredHeight"] = sizing.preferredHeight;
|
|
4491
|
-
if (sizing.minHeight !== void 0) meta["ui/minHeight"] = sizing.minHeight;
|
|
4492
|
-
if (sizing.maxHeight !== void 0) meta["ui/maxHeight"] = sizing.maxHeight;
|
|
4493
|
-
if (sizing.aspectRatio !== void 0) meta["ui/aspectRatio"] = sizing.aspectRatio;
|
|
4494
|
-
}
|
|
4495
|
-
return {
|
|
4496
|
-
html: html2,
|
|
4497
|
-
uiType,
|
|
4498
|
-
hash,
|
|
4499
|
-
size: size || Buffer.byteLength(html2, "utf8"),
|
|
4500
|
-
meta
|
|
4501
|
-
};
|
|
4502
|
-
}
|
|
4503
|
-
|
|
4504
|
-
// libs/uipack/src/adapters/base-template.ts
|
|
4505
|
-
function createDefaultBaseTemplate(options) {
|
|
4506
|
-
const { toolName } = options;
|
|
4507
|
-
const safeToolName = escapeHtml(toolName);
|
|
4508
|
-
const bridgeScript = `<script>${generateBridgeIIFE({ minify: true })}</script>`;
|
|
4509
|
-
const content = `
|
|
4510
|
-
${bridgeScript}
|
|
4511
|
-
<div id="root">
|
|
4512
|
-
<div style="font-family:system-ui,sans-serif;padding:1rem;color:#374151;">
|
|
4513
|
-
<p style="color:#6b7280;font-size:0.875rem;">Waiting for tool output...</p>
|
|
4514
|
-
<p style="color:#9ca3af;font-size:0.75rem;">Tool: <code>${safeToolName}</code></p>
|
|
4515
|
-
</div>
|
|
4516
|
-
</div>
|
|
4517
|
-
<script>
|
|
4518
|
-
(function() {
|
|
4519
|
-
var bridge = window.FrontMcpBridge;
|
|
4520
|
-
if (bridge && typeof bridge.onToolResult === 'function') {
|
|
4521
|
-
bridge.onToolResult(function(data) {
|
|
4522
|
-
var root = document.getElementById('root');
|
|
4523
|
-
if (root && data) {
|
|
4524
|
-
// textContent, not innerHTML: the previous version escaped only < and >, leaving
|
|
4525
|
-
// the rendered JSON wrong wherever the data contained an entity, and relying on a
|
|
4526
|
-
// hand-rolled replace for safety (GHSA-rhr9-vhpf-jqp7).
|
|
4527
|
-
var pre = document.createElement('pre');
|
|
4528
|
-
pre.setAttribute('style', 'font-family:monospace;white-space:pre-wrap;padding:1rem;');
|
|
4529
|
-
pre.textContent = JSON.stringify(data, null, 2);
|
|
4530
|
-
root.replaceChildren(pre);
|
|
1321
|
+
</script>`;const result=buildShell(content,{toolName:"pdf",csp:{resourceDomains:["https://esm.sh"],connectDomains:["https://esm.sh"]},includeBridge:false});return result.html}function wrapDetectedContent(value){const contentType=detectContentType(value);switch(contentType){case"chart":return buildChartHtml(value);case"mermaid":return buildMermaidHtml(value);case"pdf":return buildPdfHtml(value);case"html":return void 0;default:return void 0}}var SAFE_HREF=/^(https?:|mailto:|\/|#)/i;function emphasis(text){return text.replace(/\*\*([^*]+)\*\*/g,"<strong>$1</strong>").replace(/\*([^*]+)\*/g,"<em>$1</em>")}function inline(text){const stash=[];const hold=html2=>`\uE000${stash.push(html2)-1}\uE000`;let out=escapeHtml(text.replace(/\uE000/g,"")).replace(/`([^`]+)`/g,(_m,code)=>hold(`<code>${code}</code>`));out=out.replace(/\[([^\]]+)\]\(([^)\s]+)\)/g,(_m,label,href)=>SAFE_HREF.test(href)?hold(`<a href="${href}" rel="noopener noreferrer">${emphasis(label)}</a>`):label);out=emphasis(out);for(let i=0;i<2;i++)out=out.replace(/\uE000(\d+)\uE000/g,(_m,n)=>stash[Number(n)]);return out}function markdownToHtml(source){const lines=source.replace(/\r\n?/g,"\n").split("\n");const blocks=[];let paragraph=[];let list;const flushParagraph=()=>{if(paragraph.length>0)blocks.push(`<p>${inline(paragraph.join(" "))}</p>`);paragraph=[]};const flushList=()=>{if(list)blocks.push(`<${list.tag}>${list.items.map(item=>`<li>${inline(item)}</li>`).join("")}</${list.tag}>`);list=void 0};for(let i=0;i<lines.length;i++){const line=lines[i];const fence=/^\s*(`{3,})/.exec(line);if(fence){flushParagraph();flushList();const fenceLength=fence[1].length;const isClosing=l=>{const m=/^\s*(`{3,})\s*$/.exec(l);return m!==null&&m[1].length>=fenceLength};const code=[];for(i++;i<lines.length&&!isClosing(lines[i]);i++)code.push(lines[i]);blocks.push(`<pre><code>${escapeHtml(code.join("\n"))}
|
|
1322
|
+
</code></pre>`);continue}const heading=/^(#{1,6})\s+(.*)$/.exec(line);if(heading){flushParagraph();flushList();blocks.push(`<h${heading[1].length}>${inline(heading[2].trim())}</h${heading[1].length}>`);continue}const item=/^\s*([-*+]|\d+\.)\s+(.*)$/.exec(line);if(item){flushParagraph();const tag=/\d/.test(item[1])?"ol":"ul";if(list&&list.tag!==tag)flushList();list??={tag,items:[]};list.items.push(item[2]);continue}if(line.trim()===""){flushParagraph();flushList();continue}flushList();paragraph.push(line.trim())}flushParagraph();flushList();return blocks.join("\n")}function detectUIType(template){if(template===null||template===void 0){return"auto"}if(typeof template==="object"&&template!==null&&"file"in template){const file=template.file;if(/\.(tsx|jsx)$/i.test(file))return"react"}if(typeof template==="function"){const proto=template.prototype;if(proto&&typeof proto.render==="function"){return"react"}const asRecord=template;if(asRecord["$$typeof"]!==void 0){return"react"}if(template.name&&/^[A-Z]/.test(template.name)){return"react"}return"html"}if(typeof template==="string"){if(template.includes("<")&&template.includes(">")){return"html"}return"markdown"}return"auto"}function buildCspConfig(resolver,extra){const cspResourceDomains=["https://esm.sh"];const cspConnectDomains=["https://esm.sh"];for(const domain of extra?.resourceDomains??[]){if(!cspResourceDomains.includes(domain))cspResourceDomains.push(domain)}for(const domain of extra?.connectDomains??[]){if(!cspConnectDomains.includes(domain))cspConnectDomains.push(domain)}if(resolver&&"overrides"in resolver){const overrides=resolver.overrides;if(overrides){for(const url of Object.values(overrides)){try{const origin=new URL(url).origin;if(!cspResourceDomains.includes(origin))cspResourceDomains.push(origin);if(!cspConnectDomains.includes(origin))cspConnectDomains.push(origin)}catch{}}}}return{resourceDomains:cspResourceDomains,connectDomains:cspConnectDomains}}var STRING_RESULT_DOCS="https://frontmcp.dev/learn/your-first-widget";var noticedStringResultTools=new Set;function noticeStringResult(toolName,logger){if(noticedStringResultTools.has(toolName))return;noticedStringResultTools.add(toolName);logger.warn(`[frontmcp] The UI template of tool "${toolName}" returned a plain string containing markup. Since 1.9.2, FrontMCP HTML-escapes plain string results by default, so it is shown as text. Build the markup with ctx.helpers.html\`\u2026\` (interpolated values are escaped) or wrap safe markup with ctx.helpers.trustedHtml(). Set ui.escapeStringResults: false to render plain strings as markup, or true to silence this notice. See ${STRING_RESULT_DOCS}`)}function renderUnwrappedResult(rawResult,options){if(isTrustedHtml(rawResult)){return String(rawResult)}if(typeof rawResult!=="string"){return`<pre>${escapeHtml(JSON.stringify(rawResult,null,2))}</pre>`}const looksLikeMarkup=detectContentType(rawResult)==="html";if(looksLikeMarkup&&options.escapeStringResults===false){return rawResult}if(looksLikeMarkup&&options.escapeStringResults===void 0){noticeStringResult(options.toolName,options.logger??console)}return escapeHtml(rawResult)}var warnedComponentReferences=new WeakMap;function warnComponentReference(template,toolName,logger){const warned=warnedComponentReferences.get(template)??new Set;if(warned.has(toolName))return;warned.add(toolName);warnedComponentReferences.set(template,warned);logger.warn(`[FrontMCP] Tool "${toolName}": \`ui.template\` is a React component reference, which cannot be bundled (there is no source file to compile), so the widget would render an empty page. Use \`template: { file: './widget.tsx' }\` instead.`)}var COMPILED_JSX_PATTERN=/\b(?:React\.createElement|_?jsx(?:s|DEV)?)\)?\s*\(/;function isComponentReference(template){if(isReactTypedObject(template))return true;if(typeof template!=="function"||detectUIType(template)!=="react")return false;return isDefinitelyReactComponent(template)||COMPILED_JSX_PATTERN.test(Function.prototype.toString.call(template))}function warnIfComponentReference(toolName,template,logger=console){if(!isComponentReference(template))return false;warnComponentReference(template,toolName,logger);return true}function isReactTypedObject(value){return typeof value==="object"&&value!==null&&"$$typeof"in value}function isDefinitelyReactComponent(template){if(typeof template!=="function")return false;const proto=template.prototype;return typeof proto?.render==="function"||template["$$typeof"]!==void 0}function renderToolTemplate(options){const{toolName,input,output,template,resolver,platformType,sizing}=options;const uiType=detectUIType(template);const resourceMode=options.resourceMode??(platformType==="claude"?"inline":"cdn");const shellConfig={toolName,input,output,includeBridge:true,resolver,sizing,displayMode:options.displayMode,csp:options.csp?buildCspConfig(resolver,options.csp):void 0};let html2;let hash="";let size=0;if(typeof template==="object"&&template!==null&&"file"in template){const result=renderComponent({source:template,inlineReact:resourceMode==="inline"},{...shellConfig,csp:buildCspConfig(resolver,options.csp)});html2=result.html;hash=result.hash;size=result.size}else if(typeof template==="function"){const helpers=createTemplateHelpers();const ctx={input:input??{},output:output??{},helpers};let rawResult;let isHtmlBuilder=uiType!=="react"||!isDefinitelyReactComponent(template);if(isHtmlBuilder){try{rawResult=template(ctx);isHtmlBuilder=!isReactTypedObject(rawResult)}catch(error){if(uiType!=="react")throw error;isHtmlBuilder=false}}if(!isHtmlBuilder){warnComponentReference(template,toolName,options.logger??console);const shellResult=buildShell('<div id="root"></div>',shellConfig);html2=shellResult.html;hash=shellResult.hash;size=shellResult.size}else{const wrapped=isTrustedHtml(rawResult)?void 0:wrapDetectedContent(rawResult);if(wrapped){html2=wrapped}else{const shellResult=buildShell(renderUnwrappedResult(rawResult,options),shellConfig);html2=shellResult.html;hash=shellResult.hash;size=shellResult.size}}}else if(typeof template==="string"){const shellResult=buildShell(uiType==="markdown"?markdownToHtml(template):template,shellConfig);html2=shellResult.html;hash=shellResult.hash;size=shellResult.size}else{if(isReactTypedObject(template))warnComponentReference(template,toolName,options.logger??console);const shellResult=buildShell('<div id="root"></div>',shellConfig);html2=shellResult.html;hash=shellResult.hash;size=shellResult.size}const htmlKey="ui/html";const meta={[htmlKey]:html2,"ui/type":uiType,"ui/mimeType":MCP_APPS_MIME_TYPE};if(hasSizing(sizing)){if(sizing.preferredHeight!==void 0)meta["ui/preferredHeight"]=sizing.preferredHeight;if(sizing.minHeight!==void 0)meta["ui/minHeight"]=sizing.minHeight;if(sizing.maxHeight!==void 0)meta["ui/maxHeight"]=sizing.maxHeight;if(sizing.aspectRatio!==void 0)meta["ui/aspectRatio"]=sizing.aspectRatio}return{html:html2,uiType,hash,size:size||Buffer.byteLength(html2,"utf8"),meta}}function createDefaultBaseTemplate(options){const{toolName}=options;const safeToolName=escapeHtml(toolName);const bridgeScript=`<script>${generateBridgeIIFE({minify:true})}</script>`;const content=`
|
|
1323
|
+
${bridgeScript}
|
|
1324
|
+
<div id="root">
|
|
1325
|
+
<div style="font-family:system-ui,sans-serif;padding:1rem;color:#374151;">
|
|
1326
|
+
<p style="color:#6b7280;font-size:0.875rem;">Waiting for tool output...</p>
|
|
1327
|
+
<p style="color:#9ca3af;font-size:0.75rem;">Tool: <code>${safeToolName}</code></p>
|
|
1328
|
+
</div>
|
|
1329
|
+
</div>
|
|
1330
|
+
<script>
|
|
1331
|
+
(function() {
|
|
1332
|
+
var bridge = window.FrontMcpBridge;
|
|
1333
|
+
if (bridge && typeof bridge.onToolResult === 'function') {
|
|
1334
|
+
bridge.onToolResult(function(data) {
|
|
1335
|
+
var root = document.getElementById('root');
|
|
1336
|
+
if (root && data) {
|
|
1337
|
+
// textContent, not innerHTML: the previous version escaped only < and >, leaving
|
|
1338
|
+
// the rendered JSON wrong wherever the data contained an entity, and relying on a
|
|
1339
|
+
// hand-rolled replace for safety (GHSA-rhr9-vhpf-jqp7).
|
|
1340
|
+
var pre = document.createElement('pre');
|
|
1341
|
+
pre.setAttribute('style', 'font-family:monospace;white-space:pre-wrap;padding:1rem;');
|
|
1342
|
+
pre.textContent = JSON.stringify(data, null, 2);
|
|
1343
|
+
root.replaceChildren(pre);
|
|
4531
1344
|
}
|
|
4532
1345
|
});
|
|
4533
1346
|
}
|
|
4534
1347
|
})();
|
|
4535
|
-
</script>`;
|
|
4536
|
-
const result = buildShell(content, {
|
|
4537
|
-
toolName,
|
|
4538
|
-
includeBridge: false,
|
|
4539
|
-
// Bridge already included via inline script above
|
|
4540
|
-
title: `Widget: ${toolName}`
|
|
4541
|
-
});
|
|
4542
|
-
return result.html;
|
|
4543
|
-
}
|
|
4544
|
-
|
|
4545
|
-
// libs/uipack/src/adapters/cdn-info.ts
|
|
4546
|
-
var ESM_SH_BASE = "https://esm.sh";
|
|
4547
|
-
var REACT_DEPS = [
|
|
4548
|
-
{ name: "react", url: `${ESM_SH_BASE}/react@18` },
|
|
4549
|
-
{ name: "react-dom", url: `${ESM_SH_BASE}/react-dom@18` }
|
|
4550
|
-
];
|
|
4551
|
-
function buildCDNInfoForUIType(uiType) {
|
|
4552
|
-
const deps = [];
|
|
4553
|
-
switch (uiType) {
|
|
4554
|
-
case "react":
|
|
4555
|
-
deps.push(...REACT_DEPS);
|
|
4556
|
-
break;
|
|
4557
|
-
case "mdx":
|
|
4558
|
-
deps.push(...REACT_DEPS);
|
|
4559
|
-
deps.push({ name: "marked", url: `${ESM_SH_BASE}/marked@latest` });
|
|
4560
|
-
break;
|
|
4561
|
-
case "markdown":
|
|
4562
|
-
deps.push({ name: "marked", url: `${ESM_SH_BASE}/marked@latest` });
|
|
4563
|
-
break;
|
|
4564
|
-
case "auto":
|
|
4565
|
-
deps.push(...REACT_DEPS);
|
|
4566
|
-
deps.push({ name: "marked", url: `${ESM_SH_BASE}/marked@latest` });
|
|
4567
|
-
break;
|
|
4568
|
-
case "html":
|
|
4569
|
-
default:
|
|
4570
|
-
break;
|
|
4571
|
-
}
|
|
4572
|
-
return {
|
|
4573
|
-
base: ESM_SH_BASE,
|
|
4574
|
-
dependencies: deps
|
|
4575
|
-
};
|
|
4576
|
-
}
|
|
4577
|
-
export {
|
|
4578
|
-
BRIDGE_SCRIPT_TAGS,
|
|
4579
|
-
DEFAULT_CDN_DOMAINS,
|
|
4580
|
-
DEFAULT_CDN_REGISTRY,
|
|
4581
|
-
DEFAULT_CSP_BY_TYPE,
|
|
4582
|
-
DEFAULT_CSP_DIRECTIVES,
|
|
4583
|
-
DEFAULT_RENDERER_ASSETS,
|
|
4584
|
-
DEFAULT_CACHE_OPTIONS as DEFAULT_TYPE_CACHE_OPTIONS,
|
|
4585
|
-
DEFAULT_TYPE_CACHE_TTL,
|
|
4586
|
-
DEFAULT_TYPE_FETCHER_OPTIONS,
|
|
4587
|
-
FRONTMCP_META_KEY,
|
|
4588
|
-
MCP_APPS_EXTENSION_ID,
|
|
4589
|
-
MCP_APPS_MIME_TYPE,
|
|
4590
|
-
MemoryTypeCache,
|
|
4591
|
-
OPTIONAL_PLACEHOLDERS,
|
|
4592
|
-
REQUIRED_PLACEHOLDERS,
|
|
4593
|
-
RESTRICTIVE_CSP_DIRECTIVES,
|
|
4594
|
-
SHELL_PLACEHOLDERS,
|
|
4595
|
-
SHELL_PLACEHOLDER_NAMES,
|
|
4596
|
-
TYPE_CACHE_PREFIX,
|
|
4597
|
-
TypeFetcher,
|
|
4598
|
-
UNIVERSAL_BRIDGE_SCRIPT,
|
|
4599
|
-
applyShellTemplate,
|
|
4600
|
-
buildCDNInfoForUIType,
|
|
4601
|
-
buildCSPDirectives,
|
|
4602
|
-
buildCSPMetaTag,
|
|
4603
|
-
buildChartHtml,
|
|
4604
|
-
buildCustomDataInjectionScript,
|
|
4605
|
-
buildDataInjectionScript,
|
|
4606
|
-
buildMermaidHtml,
|
|
4607
|
-
buildPdfHtml,
|
|
4608
|
-
buildShell,
|
|
4609
|
-
buildToolResponseContent,
|
|
4610
|
-
bundleFileSource,
|
|
4611
|
-
clearShellTemplateCache,
|
|
4612
|
-
combineDtsContents,
|
|
4613
|
-
createDefaultBaseTemplate,
|
|
4614
|
-
createEsmShResolver,
|
|
4615
|
-
createImportMap,
|
|
4616
|
-
createImportMapFromResolved,
|
|
4617
|
-
createResolverWithOverrides,
|
|
4618
|
-
createTemplateHelpers,
|
|
4619
|
-
createTypeFetcher,
|
|
4620
|
-
detectContentType,
|
|
4621
|
-
detectUIType,
|
|
4622
|
-
escapeHtml,
|
|
4623
|
-
escapeHtmlAttr,
|
|
4624
|
-
escapeJsString,
|
|
4625
|
-
escapeScriptClose,
|
|
4626
|
-
extractDefaultExportName,
|
|
4627
|
-
extractExternalPackages,
|
|
4628
|
-
generateBridgeIIFE,
|
|
4629
|
-
generateCDNScriptTags,
|
|
4630
|
-
generateDependencyHTML,
|
|
4631
|
-
generateImportMapScriptTag,
|
|
4632
|
-
generateImportMapScriptTagMinified,
|
|
4633
|
-
generateMountScript,
|
|
4634
|
-
generatePlatformBundle,
|
|
4635
|
-
getPackageCDNUrl,
|
|
4636
|
-
getPackageFromSpecifier,
|
|
4637
|
-
getPackageName,
|
|
4638
|
-
getPackagePeerDependencies,
|
|
4639
|
-
getRegisteredPackages,
|
|
4640
|
-
getSubpathFromSpecifier,
|
|
4641
|
-
globalTypeCache,
|
|
4642
|
-
html,
|
|
4643
|
-
isBundlingMode,
|
|
4644
|
-
isDisplayMode,
|
|
4645
|
-
isFileSource,
|
|
4646
|
-
isFunctionSource,
|
|
4647
|
-
isImportSource,
|
|
4648
|
-
isInlineShellSource,
|
|
4649
|
-
isNpmShellSource,
|
|
4650
|
-
isNpmSource,
|
|
4651
|
-
isOutputMode,
|
|
4652
|
-
isPackageRegistered,
|
|
4653
|
-
isRelativeImport,
|
|
4654
|
-
isResourceMode,
|
|
4655
|
-
isTrustedHtml,
|
|
4656
|
-
isUIRenderFailure,
|
|
4657
|
-
isUIType,
|
|
4658
|
-
isUrlShellSource,
|
|
4659
|
-
lookupPackage,
|
|
4660
|
-
mergeImportMaps,
|
|
4661
|
-
mergeRegistries,
|
|
4662
|
-
parseDtsImports,
|
|
4663
|
-
parseImportStatement,
|
|
4664
|
-
parseImports,
|
|
4665
|
-
renderComponent,
|
|
4666
|
-
renderToolTemplate,
|
|
4667
|
-
resolveServingMode,
|
|
4668
|
-
resolveShellTemplate,
|
|
4669
|
-
resolveUISource,
|
|
4670
|
-
rewriteImports,
|
|
4671
|
-
safeJsonForScript,
|
|
4672
|
-
safeStringify,
|
|
4673
|
-
sanitizeCSPDomains,
|
|
4674
|
-
transpileReactSource,
|
|
4675
|
-
trustedHtml,
|
|
4676
|
-
validateCSPDomain,
|
|
4677
|
-
validateImportMap,
|
|
4678
|
-
validateShellTemplate,
|
|
4679
|
-
warnIfComponentReference,
|
|
4680
|
-
wrapDetectedContent
|
|
4681
|
-
};
|
|
1348
|
+
</script>`;const result=buildShell(content,{toolName,includeBridge:false,title:`Widget: ${toolName}`});return result.html}var ESM_SH_BASE="https://esm.sh";var REACT_DEPS=[{name:"react",url:`${ESM_SH_BASE}/react@18`},{name:"react-dom",url:`${ESM_SH_BASE}/react-dom@18`}];function buildCDNInfoForUIType(uiType){const deps=[];switch(uiType){case"react":deps.push(...REACT_DEPS);break;case"mdx":deps.push(...REACT_DEPS);deps.push({name:"marked",url:`${ESM_SH_BASE}/marked@latest`});break;case"markdown":deps.push({name:"marked",url:`${ESM_SH_BASE}/marked@latest`});break;case"auto":deps.push(...REACT_DEPS);deps.push({name:"marked",url:`${ESM_SH_BASE}/marked@latest`});break;case"html":default:break}return{base:ESM_SH_BASE,dependencies:deps}}export{BRIDGE_SCRIPT_TAGS,DEFAULT_CDN_DOMAINS,DEFAULT_CDN_REGISTRY,DEFAULT_CSP_BY_TYPE,DEFAULT_CSP_DIRECTIVES,DEFAULT_RENDERER_ASSETS,DEFAULT_CACHE_OPTIONS as DEFAULT_TYPE_CACHE_OPTIONS,DEFAULT_TYPE_CACHE_TTL,DEFAULT_TYPE_FETCHER_OPTIONS,FRONTMCP_META_KEY,MCP_APPS_EXTENSION_ID,MCP_APPS_MIME_TYPE,MemoryTypeCache,OPTIONAL_PLACEHOLDERS,REQUIRED_PLACEHOLDERS,RESTRICTIVE_CSP_DIRECTIVES,SHELL_PLACEHOLDERS,SHELL_PLACEHOLDER_NAMES,TYPE_CACHE_PREFIX,TypeFetcher,UNIVERSAL_BRIDGE_SCRIPT,applyShellTemplate,buildCDNInfoForUIType,buildCSPDirectives,buildCSPMetaTag,buildChartHtml,buildCustomDataInjectionScript,buildDataInjectionScript,buildMermaidHtml,buildPdfHtml,buildShell,buildToolResponseContent,bundleFileSource,clearShellTemplateCache,combineDtsContents,createDefaultBaseTemplate,createEsmShResolver,createImportMap,createImportMapFromResolved,createResolverWithOverrides,createTemplateHelpers,createTypeFetcher,detectContentType,detectUIType,escapeHtml,escapeHtmlAttr,escapeJsString,escapeScriptClose,extractDefaultExportName,extractExternalPackages,generateBridgeIIFE,generateCDNScriptTags,generateDependencyHTML,generateImportMapScriptTag,generateImportMapScriptTagMinified,generateMountScript,generatePlatformBundle,getPackageCDNUrl,getPackageFromSpecifier,getPackageName,getPackagePeerDependencies,getRegisteredPackages,getSubpathFromSpecifier,globalTypeCache,html,isBundlingMode,isDisplayMode,isFileSource,isFunctionSource,isImportSource,isInlineShellSource,isNpmShellSource,isNpmSource,isOutputMode,isPackageRegistered,isRelativeImport,isResourceMode,isTrustedHtml,isUIRenderFailure,isUIType,isUrlShellSource,lookupPackage,mergeImportMaps,mergeRegistries,parseDtsImports,parseImportStatement,parseImports,renderComponent,renderToolTemplate,resolveServingMode,resolveShellTemplate,resolveUISource,rewriteImports,safeJsonForScript,safeStringify,sanitizeCSPDomains,transpileReactSource,trustedHtml,validateCSPDomain,validateImportMap,validateShellTemplate,warnIfComponentReference,wrapDetectedContent};
|