@webmate-studio/builder 0.2.113 → 0.2.114

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webmate-studio/builder",
3
- "version": "0.2.113",
3
+ "version": "0.2.114",
4
4
  "type": "module",
5
5
  "description": "Webmate Studio Component Builder",
6
6
  "keywords": [
package/src/index.js CHANGED
@@ -5,6 +5,7 @@ import { bundleIsland, bundleComponentIslands } from './bundler.js';
5
5
  import { deduplicateCSS } from './css-deduplicator.js';
6
6
  import { markdownToHtml, processMarkdownProps } from './markdown.js';
7
7
  import TemplateProcessor, { templateProcessor } from './template-processor.js';
8
+ import { SafeHtml } from './safe-html.js';
8
9
  import { defaultDesignTokens, generateTailwindV4Theme, generateTailwindConfig, generateCSSFromTokens } from './design-tokens.js';
9
10
  import { readFileSync } from 'fs';
10
11
  import { fileURLToPath } from 'url';
@@ -18,4 +19,4 @@ function getMotionRuntime() {
18
19
  return readFileSync(join(__dirname, '../dist/motion-runtime.min.js'), 'utf-8');
19
20
  }
20
21
 
21
- export { build, generateComponentCSS, generateTailwindCSS, extractTailwindClasses, cleanComponentHTML, bundleIsland, bundleComponentIslands, deduplicateCSS, markdownToHtml, processMarkdownProps, getMotionRuntime, TemplateProcessor, templateProcessor, defaultDesignTokens, generateTailwindV4Theme, generateTailwindConfig, generateCSSFromTokens };
22
+ export { build, generateComponentCSS, generateTailwindCSS, extractTailwindClasses, cleanComponentHTML, bundleIsland, bundleComponentIslands, deduplicateCSS, markdownToHtml, processMarkdownProps, SafeHtml, getMotionRuntime, TemplateProcessor, templateProcessor, defaultDesignTokens, generateTailwindV4Theme, generateTailwindConfig, generateCSSFromTokens };
package/src/markdown.js CHANGED
@@ -6,6 +6,7 @@
6
6
  import { marked } from 'marked';
7
7
  import DOMPurify from 'dompurify';
8
8
  import { JSDOM } from 'jsdom';
9
+ import { SafeHtml } from './safe-html.js';
9
10
 
10
11
  // DOMPurify für Server-Side Rendering konfigurieren
11
12
  const window = new JSDOM('').window;
@@ -160,8 +161,9 @@ export function processMarkdownProps(props, propSchema = null, componentMetadata
160
161
  options.headingStartLevel = headingStartLevel;
161
162
  }
162
163
 
163
- // Convert markdown to HTML
164
- processed[key] = markdownToHtml(value, options);
164
+ // Convert markdown to HTML and wrap in SafeHtml
165
+ // (already sanitized by DOMPurify — evaluator will not escape)
166
+ processed[key] = new SafeHtml(markdownToHtml(value, options));
165
167
  }
166
168
  }
167
169
 
@@ -0,0 +1,30 @@
1
+ /**
2
+ * SafeHtml — Marker for pre-sanitized HTML content.
3
+ *
4
+ * When processMarkdownProps() converts markdown to HTML, the result is
5
+ * already sanitized by DOMPurify. Wrapping it in SafeHtml tells the
6
+ * template evaluator to output it without escaping.
7
+ *
8
+ * Usage:
9
+ * const safe = new SafeHtml('<h2>Hello</h2>');
10
+ * safe.toString() // → '<h2>Hello</h2>'
11
+ * safe.__safeHtml // → true (marker for evaluator)
12
+ */
13
+
14
+ class SafeHtml {
15
+ constructor(html) {
16
+ this.html = String(html ?? '');
17
+ this.__safeHtml = true;
18
+ }
19
+
20
+ toString() {
21
+ return this.html;
22
+ }
23
+
24
+ valueOf() {
25
+ return this.html;
26
+ }
27
+ }
28
+
29
+ export { SafeHtml };
30
+ export default SafeHtml;
@@ -72,7 +72,8 @@ class TemplateEvaluator {
72
72
  }
73
73
 
74
74
  /**
75
- * {expression} — evaluate and escape for safe HTML output
75
+ * {expression} — evaluate and escape for safe HTML output.
76
+ * SafeHtml instances (from processMarkdownProps) are output without escaping.
76
77
  */
77
78
  evalExpression(node, ctx) {
78
79
  const value = this.expr.evaluate(node.expression, ctx);
@@ -80,6 +81,11 @@ class TemplateEvaluator {
80
81
  if (value === undefined || value === null) return '';
81
82
  if (typeof value === 'boolean') return '';
82
83
 
84
+ // SafeHtml marker — already sanitized by DOMPurify, don't escape
85
+ if (value && value.__safeHtml) {
86
+ return value.toString();
87
+ }
88
+
83
89
  // Handle special objects
84
90
  const resolved = this.resolveValue(value);
85
91
  return this.escapeHtml(String(resolved));
@@ -360,12 +366,16 @@ class TemplateEvaluator {
360
366
  // HTML escaping
361
367
  // ========================================
362
368
 
369
+ /**
370
+ * Escape HTML for text content.
371
+ * Only escapes < and > (XSS protection).
372
+ * Does NOT escape & — CMS users legitimately use & in content,
373
+ * and escaping it would turn "Hallo & Hi" into "Hallo &amp; Hi".
374
+ */
363
375
  escapeHtml(str) {
364
376
  return str
365
- .replace(/&/g, '&amp;')
366
377
  .replace(/</g, '&lt;')
367
- .replace(/>/g, '&gt;')
368
- .replace(/"/g, '&quot;');
378
+ .replace(/>/g, '&gt;');
369
379
  }
370
380
 
371
381
  escapeAttr(str) {