@frontmcp/uipack 1.2.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/adapters/index.js +1046 -698
- package/adapters/template-renderer.d.ts +14 -0
- package/adapters/template-renderer.d.ts.map +1 -1
- package/bridge-runtime/iife-generator.d.ts.map +1 -1
- package/bridge-runtime/index.js +149 -0
- package/component/index.d.ts +1 -0
- package/component/index.d.ts.map +1 -1
- package/component/index.js +468 -145
- package/component/loader.d.ts +21 -2
- package/component/loader.d.ts.map +1 -1
- package/component/renderer.d.ts +2 -2
- package/component/renderer.d.ts.map +1 -1
- package/component/transpiler.d.ts +16 -1
- package/component/transpiler.d.ts.map +1 -1
- package/component/types.d.ts +19 -0
- package/component/types.d.ts.map +1 -1
- package/component/ui-availability.d.ts +27 -0
- package/component/ui-availability.d.ts.map +1 -0
- package/esm/adapters/index.mjs +1046 -698
- package/esm/bridge-runtime/index.mjs +149 -0
- package/esm/component/index.mjs +468 -145
- package/esm/index.mjs +444 -109
- package/esm/package.json +2 -2
- package/esm/shell/index.mjs +420 -171
- package/index.d.ts +1 -1
- package/index.d.ts.map +1 -1
- package/index.js +445 -109
- package/package.json +2 -2
- package/shell/builder.d.ts.map +1 -1
- package/shell/data-injector.d.ts +27 -1
- package/shell/data-injector.d.ts.map +1 -1
- package/shell/index.d.ts +3 -2
- package/shell/index.d.ts.map +1 -1
- package/shell/index.js +423 -171
- package/shell/sizing-css.d.ts +27 -0
- package/shell/sizing-css.d.ts.map +1 -0
- package/shell/types.d.ts +102 -0
- package/shell/types.d.ts.map +1 -1
- package/types/index.d.ts +1 -1
- package/types/index.d.ts.map +1 -1
- package/types/ui-config.d.ts +105 -11
- package/types/ui-config.d.ts.map +1 -1
- package/types/ui-runtime.d.ts +23 -2
- package/types/ui-runtime.d.ts.map +1 -1
package/esm/adapters/index.mjs
CHANGED
|
@@ -74,78 +74,404 @@ function isUIRenderFailure(result) {
|
|
|
74
74
|
return typeof rec["reason"] === "string" && !("meta" in rec);
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
// libs/uipack/src/
|
|
78
|
-
var
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
77
|
+
// libs/uipack/src/resolver/cdn-registry.ts
|
|
78
|
+
var DEFAULT_CDN_REGISTRY = {
|
|
79
|
+
react: {
|
|
80
|
+
packageName: "react",
|
|
81
|
+
defaultVersion: "19.2.4",
|
|
82
|
+
providers: {
|
|
83
|
+
cloudflare: {
|
|
84
|
+
url: "https://cdnjs.cloudflare.com/ajax/libs/react/19.2.4/umd/react.production.min.js",
|
|
85
|
+
global: "React",
|
|
86
|
+
crossorigin: "anonymous"
|
|
87
|
+
},
|
|
88
|
+
jsdelivr: {
|
|
89
|
+
url: "https://cdn.jsdelivr.net/npm/react@19.2.4/umd/react.production.min.js",
|
|
90
|
+
global: "React",
|
|
91
|
+
crossorigin: "anonymous"
|
|
92
|
+
},
|
|
93
|
+
unpkg: {
|
|
94
|
+
url: "https://unpkg.com/react@19.2.4/umd/react.production.min.js",
|
|
95
|
+
global: "React",
|
|
96
|
+
crossorigin: "anonymous"
|
|
97
|
+
},
|
|
98
|
+
"esm.sh": {
|
|
99
|
+
url: "https://esm.sh/react@19.2.4",
|
|
100
|
+
esm: true,
|
|
101
|
+
crossorigin: "anonymous"
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
preferredProviders: ["esm.sh", "cloudflare", "jsdelivr", "unpkg"],
|
|
105
|
+
metadata: {
|
|
106
|
+
description: "A JavaScript library for building user interfaces",
|
|
107
|
+
homepage: "https://react.dev",
|
|
108
|
+
license: "MIT"
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
"react-dom": {
|
|
112
|
+
packageName: "react-dom",
|
|
113
|
+
defaultVersion: "19.2.4",
|
|
114
|
+
providers: {
|
|
115
|
+
cloudflare: {
|
|
116
|
+
url: "https://cdnjs.cloudflare.com/ajax/libs/react-dom/19.2.4/umd/react-dom.production.min.js",
|
|
117
|
+
global: "ReactDOM",
|
|
118
|
+
crossorigin: "anonymous",
|
|
119
|
+
peerDependencies: ["react"]
|
|
120
|
+
},
|
|
121
|
+
jsdelivr: {
|
|
122
|
+
url: "https://cdn.jsdelivr.net/npm/react-dom@19.2.4/umd/react-dom.production.min.js",
|
|
123
|
+
global: "ReactDOM",
|
|
124
|
+
crossorigin: "anonymous",
|
|
125
|
+
peerDependencies: ["react"]
|
|
126
|
+
},
|
|
127
|
+
unpkg: {
|
|
128
|
+
url: "https://unpkg.com/react-dom@19.2.4/umd/react-dom.production.min.js",
|
|
129
|
+
global: "ReactDOM",
|
|
130
|
+
crossorigin: "anonymous",
|
|
131
|
+
peerDependencies: ["react"]
|
|
132
|
+
},
|
|
133
|
+
"esm.sh": {
|
|
134
|
+
url: "https://esm.sh/react-dom@19.2.4",
|
|
135
|
+
esm: true,
|
|
136
|
+
crossorigin: "anonymous",
|
|
137
|
+
peerDependencies: ["react"]
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
preferredProviders: ["esm.sh", "cloudflare", "jsdelivr", "unpkg"],
|
|
141
|
+
metadata: {
|
|
142
|
+
description: "React package for working with the DOM",
|
|
143
|
+
homepage: "https://react.dev",
|
|
144
|
+
license: "MIT"
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
"chart.js": {
|
|
148
|
+
packageName: "chart.js",
|
|
149
|
+
defaultVersion: "4.5.1",
|
|
150
|
+
providers: {
|
|
151
|
+
cloudflare: {
|
|
152
|
+
url: "https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.5.1/chart.umd.min.js",
|
|
153
|
+
global: "Chart",
|
|
154
|
+
crossorigin: "anonymous"
|
|
155
|
+
},
|
|
156
|
+
jsdelivr: {
|
|
157
|
+
url: "https://cdn.jsdelivr.net/npm/chart.js@4.5.1/dist/chart.umd.min.js",
|
|
158
|
+
global: "Chart",
|
|
159
|
+
crossorigin: "anonymous"
|
|
160
|
+
},
|
|
161
|
+
unpkg: {
|
|
162
|
+
url: "https://unpkg.com/chart.js@4.5.1/dist/chart.umd.min.js",
|
|
163
|
+
global: "Chart",
|
|
164
|
+
crossorigin: "anonymous"
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
preferredProviders: ["esm.sh", "cloudflare", "jsdelivr", "unpkg"],
|
|
168
|
+
metadata: {
|
|
169
|
+
description: "Simple yet flexible JavaScript charting library",
|
|
170
|
+
homepage: "https://www.chartjs.org",
|
|
171
|
+
license: "MIT"
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
d3: {
|
|
175
|
+
packageName: "d3",
|
|
176
|
+
defaultVersion: "7.9.0",
|
|
177
|
+
providers: {
|
|
178
|
+
cloudflare: {
|
|
179
|
+
url: "https://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.min.js",
|
|
180
|
+
integrity: "sha512-vc58qvvBdrDR4etbxMdlTt4GBQk1qjvyORR2nrsPsFPyrs+/u5c3+1Ct6upOgdZoIl7eq6k3a1UPDSNAQi/32A==",
|
|
181
|
+
global: "d3",
|
|
182
|
+
crossorigin: "anonymous"
|
|
183
|
+
},
|
|
184
|
+
jsdelivr: {
|
|
185
|
+
url: "https://cdn.jsdelivr.net/npm/d3@7.9.0/dist/d3.min.js",
|
|
186
|
+
global: "d3",
|
|
187
|
+
crossorigin: "anonymous"
|
|
188
|
+
},
|
|
189
|
+
unpkg: {
|
|
190
|
+
url: "https://unpkg.com/d3@7.9.0/dist/d3.min.js",
|
|
191
|
+
global: "d3",
|
|
192
|
+
crossorigin: "anonymous"
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
preferredProviders: ["esm.sh", "cloudflare", "jsdelivr", "unpkg"],
|
|
196
|
+
metadata: {
|
|
197
|
+
description: "Data-Driven Documents",
|
|
198
|
+
homepage: "https://d3js.org",
|
|
199
|
+
license: "ISC"
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
lodash: {
|
|
203
|
+
packageName: "lodash",
|
|
204
|
+
defaultVersion: "4.17.21",
|
|
205
|
+
providers: {
|
|
206
|
+
cloudflare: {
|
|
207
|
+
url: "https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js",
|
|
208
|
+
integrity: "sha512-WFN04846sdKMIP5LKNphMaWzU7YpMyCU245etK3g/2ARYbPK9Ub18eG+ljU96qKRCWh+quCY7yefSmlkQw1ANQ==",
|
|
209
|
+
global: "_",
|
|
210
|
+
crossorigin: "anonymous"
|
|
211
|
+
},
|
|
212
|
+
jsdelivr: {
|
|
213
|
+
url: "https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js",
|
|
214
|
+
global: "_",
|
|
215
|
+
crossorigin: "anonymous"
|
|
216
|
+
},
|
|
217
|
+
unpkg: {
|
|
218
|
+
url: "https://unpkg.com/lodash@4.17.21/lodash.min.js",
|
|
219
|
+
global: "_",
|
|
220
|
+
crossorigin: "anonymous"
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
preferredProviders: ["esm.sh", "cloudflare", "jsdelivr", "unpkg"],
|
|
224
|
+
metadata: {
|
|
225
|
+
description: "A modern JavaScript utility library",
|
|
226
|
+
homepage: "https://lodash.com",
|
|
227
|
+
license: "MIT"
|
|
228
|
+
}
|
|
97
229
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
]
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
230
|
+
};
|
|
231
|
+
function lookupPackage(packageName, registry = DEFAULT_CDN_REGISTRY) {
|
|
232
|
+
return registry[packageName];
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// libs/uipack/src/resolver/import-parser.ts
|
|
236
|
+
var IMPORT_PATTERNS = {
|
|
237
|
+
named: /import\s*\{([^}]+)\}\s*from\s*['"]([^'"]+)['"]/g,
|
|
238
|
+
default: /import\s+(\w+)\s+from\s*['"]([^'"]+)['"]/g,
|
|
239
|
+
namespace: /import\s*\*\s*as\s+(\w+)\s+from\s*['"]([^'"]+)['"]/g,
|
|
240
|
+
sideEffect: /import\s*['"]([^'"]+)['"]/g,
|
|
241
|
+
dynamic: /(?:await\s+)?import\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
242
|
+
defaultAndNamed: /import\s+(\w+)\s*,\s*\{([^}]+)\}\s*from\s*['"]([^'"]+)['"]/g,
|
|
243
|
+
defaultAndNamespace: /import\s+(\w+)\s*,\s*\*\s*as\s+(\w+)\s+from\s*['"]([^'"]+)['"]/g,
|
|
244
|
+
reExport: /export\s*\{[^}]+\}\s*from\s*['"]([^'"]+)['"]/g,
|
|
245
|
+
reExportAll: /export\s*\*\s*from\s*['"]([^'"]+)['"]/g
|
|
246
|
+
};
|
|
247
|
+
function parseNamedImports(namedString) {
|
|
248
|
+
return namedString.split(",").map((s) => s.trim()).filter((s) => s.length > 0).map((s) => {
|
|
249
|
+
const asMatch = s.match(/^(\w+)\s+as\s+\w+$/);
|
|
250
|
+
return asMatch ? asMatch[1] : s;
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
function isRelativeImport(specifier) {
|
|
254
|
+
return specifier.startsWith("./") || specifier.startsWith("../") || specifier.startsWith("/");
|
|
255
|
+
}
|
|
256
|
+
function isExternalImport(specifier) {
|
|
257
|
+
return !isRelativeImport(specifier) && !specifier.startsWith("#");
|
|
258
|
+
}
|
|
259
|
+
function getPackageName(specifier) {
|
|
260
|
+
if (specifier.startsWith("@")) {
|
|
261
|
+
const parts = specifier.split("/");
|
|
262
|
+
if (parts.length >= 2) {
|
|
263
|
+
return `${parts[0]}/${parts[1]}`;
|
|
264
|
+
}
|
|
265
|
+
return specifier;
|
|
114
266
|
}
|
|
115
|
-
|
|
116
|
-
return
|
|
267
|
+
const firstSlash = specifier.indexOf("/");
|
|
268
|
+
return firstSlash === -1 ? specifier : specifier.slice(0, firstSlash);
|
|
117
269
|
}
|
|
118
|
-
function
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
270
|
+
function getLineNumber(source, index) {
|
|
271
|
+
let line = 1;
|
|
272
|
+
for (let i = 0; i < index && i < source.length; i++) {
|
|
273
|
+
if (source[i] === "\n") {
|
|
274
|
+
line++;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return line;
|
|
122
278
|
}
|
|
123
|
-
function
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
279
|
+
function getColumnNumber(source, index) {
|
|
280
|
+
let column = 0;
|
|
281
|
+
for (let i = index - 1; i >= 0 && source[i] !== "\n"; i--) {
|
|
282
|
+
column++;
|
|
127
283
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
284
|
+
return column;
|
|
285
|
+
}
|
|
286
|
+
function parseImports(source) {
|
|
287
|
+
const imports = [];
|
|
288
|
+
const seenStatements = /* @__PURE__ */ new Set();
|
|
289
|
+
const addImport = (imp) => {
|
|
290
|
+
const key = `${imp.type}:${imp.specifier}:${imp.statement}`;
|
|
291
|
+
if (!seenStatements.has(key)) {
|
|
292
|
+
seenStatements.add(key);
|
|
293
|
+
imports.push(imp);
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
let match;
|
|
297
|
+
const defaultAndNamedRegex = new RegExp(IMPORT_PATTERNS.defaultAndNamed.source, "g");
|
|
298
|
+
while ((match = defaultAndNamedRegex.exec(source)) !== null) {
|
|
299
|
+
const [statement, defaultName, namedString, specifier] = match;
|
|
300
|
+
addImport({
|
|
301
|
+
statement,
|
|
302
|
+
specifier,
|
|
303
|
+
type: "default",
|
|
304
|
+
defaultImport: defaultName,
|
|
305
|
+
namedImports: parseNamedImports(namedString),
|
|
306
|
+
line: getLineNumber(source, match.index),
|
|
307
|
+
column: getColumnNumber(source, match.index)
|
|
308
|
+
});
|
|
133
309
|
}
|
|
310
|
+
const defaultAndNamespaceRegex = new RegExp(IMPORT_PATTERNS.defaultAndNamespace.source, "g");
|
|
311
|
+
while ((match = defaultAndNamespaceRegex.exec(source)) !== null) {
|
|
312
|
+
const [statement, defaultName, namespaceName, specifier] = match;
|
|
313
|
+
addImport({
|
|
314
|
+
statement,
|
|
315
|
+
specifier,
|
|
316
|
+
type: "default",
|
|
317
|
+
defaultImport: defaultName,
|
|
318
|
+
namespaceImport: namespaceName,
|
|
319
|
+
line: getLineNumber(source, match.index),
|
|
320
|
+
column: getColumnNumber(source, match.index)
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
const namedRegex = new RegExp(IMPORT_PATTERNS.named.source, "g");
|
|
324
|
+
while ((match = namedRegex.exec(source)) !== null) {
|
|
325
|
+
const [statement, namedString, specifier] = match;
|
|
326
|
+
addImport({
|
|
327
|
+
statement,
|
|
328
|
+
specifier,
|
|
329
|
+
type: "named",
|
|
330
|
+
namedImports: parseNamedImports(namedString),
|
|
331
|
+
line: getLineNumber(source, match.index),
|
|
332
|
+
column: getColumnNumber(source, match.index)
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
const defaultRegex = new RegExp(IMPORT_PATTERNS.default.source, "g");
|
|
336
|
+
while ((match = defaultRegex.exec(source)) !== null) {
|
|
337
|
+
const [statement, defaultName, specifier] = match;
|
|
338
|
+
const afterMatch = source.slice(match.index + match[0].length - specifier.length - 2);
|
|
339
|
+
if (afterMatch.startsWith(",")) continue;
|
|
340
|
+
addImport({
|
|
341
|
+
statement,
|
|
342
|
+
specifier,
|
|
343
|
+
type: "default",
|
|
344
|
+
defaultImport: defaultName,
|
|
345
|
+
line: getLineNumber(source, match.index),
|
|
346
|
+
column: getColumnNumber(source, match.index)
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
const namespaceRegex = new RegExp(IMPORT_PATTERNS.namespace.source, "g");
|
|
350
|
+
while ((match = namespaceRegex.exec(source)) !== null) {
|
|
351
|
+
const [statement, namespaceName, specifier] = match;
|
|
352
|
+
addImport({
|
|
353
|
+
statement,
|
|
354
|
+
specifier,
|
|
355
|
+
type: "namespace",
|
|
356
|
+
namespaceImport: namespaceName,
|
|
357
|
+
line: getLineNumber(source, match.index),
|
|
358
|
+
column: getColumnNumber(source, match.index)
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
const sideEffectRegex = new RegExp(IMPORT_PATTERNS.sideEffect.source, "g");
|
|
362
|
+
while ((match = sideEffectRegex.exec(source)) !== null) {
|
|
363
|
+
const [statement, specifier] = match;
|
|
364
|
+
const beforeMatch = source.slice(Math.max(0, match.index - 50), match.index);
|
|
365
|
+
if (beforeMatch.includes("from")) continue;
|
|
366
|
+
addImport({
|
|
367
|
+
statement,
|
|
368
|
+
specifier,
|
|
369
|
+
type: "side-effect",
|
|
370
|
+
line: getLineNumber(source, match.index),
|
|
371
|
+
column: getColumnNumber(source, match.index)
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
const dynamicRegex = new RegExp(IMPORT_PATTERNS.dynamic.source, "g");
|
|
375
|
+
while ((match = dynamicRegex.exec(source)) !== null) {
|
|
376
|
+
const [statement, specifier] = match;
|
|
377
|
+
addImport({
|
|
378
|
+
statement,
|
|
379
|
+
specifier,
|
|
380
|
+
type: "dynamic",
|
|
381
|
+
line: getLineNumber(source, match.index),
|
|
382
|
+
column: getColumnNumber(source, match.index)
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
const reExportRegex = new RegExp(IMPORT_PATTERNS.reExport.source, "g");
|
|
386
|
+
while ((match = reExportRegex.exec(source)) !== null) {
|
|
387
|
+
const [statement, specifier] = match;
|
|
388
|
+
addImport({
|
|
389
|
+
statement,
|
|
390
|
+
specifier,
|
|
391
|
+
type: "named",
|
|
392
|
+
line: getLineNumber(source, match.index),
|
|
393
|
+
column: getColumnNumber(source, match.index)
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
const reExportAllRegex = new RegExp(IMPORT_PATTERNS.reExportAll.source, "g");
|
|
397
|
+
while ((match = reExportAllRegex.exec(source)) !== null) {
|
|
398
|
+
const [statement, specifier] = match;
|
|
399
|
+
addImport({
|
|
400
|
+
statement,
|
|
401
|
+
specifier,
|
|
402
|
+
type: "namespace",
|
|
403
|
+
line: getLineNumber(source, match.index),
|
|
404
|
+
column: getColumnNumber(source, match.index)
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
const externalImports = imports.filter((imp) => isExternalImport(imp.specifier));
|
|
408
|
+
const relativeImports = imports.filter((imp) => isRelativeImport(imp.specifier));
|
|
409
|
+
const externalPackages = [...new Set(externalImports.map((imp) => getPackageName(imp.specifier)))];
|
|
410
|
+
return {
|
|
411
|
+
imports,
|
|
412
|
+
externalImports,
|
|
413
|
+
relativeImports,
|
|
414
|
+
externalPackages
|
|
415
|
+
};
|
|
134
416
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
417
|
+
|
|
418
|
+
// libs/uipack/src/resolver/esm-sh.resolver.ts
|
|
419
|
+
var DEFAULT_FALLBACK_CDN = "https://esm.sh";
|
|
420
|
+
function createEsmShResolver(options = {}) {
|
|
421
|
+
const {
|
|
422
|
+
fallbackCdnBase = DEFAULT_FALLBACK_CDN,
|
|
423
|
+
registry = DEFAULT_CDN_REGISTRY,
|
|
424
|
+
providerOrder = ["esm.sh", "cloudflare", "jsdelivr", "unpkg"]
|
|
425
|
+
} = options;
|
|
426
|
+
return {
|
|
427
|
+
resolve(specifier, _context) {
|
|
428
|
+
if (specifier.startsWith("./") || specifier.startsWith("../") || specifier.startsWith("/")) {
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
431
|
+
if (specifier.startsWith("node:") || specifier.startsWith("#")) {
|
|
432
|
+
return null;
|
|
433
|
+
}
|
|
434
|
+
const pkgName = getPackageName(specifier);
|
|
435
|
+
const entry = lookupPackage(pkgName, registry);
|
|
436
|
+
if (entry) {
|
|
437
|
+
for (const provider of providerOrder) {
|
|
438
|
+
const config = entry.providers[provider];
|
|
439
|
+
if (config?.url) {
|
|
440
|
+
const subpath = specifier.slice(pkgName.length);
|
|
441
|
+
if (subpath && config.url.includes("esm.sh/")) {
|
|
442
|
+
return {
|
|
443
|
+
value: config.url + subpath,
|
|
444
|
+
type: "url",
|
|
445
|
+
integrity: config.integrity
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
if (subpath) {
|
|
449
|
+
return {
|
|
450
|
+
value: `${fallbackCdnBase}/${specifier}`,
|
|
451
|
+
type: "url"
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
if (config.global && !config.esm) {
|
|
455
|
+
return {
|
|
456
|
+
value: config.url,
|
|
457
|
+
type: "url",
|
|
458
|
+
integrity: config.integrity
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
return {
|
|
462
|
+
value: config.url,
|
|
463
|
+
type: "url",
|
|
464
|
+
integrity: config.integrity
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
return {
|
|
470
|
+
value: `${fallbackCdnBase}/${specifier}`,
|
|
471
|
+
type: "url"
|
|
472
|
+
};
|
|
143
473
|
}
|
|
144
|
-
}
|
|
145
|
-
return valid;
|
|
146
|
-
}
|
|
147
|
-
function escapeAttribute(str) {
|
|
148
|
-
return str.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">");
|
|
474
|
+
};
|
|
149
475
|
}
|
|
150
476
|
|
|
151
477
|
// libs/uipack/src/utils/index.ts
|
|
@@ -179,46 +505,29 @@ function safeJsonForScript(value) {
|
|
|
179
505
|
}
|
|
180
506
|
}
|
|
181
507
|
|
|
182
|
-
// libs/uipack/src/
|
|
183
|
-
function
|
|
184
|
-
const
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
return `<script>
|
|
193
|
-
${lines.join("\n")}
|
|
194
|
-
</script>`;
|
|
195
|
-
}
|
|
196
|
-
var _uniqueIdCounter = 0;
|
|
197
|
-
function createTemplateHelpers() {
|
|
198
|
-
return {
|
|
199
|
-
escapeHtml: (str) => escapeHtml(str),
|
|
200
|
-
formatDate: (date, format) => {
|
|
201
|
-
const d = date instanceof Date ? date : new Date(date);
|
|
202
|
-
if (isNaN(d.getTime())) return String(date);
|
|
203
|
-
if (format === "iso") return d.toISOString();
|
|
204
|
-
if (format === "date") return d.toLocaleDateString();
|
|
205
|
-
if (format === "time") return d.toLocaleTimeString();
|
|
206
|
-
return d.toLocaleString();
|
|
207
|
-
},
|
|
208
|
-
formatCurrency: (amount, currency = "USD") => {
|
|
209
|
-
return new Intl.NumberFormat("en-US", {
|
|
210
|
-
style: "currency",
|
|
211
|
-
currency
|
|
212
|
-
}).format(amount);
|
|
213
|
-
},
|
|
214
|
-
uniqueId: (prefix = "mcp") => {
|
|
215
|
-
return `${prefix}-${++_uniqueIdCounter}`;
|
|
216
|
-
},
|
|
217
|
-
jsonEmbed: (data) => {
|
|
218
|
-
return escapeScriptClose(JSON.stringify(data));
|
|
508
|
+
// libs/uipack/src/resolver/import-map.ts
|
|
509
|
+
function createImportMapFromResolved(resolved) {
|
|
510
|
+
const imports = {};
|
|
511
|
+
const integrity = {};
|
|
512
|
+
for (const [specifier, res] of Object.entries(resolved)) {
|
|
513
|
+
if (res.type === "url") {
|
|
514
|
+
imports[specifier] = res.value;
|
|
515
|
+
if (res.integrity) {
|
|
516
|
+
integrity[res.value] = res.integrity;
|
|
517
|
+
}
|
|
219
518
|
}
|
|
519
|
+
}
|
|
520
|
+
return {
|
|
521
|
+
imports,
|
|
522
|
+
integrity: Object.keys(integrity).length > 0 ? integrity : void 0
|
|
220
523
|
};
|
|
221
524
|
}
|
|
525
|
+
function generateImportMapScriptTag(map) {
|
|
526
|
+
const json = JSON.stringify(map, null, 2).replace(/<\//g, "<\\/");
|
|
527
|
+
return `<script type="importmap">
|
|
528
|
+
${json}
|
|
529
|
+
</script>`;
|
|
530
|
+
}
|
|
222
531
|
|
|
223
532
|
// libs/uipack/src/bridge-runtime/iife-generator.ts
|
|
224
533
|
function generateBridgeIIFE(options = {}) {
|
|
@@ -274,6 +583,8 @@ function generateBridgeIIFE(options = {}) {
|
|
|
274
583
|
parts.push("});");
|
|
275
584
|
parts.push("");
|
|
276
585
|
parts.push("window.FrontMcpBridge = bridge;");
|
|
586
|
+
parts.push("");
|
|
587
|
+
parts.push(generateAutoResize());
|
|
277
588
|
parts.push("function __showLoading() {");
|
|
278
589
|
parts.push(' var root = document.getElementById("root");');
|
|
279
590
|
parts.push(" if (root && !root.hasChildNodes()) {");
|
|
@@ -294,6 +605,112 @@ function generateBridgeIIFE(options = {}) {
|
|
|
294
605
|
}
|
|
295
606
|
return code;
|
|
296
607
|
}
|
|
608
|
+
function generateAutoResize() {
|
|
609
|
+
return `
|
|
610
|
+
function __applySizingCss(sizing) {
|
|
611
|
+
if (typeof document === 'undefined' || !document.documentElement) return;
|
|
612
|
+
function toLen(v) { return typeof v === 'number' ? v + 'px' : v; }
|
|
613
|
+
var de = document.documentElement;
|
|
614
|
+
var body = document.body;
|
|
615
|
+
var root = document.getElementById('root');
|
|
616
|
+
if (sizing.preferredHeight != null) {
|
|
617
|
+
var ph = toLen(sizing.preferredHeight);
|
|
618
|
+
de.style.height = ph;
|
|
619
|
+
if (body) body.style.height = ph;
|
|
620
|
+
if (root && !root.style.minHeight) root.style.minHeight = ph;
|
|
621
|
+
}
|
|
622
|
+
if (sizing.minHeight != null) {
|
|
623
|
+
var mh = toLen(sizing.minHeight);
|
|
624
|
+
de.style.minHeight = mh;
|
|
625
|
+
if (body) body.style.minHeight = mh;
|
|
626
|
+
if (root) root.style.minHeight = mh;
|
|
627
|
+
}
|
|
628
|
+
if (sizing.maxHeight != null) {
|
|
629
|
+
var mx = toLen(sizing.maxHeight);
|
|
630
|
+
de.style.maxHeight = mx;
|
|
631
|
+
if (body) body.style.maxHeight = mx;
|
|
632
|
+
if (root) root.style.maxHeight = mx;
|
|
633
|
+
}
|
|
634
|
+
if (sizing.aspectRatio != null && root) {
|
|
635
|
+
root.style.aspectRatio = String(sizing.aspectRatio);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
function __initAutoResize() {
|
|
640
|
+
if (typeof window === 'undefined') return;
|
|
641
|
+
// Idempotent: a re-injected IIFE must not stack observers.
|
|
642
|
+
if (window.__mcpAutoResizeInit) return;
|
|
643
|
+
window.__mcpAutoResizeInit = true;
|
|
644
|
+
var sizing = window.__mcpWidgetSizing;
|
|
645
|
+
if (!sizing || typeof sizing !== 'object') return;
|
|
646
|
+
|
|
647
|
+
// Apply CSS as a runtime fallback (the static <style> may be absent on some
|
|
648
|
+
// render paths). Wait for the body if it isn't ready yet.
|
|
649
|
+
function apply() { try { __applySizingCss(sizing); } catch (e) {} }
|
|
650
|
+
if (typeof document !== 'undefined' && document.readyState === 'loading') {
|
|
651
|
+
document.addEventListener('DOMContentLoaded', apply);
|
|
652
|
+
} else {
|
|
653
|
+
apply();
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// Auto-resize defaults ON; opt out with autoResize:false.
|
|
657
|
+
if (sizing.autoResize === false) return;
|
|
658
|
+
if (typeof ResizeObserver === 'undefined') return;
|
|
659
|
+
|
|
660
|
+
function startObserving() {
|
|
661
|
+
var target = document.getElementById('root') || document.body;
|
|
662
|
+
if (!target) return;
|
|
663
|
+
|
|
664
|
+
var rafId = null;
|
|
665
|
+
var lastReported = -1;
|
|
666
|
+
function report() {
|
|
667
|
+
rafId = null;
|
|
668
|
+
try {
|
|
669
|
+
var rect = target.getBoundingClientRect();
|
|
670
|
+
var height = Math.ceil(rect.height);
|
|
671
|
+
var width = Math.ceil(rect.width);
|
|
672
|
+
if (height === lastReported || height <= 0) return;
|
|
673
|
+
lastReported = height;
|
|
674
|
+
var payload = { height: height, width: width };
|
|
675
|
+
if (sizing.aspectRatio != null) payload.aspectRatio = sizing.aspectRatio;
|
|
676
|
+
if (window.FrontMcpBridge && typeof window.FrontMcpBridge.setSize === 'function') {
|
|
677
|
+
window.FrontMcpBridge.setSize(payload).catch(function() {});
|
|
678
|
+
}
|
|
679
|
+
window.dispatchEvent(new CustomEvent('widget:resize', { detail: payload }));
|
|
680
|
+
} catch (e) {}
|
|
681
|
+
}
|
|
682
|
+
function schedule() {
|
|
683
|
+
// Debounce via rAF; fall back to setTimeout if rAF is unavailable.
|
|
684
|
+
if (rafId != null) return;
|
|
685
|
+
if (typeof requestAnimationFrame === 'function') {
|
|
686
|
+
rafId = requestAnimationFrame(report);
|
|
687
|
+
} else {
|
|
688
|
+
rafId = setTimeout(report, 100);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
try {
|
|
693
|
+
// Disconnect any prior observer before creating a new one (no leaks/dupes).
|
|
694
|
+
if (window.__mcpResizeObserver && typeof window.__mcpResizeObserver.disconnect === 'function') {
|
|
695
|
+
window.__mcpResizeObserver.disconnect();
|
|
696
|
+
}
|
|
697
|
+
var ro = new ResizeObserver(function() { schedule(); });
|
|
698
|
+
ro.observe(target);
|
|
699
|
+
window.__mcpResizeObserver = ro;
|
|
700
|
+
} catch (e) {}
|
|
701
|
+
// Report once on init so the host gets an initial measurement.
|
|
702
|
+
schedule();
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
if (typeof document !== 'undefined' && document.readyState === 'loading') {
|
|
706
|
+
document.addEventListener('DOMContentLoaded', startObserving);
|
|
707
|
+
} else {
|
|
708
|
+
startObserving();
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
__initAutoResize();
|
|
712
|
+
`.trim();
|
|
713
|
+
}
|
|
297
714
|
function generateContextDetection() {
|
|
298
715
|
return `
|
|
299
716
|
function detectTheme() {
|
|
@@ -401,6 +818,19 @@ var OpenAIAdapter = {
|
|
|
401
818
|
requestDisplayMode: function(context, mode) {
|
|
402
819
|
return Promise.resolve();
|
|
403
820
|
},
|
|
821
|
+
setSize: function(context, size) {
|
|
822
|
+
// OpenAI Apps SDK measures DOM height itself; if a sizing API surfaces on
|
|
823
|
+
// window.openai, forward to it, otherwise no-op (CSS drives layout).
|
|
824
|
+
try {
|
|
825
|
+
if (window.openai && typeof window.openai.requestDisplayMode === 'function' && size && size.displayMode) {
|
|
826
|
+
window.openai.requestDisplayMode(size.displayMode);
|
|
827
|
+
}
|
|
828
|
+
if (window.openai && typeof window.openai.setWidgetHeight === 'function' && size && typeof size.height === 'number') {
|
|
829
|
+
window.openai.setWidgetHeight(size.height);
|
|
830
|
+
}
|
|
831
|
+
} catch (e) {}
|
|
832
|
+
return Promise.resolve();
|
|
833
|
+
},
|
|
404
834
|
requestClose: function(context) {
|
|
405
835
|
return Promise.resolve();
|
|
406
836
|
}
|
|
@@ -645,6 +1075,15 @@ var ExtAppsAdapter = {
|
|
|
645
1075
|
requestDisplayMode: function(context, mode) {
|
|
646
1076
|
return this.sendRequest('ui/setDisplayMode', { mode: mode });
|
|
647
1077
|
},
|
|
1078
|
+
setSize: function(context, size) {
|
|
1079
|
+
// FrontMCP sizing channel \u2014 parallels ui/setDisplayMode. Reports the
|
|
1080
|
+
// measured/desired widget dimensions to the host.
|
|
1081
|
+
return this.sendRequest('ui/setSize', {
|
|
1082
|
+
height: size && size.height,
|
|
1083
|
+
width: size && size.width,
|
|
1084
|
+
aspectRatio: size && size.aspectRatio
|
|
1085
|
+
});
|
|
1086
|
+
},
|
|
648
1087
|
requestClose: function(context) {
|
|
649
1088
|
return this.sendRequest('ui/close', {});
|
|
650
1089
|
},
|
|
@@ -733,6 +1172,10 @@ var ClaudeAdapter = {
|
|
|
733
1172
|
requestDisplayMode: function() {
|
|
734
1173
|
return Promise.resolve();
|
|
735
1174
|
},
|
|
1175
|
+
setSize: function() {
|
|
1176
|
+
// Claude measures the rendered DOM height itself \u2014 CSS-only, no reporting.
|
|
1177
|
+
return Promise.resolve();
|
|
1178
|
+
},
|
|
736
1179
|
requestClose: function() {
|
|
737
1180
|
return Promise.resolve();
|
|
738
1181
|
}
|
|
@@ -784,6 +1227,9 @@ var GeminiAdapter = {
|
|
|
784
1227
|
requestDisplayMode: function() {
|
|
785
1228
|
return Promise.resolve();
|
|
786
1229
|
},
|
|
1230
|
+
setSize: function() {
|
|
1231
|
+
return Promise.resolve();
|
|
1232
|
+
},
|
|
787
1233
|
requestClose: function() {
|
|
788
1234
|
return Promise.resolve();
|
|
789
1235
|
}
|
|
@@ -819,6 +1265,9 @@ var GenericAdapter = {
|
|
|
819
1265
|
requestDisplayMode: function() {
|
|
820
1266
|
return Promise.resolve();
|
|
821
1267
|
},
|
|
1268
|
+
setSize: function() {
|
|
1269
|
+
return Promise.resolve();
|
|
1270
|
+
},
|
|
822
1271
|
requestClose: function() {
|
|
823
1272
|
return Promise.resolve();
|
|
824
1273
|
}
|
|
@@ -1053,6 +1502,15 @@ FrontMcpBridge.prototype.requestDisplayMode = function(mode) {
|
|
|
1053
1502
|
});
|
|
1054
1503
|
};
|
|
1055
1504
|
|
|
1505
|
+
// Report a desired widget size to the host. \`size\` is { height?, width?, aspectRatio? }.
|
|
1506
|
+
// Per-adapter behaviour: Claude/generic no-op (host measures the DOM),
|
|
1507
|
+
// ext-apps sends ui/setSize, OpenAI forwards to its SDK when available.
|
|
1508
|
+
FrontMcpBridge.prototype.setSize = function(size) {
|
|
1509
|
+
if (!this._adapter) return Promise.reject(new Error('Not initialized'));
|
|
1510
|
+
if (!this._adapter.setSize) return Promise.resolve();
|
|
1511
|
+
return this._adapter.setSize(this._context, size || {});
|
|
1512
|
+
};
|
|
1513
|
+
|
|
1056
1514
|
FrontMcpBridge.prototype.requestClose = function() {
|
|
1057
1515
|
if (!this._adapter) return Promise.reject(new Error('Not initialized'));
|
|
1058
1516
|
return this._adapter.requestClose(this._context);
|
|
@@ -1251,564 +1709,404 @@ var BRIDGE_SCRIPT_TAGS = {
|
|
|
1251
1709
|
gemini: `<script>${generatePlatformBundle("gemini")}</script>`
|
|
1252
1710
|
};
|
|
1253
1711
|
|
|
1254
|
-
// libs/uipack/src/shell/
|
|
1255
|
-
var
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
return {
|
|
1275
|
-
valid: missingRequired.length === 0,
|
|
1276
|
-
found,
|
|
1277
|
-
missingRequired,
|
|
1278
|
-
missingOptional
|
|
1279
|
-
};
|
|
1280
|
-
}
|
|
1281
|
-
|
|
1282
|
-
// libs/uipack/src/shell/custom-shell-applier.ts
|
|
1283
|
-
function applyShellTemplate(template, values) {
|
|
1284
|
-
let result = template;
|
|
1285
|
-
result = result.replaceAll(SHELL_PLACEHOLDERS.CSP, values.csp);
|
|
1286
|
-
result = result.replaceAll(SHELL_PLACEHOLDERS.DATA, values.data);
|
|
1287
|
-
result = result.replaceAll(SHELL_PLACEHOLDERS.BRIDGE, values.bridge);
|
|
1288
|
-
result = result.replaceAll(SHELL_PLACEHOLDERS.CONTENT, values.content);
|
|
1289
|
-
result = result.replaceAll(SHELL_PLACEHOLDERS.TITLE, values.title);
|
|
1290
|
-
return result;
|
|
1291
|
-
}
|
|
1292
|
-
|
|
1293
|
-
// libs/uipack/src/shell/builder.ts
|
|
1294
|
-
function buildShell(content, config) {
|
|
1295
|
-
const { toolName, csp, withShell = true, input, output, structuredContent, includeBridge = true, title } = config;
|
|
1296
|
-
const { customShell } = config;
|
|
1297
|
-
const dataScript = buildDataInjectionScript({ toolName, input, output, structuredContent });
|
|
1298
|
-
if (!withShell) {
|
|
1299
|
-
const html2 = `${dataScript}
|
|
1300
|
-
${content}`;
|
|
1301
|
-
return {
|
|
1302
|
-
html: html2,
|
|
1303
|
-
hash: simpleHash(html2),
|
|
1304
|
-
size: Buffer.byteLength(html2, "utf-8")
|
|
1305
|
-
};
|
|
1306
|
-
}
|
|
1307
|
-
if (customShell) {
|
|
1308
|
-
return buildCustomShell(content, customShell, {
|
|
1309
|
-
csp,
|
|
1310
|
-
toolName,
|
|
1311
|
-
input,
|
|
1312
|
-
output,
|
|
1313
|
-
structuredContent,
|
|
1314
|
-
includeBridge,
|
|
1315
|
-
title
|
|
1316
|
-
});
|
|
1317
|
-
}
|
|
1318
|
-
const headParts = [
|
|
1319
|
-
'<meta charset="UTF-8">',
|
|
1320
|
-
'<meta name="viewport" content="width=device-width, initial-scale=1.0">'
|
|
1321
|
-
];
|
|
1322
|
-
if (title) {
|
|
1323
|
-
headParts.push(`<title>${escapeHtmlForTag(title)}</title>`);
|
|
1324
|
-
}
|
|
1325
|
-
headParts.push(buildCSPMetaTag(csp));
|
|
1326
|
-
headParts.push(dataScript);
|
|
1327
|
-
if (includeBridge) {
|
|
1328
|
-
const bridgeScript = generateBridgeIIFE({ minify: true });
|
|
1329
|
-
headParts.push(`<script>${bridgeScript}</script>`);
|
|
1330
|
-
}
|
|
1331
|
-
const html = `<!DOCTYPE html>
|
|
1332
|
-
<html lang="en">
|
|
1333
|
-
<head>
|
|
1334
|
-
${headParts.map((p) => ` ${p}`).join("\n")}
|
|
1335
|
-
</head>
|
|
1336
|
-
<body>
|
|
1337
|
-
${content}
|
|
1338
|
-
</body>
|
|
1339
|
-
</html>`;
|
|
1340
|
-
return {
|
|
1341
|
-
html,
|
|
1342
|
-
hash: simpleHash(html),
|
|
1343
|
-
size: Buffer.byteLength(html, "utf-8")
|
|
1344
|
-
};
|
|
1345
|
-
}
|
|
1346
|
-
function buildCustomShell(content, customShell, ctx) {
|
|
1347
|
-
let template;
|
|
1348
|
-
if (typeof customShell === "string") {
|
|
1349
|
-
const validation = validateShellTemplate(customShell);
|
|
1350
|
-
if (!validation.valid) {
|
|
1351
|
-
throw new Error(
|
|
1352
|
-
`Custom shell template is missing required placeholder(s): ${validation.missingRequired.map((n) => `{{${n}}}`).join(", ")}`
|
|
1353
|
-
);
|
|
1354
|
-
}
|
|
1355
|
-
template = customShell;
|
|
1356
|
-
} else {
|
|
1357
|
-
template = customShell.template;
|
|
1712
|
+
// libs/uipack/src/shell/csp.ts
|
|
1713
|
+
var DEFAULT_CDN_DOMAINS = [
|
|
1714
|
+
"https://cdn.jsdelivr.net",
|
|
1715
|
+
"https://cdnjs.cloudflare.com",
|
|
1716
|
+
"https://fonts.googleapis.com",
|
|
1717
|
+
"https://fonts.gstatic.com",
|
|
1718
|
+
"https://esm.sh"
|
|
1719
|
+
];
|
|
1720
|
+
var DEFAULT_CSP_DIRECTIVES = [
|
|
1721
|
+
"default-src 'none'",
|
|
1722
|
+
`script-src 'self' 'unsafe-inline' ${DEFAULT_CDN_DOMAINS.join(" ")}`,
|
|
1723
|
+
`style-src 'self' 'unsafe-inline' ${DEFAULT_CDN_DOMAINS.join(" ")}`,
|
|
1724
|
+
`img-src 'self' data: ${DEFAULT_CDN_DOMAINS.join(" ")}`,
|
|
1725
|
+
`font-src 'self' data: ${DEFAULT_CDN_DOMAINS.join(" ")}`,
|
|
1726
|
+
`connect-src ${DEFAULT_CDN_DOMAINS.join(" ")}`,
|
|
1727
|
+
"object-src 'self' data:"
|
|
1728
|
+
];
|
|
1729
|
+
function buildCSPDirectives(csp) {
|
|
1730
|
+
if (!csp) {
|
|
1731
|
+
return [...DEFAULT_CSP_DIRECTIVES];
|
|
1358
1732
|
}
|
|
1359
|
-
const
|
|
1360
|
-
const
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1733
|
+
const validResourceDomains = sanitizeCSPDomains(csp.resourceDomains);
|
|
1734
|
+
const validConnectDomains = sanitizeCSPDomains(csp.connectDomains);
|
|
1735
|
+
const allResourceDomains = [.../* @__PURE__ */ new Set([...DEFAULT_CDN_DOMAINS, ...validResourceDomains])];
|
|
1736
|
+
const directives = [
|
|
1737
|
+
"default-src 'none'",
|
|
1738
|
+
`script-src 'self' 'unsafe-inline' ${allResourceDomains.join(" ")}`,
|
|
1739
|
+
`style-src 'self' 'unsafe-inline' ${allResourceDomains.join(" ")}`
|
|
1740
|
+
];
|
|
1741
|
+
const imgSources = ["'self'", "data:", ...allResourceDomains];
|
|
1742
|
+
directives.push(`img-src ${imgSources.join(" ")}`);
|
|
1743
|
+
const fontSources = ["'self'", "data:", ...allResourceDomains];
|
|
1744
|
+
directives.push(`font-src ${fontSources.join(" ")}`);
|
|
1745
|
+
if (validConnectDomains.length) {
|
|
1746
|
+
directives.push(`connect-src ${validConnectDomains.join(" ")}`);
|
|
1747
|
+
} else {
|
|
1748
|
+
directives.push(`connect-src ${allResourceDomains.join(" ")}`);
|
|
1370
1749
|
}
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
data: dataScript,
|
|
1374
|
-
bridge: bridgeHtml,
|
|
1375
|
-
content,
|
|
1376
|
-
title: ctx.title ? escapeHtmlForTag(ctx.title) : ""
|
|
1377
|
-
});
|
|
1378
|
-
return {
|
|
1379
|
-
html,
|
|
1380
|
-
hash: simpleHash(html),
|
|
1381
|
-
size: Buffer.byteLength(html, "utf-8")
|
|
1382
|
-
};
|
|
1750
|
+
directives.push("object-src 'self' data:");
|
|
1751
|
+
return directives;
|
|
1383
1752
|
}
|
|
1384
|
-
function
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
hash = (hash << 5) - hash + char | 0;
|
|
1389
|
-
}
|
|
1390
|
-
return Math.abs(hash).toString(16).padStart(8, "0");
|
|
1753
|
+
function buildCSPMetaTag(csp) {
|
|
1754
|
+
const directives = buildCSPDirectives(csp);
|
|
1755
|
+
const content = directives.join("; ");
|
|
1756
|
+
return `<meta http-equiv="Content-Security-Policy" content="${escapeAttribute(content)}">`;
|
|
1391
1757
|
}
|
|
1392
|
-
function
|
|
1393
|
-
|
|
1758
|
+
function validateCSPDomain(domain) {
|
|
1759
|
+
if (domain.startsWith("https://*.")) {
|
|
1760
|
+
const rest = domain.slice(10);
|
|
1761
|
+
return /^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?\.[a-zA-Z]{2,}$/.test(rest);
|
|
1762
|
+
}
|
|
1763
|
+
try {
|
|
1764
|
+
const url = new URL(domain);
|
|
1765
|
+
return url.protocol === "https:";
|
|
1766
|
+
} catch {
|
|
1767
|
+
return false;
|
|
1768
|
+
}
|
|
1394
1769
|
}
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
url: "https://cdnjs.cloudflare.com/ajax/libs/react/19.2.4/umd/react.production.min.js",
|
|
1404
|
-
global: "React",
|
|
1405
|
-
crossorigin: "anonymous"
|
|
1406
|
-
},
|
|
1407
|
-
jsdelivr: {
|
|
1408
|
-
url: "https://cdn.jsdelivr.net/npm/react@19.2.4/umd/react.production.min.js",
|
|
1409
|
-
global: "React",
|
|
1410
|
-
crossorigin: "anonymous"
|
|
1411
|
-
},
|
|
1412
|
-
unpkg: {
|
|
1413
|
-
url: "https://unpkg.com/react@19.2.4/umd/react.production.min.js",
|
|
1414
|
-
global: "React",
|
|
1415
|
-
crossorigin: "anonymous"
|
|
1416
|
-
},
|
|
1417
|
-
"esm.sh": {
|
|
1418
|
-
url: "https://esm.sh/react@19.2.4",
|
|
1419
|
-
esm: true,
|
|
1420
|
-
crossorigin: "anonymous"
|
|
1421
|
-
}
|
|
1422
|
-
},
|
|
1423
|
-
preferredProviders: ["esm.sh", "cloudflare", "jsdelivr", "unpkg"],
|
|
1424
|
-
metadata: {
|
|
1425
|
-
description: "A JavaScript library for building user interfaces",
|
|
1426
|
-
homepage: "https://react.dev",
|
|
1427
|
-
license: "MIT"
|
|
1428
|
-
}
|
|
1429
|
-
},
|
|
1430
|
-
"react-dom": {
|
|
1431
|
-
packageName: "react-dom",
|
|
1432
|
-
defaultVersion: "19.2.4",
|
|
1433
|
-
providers: {
|
|
1434
|
-
cloudflare: {
|
|
1435
|
-
url: "https://cdnjs.cloudflare.com/ajax/libs/react-dom/19.2.4/umd/react-dom.production.min.js",
|
|
1436
|
-
global: "ReactDOM",
|
|
1437
|
-
crossorigin: "anonymous",
|
|
1438
|
-
peerDependencies: ["react"]
|
|
1439
|
-
},
|
|
1440
|
-
jsdelivr: {
|
|
1441
|
-
url: "https://cdn.jsdelivr.net/npm/react-dom@19.2.4/umd/react-dom.production.min.js",
|
|
1442
|
-
global: "ReactDOM",
|
|
1443
|
-
crossorigin: "anonymous",
|
|
1444
|
-
peerDependencies: ["react"]
|
|
1445
|
-
},
|
|
1446
|
-
unpkg: {
|
|
1447
|
-
url: "https://unpkg.com/react-dom@19.2.4/umd/react-dom.production.min.js",
|
|
1448
|
-
global: "ReactDOM",
|
|
1449
|
-
crossorigin: "anonymous",
|
|
1450
|
-
peerDependencies: ["react"]
|
|
1451
|
-
},
|
|
1452
|
-
"esm.sh": {
|
|
1453
|
-
url: "https://esm.sh/react-dom@19.2.4",
|
|
1454
|
-
esm: true,
|
|
1455
|
-
crossorigin: "anonymous",
|
|
1456
|
-
peerDependencies: ["react"]
|
|
1457
|
-
}
|
|
1458
|
-
},
|
|
1459
|
-
preferredProviders: ["esm.sh", "cloudflare", "jsdelivr", "unpkg"],
|
|
1460
|
-
metadata: {
|
|
1461
|
-
description: "React package for working with the DOM",
|
|
1462
|
-
homepage: "https://react.dev",
|
|
1463
|
-
license: "MIT"
|
|
1464
|
-
}
|
|
1465
|
-
},
|
|
1466
|
-
"chart.js": {
|
|
1467
|
-
packageName: "chart.js",
|
|
1468
|
-
defaultVersion: "4.5.1",
|
|
1469
|
-
providers: {
|
|
1470
|
-
cloudflare: {
|
|
1471
|
-
url: "https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.5.1/chart.umd.min.js",
|
|
1472
|
-
global: "Chart",
|
|
1473
|
-
crossorigin: "anonymous"
|
|
1474
|
-
},
|
|
1475
|
-
jsdelivr: {
|
|
1476
|
-
url: "https://cdn.jsdelivr.net/npm/chart.js@4.5.1/dist/chart.umd.min.js",
|
|
1477
|
-
global: "Chart",
|
|
1478
|
-
crossorigin: "anonymous"
|
|
1479
|
-
},
|
|
1480
|
-
unpkg: {
|
|
1481
|
-
url: "https://unpkg.com/chart.js@4.5.1/dist/chart.umd.min.js",
|
|
1482
|
-
global: "Chart",
|
|
1483
|
-
crossorigin: "anonymous"
|
|
1484
|
-
}
|
|
1485
|
-
},
|
|
1486
|
-
preferredProviders: ["esm.sh", "cloudflare", "jsdelivr", "unpkg"],
|
|
1487
|
-
metadata: {
|
|
1488
|
-
description: "Simple yet flexible JavaScript charting library",
|
|
1489
|
-
homepage: "https://www.chartjs.org",
|
|
1490
|
-
license: "MIT"
|
|
1491
|
-
}
|
|
1492
|
-
},
|
|
1493
|
-
d3: {
|
|
1494
|
-
packageName: "d3",
|
|
1495
|
-
defaultVersion: "7.9.0",
|
|
1496
|
-
providers: {
|
|
1497
|
-
cloudflare: {
|
|
1498
|
-
url: "https://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.min.js",
|
|
1499
|
-
integrity: "sha512-vc58qvvBdrDR4etbxMdlTt4GBQk1qjvyORR2nrsPsFPyrs+/u5c3+1Ct6upOgdZoIl7eq6k3a1UPDSNAQi/32A==",
|
|
1500
|
-
global: "d3",
|
|
1501
|
-
crossorigin: "anonymous"
|
|
1502
|
-
},
|
|
1503
|
-
jsdelivr: {
|
|
1504
|
-
url: "https://cdn.jsdelivr.net/npm/d3@7.9.0/dist/d3.min.js",
|
|
1505
|
-
global: "d3",
|
|
1506
|
-
crossorigin: "anonymous"
|
|
1507
|
-
},
|
|
1508
|
-
unpkg: {
|
|
1509
|
-
url: "https://unpkg.com/d3@7.9.0/dist/d3.min.js",
|
|
1510
|
-
global: "d3",
|
|
1511
|
-
crossorigin: "anonymous"
|
|
1512
|
-
}
|
|
1513
|
-
},
|
|
1514
|
-
preferredProviders: ["esm.sh", "cloudflare", "jsdelivr", "unpkg"],
|
|
1515
|
-
metadata: {
|
|
1516
|
-
description: "Data-Driven Documents",
|
|
1517
|
-
homepage: "https://d3js.org",
|
|
1518
|
-
license: "ISC"
|
|
1519
|
-
}
|
|
1520
|
-
},
|
|
1521
|
-
lodash: {
|
|
1522
|
-
packageName: "lodash",
|
|
1523
|
-
defaultVersion: "4.17.21",
|
|
1524
|
-
providers: {
|
|
1525
|
-
cloudflare: {
|
|
1526
|
-
url: "https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js",
|
|
1527
|
-
integrity: "sha512-WFN04846sdKMIP5LKNphMaWzU7YpMyCU245etK3g/2ARYbPK9Ub18eG+ljU96qKRCWh+quCY7yefSmlkQw1ANQ==",
|
|
1528
|
-
global: "_",
|
|
1529
|
-
crossorigin: "anonymous"
|
|
1530
|
-
},
|
|
1531
|
-
jsdelivr: {
|
|
1532
|
-
url: "https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js",
|
|
1533
|
-
global: "_",
|
|
1534
|
-
crossorigin: "anonymous"
|
|
1535
|
-
},
|
|
1536
|
-
unpkg: {
|
|
1537
|
-
url: "https://unpkg.com/lodash@4.17.21/lodash.min.js",
|
|
1538
|
-
global: "_",
|
|
1539
|
-
crossorigin: "anonymous"
|
|
1540
|
-
}
|
|
1541
|
-
},
|
|
1542
|
-
preferredProviders: ["esm.sh", "cloudflare", "jsdelivr", "unpkg"],
|
|
1543
|
-
metadata: {
|
|
1544
|
-
description: "A modern JavaScript utility library",
|
|
1545
|
-
homepage: "https://lodash.com",
|
|
1546
|
-
license: "MIT"
|
|
1770
|
+
function sanitizeCSPDomains(domains) {
|
|
1771
|
+
if (!domains) return [];
|
|
1772
|
+
const valid = [];
|
|
1773
|
+
for (const domain of domains) {
|
|
1774
|
+
if (validateCSPDomain(domain)) {
|
|
1775
|
+
valid.push(domain);
|
|
1776
|
+
} else {
|
|
1777
|
+
console.warn(`Invalid CSP domain ignored: ${domain}`);
|
|
1547
1778
|
}
|
|
1548
1779
|
}
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1780
|
+
return valid;
|
|
1781
|
+
}
|
|
1782
|
+
function escapeAttribute(str) {
|
|
1783
|
+
return str.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">");
|
|
1552
1784
|
}
|
|
1553
1785
|
|
|
1554
|
-
// libs/uipack/src/
|
|
1555
|
-
var
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
defaultAndNamespace: /import\s+(\w+)\s*,\s*\*\s*as\s+(\w+)\s+from\s*['"]([^'"]+)['"]/g,
|
|
1563
|
-
reExport: /export\s*\{[^}]+\}\s*from\s*['"]([^'"]+)['"]/g,
|
|
1564
|
-
reExportAll: /export\s*\*\s*from\s*['"]([^'"]+)['"]/g
|
|
1786
|
+
// libs/uipack/src/shell/custom-shell-types.ts
|
|
1787
|
+
var SHELL_PLACEHOLDER_NAMES = ["CSP", "DATA", "BRIDGE", "CONTENT", "TITLE"];
|
|
1788
|
+
var SHELL_PLACEHOLDERS = {
|
|
1789
|
+
CSP: "{{CSP}}",
|
|
1790
|
+
DATA: "{{DATA}}",
|
|
1791
|
+
BRIDGE: "{{BRIDGE}}",
|
|
1792
|
+
CONTENT: "{{CONTENT}}",
|
|
1793
|
+
TITLE: "{{TITLE}}"
|
|
1565
1794
|
};
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1795
|
+
var REQUIRED_PLACEHOLDERS = ["CONTENT"];
|
|
1796
|
+
var OPTIONAL_PLACEHOLDERS = ["CSP", "DATA", "BRIDGE", "TITLE"];
|
|
1797
|
+
|
|
1798
|
+
// libs/uipack/src/shell/custom-shell-applier.ts
|
|
1799
|
+
function applyShellTemplate(template, values) {
|
|
1800
|
+
let result = template;
|
|
1801
|
+
result = result.replaceAll(SHELL_PLACEHOLDERS.CSP, values.csp);
|
|
1802
|
+
result = result.replaceAll(SHELL_PLACEHOLDERS.DATA, values.data);
|
|
1803
|
+
result = result.replaceAll(SHELL_PLACEHOLDERS.BRIDGE, values.bridge);
|
|
1804
|
+
result = result.replaceAll(SHELL_PLACEHOLDERS.CONTENT, values.content);
|
|
1805
|
+
result = result.replaceAll(SHELL_PLACEHOLDERS.TITLE, values.title);
|
|
1806
|
+
return result;
|
|
1571
1807
|
}
|
|
1572
|
-
|
|
1573
|
-
|
|
1808
|
+
|
|
1809
|
+
// libs/uipack/src/shell/custom-shell-validator.ts
|
|
1810
|
+
function validateShellTemplate(template) {
|
|
1811
|
+
const found = {};
|
|
1812
|
+
for (const name of SHELL_PLACEHOLDER_NAMES) {
|
|
1813
|
+
found[name] = template.includes(SHELL_PLACEHOLDERS[name]);
|
|
1814
|
+
}
|
|
1815
|
+
const missingRequired = REQUIRED_PLACEHOLDERS.filter((name) => !found[name]);
|
|
1816
|
+
const missingOptional = OPTIONAL_PLACEHOLDERS.filter((name) => !found[name]);
|
|
1817
|
+
return {
|
|
1818
|
+
valid: missingRequired.length === 0,
|
|
1819
|
+
found,
|
|
1820
|
+
missingRequired,
|
|
1821
|
+
missingOptional
|
|
1822
|
+
};
|
|
1574
1823
|
}
|
|
1575
|
-
|
|
1576
|
-
|
|
1824
|
+
|
|
1825
|
+
// libs/uipack/src/shell/data-injector.ts
|
|
1826
|
+
function hasSizing(sizing) {
|
|
1827
|
+
if (!sizing) return false;
|
|
1828
|
+
return sizing.preferredHeight !== void 0 || sizing.minHeight !== void 0 || sizing.maxHeight !== void 0 || sizing.aspectRatio !== void 0 || sizing.autoResize !== void 0;
|
|
1577
1829
|
}
|
|
1578
|
-
function
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
}
|
|
1584
|
-
|
|
1830
|
+
function buildDataInjectionScript(options) {
|
|
1831
|
+
const { toolName, input, output, structuredContent, sizing } = options;
|
|
1832
|
+
const lines = [
|
|
1833
|
+
`window.__mcpAppsEnabled = true;`,
|
|
1834
|
+
`window.__mcpToolName = ${safeJsonForScript(toolName)};`,
|
|
1835
|
+
`window.__mcpToolInput = ${safeJsonForScript(input ?? null)};`,
|
|
1836
|
+
`window.__mcpToolOutput = ${safeJsonForScript(output ?? null)};`,
|
|
1837
|
+
`window.__mcpStructuredContent = ${safeJsonForScript(structuredContent ?? null)};`
|
|
1838
|
+
];
|
|
1839
|
+
if (hasSizing(sizing)) {
|
|
1840
|
+
lines.push(`window.__mcpWidgetSizing = ${safeJsonForScript(sizing)};`);
|
|
1585
1841
|
}
|
|
1586
|
-
|
|
1587
|
-
|
|
1842
|
+
return `<script>
|
|
1843
|
+
${lines.join("\n")}
|
|
1844
|
+
</script>`;
|
|
1588
1845
|
}
|
|
1589
|
-
function
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
if (source[i] === "\n") {
|
|
1593
|
-
line++;
|
|
1594
|
-
}
|
|
1846
|
+
function buildCustomDataInjectionScript(descriptor) {
|
|
1847
|
+
if (descriptor.script !== void 0) {
|
|
1848
|
+
return descriptor.script;
|
|
1595
1849
|
}
|
|
1596
|
-
|
|
1597
|
-
}
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
for (let i = index - 1; i >= 0 && source[i] !== "\n"; i--) {
|
|
1601
|
-
column++;
|
|
1850
|
+
if (descriptor.globalKey !== void 0) {
|
|
1851
|
+
return `<script>window[${safeJsonForScript(descriptor.globalKey)}] = ${safeJsonForScript(
|
|
1852
|
+
descriptor.value ?? null
|
|
1853
|
+
)};</script>`;
|
|
1602
1854
|
}
|
|
1603
|
-
return
|
|
1855
|
+
return "";
|
|
1604
1856
|
}
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1857
|
+
var _uniqueIdCounter = 0;
|
|
1858
|
+
function createTemplateHelpers() {
|
|
1859
|
+
return {
|
|
1860
|
+
escapeHtml: (str) => escapeHtml(str),
|
|
1861
|
+
formatDate: (date, format) => {
|
|
1862
|
+
const d = date instanceof Date ? date : new Date(date);
|
|
1863
|
+
if (isNaN(d.getTime())) return String(date);
|
|
1864
|
+
if (format === "iso") return d.toISOString();
|
|
1865
|
+
if (format === "date") return d.toLocaleDateString();
|
|
1866
|
+
if (format === "time") return d.toLocaleTimeString();
|
|
1867
|
+
return d.toLocaleString();
|
|
1868
|
+
},
|
|
1869
|
+
formatCurrency: (amount, currency = "USD") => {
|
|
1870
|
+
return new Intl.NumberFormat("en-US", {
|
|
1871
|
+
style: "currency",
|
|
1872
|
+
currency
|
|
1873
|
+
}).format(amount);
|
|
1874
|
+
},
|
|
1875
|
+
uniqueId: (prefix = "mcp") => {
|
|
1876
|
+
return `${prefix}-${++_uniqueIdCounter}`;
|
|
1877
|
+
},
|
|
1878
|
+
jsonEmbed: (data) => {
|
|
1879
|
+
return escapeScriptClose(JSON.stringify(data));
|
|
1613
1880
|
}
|
|
1614
1881
|
};
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
// libs/uipack/src/shell/sizing-css.ts
|
|
1885
|
+
function sanitizeCssValue(value) {
|
|
1886
|
+
return value.replace(/[<>{};]/g, "").trim();
|
|
1887
|
+
}
|
|
1888
|
+
function toCssLength(value) {
|
|
1889
|
+
return typeof value === "number" ? `${value}px` : sanitizeCssValue(value);
|
|
1890
|
+
}
|
|
1891
|
+
function buildSizingStyleTag(sizing) {
|
|
1892
|
+
if (!hasSizing(sizing)) return "";
|
|
1893
|
+
const hasCssSizing = sizing.preferredHeight !== void 0 || sizing.minHeight !== void 0 || sizing.maxHeight !== void 0 || sizing.aspectRatio !== void 0;
|
|
1894
|
+
if (!hasCssSizing) return "";
|
|
1895
|
+
const rootRules = [];
|
|
1896
|
+
const docRules = ["margin: 0;"];
|
|
1897
|
+
if (sizing.preferredHeight !== void 0) {
|
|
1898
|
+
const h = toCssLength(sizing.preferredHeight);
|
|
1899
|
+
docRules.push(`height: ${h};`);
|
|
1900
|
+
rootRules.push(`min-height: ${h};`);
|
|
1628
1901
|
}
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
statement,
|
|
1634
|
-
specifier,
|
|
1635
|
-
type: "default",
|
|
1636
|
-
defaultImport: defaultName,
|
|
1637
|
-
namespaceImport: namespaceName,
|
|
1638
|
-
line: getLineNumber(source, match.index),
|
|
1639
|
-
column: getColumnNumber(source, match.index)
|
|
1640
|
-
});
|
|
1902
|
+
if (sizing.minHeight !== void 0) {
|
|
1903
|
+
const mh = toCssLength(sizing.minHeight);
|
|
1904
|
+
docRules.push(`min-height: ${mh};`);
|
|
1905
|
+
rootRules.push(`min-height: ${mh};`);
|
|
1641
1906
|
}
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
statement,
|
|
1647
|
-
specifier,
|
|
1648
|
-
type: "named",
|
|
1649
|
-
namedImports: parseNamedImports(namedString),
|
|
1650
|
-
line: getLineNumber(source, match.index),
|
|
1651
|
-
column: getColumnNumber(source, match.index)
|
|
1652
|
-
});
|
|
1907
|
+
if (sizing.maxHeight !== void 0) {
|
|
1908
|
+
const mx = toCssLength(sizing.maxHeight);
|
|
1909
|
+
docRules.push(`max-height: ${mx};`);
|
|
1910
|
+
rootRules.push(`max-height: ${mx};`);
|
|
1653
1911
|
}
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
const afterMatch = source.slice(match.index + match[0].length - specifier.length - 2);
|
|
1658
|
-
if (afterMatch.startsWith(",")) continue;
|
|
1659
|
-
addImport({
|
|
1660
|
-
statement,
|
|
1661
|
-
specifier,
|
|
1662
|
-
type: "default",
|
|
1663
|
-
defaultImport: defaultName,
|
|
1664
|
-
line: getLineNumber(source, match.index),
|
|
1665
|
-
column: getColumnNumber(source, match.index)
|
|
1666
|
-
});
|
|
1912
|
+
if (sizing.aspectRatio !== void 0) {
|
|
1913
|
+
const ar = typeof sizing.aspectRatio === "number" ? String(sizing.aspectRatio) : sanitizeCssValue(sizing.aspectRatio);
|
|
1914
|
+
if (ar) rootRules.push(`aspect-ratio: ${ar};`);
|
|
1667
1915
|
}
|
|
1668
|
-
const
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
addImport({
|
|
1672
|
-
statement,
|
|
1673
|
-
specifier,
|
|
1674
|
-
type: "namespace",
|
|
1675
|
-
namespaceImport: namespaceName,
|
|
1676
|
-
line: getLineNumber(source, match.index),
|
|
1677
|
-
column: getColumnNumber(source, match.index)
|
|
1678
|
-
});
|
|
1916
|
+
const parts = [`html, body { ${docRules.join(" ")} }`];
|
|
1917
|
+
if (rootRules.length > 0) {
|
|
1918
|
+
parts.push(`#root { ${rootRules.join(" ")} }`);
|
|
1679
1919
|
}
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1920
|
+
return `<style>${parts.join("\n")}</style>`;
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1923
|
+
// libs/uipack/src/shell/builder.ts
|
|
1924
|
+
function resolveDataInjectionScript(args) {
|
|
1925
|
+
if (args.dataInjection) {
|
|
1926
|
+
return buildCustomDataInjectionScript(args.dataInjection);
|
|
1927
|
+
}
|
|
1928
|
+
return buildDataInjectionScript({
|
|
1929
|
+
toolName: args.toolName,
|
|
1930
|
+
input: args.input,
|
|
1931
|
+
output: args.output,
|
|
1932
|
+
structuredContent: args.structuredContent,
|
|
1933
|
+
sizing: args.sizing
|
|
1934
|
+
});
|
|
1935
|
+
}
|
|
1936
|
+
function buildShell(content, config) {
|
|
1937
|
+
const {
|
|
1938
|
+
toolName,
|
|
1939
|
+
csp,
|
|
1940
|
+
withShell = true,
|
|
1941
|
+
input,
|
|
1942
|
+
output,
|
|
1943
|
+
structuredContent,
|
|
1944
|
+
includeBridge = true,
|
|
1945
|
+
title,
|
|
1946
|
+
sizing
|
|
1947
|
+
} = config;
|
|
1948
|
+
const { customShell, dataInjection } = config;
|
|
1949
|
+
const dataScript = resolveDataInjectionScript({
|
|
1950
|
+
dataInjection,
|
|
1951
|
+
toolName,
|
|
1952
|
+
input,
|
|
1953
|
+
output,
|
|
1954
|
+
structuredContent,
|
|
1955
|
+
sizing
|
|
1956
|
+
});
|
|
1957
|
+
if (!withShell) {
|
|
1958
|
+
const html2 = `${dataScript}
|
|
1959
|
+
${content}`;
|
|
1960
|
+
return {
|
|
1961
|
+
html: html2,
|
|
1962
|
+
hash: simpleHash(html2),
|
|
1963
|
+
size: Buffer.byteLength(html2, "utf-8")
|
|
1964
|
+
};
|
|
1692
1965
|
}
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1966
|
+
if (customShell) {
|
|
1967
|
+
return buildCustomShell(content, customShell, {
|
|
1968
|
+
csp,
|
|
1969
|
+
toolName,
|
|
1970
|
+
input,
|
|
1971
|
+
output,
|
|
1972
|
+
structuredContent,
|
|
1973
|
+
includeBridge,
|
|
1974
|
+
title,
|
|
1975
|
+
sizing,
|
|
1976
|
+
dataInjection
|
|
1702
1977
|
});
|
|
1703
1978
|
}
|
|
1704
|
-
const
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
type: "named",
|
|
1711
|
-
line: getLineNumber(source, match.index),
|
|
1712
|
-
column: getColumnNumber(source, match.index)
|
|
1713
|
-
});
|
|
1979
|
+
const headParts = [
|
|
1980
|
+
'<meta charset="UTF-8">',
|
|
1981
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1.0">'
|
|
1982
|
+
];
|
|
1983
|
+
if (title) {
|
|
1984
|
+
headParts.push(`<title>${escapeHtmlForTag(title)}</title>`);
|
|
1714
1985
|
}
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
specifier,
|
|
1721
|
-
type: "namespace",
|
|
1722
|
-
line: getLineNumber(source, match.index),
|
|
1723
|
-
column: getColumnNumber(source, match.index)
|
|
1724
|
-
});
|
|
1986
|
+
headParts.push(buildCSPMetaTag(csp));
|
|
1987
|
+
headParts.push(dataScript);
|
|
1988
|
+
const sizingStyle = buildSizingStyleTag(sizing);
|
|
1989
|
+
if (sizingStyle) {
|
|
1990
|
+
headParts.push(sizingStyle);
|
|
1725
1991
|
}
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1992
|
+
if (includeBridge) {
|
|
1993
|
+
const bridgeScript = generateBridgeIIFE({ minify: true });
|
|
1994
|
+
headParts.push(`<script>${bridgeScript}</script>`);
|
|
1995
|
+
}
|
|
1996
|
+
const html = `<!DOCTYPE html>
|
|
1997
|
+
<html lang="en">
|
|
1998
|
+
<head>
|
|
1999
|
+
${headParts.map((p) => ` ${p}`).join("\n")}
|
|
2000
|
+
</head>
|
|
2001
|
+
<body>
|
|
2002
|
+
${content}
|
|
2003
|
+
</body>
|
|
2004
|
+
</html>`;
|
|
1729
2005
|
return {
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
externalPackages
|
|
2006
|
+
html,
|
|
2007
|
+
hash: simpleHash(html),
|
|
2008
|
+
size: Buffer.byteLength(html, "utf-8")
|
|
1734
2009
|
};
|
|
1735
2010
|
}
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
} = options;
|
|
1745
|
-
return {
|
|
1746
|
-
resolve(specifier, _context) {
|
|
1747
|
-
if (specifier.startsWith("./") || specifier.startsWith("../") || specifier.startsWith("/")) {
|
|
1748
|
-
return null;
|
|
1749
|
-
}
|
|
1750
|
-
if (specifier.startsWith("node:") || specifier.startsWith("#")) {
|
|
1751
|
-
return null;
|
|
1752
|
-
}
|
|
1753
|
-
const pkgName = getPackageName(specifier);
|
|
1754
|
-
const entry = lookupPackage(pkgName, registry);
|
|
1755
|
-
if (entry) {
|
|
1756
|
-
for (const provider of providerOrder) {
|
|
1757
|
-
const config = entry.providers[provider];
|
|
1758
|
-
if (config?.url) {
|
|
1759
|
-
const subpath = specifier.slice(pkgName.length);
|
|
1760
|
-
if (subpath && config.url.includes("esm.sh/")) {
|
|
1761
|
-
return {
|
|
1762
|
-
value: config.url + subpath,
|
|
1763
|
-
type: "url",
|
|
1764
|
-
integrity: config.integrity
|
|
1765
|
-
};
|
|
1766
|
-
}
|
|
1767
|
-
if (subpath) {
|
|
1768
|
-
return {
|
|
1769
|
-
value: `${fallbackCdnBase}/${specifier}`,
|
|
1770
|
-
type: "url"
|
|
1771
|
-
};
|
|
1772
|
-
}
|
|
1773
|
-
if (config.global && !config.esm) {
|
|
1774
|
-
return {
|
|
1775
|
-
value: config.url,
|
|
1776
|
-
type: "url",
|
|
1777
|
-
integrity: config.integrity
|
|
1778
|
-
};
|
|
1779
|
-
}
|
|
1780
|
-
return {
|
|
1781
|
-
value: config.url,
|
|
1782
|
-
type: "url",
|
|
1783
|
-
integrity: config.integrity
|
|
1784
|
-
};
|
|
1785
|
-
}
|
|
1786
|
-
}
|
|
1787
|
-
}
|
|
1788
|
-
return {
|
|
1789
|
-
value: `${fallbackCdnBase}/${specifier}`,
|
|
1790
|
-
type: "url"
|
|
1791
|
-
};
|
|
2011
|
+
function buildCustomShell(content, customShell, ctx) {
|
|
2012
|
+
let template;
|
|
2013
|
+
if (typeof customShell === "string") {
|
|
2014
|
+
const validation = validateShellTemplate(customShell);
|
|
2015
|
+
if (!validation.valid) {
|
|
2016
|
+
throw new Error(
|
|
2017
|
+
`Custom shell template is missing required placeholder(s): ${validation.missingRequired.map((n) => `{{${n}}}`).join(", ")}`
|
|
2018
|
+
);
|
|
1792
2019
|
}
|
|
2020
|
+
template = customShell;
|
|
2021
|
+
} else {
|
|
2022
|
+
template = customShell.template;
|
|
2023
|
+
}
|
|
2024
|
+
const cspTag = buildCSPMetaTag(ctx.csp);
|
|
2025
|
+
const dataScript = resolveDataInjectionScript({
|
|
2026
|
+
dataInjection: ctx.dataInjection,
|
|
2027
|
+
toolName: ctx.toolName,
|
|
2028
|
+
input: ctx.input,
|
|
2029
|
+
output: ctx.output,
|
|
2030
|
+
structuredContent: ctx.structuredContent,
|
|
2031
|
+
sizing: ctx.sizing
|
|
2032
|
+
});
|
|
2033
|
+
const sizingStyle = buildSizingStyleTag(ctx.sizing);
|
|
2034
|
+
const dataWithSizing = sizingStyle ? `${dataScript}
|
|
2035
|
+
${sizingStyle}` : dataScript;
|
|
2036
|
+
let bridgeHtml = "";
|
|
2037
|
+
if (ctx.includeBridge) {
|
|
2038
|
+
const bridgeScript = generateBridgeIIFE({ minify: true });
|
|
2039
|
+
bridgeHtml = `<script>${bridgeScript}</script>`;
|
|
2040
|
+
}
|
|
2041
|
+
const html = applyShellTemplate(template, {
|
|
2042
|
+
csp: cspTag,
|
|
2043
|
+
data: dataWithSizing,
|
|
2044
|
+
bridge: bridgeHtml,
|
|
2045
|
+
content,
|
|
2046
|
+
title: ctx.title ? escapeHtmlForTag(ctx.title) : ""
|
|
2047
|
+
});
|
|
2048
|
+
return {
|
|
2049
|
+
html,
|
|
2050
|
+
hash: simpleHash(html),
|
|
2051
|
+
size: Buffer.byteLength(html, "utf-8")
|
|
1793
2052
|
};
|
|
1794
2053
|
}
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
return
|
|
2054
|
+
function simpleHash(str) {
|
|
2055
|
+
let hash = 0;
|
|
2056
|
+
for (let i = 0; i < str.length; i++) {
|
|
2057
|
+
const char = str.charCodeAt(i);
|
|
2058
|
+
hash = (hash << 5) - hash + char | 0;
|
|
2059
|
+
}
|
|
2060
|
+
return Math.abs(hash).toString(16).padStart(8, "0");
|
|
1802
2061
|
}
|
|
1803
|
-
function
|
|
1804
|
-
return
|
|
2062
|
+
function escapeHtmlForTag(str) {
|
|
2063
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
1805
2064
|
}
|
|
1806
|
-
|
|
1807
|
-
|
|
2065
|
+
|
|
2066
|
+
// libs/uipack/src/component/ui-availability.ts
|
|
2067
|
+
function isFrontmcpUiResolvable(...candidatePaths) {
|
|
2068
|
+
try {
|
|
2069
|
+
const nodeFs = __require("fs");
|
|
2070
|
+
const nodePath = __require("path");
|
|
2071
|
+
const ORG = "@frontmcp";
|
|
2072
|
+
const PKG = "ui";
|
|
2073
|
+
for (const candidate of candidatePaths) {
|
|
2074
|
+
let dir = candidate;
|
|
2075
|
+
while (true) {
|
|
2076
|
+
const pkgJson = nodePath.join(dir, "node_modules", ORG, PKG, "package.json");
|
|
2077
|
+
if (nodeFs.existsSync(pkgJson)) return true;
|
|
2078
|
+
const parent = nodePath.dirname(dir);
|
|
2079
|
+
if (parent === dir) break;
|
|
2080
|
+
dir = parent;
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
2083
|
+
return false;
|
|
2084
|
+
} catch {
|
|
2085
|
+
return false;
|
|
2086
|
+
}
|
|
1808
2087
|
}
|
|
1809
2088
|
|
|
1810
2089
|
// libs/uipack/src/component/transpiler.ts
|
|
1811
|
-
function
|
|
2090
|
+
function transpileReactSource(source, filename) {
|
|
2091
|
+
const esbuild = __require("esbuild");
|
|
2092
|
+
const loader = filename?.endsWith(".tsx") ? "tsx" : "jsx";
|
|
2093
|
+
const result = esbuild.transformSync(source, {
|
|
2094
|
+
loader,
|
|
2095
|
+
jsx: "transform",
|
|
2096
|
+
jsxFactory: "React.createElement",
|
|
2097
|
+
jsxFragment: "React.Fragment",
|
|
2098
|
+
format: "esm",
|
|
2099
|
+
target: "es2020",
|
|
2100
|
+
define: { "process.env.NODE_ENV": '"production"' }
|
|
2101
|
+
});
|
|
2102
|
+
return result.code;
|
|
2103
|
+
}
|
|
2104
|
+
function bundleFileSource(source, filename, resolveDir, componentName, options = {}) {
|
|
2105
|
+
if (!isFrontmcpUiResolvable(resolveDir, process.cwd())) {
|
|
2106
|
+
throw new Error(
|
|
2107
|
+
`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.`
|
|
2108
|
+
);
|
|
2109
|
+
}
|
|
1812
2110
|
const esbuild = __require("esbuild");
|
|
1813
2111
|
const mountCode = `
|
|
1814
2112
|
// --- Auto-generated mount ---
|
|
@@ -1890,7 +2188,11 @@ if (__root) {
|
|
|
1890
2188
|
format: "esm",
|
|
1891
2189
|
target: "es2020",
|
|
1892
2190
|
jsx: "automatic",
|
|
1893
|
-
|
|
2191
|
+
// When `bundleReact` is set (resourceMode: 'inline'), React itself is
|
|
2192
|
+
// bundled — required for hosts that block external script execution
|
|
2193
|
+
// such as Claude (#454). Otherwise React stays external and is loaded
|
|
2194
|
+
// via the import map emitted by the renderer.
|
|
2195
|
+
external: options.bundleReact ? [] : ["react", "react-dom", "react/jsx-runtime", "react/jsx-dev-runtime"],
|
|
1894
2196
|
alias,
|
|
1895
2197
|
define: { "process.env.NODE_ENV": '"production"' },
|
|
1896
2198
|
platform: "browser",
|
|
@@ -1902,7 +2204,7 @@ if (__root) {
|
|
|
1902
2204
|
} catch (err) {
|
|
1903
2205
|
const message = err instanceof Error ? err.message : String(err);
|
|
1904
2206
|
throw new Error(
|
|
1905
|
-
`Failed to bundle FileSource "${filename}": ${message}.
|
|
2207
|
+
`Failed to bundle FileSource "${filename}": ${message}. If the error mentions @frontmcp/ui or @frontmcp/uipack, ensure both packages are installed in the consuming project.`
|
|
1906
2208
|
);
|
|
1907
2209
|
}
|
|
1908
2210
|
}
|
|
@@ -1914,12 +2216,29 @@ function extractDefaultExportName(code) {
|
|
|
1914
2216
|
return null;
|
|
1915
2217
|
}
|
|
1916
2218
|
|
|
2219
|
+
// libs/uipack/src/component/types.ts
|
|
2220
|
+
function isNpmSource(source) {
|
|
2221
|
+
return typeof source === "object" && source !== null && "npm" in source;
|
|
2222
|
+
}
|
|
2223
|
+
function isFileSource(source) {
|
|
2224
|
+
return typeof source === "object" && source !== null && "file" in source;
|
|
2225
|
+
}
|
|
2226
|
+
function isImportSource(source) {
|
|
2227
|
+
return typeof source === "object" && source !== null && "import" in source;
|
|
2228
|
+
}
|
|
2229
|
+
function isFunctionSource(source) {
|
|
2230
|
+
return typeof source === "function";
|
|
2231
|
+
}
|
|
2232
|
+
|
|
1917
2233
|
// libs/uipack/src/component/loader.ts
|
|
1918
2234
|
var DEFAULT_META = {
|
|
1919
2235
|
mcpAware: false,
|
|
1920
2236
|
renderer: "auto"
|
|
1921
2237
|
};
|
|
1922
2238
|
function resolveUISource(source, options) {
|
|
2239
|
+
if (options?.inlineReact && options?.transformOnly) {
|
|
2240
|
+
throw new Error("resolveUISource: `inlineReact` and `transformOnly` are mutually exclusive \u2014 set at most one.");
|
|
2241
|
+
}
|
|
1923
2242
|
const resolver = options?.resolver ?? createEsmShResolver();
|
|
1924
2243
|
if (isNpmSource(source)) {
|
|
1925
2244
|
return resolveNpmSource(source, resolver);
|
|
@@ -1928,7 +2247,7 @@ function resolveUISource(source, options) {
|
|
|
1928
2247
|
return resolveImportSource(source);
|
|
1929
2248
|
}
|
|
1930
2249
|
if (isFileSource(source)) {
|
|
1931
|
-
return resolveFileSource(source);
|
|
2250
|
+
return resolveFileSource(source, { inlineReact: options?.inlineReact, transformOnly: options?.transformOnly });
|
|
1932
2251
|
}
|
|
1933
2252
|
if (isFunctionSource(source)) {
|
|
1934
2253
|
return resolveFunctionSource(source, options?.input, options?.output);
|
|
@@ -1961,15 +2280,42 @@ function resolveImportSource(source) {
|
|
|
1961
2280
|
peerDependencies: []
|
|
1962
2281
|
};
|
|
1963
2282
|
}
|
|
1964
|
-
function resolveFileSource(source) {
|
|
2283
|
+
function resolveFileSource(source, options = {}) {
|
|
1965
2284
|
const path = __require("path");
|
|
1966
2285
|
const ext = path.extname(source.file).toLowerCase();
|
|
1967
2286
|
if (ext === ".tsx" || ext === ".jsx") {
|
|
1968
2287
|
const fs = __require("fs");
|
|
1969
|
-
const
|
|
1970
|
-
const
|
|
2288
|
+
const wasRelative = !path.isAbsolute(source.file);
|
|
2289
|
+
const filePath = wasRelative ? path.resolve(process.cwd(), source.file) : source.file;
|
|
2290
|
+
let rawSource;
|
|
2291
|
+
try {
|
|
2292
|
+
rawSource = fs.readFileSync(filePath, "utf-8");
|
|
2293
|
+
} catch (err) {
|
|
2294
|
+
const isNotFound = err?.code === "ENOENT";
|
|
2295
|
+
if (isNotFound && wasRelative) {
|
|
2296
|
+
throw new Error(
|
|
2297
|
+
`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).`
|
|
2298
|
+
);
|
|
2299
|
+
}
|
|
2300
|
+
throw err;
|
|
2301
|
+
}
|
|
1971
2302
|
const componentName = source.exportName || extractDefaultExportName(rawSource) || "Component";
|
|
1972
|
-
|
|
2303
|
+
if (options.transformOnly === true) {
|
|
2304
|
+
const code = transpileReactSource(rawSource, path.basename(filePath));
|
|
2305
|
+
const parsed2 = parseImports(code);
|
|
2306
|
+
return {
|
|
2307
|
+
mode: "module",
|
|
2308
|
+
code,
|
|
2309
|
+
imports: [...new Set(parsed2.externalImports.map((i) => i.specifier))],
|
|
2310
|
+
exportName: componentName,
|
|
2311
|
+
meta: { mcpAware: true, renderer: "react" },
|
|
2312
|
+
peerDependencies: source.peerDependencies || ["react", "react-dom"],
|
|
2313
|
+
bundled: false
|
|
2314
|
+
};
|
|
2315
|
+
}
|
|
2316
|
+
const bundled = bundleFileSource(rawSource, source.file, path.dirname(filePath), componentName, {
|
|
2317
|
+
bundleReact: options.inlineReact === true
|
|
2318
|
+
});
|
|
1973
2319
|
const parsed = parseImports(bundled.code);
|
|
1974
2320
|
return {
|
|
1975
2321
|
mode: "module",
|
|
@@ -2037,37 +2383,19 @@ function generateMappedPropsCode(mapping) {
|
|
|
2037
2383
|
return `({ ${entries} })`;
|
|
2038
2384
|
}
|
|
2039
2385
|
|
|
2040
|
-
// libs/uipack/src/resolver/import-map.ts
|
|
2041
|
-
function createImportMapFromResolved(resolved) {
|
|
2042
|
-
const imports = {};
|
|
2043
|
-
const integrity = {};
|
|
2044
|
-
for (const [specifier, res] of Object.entries(resolved)) {
|
|
2045
|
-
if (res.type === "url") {
|
|
2046
|
-
imports[specifier] = res.value;
|
|
2047
|
-
if (res.integrity) {
|
|
2048
|
-
integrity[res.value] = res.integrity;
|
|
2049
|
-
}
|
|
2050
|
-
}
|
|
2051
|
-
}
|
|
2052
|
-
return {
|
|
2053
|
-
imports,
|
|
2054
|
-
integrity: Object.keys(integrity).length > 0 ? integrity : void 0
|
|
2055
|
-
};
|
|
2056
|
-
}
|
|
2057
|
-
function generateImportMapScriptTag(map) {
|
|
2058
|
-
const json = JSON.stringify(map, null, 2).replace(/<\//g, "<\\/");
|
|
2059
|
-
return `<script type="importmap">
|
|
2060
|
-
${json}
|
|
2061
|
-
</script>`;
|
|
2062
|
-
}
|
|
2063
|
-
|
|
2064
2386
|
// libs/uipack/src/component/renderer.ts
|
|
2387
|
+
var DEFAULT_WIDGET_MOUNT = {
|
|
2388
|
+
moduleSpecifier: "@frontmcp/ui/react",
|
|
2389
|
+
wrapperImportName: "McpBridgeProvider"
|
|
2390
|
+
};
|
|
2065
2391
|
function renderComponent(config, shellConfig) {
|
|
2066
2392
|
const resolver = shellConfig.resolver ?? createEsmShResolver();
|
|
2067
2393
|
const resolved = resolveUISource(config.source, {
|
|
2068
2394
|
resolver,
|
|
2069
2395
|
input: shellConfig.input,
|
|
2070
|
-
output: shellConfig.output
|
|
2396
|
+
output: shellConfig.output,
|
|
2397
|
+
inlineReact: config.inlineReact === true,
|
|
2398
|
+
transformOnly: config.transformOnly === true
|
|
2071
2399
|
});
|
|
2072
2400
|
const mergedShellConfig = {
|
|
2073
2401
|
...shellConfig,
|
|
@@ -2078,10 +2406,10 @@ function renderComponent(config, shellConfig) {
|
|
|
2078
2406
|
if (resolved.mode === "inline") {
|
|
2079
2407
|
return buildShell(resolved.html ?? "", mergedShellConfig);
|
|
2080
2408
|
}
|
|
2081
|
-
const content = buildModuleContent(resolved, resolver, config.props);
|
|
2409
|
+
const content = buildModuleContent(resolved, resolver, config.props, shellConfig.mount);
|
|
2082
2410
|
return buildShell(content, mergedShellConfig);
|
|
2083
2411
|
}
|
|
2084
|
-
function buildModuleContent(resolved, resolver, propsMapping) {
|
|
2412
|
+
function buildModuleContent(resolved, resolver, propsMapping, mount = DEFAULT_WIDGET_MOUNT) {
|
|
2085
2413
|
const parts = [];
|
|
2086
2414
|
if (resolved.code && resolved.bundled) {
|
|
2087
2415
|
const importEntries = {};
|
|
@@ -2105,14 +2433,16 @@ ${resolved.code}
|
|
|
2105
2433
|
...resolved.imports || [],
|
|
2106
2434
|
"react-dom/client",
|
|
2107
2435
|
// needed by mount script
|
|
2108
|
-
|
|
2109
|
-
//
|
|
2436
|
+
mount.moduleSpecifier,
|
|
2437
|
+
// mounter/provider package (e.g. @frontmcp/ui/react)
|
|
2110
2438
|
// React subpath entries needed by esm.sh externalized modules:
|
|
2111
2439
|
"react/jsx-runtime",
|
|
2112
|
-
"react/jsx-dev-runtime"
|
|
2113
|
-
"react-dom/server",
|
|
2114
|
-
"react-dom/static"
|
|
2440
|
+
"react/jsx-dev-runtime"
|
|
2115
2441
|
]);
|
|
2442
|
+
if (mount === DEFAULT_WIDGET_MOUNT) {
|
|
2443
|
+
allSpecifiers.add("react-dom/server");
|
|
2444
|
+
allSpecifiers.add("react-dom/static");
|
|
2445
|
+
}
|
|
2116
2446
|
const coreDeps = /* @__PURE__ */ new Set([
|
|
2117
2447
|
"react",
|
|
2118
2448
|
"react-dom",
|
|
@@ -2143,8 +2473,9 @@ ${resolved.code}
|
|
|
2143
2473
|
const importMap = createImportMapFromResolved(importEntries);
|
|
2144
2474
|
parts.push(generateImportMapScriptTag(importMap));
|
|
2145
2475
|
}
|
|
2146
|
-
|
|
2147
|
-
|
|
2476
|
+
const mountNodeId = mount.mountNodeId ?? "root";
|
|
2477
|
+
parts.push(`<div id="${mountNodeId}">${mount.mountNodeInnerHtml ?? ""}</div>`);
|
|
2478
|
+
const mountCode = generateInlineMountCode(resolved.exportName, mount);
|
|
2148
2479
|
parts.push(`<script type="module">
|
|
2149
2480
|
${resolved.code}
|
|
2150
2481
|
${mountCode}
|
|
@@ -2181,53 +2512,27 @@ function addExternalParam(url, externals) {
|
|
|
2181
2512
|
const sep = url.includes("?") ? "&" : "?";
|
|
2182
2513
|
return `${url}${sep}external=${externals.join(",")}`;
|
|
2183
2514
|
}
|
|
2184
|
-
function generateInlineMountCode(componentName) {
|
|
2515
|
+
function generateInlineMountCode(componentName, mount) {
|
|
2516
|
+
if (mount.generate) {
|
|
2517
|
+
return mount.generate(componentName);
|
|
2518
|
+
}
|
|
2519
|
+
const wrapperName = mount.wrapperImportName ?? "McpBridgeProvider";
|
|
2520
|
+
const wrapperAlias = `__${wrapperName}`;
|
|
2521
|
+
const mountNodeId = mount.mountNodeId ?? "root";
|
|
2185
2522
|
return `
|
|
2186
2523
|
// --- Mount ---
|
|
2187
2524
|
import { createRoot as __createRoot } from 'react-dom/client';
|
|
2188
|
-
import {
|
|
2189
|
-
const __root = document.getElementById('
|
|
2525
|
+
import { ${wrapperName} as ${wrapperAlias} } from '${mount.moduleSpecifier}';
|
|
2526
|
+
const __root = document.getElementById('${mountNodeId}');
|
|
2190
2527
|
if (__root) {
|
|
2191
2528
|
__createRoot(__root).render(
|
|
2192
|
-
React.createElement(
|
|
2529
|
+
React.createElement(${wrapperAlias}, null,
|
|
2193
2530
|
React.createElement(${componentName})
|
|
2194
2531
|
)
|
|
2195
2532
|
);
|
|
2196
2533
|
}`;
|
|
2197
2534
|
}
|
|
2198
2535
|
|
|
2199
|
-
// libs/uipack/src/adapters/type-detector.ts
|
|
2200
|
-
function detectUIType(template) {
|
|
2201
|
-
if (template === null || template === void 0) {
|
|
2202
|
-
return "auto";
|
|
2203
|
-
}
|
|
2204
|
-
if (typeof template === "object" && template !== null && "file" in template) {
|
|
2205
|
-
const file = template.file;
|
|
2206
|
-
if (/\.(tsx|jsx)$/i.test(file)) return "react";
|
|
2207
|
-
}
|
|
2208
|
-
if (typeof template === "function") {
|
|
2209
|
-
const proto = template.prototype;
|
|
2210
|
-
if (proto && typeof proto.render === "function") {
|
|
2211
|
-
return "react";
|
|
2212
|
-
}
|
|
2213
|
-
const asRecord = template;
|
|
2214
|
-
if (asRecord["$$typeof"] !== void 0) {
|
|
2215
|
-
return "react";
|
|
2216
|
-
}
|
|
2217
|
-
if (template.name && /^[A-Z]/.test(template.name)) {
|
|
2218
|
-
return "react";
|
|
2219
|
-
}
|
|
2220
|
-
return "html";
|
|
2221
|
-
}
|
|
2222
|
-
if (typeof template === "string") {
|
|
2223
|
-
if (template.includes("<") && template.includes(">")) {
|
|
2224
|
-
return "html";
|
|
2225
|
-
}
|
|
2226
|
-
return "markdown";
|
|
2227
|
-
}
|
|
2228
|
-
return "auto";
|
|
2229
|
-
}
|
|
2230
|
-
|
|
2231
2536
|
// libs/uipack/src/adapters/content-detector.ts
|
|
2232
2537
|
var CHART_TYPES = /* @__PURE__ */ new Set(["bar", "line", "pie", "area", "scatter", "doughnut", "radar", "polarArea", "bubble"]);
|
|
2233
2538
|
var MERMAID_PREFIXES = [
|
|
@@ -2368,6 +2673,38 @@ function wrapDetectedContent(value) {
|
|
|
2368
2673
|
}
|
|
2369
2674
|
}
|
|
2370
2675
|
|
|
2676
|
+
// libs/uipack/src/adapters/type-detector.ts
|
|
2677
|
+
function detectUIType(template) {
|
|
2678
|
+
if (template === null || template === void 0) {
|
|
2679
|
+
return "auto";
|
|
2680
|
+
}
|
|
2681
|
+
if (typeof template === "object" && template !== null && "file" in template) {
|
|
2682
|
+
const file = template.file;
|
|
2683
|
+
if (/\.(tsx|jsx)$/i.test(file)) return "react";
|
|
2684
|
+
}
|
|
2685
|
+
if (typeof template === "function") {
|
|
2686
|
+
const proto = template.prototype;
|
|
2687
|
+
if (proto && typeof proto.render === "function") {
|
|
2688
|
+
return "react";
|
|
2689
|
+
}
|
|
2690
|
+
const asRecord = template;
|
|
2691
|
+
if (asRecord["$$typeof"] !== void 0) {
|
|
2692
|
+
return "react";
|
|
2693
|
+
}
|
|
2694
|
+
if (template.name && /^[A-Z]/.test(template.name)) {
|
|
2695
|
+
return "react";
|
|
2696
|
+
}
|
|
2697
|
+
return "html";
|
|
2698
|
+
}
|
|
2699
|
+
if (typeof template === "string") {
|
|
2700
|
+
if (template.includes("<") && template.includes(">")) {
|
|
2701
|
+
return "html";
|
|
2702
|
+
}
|
|
2703
|
+
return "markdown";
|
|
2704
|
+
}
|
|
2705
|
+
return "auto";
|
|
2706
|
+
}
|
|
2707
|
+
|
|
2371
2708
|
// libs/uipack/src/adapters/template-renderer.ts
|
|
2372
2709
|
function buildCspConfig(resolver) {
|
|
2373
2710
|
const cspResourceDomains = ["https://esm.sh"];
|
|
@@ -2388,21 +2725,26 @@ function buildCspConfig(resolver) {
|
|
|
2388
2725
|
return { resourceDomains: cspResourceDomains, connectDomains: cspConnectDomains };
|
|
2389
2726
|
}
|
|
2390
2727
|
function renderToolTemplate(options) {
|
|
2391
|
-
const { toolName, input, output, template, resolver } = options;
|
|
2728
|
+
const { toolName, input, output, template, resolver, platformType, sizing } = options;
|
|
2392
2729
|
const uiType = detectUIType(template);
|
|
2730
|
+
const resourceMode = options.resourceMode ?? (platformType === "claude" ? "inline" : "cdn");
|
|
2393
2731
|
const shellConfig = {
|
|
2394
2732
|
toolName,
|
|
2395
2733
|
input,
|
|
2396
2734
|
output,
|
|
2397
2735
|
includeBridge: true,
|
|
2398
|
-
resolver
|
|
2736
|
+
resolver,
|
|
2737
|
+
sizing
|
|
2399
2738
|
};
|
|
2400
2739
|
let html;
|
|
2401
2740
|
let hash = "";
|
|
2402
2741
|
let size = 0;
|
|
2403
2742
|
if (typeof template === "object" && template !== null && "file" in template) {
|
|
2404
2743
|
const cspConfig = buildCspConfig(resolver);
|
|
2405
|
-
const result = renderComponent(
|
|
2744
|
+
const result = renderComponent(
|
|
2745
|
+
{ source: template, inlineReact: resourceMode === "inline" },
|
|
2746
|
+
{ ...shellConfig, csp: cspConfig }
|
|
2747
|
+
);
|
|
2406
2748
|
html = result.html;
|
|
2407
2749
|
hash = result.hash;
|
|
2408
2750
|
size = result.size;
|
|
@@ -2444,6 +2786,12 @@ function renderToolTemplate(options) {
|
|
|
2444
2786
|
"ui/type": uiType,
|
|
2445
2787
|
"ui/mimeType": MCP_APPS_MIME_TYPE
|
|
2446
2788
|
};
|
|
2789
|
+
if (hasSizing(sizing)) {
|
|
2790
|
+
if (sizing.preferredHeight !== void 0) meta["ui/preferredHeight"] = sizing.preferredHeight;
|
|
2791
|
+
if (sizing.minHeight !== void 0) meta["ui/minHeight"] = sizing.minHeight;
|
|
2792
|
+
if (sizing.maxHeight !== void 0) meta["ui/maxHeight"] = sizing.maxHeight;
|
|
2793
|
+
if (sizing.aspectRatio !== void 0) meta["ui/aspectRatio"] = sizing.aspectRatio;
|
|
2794
|
+
}
|
|
2447
2795
|
return {
|
|
2448
2796
|
html,
|
|
2449
2797
|
uiType,
|