@vielzeug/sandbox 26.11.4 → 26.11.6

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/dist/_bridge.js CHANGED
@@ -1,4 +1,4 @@
1
- //#region src/_bridge.ts
1
+ // src/_bridge.ts
2
2
  function buildBridgeScript(e) {
3
3
  return `
4
4
  (function() {
@@ -67,7 +67,6 @@ window.addEventListener('DOMContentLoaded', function() {
67
67
  })();
68
68
  `.trim();
69
69
  }
70
- //#endregion
71
70
  export { buildBridgeScript };
72
71
 
73
72
  //# sourceMappingURL=_bridge.js.map
package/dist/_dev.js CHANGED
@@ -1,7 +1,6 @@
1
- //#region src/_dev.ts
1
+ // src/_dev.ts
2
2
  function warn(e) {}
3
3
  function devOnly(e) {}
4
- //#endregion
5
4
  export { devOnly, warn };
6
5
 
7
6
  //# sourceMappingURL=_dev.js.map
package/dist/_document.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { buildBridgeScript as e } from "./_bridge.js";
2
2
  import { buildCspFromOptions as t } from "./_policy.js";
3
- //#region src/_document.ts
3
+ // src/_document.ts
4
4
  function escapeCss(e) {
5
5
  return e.replace(/<\/style/gi, "\\3C /style");
6
6
  }
@@ -30,7 +30,6 @@ ${n}
30
30
  </body>
31
31
  </html>`;
32
32
  }
33
- //#endregion
34
33
  export { buildDocumentFromOptions };
35
34
 
36
35
  //# sourceMappingURL=_document.js.map
package/dist/_policy.cjs CHANGED
@@ -1,2 +1,2 @@
1
- const e=require("./errors.cjs");var t=5e3,n=/^[A-Za-z][A-Za-z0-9_-]*$/,r=/^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/,i=/^[A-Za-z0-9+/_-]+={0,2}$/;function fail(t,n){throw new e.SandboxConfigurationError(`${t} is invalid: ${String(n)}`)}function normalizeOrigin(e,t){if(typeof e!=`string`)return fail(t,e);let n;try{n=new URL(e)}catch{return fail(t,e)}return n.protocol!==`http:`&&n.protocol!==`https:`||n.username||n.password||n.pathname!==`/`||n.search||n.hash?fail(t,e):n.origin}function normalizeOrigins(e,t){return e===void 0?[]:Array.isArray(e)?e.map(e=>normalizeOrigin(e,t)):fail(t,e)}function normalizeScript(e){if(typeof e!=`string`)return fail(`scripts`,e);let t;try{t=new URL(e)}catch{return fail(`scripts`,e)}return t.protocol!==`http:`&&t.protocol!==`https:`?fail(`scripts`,e):t.href}function normalizeStyles(e){if(e===void 0)return{};if(typeof e!=`object`||!e||Array.isArray(e))return fail(`styles`,e);let t={};for(let[r,i]of Object.entries(e))(!n.test(r)||typeof i!=`string`)&&fail(`styles`,r),t[r]=i;return t}function normalizeSandboxOptions(e={}){(typeof e!=`object`||!e||Array.isArray(e))&&fail(`options`,e);let t=e.lang??`en`;r.test(t)||fail(`lang`,t),e.nonce!==void 0&&(!i.test(e.nonce)||e.nonce.length===0)&&fail(`nonce`,e.nonce),e.title!==void 0&&typeof e.title!=`string`&&fail(`title`,e.title);let n=e.readyTimeout,a=n??5e3;n!==void 0&&(n===null||typeof n!=`number`||!Number.isFinite(n)||n<=0||n>2**31-1)&&fail(`readyTimeout`,n),e.scripts!==void 0&&!Array.isArray(e.scripts)&&fail(`scripts`,e.scripts);let o=(e.scripts??[]).map(normalizeScript);return{allowedFontOrigins:normalizeOrigins(e.allowedFontOrigins,`allowedFontOrigins`),allowedImageOrigins:normalizeOrigins(e.allowedImageOrigins,`allowedImageOrigins`),allowedScriptOrigins:normalizeOrigins(e.allowedScriptOrigins,`allowedScriptOrigins`),allowedStyleOrigins:normalizeOrigins(e.allowedStyleOrigins,`allowedStyleOrigins`),lang:t,nonce:e.nonce,readyTimeout:a,scripts:o,styles:normalizeStyles(e.styles),title:e.title??``}}function buildCspFromOptions(e){let t=[`'unsafe-inline'`,...e.nonce?[`'nonce-${e.nonce}'`]:[],...e.allowedScriptOrigins,...e.scripts.map(e=>new URL(e).origin)].join(` `),n=[`'unsafe-inline'`,...e.allowedStyleOrigins].join(` `),r=[`data:`,...e.allowedImageOrigins].join(` `),i=e.allowedFontOrigins.join(` `)||`'none'`;return[`default-src 'none'`,`script-src ${t}`,`style-src ${n}`,`img-src ${r}`,`font-src ${i}`,`connect-src 'none'`,`form-action 'none'`,`base-uri 'none'`].join(`; `)}exports.DEFAULT_READY_TIMEOUT_MS=t,exports.buildCspFromOptions=buildCspFromOptions,exports.normalizeSandboxOptions=normalizeSandboxOptions;
1
+ const e=require("./errors.cjs");var t=5e3,n=/^[A-Za-z][A-Za-z0-9_-]*$/,r=/^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/,i=/^[A-Za-z0-9+/_-]+={0,2}$/;function fail(t,n){throw new e.SandboxConfigurationError(`${t} is invalid: ${String(n)}`)}function normalizeOrigin(e,t){if(typeof e!=`string`)return fail(t,e);let n;try{n=new URL(e)}catch{return fail(t,e)}return n.protocol!==`http:`&&n.protocol!==`https:`||n.username||n.password||n.pathname!==`/`||n.search||n.hash?fail(t,e):n.origin}function normalizeOrigins(e,t){return e===void 0?[]:Array.isArray(e)?e.map(e=>normalizeOrigin(e,t)):fail(t,e)}function normalizeScript(e){if(typeof e!=`string`)return fail(`scripts`,e);let t;try{t=new URL(e)}catch{return fail(`scripts`,e)}return t.protocol!==`http:`&&t.protocol!==`https:`?fail(`scripts`,e):t.href}function normalizeStyles(e){if(e===void 0)return{};if(typeof e!=`object`||!e||Array.isArray(e))return fail(`styles`,e);let t={};for(let[r,i]of Object.entries(e))n.test(r)&&typeof i==`string`||fail(`styles`,r),t[r]=i;return t}function normalizeSandboxOptions(e={}){(typeof e!=`object`||!e||Array.isArray(e))&&fail(`options`,e);let t=e.lang??`en`;r.test(t)||fail(`lang`,t),e.nonce===void 0||i.test(e.nonce)&&e.nonce.length!==0||fail(`nonce`,e.nonce),e.title!==void 0&&typeof e.title!=`string`&&fail(`title`,e.title);let n=e.readyTimeout,a=n??5e3;n!==void 0&&(n===null||typeof n!=`number`||!Number.isFinite(n)||n<=0||n>2**31-1)&&fail(`readyTimeout`,n),e.scripts!==void 0&&!Array.isArray(e.scripts)&&fail(`scripts`,e.scripts);let o=(e.scripts??[]).map(normalizeScript);return{allowedFontOrigins:normalizeOrigins(e.allowedFontOrigins,`allowedFontOrigins`),allowedImageOrigins:normalizeOrigins(e.allowedImageOrigins,`allowedImageOrigins`),allowedScriptOrigins:normalizeOrigins(e.allowedScriptOrigins,`allowedScriptOrigins`),allowedStyleOrigins:normalizeOrigins(e.allowedStyleOrigins,`allowedStyleOrigins`),lang:t,nonce:e.nonce,readyTimeout:a,scripts:o,styles:normalizeStyles(e.styles),title:e.title??``}}function buildCspFromOptions(e){let t=[`'unsafe-inline'`,...e.nonce?[`'nonce-${e.nonce}'`]:[],...e.allowedScriptOrigins,...e.scripts.map(e=>new URL(e).origin)].join(` `),n=[`'unsafe-inline'`,...e.allowedStyleOrigins].join(` `),r=[`data:`,...e.allowedImageOrigins].join(` `),i=e.allowedFontOrigins.join(` `)||`'none'`;return[`default-src 'none'`,`script-src ${t}`,`style-src ${n}`,`img-src ${r}`,`font-src ${i}`,`connect-src 'none'`,`form-action 'none'`,`base-uri 'none'`].join(`; `)}exports.DEFAULT_READY_TIMEOUT_MS=t,exports.buildCspFromOptions=buildCspFromOptions,exports.normalizeSandboxOptions=normalizeSandboxOptions;
2
2
  //# sourceMappingURL=_policy.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"_policy.cjs","names":["SandboxConfigurationError","require_errors","DEFAULT_READY_TIMEOUT_MS","ID","LANG","NONCE","field","value","url","values","styles","normalized","id","css","options","lang","rawReadyTimeout","readyTimeout","scripts","scriptOrigins","script","styleOrigins","imageOrigins","fontOrigins"],"sources":["../src/_policy.ts"],"sourcesContent":["import { SandboxConfigurationError } from './errors.js';\nimport type { SandboxOptions } from './types.js';\n\nexport interface NormalizedSandboxOptions {\n allowedFontOrigins: string[];\n allowedImageOrigins: string[];\n allowedScriptOrigins: string[];\n allowedStyleOrigins: string[];\n lang: string;\n nonce: string | undefined;\n readyTimeout: number;\n scripts: string[];\n styles: Record<string, string>;\n title: string;\n}\n\nexport const DEFAULT_READY_TIMEOUT_MS = 5000;\n\nconst ID = /^[A-Za-z][A-Za-z0-9_-]*$/;\nconst LANG = /^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/;\nconst NONCE = /^[A-Za-z0-9+/_-]+={0,2}$/;\n\nfunction fail(field: string, value: unknown): never {\n throw new SandboxConfigurationError(`${field} is invalid: ${String(value)}`);\n}\n\nfunction normalizeOrigin(value: unknown, field: string): string {\n if (typeof value !== 'string') return fail(field, value);\n\n let url: URL;\n\n try {\n url = new URL(value);\n } catch {\n return fail(field, value);\n }\n\n if (\n (url.protocol !== 'http:' && url.protocol !== 'https:') ||\n url.username ||\n url.password ||\n url.pathname !== '/' ||\n url.search ||\n url.hash\n ) {\n return fail(field, value);\n }\n\n return url.origin;\n}\n\nfunction normalizeOrigins(values: string[] | undefined, field: string): string[] {\n if (values === undefined) return [];\n if (!Array.isArray(values)) return fail(field, values);\n\n return values.map((value) => normalizeOrigin(value, field));\n}\n\nfunction normalizeScript(value: unknown): string {\n if (typeof value !== 'string') return fail('scripts', value);\n\n let url: URL;\n\n try {\n url = new URL(value);\n } catch {\n return fail('scripts', value);\n }\n\n if (url.protocol !== 'http:' && url.protocol !== 'https:') return fail('scripts', value);\n\n return url.href;\n}\n\nfunction normalizeStyles(styles: Record<string, string> | undefined): Record<string, string> {\n if (styles === undefined) return {};\n if (typeof styles !== 'object' || styles === null || Array.isArray(styles)) return fail('styles', styles);\n\n const normalized: Record<string, string> = {};\n\n for (const [id, css] of Object.entries(styles)) {\n if (!ID.test(id) || typeof css !== 'string') fail('styles', id);\n\n normalized[id] = css;\n }\n\n return normalized;\n}\n\nexport function normalizeSandboxOptions(options: SandboxOptions = {}): NormalizedSandboxOptions {\n if (typeof options !== 'object' || options === null || Array.isArray(options)) fail('options', options);\n\n const lang = options.lang ?? 'en';\n\n if (!LANG.test(lang)) fail('lang', lang);\n\n if (options.nonce !== undefined && (!NONCE.test(options.nonce) || options.nonce.length === 0)) {\n fail('nonce', options.nonce);\n }\n\n if (options.title !== undefined && typeof options.title !== 'string') fail('title', options.title);\n\n const rawReadyTimeout = options.readyTimeout;\n const readyTimeout = rawReadyTimeout ?? DEFAULT_READY_TIMEOUT_MS;\n\n if (\n rawReadyTimeout !== undefined &&\n (rawReadyTimeout === null ||\n typeof rawReadyTimeout !== 'number' ||\n !Number.isFinite(rawReadyTimeout) ||\n rawReadyTimeout <= 0 ||\n rawReadyTimeout > 2 ** 31 - 1)\n ) {\n fail('readyTimeout', rawReadyTimeout);\n }\n\n if (options.scripts !== undefined && !Array.isArray(options.scripts)) fail('scripts', options.scripts);\n\n const scripts = (options.scripts ?? []).map(normalizeScript);\n\n return {\n allowedFontOrigins: normalizeOrigins(options.allowedFontOrigins, 'allowedFontOrigins'),\n allowedImageOrigins: normalizeOrigins(options.allowedImageOrigins, 'allowedImageOrigins'),\n allowedScriptOrigins: normalizeOrigins(options.allowedScriptOrigins, 'allowedScriptOrigins'),\n allowedStyleOrigins: normalizeOrigins(options.allowedStyleOrigins, 'allowedStyleOrigins'),\n lang,\n nonce: options.nonce,\n readyTimeout,\n scripts,\n styles: normalizeStyles(options.styles),\n title: options.title ?? '',\n };\n}\n\nexport function buildCspFromOptions(options: NormalizedSandboxOptions): string {\n const scriptOrigins = [\n \"'unsafe-inline'\",\n ...(options.nonce ? [`'nonce-${options.nonce}'`] : []),\n ...options.allowedScriptOrigins,\n ...options.scripts.map((script) => new URL(script).origin),\n ].join(' ');\n const styleOrigins = [\"'unsafe-inline'\", ...options.allowedStyleOrigins].join(' ');\n const imageOrigins = ['data:', ...options.allowedImageOrigins].join(' ');\n const fontOrigins = options.allowedFontOrigins.join(' ') || \"'none'\";\n\n return [\n \"default-src 'none'\",\n `script-src ${scriptOrigins}`,\n `style-src ${styleOrigins}`,\n `img-src ${imageOrigins}`,\n `font-src ${fontOrigins}`,\n \"connect-src 'none'\",\n \"form-action 'none'\",\n \"base-uri 'none'\",\n ].join('; ');\n}\n\nexport function buildCsp(options: SandboxOptions = {}): string {\n return buildCspFromOptions(normalizeSandboxOptions(options));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"_policy.cjs","names":["SandboxConfigurationError","require_errors","DEFAULT_READY_TIMEOUT_MS","ID","LANG","NONCE","field","value","url","values","styles","normalized","id","css","options","lang","rawReadyTimeout","readyTimeout","scripts","scriptOrigins","script","styleOrigins","imageOrigins","fontOrigins"],"sources":["../src/_policy.ts"],"sourcesContent":["import { SandboxConfigurationError } from './errors.js';\nimport type { SandboxOptions } from './types.js';\n\nexport interface NormalizedSandboxOptions {\n allowedFontOrigins: string[];\n allowedImageOrigins: string[];\n allowedScriptOrigins: string[];\n allowedStyleOrigins: string[];\n lang: string;\n nonce: string | undefined;\n readyTimeout: number;\n scripts: string[];\n styles: Record<string, string>;\n title: string;\n}\n\nexport const DEFAULT_READY_TIMEOUT_MS = 5000;\n\nconst ID = /^[A-Za-z][A-Za-z0-9_-]*$/;\nconst LANG = /^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/;\nconst NONCE = /^[A-Za-z0-9+/_-]+={0,2}$/;\n\nfunction fail(field: string, value: unknown): never {\n throw new SandboxConfigurationError(`${field} is invalid: ${String(value)}`);\n}\n\nfunction normalizeOrigin(value: unknown, field: string): string {\n if (typeof value !== 'string') return fail(field, value);\n\n let url: URL;\n\n try {\n url = new URL(value);\n } catch {\n return fail(field, value);\n }\n\n if (\n (url.protocol !== 'http:' && url.protocol !== 'https:') ||\n url.username ||\n url.password ||\n url.pathname !== '/' ||\n url.search ||\n url.hash\n ) {\n return fail(field, value);\n }\n\n return url.origin;\n}\n\nfunction normalizeOrigins(values: string[] | undefined, field: string): string[] {\n if (values === undefined) return [];\n if (!Array.isArray(values)) return fail(field, values);\n\n return values.map((value) => normalizeOrigin(value, field));\n}\n\nfunction normalizeScript(value: unknown): string {\n if (typeof value !== 'string') return fail('scripts', value);\n\n let url: URL;\n\n try {\n url = new URL(value);\n } catch {\n return fail('scripts', value);\n }\n\n if (url.protocol !== 'http:' && url.protocol !== 'https:') return fail('scripts', value);\n\n return url.href;\n}\n\nfunction normalizeStyles(styles: Record<string, string> | undefined): Record<string, string> {\n if (styles === undefined) return {};\n if (typeof styles !== 'object' || styles === null || Array.isArray(styles)) return fail('styles', styles);\n\n const normalized: Record<string, string> = {};\n\n for (const [id, css] of Object.entries(styles)) {\n if (!ID.test(id) || typeof css !== 'string') fail('styles', id);\n\n normalized[id] = css;\n }\n\n return normalized;\n}\n\nexport function normalizeSandboxOptions(options: SandboxOptions = {}): NormalizedSandboxOptions {\n if (typeof options !== 'object' || options === null || Array.isArray(options)) fail('options', options);\n\n const lang = options.lang ?? 'en';\n\n if (!LANG.test(lang)) fail('lang', lang);\n\n if (options.nonce !== undefined && (!NONCE.test(options.nonce) || options.nonce.length === 0)) {\n fail('nonce', options.nonce);\n }\n\n if (options.title !== undefined && typeof options.title !== 'string') fail('title', options.title);\n\n const rawReadyTimeout = options.readyTimeout;\n const readyTimeout = rawReadyTimeout ?? DEFAULT_READY_TIMEOUT_MS;\n\n if (\n rawReadyTimeout !== undefined &&\n (rawReadyTimeout === null ||\n typeof rawReadyTimeout !== 'number' ||\n !Number.isFinite(rawReadyTimeout) ||\n rawReadyTimeout <= 0 ||\n rawReadyTimeout > 2 ** 31 - 1)\n ) {\n fail('readyTimeout', rawReadyTimeout);\n }\n\n if (options.scripts !== undefined && !Array.isArray(options.scripts)) fail('scripts', options.scripts);\n\n const scripts = (options.scripts ?? []).map(normalizeScript);\n\n return {\n allowedFontOrigins: normalizeOrigins(options.allowedFontOrigins, 'allowedFontOrigins'),\n allowedImageOrigins: normalizeOrigins(options.allowedImageOrigins, 'allowedImageOrigins'),\n allowedScriptOrigins: normalizeOrigins(options.allowedScriptOrigins, 'allowedScriptOrigins'),\n allowedStyleOrigins: normalizeOrigins(options.allowedStyleOrigins, 'allowedStyleOrigins'),\n lang,\n nonce: options.nonce,\n readyTimeout,\n scripts,\n styles: normalizeStyles(options.styles),\n title: options.title ?? '',\n };\n}\n\nexport function buildCspFromOptions(options: NormalizedSandboxOptions): string {\n const scriptOrigins = [\n \"'unsafe-inline'\",\n ...(options.nonce ? [`'nonce-${options.nonce}'`] : []),\n ...options.allowedScriptOrigins,\n ...options.scripts.map((script) => new URL(script).origin),\n ].join(' ');\n const styleOrigins = [\"'unsafe-inline'\", ...options.allowedStyleOrigins].join(' ');\n const imageOrigins = ['data:', ...options.allowedImageOrigins].join(' ');\n const fontOrigins = options.allowedFontOrigins.join(' ') || \"'none'\";\n\n return [\n \"default-src 'none'\",\n `script-src ${scriptOrigins}`,\n `style-src ${styleOrigins}`,\n `img-src ${imageOrigins}`,\n `font-src ${fontOrigins}`,\n \"connect-src 'none'\",\n \"form-action 'none'\",\n \"base-uri 'none'\",\n ].join('; ');\n}\n\nexport function buildCsp(options: SandboxOptions = {}): string {\n return buildCspFromOptions(normalizeSandboxOptions(options));\n}\n"],"mappings":"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"}
package/dist/_policy.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { SandboxConfigurationError as e } from "./errors.js";
2
- //#region src/_policy.ts
2
+ // src/_policy.ts
3
3
  var t = 5e3, n = /^[A-Za-z][A-Za-z0-9_-]*$/, r = /^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/, i = /^[A-Za-z0-9+/_-]+={0,2}$/;
4
4
  function fail(t, n) {
5
5
  throw new e(`${t} is invalid: ${String(n)}`);
@@ -31,13 +31,13 @@ function normalizeStyles(e) {
31
31
  if (e === void 0) return {};
32
32
  if (typeof e != "object" || !e || Array.isArray(e)) return fail("styles", e);
33
33
  let t = {};
34
- for (let [r, i] of Object.entries(e)) (!n.test(r) || typeof i != "string") && fail("styles", r), t[r] = i;
34
+ for (let [r, i] of Object.entries(e)) n.test(r) && typeof i == "string" || fail("styles", r), t[r] = i;
35
35
  return t;
36
36
  }
37
37
  function normalizeSandboxOptions(e = {}) {
38
38
  (typeof e != "object" || !e || Array.isArray(e)) && fail("options", e);
39
39
  let t = e.lang ?? "en";
40
- r.test(t) || fail("lang", t), e.nonce !== void 0 && (!i.test(e.nonce) || e.nonce.length === 0) && fail("nonce", e.nonce), e.title !== void 0 && typeof e.title != "string" && fail("title", e.title);
40
+ r.test(t) || fail("lang", t), e.nonce === void 0 || i.test(e.nonce) && e.nonce.length !== 0 || fail("nonce", e.nonce), e.title !== void 0 && typeof e.title != "string" && fail("title", e.title);
41
41
  let n = e.readyTimeout, a = n ?? 5e3;
42
42
  n !== void 0 && (n === null || typeof n != "number" || !Number.isFinite(n) || n <= 0 || n > 2 ** 31 - 1) && fail("readyTimeout", n), e.scripts !== void 0 && !Array.isArray(e.scripts) && fail("scripts", e.scripts);
43
43
  let o = (e.scripts ?? []).map(normalizeScript);
@@ -72,7 +72,6 @@ function buildCspFromOptions(e) {
72
72
  "base-uri 'none'"
73
73
  ].join("; ");
74
74
  }
75
- //#endregion
76
75
  export { t as DEFAULT_READY_TIMEOUT_MS, buildCspFromOptions, normalizeSandboxOptions };
77
76
 
78
77
  //# sourceMappingURL=_policy.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"_policy.js","names":["DEFAULT_READY_TIMEOUT_MS","ID","LANG","NONCE","SandboxConfigurationError","field","value","url","values","styles","normalized","id","css","options","lang","rawReadyTimeout","readyTimeout","scripts","scriptOrigins","script","styleOrigins","imageOrigins","fontOrigins"],"sources":["../src/_policy.ts"],"sourcesContent":["import { SandboxConfigurationError } from './errors.js';\nimport type { SandboxOptions } from './types.js';\n\nexport interface NormalizedSandboxOptions {\n allowedFontOrigins: string[];\n allowedImageOrigins: string[];\n allowedScriptOrigins: string[];\n allowedStyleOrigins: string[];\n lang: string;\n nonce: string | undefined;\n readyTimeout: number;\n scripts: string[];\n styles: Record<string, string>;\n title: string;\n}\n\nexport const DEFAULT_READY_TIMEOUT_MS = 5000;\n\nconst ID = /^[A-Za-z][A-Za-z0-9_-]*$/;\nconst LANG = /^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/;\nconst NONCE = /^[A-Za-z0-9+/_-]+={0,2}$/;\n\nfunction fail(field: string, value: unknown): never {\n throw new SandboxConfigurationError(`${field} is invalid: ${String(value)}`);\n}\n\nfunction normalizeOrigin(value: unknown, field: string): string {\n if (typeof value !== 'string') return fail(field, value);\n\n let url: URL;\n\n try {\n url = new URL(value);\n } catch {\n return fail(field, value);\n }\n\n if (\n (url.protocol !== 'http:' && url.protocol !== 'https:') ||\n url.username ||\n url.password ||\n url.pathname !== '/' ||\n url.search ||\n url.hash\n ) {\n return fail(field, value);\n }\n\n return url.origin;\n}\n\nfunction normalizeOrigins(values: string[] | undefined, field: string): string[] {\n if (values === undefined) return [];\n if (!Array.isArray(values)) return fail(field, values);\n\n return values.map((value) => normalizeOrigin(value, field));\n}\n\nfunction normalizeScript(value: unknown): string {\n if (typeof value !== 'string') return fail('scripts', value);\n\n let url: URL;\n\n try {\n url = new URL(value);\n } catch {\n return fail('scripts', value);\n }\n\n if (url.protocol !== 'http:' && url.protocol !== 'https:') return fail('scripts', value);\n\n return url.href;\n}\n\nfunction normalizeStyles(styles: Record<string, string> | undefined): Record<string, string> {\n if (styles === undefined) return {};\n if (typeof styles !== 'object' || styles === null || Array.isArray(styles)) return fail('styles', styles);\n\n const normalized: Record<string, string> = {};\n\n for (const [id, css] of Object.entries(styles)) {\n if (!ID.test(id) || typeof css !== 'string') fail('styles', id);\n\n normalized[id] = css;\n }\n\n return normalized;\n}\n\nexport function normalizeSandboxOptions(options: SandboxOptions = {}): NormalizedSandboxOptions {\n if (typeof options !== 'object' || options === null || Array.isArray(options)) fail('options', options);\n\n const lang = options.lang ?? 'en';\n\n if (!LANG.test(lang)) fail('lang', lang);\n\n if (options.nonce !== undefined && (!NONCE.test(options.nonce) || options.nonce.length === 0)) {\n fail('nonce', options.nonce);\n }\n\n if (options.title !== undefined && typeof options.title !== 'string') fail('title', options.title);\n\n const rawReadyTimeout = options.readyTimeout;\n const readyTimeout = rawReadyTimeout ?? DEFAULT_READY_TIMEOUT_MS;\n\n if (\n rawReadyTimeout !== undefined &&\n (rawReadyTimeout === null ||\n typeof rawReadyTimeout !== 'number' ||\n !Number.isFinite(rawReadyTimeout) ||\n rawReadyTimeout <= 0 ||\n rawReadyTimeout > 2 ** 31 - 1)\n ) {\n fail('readyTimeout', rawReadyTimeout);\n }\n\n if (options.scripts !== undefined && !Array.isArray(options.scripts)) fail('scripts', options.scripts);\n\n const scripts = (options.scripts ?? []).map(normalizeScript);\n\n return {\n allowedFontOrigins: normalizeOrigins(options.allowedFontOrigins, 'allowedFontOrigins'),\n allowedImageOrigins: normalizeOrigins(options.allowedImageOrigins, 'allowedImageOrigins'),\n allowedScriptOrigins: normalizeOrigins(options.allowedScriptOrigins, 'allowedScriptOrigins'),\n allowedStyleOrigins: normalizeOrigins(options.allowedStyleOrigins, 'allowedStyleOrigins'),\n lang,\n nonce: options.nonce,\n readyTimeout,\n scripts,\n styles: normalizeStyles(options.styles),\n title: options.title ?? '',\n };\n}\n\nexport function buildCspFromOptions(options: NormalizedSandboxOptions): string {\n const scriptOrigins = [\n \"'unsafe-inline'\",\n ...(options.nonce ? [`'nonce-${options.nonce}'`] : []),\n ...options.allowedScriptOrigins,\n ...options.scripts.map((script) => new URL(script).origin),\n ].join(' ');\n const styleOrigins = [\"'unsafe-inline'\", ...options.allowedStyleOrigins].join(' ');\n const imageOrigins = ['data:', ...options.allowedImageOrigins].join(' ');\n const fontOrigins = options.allowedFontOrigins.join(' ') || \"'none'\";\n\n return [\n \"default-src 'none'\",\n `script-src ${scriptOrigins}`,\n `style-src ${styleOrigins}`,\n `img-src ${imageOrigins}`,\n `font-src ${fontOrigins}`,\n \"connect-src 'none'\",\n \"form-action 'none'\",\n \"base-uri 'none'\",\n ].join('; ');\n}\n\nexport function buildCsp(options: SandboxOptions = {}): string {\n return buildCspFromOptions(normalizeSandboxOptions(options));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"_policy.js","names":["DEFAULT_READY_TIMEOUT_MS","ID","LANG","NONCE","SandboxConfigurationError","field","value","url","values","styles","normalized","id","css","options","lang","rawReadyTimeout","readyTimeout","scripts","scriptOrigins","script","styleOrigins","imageOrigins","fontOrigins"],"sources":["../src/_policy.ts"],"sourcesContent":["import { SandboxConfigurationError } from './errors.js';\nimport type { SandboxOptions } from './types.js';\n\nexport interface NormalizedSandboxOptions {\n allowedFontOrigins: string[];\n allowedImageOrigins: string[];\n allowedScriptOrigins: string[];\n allowedStyleOrigins: string[];\n lang: string;\n nonce: string | undefined;\n readyTimeout: number;\n scripts: string[];\n styles: Record<string, string>;\n title: string;\n}\n\nexport const DEFAULT_READY_TIMEOUT_MS = 5000;\n\nconst ID = /^[A-Za-z][A-Za-z0-9_-]*$/;\nconst LANG = /^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/;\nconst NONCE = /^[A-Za-z0-9+/_-]+={0,2}$/;\n\nfunction fail(field: string, value: unknown): never {\n throw new SandboxConfigurationError(`${field} is invalid: ${String(value)}`);\n}\n\nfunction normalizeOrigin(value: unknown, field: string): string {\n if (typeof value !== 'string') return fail(field, value);\n\n let url: URL;\n\n try {\n url = new URL(value);\n } catch {\n return fail(field, value);\n }\n\n if (\n (url.protocol !== 'http:' && url.protocol !== 'https:') ||\n url.username ||\n url.password ||\n url.pathname !== '/' ||\n url.search ||\n url.hash\n ) {\n return fail(field, value);\n }\n\n return url.origin;\n}\n\nfunction normalizeOrigins(values: string[] | undefined, field: string): string[] {\n if (values === undefined) return [];\n if (!Array.isArray(values)) return fail(field, values);\n\n return values.map((value) => normalizeOrigin(value, field));\n}\n\nfunction normalizeScript(value: unknown): string {\n if (typeof value !== 'string') return fail('scripts', value);\n\n let url: URL;\n\n try {\n url = new URL(value);\n } catch {\n return fail('scripts', value);\n }\n\n if (url.protocol !== 'http:' && url.protocol !== 'https:') return fail('scripts', value);\n\n return url.href;\n}\n\nfunction normalizeStyles(styles: Record<string, string> | undefined): Record<string, string> {\n if (styles === undefined) return {};\n if (typeof styles !== 'object' || styles === null || Array.isArray(styles)) return fail('styles', styles);\n\n const normalized: Record<string, string> = {};\n\n for (const [id, css] of Object.entries(styles)) {\n if (!ID.test(id) || typeof css !== 'string') fail('styles', id);\n\n normalized[id] = css;\n }\n\n return normalized;\n}\n\nexport function normalizeSandboxOptions(options: SandboxOptions = {}): NormalizedSandboxOptions {\n if (typeof options !== 'object' || options === null || Array.isArray(options)) fail('options', options);\n\n const lang = options.lang ?? 'en';\n\n if (!LANG.test(lang)) fail('lang', lang);\n\n if (options.nonce !== undefined && (!NONCE.test(options.nonce) || options.nonce.length === 0)) {\n fail('nonce', options.nonce);\n }\n\n if (options.title !== undefined && typeof options.title !== 'string') fail('title', options.title);\n\n const rawReadyTimeout = options.readyTimeout;\n const readyTimeout = rawReadyTimeout ?? DEFAULT_READY_TIMEOUT_MS;\n\n if (\n rawReadyTimeout !== undefined &&\n (rawReadyTimeout === null ||\n typeof rawReadyTimeout !== 'number' ||\n !Number.isFinite(rawReadyTimeout) ||\n rawReadyTimeout <= 0 ||\n rawReadyTimeout > 2 ** 31 - 1)\n ) {\n fail('readyTimeout', rawReadyTimeout);\n }\n\n if (options.scripts !== undefined && !Array.isArray(options.scripts)) fail('scripts', options.scripts);\n\n const scripts = (options.scripts ?? []).map(normalizeScript);\n\n return {\n allowedFontOrigins: normalizeOrigins(options.allowedFontOrigins, 'allowedFontOrigins'),\n allowedImageOrigins: normalizeOrigins(options.allowedImageOrigins, 'allowedImageOrigins'),\n allowedScriptOrigins: normalizeOrigins(options.allowedScriptOrigins, 'allowedScriptOrigins'),\n allowedStyleOrigins: normalizeOrigins(options.allowedStyleOrigins, 'allowedStyleOrigins'),\n lang,\n nonce: options.nonce,\n readyTimeout,\n scripts,\n styles: normalizeStyles(options.styles),\n title: options.title ?? '',\n };\n}\n\nexport function buildCspFromOptions(options: NormalizedSandboxOptions): string {\n const scriptOrigins = [\n \"'unsafe-inline'\",\n ...(options.nonce ? [`'nonce-${options.nonce}'`] : []),\n ...options.allowedScriptOrigins,\n ...options.scripts.map((script) => new URL(script).origin),\n ].join(' ');\n const styleOrigins = [\"'unsafe-inline'\", ...options.allowedStyleOrigins].join(' ');\n const imageOrigins = ['data:', ...options.allowedImageOrigins].join(' ');\n const fontOrigins = options.allowedFontOrigins.join(' ') || \"'none'\";\n\n return [\n \"default-src 'none'\",\n `script-src ${scriptOrigins}`,\n `style-src ${styleOrigins}`,\n `img-src ${imageOrigins}`,\n `font-src ${fontOrigins}`,\n \"connect-src 'none'\",\n \"form-action 'none'\",\n \"base-uri 'none'\",\n ].join('; ');\n}\n\nexport function buildCsp(options: SandboxOptions = {}): string {\n return buildCspFromOptions(normalizeSandboxOptions(options));\n}\n"],"mappings":"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"}
package/dist/_protocol.js CHANGED
@@ -1,4 +1,4 @@
1
- //#region src/_protocol.ts
1
+ // src/_protocol.ts
2
2
  var e = "custom", t = "error", n = "html-replace", r = "ready", i = "resize", a = "state-update-all", o = "style-patch";
3
3
  function isProtocolMessage(e, t) {
4
4
  return typeof e == "object" && !!e && typeof e.channel == "string" && e.channel === t.channel && typeof e.generation == "number" && e.generation === t.generation && typeof e.type == "string";
@@ -15,7 +15,6 @@ function envelope(e, t) {
15
15
  generation: e.generation
16
16
  };
17
17
  }
18
- //#endregion
19
18
  export { e as MSG_CUSTOM, t as MSG_ERROR, n as MSG_HTML_REPLACE, r as MSG_READY, i as MSG_RESIZE, a as MSG_STATE_UPDATE_ALL, o as MSG_STYLE_PATCH, createChannel, envelope, isProtocolMessage };
20
19
 
21
20
  //# sourceMappingURL=_protocol.js.map
package/dist/_runtime.cjs CHANGED
@@ -1,2 +1,2 @@
1
- require("./_dev.cjs");const e=require("./errors.cjs"),t=require("./_policy.cjs"),n=require("./_document.cjs"),r=require("./_protocol.cjs");function createSandbox(i,a={}){let o=t.normalizeSandboxOptions(a),s=r.createChannel(),c={...o.styles},l=new Set,u={},d=new AbortController,f=null,p=!1,m=!1,h=0,g=null,_=null;function currentBootstrap(){return{channel:s,generation:h}}function clearReadyTimeout(){g!==null&&(clearTimeout(g),g=null)}function supersedePendingRender(){clearReadyTimeout(),_?.resolve(),_=null,m=!1}function broadcast(e){for(let t of l)try{t(e)}catch{}}function handleMessage(e){if(!f||e.source!==f.contentWindow)return;let t=currentBootstrap();if(r.isProtocolMessage(e.data,t))switch(e.data.type){case r.MSG_CUSTOM:typeof e.data.event==`string`&&broadcast({detail:e.data.detail,event:e.data.event,type:r.MSG_CUSTOM});break;case r.MSG_ERROR:typeof e.data.message==`string`&&broadcast({message:e.data.message,...typeof e.data.stack==`string`?{stack:e.data.stack}:{},type:r.MSG_ERROR});break;case r.MSG_READY:supersedePendingRender(),m=!0,flushPendingState();break;case r.MSG_RESIZE:typeof e.data.height==`number`&&Number.isFinite(e.data.height)&&e.data.height>=0&&broadcast({height:e.data.height,type:r.MSG_RESIZE})}}function ensureIframe(){return f||(f=document.createElement(`iframe`),f.setAttribute(`sandbox`,`allow-scripts`),f.setAttribute(`referrerpolicy`,`no-referrer`),f.dataset.sandboxChannel=s,i.appendChild(f),window.addEventListener(`message`,handleMessage),f)}function dispose(){p||(p=!0,supersedePendingRender(),d.abort(),f&&=(window.removeEventListener(`message`,handleMessage),f.remove(),null),l.clear())}function onMessage(e){return p?()=>{}:(l.add(e),()=>l.delete(e))}function render(t){if(p)return Promise.resolve();t.trim(),supersedePendingRender(),h+=1;let r=ensureIframe(),i=currentBootstrap(),a={...o,styles:{...c}};r.dataset.sandboxGeneration=String(h),r.srcdoc=n.buildDocumentFromOptions(t,a,i);let s=new Promise((e,t)=>{_={reject:t,resolve:e}});return g=setTimeout(()=>{_&&(_.reject(new e.SandboxTimeoutError(`render() did not receive a 'ready' signal within ${o.readyTimeout}ms. An injected script may have blocked document initialization.`)),_=null,g=null)},o.readyTimeout),s}function send(e){f?.contentWindow?.postMessage(r.envelope(currentBootstrap(),e),`*`)}function flushPendingState(){m&&f?.contentWindow&&Object.keys(u).length!==0&&send({record:{...u},type:r.MSG_STATE_UPDATE_ALL})}function setState(e){p||(Object.assign(u,e),m&&f?.contentWindow&&Object.keys(e).length>0&&send({record:e,type:r.MSG_STATE_UPDATE_ALL}))}function replaceBody(e){p||m&&f?.contentWindow&&send({html:e,type:r.MSG_HTML_REPLACE})}function updateStyle(e,t){p||(c[e]=t,m&&f?.contentWindow&&send({css:t,id:e,type:r.MSG_STYLE_PATCH}))}return{get disposalSignal(){return d.signal},dispose,get disposed(){return p},onMessage,render,replaceBody,setState,[Symbol.dispose](){dispose()},updateStyle}}exports.createSandbox=createSandbox;
1
+ require("./_dev.cjs");const e=require("./_document.cjs"),t=require("./_policy.cjs"),n=require("./_protocol.cjs"),r=require("./errors.cjs");function createSandbox(i,a={}){let o=t.normalizeSandboxOptions(a),s=n.createChannel(),c={...o.styles},l=new Set,u={},d=new AbortController,f=null,p=!1,m=!1,h=0,g=null,_=null;function currentBootstrap(){return{channel:s,generation:h}}function clearReadyTimeout(){g!==null&&(clearTimeout(g),g=null)}function supersedePendingRender(){clearReadyTimeout(),_?.resolve(),_=null,m=!1}function broadcast(e){for(let t of l)try{t(e)}catch{}}function handleMessage(e){if(!f||e.source!==f.contentWindow)return;let t=currentBootstrap();if(n.isProtocolMessage(e.data,t))switch(e.data.type){case n.MSG_CUSTOM:typeof e.data.event==`string`&&broadcast({detail:e.data.detail,event:e.data.event,type:n.MSG_CUSTOM});break;case n.MSG_ERROR:typeof e.data.message==`string`&&broadcast({message:e.data.message,...typeof e.data.stack==`string`?{stack:e.data.stack}:{},type:n.MSG_ERROR});break;case n.MSG_READY:supersedePendingRender(),m=!0,flushPendingState();break;case n.MSG_RESIZE:typeof e.data.height==`number`&&Number.isFinite(e.data.height)&&e.data.height>=0&&broadcast({height:e.data.height,type:n.MSG_RESIZE})}}function ensureIframe(){return f||(f=document.createElement(`iframe`),f.setAttribute(`sandbox`,`allow-scripts`),f.setAttribute(`referrerpolicy`,`no-referrer`),f.dataset.sandboxChannel=s,i.appendChild(f),window.addEventListener(`message`,handleMessage),f)}function dispose(){p||(p=!0,supersedePendingRender(),d.abort(),f&&=(window.removeEventListener(`message`,handleMessage),f.remove(),null),l.clear())}function onMessage(e){return p?()=>{}:(l.add(e),()=>l.delete(e))}function render(t){if(p)return Promise.resolve();t.trim(),supersedePendingRender(),h+=1;let n=ensureIframe(),i=currentBootstrap(),a={...o,styles:{...c}};n.dataset.sandboxGeneration=String(h),n.srcdoc=e.buildDocumentFromOptions(t,a,i);let s=new Promise((e,t)=>{_={reject:t,resolve:e}});return g=setTimeout(()=>{_&&(_.reject(new r.SandboxTimeoutError(`render() did not receive a 'ready' signal within ${o.readyTimeout}ms. An injected script may have blocked document initialization.`)),_=null,g=null)},o.readyTimeout),s}function send(e){f?.contentWindow?.postMessage(n.envelope(currentBootstrap(),e),`*`)}function flushPendingState(){m&&f?.contentWindow&&Object.keys(u).length!==0&&send({record:{...u},type:n.MSG_STATE_UPDATE_ALL})}function setState(e){p||(Object.assign(u,e),m&&f?.contentWindow&&Object.keys(e).length>0&&send({record:e,type:n.MSG_STATE_UPDATE_ALL}))}function replaceBody(e){p||m&&f?.contentWindow&&send({html:e,type:n.MSG_HTML_REPLACE})}function updateStyle(e,t){p||(c[e]=t,m&&f?.contentWindow&&send({css:t,id:e,type:n.MSG_STYLE_PATCH}))}return{get disposalSignal(){return d.signal},dispose,get disposed(){return p},onMessage,render,replaceBody,setState,[Symbol.dispose](){dispose()},updateStyle}}exports.createSandbox=createSandbox;
2
2
  //# sourceMappingURL=_runtime.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"_runtime.cjs","names":["normalizeSandboxOptions","createChannel","isProtocolMessage","MSG_CUSTOM","MSG_ERROR","MSG_READY","MSG_RESIZE","buildDocumentFromOptions","SandboxTimeoutError","envelope","MSG_STATE_UPDATE_ALL","MSG_HTML_REPLACE","MSG_STYLE_PATCH","require_errors","require__policy","require__document","require__protocol","normalized","options","channel","styles","listeners","pendingState","controller","iframe","disposed","bridgeReady","generation","readyTimeout","currentRender","listener","message","event","bootstrap","container","handler","html","current","documentOptions","promise","update","id","css"],"sources":["../src/_runtime.ts"],"sourcesContent":["import { devOnly, warn } from './_dev.js';\nimport { buildDocumentFromOptions } from './_document.js';\nimport { normalizeSandboxOptions } from './_policy.js';\nimport {\n type BridgeBootstrap,\n createChannel,\n envelope,\n isProtocolMessage,\n MSG_CUSTOM,\n MSG_ERROR,\n MSG_HTML_REPLACE,\n MSG_READY,\n MSG_RESIZE,\n MSG_STATE_UPDATE_ALL,\n MSG_STYLE_PATCH,\n} from './_protocol.js';\nimport { SandboxTimeoutError } from './errors.js';\nimport type { SandboxHandle, SandboxMessage, SandboxOptions } from './types.js';\n\nexport function createSandbox<State extends object = Record<string, unknown>>(\n container: HTMLElement,\n options: SandboxOptions = {},\n): SandboxHandle<State> {\n const normalized = normalizeSandboxOptions(options);\n const channel = createChannel();\n const styles: Record<string, string> = { ...normalized.styles };\n const listeners = new Set<(message: SandboxMessage) => void>();\n const pendingState: Partial<State> = {};\n const controller = new AbortController();\n let iframe: HTMLIFrameElement | null = null;\n let disposed = false;\n let bridgeReady = false;\n let generation = 0;\n let readyTimeout: ReturnType<typeof setTimeout> | null = null;\n let currentRender: { reject: (error: unknown) => void; resolve: () => void } | null = null;\n\n function currentBootstrap(): BridgeBootstrap {\n return { channel, generation };\n }\n\n function clearReadyTimeout(): void {\n if (readyTimeout === null) return;\n\n clearTimeout(readyTimeout);\n readyTimeout = null;\n }\n\n function supersedePendingRender(): void {\n clearReadyTimeout();\n currentRender?.resolve();\n currentRender = null;\n bridgeReady = false;\n }\n\n function broadcast(message: SandboxMessage): void {\n for (const listener of listeners) {\n try {\n listener(message);\n } catch {\n warn('onMessage() handler threw: remaining handlers will still run.');\n }\n }\n }\n\n function handleMessage(event: MessageEvent): void {\n if (!iframe || event.source !== iframe.contentWindow) return;\n\n const bootstrap = currentBootstrap();\n\n if (!isProtocolMessage(event.data, bootstrap)) return;\n\n switch (event.data.type) {\n case MSG_CUSTOM:\n if (typeof event.data.event === 'string') {\n broadcast({ detail: event.data.detail, event: event.data.event, type: MSG_CUSTOM });\n }\n\n break;\n case MSG_ERROR:\n if (typeof event.data.message === 'string') {\n broadcast({\n message: event.data.message,\n ...(typeof event.data.stack === 'string' ? { stack: event.data.stack } : {}),\n type: MSG_ERROR,\n });\n }\n\n break;\n case MSG_READY:\n supersedePendingRender();\n bridgeReady = true;\n flushPendingState();\n break;\n case MSG_RESIZE:\n if (typeof event.data.height === 'number' && Number.isFinite(event.data.height) && event.data.height >= 0) {\n broadcast({ height: event.data.height, type: MSG_RESIZE });\n }\n\n break;\n }\n }\n\n function ensureIframe(): HTMLIFrameElement {\n if (iframe) return iframe;\n\n iframe = document.createElement('iframe');\n iframe.setAttribute('sandbox', 'allow-scripts');\n iframe.setAttribute('referrerpolicy', 'no-referrer');\n iframe.dataset.sandboxChannel = channel;\n container.appendChild(iframe);\n window.addEventListener('message', handleMessage);\n\n return iframe;\n }\n\n function dispose(): void {\n if (disposed) return;\n\n disposed = true;\n supersedePendingRender();\n controller.abort();\n\n if (iframe) {\n window.removeEventListener('message', handleMessage);\n iframe.remove();\n iframe = null;\n }\n\n listeners.clear();\n }\n\n function onMessage(handler: (message: SandboxMessage) => void): () => void {\n if (disposed) {\n warn('onMessage() called on a disposed sandbox: handler will never fire.');\n\n return () => {};\n }\n\n listeners.add(handler);\n\n return () => listeners.delete(handler);\n }\n\n function render(html: string): Promise<void> {\n if (disposed) {\n warn('render() called on a disposed sandbox.');\n\n return Promise.resolve();\n }\n\n if (!html.trim()) warn('render() called with empty HTML.');\n\n supersedePendingRender();\n generation += 1;\n\n const current = ensureIframe();\n const bootstrap = currentBootstrap();\n const documentOptions = { ...normalized, styles: { ...styles } };\n\n current.dataset.sandboxGeneration = String(generation);\n current.srcdoc = buildDocumentFromOptions(html, documentOptions, bootstrap);\n\n const promise = new Promise<void>((resolve, reject) => {\n currentRender = { reject, resolve };\n });\n\n readyTimeout = setTimeout(() => {\n if (!currentRender) return;\n\n currentRender.reject(\n new SandboxTimeoutError(\n `render() did not receive a 'ready' signal within ${normalized.readyTimeout}ms. ` +\n 'An injected script may have blocked document initialization.',\n ),\n );\n currentRender = null;\n readyTimeout = null;\n }, normalized.readyTimeout);\n\n return promise;\n }\n\n function send(message: Record<string, unknown>): void {\n iframe?.contentWindow?.postMessage(envelope(currentBootstrap(), message), '*');\n }\n\n function flushPendingState(): void {\n if (!bridgeReady || !iframe?.contentWindow || Object.keys(pendingState).length === 0) return;\n\n send({ record: { ...pendingState }, type: MSG_STATE_UPDATE_ALL });\n }\n\n function setState(update: Partial<State>): void {\n if (disposed) {\n warn('setState() called on a disposed sandbox.');\n\n return;\n }\n\n Object.assign(pendingState, update);\n\n if (bridgeReady && iframe?.contentWindow && Object.keys(update).length > 0) {\n send({ record: update, type: MSG_STATE_UPDATE_ALL });\n }\n }\n\n function replaceBody(html: string): void {\n if (disposed) {\n warn('replaceBody() called on a disposed sandbox.');\n\n return;\n }\n\n if (!bridgeReady || !iframe?.contentWindow) {\n warn('replaceBody() called before render() has resolved: bridge is not ready. Await render() first.');\n\n return;\n }\n\n send({ html, type: MSG_HTML_REPLACE });\n }\n\n function updateStyle(id: string, css: string): void {\n if (disposed) {\n warn('updateStyle() called on a disposed sandbox.');\n\n return;\n }\n\n devOnly(() => {\n if (!(id in styles)) {\n warn(`updateStyle('${id}', …): '${id}' is not a known styles key.`);\n }\n });\n\n styles[id] = css;\n\n if (bridgeReady && iframe?.contentWindow) send({ css, id, type: MSG_STYLE_PATCH });\n }\n\n return {\n get disposalSignal() {\n return controller.signal;\n },\n dispose,\n get disposed() {\n return disposed;\n },\n onMessage,\n render,\n replaceBody,\n setState,\n [Symbol.dispose]() {\n dispose();\n },\n updateStyle,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"_runtime.cjs","names":["normalizeSandboxOptions","createChannel","isProtocolMessage","MSG_CUSTOM","MSG_ERROR","MSG_READY","MSG_RESIZE","buildDocumentFromOptions","SandboxTimeoutError","envelope","MSG_STATE_UPDATE_ALL","MSG_HTML_REPLACE","MSG_STYLE_PATCH","require__document","require__policy","require__protocol","require_errors","normalized","options","channel","styles","listeners","pendingState","controller","iframe","disposed","bridgeReady","generation","readyTimeout","currentRender","listener","message","event","bootstrap","container","handler","html","current","documentOptions","promise","update","id","css"],"sources":["../src/_runtime.ts"],"sourcesContent":["import { devOnly, warn } from './_dev.js';\nimport { buildDocumentFromOptions } from './_document.js';\nimport { normalizeSandboxOptions } from './_policy.js';\nimport {\n type BridgeBootstrap,\n createChannel,\n envelope,\n isProtocolMessage,\n MSG_CUSTOM,\n MSG_ERROR,\n MSG_HTML_REPLACE,\n MSG_READY,\n MSG_RESIZE,\n MSG_STATE_UPDATE_ALL,\n MSG_STYLE_PATCH,\n} from './_protocol.js';\nimport { SandboxTimeoutError } from './errors.js';\nimport type { SandboxHandle, SandboxMessage, SandboxOptions } from './types.js';\n\nexport function createSandbox<State extends object = Record<string, unknown>>(\n container: HTMLElement,\n options: SandboxOptions = {},\n): SandboxHandle<State> {\n const normalized = normalizeSandboxOptions(options);\n const channel = createChannel();\n const styles: Record<string, string> = { ...normalized.styles };\n const listeners = new Set<(message: SandboxMessage) => void>();\n const pendingState: Partial<State> = {};\n const controller = new AbortController();\n let iframe: HTMLIFrameElement | null = null;\n let disposed = false;\n let bridgeReady = false;\n let generation = 0;\n let readyTimeout: ReturnType<typeof setTimeout> | null = null;\n let currentRender: { reject: (error: unknown) => void; resolve: () => void } | null = null;\n\n function currentBootstrap(): BridgeBootstrap {\n return { channel, generation };\n }\n\n function clearReadyTimeout(): void {\n if (readyTimeout === null) return;\n\n clearTimeout(readyTimeout);\n readyTimeout = null;\n }\n\n function supersedePendingRender(): void {\n clearReadyTimeout();\n currentRender?.resolve();\n currentRender = null;\n bridgeReady = false;\n }\n\n function broadcast(message: SandboxMessage): void {\n for (const listener of listeners) {\n try {\n listener(message);\n } catch {\n warn('onMessage() handler threw: remaining handlers will still run.');\n }\n }\n }\n\n function handleMessage(event: MessageEvent): void {\n if (!iframe || event.source !== iframe.contentWindow) return;\n\n const bootstrap = currentBootstrap();\n\n if (!isProtocolMessage(event.data, bootstrap)) return;\n\n switch (event.data.type) {\n case MSG_CUSTOM:\n if (typeof event.data.event === 'string') {\n broadcast({ detail: event.data.detail, event: event.data.event, type: MSG_CUSTOM });\n }\n\n break;\n case MSG_ERROR:\n if (typeof event.data.message === 'string') {\n broadcast({\n message: event.data.message,\n ...(typeof event.data.stack === 'string' ? { stack: event.data.stack } : {}),\n type: MSG_ERROR,\n });\n }\n\n break;\n case MSG_READY:\n supersedePendingRender();\n bridgeReady = true;\n flushPendingState();\n break;\n case MSG_RESIZE:\n if (typeof event.data.height === 'number' && Number.isFinite(event.data.height) && event.data.height >= 0) {\n broadcast({ height: event.data.height, type: MSG_RESIZE });\n }\n\n break;\n }\n }\n\n function ensureIframe(): HTMLIFrameElement {\n if (iframe) return iframe;\n\n iframe = document.createElement('iframe');\n iframe.setAttribute('sandbox', 'allow-scripts');\n iframe.setAttribute('referrerpolicy', 'no-referrer');\n iframe.dataset.sandboxChannel = channel;\n container.appendChild(iframe);\n window.addEventListener('message', handleMessage);\n\n return iframe;\n }\n\n function dispose(): void {\n if (disposed) return;\n\n disposed = true;\n supersedePendingRender();\n controller.abort();\n\n if (iframe) {\n window.removeEventListener('message', handleMessage);\n iframe.remove();\n iframe = null;\n }\n\n listeners.clear();\n }\n\n function onMessage(handler: (message: SandboxMessage) => void): () => void {\n if (disposed) {\n warn('onMessage() called on a disposed sandbox: handler will never fire.');\n\n return () => {};\n }\n\n listeners.add(handler);\n\n return () => listeners.delete(handler);\n }\n\n function render(html: string): Promise<void> {\n if (disposed) {\n warn('render() called on a disposed sandbox.');\n\n return Promise.resolve();\n }\n\n if (!html.trim()) warn('render() called with empty HTML.');\n\n supersedePendingRender();\n generation += 1;\n\n const current = ensureIframe();\n const bootstrap = currentBootstrap();\n const documentOptions = { ...normalized, styles: { ...styles } };\n\n current.dataset.sandboxGeneration = String(generation);\n current.srcdoc = buildDocumentFromOptions(html, documentOptions, bootstrap);\n\n const promise = new Promise<void>((resolve, reject) => {\n currentRender = { reject, resolve };\n });\n\n readyTimeout = setTimeout(() => {\n if (!currentRender) return;\n\n currentRender.reject(\n new SandboxTimeoutError(\n `render() did not receive a 'ready' signal within ${normalized.readyTimeout}ms. ` +\n 'An injected script may have blocked document initialization.',\n ),\n );\n currentRender = null;\n readyTimeout = null;\n }, normalized.readyTimeout);\n\n return promise;\n }\n\n function send(message: Record<string, unknown>): void {\n iframe?.contentWindow?.postMessage(envelope(currentBootstrap(), message), '*');\n }\n\n function flushPendingState(): void {\n if (!bridgeReady || !iframe?.contentWindow || Object.keys(pendingState).length === 0) return;\n\n send({ record: { ...pendingState }, type: MSG_STATE_UPDATE_ALL });\n }\n\n function setState(update: Partial<State>): void {\n if (disposed) {\n warn('setState() called on a disposed sandbox.');\n\n return;\n }\n\n Object.assign(pendingState, update);\n\n if (bridgeReady && iframe?.contentWindow && Object.keys(update).length > 0) {\n send({ record: update, type: MSG_STATE_UPDATE_ALL });\n }\n }\n\n function replaceBody(html: string): void {\n if (disposed) {\n warn('replaceBody() called on a disposed sandbox.');\n\n return;\n }\n\n if (!bridgeReady || !iframe?.contentWindow) {\n warn('replaceBody() called before render() has resolved: bridge is not ready. Await render() first.');\n\n return;\n }\n\n send({ html, type: MSG_HTML_REPLACE });\n }\n\n function updateStyle(id: string, css: string): void {\n if (disposed) {\n warn('updateStyle() called on a disposed sandbox.');\n\n return;\n }\n\n devOnly(() => {\n if (!(id in styles)) {\n warn(`updateStyle('${id}', …): '${id}' is not a known styles key.`);\n }\n });\n\n styles[id] = css;\n\n if (bridgeReady && iframe?.contentWindow) send({ css, id, type: MSG_STYLE_PATCH });\n }\n\n return {\n get disposalSignal() {\n return controller.signal;\n },\n dispose,\n get disposed() {\n return disposed;\n },\n onMessage,\n render,\n replaceBody,\n setState,\n [Symbol.dispose]() {\n dispose();\n },\n updateStyle,\n };\n}\n"],"mappings":"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"}
package/dist/_runtime.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import "./_dev.js";
2
- import { SandboxTimeoutError as e } from "./errors.js";
2
+ import { buildDocumentFromOptions as e } from "./_document.js";
3
3
  import { normalizeSandboxOptions as t } from "./_policy.js";
4
- import { buildDocumentFromOptions as n } from "./_document.js";
5
- import { MSG_CUSTOM as r, MSG_ERROR as i, MSG_HTML_REPLACE as a, MSG_READY as o, MSG_RESIZE as s, MSG_STATE_UPDATE_ALL as c, MSG_STYLE_PATCH as l, createChannel as u, envelope as d, isProtocolMessage as f } from "./_protocol.js";
6
- //#region src/_runtime.ts
4
+ import { MSG_CUSTOM as n, MSG_ERROR as r, MSG_HTML_REPLACE as i, MSG_READY as a, MSG_RESIZE as o, MSG_STATE_UPDATE_ALL as s, MSG_STYLE_PATCH as c, createChannel as l, envelope as u, isProtocolMessage as d } from "./_protocol.js";
5
+ import { SandboxTimeoutError as f } from "./errors.js";
6
+ // src/_runtime.ts
7
7
  function createSandbox(p, m = {}) {
8
- let h = t(m), g = u(), _ = { ...h.styles }, v = /* @__PURE__ */ new Set(), y = {}, b = new AbortController(), x = null, S = !1, C = !1, w = 0, T = null, E = null;
8
+ let h = t(m), g = l(), _ = { ...h.styles }, v = /* @__PURE__ */ new Set(), y = {}, b = new AbortController(), x = null, S = !1, C = !1, w = 0, T = null, E = null;
9
9
  function currentBootstrap() {
10
10
  return {
11
11
  channel: g,
@@ -26,27 +26,27 @@ function createSandbox(p, m = {}) {
26
26
  function handleMessage(e) {
27
27
  if (!x || e.source !== x.contentWindow) return;
28
28
  let t = currentBootstrap();
29
- if (f(e.data, t)) switch (e.data.type) {
30
- case r:
29
+ if (d(e.data, t)) switch (e.data.type) {
30
+ case n:
31
31
  typeof e.data.event == "string" && broadcast({
32
32
  detail: e.data.detail,
33
33
  event: e.data.event,
34
- type: r
34
+ type: n
35
35
  });
36
36
  break;
37
- case i:
37
+ case r:
38
38
  typeof e.data.message == "string" && broadcast({
39
39
  message: e.data.message,
40
40
  ...typeof e.data.stack == "string" ? { stack: e.data.stack } : {},
41
- type: i
41
+ type: r
42
42
  });
43
43
  break;
44
- case o:
44
+ case a:
45
45
  supersedePendingRender(), C = !0, flushPendingState();
46
46
  break;
47
- case s: typeof e.data.height == "number" && Number.isFinite(e.data.height) && e.data.height >= 0 && broadcast({
47
+ case o: typeof e.data.height == "number" && Number.isFinite(e.data.height) && e.data.height >= 0 && broadcast({
48
48
  height: e.data.height,
49
- type: s
49
+ type: o
50
50
  });
51
51
  }
52
52
  }
@@ -62,47 +62,47 @@ function createSandbox(p, m = {}) {
62
62
  function render(t) {
63
63
  if (S) return Promise.resolve();
64
64
  t.trim(), supersedePendingRender(), w += 1;
65
- let r = ensureIframe(), i = currentBootstrap(), a = {
65
+ let n = ensureIframe(), r = currentBootstrap(), i = {
66
66
  ...h,
67
67
  styles: { ..._ }
68
68
  };
69
- r.dataset.sandboxGeneration = String(w), r.srcdoc = n(t, a, i);
70
- let o = new Promise((e, t) => {
69
+ n.dataset.sandboxGeneration = String(w), n.srcdoc = e(t, i, r);
70
+ let a = new Promise((e, t) => {
71
71
  E = {
72
72
  reject: t,
73
73
  resolve: e
74
74
  };
75
75
  });
76
76
  return T = setTimeout(() => {
77
- E && (E.reject(new e(`render() did not receive a 'ready' signal within ${h.readyTimeout}ms. An injected script may have blocked document initialization.`)), E = null, T = null);
78
- }, h.readyTimeout), o;
77
+ E && (E.reject(new f(`render() did not receive a 'ready' signal within ${h.readyTimeout}ms. An injected script may have blocked document initialization.`)), E = null, T = null);
78
+ }, h.readyTimeout), a;
79
79
  }
80
80
  function send(e) {
81
- x?.contentWindow?.postMessage(d(currentBootstrap(), e), "*");
81
+ x?.contentWindow?.postMessage(u(currentBootstrap(), e), "*");
82
82
  }
83
83
  function flushPendingState() {
84
84
  C && x?.contentWindow && Object.keys(y).length !== 0 && send({
85
85
  record: { ...y },
86
- type: c
86
+ type: s
87
87
  });
88
88
  }
89
89
  function setState(e) {
90
90
  S || (Object.assign(y, e), C && x?.contentWindow && Object.keys(e).length > 0 && send({
91
91
  record: e,
92
- type: c
92
+ type: s
93
93
  }));
94
94
  }
95
95
  function replaceBody(e) {
96
96
  S || C && x?.contentWindow && send({
97
97
  html: e,
98
- type: a
98
+ type: i
99
99
  });
100
100
  }
101
101
  function updateStyle(e, t) {
102
102
  S || (_[e] = t, C && x?.contentWindow && send({
103
103
  css: t,
104
104
  id: e,
105
- type: l
105
+ type: c
106
106
  }));
107
107
  }
108
108
  return {
@@ -123,7 +123,6 @@ function createSandbox(p, m = {}) {
123
123
  updateStyle
124
124
  };
125
125
  }
126
- //#endregion
127
126
  export { createSandbox };
128
127
 
129
128
  //# sourceMappingURL=_runtime.js.map
package/dist/errors.js CHANGED
@@ -1,10 +1,9 @@
1
- //#region src/errors.ts
1
+ // src/errors.ts
2
2
  var SandboxError = class extends Error {
3
3
  constructor(e, t) {
4
4
  super(e, t), this.name = new.target.name, Object.setPrototypeOf(this, new.target.prototype);
5
5
  }
6
6
  }, SandboxTimeoutError = class extends SandboxError {}, SandboxConfigurationError = class extends SandboxError {};
7
- //#endregion
8
7
  export { SandboxConfigurationError, SandboxError, SandboxTimeoutError };
9
8
 
10
9
  //# sourceMappingURL=errors.js.map
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./errors.cjs"),t=require("./_runtime.cjs");exports.SandboxConfigurationError=e.SandboxConfigurationError,exports.SandboxError=e.SandboxError,exports.SandboxTimeoutError=e.SandboxTimeoutError,exports.createSandbox=t.createSandbox;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./_runtime.cjs"),t=require("./errors.cjs");exports.SandboxConfigurationError=t.SandboxConfigurationError,exports.SandboxError=t.SandboxError,exports.SandboxTimeoutError=t.SandboxTimeoutError,exports.createSandbox=e.createSandbox;
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import { SandboxConfigurationError as e, SandboxError as t, SandboxTimeoutError as n } from "./errors.js";
2
- import { createSandbox as r } from "./_runtime.js";
3
- export { e as SandboxConfigurationError, t as SandboxError, n as SandboxTimeoutError, r as createSandbox };
1
+ import { createSandbox as e } from "./_runtime.js";
2
+ import { SandboxConfigurationError as t, SandboxError as n, SandboxTimeoutError as r } from "./errors.js";
3
+ export { t as SandboxConfigurationError, n as SandboxError, r as SandboxTimeoutError, e as createSandbox };
package/dist/sandbox.cjs CHANGED
@@ -63,7 +63,7 @@ window.addEventListener('DOMContentLoaded', function() {
63
63
  }).observe(document.body);
64
64
  });
65
65
  })();
66
- `.trim()}var SandboxError=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},SandboxTimeoutError=class extends SandboxError{},SandboxConfigurationError=class extends SandboxError{},e=/^[A-Za-z][A-Za-z0-9_-]*$/,t=/^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/,n=/^[A-Za-z0-9+/_-]+={0,2}$/;function fail(e,t){throw new SandboxConfigurationError(`${e} is invalid: ${String(t)}`)}function normalizeOrigin(e,t){if(typeof e!=`string`)return fail(t,e);let n;try{n=new URL(e)}catch{return fail(t,e)}return n.protocol!==`http:`&&n.protocol!==`https:`||n.username||n.password||n.pathname!==`/`||n.search||n.hash?fail(t,e):n.origin}function normalizeOrigins(e,t){return e===void 0?[]:Array.isArray(e)?e.map(e=>normalizeOrigin(e,t)):fail(t,e)}function normalizeScript(e){if(typeof e!=`string`)return fail(`scripts`,e);let t;try{t=new URL(e)}catch{return fail(`scripts`,e)}return t.protocol!==`http:`&&t.protocol!==`https:`?fail(`scripts`,e):t.href}function normalizeStyles(t){if(t===void 0)return{};if(typeof t!=`object`||!t||Array.isArray(t))return fail(`styles`,t);let n={};for(let[r,i]of Object.entries(t))(!e.test(r)||typeof i!=`string`)&&fail(`styles`,r),n[r]=i;return n}function normalizeSandboxOptions(e={}){(typeof e!=`object`||!e||Array.isArray(e))&&fail(`options`,e);let r=e.lang??`en`;t.test(r)||fail(`lang`,r),e.nonce!==void 0&&(!n.test(e.nonce)||e.nonce.length===0)&&fail(`nonce`,e.nonce),e.title!==void 0&&typeof e.title!=`string`&&fail(`title`,e.title);let i=e.readyTimeout,a=i??5e3;i!==void 0&&(i===null||typeof i!=`number`||!Number.isFinite(i)||i<=0||i>2**31-1)&&fail(`readyTimeout`,i),e.scripts!==void 0&&!Array.isArray(e.scripts)&&fail(`scripts`,e.scripts);let o=(e.scripts??[]).map(normalizeScript);return{allowedFontOrigins:normalizeOrigins(e.allowedFontOrigins,`allowedFontOrigins`),allowedImageOrigins:normalizeOrigins(e.allowedImageOrigins,`allowedImageOrigins`),allowedScriptOrigins:normalizeOrigins(e.allowedScriptOrigins,`allowedScriptOrigins`),allowedStyleOrigins:normalizeOrigins(e.allowedStyleOrigins,`allowedStyleOrigins`),lang:r,nonce:e.nonce,readyTimeout:a,scripts:o,styles:normalizeStyles(e.styles),title:e.title??``}}function buildCspFromOptions(e){let t=[`'unsafe-inline'`,...e.nonce?[`'nonce-${e.nonce}'`]:[],...e.allowedScriptOrigins,...e.scripts.map(e=>new URL(e).origin)].join(` `),n=[`'unsafe-inline'`,...e.allowedStyleOrigins].join(` `),r=[`data:`,...e.allowedImageOrigins].join(` `),i=e.allowedFontOrigins.join(` `)||`'none'`;return[`default-src 'none'`,`script-src ${t}`,`style-src ${n}`,`img-src ${r}`,`font-src ${i}`,`connect-src 'none'`,`form-action 'none'`,`base-uri 'none'`].join(`; `)}function escapeCss(e){return e.replace(/<\/style/gi,`\\3C /style`)}function escapeAttr(e){return e.replace(/&/g,`&amp;`).replace(/"/g,`&quot;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`)}function escapeText(e){return e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`)}function buildDocumentFromOptions(e,t,n){let r=escapeAttr(buildCspFromOptions(t)),i=Object.entries(t.styles).map(([e,t])=>`<style id="${escapeAttr(e)}">${escapeCss(t)}</style>`).join(`
66
+ `.trim()}var SandboxError=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},SandboxTimeoutError=class extends SandboxError{},SandboxConfigurationError=class extends SandboxError{},e=/^[A-Za-z][A-Za-z0-9_-]*$/,t=/^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/,n=/^[A-Za-z0-9+/_-]+={0,2}$/;function fail(e,t){throw new SandboxConfigurationError(`${e} is invalid: ${String(t)}`)}function normalizeOrigin(e,t){if(typeof e!=`string`)return fail(t,e);let n;try{n=new URL(e)}catch{return fail(t,e)}return n.protocol!==`http:`&&n.protocol!==`https:`||n.username||n.password||n.pathname!==`/`||n.search||n.hash?fail(t,e):n.origin}function normalizeOrigins(e,t){return e===void 0?[]:Array.isArray(e)?e.map(e=>normalizeOrigin(e,t)):fail(t,e)}function normalizeScript(e){if(typeof e!=`string`)return fail(`scripts`,e);let t;try{t=new URL(e)}catch{return fail(`scripts`,e)}return t.protocol!==`http:`&&t.protocol!==`https:`?fail(`scripts`,e):t.href}function normalizeStyles(t){if(t===void 0)return{};if(typeof t!=`object`||!t||Array.isArray(t))return fail(`styles`,t);let n={};for(let[r,i]of Object.entries(t))e.test(r)&&typeof i==`string`||fail(`styles`,r),n[r]=i;return n}function normalizeSandboxOptions(e={}){(typeof e!=`object`||!e||Array.isArray(e))&&fail(`options`,e);let r=e.lang??`en`;t.test(r)||fail(`lang`,r),e.nonce===void 0||n.test(e.nonce)&&e.nonce.length!==0||fail(`nonce`,e.nonce),e.title!==void 0&&typeof e.title!=`string`&&fail(`title`,e.title);let i=e.readyTimeout,a=i??5e3;i!==void 0&&(i===null||typeof i!=`number`||!Number.isFinite(i)||i<=0||i>2**31-1)&&fail(`readyTimeout`,i),e.scripts!==void 0&&!Array.isArray(e.scripts)&&fail(`scripts`,e.scripts);let o=(e.scripts??[]).map(normalizeScript);return{allowedFontOrigins:normalizeOrigins(e.allowedFontOrigins,`allowedFontOrigins`),allowedImageOrigins:normalizeOrigins(e.allowedImageOrigins,`allowedImageOrigins`),allowedScriptOrigins:normalizeOrigins(e.allowedScriptOrigins,`allowedScriptOrigins`),allowedStyleOrigins:normalizeOrigins(e.allowedStyleOrigins,`allowedStyleOrigins`),lang:r,nonce:e.nonce,readyTimeout:a,scripts:o,styles:normalizeStyles(e.styles),title:e.title??``}}function buildCspFromOptions(e){let t=[`'unsafe-inline'`,...e.nonce?[`'nonce-${e.nonce}'`]:[],...e.allowedScriptOrigins,...e.scripts.map(e=>new URL(e).origin)].join(` `),n=[`'unsafe-inline'`,...e.allowedStyleOrigins].join(` `),r=[`data:`,...e.allowedImageOrigins].join(` `),i=e.allowedFontOrigins.join(` `)||`'none'`;return[`default-src 'none'`,`script-src ${t}`,`style-src ${n}`,`img-src ${r}`,`font-src ${i}`,`connect-src 'none'`,`form-action 'none'`,`base-uri 'none'`].join(`; `)}function escapeCss(e){return e.replace(/<\/style/gi,`\\3C /style`)}function escapeAttr(e){return e.replace(/&/g,`&amp;`).replace(/"/g,`&quot;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`)}function escapeText(e){return e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`)}function buildDocumentFromOptions(e,t,n){let r=escapeAttr(buildCspFromOptions(t)),i=Object.entries(t.styles).map(([e,t])=>`<style id="${escapeAttr(e)}">${escapeCss(t)}</style>`).join(`
67
67
  `),a=t.scripts.map(e=>`<script crossorigin="anonymous" src="${escapeAttr(e)}"><\/script>`).join(`
68
68
  `),o=t.nonce?` nonce="${escapeAttr(t.nonce)}"`:``;return`<!doctype html>
69
69
  <html lang="${escapeAttr(t.lang)}">
@@ -1 +1 @@
1
- {"version":3,"file":"sandbox.cjs","names":["bootstrap","message","opts","ID","LANG","NONCE","field","value","url","values","styles","normalized","id","css","options","lang","rawReadyTimeout","readyTimeout","scripts","scriptOrigins","script","styleOrigins","imageOrigins","fontOrigins","csp","src","nonce","html","MSG_CUSTOM","MSG_ERROR","MSG_HTML_REPLACE","MSG_READY","MSG_RESIZE","MSG_STATE_UPDATE_ALL","MSG_STYLE_PATCH","bytes","byte","channel","listeners","pendingState","controller","iframe","disposed","bridgeReady","generation","currentRender","listener","event","container","handler","current","documentOptions","promise","update"],"sources":["../src/_dev.ts","../src/_bridge.ts","../src/errors.ts","../src/_policy.ts","../src/_document.ts","../src/_protocol.ts","../src/_runtime.ts"],"sourcesContent":["const isDev = !(globalThis as { __SANDBOX_PROD__?: boolean }).__SANDBOX_PROD__;\n\n/** @internal @security: some callers interpolate user-supplied values (e.g. styles key) into msg; do not log PII. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/sandbox] ${msg}`);\n}\n\n/** @internal: Run fn only in dev builds. Use when dev-only logic goes beyond a single warn() / error() call. */\nexport function devOnly(fn: () => void): void {\n if (isDev) fn();\n}\n","import type { BridgeBootstrap } from './_protocol.js';\n\nexport function buildBridgeScript(bootstrap: BridgeBootstrap): string {\n const channel = JSON.stringify(bootstrap.channel);\n const generation = String(bootstrap.generation);\n\n return `\n(function() {\nwindow.__sandboxChannel = ${channel};\nwindow.__sandboxGeneration = ${generation};\nconst __sandboxChannel = window.__sandboxChannel;\nconst __sandboxGeneration = window.__sandboxGeneration;\nif ('ontouchstart' in window) document.addEventListener('touchstart', function() {}, { passive: true });\nwindow.addEventListener('message', function(e) {\n var msg = e.data;\n if (e.source !== parent || !msg || msg.channel !== __sandboxChannel || msg.generation !== __sandboxGeneration) return;\n if (msg.type === 'state-update-all' && msg.record && typeof msg.record === 'object') {\n var keys = Object.keys(msg.record);\n for (var i = 0; i < keys.length; i++) {\n var k = keys[i];\n document.dispatchEvent(new CustomEvent('sandbox:state-update', { detail: { key: k, value: msg.record[k] } }));\n }\n }\n if (msg.type === 'style-patch' && msg.id && typeof msg.css === 'string') {\n var el = document.getElementById(msg.id);\n if (el && el.tagName === 'STYLE') el.textContent = msg.css;\n }\n if (msg.type === 'html-replace' && typeof msg.html === 'string') {\n document.body.innerHTML = msg.html;\n }\n});\nfunction post(msg) {\n msg.channel = __sandboxChannel;\n msg.generation = __sandboxGeneration;\n parent.postMessage(msg, '*');\n}\nwindow.onerror = function(message, _src, _line, _col, err) {\n post({ type: 'error', message: String(message), stack: err && err.stack });\n return true;\n};\nwindow.addEventListener('unhandledrejection', function(e) {\n var reason = e.reason;\n post({ type: 'error', message: String(reason), stack: reason && reason.stack });\n});\nwindow.__sandbox__ = {\n emit: function(event, detail) {\n post({ type: 'custom', event: String(event), detail: detail });\n },\n onState: function(key, handler) {\n function listener(e) {\n if (e.detail && e.detail.key === key) handler(e.detail.value);\n }\n document.addEventListener('sandbox:state-update', listener);\n return function() {\n document.removeEventListener('sandbox:state-update', listener);\n };\n }\n};\nwindow.addEventListener('DOMContentLoaded', function() {\n post({ type: 'ready' });\n if (typeof ResizeObserver === 'undefined') return;\n new ResizeObserver(function(entries) {\n var entry = entries[0];\n if (!entry) return;\n var h = entry.borderBoxSize && entry.borderBoxSize[0]\n ? entry.borderBoxSize[0].blockSize\n : entry.contentRect.height;\n post({ type: 'resize', height: h });\n }).observe(document.body);\n});\n})();\n`.trim();\n}\n","/** Base class for all sandbox errors. */\nexport class SandboxError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Thrown when render() does not receive a 'ready' signal from the sandbox document\n * within the timeout window. An injected script may have blocked document initialization.\n */\nexport class SandboxTimeoutError extends SandboxError {}\n\n/** Thrown when Sandbox configuration cannot produce a valid document or CSP policy. */\nexport class SandboxConfigurationError extends SandboxError {}\n","import { SandboxConfigurationError } from './errors.js';\nimport type { SandboxOptions } from './types.js';\n\nexport interface NormalizedSandboxOptions {\n allowedFontOrigins: string[];\n allowedImageOrigins: string[];\n allowedScriptOrigins: string[];\n allowedStyleOrigins: string[];\n lang: string;\n nonce: string | undefined;\n readyTimeout: number;\n scripts: string[];\n styles: Record<string, string>;\n title: string;\n}\n\nexport const DEFAULT_READY_TIMEOUT_MS = 5000;\n\nconst ID = /^[A-Za-z][A-Za-z0-9_-]*$/;\nconst LANG = /^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/;\nconst NONCE = /^[A-Za-z0-9+/_-]+={0,2}$/;\n\nfunction fail(field: string, value: unknown): never {\n throw new SandboxConfigurationError(`${field} is invalid: ${String(value)}`);\n}\n\nfunction normalizeOrigin(value: unknown, field: string): string {\n if (typeof value !== 'string') return fail(field, value);\n\n let url: URL;\n\n try {\n url = new URL(value);\n } catch {\n return fail(field, value);\n }\n\n if (\n (url.protocol !== 'http:' && url.protocol !== 'https:') ||\n url.username ||\n url.password ||\n url.pathname !== '/' ||\n url.search ||\n url.hash\n ) {\n return fail(field, value);\n }\n\n return url.origin;\n}\n\nfunction normalizeOrigins(values: string[] | undefined, field: string): string[] {\n if (values === undefined) return [];\n if (!Array.isArray(values)) return fail(field, values);\n\n return values.map((value) => normalizeOrigin(value, field));\n}\n\nfunction normalizeScript(value: unknown): string {\n if (typeof value !== 'string') return fail('scripts', value);\n\n let url: URL;\n\n try {\n url = new URL(value);\n } catch {\n return fail('scripts', value);\n }\n\n if (url.protocol !== 'http:' && url.protocol !== 'https:') return fail('scripts', value);\n\n return url.href;\n}\n\nfunction normalizeStyles(styles: Record<string, string> | undefined): Record<string, string> {\n if (styles === undefined) return {};\n if (typeof styles !== 'object' || styles === null || Array.isArray(styles)) return fail('styles', styles);\n\n const normalized: Record<string, string> = {};\n\n for (const [id, css] of Object.entries(styles)) {\n if (!ID.test(id) || typeof css !== 'string') fail('styles', id);\n\n normalized[id] = css;\n }\n\n return normalized;\n}\n\nexport function normalizeSandboxOptions(options: SandboxOptions = {}): NormalizedSandboxOptions {\n if (typeof options !== 'object' || options === null || Array.isArray(options)) fail('options', options);\n\n const lang = options.lang ?? 'en';\n\n if (!LANG.test(lang)) fail('lang', lang);\n\n if (options.nonce !== undefined && (!NONCE.test(options.nonce) || options.nonce.length === 0)) {\n fail('nonce', options.nonce);\n }\n\n if (options.title !== undefined && typeof options.title !== 'string') fail('title', options.title);\n\n const rawReadyTimeout = options.readyTimeout;\n const readyTimeout = rawReadyTimeout ?? DEFAULT_READY_TIMEOUT_MS;\n\n if (\n rawReadyTimeout !== undefined &&\n (rawReadyTimeout === null ||\n typeof rawReadyTimeout !== 'number' ||\n !Number.isFinite(rawReadyTimeout) ||\n rawReadyTimeout <= 0 ||\n rawReadyTimeout > 2 ** 31 - 1)\n ) {\n fail('readyTimeout', rawReadyTimeout);\n }\n\n if (options.scripts !== undefined && !Array.isArray(options.scripts)) fail('scripts', options.scripts);\n\n const scripts = (options.scripts ?? []).map(normalizeScript);\n\n return {\n allowedFontOrigins: normalizeOrigins(options.allowedFontOrigins, 'allowedFontOrigins'),\n allowedImageOrigins: normalizeOrigins(options.allowedImageOrigins, 'allowedImageOrigins'),\n allowedScriptOrigins: normalizeOrigins(options.allowedScriptOrigins, 'allowedScriptOrigins'),\n allowedStyleOrigins: normalizeOrigins(options.allowedStyleOrigins, 'allowedStyleOrigins'),\n lang,\n nonce: options.nonce,\n readyTimeout,\n scripts,\n styles: normalizeStyles(options.styles),\n title: options.title ?? '',\n };\n}\n\nexport function buildCspFromOptions(options: NormalizedSandboxOptions): string {\n const scriptOrigins = [\n \"'unsafe-inline'\",\n ...(options.nonce ? [`'nonce-${options.nonce}'`] : []),\n ...options.allowedScriptOrigins,\n ...options.scripts.map((script) => new URL(script).origin),\n ].join(' ');\n const styleOrigins = [\"'unsafe-inline'\", ...options.allowedStyleOrigins].join(' ');\n const imageOrigins = ['data:', ...options.allowedImageOrigins].join(' ');\n const fontOrigins = options.allowedFontOrigins.join(' ') || \"'none'\";\n\n return [\n \"default-src 'none'\",\n `script-src ${scriptOrigins}`,\n `style-src ${styleOrigins}`,\n `img-src ${imageOrigins}`,\n `font-src ${fontOrigins}`,\n \"connect-src 'none'\",\n \"form-action 'none'\",\n \"base-uri 'none'\",\n ].join('; ');\n}\n\nexport function buildCsp(options: SandboxOptions = {}): string {\n return buildCspFromOptions(normalizeSandboxOptions(options));\n}\n","import { buildBridgeScript } from './_bridge.js';\nimport { buildCspFromOptions, type NormalizedSandboxOptions, normalizeSandboxOptions } from './_policy.js';\nimport type { BridgeBootstrap } from './_protocol.js';\nimport type { SandboxOptions } from './types.js';\n\nfunction escapeCss(css: string): string {\n return css.replace(/<\\/style/gi, '\\\\3C /style');\n}\n\nfunction escapeAttr(value: string): string {\n return value.replace(/&/g, '&amp;').replace(/\"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n}\n\nfunction escapeText(value: string): string {\n return value.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n}\n\nexport function buildDocumentFromOptions(\n html: string,\n options: NormalizedSandboxOptions,\n bootstrap: BridgeBootstrap,\n): string {\n const csp = escapeAttr(buildCspFromOptions(options));\n const styles = Object.entries(options.styles)\n .map(([id, css]) => `<style id=\"${escapeAttr(id)}\">${escapeCss(css)}</style>`)\n .join('\\n');\n const scripts = options.scripts\n .map((src) => `<script crossorigin=\"anonymous\" src=\"${escapeAttr(src)}\"></script>`)\n .join('\\n');\n const nonce = options.nonce ? ` nonce=\"${escapeAttr(options.nonce)}\"` : '';\n\n return `<!doctype html>\n<html lang=\"${escapeAttr(options.lang)}\">\n<head>\n<meta http-equiv=\"Content-Security-Policy\" content=\"${csp}\">\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>${escapeText(options.title)}</title>\n${styles}\n<script${nonce}>\n${buildBridgeScript(bootstrap)}\n</script>\n</head>\n<body>\n${scripts}\n${html}\n</body>\n</html>`;\n}\n\nexport function buildDocument(html: string, options: SandboxOptions = {}): string {\n return buildDocumentFromOptions(html, normalizeSandboxOptions(options), { channel: '', generation: 0 });\n}\n","export const MSG_CUSTOM = 'custom';\nexport const MSG_ERROR = 'error';\nexport const MSG_HTML_REPLACE = 'html-replace';\nexport const MSG_READY = 'ready';\nexport const MSG_RESIZE = 'resize';\nexport const MSG_STATE_UPDATE_ALL = 'state-update-all';\nexport const MSG_STYLE_PATCH = 'style-patch';\n\nexport interface BridgeBootstrap {\n channel: string;\n generation: number;\n}\n\nexport type ProtocolMessage = Record<string, unknown> & { channel: string; generation: number; type: string };\n\nexport function isProtocolMessage(value: unknown, bootstrap: BridgeBootstrap): value is ProtocolMessage {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as { channel?: unknown }).channel === 'string' &&\n (value as { channel: string }).channel === bootstrap.channel &&\n typeof (value as { generation?: unknown }).generation === 'number' &&\n (value as { generation: number }).generation === bootstrap.generation &&\n typeof (value as { type?: unknown }).type === 'string'\n );\n}\n\nexport function createChannel(): string {\n if (typeof crypto.randomUUID === 'function') return crypto.randomUUID();\n\n const bytes = crypto.getRandomValues(new Uint32Array(4));\n\n return Array.from(bytes, (byte) => byte.toString(16).padStart(8, '0')).join('');\n}\n\nexport function envelope(bootstrap: BridgeBootstrap, message: Record<string, unknown>): ProtocolMessage {\n return { ...message, channel: bootstrap.channel, generation: bootstrap.generation } as ProtocolMessage;\n}\n","import { devOnly, warn } from './_dev.js';\nimport { buildDocumentFromOptions } from './_document.js';\nimport { normalizeSandboxOptions } from './_policy.js';\nimport {\n type BridgeBootstrap,\n createChannel,\n envelope,\n isProtocolMessage,\n MSG_CUSTOM,\n MSG_ERROR,\n MSG_HTML_REPLACE,\n MSG_READY,\n MSG_RESIZE,\n MSG_STATE_UPDATE_ALL,\n MSG_STYLE_PATCH,\n} from './_protocol.js';\nimport { SandboxTimeoutError } from './errors.js';\nimport type { SandboxHandle, SandboxMessage, SandboxOptions } from './types.js';\n\nexport function createSandbox<State extends object = Record<string, unknown>>(\n container: HTMLElement,\n options: SandboxOptions = {},\n): SandboxHandle<State> {\n const normalized = normalizeSandboxOptions(options);\n const channel = createChannel();\n const styles: Record<string, string> = { ...normalized.styles };\n const listeners = new Set<(message: SandboxMessage) => void>();\n const pendingState: Partial<State> = {};\n const controller = new AbortController();\n let iframe: HTMLIFrameElement | null = null;\n let disposed = false;\n let bridgeReady = false;\n let generation = 0;\n let readyTimeout: ReturnType<typeof setTimeout> | null = null;\n let currentRender: { reject: (error: unknown) => void; resolve: () => void } | null = null;\n\n function currentBootstrap(): BridgeBootstrap {\n return { channel, generation };\n }\n\n function clearReadyTimeout(): void {\n if (readyTimeout === null) return;\n\n clearTimeout(readyTimeout);\n readyTimeout = null;\n }\n\n function supersedePendingRender(): void {\n clearReadyTimeout();\n currentRender?.resolve();\n currentRender = null;\n bridgeReady = false;\n }\n\n function broadcast(message: SandboxMessage): void {\n for (const listener of listeners) {\n try {\n listener(message);\n } catch {\n warn('onMessage() handler threw: remaining handlers will still run.');\n }\n }\n }\n\n function handleMessage(event: MessageEvent): void {\n if (!iframe || event.source !== iframe.contentWindow) return;\n\n const bootstrap = currentBootstrap();\n\n if (!isProtocolMessage(event.data, bootstrap)) return;\n\n switch (event.data.type) {\n case MSG_CUSTOM:\n if (typeof event.data.event === 'string') {\n broadcast({ detail: event.data.detail, event: event.data.event, type: MSG_CUSTOM });\n }\n\n break;\n case MSG_ERROR:\n if (typeof event.data.message === 'string') {\n broadcast({\n message: event.data.message,\n ...(typeof event.data.stack === 'string' ? { stack: event.data.stack } : {}),\n type: MSG_ERROR,\n });\n }\n\n break;\n case MSG_READY:\n supersedePendingRender();\n bridgeReady = true;\n flushPendingState();\n break;\n case MSG_RESIZE:\n if (typeof event.data.height === 'number' && Number.isFinite(event.data.height) && event.data.height >= 0) {\n broadcast({ height: event.data.height, type: MSG_RESIZE });\n }\n\n break;\n }\n }\n\n function ensureIframe(): HTMLIFrameElement {\n if (iframe) return iframe;\n\n iframe = document.createElement('iframe');\n iframe.setAttribute('sandbox', 'allow-scripts');\n iframe.setAttribute('referrerpolicy', 'no-referrer');\n iframe.dataset.sandboxChannel = channel;\n container.appendChild(iframe);\n window.addEventListener('message', handleMessage);\n\n return iframe;\n }\n\n function dispose(): void {\n if (disposed) return;\n\n disposed = true;\n supersedePendingRender();\n controller.abort();\n\n if (iframe) {\n window.removeEventListener('message', handleMessage);\n iframe.remove();\n iframe = null;\n }\n\n listeners.clear();\n }\n\n function onMessage(handler: (message: SandboxMessage) => void): () => void {\n if (disposed) {\n warn('onMessage() called on a disposed sandbox: handler will never fire.');\n\n return () => {};\n }\n\n listeners.add(handler);\n\n return () => listeners.delete(handler);\n }\n\n function render(html: string): Promise<void> {\n if (disposed) {\n warn('render() called on a disposed sandbox.');\n\n return Promise.resolve();\n }\n\n if (!html.trim()) warn('render() called with empty HTML.');\n\n supersedePendingRender();\n generation += 1;\n\n const current = ensureIframe();\n const bootstrap = currentBootstrap();\n const documentOptions = { ...normalized, styles: { ...styles } };\n\n current.dataset.sandboxGeneration = String(generation);\n current.srcdoc = buildDocumentFromOptions(html, documentOptions, bootstrap);\n\n const promise = new Promise<void>((resolve, reject) => {\n currentRender = { reject, resolve };\n });\n\n readyTimeout = setTimeout(() => {\n if (!currentRender) return;\n\n currentRender.reject(\n new SandboxTimeoutError(\n `render() did not receive a 'ready' signal within ${normalized.readyTimeout}ms. ` +\n 'An injected script may have blocked document initialization.',\n ),\n );\n currentRender = null;\n readyTimeout = null;\n }, normalized.readyTimeout);\n\n return promise;\n }\n\n function send(message: Record<string, unknown>): void {\n iframe?.contentWindow?.postMessage(envelope(currentBootstrap(), message), '*');\n }\n\n function flushPendingState(): void {\n if (!bridgeReady || !iframe?.contentWindow || Object.keys(pendingState).length === 0) return;\n\n send({ record: { ...pendingState }, type: MSG_STATE_UPDATE_ALL });\n }\n\n function setState(update: Partial<State>): void {\n if (disposed) {\n warn('setState() called on a disposed sandbox.');\n\n return;\n }\n\n Object.assign(pendingState, update);\n\n if (bridgeReady && iframe?.contentWindow && Object.keys(update).length > 0) {\n send({ record: update, type: MSG_STATE_UPDATE_ALL });\n }\n }\n\n function replaceBody(html: string): void {\n if (disposed) {\n warn('replaceBody() called on a disposed sandbox.');\n\n return;\n }\n\n if (!bridgeReady || !iframe?.contentWindow) {\n warn('replaceBody() called before render() has resolved: bridge is not ready. Await render() first.');\n\n return;\n }\n\n send({ html, type: MSG_HTML_REPLACE });\n }\n\n function updateStyle(id: string, css: string): void {\n if (disposed) {\n warn('updateStyle() called on a disposed sandbox.');\n\n return;\n }\n\n devOnly(() => {\n if (!(id in styles)) {\n warn(`updateStyle('${id}', …): '${id}' is not a known styles key.`);\n }\n });\n\n styles[id] = css;\n\n if (bridgeReady && iframe?.contentWindow) send({ css, id, type: MSG_STYLE_PATCH });\n }\n\n return {\n get disposalSignal() {\n return controller.signal;\n },\n dispose,\n get disposed() {\n return disposed;\n },\n onMessage,\n render,\n replaceBody,\n setState,\n [Symbol.dispose]() {\n dispose();\n },\n updateStyle,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"sandbox.cjs","names":["bootstrap","message","opts","ID","LANG","NONCE","field","value","url","values","styles","normalized","id","css","options","lang","rawReadyTimeout","readyTimeout","scripts","scriptOrigins","script","styleOrigins","imageOrigins","fontOrigins","csp","src","nonce","html","MSG_CUSTOM","MSG_ERROR","MSG_HTML_REPLACE","MSG_READY","MSG_RESIZE","MSG_STATE_UPDATE_ALL","MSG_STYLE_PATCH","bytes","byte","channel","listeners","pendingState","controller","iframe","disposed","bridgeReady","generation","currentRender","listener","event","container","handler","current","documentOptions","promise","update"],"sources":["../src/_dev.ts","../src/_bridge.ts","../src/errors.ts","../src/_policy.ts","../src/_document.ts","../src/_protocol.ts","../src/_runtime.ts"],"sourcesContent":["const isDev = !(globalThis as { __SANDBOX_PROD__?: boolean }).__SANDBOX_PROD__;\n\n/** @internal @security: some callers interpolate user-supplied values (e.g. styles key) into msg; do not log PII. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/sandbox] ${msg}`);\n}\n\n/** @internal: Run fn only in dev builds. Use when dev-only logic goes beyond a single warn() / error() call. */\nexport function devOnly(fn: () => void): void {\n if (isDev) fn();\n}\n","import type { BridgeBootstrap } from './_protocol.js';\n\nexport function buildBridgeScript(bootstrap: BridgeBootstrap): string {\n const channel = JSON.stringify(bootstrap.channel);\n const generation = String(bootstrap.generation);\n\n return `\n(function() {\nwindow.__sandboxChannel = ${channel};\nwindow.__sandboxGeneration = ${generation};\nconst __sandboxChannel = window.__sandboxChannel;\nconst __sandboxGeneration = window.__sandboxGeneration;\nif ('ontouchstart' in window) document.addEventListener('touchstart', function() {}, { passive: true });\nwindow.addEventListener('message', function(e) {\n var msg = e.data;\n if (e.source !== parent || !msg || msg.channel !== __sandboxChannel || msg.generation !== __sandboxGeneration) return;\n if (msg.type === 'state-update-all' && msg.record && typeof msg.record === 'object') {\n var keys = Object.keys(msg.record);\n for (var i = 0; i < keys.length; i++) {\n var k = keys[i];\n document.dispatchEvent(new CustomEvent('sandbox:state-update', { detail: { key: k, value: msg.record[k] } }));\n }\n }\n if (msg.type === 'style-patch' && msg.id && typeof msg.css === 'string') {\n var el = document.getElementById(msg.id);\n if (el && el.tagName === 'STYLE') el.textContent = msg.css;\n }\n if (msg.type === 'html-replace' && typeof msg.html === 'string') {\n document.body.innerHTML = msg.html;\n }\n});\nfunction post(msg) {\n msg.channel = __sandboxChannel;\n msg.generation = __sandboxGeneration;\n parent.postMessage(msg, '*');\n}\nwindow.onerror = function(message, _src, _line, _col, err) {\n post({ type: 'error', message: String(message), stack: err && err.stack });\n return true;\n};\nwindow.addEventListener('unhandledrejection', function(e) {\n var reason = e.reason;\n post({ type: 'error', message: String(reason), stack: reason && reason.stack });\n});\nwindow.__sandbox__ = {\n emit: function(event, detail) {\n post({ type: 'custom', event: String(event), detail: detail });\n },\n onState: function(key, handler) {\n function listener(e) {\n if (e.detail && e.detail.key === key) handler(e.detail.value);\n }\n document.addEventListener('sandbox:state-update', listener);\n return function() {\n document.removeEventListener('sandbox:state-update', listener);\n };\n }\n};\nwindow.addEventListener('DOMContentLoaded', function() {\n post({ type: 'ready' });\n if (typeof ResizeObserver === 'undefined') return;\n new ResizeObserver(function(entries) {\n var entry = entries[0];\n if (!entry) return;\n var h = entry.borderBoxSize && entry.borderBoxSize[0]\n ? entry.borderBoxSize[0].blockSize\n : entry.contentRect.height;\n post({ type: 'resize', height: h });\n }).observe(document.body);\n});\n})();\n`.trim();\n}\n","/** Base class for all sandbox errors. */\nexport class SandboxError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Thrown when render() does not receive a 'ready' signal from the sandbox document\n * within the timeout window. An injected script may have blocked document initialization.\n */\nexport class SandboxTimeoutError extends SandboxError {}\n\n/** Thrown when Sandbox configuration cannot produce a valid document or CSP policy. */\nexport class SandboxConfigurationError extends SandboxError {}\n","import { SandboxConfigurationError } from './errors.js';\nimport type { SandboxOptions } from './types.js';\n\nexport interface NormalizedSandboxOptions {\n allowedFontOrigins: string[];\n allowedImageOrigins: string[];\n allowedScriptOrigins: string[];\n allowedStyleOrigins: string[];\n lang: string;\n nonce: string | undefined;\n readyTimeout: number;\n scripts: string[];\n styles: Record<string, string>;\n title: string;\n}\n\nexport const DEFAULT_READY_TIMEOUT_MS = 5000;\n\nconst ID = /^[A-Za-z][A-Za-z0-9_-]*$/;\nconst LANG = /^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/;\nconst NONCE = /^[A-Za-z0-9+/_-]+={0,2}$/;\n\nfunction fail(field: string, value: unknown): never {\n throw new SandboxConfigurationError(`${field} is invalid: ${String(value)}`);\n}\n\nfunction normalizeOrigin(value: unknown, field: string): string {\n if (typeof value !== 'string') return fail(field, value);\n\n let url: URL;\n\n try {\n url = new URL(value);\n } catch {\n return fail(field, value);\n }\n\n if (\n (url.protocol !== 'http:' && url.protocol !== 'https:') ||\n url.username ||\n url.password ||\n url.pathname !== '/' ||\n url.search ||\n url.hash\n ) {\n return fail(field, value);\n }\n\n return url.origin;\n}\n\nfunction normalizeOrigins(values: string[] | undefined, field: string): string[] {\n if (values === undefined) return [];\n if (!Array.isArray(values)) return fail(field, values);\n\n return values.map((value) => normalizeOrigin(value, field));\n}\n\nfunction normalizeScript(value: unknown): string {\n if (typeof value !== 'string') return fail('scripts', value);\n\n let url: URL;\n\n try {\n url = new URL(value);\n } catch {\n return fail('scripts', value);\n }\n\n if (url.protocol !== 'http:' && url.protocol !== 'https:') return fail('scripts', value);\n\n return url.href;\n}\n\nfunction normalizeStyles(styles: Record<string, string> | undefined): Record<string, string> {\n if (styles === undefined) return {};\n if (typeof styles !== 'object' || styles === null || Array.isArray(styles)) return fail('styles', styles);\n\n const normalized: Record<string, string> = {};\n\n for (const [id, css] of Object.entries(styles)) {\n if (!ID.test(id) || typeof css !== 'string') fail('styles', id);\n\n normalized[id] = css;\n }\n\n return normalized;\n}\n\nexport function normalizeSandboxOptions(options: SandboxOptions = {}): NormalizedSandboxOptions {\n if (typeof options !== 'object' || options === null || Array.isArray(options)) fail('options', options);\n\n const lang = options.lang ?? 'en';\n\n if (!LANG.test(lang)) fail('lang', lang);\n\n if (options.nonce !== undefined && (!NONCE.test(options.nonce) || options.nonce.length === 0)) {\n fail('nonce', options.nonce);\n }\n\n if (options.title !== undefined && typeof options.title !== 'string') fail('title', options.title);\n\n const rawReadyTimeout = options.readyTimeout;\n const readyTimeout = rawReadyTimeout ?? DEFAULT_READY_TIMEOUT_MS;\n\n if (\n rawReadyTimeout !== undefined &&\n (rawReadyTimeout === null ||\n typeof rawReadyTimeout !== 'number' ||\n !Number.isFinite(rawReadyTimeout) ||\n rawReadyTimeout <= 0 ||\n rawReadyTimeout > 2 ** 31 - 1)\n ) {\n fail('readyTimeout', rawReadyTimeout);\n }\n\n if (options.scripts !== undefined && !Array.isArray(options.scripts)) fail('scripts', options.scripts);\n\n const scripts = (options.scripts ?? []).map(normalizeScript);\n\n return {\n allowedFontOrigins: normalizeOrigins(options.allowedFontOrigins, 'allowedFontOrigins'),\n allowedImageOrigins: normalizeOrigins(options.allowedImageOrigins, 'allowedImageOrigins'),\n allowedScriptOrigins: normalizeOrigins(options.allowedScriptOrigins, 'allowedScriptOrigins'),\n allowedStyleOrigins: normalizeOrigins(options.allowedStyleOrigins, 'allowedStyleOrigins'),\n lang,\n nonce: options.nonce,\n readyTimeout,\n scripts,\n styles: normalizeStyles(options.styles),\n title: options.title ?? '',\n };\n}\n\nexport function buildCspFromOptions(options: NormalizedSandboxOptions): string {\n const scriptOrigins = [\n \"'unsafe-inline'\",\n ...(options.nonce ? [`'nonce-${options.nonce}'`] : []),\n ...options.allowedScriptOrigins,\n ...options.scripts.map((script) => new URL(script).origin),\n ].join(' ');\n const styleOrigins = [\"'unsafe-inline'\", ...options.allowedStyleOrigins].join(' ');\n const imageOrigins = ['data:', ...options.allowedImageOrigins].join(' ');\n const fontOrigins = options.allowedFontOrigins.join(' ') || \"'none'\";\n\n return [\n \"default-src 'none'\",\n `script-src ${scriptOrigins}`,\n `style-src ${styleOrigins}`,\n `img-src ${imageOrigins}`,\n `font-src ${fontOrigins}`,\n \"connect-src 'none'\",\n \"form-action 'none'\",\n \"base-uri 'none'\",\n ].join('; ');\n}\n\nexport function buildCsp(options: SandboxOptions = {}): string {\n return buildCspFromOptions(normalizeSandboxOptions(options));\n}\n","import { buildBridgeScript } from './_bridge.js';\nimport { buildCspFromOptions, type NormalizedSandboxOptions, normalizeSandboxOptions } from './_policy.js';\nimport type { BridgeBootstrap } from './_protocol.js';\nimport type { SandboxOptions } from './types.js';\n\nfunction escapeCss(css: string): string {\n return css.replace(/<\\/style/gi, '\\\\3C /style');\n}\n\nfunction escapeAttr(value: string): string {\n return value.replace(/&/g, '&amp;').replace(/\"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n}\n\nfunction escapeText(value: string): string {\n return value.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n}\n\nexport function buildDocumentFromOptions(\n html: string,\n options: NormalizedSandboxOptions,\n bootstrap: BridgeBootstrap,\n): string {\n const csp = escapeAttr(buildCspFromOptions(options));\n const styles = Object.entries(options.styles)\n .map(([id, css]) => `<style id=\"${escapeAttr(id)}\">${escapeCss(css)}</style>`)\n .join('\\n');\n const scripts = options.scripts\n .map((src) => `<script crossorigin=\"anonymous\" src=\"${escapeAttr(src)}\"></script>`)\n .join('\\n');\n const nonce = options.nonce ? ` nonce=\"${escapeAttr(options.nonce)}\"` : '';\n\n return `<!doctype html>\n<html lang=\"${escapeAttr(options.lang)}\">\n<head>\n<meta http-equiv=\"Content-Security-Policy\" content=\"${csp}\">\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>${escapeText(options.title)}</title>\n${styles}\n<script${nonce}>\n${buildBridgeScript(bootstrap)}\n</script>\n</head>\n<body>\n${scripts}\n${html}\n</body>\n</html>`;\n}\n\nexport function buildDocument(html: string, options: SandboxOptions = {}): string {\n return buildDocumentFromOptions(html, normalizeSandboxOptions(options), { channel: '', generation: 0 });\n}\n","export const MSG_CUSTOM = 'custom';\nexport const MSG_ERROR = 'error';\nexport const MSG_HTML_REPLACE = 'html-replace';\nexport const MSG_READY = 'ready';\nexport const MSG_RESIZE = 'resize';\nexport const MSG_STATE_UPDATE_ALL = 'state-update-all';\nexport const MSG_STYLE_PATCH = 'style-patch';\n\nexport interface BridgeBootstrap {\n channel: string;\n generation: number;\n}\n\nexport type ProtocolMessage = Record<string, unknown> & { channel: string; generation: number; type: string };\n\nexport function isProtocolMessage(value: unknown, bootstrap: BridgeBootstrap): value is ProtocolMessage {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as { channel?: unknown }).channel === 'string' &&\n (value as { channel: string }).channel === bootstrap.channel &&\n typeof (value as { generation?: unknown }).generation === 'number' &&\n (value as { generation: number }).generation === bootstrap.generation &&\n typeof (value as { type?: unknown }).type === 'string'\n );\n}\n\nexport function createChannel(): string {\n if (typeof crypto.randomUUID === 'function') return crypto.randomUUID();\n\n const bytes = crypto.getRandomValues(new Uint32Array(4));\n\n return Array.from(bytes, (byte) => byte.toString(16).padStart(8, '0')).join('');\n}\n\nexport function envelope(bootstrap: BridgeBootstrap, message: Record<string, unknown>): ProtocolMessage {\n return { ...message, channel: bootstrap.channel, generation: bootstrap.generation } as ProtocolMessage;\n}\n","import { devOnly, warn } from './_dev.js';\nimport { buildDocumentFromOptions } from './_document.js';\nimport { normalizeSandboxOptions } from './_policy.js';\nimport {\n type BridgeBootstrap,\n createChannel,\n envelope,\n isProtocolMessage,\n MSG_CUSTOM,\n MSG_ERROR,\n MSG_HTML_REPLACE,\n MSG_READY,\n MSG_RESIZE,\n MSG_STATE_UPDATE_ALL,\n MSG_STYLE_PATCH,\n} from './_protocol.js';\nimport { SandboxTimeoutError } from './errors.js';\nimport type { SandboxHandle, SandboxMessage, SandboxOptions } from './types.js';\n\nexport function createSandbox<State extends object = Record<string, unknown>>(\n container: HTMLElement,\n options: SandboxOptions = {},\n): SandboxHandle<State> {\n const normalized = normalizeSandboxOptions(options);\n const channel = createChannel();\n const styles: Record<string, string> = { ...normalized.styles };\n const listeners = new Set<(message: SandboxMessage) => void>();\n const pendingState: Partial<State> = {};\n const controller = new AbortController();\n let iframe: HTMLIFrameElement | null = null;\n let disposed = false;\n let bridgeReady = false;\n let generation = 0;\n let readyTimeout: ReturnType<typeof setTimeout> | null = null;\n let currentRender: { reject: (error: unknown) => void; resolve: () => void } | null = null;\n\n function currentBootstrap(): BridgeBootstrap {\n return { channel, generation };\n }\n\n function clearReadyTimeout(): void {\n if (readyTimeout === null) return;\n\n clearTimeout(readyTimeout);\n readyTimeout = null;\n }\n\n function supersedePendingRender(): void {\n clearReadyTimeout();\n currentRender?.resolve();\n currentRender = null;\n bridgeReady = false;\n }\n\n function broadcast(message: SandboxMessage): void {\n for (const listener of listeners) {\n try {\n listener(message);\n } catch {\n warn('onMessage() handler threw: remaining handlers will still run.');\n }\n }\n }\n\n function handleMessage(event: MessageEvent): void {\n if (!iframe || event.source !== iframe.contentWindow) return;\n\n const bootstrap = currentBootstrap();\n\n if (!isProtocolMessage(event.data, bootstrap)) return;\n\n switch (event.data.type) {\n case MSG_CUSTOM:\n if (typeof event.data.event === 'string') {\n broadcast({ detail: event.data.detail, event: event.data.event, type: MSG_CUSTOM });\n }\n\n break;\n case MSG_ERROR:\n if (typeof event.data.message === 'string') {\n broadcast({\n message: event.data.message,\n ...(typeof event.data.stack === 'string' ? { stack: event.data.stack } : {}),\n type: MSG_ERROR,\n });\n }\n\n break;\n case MSG_READY:\n supersedePendingRender();\n bridgeReady = true;\n flushPendingState();\n break;\n case MSG_RESIZE:\n if (typeof event.data.height === 'number' && Number.isFinite(event.data.height) && event.data.height >= 0) {\n broadcast({ height: event.data.height, type: MSG_RESIZE });\n }\n\n break;\n }\n }\n\n function ensureIframe(): HTMLIFrameElement {\n if (iframe) return iframe;\n\n iframe = document.createElement('iframe');\n iframe.setAttribute('sandbox', 'allow-scripts');\n iframe.setAttribute('referrerpolicy', 'no-referrer');\n iframe.dataset.sandboxChannel = channel;\n container.appendChild(iframe);\n window.addEventListener('message', handleMessage);\n\n return iframe;\n }\n\n function dispose(): void {\n if (disposed) return;\n\n disposed = true;\n supersedePendingRender();\n controller.abort();\n\n if (iframe) {\n window.removeEventListener('message', handleMessage);\n iframe.remove();\n iframe = null;\n }\n\n listeners.clear();\n }\n\n function onMessage(handler: (message: SandboxMessage) => void): () => void {\n if (disposed) {\n warn('onMessage() called on a disposed sandbox: handler will never fire.');\n\n return () => {};\n }\n\n listeners.add(handler);\n\n return () => listeners.delete(handler);\n }\n\n function render(html: string): Promise<void> {\n if (disposed) {\n warn('render() called on a disposed sandbox.');\n\n return Promise.resolve();\n }\n\n if (!html.trim()) warn('render() called with empty HTML.');\n\n supersedePendingRender();\n generation += 1;\n\n const current = ensureIframe();\n const bootstrap = currentBootstrap();\n const documentOptions = { ...normalized, styles: { ...styles } };\n\n current.dataset.sandboxGeneration = String(generation);\n current.srcdoc = buildDocumentFromOptions(html, documentOptions, bootstrap);\n\n const promise = new Promise<void>((resolve, reject) => {\n currentRender = { reject, resolve };\n });\n\n readyTimeout = setTimeout(() => {\n if (!currentRender) return;\n\n currentRender.reject(\n new SandboxTimeoutError(\n `render() did not receive a 'ready' signal within ${normalized.readyTimeout}ms. ` +\n 'An injected script may have blocked document initialization.',\n ),\n );\n currentRender = null;\n readyTimeout = null;\n }, normalized.readyTimeout);\n\n return promise;\n }\n\n function send(message: Record<string, unknown>): void {\n iframe?.contentWindow?.postMessage(envelope(currentBootstrap(), message), '*');\n }\n\n function flushPendingState(): void {\n if (!bridgeReady || !iframe?.contentWindow || Object.keys(pendingState).length === 0) return;\n\n send({ record: { ...pendingState }, type: MSG_STATE_UPDATE_ALL });\n }\n\n function setState(update: Partial<State>): void {\n if (disposed) {\n warn('setState() called on a disposed sandbox.');\n\n return;\n }\n\n Object.assign(pendingState, update);\n\n if (bridgeReady && iframe?.contentWindow && Object.keys(update).length > 0) {\n send({ record: update, type: MSG_STATE_UPDATE_ALL });\n }\n }\n\n function replaceBody(html: string): void {\n if (disposed) {\n warn('replaceBody() called on a disposed sandbox.');\n\n return;\n }\n\n if (!bridgeReady || !iframe?.contentWindow) {\n warn('replaceBody() called before render() has resolved: bridge is not ready. Await render() first.');\n\n return;\n }\n\n send({ html, type: MSG_HTML_REPLACE });\n }\n\n function updateStyle(id: string, css: string): void {\n if (disposed) {\n warn('updateStyle() called on a disposed sandbox.');\n\n return;\n }\n\n devOnly(() => {\n if (!(id in styles)) {\n warn(`updateStyle('${id}', …): '${id}' is not a known styles key.`);\n }\n });\n\n styles[id] = css;\n\n if (bridgeReady && iframe?.contentWindow) send({ css, id, type: MSG_STYLE_PATCH });\n }\n\n return {\n get disposalSignal() {\n return controller.signal;\n },\n dispose,\n get disposed() {\n return disposed;\n },\n onMessage,\n render,\n replaceBody,\n setState,\n [Symbol.dispose]() {\n dispose();\n },\n updateStyle,\n };\n}\n"],"mappings":"A,mECEA,SAAgB,kBAAkB,EAAoC,CAIpE,MAAO;;4BAHS,KAAK,UAAUA,EAAU,OAKf,EAAQ;+BAJf,OAAOA,EAAU,UAKP,EAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8DxC,KAAK,CACP,CCvEA,IAAa,aAAb,cAAkC,KAAM,CACtC,YAAY,EAAiB,EAAqB,CAChD,MAAMC,EAASC,CAAI,EACnB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAMa,oBAAb,cAAyC,YAAa,CAAC,EAG1C,0BAAb,cAA+C,YAAa,CAAC,ECEvDC,EAAK,2BACLC,EAAO,wCACPC,EAAQ,2BAEd,SAAS,KAAK,EAAe,EAAuB,CAClD,MAAM,IAAI,0BAA0B,GAAGC,EAAM,eAAe,OAAOC,CAAK,GAAG,CAC7E,CAEA,SAAS,gBAAgB,EAAgB,EAAuB,CAC9D,GAAI,OAAOA,GAAU,SAAU,OAAO,KAAKD,EAAOC,CAAK,EAEvD,IAAIC,EAEJ,GAAI,CACF,EAAM,IAAI,IAAID,CAAK,CACrB,MAAQ,CACN,OAAO,KAAKD,EAAOC,CAAK,CAC1B,CAaA,OAVGC,EAAI,WAAa,SAAWA,EAAI,WAAa,UAC9CA,EAAI,UACJA,EAAI,UACJA,EAAI,WAAa,KACjBA,EAAI,QACJA,EAAI,KAEG,KAAKF,EAAOC,CAAK,EAGnBC,EAAI,MACb,CAEA,SAAS,iBAAiB,EAA8B,EAAyB,CAI/E,OAHIC,IAAW,IAAA,GAAkB,CAAC,EAC7B,MAAM,QAAQA,CAAM,EAElBA,EAAO,IAAK,GAAU,gBAAgBF,EAAOD,CAAK,CAAC,EAFvB,KAAKA,EAAOG,CAAM,CAGvD,CAEA,SAAS,gBAAgB,EAAwB,CAC/C,GAAI,OAAOF,GAAU,SAAU,OAAO,KAAK,UAAWA,CAAK,EAE3D,IAAIC,EAEJ,GAAI,CACF,EAAM,IAAI,IAAID,CAAK,CACrB,MAAQ,CACN,OAAO,KAAK,UAAWA,CAAK,CAC9B,CAIA,OAFIC,EAAI,WAAa,SAAWA,EAAI,WAAa,SAAiB,KAAK,UAAWD,CAAK,EAEhFC,EAAI,IACb,CAEA,SAAS,gBAAgB,EAAoE,CAC3F,GAAIE,IAAW,IAAA,GAAW,MAAO,CAAC,EAClC,GAAI,OAAOA,GAAW,WAAYA,GAAmB,MAAM,QAAQA,CAAM,EAAG,OAAO,KAAK,SAAUA,CAAM,EAExG,IAAMC,EAAqC,CAAC,EAE5C,IAAK,GAAM,CAACC,EAAIC,KAAQ,OAAO,QAAQH,CAAM,EACtCP,EAAG,KAAKS,CAAE,GAAK,OAAOC,GAAQ,UAAU,KAAK,SAAUD,CAAE,EAE9D,EAAWA,GAAMC,EAGnB,OAAOF,CACT,CAEA,SAAgB,wBAAwB,EAA0B,CAAC,EAA6B,EAC1F,OAAOG,GAAY,WAAYA,GAAoB,MAAM,QAAQA,CAAO,IAAG,KAAK,UAAWA,CAAO,EAEtG,IAAMC,EAAOD,EAAQ,MAAQ,KAExBV,EAAK,KAAKW,CAAI,GAAG,KAAK,OAAQA,CAAI,EAEnCD,EAAQ,QAAU,IAAA,IAAeT,EAAM,KAAKS,EAAQ,KAAK,GAAKA,EAAQ,MAAM,SAAW,GACzF,KAAK,QAASA,EAAQ,KAAK,EAGzBA,EAAQ,QAAU,IAAA,IAAa,OAAOA,EAAQ,OAAU,UAAU,KAAK,QAASA,EAAQ,KAAK,EAEjG,IAAME,EAAkBF,EAAQ,aAC1BG,EAAeD,GAAA,IAGnBA,IAAoB,IAAA,KACnBA,IAAoB,MACnB,OAAOA,GAAoB,UAC3B,CAAC,OAAO,SAASA,CAAe,GAChCA,GAAmB,GACnBA,EAAkB,GAAK,GAAK,IAE9B,KAAK,eAAgBA,CAAe,EAGlCF,EAAQ,UAAY,IAAA,IAAa,CAAC,MAAM,QAAQA,EAAQ,OAAO,GAAG,KAAK,UAAWA,EAAQ,OAAO,EAErG,IAAMI,GAAWJ,EAAQ,SAAW,CAAC,EAAA,CAAG,IAAI,eAAe,EAE3D,MAAO,CACL,mBAAoB,iBAAiBA,EAAQ,mBAAoB,oBAAoB,EACrF,oBAAqB,iBAAiBA,EAAQ,oBAAqB,qBAAqB,EACxF,qBAAsB,iBAAiBA,EAAQ,qBAAsB,sBAAsB,EAC3F,oBAAqB,iBAAiBA,EAAQ,oBAAqB,qBAAqB,EACxF,OACA,MAAOA,EAAQ,MACf,eACA,UACA,OAAQ,gBAAgBA,EAAQ,MAAM,EACtC,MAAOA,EAAQ,OAAS,EAC1B,CACF,CAEA,SAAgB,oBAAoB,EAA2C,CAC7E,IAAMK,EAAgB,CACpB,kBACA,GAAIL,EAAQ,MAAQ,CAAC,UAAUA,EAAQ,MAAM,EAAE,EAAI,CAAC,EACpD,GAAGA,EAAQ,qBACX,GAAGA,EAAQ,QAAQ,IAAK,GAAW,IAAI,IAAIM,CAAM,CAAC,CAAC,MAAM,CAC3D,CAAC,CAAC,KAAK,GAAG,EACJC,EAAe,CAAC,kBAAmB,GAAGP,EAAQ,mBAAmB,CAAC,CAAC,KAAK,GAAG,EAC3EQ,EAAe,CAAC,QAAS,GAAGR,EAAQ,mBAAmB,CAAC,CAAC,KAAK,GAAG,EACjES,EAAcT,EAAQ,mBAAmB,KAAK,GAAG,GAAK,SAE5D,MAAO,CACL,qBACA,cAAcK,IACd,aAAaE,IACb,WAAWC,IACX,YAAYC,IACZ,qBACA,qBACA,iBACF,CAAC,CAAC,KAAK,IAAI,CACb,CCtJA,SAAS,UAAU,EAAqB,CACtC,OAAOV,EAAI,QAAQ,aAAc,aAAa,CAChD,CAEA,SAAS,WAAW,EAAuB,CACzC,OAAON,EAAM,QAAQ,KAAM,OAAO,CAAC,CAAC,QAAQ,KAAM,QAAQ,CAAC,CAAC,QAAQ,KAAM,MAAM,CAAC,CAAC,QAAQ,KAAM,MAAM,CACxG,CAEA,SAAS,WAAW,EAAuB,CACzC,OAAOA,EAAM,QAAQ,KAAM,OAAO,CAAC,CAAC,QAAQ,KAAM,MAAM,CAAC,CAAC,QAAQ,KAAM,MAAM,CAChF,CAEA,SAAgB,yBACd,EACA,EACA,EACQ,CACR,IAAMiB,EAAM,WAAW,oBAAoBV,CAAO,CAAC,EAC7CJ,EAAS,OAAO,QAAQI,EAAQ,MAAM,CAAC,CAC1C,KAAK,CAACF,EAAIC,KAAS,cAAc,WAAWD,CAAE,EAAE,IAAI,UAAUC,CAAG,EAAE,SAAS,CAAC,CAC7E,KAAK;CAAI,EACNK,EAAUJ,EAAQ,QACrB,IAAK,GAAQ,wCAAwC,WAAWW,CAAG,EAAE,aAAY,CAAC,CAClF,KAAK;CAAI,EACNC,EAAQZ,EAAQ,MAAQ,WAAW,WAAWA,EAAQ,KAAK,EAAE,GAAK,GAExE,MAAO;cACK,WAAWA,EAAQ,IAAI,EAAE;;sDAEeU,EAAI;;;SAGjD,WAAWV,EAAQ,KAAK,EAAE;EACjCJ,EAAO;SACAgB,EAAM;EACb,kBAAkB1B,CAAS,EAAE;;;;EAI7BkB,EAAQ;EACRS,EAAK;;QAGP,CChDA,IAAaC,EAAa,SACbC,EAAY,QACZC,EAAmB,eACnBC,EAAY,QACZC,EAAa,SACbC,EAAuB,mBACvBC,EAAkB,cAS/B,SAAgB,kBAAkB,EAAgB,EAAsD,CACtG,OACE,OAAO3B,GAAU,YACjBA,GACA,OAAQA,EAAgC,SAAY,UACnDA,EAA8B,UAAYP,EAAU,SACrD,OAAQO,EAAmC,YAAe,UACzDA,EAAiC,aAAeP,EAAU,YAC3D,OAAQO,EAA6B,MAAS,QAElD,CAEA,SAAgB,eAAwB,CACtC,GAAI,OAAO,OAAO,YAAe,WAAY,OAAO,OAAO,WAAW,EAEtE,IAAM4B,EAAQ,OAAO,gBAAgB,IAAI,YAAY,CAAC,CAAC,EAEvD,OAAO,MAAM,KAAKA,EAAQ,GAASC,EAAK,SAAS,EAAE,CAAC,CAAC,SAAS,EAAG,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,CAChF,CAEA,SAAgB,SAAS,EAA4B,EAAmD,CACtG,MAAO,CAAE,GAAGnC,EAAS,QAASD,EAAU,QAAS,WAAYA,EAAU,UAAW,CACpF,CClBA,SAAgB,cACd,EACA,EAA0B,CAAC,EACL,CACtB,IAAMW,EAAa,wBAAwBG,CAAO,EAC5CuB,EAAU,cAAc,EACxB3B,EAAiC,CAAE,GAAGC,EAAW,MAAO,EACxD2B,EAAY,IAAI,IAChBC,EAA+B,CAAC,EAChCC,EAAa,IAAI,gBACnBC,EAAmC,KACnCC,EAAW,GACXC,EAAc,GACdC,EAAa,EACb3B,EAAqD,KACrD4B,EAAkF,KAEtF,SAAS,kBAAoC,CAC3C,MAAO,CAAE,UAAS,YAAW,CAC/B,CAEA,SAAS,mBAA0B,CAC7B5B,IAAiB,OAErB,aAAaA,CAAY,EACzB,EAAe,KACjB,CAEA,SAAS,wBAA+B,CACtC,kBAAkB,EAClB4B,GAAe,QAAQ,EACvB,EAAgB,KAChB,EAAc,EAChB,CAEA,SAAS,UAAU,EAA+B,CAChD,IAAK,IAAMC,KAAYR,EACrB,GAAI,CACFQ,EAAS7C,CAAO,CAClB,MAAQ,CAER,CAEJ,CAEA,SAAS,cAAc,EAA2B,CAChD,GAAI,CAACwC,GAAUM,EAAM,SAAWN,EAAO,cAAe,OAEtD,IAAMzC,EAAY,iBAAiB,EAEnC,GAAK,kBAAkB+C,EAAM,KAAM/C,CAAS,EAE5C,OAAQ+C,EAAM,KAAK,KAAnB,CACE,KAAKnB,EACC,OAAOmB,EAAM,KAAK,OAAU,UAC9B,UAAU,CAAE,OAAQA,EAAM,KAAK,OAAQ,MAAOA,EAAM,KAAK,MAAO,KAAMnB,CAAW,CAAC,EAGpF,MACF,KAAKC,EACC,OAAOkB,EAAM,KAAK,SAAY,UAChC,UAAU,CACR,QAASA,EAAM,KAAK,QACpB,GAAI,OAAOA,EAAM,KAAK,OAAU,SAAW,CAAE,MAAOA,EAAM,KAAK,KAAM,EAAI,CAAC,EAC1E,KAAMlB,CACR,CAAC,EAGH,MACF,KAAKE,EACH,uBAAuB,EACvB,EAAc,GACd,kBAAkB,EAClB,MACF,KAAKC,EACC,OAAOe,EAAM,KAAK,QAAW,UAAY,OAAO,SAASA,EAAM,KAAK,MAAM,GAAKA,EAAM,KAAK,QAAU,GACtG,UAAU,CAAE,OAAQA,EAAM,KAAK,OAAQ,KAAMf,CAAW,CAAC,CAI/D,CACF,CAEA,SAAS,cAAkC,CAUzC,OATIS,IAEJ,EAAS,SAAS,cAAc,QAAQ,EACxCA,EAAO,aAAa,UAAW,eAAe,EAC9CA,EAAO,aAAa,iBAAkB,aAAa,EACnD,EAAO,QAAQ,eAAiBJ,EAChCW,EAAU,YAAYP,CAAM,EAC5B,OAAO,iBAAiB,UAAW,aAAa,EAEzCA,EACT,CAEA,SAAS,SAAgB,CACnBC,IAEJ,EAAW,GACX,uBAAuB,EACvBF,EAAW,MAAM,EAEjB,AAGEC,KAFA,OAAO,oBAAoB,UAAW,aAAa,EACnDA,EAAO,OAAO,EACL,MAGXH,EAAU,MAAM,EAClB,CAEA,SAAS,UAAU,EAAwD,CASzE,OARII,MAGW,CAAC,GAGhBJ,EAAU,IAAIW,CAAO,MAERX,EAAU,OAAOW,CAAO,EACvC,CAEA,SAAS,OAAO,EAA6B,CAC3C,GAAIP,EAGF,OAAO,QAAQ,QAAQ,EAGpBf,EAAK,KAAK,EAEf,uBAAuB,EACvB,GAAc,EAEd,IAAMuB,EAAU,aAAa,EACvBlD,EAAY,iBAAiB,EAC7BmD,EAAkB,CAAE,GAAGxC,EAAY,OAAQ,CAAE,GAAGD,CAAO,CAAE,EAE/D,EAAQ,QAAQ,kBAAoB,OAAOkC,CAAU,EACrD,EAAQ,OAAS,yBAAyBjB,EAAMwB,EAAiBnD,CAAS,EAE1E,IAAMoD,EAAU,IAAI,SAAe,EAAS,IAAW,CACrD,EAAgB,CAAE,SAAQ,SAAQ,CACpC,CAAC,EAeD,MAbA,GAAe,eAAiB,CACzBP,IAELA,EAAc,OACZ,IAAI,oBACF,oDAAoDlC,EAAW,aAAa,iEAE9E,CACF,EACA,EAAgB,KAChB,EAAe,KACjB,EAAGA,EAAW,YAAY,EAEnByC,CACT,CAEA,SAAS,KAAK,EAAwC,CACpDX,GAAQ,eAAe,YAAY,SAAS,iBAAiB,EAAGxC,CAAO,EAAG,GAAG,CAC/E,CAEA,SAAS,mBAA0B,CAC5B0C,GAAgBF,GAAQ,eAAiB,OAAO,KAAKF,CAAY,CAAC,CAAC,SAAW,GAEnF,KAAK,CAAE,OAAQ,CAAE,GAAGA,CAAa,EAAG,KAAMN,CAAqB,CAAC,CAClE,CAEA,SAAS,SAAS,EAA8B,CAC1CS,IAMJ,OAAO,OAAOH,EAAcc,CAAM,EAE9BV,GAAeF,GAAQ,eAAiB,OAAO,KAAKY,CAAM,CAAC,CAAC,OAAS,GACvE,KAAK,CAAE,OAAQA,EAAQ,KAAMpB,CAAqB,CAAC,EAEvD,CAEA,SAAS,YAAY,EAAoB,CACnCS,GAMCC,GAAgBF,GAAQ,eAM7B,KAAK,CAAE,OAAM,KAAMX,CAAiB,CAAC,CACvC,CAEA,SAAS,YAAY,EAAY,EAAmB,CAC9CY,IAYJ,EAAO9B,GAAMC,EAET8B,GAAeF,GAAQ,eAAe,KAAK,CAAE,MAAK,KAAI,KAAMP,CAAgB,CAAC,EACnF,CAEA,MAAO,CACL,IAAI,gBAAiB,CACnB,OAAOM,EAAW,MACpB,EACA,QACA,IAAI,UAAW,CACb,OAAOE,CACT,EACA,UACA,OACA,YACA,SACA,CAAC,OAAO,UAAW,CACjB,QAAQ,CACV,EACA,WACF,CACF,C"}
@@ -63,7 +63,7 @@ window.addEventListener('DOMContentLoaded', function() {
63
63
  }).observe(document.body);
64
64
  });
65
65
  })();
66
- `.trim()}var SandboxError=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},SandboxTimeoutError=class extends SandboxError{},SandboxConfigurationError=class extends SandboxError{},t=/^[A-Za-z][A-Za-z0-9_-]*$/,n=/^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/,r=/^[A-Za-z0-9+/_-]+={0,2}$/;function fail(e,t){throw new SandboxConfigurationError(`${e} is invalid: ${String(t)}`)}function normalizeOrigin(e,t){if(typeof e!=`string`)return fail(t,e);let n;try{n=new URL(e)}catch{return fail(t,e)}return n.protocol!==`http:`&&n.protocol!==`https:`||n.username||n.password||n.pathname!==`/`||n.search||n.hash?fail(t,e):n.origin}function normalizeOrigins(e,t){return e===void 0?[]:Array.isArray(e)?e.map(e=>normalizeOrigin(e,t)):fail(t,e)}function normalizeScript(e){if(typeof e!=`string`)return fail(`scripts`,e);let t;try{t=new URL(e)}catch{return fail(`scripts`,e)}return t.protocol!==`http:`&&t.protocol!==`https:`?fail(`scripts`,e):t.href}function normalizeStyles(e){if(e===void 0)return{};if(typeof e!=`object`||!e||Array.isArray(e))return fail(`styles`,e);let n={};for(let[r,i]of Object.entries(e))(!t.test(r)||typeof i!=`string`)&&fail(`styles`,r),n[r]=i;return n}function normalizeSandboxOptions(e={}){(typeof e!=`object`||!e||Array.isArray(e))&&fail(`options`,e);let t=e.lang??`en`;n.test(t)||fail(`lang`,t),e.nonce!==void 0&&(!r.test(e.nonce)||e.nonce.length===0)&&fail(`nonce`,e.nonce),e.title!==void 0&&typeof e.title!=`string`&&fail(`title`,e.title);let i=e.readyTimeout,a=i??5e3;i!==void 0&&(i===null||typeof i!=`number`||!Number.isFinite(i)||i<=0||i>2**31-1)&&fail(`readyTimeout`,i),e.scripts!==void 0&&!Array.isArray(e.scripts)&&fail(`scripts`,e.scripts);let o=(e.scripts??[]).map(normalizeScript);return{allowedFontOrigins:normalizeOrigins(e.allowedFontOrigins,`allowedFontOrigins`),allowedImageOrigins:normalizeOrigins(e.allowedImageOrigins,`allowedImageOrigins`),allowedScriptOrigins:normalizeOrigins(e.allowedScriptOrigins,`allowedScriptOrigins`),allowedStyleOrigins:normalizeOrigins(e.allowedStyleOrigins,`allowedStyleOrigins`),lang:t,nonce:e.nonce,readyTimeout:a,scripts:o,styles:normalizeStyles(e.styles),title:e.title??``}}function buildCspFromOptions(e){let t=[`'unsafe-inline'`,...e.nonce?[`'nonce-${e.nonce}'`]:[],...e.allowedScriptOrigins,...e.scripts.map(e=>new URL(e).origin)].join(` `),n=[`'unsafe-inline'`,...e.allowedStyleOrigins].join(` `),r=[`data:`,...e.allowedImageOrigins].join(` `),i=e.allowedFontOrigins.join(` `)||`'none'`;return[`default-src 'none'`,`script-src ${t}`,`style-src ${n}`,`img-src ${r}`,`font-src ${i}`,`connect-src 'none'`,`form-action 'none'`,`base-uri 'none'`].join(`; `)}function escapeCss(e){return e.replace(/<\/style/gi,`\\3C /style`)}function escapeAttr(e){return e.replace(/&/g,`&amp;`).replace(/"/g,`&quot;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`)}function escapeText(e){return e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`)}function buildDocumentFromOptions(e,t,n){let r=escapeAttr(buildCspFromOptions(t)),i=Object.entries(t.styles).map(([e,t])=>`<style id="${escapeAttr(e)}">${escapeCss(t)}</style>`).join(`
66
+ `.trim()}var SandboxError=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},SandboxTimeoutError=class extends SandboxError{},SandboxConfigurationError=class extends SandboxError{},t=/^[A-Za-z][A-Za-z0-9_-]*$/,n=/^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/,r=/^[A-Za-z0-9+/_-]+={0,2}$/;function fail(e,t){throw new SandboxConfigurationError(`${e} is invalid: ${String(t)}`)}function normalizeOrigin(e,t){if(typeof e!=`string`)return fail(t,e);let n;try{n=new URL(e)}catch{return fail(t,e)}return n.protocol!==`http:`&&n.protocol!==`https:`||n.username||n.password||n.pathname!==`/`||n.search||n.hash?fail(t,e):n.origin}function normalizeOrigins(e,t){return e===void 0?[]:Array.isArray(e)?e.map(e=>normalizeOrigin(e,t)):fail(t,e)}function normalizeScript(e){if(typeof e!=`string`)return fail(`scripts`,e);let t;try{t=new URL(e)}catch{return fail(`scripts`,e)}return t.protocol!==`http:`&&t.protocol!==`https:`?fail(`scripts`,e):t.href}function normalizeStyles(e){if(e===void 0)return{};if(typeof e!=`object`||!e||Array.isArray(e))return fail(`styles`,e);let n={};for(let[r,i]of Object.entries(e))t.test(r)&&typeof i==`string`||fail(`styles`,r),n[r]=i;return n}function normalizeSandboxOptions(e={}){(typeof e!=`object`||!e||Array.isArray(e))&&fail(`options`,e);let t=e.lang??`en`;n.test(t)||fail(`lang`,t),e.nonce===void 0||r.test(e.nonce)&&e.nonce.length!==0||fail(`nonce`,e.nonce),e.title!==void 0&&typeof e.title!=`string`&&fail(`title`,e.title);let i=e.readyTimeout,a=i??5e3;i!==void 0&&(i===null||typeof i!=`number`||!Number.isFinite(i)||i<=0||i>2**31-1)&&fail(`readyTimeout`,i),e.scripts!==void 0&&!Array.isArray(e.scripts)&&fail(`scripts`,e.scripts);let o=(e.scripts??[]).map(normalizeScript);return{allowedFontOrigins:normalizeOrigins(e.allowedFontOrigins,`allowedFontOrigins`),allowedImageOrigins:normalizeOrigins(e.allowedImageOrigins,`allowedImageOrigins`),allowedScriptOrigins:normalizeOrigins(e.allowedScriptOrigins,`allowedScriptOrigins`),allowedStyleOrigins:normalizeOrigins(e.allowedStyleOrigins,`allowedStyleOrigins`),lang:t,nonce:e.nonce,readyTimeout:a,scripts:o,styles:normalizeStyles(e.styles),title:e.title??``}}function buildCspFromOptions(e){let t=[`'unsafe-inline'`,...e.nonce?[`'nonce-${e.nonce}'`]:[],...e.allowedScriptOrigins,...e.scripts.map(e=>new URL(e).origin)].join(` `),n=[`'unsafe-inline'`,...e.allowedStyleOrigins].join(` `),r=[`data:`,...e.allowedImageOrigins].join(` `),i=e.allowedFontOrigins.join(` `)||`'none'`;return[`default-src 'none'`,`script-src ${t}`,`style-src ${n}`,`img-src ${r}`,`font-src ${i}`,`connect-src 'none'`,`form-action 'none'`,`base-uri 'none'`].join(`; `)}function escapeCss(e){return e.replace(/<\/style/gi,`\\3C /style`)}function escapeAttr(e){return e.replace(/&/g,`&amp;`).replace(/"/g,`&quot;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`)}function escapeText(e){return e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`)}function buildDocumentFromOptions(e,t,n){let r=escapeAttr(buildCspFromOptions(t)),i=Object.entries(t.styles).map(([e,t])=>`<style id="${escapeAttr(e)}">${escapeCss(t)}</style>`).join(`
67
67
  `),a=t.scripts.map(e=>`<script crossorigin="anonymous" src="${escapeAttr(e)}"><\/script>`).join(`
68
68
  `),o=t.nonce?` nonce="${escapeAttr(t.nonce)}"`:``;return`<!doctype html>
69
69
  <html lang="${escapeAttr(t.lang)}">
@@ -1 +1 @@
1
- {"version":3,"file":"sandbox.iife.js","names":["exports","bootstrap","message","opts","ID","LANG","NONCE","field","value","url","values","styles","normalized","id","css","options","lang","rawReadyTimeout","readyTimeout","scripts","scriptOrigins","script","styleOrigins","imageOrigins","fontOrigins","csp","src","nonce","html","MSG_CUSTOM","MSG_ERROR","MSG_HTML_REPLACE","MSG_READY","MSG_RESIZE","MSG_STATE_UPDATE_ALL","MSG_STYLE_PATCH","bytes","byte","channel","listeners","pendingState","controller","iframe","disposed","bridgeReady","generation","currentRender","listener","event","container","handler","current","documentOptions","promise","update"],"sources":["../src/_dev.ts","../src/_bridge.ts","../src/errors.ts","../src/_policy.ts","../src/_document.ts","../src/_protocol.ts","../src/_runtime.ts"],"sourcesContent":["const isDev = !(globalThis as { __SANDBOX_PROD__?: boolean }).__SANDBOX_PROD__;\n\n/** @internal @security: some callers interpolate user-supplied values (e.g. styles key) into msg; do not log PII. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/sandbox] ${msg}`);\n}\n\n/** @internal: Run fn only in dev builds. Use when dev-only logic goes beyond a single warn() / error() call. */\nexport function devOnly(fn: () => void): void {\n if (isDev) fn();\n}\n","import type { BridgeBootstrap } from './_protocol.js';\n\nexport function buildBridgeScript(bootstrap: BridgeBootstrap): string {\n const channel = JSON.stringify(bootstrap.channel);\n const generation = String(bootstrap.generation);\n\n return `\n(function() {\nwindow.__sandboxChannel = ${channel};\nwindow.__sandboxGeneration = ${generation};\nconst __sandboxChannel = window.__sandboxChannel;\nconst __sandboxGeneration = window.__sandboxGeneration;\nif ('ontouchstart' in window) document.addEventListener('touchstart', function() {}, { passive: true });\nwindow.addEventListener('message', function(e) {\n var msg = e.data;\n if (e.source !== parent || !msg || msg.channel !== __sandboxChannel || msg.generation !== __sandboxGeneration) return;\n if (msg.type === 'state-update-all' && msg.record && typeof msg.record === 'object') {\n var keys = Object.keys(msg.record);\n for (var i = 0; i < keys.length; i++) {\n var k = keys[i];\n document.dispatchEvent(new CustomEvent('sandbox:state-update', { detail: { key: k, value: msg.record[k] } }));\n }\n }\n if (msg.type === 'style-patch' && msg.id && typeof msg.css === 'string') {\n var el = document.getElementById(msg.id);\n if (el && el.tagName === 'STYLE') el.textContent = msg.css;\n }\n if (msg.type === 'html-replace' && typeof msg.html === 'string') {\n document.body.innerHTML = msg.html;\n }\n});\nfunction post(msg) {\n msg.channel = __sandboxChannel;\n msg.generation = __sandboxGeneration;\n parent.postMessage(msg, '*');\n}\nwindow.onerror = function(message, _src, _line, _col, err) {\n post({ type: 'error', message: String(message), stack: err && err.stack });\n return true;\n};\nwindow.addEventListener('unhandledrejection', function(e) {\n var reason = e.reason;\n post({ type: 'error', message: String(reason), stack: reason && reason.stack });\n});\nwindow.__sandbox__ = {\n emit: function(event, detail) {\n post({ type: 'custom', event: String(event), detail: detail });\n },\n onState: function(key, handler) {\n function listener(e) {\n if (e.detail && e.detail.key === key) handler(e.detail.value);\n }\n document.addEventListener('sandbox:state-update', listener);\n return function() {\n document.removeEventListener('sandbox:state-update', listener);\n };\n }\n};\nwindow.addEventListener('DOMContentLoaded', function() {\n post({ type: 'ready' });\n if (typeof ResizeObserver === 'undefined') return;\n new ResizeObserver(function(entries) {\n var entry = entries[0];\n if (!entry) return;\n var h = entry.borderBoxSize && entry.borderBoxSize[0]\n ? entry.borderBoxSize[0].blockSize\n : entry.contentRect.height;\n post({ type: 'resize', height: h });\n }).observe(document.body);\n});\n})();\n`.trim();\n}\n","/** Base class for all sandbox errors. */\nexport class SandboxError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Thrown when render() does not receive a 'ready' signal from the sandbox document\n * within the timeout window. An injected script may have blocked document initialization.\n */\nexport class SandboxTimeoutError extends SandboxError {}\n\n/** Thrown when Sandbox configuration cannot produce a valid document or CSP policy. */\nexport class SandboxConfigurationError extends SandboxError {}\n","import { SandboxConfigurationError } from './errors.js';\nimport type { SandboxOptions } from './types.js';\n\nexport interface NormalizedSandboxOptions {\n allowedFontOrigins: string[];\n allowedImageOrigins: string[];\n allowedScriptOrigins: string[];\n allowedStyleOrigins: string[];\n lang: string;\n nonce: string | undefined;\n readyTimeout: number;\n scripts: string[];\n styles: Record<string, string>;\n title: string;\n}\n\nexport const DEFAULT_READY_TIMEOUT_MS = 5000;\n\nconst ID = /^[A-Za-z][A-Za-z0-9_-]*$/;\nconst LANG = /^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/;\nconst NONCE = /^[A-Za-z0-9+/_-]+={0,2}$/;\n\nfunction fail(field: string, value: unknown): never {\n throw new SandboxConfigurationError(`${field} is invalid: ${String(value)}`);\n}\n\nfunction normalizeOrigin(value: unknown, field: string): string {\n if (typeof value !== 'string') return fail(field, value);\n\n let url: URL;\n\n try {\n url = new URL(value);\n } catch {\n return fail(field, value);\n }\n\n if (\n (url.protocol !== 'http:' && url.protocol !== 'https:') ||\n url.username ||\n url.password ||\n url.pathname !== '/' ||\n url.search ||\n url.hash\n ) {\n return fail(field, value);\n }\n\n return url.origin;\n}\n\nfunction normalizeOrigins(values: string[] | undefined, field: string): string[] {\n if (values === undefined) return [];\n if (!Array.isArray(values)) return fail(field, values);\n\n return values.map((value) => normalizeOrigin(value, field));\n}\n\nfunction normalizeScript(value: unknown): string {\n if (typeof value !== 'string') return fail('scripts', value);\n\n let url: URL;\n\n try {\n url = new URL(value);\n } catch {\n return fail('scripts', value);\n }\n\n if (url.protocol !== 'http:' && url.protocol !== 'https:') return fail('scripts', value);\n\n return url.href;\n}\n\nfunction normalizeStyles(styles: Record<string, string> | undefined): Record<string, string> {\n if (styles === undefined) return {};\n if (typeof styles !== 'object' || styles === null || Array.isArray(styles)) return fail('styles', styles);\n\n const normalized: Record<string, string> = {};\n\n for (const [id, css] of Object.entries(styles)) {\n if (!ID.test(id) || typeof css !== 'string') fail('styles', id);\n\n normalized[id] = css;\n }\n\n return normalized;\n}\n\nexport function normalizeSandboxOptions(options: SandboxOptions = {}): NormalizedSandboxOptions {\n if (typeof options !== 'object' || options === null || Array.isArray(options)) fail('options', options);\n\n const lang = options.lang ?? 'en';\n\n if (!LANG.test(lang)) fail('lang', lang);\n\n if (options.nonce !== undefined && (!NONCE.test(options.nonce) || options.nonce.length === 0)) {\n fail('nonce', options.nonce);\n }\n\n if (options.title !== undefined && typeof options.title !== 'string') fail('title', options.title);\n\n const rawReadyTimeout = options.readyTimeout;\n const readyTimeout = rawReadyTimeout ?? DEFAULT_READY_TIMEOUT_MS;\n\n if (\n rawReadyTimeout !== undefined &&\n (rawReadyTimeout === null ||\n typeof rawReadyTimeout !== 'number' ||\n !Number.isFinite(rawReadyTimeout) ||\n rawReadyTimeout <= 0 ||\n rawReadyTimeout > 2 ** 31 - 1)\n ) {\n fail('readyTimeout', rawReadyTimeout);\n }\n\n if (options.scripts !== undefined && !Array.isArray(options.scripts)) fail('scripts', options.scripts);\n\n const scripts = (options.scripts ?? []).map(normalizeScript);\n\n return {\n allowedFontOrigins: normalizeOrigins(options.allowedFontOrigins, 'allowedFontOrigins'),\n allowedImageOrigins: normalizeOrigins(options.allowedImageOrigins, 'allowedImageOrigins'),\n allowedScriptOrigins: normalizeOrigins(options.allowedScriptOrigins, 'allowedScriptOrigins'),\n allowedStyleOrigins: normalizeOrigins(options.allowedStyleOrigins, 'allowedStyleOrigins'),\n lang,\n nonce: options.nonce,\n readyTimeout,\n scripts,\n styles: normalizeStyles(options.styles),\n title: options.title ?? '',\n };\n}\n\nexport function buildCspFromOptions(options: NormalizedSandboxOptions): string {\n const scriptOrigins = [\n \"'unsafe-inline'\",\n ...(options.nonce ? [`'nonce-${options.nonce}'`] : []),\n ...options.allowedScriptOrigins,\n ...options.scripts.map((script) => new URL(script).origin),\n ].join(' ');\n const styleOrigins = [\"'unsafe-inline'\", ...options.allowedStyleOrigins].join(' ');\n const imageOrigins = ['data:', ...options.allowedImageOrigins].join(' ');\n const fontOrigins = options.allowedFontOrigins.join(' ') || \"'none'\";\n\n return [\n \"default-src 'none'\",\n `script-src ${scriptOrigins}`,\n `style-src ${styleOrigins}`,\n `img-src ${imageOrigins}`,\n `font-src ${fontOrigins}`,\n \"connect-src 'none'\",\n \"form-action 'none'\",\n \"base-uri 'none'\",\n ].join('; ');\n}\n\nexport function buildCsp(options: SandboxOptions = {}): string {\n return buildCspFromOptions(normalizeSandboxOptions(options));\n}\n","import { buildBridgeScript } from './_bridge.js';\nimport { buildCspFromOptions, type NormalizedSandboxOptions, normalizeSandboxOptions } from './_policy.js';\nimport type { BridgeBootstrap } from './_protocol.js';\nimport type { SandboxOptions } from './types.js';\n\nfunction escapeCss(css: string): string {\n return css.replace(/<\\/style/gi, '\\\\3C /style');\n}\n\nfunction escapeAttr(value: string): string {\n return value.replace(/&/g, '&amp;').replace(/\"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n}\n\nfunction escapeText(value: string): string {\n return value.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n}\n\nexport function buildDocumentFromOptions(\n html: string,\n options: NormalizedSandboxOptions,\n bootstrap: BridgeBootstrap,\n): string {\n const csp = escapeAttr(buildCspFromOptions(options));\n const styles = Object.entries(options.styles)\n .map(([id, css]) => `<style id=\"${escapeAttr(id)}\">${escapeCss(css)}</style>`)\n .join('\\n');\n const scripts = options.scripts\n .map((src) => `<script crossorigin=\"anonymous\" src=\"${escapeAttr(src)}\"></script>`)\n .join('\\n');\n const nonce = options.nonce ? ` nonce=\"${escapeAttr(options.nonce)}\"` : '';\n\n return `<!doctype html>\n<html lang=\"${escapeAttr(options.lang)}\">\n<head>\n<meta http-equiv=\"Content-Security-Policy\" content=\"${csp}\">\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>${escapeText(options.title)}</title>\n${styles}\n<script${nonce}>\n${buildBridgeScript(bootstrap)}\n</script>\n</head>\n<body>\n${scripts}\n${html}\n</body>\n</html>`;\n}\n\nexport function buildDocument(html: string, options: SandboxOptions = {}): string {\n return buildDocumentFromOptions(html, normalizeSandboxOptions(options), { channel: '', generation: 0 });\n}\n","export const MSG_CUSTOM = 'custom';\nexport const MSG_ERROR = 'error';\nexport const MSG_HTML_REPLACE = 'html-replace';\nexport const MSG_READY = 'ready';\nexport const MSG_RESIZE = 'resize';\nexport const MSG_STATE_UPDATE_ALL = 'state-update-all';\nexport const MSG_STYLE_PATCH = 'style-patch';\n\nexport interface BridgeBootstrap {\n channel: string;\n generation: number;\n}\n\nexport type ProtocolMessage = Record<string, unknown> & { channel: string; generation: number; type: string };\n\nexport function isProtocolMessage(value: unknown, bootstrap: BridgeBootstrap): value is ProtocolMessage {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as { channel?: unknown }).channel === 'string' &&\n (value as { channel: string }).channel === bootstrap.channel &&\n typeof (value as { generation?: unknown }).generation === 'number' &&\n (value as { generation: number }).generation === bootstrap.generation &&\n typeof (value as { type?: unknown }).type === 'string'\n );\n}\n\nexport function createChannel(): string {\n if (typeof crypto.randomUUID === 'function') return crypto.randomUUID();\n\n const bytes = crypto.getRandomValues(new Uint32Array(4));\n\n return Array.from(bytes, (byte) => byte.toString(16).padStart(8, '0')).join('');\n}\n\nexport function envelope(bootstrap: BridgeBootstrap, message: Record<string, unknown>): ProtocolMessage {\n return { ...message, channel: bootstrap.channel, generation: bootstrap.generation } as ProtocolMessage;\n}\n","import { devOnly, warn } from './_dev.js';\nimport { buildDocumentFromOptions } from './_document.js';\nimport { normalizeSandboxOptions } from './_policy.js';\nimport {\n type BridgeBootstrap,\n createChannel,\n envelope,\n isProtocolMessage,\n MSG_CUSTOM,\n MSG_ERROR,\n MSG_HTML_REPLACE,\n MSG_READY,\n MSG_RESIZE,\n MSG_STATE_UPDATE_ALL,\n MSG_STYLE_PATCH,\n} from './_protocol.js';\nimport { SandboxTimeoutError } from './errors.js';\nimport type { SandboxHandle, SandboxMessage, SandboxOptions } from './types.js';\n\nexport function createSandbox<State extends object = Record<string, unknown>>(\n container: HTMLElement,\n options: SandboxOptions = {},\n): SandboxHandle<State> {\n const normalized = normalizeSandboxOptions(options);\n const channel = createChannel();\n const styles: Record<string, string> = { ...normalized.styles };\n const listeners = new Set<(message: SandboxMessage) => void>();\n const pendingState: Partial<State> = {};\n const controller = new AbortController();\n let iframe: HTMLIFrameElement | null = null;\n let disposed = false;\n let bridgeReady = false;\n let generation = 0;\n let readyTimeout: ReturnType<typeof setTimeout> | null = null;\n let currentRender: { reject: (error: unknown) => void; resolve: () => void } | null = null;\n\n function currentBootstrap(): BridgeBootstrap {\n return { channel, generation };\n }\n\n function clearReadyTimeout(): void {\n if (readyTimeout === null) return;\n\n clearTimeout(readyTimeout);\n readyTimeout = null;\n }\n\n function supersedePendingRender(): void {\n clearReadyTimeout();\n currentRender?.resolve();\n currentRender = null;\n bridgeReady = false;\n }\n\n function broadcast(message: SandboxMessage): void {\n for (const listener of listeners) {\n try {\n listener(message);\n } catch {\n warn('onMessage() handler threw: remaining handlers will still run.');\n }\n }\n }\n\n function handleMessage(event: MessageEvent): void {\n if (!iframe || event.source !== iframe.contentWindow) return;\n\n const bootstrap = currentBootstrap();\n\n if (!isProtocolMessage(event.data, bootstrap)) return;\n\n switch (event.data.type) {\n case MSG_CUSTOM:\n if (typeof event.data.event === 'string') {\n broadcast({ detail: event.data.detail, event: event.data.event, type: MSG_CUSTOM });\n }\n\n break;\n case MSG_ERROR:\n if (typeof event.data.message === 'string') {\n broadcast({\n message: event.data.message,\n ...(typeof event.data.stack === 'string' ? { stack: event.data.stack } : {}),\n type: MSG_ERROR,\n });\n }\n\n break;\n case MSG_READY:\n supersedePendingRender();\n bridgeReady = true;\n flushPendingState();\n break;\n case MSG_RESIZE:\n if (typeof event.data.height === 'number' && Number.isFinite(event.data.height) && event.data.height >= 0) {\n broadcast({ height: event.data.height, type: MSG_RESIZE });\n }\n\n break;\n }\n }\n\n function ensureIframe(): HTMLIFrameElement {\n if (iframe) return iframe;\n\n iframe = document.createElement('iframe');\n iframe.setAttribute('sandbox', 'allow-scripts');\n iframe.setAttribute('referrerpolicy', 'no-referrer');\n iframe.dataset.sandboxChannel = channel;\n container.appendChild(iframe);\n window.addEventListener('message', handleMessage);\n\n return iframe;\n }\n\n function dispose(): void {\n if (disposed) return;\n\n disposed = true;\n supersedePendingRender();\n controller.abort();\n\n if (iframe) {\n window.removeEventListener('message', handleMessage);\n iframe.remove();\n iframe = null;\n }\n\n listeners.clear();\n }\n\n function onMessage(handler: (message: SandboxMessage) => void): () => void {\n if (disposed) {\n warn('onMessage() called on a disposed sandbox: handler will never fire.');\n\n return () => {};\n }\n\n listeners.add(handler);\n\n return () => listeners.delete(handler);\n }\n\n function render(html: string): Promise<void> {\n if (disposed) {\n warn('render() called on a disposed sandbox.');\n\n return Promise.resolve();\n }\n\n if (!html.trim()) warn('render() called with empty HTML.');\n\n supersedePendingRender();\n generation += 1;\n\n const current = ensureIframe();\n const bootstrap = currentBootstrap();\n const documentOptions = { ...normalized, styles: { ...styles } };\n\n current.dataset.sandboxGeneration = String(generation);\n current.srcdoc = buildDocumentFromOptions(html, documentOptions, bootstrap);\n\n const promise = new Promise<void>((resolve, reject) => {\n currentRender = { reject, resolve };\n });\n\n readyTimeout = setTimeout(() => {\n if (!currentRender) return;\n\n currentRender.reject(\n new SandboxTimeoutError(\n `render() did not receive a 'ready' signal within ${normalized.readyTimeout}ms. ` +\n 'An injected script may have blocked document initialization.',\n ),\n );\n currentRender = null;\n readyTimeout = null;\n }, normalized.readyTimeout);\n\n return promise;\n }\n\n function send(message: Record<string, unknown>): void {\n iframe?.contentWindow?.postMessage(envelope(currentBootstrap(), message), '*');\n }\n\n function flushPendingState(): void {\n if (!bridgeReady || !iframe?.contentWindow || Object.keys(pendingState).length === 0) return;\n\n send({ record: { ...pendingState }, type: MSG_STATE_UPDATE_ALL });\n }\n\n function setState(update: Partial<State>): void {\n if (disposed) {\n warn('setState() called on a disposed sandbox.');\n\n return;\n }\n\n Object.assign(pendingState, update);\n\n if (bridgeReady && iframe?.contentWindow && Object.keys(update).length > 0) {\n send({ record: update, type: MSG_STATE_UPDATE_ALL });\n }\n }\n\n function replaceBody(html: string): void {\n if (disposed) {\n warn('replaceBody() called on a disposed sandbox.');\n\n return;\n }\n\n if (!bridgeReady || !iframe?.contentWindow) {\n warn('replaceBody() called before render() has resolved: bridge is not ready. Await render() first.');\n\n return;\n }\n\n send({ html, type: MSG_HTML_REPLACE });\n }\n\n function updateStyle(id: string, css: string): void {\n if (disposed) {\n warn('updateStyle() called on a disposed sandbox.');\n\n return;\n }\n\n devOnly(() => {\n if (!(id in styles)) {\n warn(`updateStyle('${id}', …): '${id}' is not a known styles key.`);\n }\n });\n\n styles[id] = css;\n\n if (bridgeReady && iframe?.contentWindow) send({ css, id, type: MSG_STYLE_PATCH });\n }\n\n return {\n get disposalSignal() {\n return controller.signal;\n },\n dispose,\n get disposed() {\n return disposed;\n },\n onMessage,\n render,\n replaceBody,\n setState,\n [Symbol.dispose]() {\n dispose();\n },\n updateStyle,\n };\n}\n"],"mappings":"A,sFCEA,SAAgB,kBAAkB,EAAoC,CAIpE,MAAO;;4BAHS,KAAK,UAAUC,EAAU,OAKf,EAAQ;+BAJf,OAAOA,EAAU,UAKP,EAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8DxC,KAAK,CACP,CCvEA,IAAa,aAAb,cAAkC,KAAM,CACtC,YAAY,EAAiB,EAAqB,CAChD,MAAMC,EAASC,CAAI,EACnB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAMa,oBAAb,cAAyC,YAAa,CAAC,EAG1C,0BAAb,cAA+C,YAAa,CAAC,ECEvDC,EAAK,2BACLC,EAAO,wCACPC,EAAQ,2BAEd,SAAS,KAAK,EAAe,EAAuB,CAClD,MAAM,IAAI,0BAA0B,GAAGC,EAAM,eAAe,OAAOC,CAAK,GAAG,CAC7E,CAEA,SAAS,gBAAgB,EAAgB,EAAuB,CAC9D,GAAI,OAAOA,GAAU,SAAU,OAAO,KAAKD,EAAOC,CAAK,EAEvD,IAAIC,EAEJ,GAAI,CACF,EAAM,IAAI,IAAID,CAAK,CACrB,MAAQ,CACN,OAAO,KAAKD,EAAOC,CAAK,CAC1B,CAaA,OAVGC,EAAI,WAAa,SAAWA,EAAI,WAAa,UAC9CA,EAAI,UACJA,EAAI,UACJA,EAAI,WAAa,KACjBA,EAAI,QACJA,EAAI,KAEG,KAAKF,EAAOC,CAAK,EAGnBC,EAAI,MACb,CAEA,SAAS,iBAAiB,EAA8B,EAAyB,CAI/E,OAHIC,IAAW,IAAA,GAAkB,CAAC,EAC7B,MAAM,QAAQA,CAAM,EAElBA,EAAO,IAAK,GAAU,gBAAgBF,EAAOD,CAAK,CAAC,EAFvB,KAAKA,EAAOG,CAAM,CAGvD,CAEA,SAAS,gBAAgB,EAAwB,CAC/C,GAAI,OAAOF,GAAU,SAAU,OAAO,KAAK,UAAWA,CAAK,EAE3D,IAAIC,EAEJ,GAAI,CACF,EAAM,IAAI,IAAID,CAAK,CACrB,MAAQ,CACN,OAAO,KAAK,UAAWA,CAAK,CAC9B,CAIA,OAFIC,EAAI,WAAa,SAAWA,EAAI,WAAa,SAAiB,KAAK,UAAWD,CAAK,EAEhFC,EAAI,IACb,CAEA,SAAS,gBAAgB,EAAoE,CAC3F,GAAIE,IAAW,IAAA,GAAW,MAAO,CAAC,EAClC,GAAI,OAAOA,GAAW,WAAYA,GAAmB,MAAM,QAAQA,CAAM,EAAG,OAAO,KAAK,SAAUA,CAAM,EAExG,IAAMC,EAAqC,CAAC,EAE5C,IAAK,GAAM,CAACC,EAAIC,KAAQ,OAAO,QAAQH,CAAM,GACvC,CAACP,EAAG,KAAKS,CAAE,GAAK,OAAOC,GAAQ,WAAU,KAAK,SAAUD,CAAE,EAE9D,EAAWA,GAAMC,EAGnB,OAAOF,CACT,CAEA,SAAgB,wBAAwB,EAA0B,CAAC,EAA6B,EAC1F,OAAOG,GAAY,WAAYA,GAAoB,MAAM,QAAQA,CAAO,IAAG,KAAK,UAAWA,CAAO,EAEtG,IAAMC,EAAOD,EAAQ,MAAQ,KAExBV,EAAK,KAAKW,CAAI,GAAG,KAAK,OAAQA,CAAI,EAEnCD,EAAQ,QAAU,IAAA,KAAc,CAACT,EAAM,KAAKS,EAAQ,KAAK,GAAKA,EAAQ,MAAM,SAAW,IACzF,KAAK,QAASA,EAAQ,KAAK,EAGzBA,EAAQ,QAAU,IAAA,IAAa,OAAOA,EAAQ,OAAU,UAAU,KAAK,QAASA,EAAQ,KAAK,EAEjG,IAAME,EAAkBF,EAAQ,aAC1BG,EAAeD,GAAA,IAGnBA,IAAoB,IAAA,KACnBA,IAAoB,MACnB,OAAOA,GAAoB,UAC3B,CAAC,OAAO,SAASA,CAAe,GAChCA,GAAmB,GACnBA,EAAkB,GAAK,GAAK,IAE9B,KAAK,eAAgBA,CAAe,EAGlCF,EAAQ,UAAY,IAAA,IAAa,CAAC,MAAM,QAAQA,EAAQ,OAAO,GAAG,KAAK,UAAWA,EAAQ,OAAO,EAErG,IAAMI,GAAWJ,EAAQ,SAAW,CAAC,EAAA,CAAG,IAAI,eAAe,EAE3D,MAAO,CACL,mBAAoB,iBAAiBA,EAAQ,mBAAoB,oBAAoB,EACrF,oBAAqB,iBAAiBA,EAAQ,oBAAqB,qBAAqB,EACxF,qBAAsB,iBAAiBA,EAAQ,qBAAsB,sBAAsB,EAC3F,oBAAqB,iBAAiBA,EAAQ,oBAAqB,qBAAqB,EACxF,OACA,MAAOA,EAAQ,MACf,eACA,UACA,OAAQ,gBAAgBA,EAAQ,MAAM,EACtC,MAAOA,EAAQ,OAAS,EAC1B,CACF,CAEA,SAAgB,oBAAoB,EAA2C,CAC7E,IAAMK,EAAgB,CACpB,kBACA,GAAIL,EAAQ,MAAQ,CAAC,UAAUA,EAAQ,MAAM,EAAE,EAAI,CAAC,EACpD,GAAGA,EAAQ,qBACX,GAAGA,EAAQ,QAAQ,IAAK,GAAW,IAAI,IAAIM,CAAM,CAAC,CAAC,MAAM,CAC3D,CAAC,CAAC,KAAK,GAAG,EACJC,EAAe,CAAC,kBAAmB,GAAGP,EAAQ,mBAAmB,CAAC,CAAC,KAAK,GAAG,EAC3EQ,EAAe,CAAC,QAAS,GAAGR,EAAQ,mBAAmB,CAAC,CAAC,KAAK,GAAG,EACjES,EAAcT,EAAQ,mBAAmB,KAAK,GAAG,GAAK,SAE5D,MAAO,CACL,qBACA,cAAcK,IACd,aAAaE,IACb,WAAWC,IACX,YAAYC,IACZ,qBACA,qBACA,iBACF,CAAC,CAAC,KAAK,IAAI,CACb,CCtJA,SAAS,UAAU,EAAqB,CACtC,OAAOV,EAAI,QAAQ,aAAc,aAAa,CAChD,CAEA,SAAS,WAAW,EAAuB,CACzC,OAAON,EAAM,QAAQ,KAAM,OAAO,CAAC,CAAC,QAAQ,KAAM,QAAQ,CAAC,CAAC,QAAQ,KAAM,MAAM,CAAC,CAAC,QAAQ,KAAM,MAAM,CACxG,CAEA,SAAS,WAAW,EAAuB,CACzC,OAAOA,EAAM,QAAQ,KAAM,OAAO,CAAC,CAAC,QAAQ,KAAM,MAAM,CAAC,CAAC,QAAQ,KAAM,MAAM,CAChF,CAEA,SAAgB,yBACd,EACA,EACA,EACQ,CACR,IAAMiB,EAAM,WAAW,oBAAoBV,CAAO,CAAC,EAC7CJ,EAAS,OAAO,QAAQI,EAAQ,MAAM,CAAC,CAC1C,KAAK,CAACF,EAAIC,KAAS,cAAc,WAAWD,CAAE,EAAE,IAAI,UAAUC,CAAG,EAAE,SAAS,CAAC,CAC7E,KAAK;CAAI,EACNK,EAAUJ,EAAQ,QACrB,IAAK,GAAQ,wCAAwC,WAAWW,CAAG,EAAE,aAAY,CAAC,CAClF,KAAK;CAAI,EACNC,EAAQZ,EAAQ,MAAQ,WAAW,WAAWA,EAAQ,KAAK,EAAE,GAAK,GAExE,MAAO;cACK,WAAWA,EAAQ,IAAI,EAAE;;sDAEeU,EAAI;;;SAGjD,WAAWV,EAAQ,KAAK,EAAE;EACjCJ,EAAO;SACAgB,EAAM;EACb,kBAAkB1B,CAAS,EAAE;;;;EAI7BkB,EAAQ;EACRS,EAAK;;QAGP,CChDA,IAAaC,EAAa,SACbC,EAAY,QACZC,EAAmB,eACnBC,EAAY,QACZC,EAAa,SACbC,EAAuB,mBACvBC,EAAkB,cAS/B,SAAgB,kBAAkB,EAAgB,EAAsD,CACtG,OACE,OAAO3B,GAAU,YACjBA,GACA,OAAQA,EAAgC,SAAY,UACnDA,EAA8B,UAAYP,EAAU,SACrD,OAAQO,EAAmC,YAAe,UACzDA,EAAiC,aAAeP,EAAU,YAC3D,OAAQO,EAA6B,MAAS,QAElD,CAEA,SAAgB,eAAwB,CACtC,GAAI,OAAO,OAAO,YAAe,WAAY,OAAO,OAAO,WAAW,EAEtE,IAAM4B,EAAQ,OAAO,gBAAgB,IAAI,YAAY,CAAC,CAAC,EAEvD,OAAO,MAAM,KAAKA,EAAQ,GAASC,EAAK,SAAS,EAAE,CAAC,CAAC,SAAS,EAAG,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,CAChF,CAEA,SAAgB,SAAS,EAA4B,EAAmD,CACtG,MAAO,CAAE,GAAGnC,EAAS,QAASD,EAAU,QAAS,WAAYA,EAAU,UAAW,CACpF,CClBA,SAAgB,cACd,EACA,EAA0B,CAAC,EACL,CACtB,IAAMW,EAAa,wBAAwBG,CAAO,EAC5CuB,EAAU,cAAc,EACxB3B,EAAiC,CAAE,GAAGC,EAAW,MAAO,EACxD2B,EAAY,IAAI,IAChBC,EAA+B,CAAC,EAChCC,EAAa,IAAI,gBACnBC,EAAmC,KACnCC,EAAW,GACXC,EAAc,GACdC,EAAa,EACb3B,EAAqD,KACrD4B,EAAkF,KAEtF,SAAS,kBAAoC,CAC3C,MAAO,CAAE,UAAS,YAAW,CAC/B,CAEA,SAAS,mBAA0B,CAC7B5B,IAAiB,OAErB,aAAaA,CAAY,EACzB,EAAe,KACjB,CAEA,SAAS,wBAA+B,CACtC,kBAAkB,EAClB4B,GAAe,QAAQ,EACvB,EAAgB,KAChB,EAAc,EAChB,CAEA,SAAS,UAAU,EAA+B,CAChD,IAAK,IAAMC,KAAYR,EACrB,GAAI,CACFQ,EAAS7C,CAAO,CAClB,MAAQ,CAER,CAEJ,CAEA,SAAS,cAAc,EAA2B,CAChD,GAAI,CAACwC,GAAUM,EAAM,SAAWN,EAAO,cAAe,OAEtD,IAAMzC,EAAY,iBAAiB,EAEnC,GAAK,kBAAkB+C,EAAM,KAAM/C,CAAS,EAE5C,OAAQ+C,EAAM,KAAK,KAAnB,CACE,KAAKnB,EACC,OAAOmB,EAAM,KAAK,OAAU,UAC9B,UAAU,CAAE,OAAQA,EAAM,KAAK,OAAQ,MAAOA,EAAM,KAAK,MAAO,KAAMnB,CAAW,CAAC,EAGpF,MACF,KAAKC,EACC,OAAOkB,EAAM,KAAK,SAAY,UAChC,UAAU,CACR,QAASA,EAAM,KAAK,QACpB,GAAI,OAAOA,EAAM,KAAK,OAAU,SAAW,CAAE,MAAOA,EAAM,KAAK,KAAM,EAAI,CAAC,EAC1E,KAAMlB,CACR,CAAC,EAGH,MACF,KAAKE,EACH,uBAAuB,EACvB,EAAc,GACd,kBAAkB,EAClB,MACF,KAAKC,EACC,OAAOe,EAAM,KAAK,QAAW,UAAY,OAAO,SAASA,EAAM,KAAK,MAAM,GAAKA,EAAM,KAAK,QAAU,GACtG,UAAU,CAAE,OAAQA,EAAM,KAAK,OAAQ,KAAMf,CAAW,CAAC,CAI/D,CACF,CAEA,SAAS,cAAkC,CAUzC,OATIS,IAEJ,EAAS,SAAS,cAAc,QAAQ,EACxCA,EAAO,aAAa,UAAW,eAAe,EAC9CA,EAAO,aAAa,iBAAkB,aAAa,EACnD,EAAO,QAAQ,eAAiBJ,EAChCW,EAAU,YAAYP,CAAM,EAC5B,OAAO,iBAAiB,UAAW,aAAa,EAEzCA,EACT,CAEA,SAAS,SAAgB,CACnBC,IAEJ,EAAW,GACX,uBAAuB,EACvBF,EAAW,MAAM,EAEjB,AAGEC,KAFA,OAAO,oBAAoB,UAAW,aAAa,EACnDA,EAAO,OAAO,EACL,MAGXH,EAAU,MAAM,EAClB,CAEA,SAAS,UAAU,EAAwD,CASzE,OARII,MAGW,CAAC,GAGhBJ,EAAU,IAAIW,CAAO,MAERX,EAAU,OAAOW,CAAO,EACvC,CAEA,SAAS,OAAO,EAA6B,CAC3C,GAAIP,EAGF,OAAO,QAAQ,QAAQ,EAGpBf,EAAK,KAAK,EAEf,uBAAuB,EACvB,GAAc,EAEd,IAAMuB,EAAU,aAAa,EACvBlD,EAAY,iBAAiB,EAC7BmD,EAAkB,CAAE,GAAGxC,EAAY,OAAQ,CAAE,GAAGD,CAAO,CAAE,EAE/D,EAAQ,QAAQ,kBAAoB,OAAOkC,CAAU,EACrD,EAAQ,OAAS,yBAAyBjB,EAAMwB,EAAiBnD,CAAS,EAE1E,IAAMoD,EAAU,IAAI,SAAe,EAAS,IAAW,CACrD,EAAgB,CAAE,SAAQ,SAAQ,CACpC,CAAC,EAeD,MAbA,GAAe,eAAiB,CACzBP,IAELA,EAAc,OACZ,IAAI,oBACF,oDAAoDlC,EAAW,aAAa,iEAE9E,CACF,EACA,EAAgB,KAChB,EAAe,KACjB,EAAGA,EAAW,YAAY,EAEnByC,CACT,CAEA,SAAS,KAAK,EAAwC,CACpDX,GAAQ,eAAe,YAAY,SAAS,iBAAiB,EAAGxC,CAAO,EAAG,GAAG,CAC/E,CAEA,SAAS,mBAA0B,CAC5B0C,GAAgBF,GAAQ,eAAiB,OAAO,KAAKF,CAAY,CAAC,CAAC,SAAW,GAEnF,KAAK,CAAE,OAAQ,CAAE,GAAGA,CAAa,EAAG,KAAMN,CAAqB,CAAC,CAClE,CAEA,SAAS,SAAS,EAA8B,CAC1CS,IAMJ,OAAO,OAAOH,EAAcc,CAAM,EAE9BV,GAAeF,GAAQ,eAAiB,OAAO,KAAKY,CAAM,CAAC,CAAC,OAAS,GACvE,KAAK,CAAE,OAAQA,EAAQ,KAAMpB,CAAqB,CAAC,EAEvD,CAEA,SAAS,YAAY,EAAoB,CACnCS,GAMCC,GAAgBF,GAAQ,eAM7B,KAAK,CAAE,OAAM,KAAMX,CAAiB,CAAC,CACvC,CAEA,SAAS,YAAY,EAAY,EAAmB,CAC9CY,IAYJ,EAAO9B,GAAMC,EAET8B,GAAeF,GAAQ,eAAe,KAAK,CAAE,MAAK,KAAI,KAAMP,CAAgB,CAAC,EACnF,CAEA,MAAO,CACL,IAAI,gBAAiB,CACnB,OAAOM,EAAW,MACpB,EACA,QACA,IAAI,UAAW,CACb,OAAOE,CACT,EACA,UACA,OACA,YACA,SACA,CAAC,OAAO,UAAW,CACjB,QAAQ,CACV,EACA,WACF,CACF,C"}
1
+ {"version":3,"file":"sandbox.iife.js","names":["exports","bootstrap","message","opts","ID","LANG","NONCE","field","value","url","values","styles","normalized","id","css","options","lang","rawReadyTimeout","readyTimeout","scripts","scriptOrigins","script","styleOrigins","imageOrigins","fontOrigins","csp","src","nonce","html","MSG_CUSTOM","MSG_ERROR","MSG_HTML_REPLACE","MSG_READY","MSG_RESIZE","MSG_STATE_UPDATE_ALL","MSG_STYLE_PATCH","bytes","byte","channel","listeners","pendingState","controller","iframe","disposed","bridgeReady","generation","currentRender","listener","event","container","handler","current","documentOptions","promise","update"],"sources":["../src/_dev.ts","../src/_bridge.ts","../src/errors.ts","../src/_policy.ts","../src/_document.ts","../src/_protocol.ts","../src/_runtime.ts"],"sourcesContent":["const isDev = !(globalThis as { __SANDBOX_PROD__?: boolean }).__SANDBOX_PROD__;\n\n/** @internal @security: some callers interpolate user-supplied values (e.g. styles key) into msg; do not log PII. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/sandbox] ${msg}`);\n}\n\n/** @internal: Run fn only in dev builds. Use when dev-only logic goes beyond a single warn() / error() call. */\nexport function devOnly(fn: () => void): void {\n if (isDev) fn();\n}\n","import type { BridgeBootstrap } from './_protocol.js';\n\nexport function buildBridgeScript(bootstrap: BridgeBootstrap): string {\n const channel = JSON.stringify(bootstrap.channel);\n const generation = String(bootstrap.generation);\n\n return `\n(function() {\nwindow.__sandboxChannel = ${channel};\nwindow.__sandboxGeneration = ${generation};\nconst __sandboxChannel = window.__sandboxChannel;\nconst __sandboxGeneration = window.__sandboxGeneration;\nif ('ontouchstart' in window) document.addEventListener('touchstart', function() {}, { passive: true });\nwindow.addEventListener('message', function(e) {\n var msg = e.data;\n if (e.source !== parent || !msg || msg.channel !== __sandboxChannel || msg.generation !== __sandboxGeneration) return;\n if (msg.type === 'state-update-all' && msg.record && typeof msg.record === 'object') {\n var keys = Object.keys(msg.record);\n for (var i = 0; i < keys.length; i++) {\n var k = keys[i];\n document.dispatchEvent(new CustomEvent('sandbox:state-update', { detail: { key: k, value: msg.record[k] } }));\n }\n }\n if (msg.type === 'style-patch' && msg.id && typeof msg.css === 'string') {\n var el = document.getElementById(msg.id);\n if (el && el.tagName === 'STYLE') el.textContent = msg.css;\n }\n if (msg.type === 'html-replace' && typeof msg.html === 'string') {\n document.body.innerHTML = msg.html;\n }\n});\nfunction post(msg) {\n msg.channel = __sandboxChannel;\n msg.generation = __sandboxGeneration;\n parent.postMessage(msg, '*');\n}\nwindow.onerror = function(message, _src, _line, _col, err) {\n post({ type: 'error', message: String(message), stack: err && err.stack });\n return true;\n};\nwindow.addEventListener('unhandledrejection', function(e) {\n var reason = e.reason;\n post({ type: 'error', message: String(reason), stack: reason && reason.stack });\n});\nwindow.__sandbox__ = {\n emit: function(event, detail) {\n post({ type: 'custom', event: String(event), detail: detail });\n },\n onState: function(key, handler) {\n function listener(e) {\n if (e.detail && e.detail.key === key) handler(e.detail.value);\n }\n document.addEventListener('sandbox:state-update', listener);\n return function() {\n document.removeEventListener('sandbox:state-update', listener);\n };\n }\n};\nwindow.addEventListener('DOMContentLoaded', function() {\n post({ type: 'ready' });\n if (typeof ResizeObserver === 'undefined') return;\n new ResizeObserver(function(entries) {\n var entry = entries[0];\n if (!entry) return;\n var h = entry.borderBoxSize && entry.borderBoxSize[0]\n ? entry.borderBoxSize[0].blockSize\n : entry.contentRect.height;\n post({ type: 'resize', height: h });\n }).observe(document.body);\n});\n})();\n`.trim();\n}\n","/** Base class for all sandbox errors. */\nexport class SandboxError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Thrown when render() does not receive a 'ready' signal from the sandbox document\n * within the timeout window. An injected script may have blocked document initialization.\n */\nexport class SandboxTimeoutError extends SandboxError {}\n\n/** Thrown when Sandbox configuration cannot produce a valid document or CSP policy. */\nexport class SandboxConfigurationError extends SandboxError {}\n","import { SandboxConfigurationError } from './errors.js';\nimport type { SandboxOptions } from './types.js';\n\nexport interface NormalizedSandboxOptions {\n allowedFontOrigins: string[];\n allowedImageOrigins: string[];\n allowedScriptOrigins: string[];\n allowedStyleOrigins: string[];\n lang: string;\n nonce: string | undefined;\n readyTimeout: number;\n scripts: string[];\n styles: Record<string, string>;\n title: string;\n}\n\nexport const DEFAULT_READY_TIMEOUT_MS = 5000;\n\nconst ID = /^[A-Za-z][A-Za-z0-9_-]*$/;\nconst LANG = /^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/;\nconst NONCE = /^[A-Za-z0-9+/_-]+={0,2}$/;\n\nfunction fail(field: string, value: unknown): never {\n throw new SandboxConfigurationError(`${field} is invalid: ${String(value)}`);\n}\n\nfunction normalizeOrigin(value: unknown, field: string): string {\n if (typeof value !== 'string') return fail(field, value);\n\n let url: URL;\n\n try {\n url = new URL(value);\n } catch {\n return fail(field, value);\n }\n\n if (\n (url.protocol !== 'http:' && url.protocol !== 'https:') ||\n url.username ||\n url.password ||\n url.pathname !== '/' ||\n url.search ||\n url.hash\n ) {\n return fail(field, value);\n }\n\n return url.origin;\n}\n\nfunction normalizeOrigins(values: string[] | undefined, field: string): string[] {\n if (values === undefined) return [];\n if (!Array.isArray(values)) return fail(field, values);\n\n return values.map((value) => normalizeOrigin(value, field));\n}\n\nfunction normalizeScript(value: unknown): string {\n if (typeof value !== 'string') return fail('scripts', value);\n\n let url: URL;\n\n try {\n url = new URL(value);\n } catch {\n return fail('scripts', value);\n }\n\n if (url.protocol !== 'http:' && url.protocol !== 'https:') return fail('scripts', value);\n\n return url.href;\n}\n\nfunction normalizeStyles(styles: Record<string, string> | undefined): Record<string, string> {\n if (styles === undefined) return {};\n if (typeof styles !== 'object' || styles === null || Array.isArray(styles)) return fail('styles', styles);\n\n const normalized: Record<string, string> = {};\n\n for (const [id, css] of Object.entries(styles)) {\n if (!ID.test(id) || typeof css !== 'string') fail('styles', id);\n\n normalized[id] = css;\n }\n\n return normalized;\n}\n\nexport function normalizeSandboxOptions(options: SandboxOptions = {}): NormalizedSandboxOptions {\n if (typeof options !== 'object' || options === null || Array.isArray(options)) fail('options', options);\n\n const lang = options.lang ?? 'en';\n\n if (!LANG.test(lang)) fail('lang', lang);\n\n if (options.nonce !== undefined && (!NONCE.test(options.nonce) || options.nonce.length === 0)) {\n fail('nonce', options.nonce);\n }\n\n if (options.title !== undefined && typeof options.title !== 'string') fail('title', options.title);\n\n const rawReadyTimeout = options.readyTimeout;\n const readyTimeout = rawReadyTimeout ?? DEFAULT_READY_TIMEOUT_MS;\n\n if (\n rawReadyTimeout !== undefined &&\n (rawReadyTimeout === null ||\n typeof rawReadyTimeout !== 'number' ||\n !Number.isFinite(rawReadyTimeout) ||\n rawReadyTimeout <= 0 ||\n rawReadyTimeout > 2 ** 31 - 1)\n ) {\n fail('readyTimeout', rawReadyTimeout);\n }\n\n if (options.scripts !== undefined && !Array.isArray(options.scripts)) fail('scripts', options.scripts);\n\n const scripts = (options.scripts ?? []).map(normalizeScript);\n\n return {\n allowedFontOrigins: normalizeOrigins(options.allowedFontOrigins, 'allowedFontOrigins'),\n allowedImageOrigins: normalizeOrigins(options.allowedImageOrigins, 'allowedImageOrigins'),\n allowedScriptOrigins: normalizeOrigins(options.allowedScriptOrigins, 'allowedScriptOrigins'),\n allowedStyleOrigins: normalizeOrigins(options.allowedStyleOrigins, 'allowedStyleOrigins'),\n lang,\n nonce: options.nonce,\n readyTimeout,\n scripts,\n styles: normalizeStyles(options.styles),\n title: options.title ?? '',\n };\n}\n\nexport function buildCspFromOptions(options: NormalizedSandboxOptions): string {\n const scriptOrigins = [\n \"'unsafe-inline'\",\n ...(options.nonce ? [`'nonce-${options.nonce}'`] : []),\n ...options.allowedScriptOrigins,\n ...options.scripts.map((script) => new URL(script).origin),\n ].join(' ');\n const styleOrigins = [\"'unsafe-inline'\", ...options.allowedStyleOrigins].join(' ');\n const imageOrigins = ['data:', ...options.allowedImageOrigins].join(' ');\n const fontOrigins = options.allowedFontOrigins.join(' ') || \"'none'\";\n\n return [\n \"default-src 'none'\",\n `script-src ${scriptOrigins}`,\n `style-src ${styleOrigins}`,\n `img-src ${imageOrigins}`,\n `font-src ${fontOrigins}`,\n \"connect-src 'none'\",\n \"form-action 'none'\",\n \"base-uri 'none'\",\n ].join('; ');\n}\n\nexport function buildCsp(options: SandboxOptions = {}): string {\n return buildCspFromOptions(normalizeSandboxOptions(options));\n}\n","import { buildBridgeScript } from './_bridge.js';\nimport { buildCspFromOptions, type NormalizedSandboxOptions, normalizeSandboxOptions } from './_policy.js';\nimport type { BridgeBootstrap } from './_protocol.js';\nimport type { SandboxOptions } from './types.js';\n\nfunction escapeCss(css: string): string {\n return css.replace(/<\\/style/gi, '\\\\3C /style');\n}\n\nfunction escapeAttr(value: string): string {\n return value.replace(/&/g, '&amp;').replace(/\"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n}\n\nfunction escapeText(value: string): string {\n return value.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n}\n\nexport function buildDocumentFromOptions(\n html: string,\n options: NormalizedSandboxOptions,\n bootstrap: BridgeBootstrap,\n): string {\n const csp = escapeAttr(buildCspFromOptions(options));\n const styles = Object.entries(options.styles)\n .map(([id, css]) => `<style id=\"${escapeAttr(id)}\">${escapeCss(css)}</style>`)\n .join('\\n');\n const scripts = options.scripts\n .map((src) => `<script crossorigin=\"anonymous\" src=\"${escapeAttr(src)}\"></script>`)\n .join('\\n');\n const nonce = options.nonce ? ` nonce=\"${escapeAttr(options.nonce)}\"` : '';\n\n return `<!doctype html>\n<html lang=\"${escapeAttr(options.lang)}\">\n<head>\n<meta http-equiv=\"Content-Security-Policy\" content=\"${csp}\">\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>${escapeText(options.title)}</title>\n${styles}\n<script${nonce}>\n${buildBridgeScript(bootstrap)}\n</script>\n</head>\n<body>\n${scripts}\n${html}\n</body>\n</html>`;\n}\n\nexport function buildDocument(html: string, options: SandboxOptions = {}): string {\n return buildDocumentFromOptions(html, normalizeSandboxOptions(options), { channel: '', generation: 0 });\n}\n","export const MSG_CUSTOM = 'custom';\nexport const MSG_ERROR = 'error';\nexport const MSG_HTML_REPLACE = 'html-replace';\nexport const MSG_READY = 'ready';\nexport const MSG_RESIZE = 'resize';\nexport const MSG_STATE_UPDATE_ALL = 'state-update-all';\nexport const MSG_STYLE_PATCH = 'style-patch';\n\nexport interface BridgeBootstrap {\n channel: string;\n generation: number;\n}\n\nexport type ProtocolMessage = Record<string, unknown> & { channel: string; generation: number; type: string };\n\nexport function isProtocolMessage(value: unknown, bootstrap: BridgeBootstrap): value is ProtocolMessage {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as { channel?: unknown }).channel === 'string' &&\n (value as { channel: string }).channel === bootstrap.channel &&\n typeof (value as { generation?: unknown }).generation === 'number' &&\n (value as { generation: number }).generation === bootstrap.generation &&\n typeof (value as { type?: unknown }).type === 'string'\n );\n}\n\nexport function createChannel(): string {\n if (typeof crypto.randomUUID === 'function') return crypto.randomUUID();\n\n const bytes = crypto.getRandomValues(new Uint32Array(4));\n\n return Array.from(bytes, (byte) => byte.toString(16).padStart(8, '0')).join('');\n}\n\nexport function envelope(bootstrap: BridgeBootstrap, message: Record<string, unknown>): ProtocolMessage {\n return { ...message, channel: bootstrap.channel, generation: bootstrap.generation } as ProtocolMessage;\n}\n","import { devOnly, warn } from './_dev.js';\nimport { buildDocumentFromOptions } from './_document.js';\nimport { normalizeSandboxOptions } from './_policy.js';\nimport {\n type BridgeBootstrap,\n createChannel,\n envelope,\n isProtocolMessage,\n MSG_CUSTOM,\n MSG_ERROR,\n MSG_HTML_REPLACE,\n MSG_READY,\n MSG_RESIZE,\n MSG_STATE_UPDATE_ALL,\n MSG_STYLE_PATCH,\n} from './_protocol.js';\nimport { SandboxTimeoutError } from './errors.js';\nimport type { SandboxHandle, SandboxMessage, SandboxOptions } from './types.js';\n\nexport function createSandbox<State extends object = Record<string, unknown>>(\n container: HTMLElement,\n options: SandboxOptions = {},\n): SandboxHandle<State> {\n const normalized = normalizeSandboxOptions(options);\n const channel = createChannel();\n const styles: Record<string, string> = { ...normalized.styles };\n const listeners = new Set<(message: SandboxMessage) => void>();\n const pendingState: Partial<State> = {};\n const controller = new AbortController();\n let iframe: HTMLIFrameElement | null = null;\n let disposed = false;\n let bridgeReady = false;\n let generation = 0;\n let readyTimeout: ReturnType<typeof setTimeout> | null = null;\n let currentRender: { reject: (error: unknown) => void; resolve: () => void } | null = null;\n\n function currentBootstrap(): BridgeBootstrap {\n return { channel, generation };\n }\n\n function clearReadyTimeout(): void {\n if (readyTimeout === null) return;\n\n clearTimeout(readyTimeout);\n readyTimeout = null;\n }\n\n function supersedePendingRender(): void {\n clearReadyTimeout();\n currentRender?.resolve();\n currentRender = null;\n bridgeReady = false;\n }\n\n function broadcast(message: SandboxMessage): void {\n for (const listener of listeners) {\n try {\n listener(message);\n } catch {\n warn('onMessage() handler threw: remaining handlers will still run.');\n }\n }\n }\n\n function handleMessage(event: MessageEvent): void {\n if (!iframe || event.source !== iframe.contentWindow) return;\n\n const bootstrap = currentBootstrap();\n\n if (!isProtocolMessage(event.data, bootstrap)) return;\n\n switch (event.data.type) {\n case MSG_CUSTOM:\n if (typeof event.data.event === 'string') {\n broadcast({ detail: event.data.detail, event: event.data.event, type: MSG_CUSTOM });\n }\n\n break;\n case MSG_ERROR:\n if (typeof event.data.message === 'string') {\n broadcast({\n message: event.data.message,\n ...(typeof event.data.stack === 'string' ? { stack: event.data.stack } : {}),\n type: MSG_ERROR,\n });\n }\n\n break;\n case MSG_READY:\n supersedePendingRender();\n bridgeReady = true;\n flushPendingState();\n break;\n case MSG_RESIZE:\n if (typeof event.data.height === 'number' && Number.isFinite(event.data.height) && event.data.height >= 0) {\n broadcast({ height: event.data.height, type: MSG_RESIZE });\n }\n\n break;\n }\n }\n\n function ensureIframe(): HTMLIFrameElement {\n if (iframe) return iframe;\n\n iframe = document.createElement('iframe');\n iframe.setAttribute('sandbox', 'allow-scripts');\n iframe.setAttribute('referrerpolicy', 'no-referrer');\n iframe.dataset.sandboxChannel = channel;\n container.appendChild(iframe);\n window.addEventListener('message', handleMessage);\n\n return iframe;\n }\n\n function dispose(): void {\n if (disposed) return;\n\n disposed = true;\n supersedePendingRender();\n controller.abort();\n\n if (iframe) {\n window.removeEventListener('message', handleMessage);\n iframe.remove();\n iframe = null;\n }\n\n listeners.clear();\n }\n\n function onMessage(handler: (message: SandboxMessage) => void): () => void {\n if (disposed) {\n warn('onMessage() called on a disposed sandbox: handler will never fire.');\n\n return () => {};\n }\n\n listeners.add(handler);\n\n return () => listeners.delete(handler);\n }\n\n function render(html: string): Promise<void> {\n if (disposed) {\n warn('render() called on a disposed sandbox.');\n\n return Promise.resolve();\n }\n\n if (!html.trim()) warn('render() called with empty HTML.');\n\n supersedePendingRender();\n generation += 1;\n\n const current = ensureIframe();\n const bootstrap = currentBootstrap();\n const documentOptions = { ...normalized, styles: { ...styles } };\n\n current.dataset.sandboxGeneration = String(generation);\n current.srcdoc = buildDocumentFromOptions(html, documentOptions, bootstrap);\n\n const promise = new Promise<void>((resolve, reject) => {\n currentRender = { reject, resolve };\n });\n\n readyTimeout = setTimeout(() => {\n if (!currentRender) return;\n\n currentRender.reject(\n new SandboxTimeoutError(\n `render() did not receive a 'ready' signal within ${normalized.readyTimeout}ms. ` +\n 'An injected script may have blocked document initialization.',\n ),\n );\n currentRender = null;\n readyTimeout = null;\n }, normalized.readyTimeout);\n\n return promise;\n }\n\n function send(message: Record<string, unknown>): void {\n iframe?.contentWindow?.postMessage(envelope(currentBootstrap(), message), '*');\n }\n\n function flushPendingState(): void {\n if (!bridgeReady || !iframe?.contentWindow || Object.keys(pendingState).length === 0) return;\n\n send({ record: { ...pendingState }, type: MSG_STATE_UPDATE_ALL });\n }\n\n function setState(update: Partial<State>): void {\n if (disposed) {\n warn('setState() called on a disposed sandbox.');\n\n return;\n }\n\n Object.assign(pendingState, update);\n\n if (bridgeReady && iframe?.contentWindow && Object.keys(update).length > 0) {\n send({ record: update, type: MSG_STATE_UPDATE_ALL });\n }\n }\n\n function replaceBody(html: string): void {\n if (disposed) {\n warn('replaceBody() called on a disposed sandbox.');\n\n return;\n }\n\n if (!bridgeReady || !iframe?.contentWindow) {\n warn('replaceBody() called before render() has resolved: bridge is not ready. Await render() first.');\n\n return;\n }\n\n send({ html, type: MSG_HTML_REPLACE });\n }\n\n function updateStyle(id: string, css: string): void {\n if (disposed) {\n warn('updateStyle() called on a disposed sandbox.');\n\n return;\n }\n\n devOnly(() => {\n if (!(id in styles)) {\n warn(`updateStyle('${id}', …): '${id}' is not a known styles key.`);\n }\n });\n\n styles[id] = css;\n\n if (bridgeReady && iframe?.contentWindow) send({ css, id, type: MSG_STYLE_PATCH });\n }\n\n return {\n get disposalSignal() {\n return controller.signal;\n },\n dispose,\n get disposed() {\n return disposed;\n },\n onMessage,\n render,\n replaceBody,\n setState,\n [Symbol.dispose]() {\n dispose();\n },\n updateStyle,\n };\n}\n"],"mappings":"A,sFCEA,SAAgB,kBAAkB,EAAoC,CAIpE,MAAO;;4BAHS,KAAK,UAAUC,EAAU,OAKf,EAAQ;+BAJf,OAAOA,EAAU,UAKP,EAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8DxC,KAAK,CACP,CCvEA,IAAa,aAAb,cAAkC,KAAM,CACtC,YAAY,EAAiB,EAAqB,CAChD,MAAMC,EAASC,CAAI,EACnB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAMa,oBAAb,cAAyC,YAAa,CAAC,EAG1C,0BAAb,cAA+C,YAAa,CAAC,ECEvDC,EAAK,2BACLC,EAAO,wCACPC,EAAQ,2BAEd,SAAS,KAAK,EAAe,EAAuB,CAClD,MAAM,IAAI,0BAA0B,GAAGC,EAAM,eAAe,OAAOC,CAAK,GAAG,CAC7E,CAEA,SAAS,gBAAgB,EAAgB,EAAuB,CAC9D,GAAI,OAAOA,GAAU,SAAU,OAAO,KAAKD,EAAOC,CAAK,EAEvD,IAAIC,EAEJ,GAAI,CACF,EAAM,IAAI,IAAID,CAAK,CACrB,MAAQ,CACN,OAAO,KAAKD,EAAOC,CAAK,CAC1B,CAaA,OAVGC,EAAI,WAAa,SAAWA,EAAI,WAAa,UAC9CA,EAAI,UACJA,EAAI,UACJA,EAAI,WAAa,KACjBA,EAAI,QACJA,EAAI,KAEG,KAAKF,EAAOC,CAAK,EAGnBC,EAAI,MACb,CAEA,SAAS,iBAAiB,EAA8B,EAAyB,CAI/E,OAHIC,IAAW,IAAA,GAAkB,CAAC,EAC7B,MAAM,QAAQA,CAAM,EAElBA,EAAO,IAAK,GAAU,gBAAgBF,EAAOD,CAAK,CAAC,EAFvB,KAAKA,EAAOG,CAAM,CAGvD,CAEA,SAAS,gBAAgB,EAAwB,CAC/C,GAAI,OAAOF,GAAU,SAAU,OAAO,KAAK,UAAWA,CAAK,EAE3D,IAAIC,EAEJ,GAAI,CACF,EAAM,IAAI,IAAID,CAAK,CACrB,MAAQ,CACN,OAAO,KAAK,UAAWA,CAAK,CAC9B,CAIA,OAFIC,EAAI,WAAa,SAAWA,EAAI,WAAa,SAAiB,KAAK,UAAWD,CAAK,EAEhFC,EAAI,IACb,CAEA,SAAS,gBAAgB,EAAoE,CAC3F,GAAIE,IAAW,IAAA,GAAW,MAAO,CAAC,EAClC,GAAI,OAAOA,GAAW,WAAYA,GAAmB,MAAM,QAAQA,CAAM,EAAG,OAAO,KAAK,SAAUA,CAAM,EAExG,IAAMC,EAAqC,CAAC,EAE5C,IAAK,GAAM,CAACC,EAAIC,KAAQ,OAAO,QAAQH,CAAM,EACtCP,EAAG,KAAKS,CAAE,GAAK,OAAOC,GAAQ,UAAU,KAAK,SAAUD,CAAE,EAE9D,EAAWA,GAAMC,EAGnB,OAAOF,CACT,CAEA,SAAgB,wBAAwB,EAA0B,CAAC,EAA6B,EAC1F,OAAOG,GAAY,WAAYA,GAAoB,MAAM,QAAQA,CAAO,IAAG,KAAK,UAAWA,CAAO,EAEtG,IAAMC,EAAOD,EAAQ,MAAQ,KAExBV,EAAK,KAAKW,CAAI,GAAG,KAAK,OAAQA,CAAI,EAEnCD,EAAQ,QAAU,IAAA,IAAeT,EAAM,KAAKS,EAAQ,KAAK,GAAKA,EAAQ,MAAM,SAAW,GACzF,KAAK,QAASA,EAAQ,KAAK,EAGzBA,EAAQ,QAAU,IAAA,IAAa,OAAOA,EAAQ,OAAU,UAAU,KAAK,QAASA,EAAQ,KAAK,EAEjG,IAAME,EAAkBF,EAAQ,aAC1BG,EAAeD,GAAA,IAGnBA,IAAoB,IAAA,KACnBA,IAAoB,MACnB,OAAOA,GAAoB,UAC3B,CAAC,OAAO,SAASA,CAAe,GAChCA,GAAmB,GACnBA,EAAkB,GAAK,GAAK,IAE9B,KAAK,eAAgBA,CAAe,EAGlCF,EAAQ,UAAY,IAAA,IAAa,CAAC,MAAM,QAAQA,EAAQ,OAAO,GAAG,KAAK,UAAWA,EAAQ,OAAO,EAErG,IAAMI,GAAWJ,EAAQ,SAAW,CAAC,EAAA,CAAG,IAAI,eAAe,EAE3D,MAAO,CACL,mBAAoB,iBAAiBA,EAAQ,mBAAoB,oBAAoB,EACrF,oBAAqB,iBAAiBA,EAAQ,oBAAqB,qBAAqB,EACxF,qBAAsB,iBAAiBA,EAAQ,qBAAsB,sBAAsB,EAC3F,oBAAqB,iBAAiBA,EAAQ,oBAAqB,qBAAqB,EACxF,OACA,MAAOA,EAAQ,MACf,eACA,UACA,OAAQ,gBAAgBA,EAAQ,MAAM,EACtC,MAAOA,EAAQ,OAAS,EAC1B,CACF,CAEA,SAAgB,oBAAoB,EAA2C,CAC7E,IAAMK,EAAgB,CACpB,kBACA,GAAIL,EAAQ,MAAQ,CAAC,UAAUA,EAAQ,MAAM,EAAE,EAAI,CAAC,EACpD,GAAGA,EAAQ,qBACX,GAAGA,EAAQ,QAAQ,IAAK,GAAW,IAAI,IAAIM,CAAM,CAAC,CAAC,MAAM,CAC3D,CAAC,CAAC,KAAK,GAAG,EACJC,EAAe,CAAC,kBAAmB,GAAGP,EAAQ,mBAAmB,CAAC,CAAC,KAAK,GAAG,EAC3EQ,EAAe,CAAC,QAAS,GAAGR,EAAQ,mBAAmB,CAAC,CAAC,KAAK,GAAG,EACjES,EAAcT,EAAQ,mBAAmB,KAAK,GAAG,GAAK,SAE5D,MAAO,CACL,qBACA,cAAcK,IACd,aAAaE,IACb,WAAWC,IACX,YAAYC,IACZ,qBACA,qBACA,iBACF,CAAC,CAAC,KAAK,IAAI,CACb,CCtJA,SAAS,UAAU,EAAqB,CACtC,OAAOV,EAAI,QAAQ,aAAc,aAAa,CAChD,CAEA,SAAS,WAAW,EAAuB,CACzC,OAAON,EAAM,QAAQ,KAAM,OAAO,CAAC,CAAC,QAAQ,KAAM,QAAQ,CAAC,CAAC,QAAQ,KAAM,MAAM,CAAC,CAAC,QAAQ,KAAM,MAAM,CACxG,CAEA,SAAS,WAAW,EAAuB,CACzC,OAAOA,EAAM,QAAQ,KAAM,OAAO,CAAC,CAAC,QAAQ,KAAM,MAAM,CAAC,CAAC,QAAQ,KAAM,MAAM,CAChF,CAEA,SAAgB,yBACd,EACA,EACA,EACQ,CACR,IAAMiB,EAAM,WAAW,oBAAoBV,CAAO,CAAC,EAC7CJ,EAAS,OAAO,QAAQI,EAAQ,MAAM,CAAC,CAC1C,KAAK,CAACF,EAAIC,KAAS,cAAc,WAAWD,CAAE,EAAE,IAAI,UAAUC,CAAG,EAAE,SAAS,CAAC,CAC7E,KAAK;CAAI,EACNK,EAAUJ,EAAQ,QACrB,IAAK,GAAQ,wCAAwC,WAAWW,CAAG,EAAE,aAAY,CAAC,CAClF,KAAK;CAAI,EACNC,EAAQZ,EAAQ,MAAQ,WAAW,WAAWA,EAAQ,KAAK,EAAE,GAAK,GAExE,MAAO;cACK,WAAWA,EAAQ,IAAI,EAAE;;sDAEeU,EAAI;;;SAGjD,WAAWV,EAAQ,KAAK,EAAE;EACjCJ,EAAO;SACAgB,EAAM;EACb,kBAAkB1B,CAAS,EAAE;;;;EAI7BkB,EAAQ;EACRS,EAAK;;QAGP,CChDA,IAAaC,EAAa,SACbC,EAAY,QACZC,EAAmB,eACnBC,EAAY,QACZC,EAAa,SACbC,EAAuB,mBACvBC,EAAkB,cAS/B,SAAgB,kBAAkB,EAAgB,EAAsD,CACtG,OACE,OAAO3B,GAAU,YACjBA,GACA,OAAQA,EAAgC,SAAY,UACnDA,EAA8B,UAAYP,EAAU,SACrD,OAAQO,EAAmC,YAAe,UACzDA,EAAiC,aAAeP,EAAU,YAC3D,OAAQO,EAA6B,MAAS,QAElD,CAEA,SAAgB,eAAwB,CACtC,GAAI,OAAO,OAAO,YAAe,WAAY,OAAO,OAAO,WAAW,EAEtE,IAAM4B,EAAQ,OAAO,gBAAgB,IAAI,YAAY,CAAC,CAAC,EAEvD,OAAO,MAAM,KAAKA,EAAQ,GAASC,EAAK,SAAS,EAAE,CAAC,CAAC,SAAS,EAAG,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,CAChF,CAEA,SAAgB,SAAS,EAA4B,EAAmD,CACtG,MAAO,CAAE,GAAGnC,EAAS,QAASD,EAAU,QAAS,WAAYA,EAAU,UAAW,CACpF,CClBA,SAAgB,cACd,EACA,EAA0B,CAAC,EACL,CACtB,IAAMW,EAAa,wBAAwBG,CAAO,EAC5CuB,EAAU,cAAc,EACxB3B,EAAiC,CAAE,GAAGC,EAAW,MAAO,EACxD2B,EAAY,IAAI,IAChBC,EAA+B,CAAC,EAChCC,EAAa,IAAI,gBACnBC,EAAmC,KACnCC,EAAW,GACXC,EAAc,GACdC,EAAa,EACb3B,EAAqD,KACrD4B,EAAkF,KAEtF,SAAS,kBAAoC,CAC3C,MAAO,CAAE,UAAS,YAAW,CAC/B,CAEA,SAAS,mBAA0B,CAC7B5B,IAAiB,OAErB,aAAaA,CAAY,EACzB,EAAe,KACjB,CAEA,SAAS,wBAA+B,CACtC,kBAAkB,EAClB4B,GAAe,QAAQ,EACvB,EAAgB,KAChB,EAAc,EAChB,CAEA,SAAS,UAAU,EAA+B,CAChD,IAAK,IAAMC,KAAYR,EACrB,GAAI,CACFQ,EAAS7C,CAAO,CAClB,MAAQ,CAER,CAEJ,CAEA,SAAS,cAAc,EAA2B,CAChD,GAAI,CAACwC,GAAUM,EAAM,SAAWN,EAAO,cAAe,OAEtD,IAAMzC,EAAY,iBAAiB,EAEnC,GAAK,kBAAkB+C,EAAM,KAAM/C,CAAS,EAE5C,OAAQ+C,EAAM,KAAK,KAAnB,CACE,KAAKnB,EACC,OAAOmB,EAAM,KAAK,OAAU,UAC9B,UAAU,CAAE,OAAQA,EAAM,KAAK,OAAQ,MAAOA,EAAM,KAAK,MAAO,KAAMnB,CAAW,CAAC,EAGpF,MACF,KAAKC,EACC,OAAOkB,EAAM,KAAK,SAAY,UAChC,UAAU,CACR,QAASA,EAAM,KAAK,QACpB,GAAI,OAAOA,EAAM,KAAK,OAAU,SAAW,CAAE,MAAOA,EAAM,KAAK,KAAM,EAAI,CAAC,EAC1E,KAAMlB,CACR,CAAC,EAGH,MACF,KAAKE,EACH,uBAAuB,EACvB,EAAc,GACd,kBAAkB,EAClB,MACF,KAAKC,EACC,OAAOe,EAAM,KAAK,QAAW,UAAY,OAAO,SAASA,EAAM,KAAK,MAAM,GAAKA,EAAM,KAAK,QAAU,GACtG,UAAU,CAAE,OAAQA,EAAM,KAAK,OAAQ,KAAMf,CAAW,CAAC,CAI/D,CACF,CAEA,SAAS,cAAkC,CAUzC,OATIS,IAEJ,EAAS,SAAS,cAAc,QAAQ,EACxCA,EAAO,aAAa,UAAW,eAAe,EAC9CA,EAAO,aAAa,iBAAkB,aAAa,EACnD,EAAO,QAAQ,eAAiBJ,EAChCW,EAAU,YAAYP,CAAM,EAC5B,OAAO,iBAAiB,UAAW,aAAa,EAEzCA,EACT,CAEA,SAAS,SAAgB,CACnBC,IAEJ,EAAW,GACX,uBAAuB,EACvBF,EAAW,MAAM,EAEjB,AAGEC,KAFA,OAAO,oBAAoB,UAAW,aAAa,EACnDA,EAAO,OAAO,EACL,MAGXH,EAAU,MAAM,EAClB,CAEA,SAAS,UAAU,EAAwD,CASzE,OARII,MAGW,CAAC,GAGhBJ,EAAU,IAAIW,CAAO,MAERX,EAAU,OAAOW,CAAO,EACvC,CAEA,SAAS,OAAO,EAA6B,CAC3C,GAAIP,EAGF,OAAO,QAAQ,QAAQ,EAGpBf,EAAK,KAAK,EAEf,uBAAuB,EACvB,GAAc,EAEd,IAAMuB,EAAU,aAAa,EACvBlD,EAAY,iBAAiB,EAC7BmD,EAAkB,CAAE,GAAGxC,EAAY,OAAQ,CAAE,GAAGD,CAAO,CAAE,EAE/D,EAAQ,QAAQ,kBAAoB,OAAOkC,CAAU,EACrD,EAAQ,OAAS,yBAAyBjB,EAAMwB,EAAiBnD,CAAS,EAE1E,IAAMoD,EAAU,IAAI,SAAe,EAAS,IAAW,CACrD,EAAgB,CAAE,SAAQ,SAAQ,CACpC,CAAC,EAeD,MAbA,GAAe,eAAiB,CACzBP,IAELA,EAAc,OACZ,IAAI,oBACF,oDAAoDlC,EAAW,aAAa,iEAE9E,CACF,EACA,EAAgB,KAChB,EAAe,KACjB,EAAGA,EAAW,YAAY,EAEnByC,CACT,CAEA,SAAS,KAAK,EAAwC,CACpDX,GAAQ,eAAe,YAAY,SAAS,iBAAiB,EAAGxC,CAAO,EAAG,GAAG,CAC/E,CAEA,SAAS,mBAA0B,CAC5B0C,GAAgBF,GAAQ,eAAiB,OAAO,KAAKF,CAAY,CAAC,CAAC,SAAW,GAEnF,KAAK,CAAE,OAAQ,CAAE,GAAGA,CAAa,EAAG,KAAMN,CAAqB,CAAC,CAClE,CAEA,SAAS,SAAS,EAA8B,CAC1CS,IAMJ,OAAO,OAAOH,EAAcc,CAAM,EAE9BV,GAAeF,GAAQ,eAAiB,OAAO,KAAKY,CAAM,CAAC,CAAC,OAAS,GACvE,KAAK,CAAE,OAAQA,EAAQ,KAAMpB,CAAqB,CAAC,EAEvD,CAEA,SAAS,YAAY,EAAoB,CACnCS,GAMCC,GAAgBF,GAAQ,eAM7B,KAAK,CAAE,OAAM,KAAMX,CAAiB,CAAC,CACvC,CAEA,SAAS,YAAY,EAAY,EAAmB,CAC9CY,IAYJ,EAAO9B,GAAMC,EAET8B,GAAeF,GAAQ,eAAe,KAAK,CAAE,MAAK,KAAI,KAAMP,CAAgB,CAAC,EACnF,CAEA,MAAO,CACL,IAAI,gBAAiB,CACnB,OAAOM,EAAW,MACpB,EACA,QACA,IAAI,UAAW,CACb,OAAOE,CACT,EACA,UACA,OACA,YACA,SACA,CAAC,OAAO,UAAW,CACjB,QAAQ,CACV,EACA,WACF,CACF,C"}
package/dist/sandbox.js CHANGED
@@ -1,5 +1,4 @@
1
- //#endregion
2
- //#region src/_bridge.ts
1
+ // src/_bridge.ts
3
2
  function buildBridgeScript(e) {
4
3
  return `
5
4
  (function() {
@@ -68,8 +67,7 @@ window.addEventListener('DOMContentLoaded', function() {
68
67
  })();
69
68
  `.trim();
70
69
  }
71
- //#endregion
72
- //#region src/errors.ts
70
+ // src/errors.ts
73
71
  var SandboxError = class extends Error {
74
72
  constructor(e, t) {
75
73
  super(e, t), this.name = new.target.name, Object.setPrototypeOf(this, new.target.prototype);
@@ -105,13 +103,13 @@ function normalizeStyles(t) {
105
103
  if (t === void 0) return {};
106
104
  if (typeof t != "object" || !t || Array.isArray(t)) return fail("styles", t);
107
105
  let n = {};
108
- for (let [r, i] of Object.entries(t)) (!e.test(r) || typeof i != "string") && fail("styles", r), n[r] = i;
106
+ for (let [r, i] of Object.entries(t)) e.test(r) && typeof i == "string" || fail("styles", r), n[r] = i;
109
107
  return n;
110
108
  }
111
109
  function normalizeSandboxOptions(e = {}) {
112
110
  (typeof e != "object" || !e || Array.isArray(e)) && fail("options", e);
113
111
  let r = e.lang ?? "en";
114
- t.test(r) || fail("lang", r), e.nonce !== void 0 && (!n.test(e.nonce) || e.nonce.length === 0) && fail("nonce", e.nonce), e.title !== void 0 && typeof e.title != "string" && fail("title", e.title);
112
+ t.test(r) || fail("lang", r), e.nonce === void 0 || n.test(e.nonce) && e.nonce.length !== 0 || fail("nonce", e.nonce), e.title !== void 0 && typeof e.title != "string" && fail("title", e.title);
115
113
  let i = e.readyTimeout, a = i ?? 5e3;
116
114
  i !== void 0 && (i === null || typeof i != "number" || !Number.isFinite(i) || i <= 0 || i > 2 ** 31 - 1) && fail("readyTimeout", i), e.scripts !== void 0 && !Array.isArray(e.scripts) && fail("scripts", e.scripts);
117
115
  let o = (e.scripts ?? []).map(normalizeScript);
@@ -146,8 +144,7 @@ function buildCspFromOptions(e) {
146
144
  "base-uri 'none'"
147
145
  ].join("; ");
148
146
  }
149
- //#endregion
150
- //#region src/_document.ts
147
+ // src/_document.ts
151
148
  function escapeCss(e) {
152
149
  return e.replace(/<\/style/gi, "\\3C /style");
153
150
  }
@@ -177,8 +174,7 @@ ${e}
177
174
  </body>
178
175
  </html>`;
179
176
  }
180
- //#endregion
181
- //#region src/_protocol.ts
177
+ // src/_protocol.ts
182
178
  var r = "custom", i = "error", a = "html-replace", o = "ready", s = "resize", c = "state-update-all", l = "style-patch";
183
179
  function isProtocolMessage(e, t) {
184
180
  return typeof e == "object" && !!e && typeof e.channel == "string" && e.channel === t.channel && typeof e.generation == "number" && e.generation === t.generation && typeof e.type == "string";
@@ -195,8 +191,7 @@ function envelope(e, t) {
195
191
  generation: e.generation
196
192
  };
197
193
  }
198
- //#endregion
199
- //#region src/_runtime.ts
194
+ // src/_runtime.ts
200
195
  function createSandbox(e, t = {}) {
201
196
  let n = normalizeSandboxOptions(t), u = createChannel(), d = { ...n.styles }, f = /* @__PURE__ */ new Set(), p = {}, m = new AbortController(), h = null, g = !1, _ = !1, v = 0, y = null, b = null;
202
197
  function currentBootstrap() {
@@ -316,7 +311,6 @@ function createSandbox(e, t = {}) {
316
311
  updateStyle
317
312
  };
318
313
  }
319
- //#endregion
320
314
  export { SandboxConfigurationError, SandboxError, SandboxTimeoutError, createSandbox };
321
315
 
322
316
  //# sourceMappingURL=sandbox.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sandbox.js","names":["bootstrap","message","opts","ID","LANG","NONCE","field","value","url","values","styles","normalized","id","css","options","lang","rawReadyTimeout","readyTimeout","scripts","scriptOrigins","script","styleOrigins","imageOrigins","fontOrigins","csp","src","nonce","html","MSG_CUSTOM","MSG_ERROR","MSG_HTML_REPLACE","MSG_READY","MSG_RESIZE","MSG_STATE_UPDATE_ALL","MSG_STYLE_PATCH","bytes","byte","channel","listeners","pendingState","controller","iframe","disposed","bridgeReady","generation","currentRender","listener","event","container","handler","current","documentOptions","promise","update"],"sources":["../src/_dev.ts","../src/_bridge.ts","../src/errors.ts","../src/_policy.ts","../src/_document.ts","../src/_protocol.ts","../src/_runtime.ts"],"sourcesContent":["const isDev = !(globalThis as { __SANDBOX_PROD__?: boolean }).__SANDBOX_PROD__;\n\n/** @internal @security: some callers interpolate user-supplied values (e.g. styles key) into msg; do not log PII. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/sandbox] ${msg}`);\n}\n\n/** @internal: Run fn only in dev builds. Use when dev-only logic goes beyond a single warn() / error() call. */\nexport function devOnly(fn: () => void): void {\n if (isDev) fn();\n}\n","import type { BridgeBootstrap } from './_protocol.js';\n\nexport function buildBridgeScript(bootstrap: BridgeBootstrap): string {\n const channel = JSON.stringify(bootstrap.channel);\n const generation = String(bootstrap.generation);\n\n return `\n(function() {\nwindow.__sandboxChannel = ${channel};\nwindow.__sandboxGeneration = ${generation};\nconst __sandboxChannel = window.__sandboxChannel;\nconst __sandboxGeneration = window.__sandboxGeneration;\nif ('ontouchstart' in window) document.addEventListener('touchstart', function() {}, { passive: true });\nwindow.addEventListener('message', function(e) {\n var msg = e.data;\n if (e.source !== parent || !msg || msg.channel !== __sandboxChannel || msg.generation !== __sandboxGeneration) return;\n if (msg.type === 'state-update-all' && msg.record && typeof msg.record === 'object') {\n var keys = Object.keys(msg.record);\n for (var i = 0; i < keys.length; i++) {\n var k = keys[i];\n document.dispatchEvent(new CustomEvent('sandbox:state-update', { detail: { key: k, value: msg.record[k] } }));\n }\n }\n if (msg.type === 'style-patch' && msg.id && typeof msg.css === 'string') {\n var el = document.getElementById(msg.id);\n if (el && el.tagName === 'STYLE') el.textContent = msg.css;\n }\n if (msg.type === 'html-replace' && typeof msg.html === 'string') {\n document.body.innerHTML = msg.html;\n }\n});\nfunction post(msg) {\n msg.channel = __sandboxChannel;\n msg.generation = __sandboxGeneration;\n parent.postMessage(msg, '*');\n}\nwindow.onerror = function(message, _src, _line, _col, err) {\n post({ type: 'error', message: String(message), stack: err && err.stack });\n return true;\n};\nwindow.addEventListener('unhandledrejection', function(e) {\n var reason = e.reason;\n post({ type: 'error', message: String(reason), stack: reason && reason.stack });\n});\nwindow.__sandbox__ = {\n emit: function(event, detail) {\n post({ type: 'custom', event: String(event), detail: detail });\n },\n onState: function(key, handler) {\n function listener(e) {\n if (e.detail && e.detail.key === key) handler(e.detail.value);\n }\n document.addEventListener('sandbox:state-update', listener);\n return function() {\n document.removeEventListener('sandbox:state-update', listener);\n };\n }\n};\nwindow.addEventListener('DOMContentLoaded', function() {\n post({ type: 'ready' });\n if (typeof ResizeObserver === 'undefined') return;\n new ResizeObserver(function(entries) {\n var entry = entries[0];\n if (!entry) return;\n var h = entry.borderBoxSize && entry.borderBoxSize[0]\n ? entry.borderBoxSize[0].blockSize\n : entry.contentRect.height;\n post({ type: 'resize', height: h });\n }).observe(document.body);\n});\n})();\n`.trim();\n}\n","/** Base class for all sandbox errors. */\nexport class SandboxError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Thrown when render() does not receive a 'ready' signal from the sandbox document\n * within the timeout window. An injected script may have blocked document initialization.\n */\nexport class SandboxTimeoutError extends SandboxError {}\n\n/** Thrown when Sandbox configuration cannot produce a valid document or CSP policy. */\nexport class SandboxConfigurationError extends SandboxError {}\n","import { SandboxConfigurationError } from './errors.js';\nimport type { SandboxOptions } from './types.js';\n\nexport interface NormalizedSandboxOptions {\n allowedFontOrigins: string[];\n allowedImageOrigins: string[];\n allowedScriptOrigins: string[];\n allowedStyleOrigins: string[];\n lang: string;\n nonce: string | undefined;\n readyTimeout: number;\n scripts: string[];\n styles: Record<string, string>;\n title: string;\n}\n\nexport const DEFAULT_READY_TIMEOUT_MS = 5000;\n\nconst ID = /^[A-Za-z][A-Za-z0-9_-]*$/;\nconst LANG = /^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/;\nconst NONCE = /^[A-Za-z0-9+/_-]+={0,2}$/;\n\nfunction fail(field: string, value: unknown): never {\n throw new SandboxConfigurationError(`${field} is invalid: ${String(value)}`);\n}\n\nfunction normalizeOrigin(value: unknown, field: string): string {\n if (typeof value !== 'string') return fail(field, value);\n\n let url: URL;\n\n try {\n url = new URL(value);\n } catch {\n return fail(field, value);\n }\n\n if (\n (url.protocol !== 'http:' && url.protocol !== 'https:') ||\n url.username ||\n url.password ||\n url.pathname !== '/' ||\n url.search ||\n url.hash\n ) {\n return fail(field, value);\n }\n\n return url.origin;\n}\n\nfunction normalizeOrigins(values: string[] | undefined, field: string): string[] {\n if (values === undefined) return [];\n if (!Array.isArray(values)) return fail(field, values);\n\n return values.map((value) => normalizeOrigin(value, field));\n}\n\nfunction normalizeScript(value: unknown): string {\n if (typeof value !== 'string') return fail('scripts', value);\n\n let url: URL;\n\n try {\n url = new URL(value);\n } catch {\n return fail('scripts', value);\n }\n\n if (url.protocol !== 'http:' && url.protocol !== 'https:') return fail('scripts', value);\n\n return url.href;\n}\n\nfunction normalizeStyles(styles: Record<string, string> | undefined): Record<string, string> {\n if (styles === undefined) return {};\n if (typeof styles !== 'object' || styles === null || Array.isArray(styles)) return fail('styles', styles);\n\n const normalized: Record<string, string> = {};\n\n for (const [id, css] of Object.entries(styles)) {\n if (!ID.test(id) || typeof css !== 'string') fail('styles', id);\n\n normalized[id] = css;\n }\n\n return normalized;\n}\n\nexport function normalizeSandboxOptions(options: SandboxOptions = {}): NormalizedSandboxOptions {\n if (typeof options !== 'object' || options === null || Array.isArray(options)) fail('options', options);\n\n const lang = options.lang ?? 'en';\n\n if (!LANG.test(lang)) fail('lang', lang);\n\n if (options.nonce !== undefined && (!NONCE.test(options.nonce) || options.nonce.length === 0)) {\n fail('nonce', options.nonce);\n }\n\n if (options.title !== undefined && typeof options.title !== 'string') fail('title', options.title);\n\n const rawReadyTimeout = options.readyTimeout;\n const readyTimeout = rawReadyTimeout ?? DEFAULT_READY_TIMEOUT_MS;\n\n if (\n rawReadyTimeout !== undefined &&\n (rawReadyTimeout === null ||\n typeof rawReadyTimeout !== 'number' ||\n !Number.isFinite(rawReadyTimeout) ||\n rawReadyTimeout <= 0 ||\n rawReadyTimeout > 2 ** 31 - 1)\n ) {\n fail('readyTimeout', rawReadyTimeout);\n }\n\n if (options.scripts !== undefined && !Array.isArray(options.scripts)) fail('scripts', options.scripts);\n\n const scripts = (options.scripts ?? []).map(normalizeScript);\n\n return {\n allowedFontOrigins: normalizeOrigins(options.allowedFontOrigins, 'allowedFontOrigins'),\n allowedImageOrigins: normalizeOrigins(options.allowedImageOrigins, 'allowedImageOrigins'),\n allowedScriptOrigins: normalizeOrigins(options.allowedScriptOrigins, 'allowedScriptOrigins'),\n allowedStyleOrigins: normalizeOrigins(options.allowedStyleOrigins, 'allowedStyleOrigins'),\n lang,\n nonce: options.nonce,\n readyTimeout,\n scripts,\n styles: normalizeStyles(options.styles),\n title: options.title ?? '',\n };\n}\n\nexport function buildCspFromOptions(options: NormalizedSandboxOptions): string {\n const scriptOrigins = [\n \"'unsafe-inline'\",\n ...(options.nonce ? [`'nonce-${options.nonce}'`] : []),\n ...options.allowedScriptOrigins,\n ...options.scripts.map((script) => new URL(script).origin),\n ].join(' ');\n const styleOrigins = [\"'unsafe-inline'\", ...options.allowedStyleOrigins].join(' ');\n const imageOrigins = ['data:', ...options.allowedImageOrigins].join(' ');\n const fontOrigins = options.allowedFontOrigins.join(' ') || \"'none'\";\n\n return [\n \"default-src 'none'\",\n `script-src ${scriptOrigins}`,\n `style-src ${styleOrigins}`,\n `img-src ${imageOrigins}`,\n `font-src ${fontOrigins}`,\n \"connect-src 'none'\",\n \"form-action 'none'\",\n \"base-uri 'none'\",\n ].join('; ');\n}\n\nexport function buildCsp(options: SandboxOptions = {}): string {\n return buildCspFromOptions(normalizeSandboxOptions(options));\n}\n","import { buildBridgeScript } from './_bridge.js';\nimport { buildCspFromOptions, type NormalizedSandboxOptions, normalizeSandboxOptions } from './_policy.js';\nimport type { BridgeBootstrap } from './_protocol.js';\nimport type { SandboxOptions } from './types.js';\n\nfunction escapeCss(css: string): string {\n return css.replace(/<\\/style/gi, '\\\\3C /style');\n}\n\nfunction escapeAttr(value: string): string {\n return value.replace(/&/g, '&amp;').replace(/\"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n}\n\nfunction escapeText(value: string): string {\n return value.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n}\n\nexport function buildDocumentFromOptions(\n html: string,\n options: NormalizedSandboxOptions,\n bootstrap: BridgeBootstrap,\n): string {\n const csp = escapeAttr(buildCspFromOptions(options));\n const styles = Object.entries(options.styles)\n .map(([id, css]) => `<style id=\"${escapeAttr(id)}\">${escapeCss(css)}</style>`)\n .join('\\n');\n const scripts = options.scripts\n .map((src) => `<script crossorigin=\"anonymous\" src=\"${escapeAttr(src)}\"></script>`)\n .join('\\n');\n const nonce = options.nonce ? ` nonce=\"${escapeAttr(options.nonce)}\"` : '';\n\n return `<!doctype html>\n<html lang=\"${escapeAttr(options.lang)}\">\n<head>\n<meta http-equiv=\"Content-Security-Policy\" content=\"${csp}\">\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>${escapeText(options.title)}</title>\n${styles}\n<script${nonce}>\n${buildBridgeScript(bootstrap)}\n</script>\n</head>\n<body>\n${scripts}\n${html}\n</body>\n</html>`;\n}\n\nexport function buildDocument(html: string, options: SandboxOptions = {}): string {\n return buildDocumentFromOptions(html, normalizeSandboxOptions(options), { channel: '', generation: 0 });\n}\n","export const MSG_CUSTOM = 'custom';\nexport const MSG_ERROR = 'error';\nexport const MSG_HTML_REPLACE = 'html-replace';\nexport const MSG_READY = 'ready';\nexport const MSG_RESIZE = 'resize';\nexport const MSG_STATE_UPDATE_ALL = 'state-update-all';\nexport const MSG_STYLE_PATCH = 'style-patch';\n\nexport interface BridgeBootstrap {\n channel: string;\n generation: number;\n}\n\nexport type ProtocolMessage = Record<string, unknown> & { channel: string; generation: number; type: string };\n\nexport function isProtocolMessage(value: unknown, bootstrap: BridgeBootstrap): value is ProtocolMessage {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as { channel?: unknown }).channel === 'string' &&\n (value as { channel: string }).channel === bootstrap.channel &&\n typeof (value as { generation?: unknown }).generation === 'number' &&\n (value as { generation: number }).generation === bootstrap.generation &&\n typeof (value as { type?: unknown }).type === 'string'\n );\n}\n\nexport function createChannel(): string {\n if (typeof crypto.randomUUID === 'function') return crypto.randomUUID();\n\n const bytes = crypto.getRandomValues(new Uint32Array(4));\n\n return Array.from(bytes, (byte) => byte.toString(16).padStart(8, '0')).join('');\n}\n\nexport function envelope(bootstrap: BridgeBootstrap, message: Record<string, unknown>): ProtocolMessage {\n return { ...message, channel: bootstrap.channel, generation: bootstrap.generation } as ProtocolMessage;\n}\n","import { devOnly, warn } from './_dev.js';\nimport { buildDocumentFromOptions } from './_document.js';\nimport { normalizeSandboxOptions } from './_policy.js';\nimport {\n type BridgeBootstrap,\n createChannel,\n envelope,\n isProtocolMessage,\n MSG_CUSTOM,\n MSG_ERROR,\n MSG_HTML_REPLACE,\n MSG_READY,\n MSG_RESIZE,\n MSG_STATE_UPDATE_ALL,\n MSG_STYLE_PATCH,\n} from './_protocol.js';\nimport { SandboxTimeoutError } from './errors.js';\nimport type { SandboxHandle, SandboxMessage, SandboxOptions } from './types.js';\n\nexport function createSandbox<State extends object = Record<string, unknown>>(\n container: HTMLElement,\n options: SandboxOptions = {},\n): SandboxHandle<State> {\n const normalized = normalizeSandboxOptions(options);\n const channel = createChannel();\n const styles: Record<string, string> = { ...normalized.styles };\n const listeners = new Set<(message: SandboxMessage) => void>();\n const pendingState: Partial<State> = {};\n const controller = new AbortController();\n let iframe: HTMLIFrameElement | null = null;\n let disposed = false;\n let bridgeReady = false;\n let generation = 0;\n let readyTimeout: ReturnType<typeof setTimeout> | null = null;\n let currentRender: { reject: (error: unknown) => void; resolve: () => void } | null = null;\n\n function currentBootstrap(): BridgeBootstrap {\n return { channel, generation };\n }\n\n function clearReadyTimeout(): void {\n if (readyTimeout === null) return;\n\n clearTimeout(readyTimeout);\n readyTimeout = null;\n }\n\n function supersedePendingRender(): void {\n clearReadyTimeout();\n currentRender?.resolve();\n currentRender = null;\n bridgeReady = false;\n }\n\n function broadcast(message: SandboxMessage): void {\n for (const listener of listeners) {\n try {\n listener(message);\n } catch {\n warn('onMessage() handler threw: remaining handlers will still run.');\n }\n }\n }\n\n function handleMessage(event: MessageEvent): void {\n if (!iframe || event.source !== iframe.contentWindow) return;\n\n const bootstrap = currentBootstrap();\n\n if (!isProtocolMessage(event.data, bootstrap)) return;\n\n switch (event.data.type) {\n case MSG_CUSTOM:\n if (typeof event.data.event === 'string') {\n broadcast({ detail: event.data.detail, event: event.data.event, type: MSG_CUSTOM });\n }\n\n break;\n case MSG_ERROR:\n if (typeof event.data.message === 'string') {\n broadcast({\n message: event.data.message,\n ...(typeof event.data.stack === 'string' ? { stack: event.data.stack } : {}),\n type: MSG_ERROR,\n });\n }\n\n break;\n case MSG_READY:\n supersedePendingRender();\n bridgeReady = true;\n flushPendingState();\n break;\n case MSG_RESIZE:\n if (typeof event.data.height === 'number' && Number.isFinite(event.data.height) && event.data.height >= 0) {\n broadcast({ height: event.data.height, type: MSG_RESIZE });\n }\n\n break;\n }\n }\n\n function ensureIframe(): HTMLIFrameElement {\n if (iframe) return iframe;\n\n iframe = document.createElement('iframe');\n iframe.setAttribute('sandbox', 'allow-scripts');\n iframe.setAttribute('referrerpolicy', 'no-referrer');\n iframe.dataset.sandboxChannel = channel;\n container.appendChild(iframe);\n window.addEventListener('message', handleMessage);\n\n return iframe;\n }\n\n function dispose(): void {\n if (disposed) return;\n\n disposed = true;\n supersedePendingRender();\n controller.abort();\n\n if (iframe) {\n window.removeEventListener('message', handleMessage);\n iframe.remove();\n iframe = null;\n }\n\n listeners.clear();\n }\n\n function onMessage(handler: (message: SandboxMessage) => void): () => void {\n if (disposed) {\n warn('onMessage() called on a disposed sandbox: handler will never fire.');\n\n return () => {};\n }\n\n listeners.add(handler);\n\n return () => listeners.delete(handler);\n }\n\n function render(html: string): Promise<void> {\n if (disposed) {\n warn('render() called on a disposed sandbox.');\n\n return Promise.resolve();\n }\n\n if (!html.trim()) warn('render() called with empty HTML.');\n\n supersedePendingRender();\n generation += 1;\n\n const current = ensureIframe();\n const bootstrap = currentBootstrap();\n const documentOptions = { ...normalized, styles: { ...styles } };\n\n current.dataset.sandboxGeneration = String(generation);\n current.srcdoc = buildDocumentFromOptions(html, documentOptions, bootstrap);\n\n const promise = new Promise<void>((resolve, reject) => {\n currentRender = { reject, resolve };\n });\n\n readyTimeout = setTimeout(() => {\n if (!currentRender) return;\n\n currentRender.reject(\n new SandboxTimeoutError(\n `render() did not receive a 'ready' signal within ${normalized.readyTimeout}ms. ` +\n 'An injected script may have blocked document initialization.',\n ),\n );\n currentRender = null;\n readyTimeout = null;\n }, normalized.readyTimeout);\n\n return promise;\n }\n\n function send(message: Record<string, unknown>): void {\n iframe?.contentWindow?.postMessage(envelope(currentBootstrap(), message), '*');\n }\n\n function flushPendingState(): void {\n if (!bridgeReady || !iframe?.contentWindow || Object.keys(pendingState).length === 0) return;\n\n send({ record: { ...pendingState }, type: MSG_STATE_UPDATE_ALL });\n }\n\n function setState(update: Partial<State>): void {\n if (disposed) {\n warn('setState() called on a disposed sandbox.');\n\n return;\n }\n\n Object.assign(pendingState, update);\n\n if (bridgeReady && iframe?.contentWindow && Object.keys(update).length > 0) {\n send({ record: update, type: MSG_STATE_UPDATE_ALL });\n }\n }\n\n function replaceBody(html: string): void {\n if (disposed) {\n warn('replaceBody() called on a disposed sandbox.');\n\n return;\n }\n\n if (!bridgeReady || !iframe?.contentWindow) {\n warn('replaceBody() called before render() has resolved: bridge is not ready. Await render() first.');\n\n return;\n }\n\n send({ html, type: MSG_HTML_REPLACE });\n }\n\n function updateStyle(id: string, css: string): void {\n if (disposed) {\n warn('updateStyle() called on a disposed sandbox.');\n\n return;\n }\n\n devOnly(() => {\n if (!(id in styles)) {\n warn(`updateStyle('${id}', …): '${id}' is not a known styles key.`);\n }\n });\n\n styles[id] = css;\n\n if (bridgeReady && iframe?.contentWindow) send({ css, id, type: MSG_STYLE_PATCH });\n }\n\n return {\n get disposalSignal() {\n return controller.signal;\n },\n dispose,\n get disposed() {\n return disposed;\n },\n onMessage,\n render,\n replaceBody,\n setState,\n [Symbol.dispose]() {\n dispose();\n },\n updateStyle,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"sandbox.js","names":["bootstrap","message","opts","ID","LANG","NONCE","field","value","url","values","styles","normalized","id","css","options","lang","rawReadyTimeout","readyTimeout","scripts","scriptOrigins","script","styleOrigins","imageOrigins","fontOrigins","csp","src","nonce","html","MSG_CUSTOM","MSG_ERROR","MSG_HTML_REPLACE","MSG_READY","MSG_RESIZE","MSG_STATE_UPDATE_ALL","MSG_STYLE_PATCH","bytes","byte","channel","listeners","pendingState","controller","iframe","disposed","bridgeReady","generation","currentRender","listener","event","container","handler","current","documentOptions","promise","update"],"sources":["../src/_dev.ts","../src/_bridge.ts","../src/errors.ts","../src/_policy.ts","../src/_document.ts","../src/_protocol.ts","../src/_runtime.ts"],"sourcesContent":["const isDev = !(globalThis as { __SANDBOX_PROD__?: boolean }).__SANDBOX_PROD__;\n\n/** @internal @security: some callers interpolate user-supplied values (e.g. styles key) into msg; do not log PII. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/sandbox] ${msg}`);\n}\n\n/** @internal: Run fn only in dev builds. Use when dev-only logic goes beyond a single warn() / error() call. */\nexport function devOnly(fn: () => void): void {\n if (isDev) fn();\n}\n","import type { BridgeBootstrap } from './_protocol.js';\n\nexport function buildBridgeScript(bootstrap: BridgeBootstrap): string {\n const channel = JSON.stringify(bootstrap.channel);\n const generation = String(bootstrap.generation);\n\n return `\n(function() {\nwindow.__sandboxChannel = ${channel};\nwindow.__sandboxGeneration = ${generation};\nconst __sandboxChannel = window.__sandboxChannel;\nconst __sandboxGeneration = window.__sandboxGeneration;\nif ('ontouchstart' in window) document.addEventListener('touchstart', function() {}, { passive: true });\nwindow.addEventListener('message', function(e) {\n var msg = e.data;\n if (e.source !== parent || !msg || msg.channel !== __sandboxChannel || msg.generation !== __sandboxGeneration) return;\n if (msg.type === 'state-update-all' && msg.record && typeof msg.record === 'object') {\n var keys = Object.keys(msg.record);\n for (var i = 0; i < keys.length; i++) {\n var k = keys[i];\n document.dispatchEvent(new CustomEvent('sandbox:state-update', { detail: { key: k, value: msg.record[k] } }));\n }\n }\n if (msg.type === 'style-patch' && msg.id && typeof msg.css === 'string') {\n var el = document.getElementById(msg.id);\n if (el && el.tagName === 'STYLE') el.textContent = msg.css;\n }\n if (msg.type === 'html-replace' && typeof msg.html === 'string') {\n document.body.innerHTML = msg.html;\n }\n});\nfunction post(msg) {\n msg.channel = __sandboxChannel;\n msg.generation = __sandboxGeneration;\n parent.postMessage(msg, '*');\n}\nwindow.onerror = function(message, _src, _line, _col, err) {\n post({ type: 'error', message: String(message), stack: err && err.stack });\n return true;\n};\nwindow.addEventListener('unhandledrejection', function(e) {\n var reason = e.reason;\n post({ type: 'error', message: String(reason), stack: reason && reason.stack });\n});\nwindow.__sandbox__ = {\n emit: function(event, detail) {\n post({ type: 'custom', event: String(event), detail: detail });\n },\n onState: function(key, handler) {\n function listener(e) {\n if (e.detail && e.detail.key === key) handler(e.detail.value);\n }\n document.addEventListener('sandbox:state-update', listener);\n return function() {\n document.removeEventListener('sandbox:state-update', listener);\n };\n }\n};\nwindow.addEventListener('DOMContentLoaded', function() {\n post({ type: 'ready' });\n if (typeof ResizeObserver === 'undefined') return;\n new ResizeObserver(function(entries) {\n var entry = entries[0];\n if (!entry) return;\n var h = entry.borderBoxSize && entry.borderBoxSize[0]\n ? entry.borderBoxSize[0].blockSize\n : entry.contentRect.height;\n post({ type: 'resize', height: h });\n }).observe(document.body);\n});\n})();\n`.trim();\n}\n","/** Base class for all sandbox errors. */\nexport class SandboxError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Thrown when render() does not receive a 'ready' signal from the sandbox document\n * within the timeout window. An injected script may have blocked document initialization.\n */\nexport class SandboxTimeoutError extends SandboxError {}\n\n/** Thrown when Sandbox configuration cannot produce a valid document or CSP policy. */\nexport class SandboxConfigurationError extends SandboxError {}\n","import { SandboxConfigurationError } from './errors.js';\nimport type { SandboxOptions } from './types.js';\n\nexport interface NormalizedSandboxOptions {\n allowedFontOrigins: string[];\n allowedImageOrigins: string[];\n allowedScriptOrigins: string[];\n allowedStyleOrigins: string[];\n lang: string;\n nonce: string | undefined;\n readyTimeout: number;\n scripts: string[];\n styles: Record<string, string>;\n title: string;\n}\n\nexport const DEFAULT_READY_TIMEOUT_MS = 5000;\n\nconst ID = /^[A-Za-z][A-Za-z0-9_-]*$/;\nconst LANG = /^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/;\nconst NONCE = /^[A-Za-z0-9+/_-]+={0,2}$/;\n\nfunction fail(field: string, value: unknown): never {\n throw new SandboxConfigurationError(`${field} is invalid: ${String(value)}`);\n}\n\nfunction normalizeOrigin(value: unknown, field: string): string {\n if (typeof value !== 'string') return fail(field, value);\n\n let url: URL;\n\n try {\n url = new URL(value);\n } catch {\n return fail(field, value);\n }\n\n if (\n (url.protocol !== 'http:' && url.protocol !== 'https:') ||\n url.username ||\n url.password ||\n url.pathname !== '/' ||\n url.search ||\n url.hash\n ) {\n return fail(field, value);\n }\n\n return url.origin;\n}\n\nfunction normalizeOrigins(values: string[] | undefined, field: string): string[] {\n if (values === undefined) return [];\n if (!Array.isArray(values)) return fail(field, values);\n\n return values.map((value) => normalizeOrigin(value, field));\n}\n\nfunction normalizeScript(value: unknown): string {\n if (typeof value !== 'string') return fail('scripts', value);\n\n let url: URL;\n\n try {\n url = new URL(value);\n } catch {\n return fail('scripts', value);\n }\n\n if (url.protocol !== 'http:' && url.protocol !== 'https:') return fail('scripts', value);\n\n return url.href;\n}\n\nfunction normalizeStyles(styles: Record<string, string> | undefined): Record<string, string> {\n if (styles === undefined) return {};\n if (typeof styles !== 'object' || styles === null || Array.isArray(styles)) return fail('styles', styles);\n\n const normalized: Record<string, string> = {};\n\n for (const [id, css] of Object.entries(styles)) {\n if (!ID.test(id) || typeof css !== 'string') fail('styles', id);\n\n normalized[id] = css;\n }\n\n return normalized;\n}\n\nexport function normalizeSandboxOptions(options: SandboxOptions = {}): NormalizedSandboxOptions {\n if (typeof options !== 'object' || options === null || Array.isArray(options)) fail('options', options);\n\n const lang = options.lang ?? 'en';\n\n if (!LANG.test(lang)) fail('lang', lang);\n\n if (options.nonce !== undefined && (!NONCE.test(options.nonce) || options.nonce.length === 0)) {\n fail('nonce', options.nonce);\n }\n\n if (options.title !== undefined && typeof options.title !== 'string') fail('title', options.title);\n\n const rawReadyTimeout = options.readyTimeout;\n const readyTimeout = rawReadyTimeout ?? DEFAULT_READY_TIMEOUT_MS;\n\n if (\n rawReadyTimeout !== undefined &&\n (rawReadyTimeout === null ||\n typeof rawReadyTimeout !== 'number' ||\n !Number.isFinite(rawReadyTimeout) ||\n rawReadyTimeout <= 0 ||\n rawReadyTimeout > 2 ** 31 - 1)\n ) {\n fail('readyTimeout', rawReadyTimeout);\n }\n\n if (options.scripts !== undefined && !Array.isArray(options.scripts)) fail('scripts', options.scripts);\n\n const scripts = (options.scripts ?? []).map(normalizeScript);\n\n return {\n allowedFontOrigins: normalizeOrigins(options.allowedFontOrigins, 'allowedFontOrigins'),\n allowedImageOrigins: normalizeOrigins(options.allowedImageOrigins, 'allowedImageOrigins'),\n allowedScriptOrigins: normalizeOrigins(options.allowedScriptOrigins, 'allowedScriptOrigins'),\n allowedStyleOrigins: normalizeOrigins(options.allowedStyleOrigins, 'allowedStyleOrigins'),\n lang,\n nonce: options.nonce,\n readyTimeout,\n scripts,\n styles: normalizeStyles(options.styles),\n title: options.title ?? '',\n };\n}\n\nexport function buildCspFromOptions(options: NormalizedSandboxOptions): string {\n const scriptOrigins = [\n \"'unsafe-inline'\",\n ...(options.nonce ? [`'nonce-${options.nonce}'`] : []),\n ...options.allowedScriptOrigins,\n ...options.scripts.map((script) => new URL(script).origin),\n ].join(' ');\n const styleOrigins = [\"'unsafe-inline'\", ...options.allowedStyleOrigins].join(' ');\n const imageOrigins = ['data:', ...options.allowedImageOrigins].join(' ');\n const fontOrigins = options.allowedFontOrigins.join(' ') || \"'none'\";\n\n return [\n \"default-src 'none'\",\n `script-src ${scriptOrigins}`,\n `style-src ${styleOrigins}`,\n `img-src ${imageOrigins}`,\n `font-src ${fontOrigins}`,\n \"connect-src 'none'\",\n \"form-action 'none'\",\n \"base-uri 'none'\",\n ].join('; ');\n}\n\nexport function buildCsp(options: SandboxOptions = {}): string {\n return buildCspFromOptions(normalizeSandboxOptions(options));\n}\n","import { buildBridgeScript } from './_bridge.js';\nimport { buildCspFromOptions, type NormalizedSandboxOptions, normalizeSandboxOptions } from './_policy.js';\nimport type { BridgeBootstrap } from './_protocol.js';\nimport type { SandboxOptions } from './types.js';\n\nfunction escapeCss(css: string): string {\n return css.replace(/<\\/style/gi, '\\\\3C /style');\n}\n\nfunction escapeAttr(value: string): string {\n return value.replace(/&/g, '&amp;').replace(/\"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n}\n\nfunction escapeText(value: string): string {\n return value.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n}\n\nexport function buildDocumentFromOptions(\n html: string,\n options: NormalizedSandboxOptions,\n bootstrap: BridgeBootstrap,\n): string {\n const csp = escapeAttr(buildCspFromOptions(options));\n const styles = Object.entries(options.styles)\n .map(([id, css]) => `<style id=\"${escapeAttr(id)}\">${escapeCss(css)}</style>`)\n .join('\\n');\n const scripts = options.scripts\n .map((src) => `<script crossorigin=\"anonymous\" src=\"${escapeAttr(src)}\"></script>`)\n .join('\\n');\n const nonce = options.nonce ? ` nonce=\"${escapeAttr(options.nonce)}\"` : '';\n\n return `<!doctype html>\n<html lang=\"${escapeAttr(options.lang)}\">\n<head>\n<meta http-equiv=\"Content-Security-Policy\" content=\"${csp}\">\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>${escapeText(options.title)}</title>\n${styles}\n<script${nonce}>\n${buildBridgeScript(bootstrap)}\n</script>\n</head>\n<body>\n${scripts}\n${html}\n</body>\n</html>`;\n}\n\nexport function buildDocument(html: string, options: SandboxOptions = {}): string {\n return buildDocumentFromOptions(html, normalizeSandboxOptions(options), { channel: '', generation: 0 });\n}\n","export const MSG_CUSTOM = 'custom';\nexport const MSG_ERROR = 'error';\nexport const MSG_HTML_REPLACE = 'html-replace';\nexport const MSG_READY = 'ready';\nexport const MSG_RESIZE = 'resize';\nexport const MSG_STATE_UPDATE_ALL = 'state-update-all';\nexport const MSG_STYLE_PATCH = 'style-patch';\n\nexport interface BridgeBootstrap {\n channel: string;\n generation: number;\n}\n\nexport type ProtocolMessage = Record<string, unknown> & { channel: string; generation: number; type: string };\n\nexport function isProtocolMessage(value: unknown, bootstrap: BridgeBootstrap): value is ProtocolMessage {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as { channel?: unknown }).channel === 'string' &&\n (value as { channel: string }).channel === bootstrap.channel &&\n typeof (value as { generation?: unknown }).generation === 'number' &&\n (value as { generation: number }).generation === bootstrap.generation &&\n typeof (value as { type?: unknown }).type === 'string'\n );\n}\n\nexport function createChannel(): string {\n if (typeof crypto.randomUUID === 'function') return crypto.randomUUID();\n\n const bytes = crypto.getRandomValues(new Uint32Array(4));\n\n return Array.from(bytes, (byte) => byte.toString(16).padStart(8, '0')).join('');\n}\n\nexport function envelope(bootstrap: BridgeBootstrap, message: Record<string, unknown>): ProtocolMessage {\n return { ...message, channel: bootstrap.channel, generation: bootstrap.generation } as ProtocolMessage;\n}\n","import { devOnly, warn } from './_dev.js';\nimport { buildDocumentFromOptions } from './_document.js';\nimport { normalizeSandboxOptions } from './_policy.js';\nimport {\n type BridgeBootstrap,\n createChannel,\n envelope,\n isProtocolMessage,\n MSG_CUSTOM,\n MSG_ERROR,\n MSG_HTML_REPLACE,\n MSG_READY,\n MSG_RESIZE,\n MSG_STATE_UPDATE_ALL,\n MSG_STYLE_PATCH,\n} from './_protocol.js';\nimport { SandboxTimeoutError } from './errors.js';\nimport type { SandboxHandle, SandboxMessage, SandboxOptions } from './types.js';\n\nexport function createSandbox<State extends object = Record<string, unknown>>(\n container: HTMLElement,\n options: SandboxOptions = {},\n): SandboxHandle<State> {\n const normalized = normalizeSandboxOptions(options);\n const channel = createChannel();\n const styles: Record<string, string> = { ...normalized.styles };\n const listeners = new Set<(message: SandboxMessage) => void>();\n const pendingState: Partial<State> = {};\n const controller = new AbortController();\n let iframe: HTMLIFrameElement | null = null;\n let disposed = false;\n let bridgeReady = false;\n let generation = 0;\n let readyTimeout: ReturnType<typeof setTimeout> | null = null;\n let currentRender: { reject: (error: unknown) => void; resolve: () => void } | null = null;\n\n function currentBootstrap(): BridgeBootstrap {\n return { channel, generation };\n }\n\n function clearReadyTimeout(): void {\n if (readyTimeout === null) return;\n\n clearTimeout(readyTimeout);\n readyTimeout = null;\n }\n\n function supersedePendingRender(): void {\n clearReadyTimeout();\n currentRender?.resolve();\n currentRender = null;\n bridgeReady = false;\n }\n\n function broadcast(message: SandboxMessage): void {\n for (const listener of listeners) {\n try {\n listener(message);\n } catch {\n warn('onMessage() handler threw: remaining handlers will still run.');\n }\n }\n }\n\n function handleMessage(event: MessageEvent): void {\n if (!iframe || event.source !== iframe.contentWindow) return;\n\n const bootstrap = currentBootstrap();\n\n if (!isProtocolMessage(event.data, bootstrap)) return;\n\n switch (event.data.type) {\n case MSG_CUSTOM:\n if (typeof event.data.event === 'string') {\n broadcast({ detail: event.data.detail, event: event.data.event, type: MSG_CUSTOM });\n }\n\n break;\n case MSG_ERROR:\n if (typeof event.data.message === 'string') {\n broadcast({\n message: event.data.message,\n ...(typeof event.data.stack === 'string' ? { stack: event.data.stack } : {}),\n type: MSG_ERROR,\n });\n }\n\n break;\n case MSG_READY:\n supersedePendingRender();\n bridgeReady = true;\n flushPendingState();\n break;\n case MSG_RESIZE:\n if (typeof event.data.height === 'number' && Number.isFinite(event.data.height) && event.data.height >= 0) {\n broadcast({ height: event.data.height, type: MSG_RESIZE });\n }\n\n break;\n }\n }\n\n function ensureIframe(): HTMLIFrameElement {\n if (iframe) return iframe;\n\n iframe = document.createElement('iframe');\n iframe.setAttribute('sandbox', 'allow-scripts');\n iframe.setAttribute('referrerpolicy', 'no-referrer');\n iframe.dataset.sandboxChannel = channel;\n container.appendChild(iframe);\n window.addEventListener('message', handleMessage);\n\n return iframe;\n }\n\n function dispose(): void {\n if (disposed) return;\n\n disposed = true;\n supersedePendingRender();\n controller.abort();\n\n if (iframe) {\n window.removeEventListener('message', handleMessage);\n iframe.remove();\n iframe = null;\n }\n\n listeners.clear();\n }\n\n function onMessage(handler: (message: SandboxMessage) => void): () => void {\n if (disposed) {\n warn('onMessage() called on a disposed sandbox: handler will never fire.');\n\n return () => {};\n }\n\n listeners.add(handler);\n\n return () => listeners.delete(handler);\n }\n\n function render(html: string): Promise<void> {\n if (disposed) {\n warn('render() called on a disposed sandbox.');\n\n return Promise.resolve();\n }\n\n if (!html.trim()) warn('render() called with empty HTML.');\n\n supersedePendingRender();\n generation += 1;\n\n const current = ensureIframe();\n const bootstrap = currentBootstrap();\n const documentOptions = { ...normalized, styles: { ...styles } };\n\n current.dataset.sandboxGeneration = String(generation);\n current.srcdoc = buildDocumentFromOptions(html, documentOptions, bootstrap);\n\n const promise = new Promise<void>((resolve, reject) => {\n currentRender = { reject, resolve };\n });\n\n readyTimeout = setTimeout(() => {\n if (!currentRender) return;\n\n currentRender.reject(\n new SandboxTimeoutError(\n `render() did not receive a 'ready' signal within ${normalized.readyTimeout}ms. ` +\n 'An injected script may have blocked document initialization.',\n ),\n );\n currentRender = null;\n readyTimeout = null;\n }, normalized.readyTimeout);\n\n return promise;\n }\n\n function send(message: Record<string, unknown>): void {\n iframe?.contentWindow?.postMessage(envelope(currentBootstrap(), message), '*');\n }\n\n function flushPendingState(): void {\n if (!bridgeReady || !iframe?.contentWindow || Object.keys(pendingState).length === 0) return;\n\n send({ record: { ...pendingState }, type: MSG_STATE_UPDATE_ALL });\n }\n\n function setState(update: Partial<State>): void {\n if (disposed) {\n warn('setState() called on a disposed sandbox.');\n\n return;\n }\n\n Object.assign(pendingState, update);\n\n if (bridgeReady && iframe?.contentWindow && Object.keys(update).length > 0) {\n send({ record: update, type: MSG_STATE_UPDATE_ALL });\n }\n }\n\n function replaceBody(html: string): void {\n if (disposed) {\n warn('replaceBody() called on a disposed sandbox.');\n\n return;\n }\n\n if (!bridgeReady || !iframe?.contentWindow) {\n warn('replaceBody() called before render() has resolved: bridge is not ready. Await render() first.');\n\n return;\n }\n\n send({ html, type: MSG_HTML_REPLACE });\n }\n\n function updateStyle(id: string, css: string): void {\n if (disposed) {\n warn('updateStyle() called on a disposed sandbox.');\n\n return;\n }\n\n devOnly(() => {\n if (!(id in styles)) {\n warn(`updateStyle('${id}', …): '${id}' is not a known styles key.`);\n }\n });\n\n styles[id] = css;\n\n if (bridgeReady && iframe?.contentWindow) send({ css, id, type: MSG_STYLE_PATCH });\n }\n\n return {\n get disposalSignal() {\n return controller.signal;\n },\n dispose,\n get disposed() {\n return disposed;\n },\n onMessage,\n render,\n replaceBody,\n setState,\n [Symbol.dispose]() {\n dispose();\n },\n updateStyle,\n };\n}\n"],"mappings":"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"}
package/dist/testing.js CHANGED
@@ -1,4 +1,4 @@
1
- //#region src/testing.ts
1
+ // src/testing.ts
2
2
  function createSandboxTestHelpers(e) {
3
3
  function getIframe() {
4
4
  return e.querySelector("iframe");
@@ -33,7 +33,6 @@ function createSandboxTestHelpers(e) {
33
33
  })
34
34
  };
35
35
  }
36
- //#endregion
37
36
  export { createSandboxTestHelpers };
38
37
 
39
38
  //# sourceMappingURL=testing.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vielzeug/sandbox",
3
- "version": "26.11.4",
3
+ "version": "26.11.6",
4
4
  "description": "Sandboxed iframe runtime with typed postMessage state bridge",
5
5
  "repository": {
6
6
  "type": "git",
@@ -42,20 +42,20 @@
42
42
  "registry": "https://registry.npmjs.org/"
43
43
  },
44
44
  "scripts": {
45
- "build": "vite build && pnpm --silent run build:bundle && pnpm --silent run build:types && pnpm --silent run test:types",
45
+ "build": "vite build && bun run --silent build:bundle && bun run --silent build:types && bun run --silent test:types",
46
46
  "build:bundle": "vite build --config vite.bundle.config.ts",
47
47
  "build:types": "tsc -p tsconfig.declarations.json",
48
48
  "fix": "biome check --write src",
49
49
  "lint": "biome ci src",
50
- "prepublishOnly": "pnpm --silent run build",
50
+ "prepublishOnly": "bun run --silent build",
51
51
  "test": "vitest",
52
52
  "test:types": "tsc -p tsconfig.type-tests.json"
53
53
  },
54
54
  "devDependencies": {
55
- "@types/node": "^26.6.5",
56
- "jsdom": "^30.1.3",
55
+ "@types/node": "^26.6.6",
56
+ "jsdom": "^31.0.0",
57
57
  "typescript": "^6.0.3",
58
- "vite": "^8.3.4",
59
- "vitest": "^5.0.5"
58
+ "vite": "^8.3.6",
59
+ "vitest": "^5.1.0"
60
60
  }
61
61
  }