@frontmcp/uipack 1.8.0 → 1.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -125,17 +125,18 @@ const result = await fetcher.fetchBatch({
125
125
 
126
126
  ### Shell
127
127
 
128
- | Export | Type | Purpose |
129
- | ----------------------------------------------------- | -------- | --------------------------------------------------------------------- |
130
- | `buildShell(content, config)` | function | Main entry point -> `ShellResult` |
131
- | `buildCSPMetaTag(config?)` | function | Generate `<meta>` CSP tag |
132
- | `buildCSPDirectives(config?)` | function | Merge custom domains with defaults |
133
- | `buildDataInjectionScript(toolName, input?, output?)` | function | `<script>` setting `window.__mcp*` globals |
134
- | `createTemplateHelpers(toolName)` | function | `escapeHtml`, `formatDate`, `formatCurrency`, `uniqueId`, `jsonEmbed` |
135
- | `resolveShellTemplate(opts)` | function | Resolve custom shell from npm/URL/inline |
136
- | `applyShellTemplate(template, values)` | function | Apply values to resolved template |
137
- | `validateShellTemplate(template)` | function | Validate a custom shell template |
138
- | `DEFAULT_CDN_DOMAINS` | constant | jsDelivr, Cloudflare, Google Fonts |
128
+ | Export | Type | Purpose |
129
+ | ------------------------------------------------------------ | -------- | ----------------------------------------------------------------------------------------------- |
130
+ | `buildShell(content, config)` | function | Main entry point -> `ShellResult` |
131
+ | `buildCSPMetaTag(config?)` | function | Generate `<meta>` CSP tag |
132
+ | `buildCSPDirectives(config?)` | function | Merge custom domains with defaults |
133
+ | `buildDataInjectionScript(toolName, input?, output?)` | function | `<script>` setting `window.__mcp*` globals |
134
+ | `createTemplateHelpers(toolName)` | function | `html`, `trustedHtml`, `escapeHtml`, `formatDate`, `formatCurrency`, `uniqueId`, `jsonEmbed` |
135
+ | `` html`…` ``, `trustedHtml(markup)`, `isTrustedHtml(value)` | function | Build / mark / detect `TrustedHtml` template markup (interpolated values in `html` are escaped) |
136
+ | `resolveShellTemplate(opts)` | function | Resolve custom shell from npm/URL/inline |
137
+ | `applyShellTemplate(template, values)` | function | Apply values to resolved template |
138
+ | `validateShellTemplate(template)` | function | Validate a custom shell template |
139
+ | `DEFAULT_CDN_DOMAINS` | constant | jsDelivr, Cloudflare, Google Fonts |
139
140
 
140
141
  ### Component
141
142
 
@@ -1 +1 @@
1
- {"version":3,"file":"content-detector.d.ts","sourceRoot":"","sources":["../../src/adapters/content-detector.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,SAAS,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAyBzF;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB,CAsCrE"}
1
+ {"version":3,"file":"content-detector.d.ts","sourceRoot":"","sources":["../../src/adapters/content-detector.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,SAAS,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AA2BzF;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB,CAsCrE"}
package/adapters/index.js CHANGED
@@ -514,8 +514,9 @@ function escapeHtml(str) {
514
514
  const s = String(str);
515
515
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
516
516
  }
517
- function escapeScriptClose(jsonString) {
518
- return jsonString.replace(/<\//g, "<\\/");
517
+ var SCRIPT_UNSAFE_CHARACTERS = /[<>&\u2028\u2029]/g;
518
+ function toUnicodeEscape(character) {
519
+ return `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`;
519
520
  }
520
521
  function safeJsonForScript(value) {
521
522
  if (value === void 0) {
@@ -531,7 +532,7 @@ function safeJsonForScript(value) {
531
532
  if (jsonString === void 0) {
532
533
  return "null";
533
534
  }
534
- return escapeScriptClose(jsonString);
535
+ return jsonString.replace(SCRIPT_UNSAFE_CHARACTERS, toUnicodeEscape);
535
536
  } catch {
536
537
  return '{"error":"Value could not be serialized"}';
537
538
  }
@@ -1828,14 +1829,16 @@ var REQUIRED_PLACEHOLDERS = ["CONTENT"];
1828
1829
  var OPTIONAL_PLACEHOLDERS = ["CSP", "DATA", "BRIDGE", "TITLE"];
1829
1830
 
1830
1831
  // libs/uipack/src/shell/custom-shell-applier.ts
1832
+ var PLACEHOLDER_PATTERN = new RegExp(`\\{\\{(${SHELL_PLACEHOLDER_NAMES.join("|")})\\}\\}`, "g");
1833
+ var VALUE_KEYS = {
1834
+ CSP: "csp",
1835
+ DATA: "data",
1836
+ BRIDGE: "bridge",
1837
+ CONTENT: "content",
1838
+ TITLE: "title"
1839
+ };
1831
1840
  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;
1841
+ return template.replace(PLACEHOLDER_PATTERN, (_token, name) => values[VALUE_KEYS[name]]);
1839
1842
  }
1840
1843
 
1841
1844
  // libs/uipack/src/shell/custom-shell-validator.ts
@@ -1854,6 +1857,29 @@ function validateShellTemplate(template) {
1854
1857
  };
1855
1858
  }
1856
1859
 
1860
+ // libs/uipack/src/shell/trusted-html.ts
1861
+ var TRUSTED_HTML = /* @__PURE__ */ Symbol.for("@frontmcp/uipack/trusted-html");
1862
+ function trustedHtml(markup) {
1863
+ const value = String(markup);
1864
+ return Object.freeze({ [TRUSTED_HTML]: value, toString: () => value });
1865
+ }
1866
+ function isTrustedHtml(value) {
1867
+ return typeof value === "object" && value !== null && typeof value[TRUSTED_HTML] === "string";
1868
+ }
1869
+ function interpolate(value) {
1870
+ if (isTrustedHtml(value)) return value[TRUSTED_HTML];
1871
+ if (Array.isArray(value)) return value.map(interpolate).join("");
1872
+ if (value === false) return "";
1873
+ return escapeHtml(value);
1874
+ }
1875
+ function html(strings, ...values) {
1876
+ let markup = strings[0];
1877
+ for (let index = 0; index < values.length; index++) {
1878
+ markup += interpolate(values[index]) + strings[index + 1];
1879
+ }
1880
+ return trustedHtml(markup);
1881
+ }
1882
+
1857
1883
  // libs/uipack/src/shell/data-injector.ts
1858
1884
  function hasSizing(sizing) {
1859
1885
  if (!sizing) return false;
@@ -1907,9 +1933,9 @@ function createTemplateHelpers() {
1907
1933
  uniqueId: (prefix = "mcp") => {
1908
1934
  return `${prefix}-${++_uniqueIdCounter}`;
1909
1935
  },
1910
- jsonEmbed: (data) => {
1911
- return escapeScriptClose(JSON.stringify(data));
1912
- }
1936
+ jsonEmbed: (data) => safeJsonForScript(data),
1937
+ html,
1938
+ trustedHtml
1913
1939
  };
1914
1940
  }
1915
1941
 
@@ -1987,12 +2013,12 @@ function buildShell(content, config) {
1987
2013
  sizing
1988
2014
  });
1989
2015
  if (!withShell) {
1990
- const html2 = `${dataScript}
2016
+ const html3 = `${dataScript}
1991
2017
  ${content}`;
1992
2018
  return {
1993
- html: html2,
1994
- hash: simpleHash(html2),
1995
- size: Buffer.byteLength(html2, "utf-8")
2019
+ html: html3,
2020
+ hash: simpleHash(html3),
2021
+ size: Buffer.byteLength(html3, "utf-8")
1996
2022
  };
1997
2023
  }
1998
2024
  if (customShell) {
@@ -2025,7 +2051,7 @@ ${content}`;
2025
2051
  const bridgeScript = generateBridgeIIFE({ minify: true });
2026
2052
  headParts.push(`<script>${bridgeScript}</script>`);
2027
2053
  }
2028
- const html = `<!DOCTYPE html>
2054
+ const html2 = `<!DOCTYPE html>
2029
2055
  <html lang="en">
2030
2056
  <head>
2031
2057
  ${headParts.map((p) => ` ${p}`).join("\n")}
@@ -2035,9 +2061,9 @@ ${content}
2035
2061
  </body>
2036
2062
  </html>`;
2037
2063
  return {
2038
- html,
2039
- hash: simpleHash(html),
2040
- size: Buffer.byteLength(html, "utf-8")
2064
+ html: html2,
2065
+ hash: simpleHash(html2),
2066
+ size: Buffer.byteLength(html2, "utf-8")
2041
2067
  };
2042
2068
  }
2043
2069
  function buildCustomShell(content, customShell, ctx) {
@@ -2070,7 +2096,7 @@ ${sizingStyle}` : dataScript;
2070
2096
  const bridgeScript = generateBridgeIIFE({ minify: true });
2071
2097
  bridgeHtml = `<script>${bridgeScript}</script>`;
2072
2098
  }
2073
- const html = applyShellTemplate(template, {
2099
+ const html2 = applyShellTemplate(template, {
2074
2100
  csp: cspTag,
2075
2101
  data: dataWithSizing,
2076
2102
  bridge: bridgeHtml,
@@ -2078,9 +2104,9 @@ ${sizingStyle}` : dataScript;
2078
2104
  title: ctx.title ? escapeHtmlForTag(ctx.title) : ""
2079
2105
  });
2080
2106
  return {
2081
- html,
2082
- hash: simpleHash(html),
2083
- size: Buffer.byteLength(html, "utf-8")
2107
+ html: html2,
2108
+ hash: simpleHash(html2),
2109
+ size: Buffer.byteLength(html2, "utf-8")
2084
2110
  };
2085
2111
  }
2086
2112
  function simpleHash(str) {
@@ -2377,10 +2403,10 @@ function resolveFileSource(source, options = {}) {
2377
2403
  };
2378
2404
  }
2379
2405
  function resolveFunctionSource(source, input, output) {
2380
- const html = source(input, output);
2406
+ const html2 = source(input, output);
2381
2407
  return {
2382
2408
  mode: "inline",
2383
- html,
2409
+ html: html2,
2384
2410
  exportName: "default",
2385
2411
  meta: { mcpAware: false, renderer: "html" },
2386
2412
  peerDependencies: []
@@ -2585,6 +2611,7 @@ var MERMAID_PREFIXES = [
2585
2611
  "xychart"
2586
2612
  ];
2587
2613
  var PDF_MAGIC = "JVBERi";
2614
+ var BASE64_PATTERN = /^[A-Za-z0-9+/=\r\n]+$/;
2588
2615
  function detectContentType(value) {
2589
2616
  if (value === null || value === void 0) {
2590
2617
  return "text";
@@ -2599,7 +2626,7 @@ function detectContentType(value) {
2599
2626
  if (typeof value === "string") {
2600
2627
  const trimmed = value.trimStart();
2601
2628
  if (trimmed.startsWith(PDF_MAGIC)) {
2602
- return "pdf";
2629
+ return BASE64_PATTERN.test(trimmed.trimEnd()) ? "pdf" : "text";
2603
2630
  }
2604
2631
  for (const prefix of MERMAID_PREFIXES) {
2605
2632
  if (trimmed.startsWith(prefix)) {
@@ -2661,7 +2688,7 @@ function buildPdfHtml(base64) {
2661
2688
  import * as pdfjsLib from 'https://esm.sh/pdfjs-dist@4/build/pdf.min.mjs';
2662
2689
  pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://esm.sh/pdfjs-dist@4/build/pdf.worker.min.mjs';
2663
2690
 
2664
- const base64 = ${JSON.stringify(base64)};
2691
+ const base64 = ${safeJsonForScript(base64)};
2665
2692
  const binaryStr = atob(base64);
2666
2693
  const bytes = new Uint8Array(binaryStr.length);
2667
2694
  for (let i = 0; i < binaryStr.length; i++) bytes[i] = binaryStr.charCodeAt(i);
@@ -2756,6 +2783,30 @@ function buildCspConfig(resolver) {
2756
2783
  }
2757
2784
  return { resourceDomains: cspResourceDomains, connectDomains: cspConnectDomains };
2758
2785
  }
2786
+ var STRING_RESULT_DOCS = "https://docs.agentfront.dev/frontmcp/guides/building-tool-ui#trusted-markup";
2787
+ var noticedStringResultTools = /* @__PURE__ */ new Set();
2788
+ function noticeStringResult(toolName, logger) {
2789
+ if (noticedStringResultTools.has(toolName)) return;
2790
+ noticedStringResultTools.add(toolName);
2791
+ logger.warn(
2792
+ `[frontmcp] The UI template of tool "${toolName}" returned a plain string containing markup, which is rendered as HTML. FrontMCP 1.9 will HTML-escape plain string results by default. Build the markup with ctx.helpers.html\`\u2026\` (interpolated values are escaped) or wrap safe markup with ctx.helpers.trustedHtml(), then set ui.escapeStringResults: true (or escapeStringResults: false to keep the current behaviour without this notice). See ${STRING_RESULT_DOCS}`
2793
+ );
2794
+ }
2795
+ function renderUnwrappedResult(rawResult, options) {
2796
+ if (isTrustedHtml(rawResult)) {
2797
+ return String(rawResult);
2798
+ }
2799
+ if (typeof rawResult !== "string") {
2800
+ return `<pre>${escapeHtml(JSON.stringify(rawResult, null, 2))}</pre>`;
2801
+ }
2802
+ if (detectContentType(rawResult) !== "html" || options.escapeStringResults === true) {
2803
+ return escapeHtml(rawResult);
2804
+ }
2805
+ if (options.escapeStringResults === void 0) {
2806
+ noticeStringResult(options.toolName, options.logger ?? console);
2807
+ }
2808
+ return rawResult;
2809
+ }
2759
2810
  function renderToolTemplate(options) {
2760
2811
  const { toolName, input, output, template, resolver, platformType, sizing } = options;
2761
2812
  const uiType = detectUIType(template);
@@ -2768,7 +2819,7 @@ function renderToolTemplate(options) {
2768
2819
  resolver,
2769
2820
  sizing
2770
2821
  };
2771
- let html;
2822
+ let html2;
2772
2823
  let hash = "";
2773
2824
  let size = 0;
2774
2825
  if (typeof template === "object" && template !== null && "file" in template) {
@@ -2777,44 +2828,43 @@ function renderToolTemplate(options) {
2777
2828
  { source: template, inlineReact: resourceMode === "inline" },
2778
2829
  { ...shellConfig, csp: cspConfig }
2779
2830
  );
2780
- html = result.html;
2831
+ html2 = result.html;
2781
2832
  hash = result.hash;
2782
2833
  size = result.size;
2783
2834
  } else if (typeof template === "function") {
2784
2835
  if (uiType === "react") {
2785
2836
  const shellResult = buildShell('<div id="root"></div>', shellConfig);
2786
- html = shellResult.html;
2837
+ html2 = shellResult.html;
2787
2838
  hash = shellResult.hash;
2788
2839
  size = shellResult.size;
2789
2840
  } else {
2790
2841
  const helpers = createTemplateHelpers();
2791
2842
  const ctx = { input, output, helpers };
2792
2843
  const rawResult = template(ctx);
2793
- const wrapped = wrapDetectedContent(rawResult);
2844
+ const wrapped = isTrustedHtml(rawResult) ? void 0 : wrapDetectedContent(rawResult);
2794
2845
  if (wrapped) {
2795
- html = wrapped;
2846
+ html2 = wrapped;
2796
2847
  } else {
2797
- const textContent = typeof rawResult === "string" ? rawResult : `<pre>${JSON.stringify(rawResult, null, 2)}</pre>`;
2798
- const shellResult = buildShell(textContent, shellConfig);
2799
- html = shellResult.html;
2848
+ const shellResult = buildShell(renderUnwrappedResult(rawResult, options), shellConfig);
2849
+ html2 = shellResult.html;
2800
2850
  hash = shellResult.hash;
2801
2851
  size = shellResult.size;
2802
2852
  }
2803
2853
  }
2804
2854
  } else if (typeof template === "string") {
2805
2855
  const shellResult = buildShell(template, shellConfig);
2806
- html = shellResult.html;
2856
+ html2 = shellResult.html;
2807
2857
  hash = shellResult.hash;
2808
2858
  size = shellResult.size;
2809
2859
  } else {
2810
2860
  const shellResult = buildShell('<div id="root"></div>', shellConfig);
2811
- html = shellResult.html;
2861
+ html2 = shellResult.html;
2812
2862
  hash = shellResult.hash;
2813
2863
  size = shellResult.size;
2814
2864
  }
2815
2865
  const htmlKey = "ui/html";
2816
2866
  const meta = {
2817
- [htmlKey]: html,
2867
+ [htmlKey]: html2,
2818
2868
  "ui/type": uiType,
2819
2869
  "ui/mimeType": MCP_APPS_MIME_TYPE
2820
2870
  };
@@ -2825,10 +2875,10 @@ function renderToolTemplate(options) {
2825
2875
  if (sizing.aspectRatio !== void 0) meta["ui/aspectRatio"] = sizing.aspectRatio;
2826
2876
  }
2827
2877
  return {
2828
- html,
2878
+ html: html2,
2829
2879
  uiType,
2830
2880
  hash,
2831
- size: size || Buffer.byteLength(html, "utf8"),
2881
+ size: size || Buffer.byteLength(html2, "utf8"),
2832
2882
  meta
2833
2883
  };
2834
2884
  }
@@ -36,6 +36,16 @@ export interface RenderToolTemplateOptions {
36
36
  * shell, and is mirrored onto the returned `meta` as `ui/preferredHeight` etc.
37
37
  */
38
38
  sizing?: WidgetSizing;
39
+ /**
40
+ * HTML-escape a plain string returned by a template function; results built with
41
+ * `html` / `trustedHtml` stay markup. When unset, strings that look like HTML render as
42
+ * markup and a one-time notice per tool is logged; `false` keeps that without the notice.
43
+ */
44
+ escapeStringResults?: boolean;
45
+ /** Receives the one-time string-result notice. Defaults to `console`. */
46
+ logger?: {
47
+ warn: (message: string) => void;
48
+ };
39
49
  }
40
50
  /**
41
51
  * Result of rendering a tool template.
@@ -57,7 +67,7 @@ export interface RenderToolTemplateResult {
57
67
  *
58
68
  * Supported template types:
59
69
  * - FileSource object `{ file: './widget.tsx' }` — bundled with esbuild, React loaded from esm.sh
60
- * - HTML template builder function `(ctx) => string`
70
+ * - HTML template builder function `(ctx) => string | TrustedHtml`
61
71
  * - Static HTML/MDX string
62
72
  *
63
73
  * React function references (`template: MyComponent`) are NOT supported for bundling.
@@ -1 +1 @@
1
- {"version":3,"file":"template-renderer.d.ts","sourceRoot":"","sources":["../../src/adapters/template-renderer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAKnD;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,gBAAgB;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,kBAAkB;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,qDAAqD;IACrD,QAAQ,EAAE,OAAO,CAAC;IAClB,4CAA4C;IAC5C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kDAAkD;IAClD,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B;;;;;OAKG;IACH,YAAY,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;IAChC;;;;OAIG;IACH,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,2BAA2B;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAsBD;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,yBAAyB,GAAG,wBAAwB,CAyG/F"}
1
+ {"version":3,"file":"template-renderer.d.ts","sourceRoot":"","sources":["../../src/adapters/template-renderer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAIxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAOnD;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,gBAAgB;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,kBAAkB;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,qDAAqD;IACrD,QAAQ,EAAE,OAAO,CAAC;IAClB,4CAA4C;IAC5C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kDAAkD;IAClD,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B;;;;;OAKG;IACH,YAAY,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;IAChC;;;;OAIG;IACH,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,yEAAyE;IACzE,MAAM,CAAC,EAAE;QAAE,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;CAC9C;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,2BAA2B;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AA6DD;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,yBAAyB,GAAG,wBAAwB,CAsG/F"}
@@ -450,8 +450,9 @@ function createEsmShResolver(options = {}) {
450
450
  }
451
451
 
452
452
  // libs/uipack/src/utils/index.ts
453
- function escapeScriptClose(jsonString) {
454
- return jsonString.replace(/<\//g, "<\\/");
453
+ var SCRIPT_UNSAFE_CHARACTERS = /[<>&\u2028\u2029]/g;
454
+ function toUnicodeEscape(character) {
455
+ return `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`;
455
456
  }
456
457
  function safeJsonForScript(value) {
457
458
  if (value === void 0) {
@@ -467,7 +468,7 @@ function safeJsonForScript(value) {
467
468
  if (jsonString === void 0) {
468
469
  return "null";
469
470
  }
470
- return escapeScriptClose(jsonString);
471
+ return jsonString.replace(SCRIPT_UNSAFE_CHARACTERS, toUnicodeEscape);
471
472
  } catch {
472
473
  return '{"error":"Value could not be serialized"}';
473
474
  }
@@ -2070,14 +2071,16 @@ var REQUIRED_PLACEHOLDERS = ["CONTENT"];
2070
2071
  var OPTIONAL_PLACEHOLDERS = ["CSP", "DATA", "BRIDGE", "TITLE"];
2071
2072
 
2072
2073
  // libs/uipack/src/shell/custom-shell-applier.ts
2074
+ var PLACEHOLDER_PATTERN = new RegExp(`\\{\\{(${SHELL_PLACEHOLDER_NAMES.join("|")})\\}\\}`, "g");
2075
+ var VALUE_KEYS = {
2076
+ CSP: "csp",
2077
+ DATA: "data",
2078
+ BRIDGE: "bridge",
2079
+ CONTENT: "content",
2080
+ TITLE: "title"
2081
+ };
2073
2082
  function applyShellTemplate(template, values) {
2074
- let result = template;
2075
- result = result.replaceAll(SHELL_PLACEHOLDERS.CSP, values.csp);
2076
- result = result.replaceAll(SHELL_PLACEHOLDERS.DATA, values.data);
2077
- result = result.replaceAll(SHELL_PLACEHOLDERS.BRIDGE, values.bridge);
2078
- result = result.replaceAll(SHELL_PLACEHOLDERS.CONTENT, values.content);
2079
- result = result.replaceAll(SHELL_PLACEHOLDERS.TITLE, values.title);
2080
- return result;
2083
+ return template.replace(PLACEHOLDER_PATTERN, (_token, name) => values[VALUE_KEYS[name]]);
2081
2084
  }
2082
2085
 
2083
2086
  // libs/uipack/src/shell/custom-shell-validator.ts