@keypuncherlabs/live-preview 1.1.0 → 1.3.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/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # @keypuncherlabs/live-preview
2
2
 
3
- Push **live, validated CSS** from a parent application into an embedded preview
4
- (such as a Storybook hosted on a different subdomain or origin) over
5
- `window.postMessage`.
3
+ Push **live, validated CSS and Google Fonts** from a parent application into an
4
+ embedded preview (such as a Storybook hosted on a different subdomain or origin)
5
+ over `window.postMessage`.
6
6
 
7
7
  It is framework-agnostic, has no runtime dependencies, and is built
8
8
  security-first — previews are often hosted on origins you do not fully control,
@@ -23,9 +23,13 @@ directly. This library gives you a tiny, safe channel:
23
23
  down to the real preview frame, since a grandparent window can only post to its
24
24
  direct child.
25
25
 
26
- Message type on the wire: `live-preview-css`. Receivers/relays announce
27
- readiness with `live-preview-ready` so the parent can (re)send without racing
28
- startup.
26
+ Message types on the wire: `live-preview-css` and `live-preview-google-fonts`.
27
+ Receivers/relays announce readiness with `live-preview-ready` so the parent can
28
+ (re)send without racing startup.
29
+
30
+ Setting a `font-family` over CSS is not enough — the font resource must be
31
+ loaded or the browser falls back. The `live-preview-google-fonts` message asks
32
+ the preview to load specific Google Fonts so the chosen families actually render.
29
33
 
30
34
  ## Security model
31
35
 
@@ -41,6 +45,12 @@ startup.
41
45
  by default), and oversized payloads.
42
46
  - **Safe injection.** CSS is written with `textContent` into one reused `<style>`
43
47
  node, so markup cannot be injected and the DOM does not grow.
48
+ - **Google Fonts carry no URLs.** A fonts message contains only `{ family, weights }`.
49
+ The receiver builds the href from a hardcoded `fonts.googleapis.com/css2` base,
50
+ so message data can never become the request's host/scheme. Family names must
51
+ match a strict charset (`A–Z a–z 0–9 space . _ -`), so they cannot inject URL
52
+ params or do CRLF tricks; weights are clamped to integers 1–1000 and the family
53
+ count is capped. The font `<link>` loads no script.
44
54
 
45
55
  ## Usage
46
56
 
@@ -64,6 +74,9 @@ window.addEventListener('message', (e) => {
64
74
 
65
75
  const result = sender.sendCss(':root { --color-primary: #06f; }');
66
76
  if (!result.valid) console.warn(result.errors);
77
+
78
+ // Ask the preview to load the fonts the CSS references:
79
+ sender.sendGoogleFonts([{ family: 'Poppins', weights: [400, 600] }]);
67
80
  ```
68
81
 
69
82
  ### Embedded app (receiver)
@@ -75,6 +88,8 @@ startLivePreviewReceiver({
75
88
  allowedOrigins: ['https://app.example.com'],
76
89
  styleId: 'live-preview-styles',
77
90
  onReject: (reason) => console.warn(reason),
91
+ // Google Fonts requested over `live-preview-google-fonts` are loaded by
92
+ // default; set `loadGoogleFonts: false` to opt out.
78
93
  });
79
94
  ```
80
95
 
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "@keypuncherlabs/live-preview",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "type": "commonjs",
5
- "description": "Send validated CSS into an embedded preview (e.g. a Storybook on another origin) over postMessage. Framework-agnostic, dependency-free, security-first.",
5
+ "description": "Send validated CSS and Google Fonts into an embedded preview (e.g. a Storybook on another origin) over postMessage. Framework-agnostic, dependency-free, security-first.",
6
6
  "keywords": [
7
7
  "live-preview",
8
8
  "postmessage",
9
9
  "iframe",
10
10
  "css-injection",
11
+ "google-fonts",
11
12
  "storybook",
12
13
  "cross-origin",
13
14
  "design-tokens"
@@ -31,4 +32,4 @@
31
32
  "dependencies": {
32
33
  "tslib": "^2.3.0"
33
34
  }
34
- }
35
+ }
package/src/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from './lib/protocol';
2
2
  export * from './lib/validate-css';
3
+ export * from './lib/google-fonts';
3
4
  export * from './lib/sender';
4
5
  export * from './lib/receiver';
5
6
  export * from './lib/relay';
package/src/index.js CHANGED
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  tslib_1.__exportStar(require("./lib/protocol"), exports);
5
5
  tslib_1.__exportStar(require("./lib/validate-css"), exports);
6
+ tslib_1.__exportStar(require("./lib/google-fonts"), exports);
6
7
  tslib_1.__exportStar(require("./lib/sender"), exports);
7
8
  tslib_1.__exportStar(require("./lib/receiver"), exports);
8
9
  tslib_1.__exportStar(require("./lib/relay"), exports);
package/src/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../libs/public/keypuncherlabs/live-preview/src/index.ts"],"names":[],"mappings":";;;AAAA,yDAA+B;AAC/B,6DAAmC;AACnC,uDAA6B;AAC7B,yDAA+B;AAC/B,sDAA4B"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../libs/public/keypuncherlabs/live-preview/src/index.ts"],"names":[],"mappings":";;;AAAA,yDAA+B;AAC/B,6DAAmC;AACnC,6DAAmC;AACnC,uDAA6B;AAC7B,yDAA+B;AAC/B,sDAA4B"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Live Google Fonts loading for an embedded preview.
3
+ *
4
+ * Setting a `font-family` is not enough — the font resource must be fetched or
5
+ * the browser falls back. This module lets a parent app ask an embedded preview
6
+ * to load specific Google Fonts so the chosen families actually render.
7
+ *
8
+ * Security is the priority here, because a family name flows into a URL the page
9
+ * then fetches. The defenses are layered so no single one is load-bearing:
10
+ *
11
+ * - **No URLs on the wire.** Messages carry only `{ family, weights }`. The href
12
+ * is always built from a hardcoded Google Fonts base, so message data can
13
+ * never become the request's origin/host/scheme.
14
+ * - **Strict family charset.** A family must fully match a small allowlist
15
+ * charset, so it cannot inject extra URL params, smuggle a second URL, or do
16
+ * CRLF tricks.
17
+ * - **Clamped weights, bounded counts.** Weights are integers in 1–1000; the
18
+ * number of families is capped.
19
+ * - **Validated at every hop** (sender, relay, receiver) and injected via
20
+ * `createElement`/`setAttribute` into one idempotent `<link>` (never
21
+ * `innerHTML`).
22
+ */
23
+ /** Attribute marking elements this module injected, so loads are idempotent. */
24
+ export declare const GOOGLE_FONTS_MARKER = "data-live-preview-google-fonts";
25
+ /** Default ceiling on families per message. */
26
+ export declare const DEFAULT_MAX_FONTS = 20;
27
+ /** A Google web font to load: a family name and (optionally) the weights wanted. */
28
+ export interface GoogleFontDef {
29
+ family: string;
30
+ weights?: number[];
31
+ }
32
+ export interface GoogleFontsValidationOptions {
33
+ /** Max number of families accepted. Defaults to {@link DEFAULT_MAX_FONTS}. */
34
+ maxFonts?: number;
35
+ }
36
+ export interface GoogleFontsValidationResult {
37
+ /** True when every font is safe to load. */
38
+ valid: boolean;
39
+ /** The sanitized fonts when valid, otherwise an empty array. */
40
+ fonts: GoogleFontDef[];
41
+ /** Human-readable rejection reasons (empty when valid). */
42
+ errors: string[];
43
+ }
44
+ export interface LoadGoogleFontsOptions {
45
+ /** `font-display` strategy. Defaults to `swap`. */
46
+ display?: string;
47
+ /** Document to inject into. Defaults to the ambient `document` (no-op if absent). */
48
+ document?: Document;
49
+ /** Inject `<link rel="preconnect">` hints. Defaults to `true`. */
50
+ preconnect?: boolean;
51
+ }
52
+ /**
53
+ * Validate and sanitize a list of Google fonts. Never throws; returns a result
54
+ * describing why the input was rejected. A single bad family rejects the whole
55
+ * batch (the sender should only ever send safe families).
56
+ */
57
+ export declare function validateGoogleFonts(input: unknown, options?: GoogleFontsValidationOptions): GoogleFontsValidationResult;
58
+ /**
59
+ * Build the Google Fonts stylesheet URL for the given fonts. Families are sorted
60
+ * so the same set always yields the same URL (idempotent loads). Assumes the
61
+ * fonts already passed {@link validateGoogleFonts}.
62
+ */
63
+ export declare function buildGoogleFontsHref(fonts: readonly GoogleFontDef[], display?: string): string;
64
+ /**
65
+ * Load Google Fonts by injecting a single, idempotent stylesheet `<link>` (plus
66
+ * preconnect hints) into `<head>`. Re-validates the fonts defensively. Replaces
67
+ * the previous link when the set changes; no-op when there is no `document` or
68
+ * no valid fonts. Returns the stylesheet `<link>`, or `null` if nothing changed.
69
+ */
70
+ export declare function loadGoogleFonts(fonts: readonly GoogleFontDef[], options?: LoadGoogleFontsOptions): HTMLLinkElement | null;
@@ -0,0 +1,139 @@
1
+ "use strict";
2
+ /**
3
+ * Live Google Fonts loading for an embedded preview.
4
+ *
5
+ * Setting a `font-family` is not enough — the font resource must be fetched or
6
+ * the browser falls back. This module lets a parent app ask an embedded preview
7
+ * to load specific Google Fonts so the chosen families actually render.
8
+ *
9
+ * Security is the priority here, because a family name flows into a URL the page
10
+ * then fetches. The defenses are layered so no single one is load-bearing:
11
+ *
12
+ * - **No URLs on the wire.** Messages carry only `{ family, weights }`. The href
13
+ * is always built from a hardcoded Google Fonts base, so message data can
14
+ * never become the request's origin/host/scheme.
15
+ * - **Strict family charset.** A family must fully match a small allowlist
16
+ * charset, so it cannot inject extra URL params, smuggle a second URL, or do
17
+ * CRLF tricks.
18
+ * - **Clamped weights, bounded counts.** Weights are integers in 1–1000; the
19
+ * number of families is capped.
20
+ * - **Validated at every hop** (sender, relay, receiver) and injected via
21
+ * `createElement`/`setAttribute` into one idempotent `<link>` (never
22
+ * `innerHTML`).
23
+ */
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.DEFAULT_MAX_FONTS = exports.GOOGLE_FONTS_MARKER = void 0;
26
+ exports.validateGoogleFonts = validateGoogleFonts;
27
+ exports.buildGoogleFontsHref = buildGoogleFontsHref;
28
+ exports.loadGoogleFonts = loadGoogleFonts;
29
+ /** Attribute marking elements this module injected, so loads are idempotent. */
30
+ exports.GOOGLE_FONTS_MARKER = 'data-live-preview-google-fonts';
31
+ const GOOGLE_FONTS_CSS_BASE = 'https://fonts.googleapis.com/css2';
32
+ const GOOGLE_FONTS_PRECONNECT = ['https://fonts.googleapis.com', 'https://fonts.gstatic.com'];
33
+ /** Default ceiling on families per message. */
34
+ exports.DEFAULT_MAX_FONTS = 20;
35
+ // A family must be exactly this: letters, digits, spaces, and . _ - only. This
36
+ // excludes & ? # : / % " ' \ < > and CR/LF, defeating URL-param injection,
37
+ // second-URL smuggling, and CRLF tricks.
38
+ const SAFE_FAMILY = /^[A-Za-z0-9 ._-]{1,100}$/;
39
+ const isValidWeight = (weight) => typeof weight === 'number' && Number.isInteger(weight) && weight >= 1 && weight <= 1000;
40
+ /**
41
+ * Validate and sanitize a list of Google fonts. Never throws; returns a result
42
+ * describing why the input was rejected. A single bad family rejects the whole
43
+ * batch (the sender should only ever send safe families).
44
+ */
45
+ function validateGoogleFonts(input, options = {}) {
46
+ const { maxFonts = exports.DEFAULT_MAX_FONTS } = options;
47
+ const errors = [];
48
+ if (!Array.isArray(input)) {
49
+ return { valid: false, fonts: [], errors: ['fonts must be an array'] };
50
+ }
51
+ if (input.length > maxFonts) {
52
+ errors.push(`too many fonts (max ${maxFonts})`);
53
+ }
54
+ const fonts = [];
55
+ for (const entry of input) {
56
+ const family = entry === null || entry === void 0 ? void 0 : entry.family;
57
+ if (typeof family !== 'string' || !SAFE_FAMILY.test(family)) {
58
+ errors.push(`invalid font family: ${JSON.stringify(family)}`);
59
+ continue;
60
+ }
61
+ const rawWeights = entry.weights;
62
+ let weights;
63
+ if (rawWeights != null) {
64
+ if (!Array.isArray(rawWeights)) {
65
+ errors.push(`invalid weights for ${family}`);
66
+ continue;
67
+ }
68
+ // Keep only valid weights; de-duplicate and sort for a deterministic URL.
69
+ weights = [...new Set(rawWeights.filter(isValidWeight))].sort((a, b) => a - b);
70
+ }
71
+ fonts.push(weights && weights.length ? { family: family.trim(), weights } : { family: family.trim() });
72
+ }
73
+ if (errors.length > 0) {
74
+ return { valid: false, fonts: [], errors };
75
+ }
76
+ return { valid: true, fonts, errors: [] };
77
+ }
78
+ /** Google Fonts css2 param for one family, e.g. `family=Open+Sans:wght@400;600`. */
79
+ const googleParam = (font) => {
80
+ const name = font.family.trim().replace(/\s+/g, '+');
81
+ const weights = font.weights && font.weights.length ? [...font.weights].sort((a, b) => a - b) : [];
82
+ return `family=${name}${weights.length ? `:wght@${weights.join(';')}` : ''}`;
83
+ };
84
+ /**
85
+ * Build the Google Fonts stylesheet URL for the given fonts. Families are sorted
86
+ * so the same set always yields the same URL (idempotent loads). Assumes the
87
+ * fonts already passed {@link validateGoogleFonts}.
88
+ */
89
+ function buildGoogleFontsHref(fonts, display = 'swap') {
90
+ const families = [...fonts]
91
+ .sort((a, b) => a.family.localeCompare(b.family))
92
+ .map(googleParam)
93
+ .join('&');
94
+ return `${GOOGLE_FONTS_CSS_BASE}?${families}&display=${display}`;
95
+ }
96
+ /**
97
+ * Load Google Fonts by injecting a single, idempotent stylesheet `<link>` (plus
98
+ * preconnect hints) into `<head>`. Re-validates the fonts defensively. Replaces
99
+ * the previous link when the set changes; no-op when there is no `document` or
100
+ * no valid fonts. Returns the stylesheet `<link>`, or `null` if nothing changed.
101
+ */
102
+ function loadGoogleFonts(fonts, options = {}) {
103
+ var _a, _b, _c;
104
+ const doc = (_a = options.document) !== null && _a !== void 0 ? _a : (typeof document !== 'undefined' ? document : undefined);
105
+ if (!doc)
106
+ return null;
107
+ // Defense in depth: never trust the caller; build the URL only from safe data.
108
+ const result = validateGoogleFonts(fonts);
109
+ if (!result.valid || result.fonts.length === 0)
110
+ return null;
111
+ const href = buildGoogleFontsHref(result.fonts, (_b = options.display) !== null && _b !== void 0 ? _b : 'swap');
112
+ const existing = doc.head.querySelector(`link[${exports.GOOGLE_FONTS_MARKER}="stylesheet"]`);
113
+ if (existing) {
114
+ if (existing.href === href)
115
+ return existing;
116
+ existing.remove();
117
+ }
118
+ if ((_c = options.preconnect) !== null && _c !== void 0 ? _c : true) {
119
+ for (const host of GOOGLE_FONTS_PRECONNECT) {
120
+ if (doc.head.querySelector(`link[${exports.GOOGLE_FONTS_MARKER}="preconnect"][href="${host}"]`)) {
121
+ continue;
122
+ }
123
+ const pre = doc.createElement('link');
124
+ pre.rel = 'preconnect';
125
+ pre.href = host;
126
+ if (host.includes('gstatic'))
127
+ pre.crossOrigin = 'anonymous';
128
+ pre.setAttribute(exports.GOOGLE_FONTS_MARKER, 'preconnect');
129
+ doc.head.appendChild(pre);
130
+ }
131
+ }
132
+ const link = doc.createElement('link');
133
+ link.rel = 'stylesheet';
134
+ link.href = href;
135
+ link.setAttribute(exports.GOOGLE_FONTS_MARKER, 'stylesheet');
136
+ doc.head.appendChild(link);
137
+ return link;
138
+ }
139
+ //# sourceMappingURL=google-fonts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google-fonts.js","sourceRoot":"","sources":["../../../../../../../libs/public/keypuncherlabs/live-preview/src/lib/google-fonts.ts"],"names":[],"mappings":"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"}
@@ -7,8 +7,11 @@
7
7
  * `live-preview-` prefix so it is easy to identify and unlikely to collide with
8
8
  * other postMessage traffic on the same window.
9
9
  */
10
+ import type { GoogleFontDef } from './google-fonts';
10
11
  /** Message type for pushing a CSS payload into an embedded preview. */
11
12
  export declare const LIVE_PREVIEW_CSS_TYPE: "live-preview-css";
13
+ /** Message type for asking an embedded preview to load Google Fonts. */
14
+ export declare const LIVE_PREVIEW_GOOGLE_FONTS_TYPE: "live-preview-google-fonts";
12
15
  /** Message type a receiver/relay emits upward once it is ready to apply CSS. */
13
16
  export declare const LIVE_PREVIEW_READY_TYPE: "live-preview-ready";
14
17
  /** Carries a CSS string from a parent app to an embedded preview. */
@@ -16,6 +19,14 @@ export interface LivePreviewCssMessage {
16
19
  type: typeof LIVE_PREVIEW_CSS_TYPE;
17
20
  css: string;
18
21
  }
22
+ /**
23
+ * Asks an embedded preview to load Google Fonts. Carries only family names and
24
+ * weights — never URLs; the receiver builds the Google CDN URL itself.
25
+ */
26
+ export interface LivePreviewGoogleFontsMessage {
27
+ type: typeof LIVE_PREVIEW_GOOGLE_FONTS_TYPE;
28
+ fonts: GoogleFontDef[];
29
+ }
19
30
  /**
20
31
  * Announces that a receiver (or relay) has attached its listener. A parent app
21
32
  * can wait for this before sending so the initial CSS is not lost to a startup
@@ -24,8 +35,10 @@ export interface LivePreviewCssMessage {
24
35
  export interface LivePreviewReadyMessage {
25
36
  type: typeof LIVE_PREVIEW_READY_TYPE;
26
37
  }
27
- export type LivePreviewMessage = LivePreviewCssMessage | LivePreviewReadyMessage;
38
+ export type LivePreviewMessage = LivePreviewCssMessage | LivePreviewGoogleFontsMessage | LivePreviewReadyMessage;
28
39
  /** Narrows arbitrary `postMessage` data to a CSS message. */
29
40
  export declare function isCssMessage(value: unknown): value is LivePreviewCssMessage;
41
+ /** Narrows arbitrary `postMessage` data to a Google Fonts message. */
42
+ export declare function isGoogleFontsMessage(value: unknown): value is LivePreviewGoogleFontsMessage;
30
43
  /** Narrows arbitrary `postMessage` data to a ready message. */
31
44
  export declare function isReadyMessage(value: unknown): value is LivePreviewReadyMessage;
@@ -9,11 +9,14 @@
9
9
  * other postMessage traffic on the same window.
10
10
  */
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.LIVE_PREVIEW_READY_TYPE = exports.LIVE_PREVIEW_CSS_TYPE = void 0;
12
+ exports.LIVE_PREVIEW_READY_TYPE = exports.LIVE_PREVIEW_GOOGLE_FONTS_TYPE = exports.LIVE_PREVIEW_CSS_TYPE = void 0;
13
13
  exports.isCssMessage = isCssMessage;
14
+ exports.isGoogleFontsMessage = isGoogleFontsMessage;
14
15
  exports.isReadyMessage = isReadyMessage;
15
16
  /** Message type for pushing a CSS payload into an embedded preview. */
16
17
  exports.LIVE_PREVIEW_CSS_TYPE = 'live-preview-css';
18
+ /** Message type for asking an embedded preview to load Google Fonts. */
19
+ exports.LIVE_PREVIEW_GOOGLE_FONTS_TYPE = 'live-preview-google-fonts';
17
20
  /** Message type a receiver/relay emits upward once it is ready to apply CSS. */
18
21
  exports.LIVE_PREVIEW_READY_TYPE = 'live-preview-ready';
19
22
  /** Narrows arbitrary `postMessage` data to a CSS message. */
@@ -23,6 +26,13 @@ function isCssMessage(value) {
23
26
  value.type === exports.LIVE_PREVIEW_CSS_TYPE &&
24
27
  typeof value.css === 'string');
25
28
  }
29
+ /** Narrows arbitrary `postMessage` data to a Google Fonts message. */
30
+ function isGoogleFontsMessage(value) {
31
+ return (typeof value === 'object' &&
32
+ value !== null &&
33
+ value.type === exports.LIVE_PREVIEW_GOOGLE_FONTS_TYPE &&
34
+ Array.isArray(value.fonts));
35
+ }
26
36
  /** Narrows arbitrary `postMessage` data to a ready message. */
27
37
  function isReadyMessage(value) {
28
38
  return (typeof value === 'object' &&
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../../../../../../libs/public/keypuncherlabs/live-preview/src/lib/protocol.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;AA0BH,oCAOC;AAGD,wCAMC;AAxCD,uEAAuE;AAC1D,QAAA,qBAAqB,GAAG,kBAA2B,CAAC;AAEjE,gFAAgF;AACnE,QAAA,uBAAuB,GAAG,oBAA6B,CAAC;AAmBrE,6DAA6D;AAC7D,SAAgB,YAAY,CAAC,KAAc;IACzC,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACb,KAA4B,CAAC,IAAI,KAAK,6BAAqB;QAC5D,OAAQ,KAA2B,CAAC,GAAG,KAAK,QAAQ,CACrD,CAAC;AACJ,CAAC;AAED,+DAA+D;AAC/D,SAAgB,cAAc,CAAC,KAAc;IAC3C,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACb,KAA4B,CAAC,IAAI,KAAK,+BAAuB,CAC/D,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../../../../../../libs/public/keypuncherlabs/live-preview/src/lib/protocol.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;AA2CH,oCAOC;AAGD,oDAOC;AAGD,wCAMC;AAjED,uEAAuE;AAC1D,QAAA,qBAAqB,GAAG,kBAA2B,CAAC;AAEjE,wEAAwE;AAC3D,QAAA,8BAA8B,GAAG,2BAAoC,CAAC;AAEnF,gFAAgF;AACnE,QAAA,uBAAuB,GAAG,oBAA6B,CAAC;AA+BrE,6DAA6D;AAC7D,SAAgB,YAAY,CAAC,KAAc;IACzC,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACb,KAA4B,CAAC,IAAI,KAAK,6BAAqB;QAC5D,OAAQ,KAA2B,CAAC,GAAG,KAAK,QAAQ,CACrD,CAAC;AACJ,CAAC;AAED,sEAAsE;AACtE,SAAgB,oBAAoB,CAAC,KAAc;IACjD,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACb,KAA4B,CAAC,IAAI,KAAK,sCAA8B;QACrE,KAAK,CAAC,OAAO,CAAE,KAA6B,CAAC,KAAK,CAAC,CACpD,CAAC;AACJ,CAAC;AAED,+DAA+D;AAC/D,SAAgB,cAAc,CAAC,KAAc;IAC3C,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACb,KAA4B,CAAC,IAAI,KAAK,+BAAuB,CAC/D,CAAC;AACJ,CAAC"}
@@ -15,6 +15,7 @@
15
15
  * markup cannot be injected and the DOM does not grow on repeated updates.
16
16
  */
17
17
  import { type CssValidationOptions } from './validate-css';
18
+ import { type GoogleFontDef, type GoogleFontsValidationOptions } from './google-fonts';
18
19
  export declare const DEFAULT_STYLE_ID = "live-preview-styles";
19
20
  export interface LivePreviewReceiverOptions {
20
21
  /**
@@ -30,8 +31,14 @@ export interface LivePreviewReceiverOptions {
30
31
  styleId?: string;
31
32
  /** Validation overrides applied to incoming CSS. */
32
33
  validation?: CssValidationOptions;
34
+ /** Validation overrides applied to incoming Google Fonts. */
35
+ fontsValidation?: GoogleFontsValidationOptions;
36
+ /** Load Google Fonts requested via `live-preview-google-fonts`. Default `true`. */
37
+ loadGoogleFonts?: boolean;
33
38
  /** Called after CSS is successfully applied. */
34
39
  onApply?: (css: string) => void;
40
+ /** Called after Google Fonts are successfully loaded. */
41
+ onGoogleFontsApply?: (fonts: GoogleFontDef[]) => void;
35
42
  /** Called when a message is rejected, with a human-readable reason. */
36
43
  onReject?: (reason: string) => void;
37
44
  }
@@ -20,6 +20,7 @@ exports.DEFAULT_STYLE_ID = void 0;
20
20
  exports.startLivePreviewReceiver = startLivePreviewReceiver;
21
21
  const protocol_1 = require("./protocol");
22
22
  const validate_css_1 = require("./validate-css");
23
+ const google_fonts_1 = require("./google-fonts");
23
24
  exports.DEFAULT_STYLE_ID = 'live-preview-styles';
24
25
  const isOriginAllowed = (origin, allowed) => allowed.includes('*') || allowed.includes(origin);
25
26
  const getOrCreateStyle = (doc, styleId) => {
@@ -34,7 +35,7 @@ const getOrCreateStyle = (doc, styleId) => {
34
35
  return style;
35
36
  };
36
37
  function startLivePreviewReceiver(options) {
37
- const { allowedOrigins, target = document, source = window, styleId = exports.DEFAULT_STYLE_ID, validation, onApply, onReject, } = options;
38
+ const { allowedOrigins, target = document, source = window, styleId = exports.DEFAULT_STYLE_ID, validation, fontsValidation, loadGoogleFonts: enableFonts = true, onApply, onGoogleFontsApply, onReject, } = options;
38
39
  if (!allowedOrigins || allowedOrigins.length === 0) {
39
40
  throw new Error('startLivePreviewReceiver requires a non-empty allowedOrigins list');
40
41
  }
@@ -42,16 +43,25 @@ function startLivePreviewReceiver(options) {
42
43
  if (!isOriginAllowed(event.origin, allowedOrigins)) {
43
44
  return; // Silently ignore untrusted origins — not actionable to the page.
44
45
  }
45
- if (!(0, protocol_1.isCssMessage)(event.data)) {
46
+ if ((0, protocol_1.isCssMessage)(event.data)) {
47
+ const result = (0, validate_css_1.validateCss)(event.data.css, validation);
48
+ if (!result.valid) {
49
+ onReject === null || onReject === void 0 ? void 0 : onReject(`rejected css from ${event.origin}: ${result.errors.join('; ')}`);
50
+ return;
51
+ }
52
+ getOrCreateStyle(target, styleId).textContent = result.css;
53
+ onApply === null || onApply === void 0 ? void 0 : onApply(result.css);
46
54
  return;
47
55
  }
48
- const result = (0, validate_css_1.validateCss)(event.data.css, validation);
49
- if (!result.valid) {
50
- onReject === null || onReject === void 0 ? void 0 : onReject(`rejected css from ${event.origin}: ${result.errors.join('; ')}`);
51
- return;
56
+ if (enableFonts && (0, protocol_1.isGoogleFontsMessage)(event.data)) {
57
+ const result = (0, google_fonts_1.validateGoogleFonts)(event.data.fonts, fontsValidation);
58
+ if (!result.valid) {
59
+ onReject === null || onReject === void 0 ? void 0 : onReject(`rejected google fonts from ${event.origin}: ${result.errors.join('; ')}`);
60
+ return;
61
+ }
62
+ (0, google_fonts_1.loadGoogleFonts)(result.fonts, { document: target });
63
+ onGoogleFontsApply === null || onGoogleFontsApply === void 0 ? void 0 : onGoogleFontsApply(result.fonts);
52
64
  }
53
- getOrCreateStyle(target, styleId).textContent = result.css;
54
- onApply === null || onApply === void 0 ? void 0 : onApply(result.css);
55
65
  };
56
66
  source.addEventListener('message', handleMessage);
57
67
  // Announce readiness upward so a parent can (re)send the current CSS without
@@ -1 +1 @@
1
- {"version":3,"file":"receiver.js","sourceRoot":"","sources":["../../../../../../../libs/public/keypuncherlabs/live-preview/src/lib/receiver.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;GAeG;;;AAmDH,4DAiDC;AAlGD,yCAIoB;AACpB,iDAAwE;AAE3D,QAAA,gBAAgB,GAAG,qBAAqB,CAAC;AA2BtD,MAAM,eAAe,GAAG,CAAC,MAAc,EAAE,OAAiB,EAAW,EAAE,CACrE,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAEpD,MAAM,gBAAgB,GAAG,CAAC,GAAa,EAAE,OAAe,EAAoB,EAAE;IAC5E,MAAM,QAAQ,GAAG,GAAG,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;IAC7C,IAAI,QAAQ,YAAY,gBAAgB,EAAE,CAAC;QACzC,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,MAAM,KAAK,GAAG,GAAG,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IACzC,KAAK,CAAC,EAAE,GAAG,OAAO,CAAC;IACnB,KAAK,CAAC,YAAY,CAAC,mBAAmB,EAAE,EAAE,CAAC,CAAC;IAC5C,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IAC5B,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAEF,SAAgB,wBAAwB,CACtC,OAAmC;IAEnC,MAAM,EACJ,cAAc,EACd,MAAM,GAAG,QAAQ,EACjB,MAAM,GAAG,MAAM,EACf,OAAO,GAAG,wBAAgB,EAC1B,UAAU,EACV,OAAO,EACP,QAAQ,GACT,GAAG,OAAO,CAAC;IAEZ,IAAI,CAAC,cAAc,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnD,MAAM,IAAI,KAAK,CAAC,mEAAmE,CAAC,CAAC;IACvF,CAAC;IAED,MAAM,aAAa,GAAG,CAAC,KAAmB,EAAQ,EAAE;QAClD,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,MAAM,EAAE,cAAc,CAAC,EAAE,CAAC;YACnD,OAAO,CAAC,kEAAkE;QAC5E,CAAC;QACD,IAAI,CAAC,IAAA,uBAAY,EAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9B,OAAO;QACT,CAAC;QAED,MAAM,MAAM,GAAG,IAAA,0BAAW,EAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QACvD,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YAClB,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAG,qBAAqB,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC7E,OAAO;QACT,CAAC;QAED,gBAAgB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,WAAW,GAAG,MAAM,CAAC,GAAG,CAAC;QAC3D,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC,CAAC;IAEF,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;IAElD,6EAA6E;IAC7E,qCAAqC;IACrC,IAAI,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QAC9C,MAAM,KAAK,GAA4B,EAAE,IAAI,EAAE,kCAAuB,EAAE,CAAC;QACzE,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACxC,CAAC;IAED,OAAO;QACL,IAAI;YACF,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;QACvD,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"receiver.js","sourceRoot":"","sources":["../../../../../../../libs/public/keypuncherlabs/live-preview/src/lib/receiver.ts"],"names":[],"mappings":"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"}
@@ -14,6 +14,7 @@
14
14
  * the original sender's readiness handshake works end to end.
15
15
  */
16
16
  import { type CssValidationOptions } from './validate-css';
17
+ import { type GoogleFontsValidationOptions } from './google-fonts';
17
18
  export interface LivePreviewRelayOptions {
18
19
  /** Parent origins permitted to send CSS. Required. `['*']` opts out (dev). */
19
20
  allowedOrigins: string[];
@@ -25,7 +26,9 @@ export interface LivePreviewRelayOptions {
25
26
  source?: Window;
26
27
  /** Validation overrides applied to relayed CSS. */
27
28
  validation?: CssValidationOptions;
28
- /** Called when CSS is rejected before forwarding, with a reason. */
29
+ /** Validation overrides applied to relayed Google Fonts. */
30
+ fontsValidation?: GoogleFontsValidationOptions;
31
+ /** Called when a message is rejected before forwarding, with a reason. */
29
32
  onReject?: (reason: string) => void;
30
33
  }
31
34
  export interface LivePreviewRelay {
package/src/lib/relay.js CHANGED
@@ -18,9 +18,26 @@ Object.defineProperty(exports, "__esModule", { value: true });
18
18
  exports.createLivePreviewRelay = createLivePreviewRelay;
19
19
  const protocol_1 = require("./protocol");
20
20
  const validate_css_1 = require("./validate-css");
21
- const isOriginAllowed = (origin, allowed) => allowed.includes('*') || allowed.includes(origin);
21
+ const google_fonts_1 = require("./google-fonts");
22
+ // An allowlist entry may be an exact origin, the lone `'*'` (allow any — dev
23
+ // opt-out), or a wildcard pattern such as `https://*.app.example.com`. In a
24
+ // pattern, `*` matches exactly one DNS label (no dots), so it never crosses a
25
+ // domain boundary into a different parent domain.
26
+ const isOriginAllowed = (origin, allowed) => allowed.some((entry) => {
27
+ if (entry === '*')
28
+ return true;
29
+ if (!entry.includes('*'))
30
+ return entry === origin;
31
+ const pattern = '^' +
32
+ entry
33
+ .split('*')
34
+ .map((part) => part.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
35
+ .join('[^.]+') +
36
+ '$';
37
+ return new RegExp(pattern).test(origin);
38
+ });
22
39
  function createLivePreviewRelay(options) {
23
- const { allowedOrigins, getTargetWindow, targetOrigin, source = window, validation, onReject, } = options;
40
+ const { allowedOrigins, getTargetWindow, targetOrigin, source = window, validation, fontsValidation, onReject, } = options;
24
41
  if (!allowedOrigins || allowedOrigins.length === 0) {
25
42
  throw new Error('createLivePreviewRelay requires a non-empty allowedOrigins list');
26
43
  }
@@ -39,19 +56,32 @@ function createLivePreviewRelay(options) {
39
56
  if (!isOriginAllowed(event.origin, allowedOrigins)) {
40
57
  return;
41
58
  }
42
- if (!(0, protocol_1.isCssMessage)(event.data)) {
43
- return;
59
+ // Re-validate here (defense in depth) and forward the relevant message types
60
+ // to the child preview frame. The child re-validates again before applying.
61
+ let message = null;
62
+ if ((0, protocol_1.isCssMessage)(event.data)) {
63
+ const result = (0, validate_css_1.validateCss)(event.data.css, validation);
64
+ if (!result.valid) {
65
+ onReject === null || onReject === void 0 ? void 0 : onReject(`relay rejected css from ${event.origin}: ${result.errors.join('; ')}`);
66
+ return;
67
+ }
68
+ message = { type: protocol_1.LIVE_PREVIEW_CSS_TYPE, css: result.css };
69
+ }
70
+ else if ((0, protocol_1.isGoogleFontsMessage)(event.data)) {
71
+ const result = (0, google_fonts_1.validateGoogleFonts)(event.data.fonts, fontsValidation);
72
+ if (!result.valid) {
73
+ onReject === null || onReject === void 0 ? void 0 : onReject(`relay rejected google fonts from ${event.origin}: ${result.errors.join('; ')}`);
74
+ return;
75
+ }
76
+ message = { type: protocol_1.LIVE_PREVIEW_GOOGLE_FONTS_TYPE, fonts: result.fonts };
44
77
  }
45
- const result = (0, validate_css_1.validateCss)(event.data.css, validation);
46
- if (!result.valid) {
47
- onReject === null || onReject === void 0 ? void 0 : onReject(`relay rejected css from ${event.origin}: ${result.errors.join('; ')}`);
78
+ else {
48
79
  return;
49
80
  }
50
81
  const child = getTargetWindow();
51
82
  if (!child) {
52
83
  return; // Preview frame not mounted yet; the sender resends on ready.
53
84
  }
54
- const message = { type: protocol_1.LIVE_PREVIEW_CSS_TYPE, css: result.css };
55
85
  child.postMessage(message, targetOrigin);
56
86
  };
57
87
  source.addEventListener('message', handleMessage);
@@ -1 +1 @@
1
- {"version":3,"file":"relay.js","sourceRoot":"","sources":["../../../../../../../libs/public/keypuncherlabs/live-preview/src/lib/relay.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;AAiCH,wDAwDC;AAvFD,yCAKoB;AACpB,iDAAwE;AAsBxE,MAAM,eAAe,GAAG,CAAC,MAAc,EAAE,OAAiB,EAAW,EAAE,CACrE,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAEpD,SAAgB,sBAAsB,CAAC,OAAgC;IACrE,MAAM,EACJ,cAAc,EACd,eAAe,EACf,YAAY,EACZ,MAAM,GAAG,MAAM,EACf,UAAU,EACV,QAAQ,GACT,GAAG,OAAO,CAAC;IAEZ,IAAI,CAAC,cAAc,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnD,MAAM,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAC;IACrF,CAAC;IACD,IAAI,CAAC,YAAY,IAAI,YAAY,KAAK,GAAG,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CAAC,0EAA0E,CAAC,CAAC;IAC9F,CAAC;IAED,MAAM,aAAa,GAAG,CAAC,KAAmB,EAAQ,EAAE;QAClD,4EAA4E;QAC5E,kDAAkD;QAClD,IAAI,IAAA,yBAAc,EAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/B,IAAI,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;gBAC9C,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YAC7C,CAAC;YACD,OAAO;QACT,CAAC;QAED,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,MAAM,EAAE,cAAc,CAAC,EAAE,CAAC;YACnD,OAAO;QACT,CAAC;QACD,IAAI,CAAC,IAAA,uBAAY,EAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9B,OAAO;QACT,CAAC;QAED,MAAM,MAAM,GAAG,IAAA,0BAAW,EAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QACvD,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YAClB,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAG,2BAA2B,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACnF,OAAO;QACT,CAAC;QAED,MAAM,KAAK,GAAG,eAAe,EAAE,CAAC;QAChC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,CAAC,8DAA8D;QACxE,CAAC;QAED,MAAM,OAAO,GAA0B,EAAE,IAAI,EAAE,gCAAqB,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,CAAC;QACxF,KAAK,CAAC,WAAW,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IAC3C,CAAC,CAAC;IAEF,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;IAElD,OAAO;QACL,IAAI;YACF,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;QACvD,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"relay.js","sourceRoot":"","sources":["../../../../../../../libs/public/keypuncherlabs/live-preview/src/lib/relay.ts"],"names":[],"mappings":"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"}
@@ -7,6 +7,7 @@
7
7
  * never `'*'` — so a navigated-away or swapped iframe can never receive it.
8
8
  */
9
9
  import { type CssValidationOptions, type CssValidationResult } from './validate-css';
10
+ import { type GoogleFontDef, type GoogleFontsValidationOptions, type GoogleFontsValidationResult } from './google-fonts';
10
11
  export interface LivePreviewSenderOptions {
11
12
  /** The window to post to, e.g. `iframe.contentWindow`. */
12
13
  targetWindow: Window;
@@ -16,8 +17,10 @@ export interface LivePreviewSenderOptions {
16
17
  * document currently occupies the frame.
17
18
  */
18
19
  targetOrigin: string;
19
- /** Validation overrides applied before sending. */
20
+ /** Validation overrides applied before sending CSS. */
20
21
  validation?: CssValidationOptions;
22
+ /** Validation overrides applied before sending Google Fonts. */
23
+ fontsValidation?: GoogleFontsValidationOptions;
21
24
  }
22
25
  export interface LivePreviewSender {
23
26
  /**
@@ -25,5 +28,10 @@ export interface LivePreviewSender {
25
28
  * invalid, nothing is sent and `result.errors` explains why.
26
29
  */
27
30
  sendCss(css: string): CssValidationResult;
31
+ /**
32
+ * Validate and post a list of Google Fonts to load. Returns the validation
33
+ * result; when invalid, nothing is sent and `result.errors` explains why.
34
+ */
35
+ sendGoogleFonts(fonts: GoogleFontDef[]): GoogleFontsValidationResult;
28
36
  }
29
37
  export declare function createLivePreviewSender(options: LivePreviewSenderOptions): LivePreviewSender;
package/src/lib/sender.js CHANGED
@@ -11,8 +11,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
11
11
  exports.createLivePreviewSender = createLivePreviewSender;
12
12
  const protocol_1 = require("./protocol");
13
13
  const validate_css_1 = require("./validate-css");
14
+ const google_fonts_1 = require("./google-fonts");
14
15
  function createLivePreviewSender(options) {
15
- const { targetWindow, targetOrigin, validation } = options;
16
+ const { targetWindow, targetOrigin, validation, fontsValidation } = options;
16
17
  if (!targetOrigin || targetOrigin === '*') {
17
18
  throw new Error('createLivePreviewSender requires an explicit targetOrigin (cannot be "*")');
18
19
  }
@@ -26,6 +27,18 @@ function createLivePreviewSender(options) {
26
27
  targetWindow.postMessage(message, targetOrigin);
27
28
  return result;
28
29
  },
30
+ sendGoogleFonts(fonts) {
31
+ const result = (0, google_fonts_1.validateGoogleFonts)(fonts, fontsValidation);
32
+ if (!result.valid) {
33
+ return result;
34
+ }
35
+ const message = {
36
+ type: protocol_1.LIVE_PREVIEW_GOOGLE_FONTS_TYPE,
37
+ fonts: result.fonts,
38
+ };
39
+ targetWindow.postMessage(message, targetOrigin);
40
+ return result;
41
+ },
29
42
  };
30
43
  }
31
44
  //# sourceMappingURL=sender.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sender.js","sourceRoot":"","sources":["../../../../../../../libs/public/keypuncherlabs/live-preview/src/lib/sender.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;AA0BH,0DAqBC;AA7CD,yCAA+E;AAC/E,iDAAkG;AAuBlG,SAAgB,uBAAuB,CAAC,OAAiC;IACvE,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC;IAE3D,IAAI,CAAC,YAAY,IAAI,YAAY,KAAK,GAAG,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CACb,2EAA2E,CAC5E,CAAC;IACJ,CAAC;IAED,OAAO;QACL,OAAO,CAAC,GAAW;YACjB,MAAM,MAAM,GAAG,IAAA,0BAAW,EAAC,GAAG,EAAE,UAAU,CAAC,CAAC;YAC5C,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;gBAClB,OAAO,MAAM,CAAC;YAChB,CAAC;YAED,MAAM,OAAO,GAA0B,EAAE,IAAI,EAAE,gCAAqB,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,CAAC;YACxF,YAAY,CAAC,WAAW,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;YAChD,OAAO,MAAM,CAAC;QAChB,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"sender.js","sourceRoot":"","sources":["../../../../../../../libs/public/keypuncherlabs/live-preview/src/lib/sender.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;AA4CH,0DAmCC;AA7ED,yCAKoB;AACpB,iDAAkG;AAClG,iDAKwB;AA8BxB,SAAgB,uBAAuB,CAAC,OAAiC;IACvE,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC;IAE5E,IAAI,CAAC,YAAY,IAAI,YAAY,KAAK,GAAG,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CACb,2EAA2E,CAC5E,CAAC;IACJ,CAAC;IAED,OAAO;QACL,OAAO,CAAC,GAAW;YACjB,MAAM,MAAM,GAAG,IAAA,0BAAW,EAAC,GAAG,EAAE,UAAU,CAAC,CAAC;YAC5C,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;gBAClB,OAAO,MAAM,CAAC;YAChB,CAAC;YAED,MAAM,OAAO,GAA0B,EAAE,IAAI,EAAE,gCAAqB,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,CAAC;YACxF,YAAY,CAAC,WAAW,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;YAChD,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,eAAe,CAAC,KAAsB;YACpC,MAAM,MAAM,GAAG,IAAA,kCAAmB,EAAC,KAAK,EAAE,eAAe,CAAC,CAAC;YAC3D,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;gBAClB,OAAO,MAAM,CAAC;YAChB,CAAC;YAED,MAAM,OAAO,GAAkC;gBAC7C,IAAI,EAAE,yCAA8B;gBACpC,KAAK,EAAE,MAAM,CAAC,KAAK;aACpB,CAAC;YACF,YAAY,CAAC,WAAW,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;YAChD,OAAO,MAAM,CAAC;QAChB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -25,7 +25,9 @@ exports.DEFAULT_ALLOWED_URL_SCHEMES = ['https', 'data'];
25
25
  const FORBIDDEN_URL_SCHEMES = ['javascript', 'vbscript', 'file'];
26
26
  // Matches any ASCII control character except tab (\t \x09), newline (\n \x0a)
27
27
  // and carriage return (\r \x0d), which are legitimate whitespace in CSS. Built
28
- // from escapes so no literal control bytes live in this source file.
28
+ // from escapes so no literal control bytes live in this source file. Matching
29
+ // control characters is the whole point here, so the rule is intentionally off.
30
+ // eslint-disable-next-line no-control-regex
29
31
  const CONTROL_CHARS = new RegExp('[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]');
30
32
  // Active-content / breakout constructs that have no place in injected CSS.
31
33
  const FORBIDDEN_PATTERNS = [
@@ -1 +1 @@
1
- {"version":3,"file":"validate-css.js","sourceRoot":"","sources":["../../../../../../../libs/public/keypuncherlabs/live-preview/src/lib/validate-css.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;;AAgEH,kCAqDC;AAnHD,+EAA+E;AAClE,QAAA,sBAAsB,GAAG,MAAO,CAAC;AAE9C,8EAA8E;AACjE,QAAA,2BAA2B,GAAG,CAAC,OAAO,EAAE,MAAM,CAAU,CAAC;AAEtE,4EAA4E;AAC5E,MAAM,qBAAqB,GAAG,CAAC,YAAY,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;AAyBjE,8EAA8E;AAC9E,+EAA+E;AAC/E,qEAAqE;AACrE,MAAM,aAAa,GAAG,IAAI,MAAM,CAAC,yCAAyC,CAAC,CAAC;AAE5E,2EAA2E;AAC3E,MAAM,kBAAkB,GAAwD;IAC9E,EAAE,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,4CAA4C,EAAE;IACnF,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,yBAAyB,EAAE;IAC9D,EAAE,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,2BAA2B,EAAE;IAClE,EAAE,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,+BAA+B,EAAE;IACzE,EAAE,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,kCAAkC,EAAE;IAC3E,EAAE,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,gCAAgC,EAAE;IACvE,EAAE,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,qCAAqC,EAAE;IAC5E,EAAE,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,kCAAkC,EAAE;IACzE,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,6BAA6B,EAAE;CACnE,CAAC;AAEF,uEAAuE;AACvE,MAAM,WAAW,GAAG,mCAAmC,CAAC;AAExD,MAAM,QAAQ,GAAG,CAAC,MAAc,EAAiB,EAAE;IACjD,MAAM,KAAK,GAAG,8BAA8B,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC1D,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAC/C,CAAC,CAAC;AAEF;;;GAGG;AACH,SAAgB,WAAW,CACzB,KAAc,EACd,UAAgC,EAAE;IAElC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,EACJ,SAAS,GAAG,8BAAsB,EAClC,aAAa,GAAG,KAAK,EACrB,iBAAiB,GAAG,KAAK,EACzB,iBAAiB,GAAG,mCAA2B,GAChD,GAAG,OAAO,CAAC;IAEZ,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,sBAAsB,CAAC,EAAE,CAAC;IACrE,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC;QAC7B,MAAM,CAAC,IAAI,CAAC,qCAAqC,SAAS,aAAa,CAAC,CAAC;IAC3E,CAAC;IAED,KAAK,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,kBAAkB,EAAE,CAAC;QACtD,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACxB,MAAM,CAAC,IAAI,CAAC,OAAO,OAAO,EAAE,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAED,IAAI,CAAC,aAAa,IAAI,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/C,MAAM,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;IAC9C,CAAC;IAED,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;QACxE,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;YAChD,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YAClC,wDAAwD;YACxD,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBACjD,MAAM,CAAC,IAAI,CAAC,2CAA2C,MAAM,GAAG,CAAC,CAAC;YACpE,CAAC;QACH,CAAC;IACH,CAAC;SAAM,CAAC;QACN,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;YAChD,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YAClC,IAAI,MAAM,KAAK,IAAI,IAAI,qBAAqB,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC9D,MAAM,CAAC,IAAI,CAAC,0CAA0C,MAAM,GAAG,CAAC,CAAC;YACnE,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC;IAC3C,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;AACjD,CAAC"}
1
+ {"version":3,"file":"validate-css.js","sourceRoot":"","sources":["../../../../../../../libs/public/keypuncherlabs/live-preview/src/lib/validate-css.ts"],"names":[],"mappings":"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"}