@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/adapters/index.js
CHANGED
|
@@ -106,78 +106,404 @@ function isUIRenderFailure(result) {
|
|
|
106
106
|
return typeof rec["reason"] === "string" && !("meta" in rec);
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
// libs/uipack/src/
|
|
110
|
-
var
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
109
|
+
// libs/uipack/src/resolver/cdn-registry.ts
|
|
110
|
+
var DEFAULT_CDN_REGISTRY = {
|
|
111
|
+
react: {
|
|
112
|
+
packageName: "react",
|
|
113
|
+
defaultVersion: "19.2.4",
|
|
114
|
+
providers: {
|
|
115
|
+
cloudflare: {
|
|
116
|
+
url: "https://cdnjs.cloudflare.com/ajax/libs/react/19.2.4/umd/react.production.min.js",
|
|
117
|
+
global: "React",
|
|
118
|
+
crossorigin: "anonymous"
|
|
119
|
+
},
|
|
120
|
+
jsdelivr: {
|
|
121
|
+
url: "https://cdn.jsdelivr.net/npm/react@19.2.4/umd/react.production.min.js",
|
|
122
|
+
global: "React",
|
|
123
|
+
crossorigin: "anonymous"
|
|
124
|
+
},
|
|
125
|
+
unpkg: {
|
|
126
|
+
url: "https://unpkg.com/react@19.2.4/umd/react.production.min.js",
|
|
127
|
+
global: "React",
|
|
128
|
+
crossorigin: "anonymous"
|
|
129
|
+
},
|
|
130
|
+
"esm.sh": {
|
|
131
|
+
url: "https://esm.sh/react@19.2.4",
|
|
132
|
+
esm: true,
|
|
133
|
+
crossorigin: "anonymous"
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
preferredProviders: ["esm.sh", "cloudflare", "jsdelivr", "unpkg"],
|
|
137
|
+
metadata: {
|
|
138
|
+
description: "A JavaScript library for building user interfaces",
|
|
139
|
+
homepage: "https://react.dev",
|
|
140
|
+
license: "MIT"
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
"react-dom": {
|
|
144
|
+
packageName: "react-dom",
|
|
145
|
+
defaultVersion: "19.2.4",
|
|
146
|
+
providers: {
|
|
147
|
+
cloudflare: {
|
|
148
|
+
url: "https://cdnjs.cloudflare.com/ajax/libs/react-dom/19.2.4/umd/react-dom.production.min.js",
|
|
149
|
+
global: "ReactDOM",
|
|
150
|
+
crossorigin: "anonymous",
|
|
151
|
+
peerDependencies: ["react"]
|
|
152
|
+
},
|
|
153
|
+
jsdelivr: {
|
|
154
|
+
url: "https://cdn.jsdelivr.net/npm/react-dom@19.2.4/umd/react-dom.production.min.js",
|
|
155
|
+
global: "ReactDOM",
|
|
156
|
+
crossorigin: "anonymous",
|
|
157
|
+
peerDependencies: ["react"]
|
|
158
|
+
},
|
|
159
|
+
unpkg: {
|
|
160
|
+
url: "https://unpkg.com/react-dom@19.2.4/umd/react-dom.production.min.js",
|
|
161
|
+
global: "ReactDOM",
|
|
162
|
+
crossorigin: "anonymous",
|
|
163
|
+
peerDependencies: ["react"]
|
|
164
|
+
},
|
|
165
|
+
"esm.sh": {
|
|
166
|
+
url: "https://esm.sh/react-dom@19.2.4",
|
|
167
|
+
esm: true,
|
|
168
|
+
crossorigin: "anonymous",
|
|
169
|
+
peerDependencies: ["react"]
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
preferredProviders: ["esm.sh", "cloudflare", "jsdelivr", "unpkg"],
|
|
173
|
+
metadata: {
|
|
174
|
+
description: "React package for working with the DOM",
|
|
175
|
+
homepage: "https://react.dev",
|
|
176
|
+
license: "MIT"
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
"chart.js": {
|
|
180
|
+
packageName: "chart.js",
|
|
181
|
+
defaultVersion: "4.5.1",
|
|
182
|
+
providers: {
|
|
183
|
+
cloudflare: {
|
|
184
|
+
url: "https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.5.1/chart.umd.min.js",
|
|
185
|
+
global: "Chart",
|
|
186
|
+
crossorigin: "anonymous"
|
|
187
|
+
},
|
|
188
|
+
jsdelivr: {
|
|
189
|
+
url: "https://cdn.jsdelivr.net/npm/chart.js@4.5.1/dist/chart.umd.min.js",
|
|
190
|
+
global: "Chart",
|
|
191
|
+
crossorigin: "anonymous"
|
|
192
|
+
},
|
|
193
|
+
unpkg: {
|
|
194
|
+
url: "https://unpkg.com/chart.js@4.5.1/dist/chart.umd.min.js",
|
|
195
|
+
global: "Chart",
|
|
196
|
+
crossorigin: "anonymous"
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
preferredProviders: ["esm.sh", "cloudflare", "jsdelivr", "unpkg"],
|
|
200
|
+
metadata: {
|
|
201
|
+
description: "Simple yet flexible JavaScript charting library",
|
|
202
|
+
homepage: "https://www.chartjs.org",
|
|
203
|
+
license: "MIT"
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
d3: {
|
|
207
|
+
packageName: "d3",
|
|
208
|
+
defaultVersion: "7.9.0",
|
|
209
|
+
providers: {
|
|
210
|
+
cloudflare: {
|
|
211
|
+
url: "https://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.min.js",
|
|
212
|
+
integrity: "sha512-vc58qvvBdrDR4etbxMdlTt4GBQk1qjvyORR2nrsPsFPyrs+/u5c3+1Ct6upOgdZoIl7eq6k3a1UPDSNAQi/32A==",
|
|
213
|
+
global: "d3",
|
|
214
|
+
crossorigin: "anonymous"
|
|
215
|
+
},
|
|
216
|
+
jsdelivr: {
|
|
217
|
+
url: "https://cdn.jsdelivr.net/npm/d3@7.9.0/dist/d3.min.js",
|
|
218
|
+
global: "d3",
|
|
219
|
+
crossorigin: "anonymous"
|
|
220
|
+
},
|
|
221
|
+
unpkg: {
|
|
222
|
+
url: "https://unpkg.com/d3@7.9.0/dist/d3.min.js",
|
|
223
|
+
global: "d3",
|
|
224
|
+
crossorigin: "anonymous"
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
preferredProviders: ["esm.sh", "cloudflare", "jsdelivr", "unpkg"],
|
|
228
|
+
metadata: {
|
|
229
|
+
description: "Data-Driven Documents",
|
|
230
|
+
homepage: "https://d3js.org",
|
|
231
|
+
license: "ISC"
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
lodash: {
|
|
235
|
+
packageName: "lodash",
|
|
236
|
+
defaultVersion: "4.17.21",
|
|
237
|
+
providers: {
|
|
238
|
+
cloudflare: {
|
|
239
|
+
url: "https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js",
|
|
240
|
+
integrity: "sha512-WFN04846sdKMIP5LKNphMaWzU7YpMyCU245etK3g/2ARYbPK9Ub18eG+ljU96qKRCWh+quCY7yefSmlkQw1ANQ==",
|
|
241
|
+
global: "_",
|
|
242
|
+
crossorigin: "anonymous"
|
|
243
|
+
},
|
|
244
|
+
jsdelivr: {
|
|
245
|
+
url: "https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js",
|
|
246
|
+
global: "_",
|
|
247
|
+
crossorigin: "anonymous"
|
|
248
|
+
},
|
|
249
|
+
unpkg: {
|
|
250
|
+
url: "https://unpkg.com/lodash@4.17.21/lodash.min.js",
|
|
251
|
+
global: "_",
|
|
252
|
+
crossorigin: "anonymous"
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
preferredProviders: ["esm.sh", "cloudflare", "jsdelivr", "unpkg"],
|
|
256
|
+
metadata: {
|
|
257
|
+
description: "A modern JavaScript utility library",
|
|
258
|
+
homepage: "https://lodash.com",
|
|
259
|
+
license: "MIT"
|
|
260
|
+
}
|
|
129
261
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
]
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
262
|
+
};
|
|
263
|
+
function lookupPackage(packageName, registry = DEFAULT_CDN_REGISTRY) {
|
|
264
|
+
return registry[packageName];
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// libs/uipack/src/resolver/import-parser.ts
|
|
268
|
+
var IMPORT_PATTERNS = {
|
|
269
|
+
named: /import\s*\{([^}]+)\}\s*from\s*['"]([^'"]+)['"]/g,
|
|
270
|
+
default: /import\s+(\w+)\s+from\s*['"]([^'"]+)['"]/g,
|
|
271
|
+
namespace: /import\s*\*\s*as\s+(\w+)\s+from\s*['"]([^'"]+)['"]/g,
|
|
272
|
+
sideEffect: /import\s*['"]([^'"]+)['"]/g,
|
|
273
|
+
dynamic: /(?:await\s+)?import\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
274
|
+
defaultAndNamed: /import\s+(\w+)\s*,\s*\{([^}]+)\}\s*from\s*['"]([^'"]+)['"]/g,
|
|
275
|
+
defaultAndNamespace: /import\s+(\w+)\s*,\s*\*\s*as\s+(\w+)\s+from\s*['"]([^'"]+)['"]/g,
|
|
276
|
+
reExport: /export\s*\{[^}]+\}\s*from\s*['"]([^'"]+)['"]/g,
|
|
277
|
+
reExportAll: /export\s*\*\s*from\s*['"]([^'"]+)['"]/g
|
|
278
|
+
};
|
|
279
|
+
function parseNamedImports(namedString) {
|
|
280
|
+
return namedString.split(",").map((s) => s.trim()).filter((s) => s.length > 0).map((s) => {
|
|
281
|
+
const asMatch = s.match(/^(\w+)\s+as\s+\w+$/);
|
|
282
|
+
return asMatch ? asMatch[1] : s;
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
function isRelativeImport(specifier) {
|
|
286
|
+
return specifier.startsWith("./") || specifier.startsWith("../") || specifier.startsWith("/");
|
|
287
|
+
}
|
|
288
|
+
function isExternalImport(specifier) {
|
|
289
|
+
return !isRelativeImport(specifier) && !specifier.startsWith("#");
|
|
290
|
+
}
|
|
291
|
+
function getPackageName(specifier) {
|
|
292
|
+
if (specifier.startsWith("@")) {
|
|
293
|
+
const parts = specifier.split("/");
|
|
294
|
+
if (parts.length >= 2) {
|
|
295
|
+
return `${parts[0]}/${parts[1]}`;
|
|
296
|
+
}
|
|
297
|
+
return specifier;
|
|
146
298
|
}
|
|
147
|
-
|
|
148
|
-
return
|
|
299
|
+
const firstSlash = specifier.indexOf("/");
|
|
300
|
+
return firstSlash === -1 ? specifier : specifier.slice(0, firstSlash);
|
|
149
301
|
}
|
|
150
|
-
function
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
302
|
+
function getLineNumber(source, index) {
|
|
303
|
+
let line = 1;
|
|
304
|
+
for (let i = 0; i < index && i < source.length; i++) {
|
|
305
|
+
if (source[i] === "\n") {
|
|
306
|
+
line++;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return line;
|
|
154
310
|
}
|
|
155
|
-
function
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
311
|
+
function getColumnNumber(source, index) {
|
|
312
|
+
let column = 0;
|
|
313
|
+
for (let i = index - 1; i >= 0 && source[i] !== "\n"; i--) {
|
|
314
|
+
column++;
|
|
159
315
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
316
|
+
return column;
|
|
317
|
+
}
|
|
318
|
+
function parseImports(source) {
|
|
319
|
+
const imports = [];
|
|
320
|
+
const seenStatements = /* @__PURE__ */ new Set();
|
|
321
|
+
const addImport = (imp) => {
|
|
322
|
+
const key = `${imp.type}:${imp.specifier}:${imp.statement}`;
|
|
323
|
+
if (!seenStatements.has(key)) {
|
|
324
|
+
seenStatements.add(key);
|
|
325
|
+
imports.push(imp);
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
let match;
|
|
329
|
+
const defaultAndNamedRegex = new RegExp(IMPORT_PATTERNS.defaultAndNamed.source, "g");
|
|
330
|
+
while ((match = defaultAndNamedRegex.exec(source)) !== null) {
|
|
331
|
+
const [statement, defaultName, namedString, specifier] = match;
|
|
332
|
+
addImport({
|
|
333
|
+
statement,
|
|
334
|
+
specifier,
|
|
335
|
+
type: "default",
|
|
336
|
+
defaultImport: defaultName,
|
|
337
|
+
namedImports: parseNamedImports(namedString),
|
|
338
|
+
line: getLineNumber(source, match.index),
|
|
339
|
+
column: getColumnNumber(source, match.index)
|
|
340
|
+
});
|
|
165
341
|
}
|
|
342
|
+
const defaultAndNamespaceRegex = new RegExp(IMPORT_PATTERNS.defaultAndNamespace.source, "g");
|
|
343
|
+
while ((match = defaultAndNamespaceRegex.exec(source)) !== null) {
|
|
344
|
+
const [statement, defaultName, namespaceName, specifier] = match;
|
|
345
|
+
addImport({
|
|
346
|
+
statement,
|
|
347
|
+
specifier,
|
|
348
|
+
type: "default",
|
|
349
|
+
defaultImport: defaultName,
|
|
350
|
+
namespaceImport: namespaceName,
|
|
351
|
+
line: getLineNumber(source, match.index),
|
|
352
|
+
column: getColumnNumber(source, match.index)
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
const namedRegex = new RegExp(IMPORT_PATTERNS.named.source, "g");
|
|
356
|
+
while ((match = namedRegex.exec(source)) !== null) {
|
|
357
|
+
const [statement, namedString, specifier] = match;
|
|
358
|
+
addImport({
|
|
359
|
+
statement,
|
|
360
|
+
specifier,
|
|
361
|
+
type: "named",
|
|
362
|
+
namedImports: parseNamedImports(namedString),
|
|
363
|
+
line: getLineNumber(source, match.index),
|
|
364
|
+
column: getColumnNumber(source, match.index)
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
const defaultRegex = new RegExp(IMPORT_PATTERNS.default.source, "g");
|
|
368
|
+
while ((match = defaultRegex.exec(source)) !== null) {
|
|
369
|
+
const [statement, defaultName, specifier] = match;
|
|
370
|
+
const afterMatch = source.slice(match.index + match[0].length - specifier.length - 2);
|
|
371
|
+
if (afterMatch.startsWith(",")) continue;
|
|
372
|
+
addImport({
|
|
373
|
+
statement,
|
|
374
|
+
specifier,
|
|
375
|
+
type: "default",
|
|
376
|
+
defaultImport: defaultName,
|
|
377
|
+
line: getLineNumber(source, match.index),
|
|
378
|
+
column: getColumnNumber(source, match.index)
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
const namespaceRegex = new RegExp(IMPORT_PATTERNS.namespace.source, "g");
|
|
382
|
+
while ((match = namespaceRegex.exec(source)) !== null) {
|
|
383
|
+
const [statement, namespaceName, specifier] = match;
|
|
384
|
+
addImport({
|
|
385
|
+
statement,
|
|
386
|
+
specifier,
|
|
387
|
+
type: "namespace",
|
|
388
|
+
namespaceImport: namespaceName,
|
|
389
|
+
line: getLineNumber(source, match.index),
|
|
390
|
+
column: getColumnNumber(source, match.index)
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
const sideEffectRegex = new RegExp(IMPORT_PATTERNS.sideEffect.source, "g");
|
|
394
|
+
while ((match = sideEffectRegex.exec(source)) !== null) {
|
|
395
|
+
const [statement, specifier] = match;
|
|
396
|
+
const beforeMatch = source.slice(Math.max(0, match.index - 50), match.index);
|
|
397
|
+
if (beforeMatch.includes("from")) continue;
|
|
398
|
+
addImport({
|
|
399
|
+
statement,
|
|
400
|
+
specifier,
|
|
401
|
+
type: "side-effect",
|
|
402
|
+
line: getLineNumber(source, match.index),
|
|
403
|
+
column: getColumnNumber(source, match.index)
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
const dynamicRegex = new RegExp(IMPORT_PATTERNS.dynamic.source, "g");
|
|
407
|
+
while ((match = dynamicRegex.exec(source)) !== null) {
|
|
408
|
+
const [statement, specifier] = match;
|
|
409
|
+
addImport({
|
|
410
|
+
statement,
|
|
411
|
+
specifier,
|
|
412
|
+
type: "dynamic",
|
|
413
|
+
line: getLineNumber(source, match.index),
|
|
414
|
+
column: getColumnNumber(source, match.index)
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
const reExportRegex = new RegExp(IMPORT_PATTERNS.reExport.source, "g");
|
|
418
|
+
while ((match = reExportRegex.exec(source)) !== null) {
|
|
419
|
+
const [statement, specifier] = match;
|
|
420
|
+
addImport({
|
|
421
|
+
statement,
|
|
422
|
+
specifier,
|
|
423
|
+
type: "named",
|
|
424
|
+
line: getLineNumber(source, match.index),
|
|
425
|
+
column: getColumnNumber(source, match.index)
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
const reExportAllRegex = new RegExp(IMPORT_PATTERNS.reExportAll.source, "g");
|
|
429
|
+
while ((match = reExportAllRegex.exec(source)) !== null) {
|
|
430
|
+
const [statement, specifier] = match;
|
|
431
|
+
addImport({
|
|
432
|
+
statement,
|
|
433
|
+
specifier,
|
|
434
|
+
type: "namespace",
|
|
435
|
+
line: getLineNumber(source, match.index),
|
|
436
|
+
column: getColumnNumber(source, match.index)
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
const externalImports = imports.filter((imp) => isExternalImport(imp.specifier));
|
|
440
|
+
const relativeImports = imports.filter((imp) => isRelativeImport(imp.specifier));
|
|
441
|
+
const externalPackages = [...new Set(externalImports.map((imp) => getPackageName(imp.specifier)))];
|
|
442
|
+
return {
|
|
443
|
+
imports,
|
|
444
|
+
externalImports,
|
|
445
|
+
relativeImports,
|
|
446
|
+
externalPackages
|
|
447
|
+
};
|
|
166
448
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
449
|
+
|
|
450
|
+
// libs/uipack/src/resolver/esm-sh.resolver.ts
|
|
451
|
+
var DEFAULT_FALLBACK_CDN = "https://esm.sh";
|
|
452
|
+
function createEsmShResolver(options = {}) {
|
|
453
|
+
const {
|
|
454
|
+
fallbackCdnBase = DEFAULT_FALLBACK_CDN,
|
|
455
|
+
registry = DEFAULT_CDN_REGISTRY,
|
|
456
|
+
providerOrder = ["esm.sh", "cloudflare", "jsdelivr", "unpkg"]
|
|
457
|
+
} = options;
|
|
458
|
+
return {
|
|
459
|
+
resolve(specifier, _context) {
|
|
460
|
+
if (specifier.startsWith("./") || specifier.startsWith("../") || specifier.startsWith("/")) {
|
|
461
|
+
return null;
|
|
462
|
+
}
|
|
463
|
+
if (specifier.startsWith("node:") || specifier.startsWith("#")) {
|
|
464
|
+
return null;
|
|
465
|
+
}
|
|
466
|
+
const pkgName = getPackageName(specifier);
|
|
467
|
+
const entry = lookupPackage(pkgName, registry);
|
|
468
|
+
if (entry) {
|
|
469
|
+
for (const provider of providerOrder) {
|
|
470
|
+
const config = entry.providers[provider];
|
|
471
|
+
if (config?.url) {
|
|
472
|
+
const subpath = specifier.slice(pkgName.length);
|
|
473
|
+
if (subpath && config.url.includes("esm.sh/")) {
|
|
474
|
+
return {
|
|
475
|
+
value: config.url + subpath,
|
|
476
|
+
type: "url",
|
|
477
|
+
integrity: config.integrity
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
if (subpath) {
|
|
481
|
+
return {
|
|
482
|
+
value: `${fallbackCdnBase}/${specifier}`,
|
|
483
|
+
type: "url"
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
if (config.global && !config.esm) {
|
|
487
|
+
return {
|
|
488
|
+
value: config.url,
|
|
489
|
+
type: "url",
|
|
490
|
+
integrity: config.integrity
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
return {
|
|
494
|
+
value: config.url,
|
|
495
|
+
type: "url",
|
|
496
|
+
integrity: config.integrity
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
return {
|
|
502
|
+
value: `${fallbackCdnBase}/${specifier}`,
|
|
503
|
+
type: "url"
|
|
504
|
+
};
|
|
175
505
|
}
|
|
176
|
-
}
|
|
177
|
-
return valid;
|
|
178
|
-
}
|
|
179
|
-
function escapeAttribute(str) {
|
|
180
|
-
return str.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">");
|
|
506
|
+
};
|
|
181
507
|
}
|
|
182
508
|
|
|
183
509
|
// libs/uipack/src/utils/index.ts
|
|
@@ -211,46 +537,29 @@ function safeJsonForScript(value) {
|
|
|
211
537
|
}
|
|
212
538
|
}
|
|
213
539
|
|
|
214
|
-
// libs/uipack/src/
|
|
215
|
-
function
|
|
216
|
-
const
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
return `<script>
|
|
225
|
-
${lines.join("\n")}
|
|
226
|
-
</script>`;
|
|
227
|
-
}
|
|
228
|
-
var _uniqueIdCounter = 0;
|
|
229
|
-
function createTemplateHelpers() {
|
|
230
|
-
return {
|
|
231
|
-
escapeHtml: (str) => escapeHtml(str),
|
|
232
|
-
formatDate: (date, format) => {
|
|
233
|
-
const d = date instanceof Date ? date : new Date(date);
|
|
234
|
-
if (isNaN(d.getTime())) return String(date);
|
|
235
|
-
if (format === "iso") return d.toISOString();
|
|
236
|
-
if (format === "date") return d.toLocaleDateString();
|
|
237
|
-
if (format === "time") return d.toLocaleTimeString();
|
|
238
|
-
return d.toLocaleString();
|
|
239
|
-
},
|
|
240
|
-
formatCurrency: (amount, currency = "USD") => {
|
|
241
|
-
return new Intl.NumberFormat("en-US", {
|
|
242
|
-
style: "currency",
|
|
243
|
-
currency
|
|
244
|
-
}).format(amount);
|
|
245
|
-
},
|
|
246
|
-
uniqueId: (prefix = "mcp") => {
|
|
247
|
-
return `${prefix}-${++_uniqueIdCounter}`;
|
|
248
|
-
},
|
|
249
|
-
jsonEmbed: (data) => {
|
|
250
|
-
return escapeScriptClose(JSON.stringify(data));
|
|
540
|
+
// libs/uipack/src/resolver/import-map.ts
|
|
541
|
+
function createImportMapFromResolved(resolved) {
|
|
542
|
+
const imports = {};
|
|
543
|
+
const integrity = {};
|
|
544
|
+
for (const [specifier, res] of Object.entries(resolved)) {
|
|
545
|
+
if (res.type === "url") {
|
|
546
|
+
imports[specifier] = res.value;
|
|
547
|
+
if (res.integrity) {
|
|
548
|
+
integrity[res.value] = res.integrity;
|
|
549
|
+
}
|
|
251
550
|
}
|
|
551
|
+
}
|
|
552
|
+
return {
|
|
553
|
+
imports,
|
|
554
|
+
integrity: Object.keys(integrity).length > 0 ? integrity : void 0
|
|
252
555
|
};
|
|
253
556
|
}
|
|
557
|
+
function generateImportMapScriptTag(map) {
|
|
558
|
+
const json = JSON.stringify(map, null, 2).replace(/<\//g, "<\\/");
|
|
559
|
+
return `<script type="importmap">
|
|
560
|
+
${json}
|
|
561
|
+
</script>`;
|
|
562
|
+
}
|
|
254
563
|
|
|
255
564
|
// libs/uipack/src/bridge-runtime/iife-generator.ts
|
|
256
565
|
function generateBridgeIIFE(options = {}) {
|
|
@@ -306,6 +615,8 @@ function generateBridgeIIFE(options = {}) {
|
|
|
306
615
|
parts.push("});");
|
|
307
616
|
parts.push("");
|
|
308
617
|
parts.push("window.FrontMcpBridge = bridge;");
|
|
618
|
+
parts.push("");
|
|
619
|
+
parts.push(generateAutoResize());
|
|
309
620
|
parts.push("function __showLoading() {");
|
|
310
621
|
parts.push(' var root = document.getElementById("root");');
|
|
311
622
|
parts.push(" if (root && !root.hasChildNodes()) {");
|
|
@@ -326,6 +637,112 @@ function generateBridgeIIFE(options = {}) {
|
|
|
326
637
|
}
|
|
327
638
|
return code;
|
|
328
639
|
}
|
|
640
|
+
function generateAutoResize() {
|
|
641
|
+
return `
|
|
642
|
+
function __applySizingCss(sizing) {
|
|
643
|
+
if (typeof document === 'undefined' || !document.documentElement) return;
|
|
644
|
+
function toLen(v) { return typeof v === 'number' ? v + 'px' : v; }
|
|
645
|
+
var de = document.documentElement;
|
|
646
|
+
var body = document.body;
|
|
647
|
+
var root = document.getElementById('root');
|
|
648
|
+
if (sizing.preferredHeight != null) {
|
|
649
|
+
var ph = toLen(sizing.preferredHeight);
|
|
650
|
+
de.style.height = ph;
|
|
651
|
+
if (body) body.style.height = ph;
|
|
652
|
+
if (root && !root.style.minHeight) root.style.minHeight = ph;
|
|
653
|
+
}
|
|
654
|
+
if (sizing.minHeight != null) {
|
|
655
|
+
var mh = toLen(sizing.minHeight);
|
|
656
|
+
de.style.minHeight = mh;
|
|
657
|
+
if (body) body.style.minHeight = mh;
|
|
658
|
+
if (root) root.style.minHeight = mh;
|
|
659
|
+
}
|
|
660
|
+
if (sizing.maxHeight != null) {
|
|
661
|
+
var mx = toLen(sizing.maxHeight);
|
|
662
|
+
de.style.maxHeight = mx;
|
|
663
|
+
if (body) body.style.maxHeight = mx;
|
|
664
|
+
if (root) root.style.maxHeight = mx;
|
|
665
|
+
}
|
|
666
|
+
if (sizing.aspectRatio != null && root) {
|
|
667
|
+
root.style.aspectRatio = String(sizing.aspectRatio);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
function __initAutoResize() {
|
|
672
|
+
if (typeof window === 'undefined') return;
|
|
673
|
+
// Idempotent: a re-injected IIFE must not stack observers.
|
|
674
|
+
if (window.__mcpAutoResizeInit) return;
|
|
675
|
+
window.__mcpAutoResizeInit = true;
|
|
676
|
+
var sizing = window.__mcpWidgetSizing;
|
|
677
|
+
if (!sizing || typeof sizing !== 'object') return;
|
|
678
|
+
|
|
679
|
+
// Apply CSS as a runtime fallback (the static <style> may be absent on some
|
|
680
|
+
// render paths). Wait for the body if it isn't ready yet.
|
|
681
|
+
function apply() { try { __applySizingCss(sizing); } catch (e) {} }
|
|
682
|
+
if (typeof document !== 'undefined' && document.readyState === 'loading') {
|
|
683
|
+
document.addEventListener('DOMContentLoaded', apply);
|
|
684
|
+
} else {
|
|
685
|
+
apply();
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// Auto-resize defaults ON; opt out with autoResize:false.
|
|
689
|
+
if (sizing.autoResize === false) return;
|
|
690
|
+
if (typeof ResizeObserver === 'undefined') return;
|
|
691
|
+
|
|
692
|
+
function startObserving() {
|
|
693
|
+
var target = document.getElementById('root') || document.body;
|
|
694
|
+
if (!target) return;
|
|
695
|
+
|
|
696
|
+
var rafId = null;
|
|
697
|
+
var lastReported = -1;
|
|
698
|
+
function report() {
|
|
699
|
+
rafId = null;
|
|
700
|
+
try {
|
|
701
|
+
var rect = target.getBoundingClientRect();
|
|
702
|
+
var height = Math.ceil(rect.height);
|
|
703
|
+
var width = Math.ceil(rect.width);
|
|
704
|
+
if (height === lastReported || height <= 0) return;
|
|
705
|
+
lastReported = height;
|
|
706
|
+
var payload = { height: height, width: width };
|
|
707
|
+
if (sizing.aspectRatio != null) payload.aspectRatio = sizing.aspectRatio;
|
|
708
|
+
if (window.FrontMcpBridge && typeof window.FrontMcpBridge.setSize === 'function') {
|
|
709
|
+
window.FrontMcpBridge.setSize(payload).catch(function() {});
|
|
710
|
+
}
|
|
711
|
+
window.dispatchEvent(new CustomEvent('widget:resize', { detail: payload }));
|
|
712
|
+
} catch (e) {}
|
|
713
|
+
}
|
|
714
|
+
function schedule() {
|
|
715
|
+
// Debounce via rAF; fall back to setTimeout if rAF is unavailable.
|
|
716
|
+
if (rafId != null) return;
|
|
717
|
+
if (typeof requestAnimationFrame === 'function') {
|
|
718
|
+
rafId = requestAnimationFrame(report);
|
|
719
|
+
} else {
|
|
720
|
+
rafId = setTimeout(report, 100);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
try {
|
|
725
|
+
// Disconnect any prior observer before creating a new one (no leaks/dupes).
|
|
726
|
+
if (window.__mcpResizeObserver && typeof window.__mcpResizeObserver.disconnect === 'function') {
|
|
727
|
+
window.__mcpResizeObserver.disconnect();
|
|
728
|
+
}
|
|
729
|
+
var ro = new ResizeObserver(function() { schedule(); });
|
|
730
|
+
ro.observe(target);
|
|
731
|
+
window.__mcpResizeObserver = ro;
|
|
732
|
+
} catch (e) {}
|
|
733
|
+
// Report once on init so the host gets an initial measurement.
|
|
734
|
+
schedule();
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
if (typeof document !== 'undefined' && document.readyState === 'loading') {
|
|
738
|
+
document.addEventListener('DOMContentLoaded', startObserving);
|
|
739
|
+
} else {
|
|
740
|
+
startObserving();
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
__initAutoResize();
|
|
744
|
+
`.trim();
|
|
745
|
+
}
|
|
329
746
|
function generateContextDetection() {
|
|
330
747
|
return `
|
|
331
748
|
function detectTheme() {
|
|
@@ -433,6 +850,19 @@ var OpenAIAdapter = {
|
|
|
433
850
|
requestDisplayMode: function(context, mode) {
|
|
434
851
|
return Promise.resolve();
|
|
435
852
|
},
|
|
853
|
+
setSize: function(context, size) {
|
|
854
|
+
// OpenAI Apps SDK measures DOM height itself; if a sizing API surfaces on
|
|
855
|
+
// window.openai, forward to it, otherwise no-op (CSS drives layout).
|
|
856
|
+
try {
|
|
857
|
+
if (window.openai && typeof window.openai.requestDisplayMode === 'function' && size && size.displayMode) {
|
|
858
|
+
window.openai.requestDisplayMode(size.displayMode);
|
|
859
|
+
}
|
|
860
|
+
if (window.openai && typeof window.openai.setWidgetHeight === 'function' && size && typeof size.height === 'number') {
|
|
861
|
+
window.openai.setWidgetHeight(size.height);
|
|
862
|
+
}
|
|
863
|
+
} catch (e) {}
|
|
864
|
+
return Promise.resolve();
|
|
865
|
+
},
|
|
436
866
|
requestClose: function(context) {
|
|
437
867
|
return Promise.resolve();
|
|
438
868
|
}
|
|
@@ -677,6 +1107,15 @@ var ExtAppsAdapter = {
|
|
|
677
1107
|
requestDisplayMode: function(context, mode) {
|
|
678
1108
|
return this.sendRequest('ui/setDisplayMode', { mode: mode });
|
|
679
1109
|
},
|
|
1110
|
+
setSize: function(context, size) {
|
|
1111
|
+
// FrontMCP sizing channel \u2014 parallels ui/setDisplayMode. Reports the
|
|
1112
|
+
// measured/desired widget dimensions to the host.
|
|
1113
|
+
return this.sendRequest('ui/setSize', {
|
|
1114
|
+
height: size && size.height,
|
|
1115
|
+
width: size && size.width,
|
|
1116
|
+
aspectRatio: size && size.aspectRatio
|
|
1117
|
+
});
|
|
1118
|
+
},
|
|
680
1119
|
requestClose: function(context) {
|
|
681
1120
|
return this.sendRequest('ui/close', {});
|
|
682
1121
|
},
|
|
@@ -765,6 +1204,10 @@ var ClaudeAdapter = {
|
|
|
765
1204
|
requestDisplayMode: function() {
|
|
766
1205
|
return Promise.resolve();
|
|
767
1206
|
},
|
|
1207
|
+
setSize: function() {
|
|
1208
|
+
// Claude measures the rendered DOM height itself \u2014 CSS-only, no reporting.
|
|
1209
|
+
return Promise.resolve();
|
|
1210
|
+
},
|
|
768
1211
|
requestClose: function() {
|
|
769
1212
|
return Promise.resolve();
|
|
770
1213
|
}
|
|
@@ -816,6 +1259,9 @@ var GeminiAdapter = {
|
|
|
816
1259
|
requestDisplayMode: function() {
|
|
817
1260
|
return Promise.resolve();
|
|
818
1261
|
},
|
|
1262
|
+
setSize: function() {
|
|
1263
|
+
return Promise.resolve();
|
|
1264
|
+
},
|
|
819
1265
|
requestClose: function() {
|
|
820
1266
|
return Promise.resolve();
|
|
821
1267
|
}
|
|
@@ -851,6 +1297,9 @@ var GenericAdapter = {
|
|
|
851
1297
|
requestDisplayMode: function() {
|
|
852
1298
|
return Promise.resolve();
|
|
853
1299
|
},
|
|
1300
|
+
setSize: function() {
|
|
1301
|
+
return Promise.resolve();
|
|
1302
|
+
},
|
|
854
1303
|
requestClose: function() {
|
|
855
1304
|
return Promise.resolve();
|
|
856
1305
|
}
|
|
@@ -1085,6 +1534,15 @@ FrontMcpBridge.prototype.requestDisplayMode = function(mode) {
|
|
|
1085
1534
|
});
|
|
1086
1535
|
};
|
|
1087
1536
|
|
|
1537
|
+
// Report a desired widget size to the host. \`size\` is { height?, width?, aspectRatio? }.
|
|
1538
|
+
// Per-adapter behaviour: Claude/generic no-op (host measures the DOM),
|
|
1539
|
+
// ext-apps sends ui/setSize, OpenAI forwards to its SDK when available.
|
|
1540
|
+
FrontMcpBridge.prototype.setSize = function(size) {
|
|
1541
|
+
if (!this._adapter) return Promise.reject(new Error('Not initialized'));
|
|
1542
|
+
if (!this._adapter.setSize) return Promise.resolve();
|
|
1543
|
+
return this._adapter.setSize(this._context, size || {});
|
|
1544
|
+
};
|
|
1545
|
+
|
|
1088
1546
|
FrontMcpBridge.prototype.requestClose = function() {
|
|
1089
1547
|
if (!this._adapter) return Promise.reject(new Error('Not initialized'));
|
|
1090
1548
|
return this._adapter.requestClose(this._context);
|
|
@@ -1283,564 +1741,404 @@ var BRIDGE_SCRIPT_TAGS = {
|
|
|
1283
1741
|
gemini: `<script>${generatePlatformBundle("gemini")}</script>`
|
|
1284
1742
|
};
|
|
1285
1743
|
|
|
1286
|
-
// libs/uipack/src/shell/
|
|
1287
|
-
var
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
return {
|
|
1307
|
-
valid: missingRequired.length === 0,
|
|
1308
|
-
found,
|
|
1309
|
-
missingRequired,
|
|
1310
|
-
missingOptional
|
|
1311
|
-
};
|
|
1312
|
-
}
|
|
1313
|
-
|
|
1314
|
-
// libs/uipack/src/shell/custom-shell-applier.ts
|
|
1315
|
-
function applyShellTemplate(template, values) {
|
|
1316
|
-
let result = template;
|
|
1317
|
-
result = result.replaceAll(SHELL_PLACEHOLDERS.CSP, values.csp);
|
|
1318
|
-
result = result.replaceAll(SHELL_PLACEHOLDERS.DATA, values.data);
|
|
1319
|
-
result = result.replaceAll(SHELL_PLACEHOLDERS.BRIDGE, values.bridge);
|
|
1320
|
-
result = result.replaceAll(SHELL_PLACEHOLDERS.CONTENT, values.content);
|
|
1321
|
-
result = result.replaceAll(SHELL_PLACEHOLDERS.TITLE, values.title);
|
|
1322
|
-
return result;
|
|
1323
|
-
}
|
|
1324
|
-
|
|
1325
|
-
// libs/uipack/src/shell/builder.ts
|
|
1326
|
-
function buildShell(content, config) {
|
|
1327
|
-
const { toolName, csp, withShell = true, input, output, structuredContent, includeBridge = true, title } = config;
|
|
1328
|
-
const { customShell } = config;
|
|
1329
|
-
const dataScript = buildDataInjectionScript({ toolName, input, output, structuredContent });
|
|
1330
|
-
if (!withShell) {
|
|
1331
|
-
const html2 = `${dataScript}
|
|
1332
|
-
${content}`;
|
|
1333
|
-
return {
|
|
1334
|
-
html: html2,
|
|
1335
|
-
hash: simpleHash(html2),
|
|
1336
|
-
size: Buffer.byteLength(html2, "utf-8")
|
|
1337
|
-
};
|
|
1338
|
-
}
|
|
1339
|
-
if (customShell) {
|
|
1340
|
-
return buildCustomShell(content, customShell, {
|
|
1341
|
-
csp,
|
|
1342
|
-
toolName,
|
|
1343
|
-
input,
|
|
1344
|
-
output,
|
|
1345
|
-
structuredContent,
|
|
1346
|
-
includeBridge,
|
|
1347
|
-
title
|
|
1348
|
-
});
|
|
1349
|
-
}
|
|
1350
|
-
const headParts = [
|
|
1351
|
-
'<meta charset="UTF-8">',
|
|
1352
|
-
'<meta name="viewport" content="width=device-width, initial-scale=1.0">'
|
|
1353
|
-
];
|
|
1354
|
-
if (title) {
|
|
1355
|
-
headParts.push(`<title>${escapeHtmlForTag(title)}</title>`);
|
|
1356
|
-
}
|
|
1357
|
-
headParts.push(buildCSPMetaTag(csp));
|
|
1358
|
-
headParts.push(dataScript);
|
|
1359
|
-
if (includeBridge) {
|
|
1360
|
-
const bridgeScript = generateBridgeIIFE({ minify: true });
|
|
1361
|
-
headParts.push(`<script>${bridgeScript}</script>`);
|
|
1362
|
-
}
|
|
1363
|
-
const html = `<!DOCTYPE html>
|
|
1364
|
-
<html lang="en">
|
|
1365
|
-
<head>
|
|
1366
|
-
${headParts.map((p) => ` ${p}`).join("\n")}
|
|
1367
|
-
</head>
|
|
1368
|
-
<body>
|
|
1369
|
-
${content}
|
|
1370
|
-
</body>
|
|
1371
|
-
</html>`;
|
|
1372
|
-
return {
|
|
1373
|
-
html,
|
|
1374
|
-
hash: simpleHash(html),
|
|
1375
|
-
size: Buffer.byteLength(html, "utf-8")
|
|
1376
|
-
};
|
|
1377
|
-
}
|
|
1378
|
-
function buildCustomShell(content, customShell, ctx) {
|
|
1379
|
-
let template;
|
|
1380
|
-
if (typeof customShell === "string") {
|
|
1381
|
-
const validation = validateShellTemplate(customShell);
|
|
1382
|
-
if (!validation.valid) {
|
|
1383
|
-
throw new Error(
|
|
1384
|
-
`Custom shell template is missing required placeholder(s): ${validation.missingRequired.map((n) => `{{${n}}}`).join(", ")}`
|
|
1385
|
-
);
|
|
1386
|
-
}
|
|
1387
|
-
template = customShell;
|
|
1388
|
-
} else {
|
|
1389
|
-
template = customShell.template;
|
|
1744
|
+
// libs/uipack/src/shell/csp.ts
|
|
1745
|
+
var DEFAULT_CDN_DOMAINS = [
|
|
1746
|
+
"https://cdn.jsdelivr.net",
|
|
1747
|
+
"https://cdnjs.cloudflare.com",
|
|
1748
|
+
"https://fonts.googleapis.com",
|
|
1749
|
+
"https://fonts.gstatic.com",
|
|
1750
|
+
"https://esm.sh"
|
|
1751
|
+
];
|
|
1752
|
+
var DEFAULT_CSP_DIRECTIVES = [
|
|
1753
|
+
"default-src 'none'",
|
|
1754
|
+
`script-src 'self' 'unsafe-inline' ${DEFAULT_CDN_DOMAINS.join(" ")}`,
|
|
1755
|
+
`style-src 'self' 'unsafe-inline' ${DEFAULT_CDN_DOMAINS.join(" ")}`,
|
|
1756
|
+
`img-src 'self' data: ${DEFAULT_CDN_DOMAINS.join(" ")}`,
|
|
1757
|
+
`font-src 'self' data: ${DEFAULT_CDN_DOMAINS.join(" ")}`,
|
|
1758
|
+
`connect-src ${DEFAULT_CDN_DOMAINS.join(" ")}`,
|
|
1759
|
+
"object-src 'self' data:"
|
|
1760
|
+
];
|
|
1761
|
+
function buildCSPDirectives(csp) {
|
|
1762
|
+
if (!csp) {
|
|
1763
|
+
return [...DEFAULT_CSP_DIRECTIVES];
|
|
1390
1764
|
}
|
|
1391
|
-
const
|
|
1392
|
-
const
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1765
|
+
const validResourceDomains = sanitizeCSPDomains(csp.resourceDomains);
|
|
1766
|
+
const validConnectDomains = sanitizeCSPDomains(csp.connectDomains);
|
|
1767
|
+
const allResourceDomains = [.../* @__PURE__ */ new Set([...DEFAULT_CDN_DOMAINS, ...validResourceDomains])];
|
|
1768
|
+
const directives = [
|
|
1769
|
+
"default-src 'none'",
|
|
1770
|
+
`script-src 'self' 'unsafe-inline' ${allResourceDomains.join(" ")}`,
|
|
1771
|
+
`style-src 'self' 'unsafe-inline' ${allResourceDomains.join(" ")}`
|
|
1772
|
+
];
|
|
1773
|
+
const imgSources = ["'self'", "data:", ...allResourceDomains];
|
|
1774
|
+
directives.push(`img-src ${imgSources.join(" ")}`);
|
|
1775
|
+
const fontSources = ["'self'", "data:", ...allResourceDomains];
|
|
1776
|
+
directives.push(`font-src ${fontSources.join(" ")}`);
|
|
1777
|
+
if (validConnectDomains.length) {
|
|
1778
|
+
directives.push(`connect-src ${validConnectDomains.join(" ")}`);
|
|
1779
|
+
} else {
|
|
1780
|
+
directives.push(`connect-src ${allResourceDomains.join(" ")}`);
|
|
1402
1781
|
}
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
data: dataScript,
|
|
1406
|
-
bridge: bridgeHtml,
|
|
1407
|
-
content,
|
|
1408
|
-
title: ctx.title ? escapeHtmlForTag(ctx.title) : ""
|
|
1409
|
-
});
|
|
1410
|
-
return {
|
|
1411
|
-
html,
|
|
1412
|
-
hash: simpleHash(html),
|
|
1413
|
-
size: Buffer.byteLength(html, "utf-8")
|
|
1414
|
-
};
|
|
1782
|
+
directives.push("object-src 'self' data:");
|
|
1783
|
+
return directives;
|
|
1415
1784
|
}
|
|
1416
|
-
function
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
hash = (hash << 5) - hash + char | 0;
|
|
1421
|
-
}
|
|
1422
|
-
return Math.abs(hash).toString(16).padStart(8, "0");
|
|
1785
|
+
function buildCSPMetaTag(csp) {
|
|
1786
|
+
const directives = buildCSPDirectives(csp);
|
|
1787
|
+
const content = directives.join("; ");
|
|
1788
|
+
return `<meta http-equiv="Content-Security-Policy" content="${escapeAttribute(content)}">`;
|
|
1423
1789
|
}
|
|
1424
|
-
function
|
|
1425
|
-
|
|
1790
|
+
function validateCSPDomain(domain) {
|
|
1791
|
+
if (domain.startsWith("https://*.")) {
|
|
1792
|
+
const rest = domain.slice(10);
|
|
1793
|
+
return /^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?\.[a-zA-Z]{2,}$/.test(rest);
|
|
1794
|
+
}
|
|
1795
|
+
try {
|
|
1796
|
+
const url = new URL(domain);
|
|
1797
|
+
return url.protocol === "https:";
|
|
1798
|
+
} catch {
|
|
1799
|
+
return false;
|
|
1800
|
+
}
|
|
1426
1801
|
}
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
url: "https://cdnjs.cloudflare.com/ajax/libs/react/19.2.4/umd/react.production.min.js",
|
|
1436
|
-
global: "React",
|
|
1437
|
-
crossorigin: "anonymous"
|
|
1438
|
-
},
|
|
1439
|
-
jsdelivr: {
|
|
1440
|
-
url: "https://cdn.jsdelivr.net/npm/react@19.2.4/umd/react.production.min.js",
|
|
1441
|
-
global: "React",
|
|
1442
|
-
crossorigin: "anonymous"
|
|
1443
|
-
},
|
|
1444
|
-
unpkg: {
|
|
1445
|
-
url: "https://unpkg.com/react@19.2.4/umd/react.production.min.js",
|
|
1446
|
-
global: "React",
|
|
1447
|
-
crossorigin: "anonymous"
|
|
1448
|
-
},
|
|
1449
|
-
"esm.sh": {
|
|
1450
|
-
url: "https://esm.sh/react@19.2.4",
|
|
1451
|
-
esm: true,
|
|
1452
|
-
crossorigin: "anonymous"
|
|
1453
|
-
}
|
|
1454
|
-
},
|
|
1455
|
-
preferredProviders: ["esm.sh", "cloudflare", "jsdelivr", "unpkg"],
|
|
1456
|
-
metadata: {
|
|
1457
|
-
description: "A JavaScript library for building user interfaces",
|
|
1458
|
-
homepage: "https://react.dev",
|
|
1459
|
-
license: "MIT"
|
|
1460
|
-
}
|
|
1461
|
-
},
|
|
1462
|
-
"react-dom": {
|
|
1463
|
-
packageName: "react-dom",
|
|
1464
|
-
defaultVersion: "19.2.4",
|
|
1465
|
-
providers: {
|
|
1466
|
-
cloudflare: {
|
|
1467
|
-
url: "https://cdnjs.cloudflare.com/ajax/libs/react-dom/19.2.4/umd/react-dom.production.min.js",
|
|
1468
|
-
global: "ReactDOM",
|
|
1469
|
-
crossorigin: "anonymous",
|
|
1470
|
-
peerDependencies: ["react"]
|
|
1471
|
-
},
|
|
1472
|
-
jsdelivr: {
|
|
1473
|
-
url: "https://cdn.jsdelivr.net/npm/react-dom@19.2.4/umd/react-dom.production.min.js",
|
|
1474
|
-
global: "ReactDOM",
|
|
1475
|
-
crossorigin: "anonymous",
|
|
1476
|
-
peerDependencies: ["react"]
|
|
1477
|
-
},
|
|
1478
|
-
unpkg: {
|
|
1479
|
-
url: "https://unpkg.com/react-dom@19.2.4/umd/react-dom.production.min.js",
|
|
1480
|
-
global: "ReactDOM",
|
|
1481
|
-
crossorigin: "anonymous",
|
|
1482
|
-
peerDependencies: ["react"]
|
|
1483
|
-
},
|
|
1484
|
-
"esm.sh": {
|
|
1485
|
-
url: "https://esm.sh/react-dom@19.2.4",
|
|
1486
|
-
esm: true,
|
|
1487
|
-
crossorigin: "anonymous",
|
|
1488
|
-
peerDependencies: ["react"]
|
|
1489
|
-
}
|
|
1490
|
-
},
|
|
1491
|
-
preferredProviders: ["esm.sh", "cloudflare", "jsdelivr", "unpkg"],
|
|
1492
|
-
metadata: {
|
|
1493
|
-
description: "React package for working with the DOM",
|
|
1494
|
-
homepage: "https://react.dev",
|
|
1495
|
-
license: "MIT"
|
|
1496
|
-
}
|
|
1497
|
-
},
|
|
1498
|
-
"chart.js": {
|
|
1499
|
-
packageName: "chart.js",
|
|
1500
|
-
defaultVersion: "4.5.1",
|
|
1501
|
-
providers: {
|
|
1502
|
-
cloudflare: {
|
|
1503
|
-
url: "https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.5.1/chart.umd.min.js",
|
|
1504
|
-
global: "Chart",
|
|
1505
|
-
crossorigin: "anonymous"
|
|
1506
|
-
},
|
|
1507
|
-
jsdelivr: {
|
|
1508
|
-
url: "https://cdn.jsdelivr.net/npm/chart.js@4.5.1/dist/chart.umd.min.js",
|
|
1509
|
-
global: "Chart",
|
|
1510
|
-
crossorigin: "anonymous"
|
|
1511
|
-
},
|
|
1512
|
-
unpkg: {
|
|
1513
|
-
url: "https://unpkg.com/chart.js@4.5.1/dist/chart.umd.min.js",
|
|
1514
|
-
global: "Chart",
|
|
1515
|
-
crossorigin: "anonymous"
|
|
1516
|
-
}
|
|
1517
|
-
},
|
|
1518
|
-
preferredProviders: ["esm.sh", "cloudflare", "jsdelivr", "unpkg"],
|
|
1519
|
-
metadata: {
|
|
1520
|
-
description: "Simple yet flexible JavaScript charting library",
|
|
1521
|
-
homepage: "https://www.chartjs.org",
|
|
1522
|
-
license: "MIT"
|
|
1523
|
-
}
|
|
1524
|
-
},
|
|
1525
|
-
d3: {
|
|
1526
|
-
packageName: "d3",
|
|
1527
|
-
defaultVersion: "7.9.0",
|
|
1528
|
-
providers: {
|
|
1529
|
-
cloudflare: {
|
|
1530
|
-
url: "https://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.min.js",
|
|
1531
|
-
integrity: "sha512-vc58qvvBdrDR4etbxMdlTt4GBQk1qjvyORR2nrsPsFPyrs+/u5c3+1Ct6upOgdZoIl7eq6k3a1UPDSNAQi/32A==",
|
|
1532
|
-
global: "d3",
|
|
1533
|
-
crossorigin: "anonymous"
|
|
1534
|
-
},
|
|
1535
|
-
jsdelivr: {
|
|
1536
|
-
url: "https://cdn.jsdelivr.net/npm/d3@7.9.0/dist/d3.min.js",
|
|
1537
|
-
global: "d3",
|
|
1538
|
-
crossorigin: "anonymous"
|
|
1539
|
-
},
|
|
1540
|
-
unpkg: {
|
|
1541
|
-
url: "https://unpkg.com/d3@7.9.0/dist/d3.min.js",
|
|
1542
|
-
global: "d3",
|
|
1543
|
-
crossorigin: "anonymous"
|
|
1544
|
-
}
|
|
1545
|
-
},
|
|
1546
|
-
preferredProviders: ["esm.sh", "cloudflare", "jsdelivr", "unpkg"],
|
|
1547
|
-
metadata: {
|
|
1548
|
-
description: "Data-Driven Documents",
|
|
1549
|
-
homepage: "https://d3js.org",
|
|
1550
|
-
license: "ISC"
|
|
1551
|
-
}
|
|
1552
|
-
},
|
|
1553
|
-
lodash: {
|
|
1554
|
-
packageName: "lodash",
|
|
1555
|
-
defaultVersion: "4.17.21",
|
|
1556
|
-
providers: {
|
|
1557
|
-
cloudflare: {
|
|
1558
|
-
url: "https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js",
|
|
1559
|
-
integrity: "sha512-WFN04846sdKMIP5LKNphMaWzU7YpMyCU245etK3g/2ARYbPK9Ub18eG+ljU96qKRCWh+quCY7yefSmlkQw1ANQ==",
|
|
1560
|
-
global: "_",
|
|
1561
|
-
crossorigin: "anonymous"
|
|
1562
|
-
},
|
|
1563
|
-
jsdelivr: {
|
|
1564
|
-
url: "https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js",
|
|
1565
|
-
global: "_",
|
|
1566
|
-
crossorigin: "anonymous"
|
|
1567
|
-
},
|
|
1568
|
-
unpkg: {
|
|
1569
|
-
url: "https://unpkg.com/lodash@4.17.21/lodash.min.js",
|
|
1570
|
-
global: "_",
|
|
1571
|
-
crossorigin: "anonymous"
|
|
1572
|
-
}
|
|
1573
|
-
},
|
|
1574
|
-
preferredProviders: ["esm.sh", "cloudflare", "jsdelivr", "unpkg"],
|
|
1575
|
-
metadata: {
|
|
1576
|
-
description: "A modern JavaScript utility library",
|
|
1577
|
-
homepage: "https://lodash.com",
|
|
1578
|
-
license: "MIT"
|
|
1802
|
+
function sanitizeCSPDomains(domains) {
|
|
1803
|
+
if (!domains) return [];
|
|
1804
|
+
const valid = [];
|
|
1805
|
+
for (const domain of domains) {
|
|
1806
|
+
if (validateCSPDomain(domain)) {
|
|
1807
|
+
valid.push(domain);
|
|
1808
|
+
} else {
|
|
1809
|
+
console.warn(`Invalid CSP domain ignored: ${domain}`);
|
|
1579
1810
|
}
|
|
1580
1811
|
}
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1812
|
+
return valid;
|
|
1813
|
+
}
|
|
1814
|
+
function escapeAttribute(str) {
|
|
1815
|
+
return str.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">");
|
|
1584
1816
|
}
|
|
1585
1817
|
|
|
1586
|
-
// libs/uipack/src/
|
|
1587
|
-
var
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
defaultAndNamespace: /import\s+(\w+)\s*,\s*\*\s*as\s+(\w+)\s+from\s*['"]([^'"]+)['"]/g,
|
|
1595
|
-
reExport: /export\s*\{[^}]+\}\s*from\s*['"]([^'"]+)['"]/g,
|
|
1596
|
-
reExportAll: /export\s*\*\s*from\s*['"]([^'"]+)['"]/g
|
|
1818
|
+
// libs/uipack/src/shell/custom-shell-types.ts
|
|
1819
|
+
var SHELL_PLACEHOLDER_NAMES = ["CSP", "DATA", "BRIDGE", "CONTENT", "TITLE"];
|
|
1820
|
+
var SHELL_PLACEHOLDERS = {
|
|
1821
|
+
CSP: "{{CSP}}",
|
|
1822
|
+
DATA: "{{DATA}}",
|
|
1823
|
+
BRIDGE: "{{BRIDGE}}",
|
|
1824
|
+
CONTENT: "{{CONTENT}}",
|
|
1825
|
+
TITLE: "{{TITLE}}"
|
|
1597
1826
|
};
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1827
|
+
var REQUIRED_PLACEHOLDERS = ["CONTENT"];
|
|
1828
|
+
var OPTIONAL_PLACEHOLDERS = ["CSP", "DATA", "BRIDGE", "TITLE"];
|
|
1829
|
+
|
|
1830
|
+
// libs/uipack/src/shell/custom-shell-applier.ts
|
|
1831
|
+
function applyShellTemplate(template, values) {
|
|
1832
|
+
let result = template;
|
|
1833
|
+
result = result.replaceAll(SHELL_PLACEHOLDERS.CSP, values.csp);
|
|
1834
|
+
result = result.replaceAll(SHELL_PLACEHOLDERS.DATA, values.data);
|
|
1835
|
+
result = result.replaceAll(SHELL_PLACEHOLDERS.BRIDGE, values.bridge);
|
|
1836
|
+
result = result.replaceAll(SHELL_PLACEHOLDERS.CONTENT, values.content);
|
|
1837
|
+
result = result.replaceAll(SHELL_PLACEHOLDERS.TITLE, values.title);
|
|
1838
|
+
return result;
|
|
1603
1839
|
}
|
|
1604
|
-
|
|
1605
|
-
|
|
1840
|
+
|
|
1841
|
+
// libs/uipack/src/shell/custom-shell-validator.ts
|
|
1842
|
+
function validateShellTemplate(template) {
|
|
1843
|
+
const found = {};
|
|
1844
|
+
for (const name of SHELL_PLACEHOLDER_NAMES) {
|
|
1845
|
+
found[name] = template.includes(SHELL_PLACEHOLDERS[name]);
|
|
1846
|
+
}
|
|
1847
|
+
const missingRequired = REQUIRED_PLACEHOLDERS.filter((name) => !found[name]);
|
|
1848
|
+
const missingOptional = OPTIONAL_PLACEHOLDERS.filter((name) => !found[name]);
|
|
1849
|
+
return {
|
|
1850
|
+
valid: missingRequired.length === 0,
|
|
1851
|
+
found,
|
|
1852
|
+
missingRequired,
|
|
1853
|
+
missingOptional
|
|
1854
|
+
};
|
|
1606
1855
|
}
|
|
1607
|
-
|
|
1608
|
-
|
|
1856
|
+
|
|
1857
|
+
// libs/uipack/src/shell/data-injector.ts
|
|
1858
|
+
function hasSizing(sizing) {
|
|
1859
|
+
if (!sizing) return false;
|
|
1860
|
+
return sizing.preferredHeight !== void 0 || sizing.minHeight !== void 0 || sizing.maxHeight !== void 0 || sizing.aspectRatio !== void 0 || sizing.autoResize !== void 0;
|
|
1609
1861
|
}
|
|
1610
|
-
function
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
}
|
|
1616
|
-
|
|
1862
|
+
function buildDataInjectionScript(options) {
|
|
1863
|
+
const { toolName, input, output, structuredContent, sizing } = options;
|
|
1864
|
+
const lines = [
|
|
1865
|
+
`window.__mcpAppsEnabled = true;`,
|
|
1866
|
+
`window.__mcpToolName = ${safeJsonForScript(toolName)};`,
|
|
1867
|
+
`window.__mcpToolInput = ${safeJsonForScript(input ?? null)};`,
|
|
1868
|
+
`window.__mcpToolOutput = ${safeJsonForScript(output ?? null)};`,
|
|
1869
|
+
`window.__mcpStructuredContent = ${safeJsonForScript(structuredContent ?? null)};`
|
|
1870
|
+
];
|
|
1871
|
+
if (hasSizing(sizing)) {
|
|
1872
|
+
lines.push(`window.__mcpWidgetSizing = ${safeJsonForScript(sizing)};`);
|
|
1617
1873
|
}
|
|
1618
|
-
|
|
1619
|
-
|
|
1874
|
+
return `<script>
|
|
1875
|
+
${lines.join("\n")}
|
|
1876
|
+
</script>`;
|
|
1620
1877
|
}
|
|
1621
|
-
function
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
if (source[i] === "\n") {
|
|
1625
|
-
line++;
|
|
1626
|
-
}
|
|
1878
|
+
function buildCustomDataInjectionScript(descriptor) {
|
|
1879
|
+
if (descriptor.script !== void 0) {
|
|
1880
|
+
return descriptor.script;
|
|
1627
1881
|
}
|
|
1628
|
-
|
|
1629
|
-
}
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
for (let i = index - 1; i >= 0 && source[i] !== "\n"; i--) {
|
|
1633
|
-
column++;
|
|
1882
|
+
if (descriptor.globalKey !== void 0) {
|
|
1883
|
+
return `<script>window[${safeJsonForScript(descriptor.globalKey)}] = ${safeJsonForScript(
|
|
1884
|
+
descriptor.value ?? null
|
|
1885
|
+
)};</script>`;
|
|
1634
1886
|
}
|
|
1635
|
-
return
|
|
1887
|
+
return "";
|
|
1636
1888
|
}
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1889
|
+
var _uniqueIdCounter = 0;
|
|
1890
|
+
function createTemplateHelpers() {
|
|
1891
|
+
return {
|
|
1892
|
+
escapeHtml: (str) => escapeHtml(str),
|
|
1893
|
+
formatDate: (date, format) => {
|
|
1894
|
+
const d = date instanceof Date ? date : new Date(date);
|
|
1895
|
+
if (isNaN(d.getTime())) return String(date);
|
|
1896
|
+
if (format === "iso") return d.toISOString();
|
|
1897
|
+
if (format === "date") return d.toLocaleDateString();
|
|
1898
|
+
if (format === "time") return d.toLocaleTimeString();
|
|
1899
|
+
return d.toLocaleString();
|
|
1900
|
+
},
|
|
1901
|
+
formatCurrency: (amount, currency = "USD") => {
|
|
1902
|
+
return new Intl.NumberFormat("en-US", {
|
|
1903
|
+
style: "currency",
|
|
1904
|
+
currency
|
|
1905
|
+
}).format(amount);
|
|
1906
|
+
},
|
|
1907
|
+
uniqueId: (prefix = "mcp") => {
|
|
1908
|
+
return `${prefix}-${++_uniqueIdCounter}`;
|
|
1909
|
+
},
|
|
1910
|
+
jsonEmbed: (data) => {
|
|
1911
|
+
return escapeScriptClose(JSON.stringify(data));
|
|
1645
1912
|
}
|
|
1646
1913
|
};
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
// libs/uipack/src/shell/sizing-css.ts
|
|
1917
|
+
function sanitizeCssValue(value) {
|
|
1918
|
+
return value.replace(/[<>{};]/g, "").trim();
|
|
1919
|
+
}
|
|
1920
|
+
function toCssLength(value) {
|
|
1921
|
+
return typeof value === "number" ? `${value}px` : sanitizeCssValue(value);
|
|
1922
|
+
}
|
|
1923
|
+
function buildSizingStyleTag(sizing) {
|
|
1924
|
+
if (!hasSizing(sizing)) return "";
|
|
1925
|
+
const hasCssSizing = sizing.preferredHeight !== void 0 || sizing.minHeight !== void 0 || sizing.maxHeight !== void 0 || sizing.aspectRatio !== void 0;
|
|
1926
|
+
if (!hasCssSizing) return "";
|
|
1927
|
+
const rootRules = [];
|
|
1928
|
+
const docRules = ["margin: 0;"];
|
|
1929
|
+
if (sizing.preferredHeight !== void 0) {
|
|
1930
|
+
const h = toCssLength(sizing.preferredHeight);
|
|
1931
|
+
docRules.push(`height: ${h};`);
|
|
1932
|
+
rootRules.push(`min-height: ${h};`);
|
|
1660
1933
|
}
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
statement,
|
|
1666
|
-
specifier,
|
|
1667
|
-
type: "default",
|
|
1668
|
-
defaultImport: defaultName,
|
|
1669
|
-
namespaceImport: namespaceName,
|
|
1670
|
-
line: getLineNumber(source, match.index),
|
|
1671
|
-
column: getColumnNumber(source, match.index)
|
|
1672
|
-
});
|
|
1934
|
+
if (sizing.minHeight !== void 0) {
|
|
1935
|
+
const mh = toCssLength(sizing.minHeight);
|
|
1936
|
+
docRules.push(`min-height: ${mh};`);
|
|
1937
|
+
rootRules.push(`min-height: ${mh};`);
|
|
1673
1938
|
}
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
statement,
|
|
1679
|
-
specifier,
|
|
1680
|
-
type: "named",
|
|
1681
|
-
namedImports: parseNamedImports(namedString),
|
|
1682
|
-
line: getLineNumber(source, match.index),
|
|
1683
|
-
column: getColumnNumber(source, match.index)
|
|
1684
|
-
});
|
|
1939
|
+
if (sizing.maxHeight !== void 0) {
|
|
1940
|
+
const mx = toCssLength(sizing.maxHeight);
|
|
1941
|
+
docRules.push(`max-height: ${mx};`);
|
|
1942
|
+
rootRules.push(`max-height: ${mx};`);
|
|
1685
1943
|
}
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
const afterMatch = source.slice(match.index + match[0].length - specifier.length - 2);
|
|
1690
|
-
if (afterMatch.startsWith(",")) continue;
|
|
1691
|
-
addImport({
|
|
1692
|
-
statement,
|
|
1693
|
-
specifier,
|
|
1694
|
-
type: "default",
|
|
1695
|
-
defaultImport: defaultName,
|
|
1696
|
-
line: getLineNumber(source, match.index),
|
|
1697
|
-
column: getColumnNumber(source, match.index)
|
|
1698
|
-
});
|
|
1944
|
+
if (sizing.aspectRatio !== void 0) {
|
|
1945
|
+
const ar = typeof sizing.aspectRatio === "number" ? String(sizing.aspectRatio) : sanitizeCssValue(sizing.aspectRatio);
|
|
1946
|
+
if (ar) rootRules.push(`aspect-ratio: ${ar};`);
|
|
1699
1947
|
}
|
|
1700
|
-
const
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
addImport({
|
|
1704
|
-
statement,
|
|
1705
|
-
specifier,
|
|
1706
|
-
type: "namespace",
|
|
1707
|
-
namespaceImport: namespaceName,
|
|
1708
|
-
line: getLineNumber(source, match.index),
|
|
1709
|
-
column: getColumnNumber(source, match.index)
|
|
1710
|
-
});
|
|
1948
|
+
const parts = [`html, body { ${docRules.join(" ")} }`];
|
|
1949
|
+
if (rootRules.length > 0) {
|
|
1950
|
+
parts.push(`#root { ${rootRules.join(" ")} }`);
|
|
1711
1951
|
}
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1952
|
+
return `<style>${parts.join("\n")}</style>`;
|
|
1953
|
+
}
|
|
1954
|
+
|
|
1955
|
+
// libs/uipack/src/shell/builder.ts
|
|
1956
|
+
function resolveDataInjectionScript(args) {
|
|
1957
|
+
if (args.dataInjection) {
|
|
1958
|
+
return buildCustomDataInjectionScript(args.dataInjection);
|
|
1959
|
+
}
|
|
1960
|
+
return buildDataInjectionScript({
|
|
1961
|
+
toolName: args.toolName,
|
|
1962
|
+
input: args.input,
|
|
1963
|
+
output: args.output,
|
|
1964
|
+
structuredContent: args.structuredContent,
|
|
1965
|
+
sizing: args.sizing
|
|
1966
|
+
});
|
|
1967
|
+
}
|
|
1968
|
+
function buildShell(content, config) {
|
|
1969
|
+
const {
|
|
1970
|
+
toolName,
|
|
1971
|
+
csp,
|
|
1972
|
+
withShell = true,
|
|
1973
|
+
input,
|
|
1974
|
+
output,
|
|
1975
|
+
structuredContent,
|
|
1976
|
+
includeBridge = true,
|
|
1977
|
+
title,
|
|
1978
|
+
sizing
|
|
1979
|
+
} = config;
|
|
1980
|
+
const { customShell, dataInjection } = config;
|
|
1981
|
+
const dataScript = resolveDataInjectionScript({
|
|
1982
|
+
dataInjection,
|
|
1983
|
+
toolName,
|
|
1984
|
+
input,
|
|
1985
|
+
output,
|
|
1986
|
+
structuredContent,
|
|
1987
|
+
sizing
|
|
1988
|
+
});
|
|
1989
|
+
if (!withShell) {
|
|
1990
|
+
const html2 = `${dataScript}
|
|
1991
|
+
${content}`;
|
|
1992
|
+
return {
|
|
1993
|
+
html: html2,
|
|
1994
|
+
hash: simpleHash(html2),
|
|
1995
|
+
size: Buffer.byteLength(html2, "utf-8")
|
|
1996
|
+
};
|
|
1724
1997
|
}
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1998
|
+
if (customShell) {
|
|
1999
|
+
return buildCustomShell(content, customShell, {
|
|
2000
|
+
csp,
|
|
2001
|
+
toolName,
|
|
2002
|
+
input,
|
|
2003
|
+
output,
|
|
2004
|
+
structuredContent,
|
|
2005
|
+
includeBridge,
|
|
2006
|
+
title,
|
|
2007
|
+
sizing,
|
|
2008
|
+
dataInjection
|
|
1734
2009
|
});
|
|
1735
2010
|
}
|
|
1736
|
-
const
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
type: "named",
|
|
1743
|
-
line: getLineNumber(source, match.index),
|
|
1744
|
-
column: getColumnNumber(source, match.index)
|
|
1745
|
-
});
|
|
2011
|
+
const headParts = [
|
|
2012
|
+
'<meta charset="UTF-8">',
|
|
2013
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1.0">'
|
|
2014
|
+
];
|
|
2015
|
+
if (title) {
|
|
2016
|
+
headParts.push(`<title>${escapeHtmlForTag(title)}</title>`);
|
|
1746
2017
|
}
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
specifier,
|
|
1753
|
-
type: "namespace",
|
|
1754
|
-
line: getLineNumber(source, match.index),
|
|
1755
|
-
column: getColumnNumber(source, match.index)
|
|
1756
|
-
});
|
|
2018
|
+
headParts.push(buildCSPMetaTag(csp));
|
|
2019
|
+
headParts.push(dataScript);
|
|
2020
|
+
const sizingStyle = buildSizingStyleTag(sizing);
|
|
2021
|
+
if (sizingStyle) {
|
|
2022
|
+
headParts.push(sizingStyle);
|
|
1757
2023
|
}
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
2024
|
+
if (includeBridge) {
|
|
2025
|
+
const bridgeScript = generateBridgeIIFE({ minify: true });
|
|
2026
|
+
headParts.push(`<script>${bridgeScript}</script>`);
|
|
2027
|
+
}
|
|
2028
|
+
const html = `<!DOCTYPE html>
|
|
2029
|
+
<html lang="en">
|
|
2030
|
+
<head>
|
|
2031
|
+
${headParts.map((p) => ` ${p}`).join("\n")}
|
|
2032
|
+
</head>
|
|
2033
|
+
<body>
|
|
2034
|
+
${content}
|
|
2035
|
+
</body>
|
|
2036
|
+
</html>`;
|
|
1761
2037
|
return {
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
externalPackages
|
|
2038
|
+
html,
|
|
2039
|
+
hash: simpleHash(html),
|
|
2040
|
+
size: Buffer.byteLength(html, "utf-8")
|
|
1766
2041
|
};
|
|
1767
2042
|
}
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
} = options;
|
|
1777
|
-
return {
|
|
1778
|
-
resolve(specifier, _context) {
|
|
1779
|
-
if (specifier.startsWith("./") || specifier.startsWith("../") || specifier.startsWith("/")) {
|
|
1780
|
-
return null;
|
|
1781
|
-
}
|
|
1782
|
-
if (specifier.startsWith("node:") || specifier.startsWith("#")) {
|
|
1783
|
-
return null;
|
|
1784
|
-
}
|
|
1785
|
-
const pkgName = getPackageName(specifier);
|
|
1786
|
-
const entry = lookupPackage(pkgName, registry);
|
|
1787
|
-
if (entry) {
|
|
1788
|
-
for (const provider of providerOrder) {
|
|
1789
|
-
const config = entry.providers[provider];
|
|
1790
|
-
if (config?.url) {
|
|
1791
|
-
const subpath = specifier.slice(pkgName.length);
|
|
1792
|
-
if (subpath && config.url.includes("esm.sh/")) {
|
|
1793
|
-
return {
|
|
1794
|
-
value: config.url + subpath,
|
|
1795
|
-
type: "url",
|
|
1796
|
-
integrity: config.integrity
|
|
1797
|
-
};
|
|
1798
|
-
}
|
|
1799
|
-
if (subpath) {
|
|
1800
|
-
return {
|
|
1801
|
-
value: `${fallbackCdnBase}/${specifier}`,
|
|
1802
|
-
type: "url"
|
|
1803
|
-
};
|
|
1804
|
-
}
|
|
1805
|
-
if (config.global && !config.esm) {
|
|
1806
|
-
return {
|
|
1807
|
-
value: config.url,
|
|
1808
|
-
type: "url",
|
|
1809
|
-
integrity: config.integrity
|
|
1810
|
-
};
|
|
1811
|
-
}
|
|
1812
|
-
return {
|
|
1813
|
-
value: config.url,
|
|
1814
|
-
type: "url",
|
|
1815
|
-
integrity: config.integrity
|
|
1816
|
-
};
|
|
1817
|
-
}
|
|
1818
|
-
}
|
|
1819
|
-
}
|
|
1820
|
-
return {
|
|
1821
|
-
value: `${fallbackCdnBase}/${specifier}`,
|
|
1822
|
-
type: "url"
|
|
1823
|
-
};
|
|
2043
|
+
function buildCustomShell(content, customShell, ctx) {
|
|
2044
|
+
let template;
|
|
2045
|
+
if (typeof customShell === "string") {
|
|
2046
|
+
const validation = validateShellTemplate(customShell);
|
|
2047
|
+
if (!validation.valid) {
|
|
2048
|
+
throw new Error(
|
|
2049
|
+
`Custom shell template is missing required placeholder(s): ${validation.missingRequired.map((n) => `{{${n}}}`).join(", ")}`
|
|
2050
|
+
);
|
|
1824
2051
|
}
|
|
2052
|
+
template = customShell;
|
|
2053
|
+
} else {
|
|
2054
|
+
template = customShell.template;
|
|
2055
|
+
}
|
|
2056
|
+
const cspTag = buildCSPMetaTag(ctx.csp);
|
|
2057
|
+
const dataScript = resolveDataInjectionScript({
|
|
2058
|
+
dataInjection: ctx.dataInjection,
|
|
2059
|
+
toolName: ctx.toolName,
|
|
2060
|
+
input: ctx.input,
|
|
2061
|
+
output: ctx.output,
|
|
2062
|
+
structuredContent: ctx.structuredContent,
|
|
2063
|
+
sizing: ctx.sizing
|
|
2064
|
+
});
|
|
2065
|
+
const sizingStyle = buildSizingStyleTag(ctx.sizing);
|
|
2066
|
+
const dataWithSizing = sizingStyle ? `${dataScript}
|
|
2067
|
+
${sizingStyle}` : dataScript;
|
|
2068
|
+
let bridgeHtml = "";
|
|
2069
|
+
if (ctx.includeBridge) {
|
|
2070
|
+
const bridgeScript = generateBridgeIIFE({ minify: true });
|
|
2071
|
+
bridgeHtml = `<script>${bridgeScript}</script>`;
|
|
2072
|
+
}
|
|
2073
|
+
const html = applyShellTemplate(template, {
|
|
2074
|
+
csp: cspTag,
|
|
2075
|
+
data: dataWithSizing,
|
|
2076
|
+
bridge: bridgeHtml,
|
|
2077
|
+
content,
|
|
2078
|
+
title: ctx.title ? escapeHtmlForTag(ctx.title) : ""
|
|
2079
|
+
});
|
|
2080
|
+
return {
|
|
2081
|
+
html,
|
|
2082
|
+
hash: simpleHash(html),
|
|
2083
|
+
size: Buffer.byteLength(html, "utf-8")
|
|
1825
2084
|
};
|
|
1826
2085
|
}
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
return
|
|
2086
|
+
function simpleHash(str) {
|
|
2087
|
+
let hash = 0;
|
|
2088
|
+
for (let i = 0; i < str.length; i++) {
|
|
2089
|
+
const char = str.charCodeAt(i);
|
|
2090
|
+
hash = (hash << 5) - hash + char | 0;
|
|
2091
|
+
}
|
|
2092
|
+
return Math.abs(hash).toString(16).padStart(8, "0");
|
|
1834
2093
|
}
|
|
1835
|
-
function
|
|
1836
|
-
return
|
|
2094
|
+
function escapeHtmlForTag(str) {
|
|
2095
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
1837
2096
|
}
|
|
1838
|
-
|
|
1839
|
-
|
|
2097
|
+
|
|
2098
|
+
// libs/uipack/src/component/ui-availability.ts
|
|
2099
|
+
function isFrontmcpUiResolvable(...candidatePaths) {
|
|
2100
|
+
try {
|
|
2101
|
+
const nodeFs = require("fs");
|
|
2102
|
+
const nodePath = require("path");
|
|
2103
|
+
const ORG = "@frontmcp";
|
|
2104
|
+
const PKG = "ui";
|
|
2105
|
+
for (const candidate of candidatePaths) {
|
|
2106
|
+
let dir = candidate;
|
|
2107
|
+
while (true) {
|
|
2108
|
+
const pkgJson = nodePath.join(dir, "node_modules", ORG, PKG, "package.json");
|
|
2109
|
+
if (nodeFs.existsSync(pkgJson)) return true;
|
|
2110
|
+
const parent = nodePath.dirname(dir);
|
|
2111
|
+
if (parent === dir) break;
|
|
2112
|
+
dir = parent;
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
return false;
|
|
2116
|
+
} catch {
|
|
2117
|
+
return false;
|
|
2118
|
+
}
|
|
1840
2119
|
}
|
|
1841
2120
|
|
|
1842
2121
|
// libs/uipack/src/component/transpiler.ts
|
|
1843
|
-
function
|
|
2122
|
+
function transpileReactSource(source, filename) {
|
|
2123
|
+
const esbuild = require("esbuild");
|
|
2124
|
+
const loader = filename?.endsWith(".tsx") ? "tsx" : "jsx";
|
|
2125
|
+
const result = esbuild.transformSync(source, {
|
|
2126
|
+
loader,
|
|
2127
|
+
jsx: "transform",
|
|
2128
|
+
jsxFactory: "React.createElement",
|
|
2129
|
+
jsxFragment: "React.Fragment",
|
|
2130
|
+
format: "esm",
|
|
2131
|
+
target: "es2020",
|
|
2132
|
+
define: { "process.env.NODE_ENV": '"production"' }
|
|
2133
|
+
});
|
|
2134
|
+
return result.code;
|
|
2135
|
+
}
|
|
2136
|
+
function bundleFileSource(source, filename, resolveDir, componentName, options = {}) {
|
|
2137
|
+
if (!isFrontmcpUiResolvable(resolveDir, process.cwd())) {
|
|
2138
|
+
throw new Error(
|
|
2139
|
+
`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.`
|
|
2140
|
+
);
|
|
2141
|
+
}
|
|
1844
2142
|
const esbuild = require("esbuild");
|
|
1845
2143
|
const mountCode = `
|
|
1846
2144
|
// --- Auto-generated mount ---
|
|
@@ -1922,7 +2220,11 @@ if (__root) {
|
|
|
1922
2220
|
format: "esm",
|
|
1923
2221
|
target: "es2020",
|
|
1924
2222
|
jsx: "automatic",
|
|
1925
|
-
|
|
2223
|
+
// When `bundleReact` is set (resourceMode: 'inline'), React itself is
|
|
2224
|
+
// bundled — required for hosts that block external script execution
|
|
2225
|
+
// such as Claude (#454). Otherwise React stays external and is loaded
|
|
2226
|
+
// via the import map emitted by the renderer.
|
|
2227
|
+
external: options.bundleReact ? [] : ["react", "react-dom", "react/jsx-runtime", "react/jsx-dev-runtime"],
|
|
1926
2228
|
alias,
|
|
1927
2229
|
define: { "process.env.NODE_ENV": '"production"' },
|
|
1928
2230
|
platform: "browser",
|
|
@@ -1934,7 +2236,7 @@ if (__root) {
|
|
|
1934
2236
|
} catch (err) {
|
|
1935
2237
|
const message = err instanceof Error ? err.message : String(err);
|
|
1936
2238
|
throw new Error(
|
|
1937
|
-
`Failed to bundle FileSource "${filename}": ${message}.
|
|
2239
|
+
`Failed to bundle FileSource "${filename}": ${message}. If the error mentions @frontmcp/ui or @frontmcp/uipack, ensure both packages are installed in the consuming project.`
|
|
1938
2240
|
);
|
|
1939
2241
|
}
|
|
1940
2242
|
}
|
|
@@ -1946,12 +2248,29 @@ function extractDefaultExportName(code) {
|
|
|
1946
2248
|
return null;
|
|
1947
2249
|
}
|
|
1948
2250
|
|
|
2251
|
+
// libs/uipack/src/component/types.ts
|
|
2252
|
+
function isNpmSource(source) {
|
|
2253
|
+
return typeof source === "object" && source !== null && "npm" in source;
|
|
2254
|
+
}
|
|
2255
|
+
function isFileSource(source) {
|
|
2256
|
+
return typeof source === "object" && source !== null && "file" in source;
|
|
2257
|
+
}
|
|
2258
|
+
function isImportSource(source) {
|
|
2259
|
+
return typeof source === "object" && source !== null && "import" in source;
|
|
2260
|
+
}
|
|
2261
|
+
function isFunctionSource(source) {
|
|
2262
|
+
return typeof source === "function";
|
|
2263
|
+
}
|
|
2264
|
+
|
|
1949
2265
|
// libs/uipack/src/component/loader.ts
|
|
1950
2266
|
var DEFAULT_META = {
|
|
1951
2267
|
mcpAware: false,
|
|
1952
2268
|
renderer: "auto"
|
|
1953
2269
|
};
|
|
1954
2270
|
function resolveUISource(source, options) {
|
|
2271
|
+
if (options?.inlineReact && options?.transformOnly) {
|
|
2272
|
+
throw new Error("resolveUISource: `inlineReact` and `transformOnly` are mutually exclusive \u2014 set at most one.");
|
|
2273
|
+
}
|
|
1955
2274
|
const resolver = options?.resolver ?? createEsmShResolver();
|
|
1956
2275
|
if (isNpmSource(source)) {
|
|
1957
2276
|
return resolveNpmSource(source, resolver);
|
|
@@ -1960,7 +2279,7 @@ function resolveUISource(source, options) {
|
|
|
1960
2279
|
return resolveImportSource(source);
|
|
1961
2280
|
}
|
|
1962
2281
|
if (isFileSource(source)) {
|
|
1963
|
-
return resolveFileSource(source);
|
|
2282
|
+
return resolveFileSource(source, { inlineReact: options?.inlineReact, transformOnly: options?.transformOnly });
|
|
1964
2283
|
}
|
|
1965
2284
|
if (isFunctionSource(source)) {
|
|
1966
2285
|
return resolveFunctionSource(source, options?.input, options?.output);
|
|
@@ -1993,15 +2312,42 @@ function resolveImportSource(source) {
|
|
|
1993
2312
|
peerDependencies: []
|
|
1994
2313
|
};
|
|
1995
2314
|
}
|
|
1996
|
-
function resolveFileSource(source) {
|
|
2315
|
+
function resolveFileSource(source, options = {}) {
|
|
1997
2316
|
const path = require("path");
|
|
1998
2317
|
const ext = path.extname(source.file).toLowerCase();
|
|
1999
2318
|
if (ext === ".tsx" || ext === ".jsx") {
|
|
2000
2319
|
const fs = require("fs");
|
|
2001
|
-
const
|
|
2002
|
-
const
|
|
2320
|
+
const wasRelative = !path.isAbsolute(source.file);
|
|
2321
|
+
const filePath = wasRelative ? path.resolve(process.cwd(), source.file) : source.file;
|
|
2322
|
+
let rawSource;
|
|
2323
|
+
try {
|
|
2324
|
+
rawSource = fs.readFileSync(filePath, "utf-8");
|
|
2325
|
+
} catch (err) {
|
|
2326
|
+
const isNotFound = err?.code === "ENOENT";
|
|
2327
|
+
if (isNotFound && wasRelative) {
|
|
2328
|
+
throw new Error(
|
|
2329
|
+
`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).`
|
|
2330
|
+
);
|
|
2331
|
+
}
|
|
2332
|
+
throw err;
|
|
2333
|
+
}
|
|
2003
2334
|
const componentName = source.exportName || extractDefaultExportName(rawSource) || "Component";
|
|
2004
|
-
|
|
2335
|
+
if (options.transformOnly === true) {
|
|
2336
|
+
const code = transpileReactSource(rawSource, path.basename(filePath));
|
|
2337
|
+
const parsed2 = parseImports(code);
|
|
2338
|
+
return {
|
|
2339
|
+
mode: "module",
|
|
2340
|
+
code,
|
|
2341
|
+
imports: [...new Set(parsed2.externalImports.map((i) => i.specifier))],
|
|
2342
|
+
exportName: componentName,
|
|
2343
|
+
meta: { mcpAware: true, renderer: "react" },
|
|
2344
|
+
peerDependencies: source.peerDependencies || ["react", "react-dom"],
|
|
2345
|
+
bundled: false
|
|
2346
|
+
};
|
|
2347
|
+
}
|
|
2348
|
+
const bundled = bundleFileSource(rawSource, source.file, path.dirname(filePath), componentName, {
|
|
2349
|
+
bundleReact: options.inlineReact === true
|
|
2350
|
+
});
|
|
2005
2351
|
const parsed = parseImports(bundled.code);
|
|
2006
2352
|
return {
|
|
2007
2353
|
mode: "module",
|
|
@@ -2069,37 +2415,19 @@ function generateMappedPropsCode(mapping) {
|
|
|
2069
2415
|
return `({ ${entries} })`;
|
|
2070
2416
|
}
|
|
2071
2417
|
|
|
2072
|
-
// libs/uipack/src/resolver/import-map.ts
|
|
2073
|
-
function createImportMapFromResolved(resolved) {
|
|
2074
|
-
const imports = {};
|
|
2075
|
-
const integrity = {};
|
|
2076
|
-
for (const [specifier, res] of Object.entries(resolved)) {
|
|
2077
|
-
if (res.type === "url") {
|
|
2078
|
-
imports[specifier] = res.value;
|
|
2079
|
-
if (res.integrity) {
|
|
2080
|
-
integrity[res.value] = res.integrity;
|
|
2081
|
-
}
|
|
2082
|
-
}
|
|
2083
|
-
}
|
|
2084
|
-
return {
|
|
2085
|
-
imports,
|
|
2086
|
-
integrity: Object.keys(integrity).length > 0 ? integrity : void 0
|
|
2087
|
-
};
|
|
2088
|
-
}
|
|
2089
|
-
function generateImportMapScriptTag(map) {
|
|
2090
|
-
const json = JSON.stringify(map, null, 2).replace(/<\//g, "<\\/");
|
|
2091
|
-
return `<script type="importmap">
|
|
2092
|
-
${json}
|
|
2093
|
-
</script>`;
|
|
2094
|
-
}
|
|
2095
|
-
|
|
2096
2418
|
// libs/uipack/src/component/renderer.ts
|
|
2419
|
+
var DEFAULT_WIDGET_MOUNT = {
|
|
2420
|
+
moduleSpecifier: "@frontmcp/ui/react",
|
|
2421
|
+
wrapperImportName: "McpBridgeProvider"
|
|
2422
|
+
};
|
|
2097
2423
|
function renderComponent(config, shellConfig) {
|
|
2098
2424
|
const resolver = shellConfig.resolver ?? createEsmShResolver();
|
|
2099
2425
|
const resolved = resolveUISource(config.source, {
|
|
2100
2426
|
resolver,
|
|
2101
2427
|
input: shellConfig.input,
|
|
2102
|
-
output: shellConfig.output
|
|
2428
|
+
output: shellConfig.output,
|
|
2429
|
+
inlineReact: config.inlineReact === true,
|
|
2430
|
+
transformOnly: config.transformOnly === true
|
|
2103
2431
|
});
|
|
2104
2432
|
const mergedShellConfig = {
|
|
2105
2433
|
...shellConfig,
|
|
@@ -2110,10 +2438,10 @@ function renderComponent(config, shellConfig) {
|
|
|
2110
2438
|
if (resolved.mode === "inline") {
|
|
2111
2439
|
return buildShell(resolved.html ?? "", mergedShellConfig);
|
|
2112
2440
|
}
|
|
2113
|
-
const content = buildModuleContent(resolved, resolver, config.props);
|
|
2441
|
+
const content = buildModuleContent(resolved, resolver, config.props, shellConfig.mount);
|
|
2114
2442
|
return buildShell(content, mergedShellConfig);
|
|
2115
2443
|
}
|
|
2116
|
-
function buildModuleContent(resolved, resolver, propsMapping) {
|
|
2444
|
+
function buildModuleContent(resolved, resolver, propsMapping, mount = DEFAULT_WIDGET_MOUNT) {
|
|
2117
2445
|
const parts = [];
|
|
2118
2446
|
if (resolved.code && resolved.bundled) {
|
|
2119
2447
|
const importEntries = {};
|
|
@@ -2137,14 +2465,16 @@ ${resolved.code}
|
|
|
2137
2465
|
...resolved.imports || [],
|
|
2138
2466
|
"react-dom/client",
|
|
2139
2467
|
// needed by mount script
|
|
2140
|
-
|
|
2141
|
-
//
|
|
2468
|
+
mount.moduleSpecifier,
|
|
2469
|
+
// mounter/provider package (e.g. @frontmcp/ui/react)
|
|
2142
2470
|
// React subpath entries needed by esm.sh externalized modules:
|
|
2143
2471
|
"react/jsx-runtime",
|
|
2144
|
-
"react/jsx-dev-runtime"
|
|
2145
|
-
"react-dom/server",
|
|
2146
|
-
"react-dom/static"
|
|
2472
|
+
"react/jsx-dev-runtime"
|
|
2147
2473
|
]);
|
|
2474
|
+
if (mount === DEFAULT_WIDGET_MOUNT) {
|
|
2475
|
+
allSpecifiers.add("react-dom/server");
|
|
2476
|
+
allSpecifiers.add("react-dom/static");
|
|
2477
|
+
}
|
|
2148
2478
|
const coreDeps = /* @__PURE__ */ new Set([
|
|
2149
2479
|
"react",
|
|
2150
2480
|
"react-dom",
|
|
@@ -2175,8 +2505,9 @@ ${resolved.code}
|
|
|
2175
2505
|
const importMap = createImportMapFromResolved(importEntries);
|
|
2176
2506
|
parts.push(generateImportMapScriptTag(importMap));
|
|
2177
2507
|
}
|
|
2178
|
-
|
|
2179
|
-
|
|
2508
|
+
const mountNodeId = mount.mountNodeId ?? "root";
|
|
2509
|
+
parts.push(`<div id="${mountNodeId}">${mount.mountNodeInnerHtml ?? ""}</div>`);
|
|
2510
|
+
const mountCode = generateInlineMountCode(resolved.exportName, mount);
|
|
2180
2511
|
parts.push(`<script type="module">
|
|
2181
2512
|
${resolved.code}
|
|
2182
2513
|
${mountCode}
|
|
@@ -2213,53 +2544,27 @@ function addExternalParam(url, externals) {
|
|
|
2213
2544
|
const sep = url.includes("?") ? "&" : "?";
|
|
2214
2545
|
return `${url}${sep}external=${externals.join(",")}`;
|
|
2215
2546
|
}
|
|
2216
|
-
function generateInlineMountCode(componentName) {
|
|
2547
|
+
function generateInlineMountCode(componentName, mount) {
|
|
2548
|
+
if (mount.generate) {
|
|
2549
|
+
return mount.generate(componentName);
|
|
2550
|
+
}
|
|
2551
|
+
const wrapperName = mount.wrapperImportName ?? "McpBridgeProvider";
|
|
2552
|
+
const wrapperAlias = `__${wrapperName}`;
|
|
2553
|
+
const mountNodeId = mount.mountNodeId ?? "root";
|
|
2217
2554
|
return `
|
|
2218
2555
|
// --- Mount ---
|
|
2219
2556
|
import { createRoot as __createRoot } from 'react-dom/client';
|
|
2220
|
-
import {
|
|
2221
|
-
const __root = document.getElementById('
|
|
2557
|
+
import { ${wrapperName} as ${wrapperAlias} } from '${mount.moduleSpecifier}';
|
|
2558
|
+
const __root = document.getElementById('${mountNodeId}');
|
|
2222
2559
|
if (__root) {
|
|
2223
2560
|
__createRoot(__root).render(
|
|
2224
|
-
React.createElement(
|
|
2561
|
+
React.createElement(${wrapperAlias}, null,
|
|
2225
2562
|
React.createElement(${componentName})
|
|
2226
2563
|
)
|
|
2227
2564
|
);
|
|
2228
2565
|
}`;
|
|
2229
2566
|
}
|
|
2230
2567
|
|
|
2231
|
-
// libs/uipack/src/adapters/type-detector.ts
|
|
2232
|
-
function detectUIType(template) {
|
|
2233
|
-
if (template === null || template === void 0) {
|
|
2234
|
-
return "auto";
|
|
2235
|
-
}
|
|
2236
|
-
if (typeof template === "object" && template !== null && "file" in template) {
|
|
2237
|
-
const file = template.file;
|
|
2238
|
-
if (/\.(tsx|jsx)$/i.test(file)) return "react";
|
|
2239
|
-
}
|
|
2240
|
-
if (typeof template === "function") {
|
|
2241
|
-
const proto = template.prototype;
|
|
2242
|
-
if (proto && typeof proto.render === "function") {
|
|
2243
|
-
return "react";
|
|
2244
|
-
}
|
|
2245
|
-
const asRecord = template;
|
|
2246
|
-
if (asRecord["$$typeof"] !== void 0) {
|
|
2247
|
-
return "react";
|
|
2248
|
-
}
|
|
2249
|
-
if (template.name && /^[A-Z]/.test(template.name)) {
|
|
2250
|
-
return "react";
|
|
2251
|
-
}
|
|
2252
|
-
return "html";
|
|
2253
|
-
}
|
|
2254
|
-
if (typeof template === "string") {
|
|
2255
|
-
if (template.includes("<") && template.includes(">")) {
|
|
2256
|
-
return "html";
|
|
2257
|
-
}
|
|
2258
|
-
return "markdown";
|
|
2259
|
-
}
|
|
2260
|
-
return "auto";
|
|
2261
|
-
}
|
|
2262
|
-
|
|
2263
2568
|
// libs/uipack/src/adapters/content-detector.ts
|
|
2264
2569
|
var CHART_TYPES = /* @__PURE__ */ new Set(["bar", "line", "pie", "area", "scatter", "doughnut", "radar", "polarArea", "bubble"]);
|
|
2265
2570
|
var MERMAID_PREFIXES = [
|
|
@@ -2400,6 +2705,38 @@ function wrapDetectedContent(value) {
|
|
|
2400
2705
|
}
|
|
2401
2706
|
}
|
|
2402
2707
|
|
|
2708
|
+
// libs/uipack/src/adapters/type-detector.ts
|
|
2709
|
+
function detectUIType(template) {
|
|
2710
|
+
if (template === null || template === void 0) {
|
|
2711
|
+
return "auto";
|
|
2712
|
+
}
|
|
2713
|
+
if (typeof template === "object" && template !== null && "file" in template) {
|
|
2714
|
+
const file = template.file;
|
|
2715
|
+
if (/\.(tsx|jsx)$/i.test(file)) return "react";
|
|
2716
|
+
}
|
|
2717
|
+
if (typeof template === "function") {
|
|
2718
|
+
const proto = template.prototype;
|
|
2719
|
+
if (proto && typeof proto.render === "function") {
|
|
2720
|
+
return "react";
|
|
2721
|
+
}
|
|
2722
|
+
const asRecord = template;
|
|
2723
|
+
if (asRecord["$$typeof"] !== void 0) {
|
|
2724
|
+
return "react";
|
|
2725
|
+
}
|
|
2726
|
+
if (template.name && /^[A-Z]/.test(template.name)) {
|
|
2727
|
+
return "react";
|
|
2728
|
+
}
|
|
2729
|
+
return "html";
|
|
2730
|
+
}
|
|
2731
|
+
if (typeof template === "string") {
|
|
2732
|
+
if (template.includes("<") && template.includes(">")) {
|
|
2733
|
+
return "html";
|
|
2734
|
+
}
|
|
2735
|
+
return "markdown";
|
|
2736
|
+
}
|
|
2737
|
+
return "auto";
|
|
2738
|
+
}
|
|
2739
|
+
|
|
2403
2740
|
// libs/uipack/src/adapters/template-renderer.ts
|
|
2404
2741
|
function buildCspConfig(resolver) {
|
|
2405
2742
|
const cspResourceDomains = ["https://esm.sh"];
|
|
@@ -2420,21 +2757,26 @@ function buildCspConfig(resolver) {
|
|
|
2420
2757
|
return { resourceDomains: cspResourceDomains, connectDomains: cspConnectDomains };
|
|
2421
2758
|
}
|
|
2422
2759
|
function renderToolTemplate(options) {
|
|
2423
|
-
const { toolName, input, output, template, resolver } = options;
|
|
2760
|
+
const { toolName, input, output, template, resolver, platformType, sizing } = options;
|
|
2424
2761
|
const uiType = detectUIType(template);
|
|
2762
|
+
const resourceMode = options.resourceMode ?? (platformType === "claude" ? "inline" : "cdn");
|
|
2425
2763
|
const shellConfig = {
|
|
2426
2764
|
toolName,
|
|
2427
2765
|
input,
|
|
2428
2766
|
output,
|
|
2429
2767
|
includeBridge: true,
|
|
2430
|
-
resolver
|
|
2768
|
+
resolver,
|
|
2769
|
+
sizing
|
|
2431
2770
|
};
|
|
2432
2771
|
let html;
|
|
2433
2772
|
let hash = "";
|
|
2434
2773
|
let size = 0;
|
|
2435
2774
|
if (typeof template === "object" && template !== null && "file" in template) {
|
|
2436
2775
|
const cspConfig = buildCspConfig(resolver);
|
|
2437
|
-
const result = renderComponent(
|
|
2776
|
+
const result = renderComponent(
|
|
2777
|
+
{ source: template, inlineReact: resourceMode === "inline" },
|
|
2778
|
+
{ ...shellConfig, csp: cspConfig }
|
|
2779
|
+
);
|
|
2438
2780
|
html = result.html;
|
|
2439
2781
|
hash = result.hash;
|
|
2440
2782
|
size = result.size;
|
|
@@ -2476,6 +2818,12 @@ function renderToolTemplate(options) {
|
|
|
2476
2818
|
"ui/type": uiType,
|
|
2477
2819
|
"ui/mimeType": MCP_APPS_MIME_TYPE
|
|
2478
2820
|
};
|
|
2821
|
+
if (hasSizing(sizing)) {
|
|
2822
|
+
if (sizing.preferredHeight !== void 0) meta["ui/preferredHeight"] = sizing.preferredHeight;
|
|
2823
|
+
if (sizing.minHeight !== void 0) meta["ui/minHeight"] = sizing.minHeight;
|
|
2824
|
+
if (sizing.maxHeight !== void 0) meta["ui/maxHeight"] = sizing.maxHeight;
|
|
2825
|
+
if (sizing.aspectRatio !== void 0) meta["ui/aspectRatio"] = sizing.aspectRatio;
|
|
2826
|
+
}
|
|
2479
2827
|
return {
|
|
2480
2828
|
html,
|
|
2481
2829
|
uiType,
|