diff --git a/builds/respec-w3c.js b/builds/respec-w3c.js index 1ca1480d66..961ed541a1 100644 --- a/builds/respec-w3c.js +++ b/builds/respec-w3c.js @@ -94,7 +94,7 @@ p:is(.respec-hint,.respec-occurrences){display:block;margin-top:.5em} ${Hs}

${e}

${t}
- `;const i=new Map([["labelledby",s]]);Ws(Gs,i),document.body.append(Vs,Gs),Vs.addEventListener("click",(()=>this.closeModal(n))),Vs.classList.toggle("respec-show-overlay"),Gs.hidden=!1,Qs(Gs)}};function ri(e){if("string"==typeof e)return e;const t=e.plugin?`

(plugin: "${e.plugin}")

`:"",n=e.hint?`\n${js(`

How to fix: ${us(e.hint)}`,{inline:!e.hint.includes("\n")})}\n`:"",r=Array.isArray(e.elements)?`

Occurred ${e.elements.length} times at:

\n ${js(e.elements.map(si).join("\n"))}`:"",s=e.details?`\n\n
\n${e.details}\n
\n`:"";return`${js(`**${Pr(e.message)}**`,{inline:!0})}${n}${r}${s}${t}`}function si(e){return`* [\`<${e.localName}>\`](#${e.id}) element`}async function ii(e){try{ni.show(),await async function(){"loading"===document.readyState&&await new Promise((e=>document.addEventListener("DOMContentLoaded",e)))}(),await As(e)}finally{ni.enable()}}document.addEventListener("keydown",(e=>{"Escape"===e.key&&ni.closeModal()})),window.respecUI=ni,hs("error",(e=>ni.error(e))),hs("warn",(e=>ni.warning(e))),window.addEventListener("error",(e=>{console.error(e.error,e.message,e)}));const oi=[Promise.resolve().then((function(){return ci})),Promise.resolve().then((function(){return s})),Promise.resolve().then((function(){return pi})),Promise.resolve().then((function(){return io})),Promise.resolve().then((function(){return uo})),Promise.resolve().then((function(){return go})),Promise.resolve().then((function(){return $o})),Promise.resolve().then((function(){return Lo})),Promise.resolve().then((function(){return qs})),Promise.resolve().then((function(){return Ao})),Promise.resolve().then((function(){return Io})),Promise.resolve().then((function(){return jo})),Promise.resolve().then((function(){return Qi})),Promise.resolve().then((function(){return Uo})),Promise.resolve().then((function(){return qo})),Promise.resolve().then((function(){return Bo})),Promise.resolve().then((function(){return Go})),Promise.resolve().then((function(){return ic})),Promise.resolve().then((function(){return cc})),Promise.resolve().then((function(){return yc})),Promise.resolve().then((function(){return wc})),Promise.resolve().then((function(){return xc})),Promise.resolve().then((function(){return Rc})),Promise.resolve().then((function(){return Nc})),Promise.resolve().then((function(){return Oc})),Promise.resolve().then((function(){return zc})),Promise.resolve().then((function(){return cl})),Promise.resolve().then((function(){return gl})),Promise.resolve().then((function(){return $a})),Promise.resolve().then((function(){return Ol})),Promise.resolve().then((function(){return Ql})),Promise.resolve().then((function(){return _l})),Promise.resolve().then((function(){return cu})),Promise.resolve().then((function(){return Ia})),Promise.resolve().then((function(){return ku})),Promise.resolve().then((function(){return xu})),Promise.resolve().then((function(){return _o})),Promise.resolve().then((function(){return Cu})),Promise.resolve().then((function(){return Su})),Promise.resolve().then((function(){return Tu})),Promise.resolve().then((function(){return Du})),Promise.resolve().then((function(){return ju})),Promise.resolve().then((function(){return Mu})),Promise.resolve().then((function(){return Hu})),Promise.resolve().then((function(){return ld})),Promise.resolve().then((function(){return yd})),Promise.resolve().then((function(){return $d})),Promise.resolve().then((function(){return _d})),Promise.resolve().then((function(){return Ld})),Promise.resolve().then((function(){return Od})),Promise.resolve().then((function(){return jd})),Promise.resolve().then((function(){return Fd})),Promise.resolve().then((function(){return Jd})),Promise.resolve().then((function(){return Kc})),Promise.resolve().then((function(){return tp})),Promise.resolve().then((function(){return op})),Promise.resolve().then((function(){return cp})),Promise.resolve().then((function(){return up})),Promise.resolve().then((function(){return yp})),Promise.resolve().then((function(){return kp})),Promise.resolve().then((function(){return _p})),Promise.resolve().then((function(){return Tp})),Promise.resolve().then((function(){return Rp})),Promise.resolve().then((function(){return Lp})),Promise.resolve().then((function(){return Np})),Promise.resolve().then((function(){return zp})),Promise.resolve().then((function(){return qp})),Promise.resolve().then((function(){return Hp})),Promise.resolve().then((function(){return Jp})),Promise.resolve().then((function(){return Qp})),Promise.resolve().then((function(){return nh})),Promise.resolve().then((function(){return ih})),Promise.resolve().then((function(){return uh})),Promise.resolve().then((function(){return gh})),Promise.resolve().then((function(){return wh})),Promise.resolve().then((function(){return $h})),Promise.resolve().then((function(){return _h})),Promise.resolve().then((function(){return Lh}))];Promise.all(oi).then((e=>ii(e))).catch((e=>console.error(e)));var ai=Object.freeze({__proto__:null,default:'// ReSpec Worker\n"use strict";\n// hljs is either inlined by core/worker.js (preferred) or loaded below via\n// importScripts as a fallback when the inline fetch was not possible.\nif (typeof self.hljs === "undefined" && self.RESPEC_HIGHLIGHT_URL) {\n try {\n importScripts(self.RESPEC_HIGHLIGHT_URL);\n } catch (err) {\n console.error("Network error loading highlighter", err);\n }\n}\n\nself.addEventListener("message", ({ data }) => {\n switch (data.action) {\n case "highlight-load-lang": {\n const { langURL, langScript, propName, lang } = data;\n console.warn(\n `[ReSpec] The "highlight-load-lang" worker action is deprecated ` +\n `and will be removed in a future version. ` +\n `To migrate, fetch your language script in the main thread and ` +\n `send the text as "langScript" instead of "langURL". ` +\n `The "langURL" path may fail in Firefox. ` +\n `See https://github.com/speced/respec/issues/5228`\n );\n try {\n if (langScript) {\n const blob = new Blob([langScript], {\n type: "application/javascript",\n });\n const objectURL = URL.createObjectURL(blob);\n try {\n importScripts(objectURL);\n } finally {\n URL.revokeObjectURL(objectURL);\n }\n } else if (langURL) {\n const { protocol, hostname } = new URL(langURL);\n const isSecure =\n protocol === "https:" ||\n (protocol === "http:" &&\n (hostname === "localhost" ||\n hostname === "127.0.0.1" ||\n hostname === "[::1]"));\n if (!isSecure) {\n throw new Error(\n `langURL must be https: or http: on localhost, got "${langURL}"`\n );\n }\n importScripts(langURL);\n } else {\n throw new Error(\n `No langScript or langURL provided for language "${lang}"`\n );\n }\n if (typeof self[propName] === "function") {\n self.hljs.registerLanguage(lang, self[propName]);\n } else {\n throw new Error(\n `Language definer "${propName}" is not a function on self`\n );\n }\n } catch (err) {\n console.error("Failed to load or register language", lang, err);\n }\n delete data.langScript;\n delete data.langURL;\n break;\n }\n case "highlight": {\n const { code } = data;\n const langs = data.languages?.length ? data.languages : undefined;\n try {\n const { value, language } = self.hljs.highlightAuto(code, langs);\n Object.assign(data, { value, language });\n } catch (err) {\n console.error("Could not transform some code?", err);\n Object.assign(data, { value: code, language: "" });\n }\n break;\n }\n }\n self.postMessage(data);\n});\n'});var ci=Object.freeze({__proto__:null,name:"core/location-hash",run:function(){window.location.hash&&document.respec.ready.then((()=>{let e=decodeURIComponent(window.location.hash).slice(1);const t=document.getElementById(e),n=/\W/.test(e);if(!t&&n){const t=e.replace(/[\W]+/gim,"-").replace(/^-+/,"").replace(/-+$/,"");document.getElementById(t)&&(e=t)}window.location.hash=`#${e}`}))}});const li="w3c/group",ui="https://respec.org/w3c/groups/";async function di(e){let t="",n=e;e.includes("/")&&([t,n]=e.split("/",2));const r=new URL(`${n}/${t}`,ui),s=await zr(r.href);if(s.ok){const e=await s.json(),{id:t,name:n,patentURI:r,patentPolicy:i,type:o,wgURI:a}=e;return{wg:n,wgId:t,wgURI:a,wgPatentURI:r,wgPatentPolicy:i,groupType:o}}const i=await s.text();let o,a=`Failed to fetch group details (HTTP: ${s.status}).`;409===s.status?[a,o]=i.split("\n",2):404===s.status&&(o=ls`See the list of [supported group names](https://respec.org/w3c/groups/) to use with the ${"[group]"} configuration option.`),ns(a,li,{hint:o})}var pi=Object.freeze({__proto__:null,name:li,run:async function(e){if(!e.group)return;const{group:t}=e,n=Array.isArray(t)?await async function(e){const t=await Promise.all(e.map(di)),n={wg:[],wgId:[],wgURI:[],wgPatentURI:[],wgPatentPolicy:[],groupType:[]};for(const e of t.filter(Boolean))for(const t of Object.keys(n))n[t].push(e[t]);return n}(t):await di(t);Object.assign(e,n)}});function hi(e){if(!e.key){const t="Found a link without `key` attribute in the configuration. See dev console.";return rs(t,"core/templates/show-link"),void console.warn(t,e)}return sr` + `;const i=new Map([["labelledby",s]]);Ws(Gs,i),document.body.append(Vs,Gs),Vs.addEventListener("click",(()=>this.closeModal(n))),Vs.classList.toggle("respec-show-overlay"),Gs.hidden=!1,Qs(Gs)}};function ri(e){if("string"==typeof e)return e;const t=e.plugin?`

(plugin: "${e.plugin}")

`:"",n=e.hint?`\n${js(`

How to fix: ${us(e.hint)}`,{inline:!e.hint.includes("\n")})}\n`:"",r=Array.isArray(e.elements)?`

Occurred ${e.elements.length} times at:

\n ${js(e.elements.map(si).join("\n"))}`:"",s=e.details?`\n\n
\n${e.details}\n
\n`:"";return`${js(`**${Pr(e.message)}**`,{inline:!0})}${n}${r}${s}${t}`}function si(e){return`* [\`<${e.localName}>\`](#${e.id}) element`}async function ii(e){try{ni.show(),await async function(){"loading"===document.readyState&&await new Promise((e=>document.addEventListener("DOMContentLoaded",e)))}(),await As(e)}finally{ni.enable()}}document.addEventListener("keydown",(e=>{"Escape"===e.key&&ni.closeModal()})),window.respecUI=ni,hs("error",(e=>ni.error(e))),hs("warn",(e=>ni.warning(e))),window.addEventListener("error",(e=>{console.error(e.error,e.message,e)}));const oi=[Promise.resolve().then((function(){return ci})),Promise.resolve().then((function(){return s})),Promise.resolve().then((function(){return pi})),Promise.resolve().then((function(){return io})),Promise.resolve().then((function(){return uo})),Promise.resolve().then((function(){return go})),Promise.resolve().then((function(){return $o})),Promise.resolve().then((function(){return Lo})),Promise.resolve().then((function(){return qs})),Promise.resolve().then((function(){return Ao})),Promise.resolve().then((function(){return Io})),Promise.resolve().then((function(){return jo})),Promise.resolve().then((function(){return Qi})),Promise.resolve().then((function(){return Uo})),Promise.resolve().then((function(){return qo})),Promise.resolve().then((function(){return Bo})),Promise.resolve().then((function(){return Go})),Promise.resolve().then((function(){return ic})),Promise.resolve().then((function(){return cc})),Promise.resolve().then((function(){return yc})),Promise.resolve().then((function(){return wc})),Promise.resolve().then((function(){return xc})),Promise.resolve().then((function(){return Rc})),Promise.resolve().then((function(){return Nc})),Promise.resolve().then((function(){return Oc})),Promise.resolve().then((function(){return zc})),Promise.resolve().then((function(){return cl})),Promise.resolve().then((function(){return gl})),Promise.resolve().then((function(){return $a})),Promise.resolve().then((function(){return Ol})),Promise.resolve().then((function(){return Ql})),Promise.resolve().then((function(){return _l})),Promise.resolve().then((function(){return cu})),Promise.resolve().then((function(){return Ia})),Promise.resolve().then((function(){return ku})),Promise.resolve().then((function(){return xu})),Promise.resolve().then((function(){return _o})),Promise.resolve().then((function(){return Cu})),Promise.resolve().then((function(){return Su})),Promise.resolve().then((function(){return Tu})),Promise.resolve().then((function(){return Du})),Promise.resolve().then((function(){return ju})),Promise.resolve().then((function(){return Mu})),Promise.resolve().then((function(){return Hu})),Promise.resolve().then((function(){return ld})),Promise.resolve().then((function(){return yd})),Promise.resolve().then((function(){return $d})),Promise.resolve().then((function(){return _d})),Promise.resolve().then((function(){return Ld})),Promise.resolve().then((function(){return Od})),Promise.resolve().then((function(){return jd})),Promise.resolve().then((function(){return Fd})),Promise.resolve().then((function(){return Jd})),Promise.resolve().then((function(){return Kc})),Promise.resolve().then((function(){return tp})),Promise.resolve().then((function(){return op})),Promise.resolve().then((function(){return cp})),Promise.resolve().then((function(){return up})),Promise.resolve().then((function(){return yp})),Promise.resolve().then((function(){return kp})),Promise.resolve().then((function(){return _p})),Promise.resolve().then((function(){return Tp})),Promise.resolve().then((function(){return Rp})),Promise.resolve().then((function(){return Lp})),Promise.resolve().then((function(){return Np})),Promise.resolve().then((function(){return zp})),Promise.resolve().then((function(){return qp})),Promise.resolve().then((function(){return Hp})),Promise.resolve().then((function(){return Jp})),Promise.resolve().then((function(){return Qp})),Promise.resolve().then((function(){return nh})),Promise.resolve().then((function(){return ih})),Promise.resolve().then((function(){return uh})),Promise.resolve().then((function(){return gh})),Promise.resolve().then((function(){return wh})),Promise.resolve().then((function(){return $h})),Promise.resolve().then((function(){return _h})),Promise.resolve().then((function(){return Lh}))];Promise.all(oi).then((e=>ii(e))).catch((e=>console.error(e)));var ai=Object.freeze({__proto__:null,default:'// ReSpec Worker\r\n"use strict";\r\n// hljs is either inlined by core/worker.js (preferred) or loaded below via\r\n// importScripts as a fallback when the inline fetch was not possible.\r\nif (typeof self.hljs === "undefined" && self.RESPEC_HIGHLIGHT_URL) {\r\n try {\r\n importScripts(self.RESPEC_HIGHLIGHT_URL);\r\n } catch (err) {\r\n console.error("Network error loading highlighter", err);\r\n }\r\n}\r\n\r\nself.addEventListener("message", ({ data }) => {\r\n switch (data.action) {\r\n case "highlight-load-lang": {\r\n const { langURL, langScript, propName, lang } = data;\r\n console.warn(\r\n `[ReSpec] The "highlight-load-lang" worker action is deprecated ` +\r\n `and will be removed in a future version. ` +\r\n `To migrate, fetch your language script in the main thread and ` +\r\n `send the text as "langScript" instead of "langURL". ` +\r\n `The "langURL" path may fail in Firefox. ` +\r\n `See https://github.com/speced/respec/issues/5228`\r\n );\r\n try {\r\n if (langScript) {\r\n const blob = new Blob([langScript], {\r\n type: "application/javascript",\r\n });\r\n const objectURL = URL.createObjectURL(blob);\r\n try {\r\n importScripts(objectURL);\r\n } finally {\r\n URL.revokeObjectURL(objectURL);\r\n }\r\n } else if (langURL) {\r\n const { protocol, hostname } = new URL(langURL);\r\n const isSecure =\r\n protocol === "https:" ||\r\n (protocol === "http:" &&\r\n (hostname === "localhost" ||\r\n hostname === "127.0.0.1" ||\r\n hostname === "[::1]"));\r\n if (!isSecure) {\r\n throw new Error(\r\n `langURL must be https: or http: on localhost, got "${langURL}"`\r\n );\r\n }\r\n importScripts(langURL);\r\n } else {\r\n throw new Error(\r\n `No langScript or langURL provided for language "${lang}"`\r\n );\r\n }\r\n if (typeof self[propName] === "function") {\r\n self.hljs.registerLanguage(lang, self[propName]);\r\n } else {\r\n throw new Error(\r\n `Language definer "${propName}" is not a function on self`\r\n );\r\n }\r\n } catch (err) {\r\n console.error("Failed to load or register language", lang, err);\r\n }\r\n delete data.langScript;\r\n delete data.langURL;\r\n break;\r\n }\r\n case "highlight": {\r\n const { code } = data;\r\n const langs = data.languages?.length ? data.languages : undefined;\r\n try {\r\n const { value, language } = self.hljs.highlightAuto(code, langs);\r\n Object.assign(data, { value, language });\r\n } catch (err) {\r\n console.error("Could not transform some code?", err);\r\n Object.assign(data, { value: code, language: "" });\r\n }\r\n break;\r\n }\r\n }\r\n self.postMessage(data);\r\n});\r\n'});var ci=Object.freeze({__proto__:null,name:"core/location-hash",run:function(){window.location.hash&&document.respec.ready.then((()=>{let e=decodeURIComponent(window.location.hash).slice(1);const t=document.getElementById(e),n=/\W/.test(e);if(!t&&n){const t=e.replace(/[\W]+/gim,"-").replace(/^-+/,"").replace(/-+$/,"");document.getElementById(t)&&(e=t)}window.location.hash=`#${e}`}))}});const li="w3c/group",ui="https://respec.org/w3c/groups/";async function di(e){let t="",n=e;e.includes("/")&&([t,n]=e.split("/",2));const r=new URL(`${n}/${t}`,ui),s=await zr(r.href);if(s.ok){const e=await s.json(),{id:t,name:n,patentURI:r,patentPolicy:i,type:o,wgURI:a}=e;return{wg:n,wgId:t,wgURI:a,wgPatentURI:r,wgPatentPolicy:i,groupType:o}}const i=await s.text();let o,a=`Failed to fetch group details (HTTP: ${s.status}).`;409===s.status?[a,o]=i.split("\n",2):404===s.status&&(o=ls`See the list of [supported group names](https://respec.org/w3c/groups/) to use with the ${"[group]"} configuration option.`),ns(a,li,{hint:o})}var pi=Object.freeze({__proto__:null,name:li,run:async function(e){if(!e.group)return;const{group:t}=e,n=Array.isArray(t)?await async function(e){const t=await Promise.all(e.map(di)),n={wg:[],wgId:[],wgURI:[],wgPatentURI:[],wgPatentPolicy:[],groupType:[]};for(const e of t.filter(Boolean))for(const t of Object.keys(n))n[t].push(e[t]);return n}(t):await di(t);Object.assign(e,n)}});function hi(e){if(!e.key){const t="Found a link without `key` attribute in the configuration. See dev console.";return rs(t,"core/templates/show-link"),void console.warn(t,e)}return sr`
${e.key}
${e.data?e.data.map(fi):fi(e)} `}function fi(e){return sr`
@@ -1021,20 +1021,23 @@ pre.cddl dfn{font-style:normal;font-weight:700} .cddlHeader a.self-link:focus-visible,pre.cddl a:focus-visible,pre.cddl dfn:focus-visible{outline:2px solid var(--cddl-focus);outline-offset:2px} .respec-button-copy-paste:focus-visible{outline:2px solid var(--cddl-focus);outline-offset:2px} pre.cddl dfn[data-dfn-type=cddl-type]{color:var(--cddl-type-dfn)} -pre.cddl dfn[data-dfn-type=cddl-key]{color:var(--cddl-key-dfn)}`;const ul="core/cddl",dl=new Set(["any","uint","nint","int","bstr","bytes","tstr","text","tdate","time","number","biguint","bignint","bigint","integer","unsigned","decfrac","bigfloat","eb64url","eb64legacy","eb16","encoded-cbor","uri","b64url","b64legacy","regexp","mime-message","cbor-any","float16","float32","float64","float16-32","float32-64","float","false","true","bool","nil","null","undefined"]);function pl(e){return e.toLowerCase().replace(/^"(.*)"$/,"$1").replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}function hl(e){let t=e.parentNode;for(;t;){if(t instanceof pr)return t.name?.name||null;t=t.parentNode}return null}const fl=String.raw;class ml extends dr{#e=null;#t;constructor(e){super(),this.#t=e}serializeName(e,t){const n=this.#t;if(function(e){let t=e.parentNode;for(;t;){if(t instanceof fr)return!0;if(t instanceof pr)return!1;t=t.parentNode}return!1}(t)){const r=hl(t);return r&&(n.genericParams.has(r)||n.genericParams.set(r,new Set),n.genericParams.get(r)?.add(e)),fl`${Pr(e)}`}if(function(e){let t=e.parentNode;for(;t;){if(t instanceof mr)return!0;if(t instanceof pr)return!1;t=t.parentNode}return!1}(t))return this.#n(e);if(function(e){return e.parentNode instanceof pr}(t))return this.#e=e,this.#r(e);if(function(e){return e.parentNode instanceof hr}(t))return this.#s(e);const r=hl(t)||this.#e;return r&&n.genericParams.has(r)&&n.genericParams.get(r)?.has(e)?fl`${Pr(e)}`:this.#n(e)}#r(e){const t=this.#t,n=`cddl-type-${pl(e)}`,r=`cddl-type:${e}`;return t.definitions.has(r)?fl`${Pr(e)}`}if(function(e){let t=e.parentNode;for(;t;){if(t instanceof mr)return!0;if(t instanceof pr)return!1;t=t.parentNode}return!1}(t))return this.#n(e);if(function(e){return e.parentNode instanceof pr}(t))return this.#e=e,this.#r(e);if(function(e){return e.parentNode instanceof hr}(t))return this.#s(e);const r=hl(t)||this.#e;return r&&n.genericParams.has(r)&&n.genericParams.get(r)?.has(e)?fl`${Pr(e)}`:this.#n(e)}#r(e){const t=this.#t,n=`cddl-type-${pl(e)}`,r=`cddl-type:${e}`;if(t.definitions.has(r))return fl`${Pr(e)}`:t.proseDfns.has(n)?(t.definitions.set(r,{type:"cddl-type",for:null,id:n}),fl``;const s=t.proseDfns.get(n);return s?(t.definitions.set(r,{type:"cddl-type",for:null,id:s}),fl`${Pr(e)}`):(t.definitions.set(r,{type:"cddl-type",for:null,id:n}),fl`${Pr(e)}`)}#s(e){const t=this.#t,n=this.#e;if(!n)return fl`${Pr(e)}`;const r=`cddl-key-${pl(n)}-${pl(e)}`,s=`cddl-key:${n}/${e}`;return t.definitions.has(s)?fl``)}#s(e){const t=this.#t,n=this.#e;if(!n)return fl`${Pr(e)}`;const r=`cddl-key-${pl(n)}-${pl(e)}`,s=`cddl-key:${n}/${e}`;if(t.definitions.has(s))return fl`${Pr(e)}`:t.proseDfns.has(r)?(t.definitions.set(s,{type:"cddl-key",for:n,id:r}),fl``;const i=t.proseDfns.get(r);return i?(t.definitions.set(s,{type:"cddl-key",for:n,id:i}),fl`${Pr(i)}`:fl`${Pr(i)}`}if(s.proseDfns.has(n))return s.definitions.set(r,{type:"cddl-value",for:e,id:n}),fl``:fl`${Pr(i)}`}const o=s.proseDfns.get(n);if(o)return s.definitions.set(r,{type:"cddl-value",for:e,id:o}),fl`${Pr(i)}`}}switch(r.type){case"text":return fl`${Pr(i)}`;case"number":case"float":return fl`${Pr(i)}`;case"bytes":case"hex":case"base64":return fl`${Pr(i)}`;default:return Pr(i)}}#i(e){const t=e.parentNode;return!!t&&t instanceof gr}serializeToken(e,t){const n=e.type,r=e.serialize();switch(n){case"COMMENT":return this.#o(r);case"CTLOP":return this.#a(r);case"=":case"/=":case"//=":case"/":case"//":case"=>":case"..":case"...":return this.#c(r);case"?":case"*":case"+":return t instanceof br?this.#l(r):r;default:return r}}#o(e){return e.replace(/(;[^\n]*)/g,'$1')}#a(e){return e.replace(/(\.\w+)/g,'$1')}#c(e){const t=e.match(/^(\s*)(.*?)(\s*)$/s);return t?`${t[1]}${t[2]}${t[3]}`:fl`${e}`}#l(e){const t=e.match(/^(\s*)(.*?)(\s*)$/s);return t?`${t[1]}${t[2]}${t[3]}`:fl`${e}`}}var gl=Object.freeze({__proto__:null,name:ul,run:async function(){const e=document.querySelectorAll("pre.cddl:not([data-no-cddl])");if(!e.length)return;const t=document.createElement("style");t.textContent=ll;const n=document.querySelector("head link, head > *:last-child");n?n.before(t):document.head.append(t);const r=e=>new ur(e).parse(),s={definitions:new Map,proseDfns:new Set,genericParams:new Map};var i,o,a,c;i=document,o=s.proseDfns,i.querySelectorAll("dfn[cddl-type], dfn[cddl-key], dfn[cddl-value]").forEach((e=>{const t=["cddl-type","cddl-key","cddl-value"].find((t=>e.hasAttribute(t)));e.dataset.dfnType=t,e.removeAttribute(t);const n=e.getAttribute("for");n&&(e.dataset.dfnFor=n,e.removeAttribute("for"));const r=e.textContent.trim(),s=n?`${pl(n)}-`:"",i=`cddl-${t.replace("cddl-","")}-${s}${pl(r)}`;e.id||=i,o.add(e.id),pc(e,[r])})),e.forEach((e=>function(e,t,n){const r=e.textContent;if(r.trim())try{const s=t(r),i=new ml(n),o=s.serialize(i),a=document.createElement("code");a.innerHTML=o,e.textContent="",e.append(a),e.classList.add("def","highlight"),qr(e,"cddl-block");const c=document.createElement("span");c.className="cddlHeader",c.innerHTML=`CDDL`;const l=Gc(".cddlHeader");c.append(l),e.prepend(c)}catch(t){ns(`CDDL processing error: ${t instanceof Error?t.message:String(t)}`,ul,{elements:[e],hint:'Check the CDDL syntax in the `
` block.'})}}(e,r,s))),a=document.body,c=s.definitions,a.querySelectorAll("[data-cddl-pending]").forEach((e=>{const t=`cddl-type:${e.dataset.cddlPending}`;if(c.has(t)){const n=c.get(t);if(!n)return;const r=a.ownerDocument.createElement("a");r.href=`#${n.id}`,r.className="cddl-name",r.dataset.linkType="cddl-type",r.textContent=e.textContent,e.replaceWith(r)}})),document.querySelectorAll("[data-cddl-pending]").forEach((e=>{rs(`No CDDL definition found for \`${e.getAttribute("data-cddl-pending")}\`.`,ul,{elements:[e],hint:"Check for typos in the type name."})})),function(e,t){e.querySelectorAll('a[data-link-type^="cddl-"]:not([href]):not([data-cite])').forEach((e=>{const n=e.getAttribute("data-link-type"),r=e.getAttribute("data-xref-for")||e.getAttribute("data-link-for")||"",s=e.textContent.trim();if("cddl-type"!==n&&"cddl-key"!==n&&"cddl-value"!==n)return;const i={"cddl-type":`cddl-type:${s}`,"cddl-key":`cddl-key:${r}/${s}`,"cddl-value":`cddl-value:${r}/${s}`}[n];if(t.has(i)){const n=t.get(i);if(!n)return;e.setAttribute("href",`#${n.id}`),e.classList.add("internalDFN")}else rs(`CDDL ${n}: no definition found for \`${s}\`${r?`, for \`${r}\`,`:""} in any \`
\` block.`,ul,{elements:[e]}),e.setAttribute("data-no-link-to-dfn","")}))}(document,s.definitions),function(e,t){t.forEach((t=>{const n=e.getElementById(t.id);"dfn"===n?.localName&&pc(n,[n.textContent.trim()])}))}(document,s.definitions),Vc(),hs("beforesave",(e=>{e.querySelectorAll("[data-cddl-pending]").forEach((e=>e.removeAttribute("data-cddl-pending")))}))}});const bl="core/data-cite",yl="__SPEC__";async function wl(e){const{key:t,frag:n,path:r,href:s}=e;let i="",o="";if(t===yl)i=document.location.href;else{const e=await ka(t);if(!e)return null;i=e.href??"",o=e.title}if(s)i=s;else{if(r){const e=r.startsWith("/")?`.${r}`:r;i=new URL(e,i).href}n&&(i=new URL(n,i).href)}return{href:i,title:o}}function vl(e,t,n){const{href:r,title:s}=t,i=!n.path&&!n.frag;switch(e.localName){case"a":{const t=e;if(""===t.textContent&&"the-empty-string"!==t.dataset.lt&&(t.textContent=s),t.href=r,i){const e=document.createElement("cite");t.replaceWith(e),e.append(t)}break}case"dfn":{const t=document.createElement("a");if(t.href=r,t.dataset.cite=n.key,t.dataset.citePath=n.path,t.dataset.citeFrag=n.frag,e.textContent?Vr(e,t):(t.textContent=s,e.append(t)),i){const n=document.createElement("cite");n.append(t),e.append(n)}if("export"in e.dataset){ns("Exporting a linked external definition is not allowed.",bl,{hint:"Please remove the `data-export` attribute.",elements:[e]}),delete e.dataset.export}e.classList.add("externalDFN"),e.dataset.noExport="";break}}}function kl(e){return t=>{const n=t.search(e);return-1!==n?t.substring(n):""}}const $l=kl("#"),xl=kl("/");function Cl(e){const{dataset:t}=e,{cite:n,citeFrag:r,citePath:s,citeHref:i}=t;if((n??"").startsWith("#")&&!r){const r=e.parentElement?.closest('[data-cite]:not([data-cite^="#"])')??null,{key:s,isNormative:i}=r?Cl(r):{key:yl,isNormative:!1};return t.cite=i?s:`?${s}`,t.citeFrag=(n??"").replace("#",""),Cl(e)}const o=r?`#${r}`:$l(n??""),a=s||xl(n??"").split("#")[0],{type:c}=Gr(n??"",e),l="normative"===c,u=/^[?|!]/.test(n??"");return{key:(n??"").split(/[/|#]/)[0].substring(Number(u)),isNormative:l,frag:o,path:a,href:i}}function Sl(e){const t=["data-cite","data-cite-frag","data-cite-path"];e.querySelectorAll("a[data-cite], dfn[data-cite]").forEach((e=>t.forEach((t=>e.removeAttribute(t)))))}var _l=Object.freeze({__proto__:null,THIS_SPEC:yl,name:bl,run:async function(){const e=document.querySelectorAll("dfn[data-cite]:not([data-cite='']), a[data-cite]:not([data-cite=''])");await async function(e){const t=e.map(Cl).map((async e=>({entry:e,result:await ka(e.key)}))),n=(await Promise.all(t)).filter((({result:e})=>null===e)).map((({entry:{key:e}})=>e)),r=await va(n);r&&Object.assign(ma,r)}([...e]);const t=new Map,n=new Map;for(const r of e){t.set(r,r.dataset.cite);const e=Cl(r);n.set(r,e)}for(const r of e){const e=t.get(r),s=n.get(r),i=await wl(s);if(i)r.dataset.citeFrag&&r.dataset.matchedText||!r.dataset.lt||"the-empty-string"===r.dataset.lt||""!==r.textContent||(r.textContent=r.dataset.lt,delete r.dataset.lt),vl(r,i,s);else{const t=`Couldn't find a match for "${e}"`;r.dataset.matchedText&&(r.textContent=r.dataset.matchedText),rs(t,bl,{elements:[r]})}}hs("beforesave",Sl)},toCiteDetails:Cl});const Tl="core/link-to-dfn",Rl=[],El={en:{duplicateMsg:e=>`Duplicate definition(s) of '${e}'`,duplicateTitle:"This is defined more than once in the document."},ja:{duplicateMsg:e=>`'${e}' の重複定義`,duplicateTitle:"この文書内で複数回定義されています."},de:{duplicateMsg:e=>`Mehrfache Definition von '${e}'`,duplicateTitle:"Das Dokument enthält mehrere Definitionen dieses Eintrags."},zh:{duplicateMsg:e=>`'${e}' 的重复定义`,duplicateTitle:"在文档中有重复的定义。"},cs:{duplicateMsg:e=>`Duplicitní definice '${e}'`,duplicateTitle:"Toto je v dokumentu definováno vícekrát."}},Ll=Ir(El);function Al(e){const t=new Map,n=[];for(const r of dc.get(e)??[]){const{dfnType:s="dfn"}=r.dataset,i=r.dataset.dfnFor?.split(",").map((e=>e.trim()))??[""];for(const o of i){if(t.has(o)&&t.get(o)?.has(s)){const e=t.get(o)?.get(s),i="dfn"===e?.localName,a="dfn"===r.localName,c=s===(e?.dataset.dfnType||"dfn"),l=!o&&!e?.dataset.dfnFor||e?.dataset.dfnFor?.split(",").map((e=>e.trim())).includes(o);if(i&&a&&c&&l){n.push(r);continue}}t.has(o)||t.set(o,new Map),t.get(o)?.set(s,r),("idl"in r.dataset||"dfn"!==s)&&t.get(o)?.set("idl",r),Fr(r,"dfn",e)}}return{result:t,duplicates:n}}function Pl(e,t){const n=function(e){const t=e.closest("[data-link-for]"),n=t?t.dataset.linkFor??"":"",r=Br(e).reduce(((e,r)=>{const s=r.split(".");2===s.length&&e.push({for:s[0],title:s[1]}),e.push({for:n,title:r}),t||e.push({for:r,title:r}),""!==n&&e.push({for:"",title:r});const i=r.replace(yr,"");return i!==r&&""!==i&&(e.push({for:n,title:i}),t||e.push({for:i,title:i}),""!==n&&e.push({for:"",title:i})),e}),[]);return r}(e),r=n.find((e=>t.has(e.title)&&t.get(e.title)?.has(e.for)));if(!r)return;const s=t.get(r.title)?.get(r.for),{linkType:i}=e.dataset;if(i){for(const e of i.split("|"))if(s?.get(e))return s.get(e);return s?.get("dfn")}{const e=r.for?"idl":"dfn";return s?.get(e)||s?.get("idl")}}function Nl(e,t,n){let r=!1;const{linkFor:s}=e.dataset,{dfnFor:i}=t.dataset;if(t.dataset.cite)e.dataset.cite=t.dataset.cite;else if(s&&!n.get(s)&&i&&!i.split(",").map((e=>e.trim())).includes(s))r=!0;else if(t.classList.contains("externalDFN")){const n=t.dataset.lt?t.dataset.lt.split("|"):[];e.dataset.lt=n[0]||t.textContent,r=!0}else"partial"!==e.dataset.idl?(e.href=`#${t.id}`,e.classList.add("internalDFN")):r=!0;return e.hasAttribute("data-link-type")||(e.dataset.linkType="idl"in t.dataset?"idl":"dfn"),function(e){if(e.closest("code,pre"))return!0;if(1!==e.childNodes.length)return!1;const[t]=e.childNodes;return"code"===t.localName}(t)&&function(e,t){const n=e.textContent.trim(),r=t.dataset.hasOwnProperty("idl"),s=Il(e)&&Il(t,n);r&&!s||Vr(e,document.createElement("code"))}(e,t),!r}function Il(e,t=""){if("a"===e.localName){if(!e.querySelector("code"))return!0}else{const{dataset:n}=e;if(e.textContent.trim()===t)return!0;if(n.title===t)return!0;if(n.lt||n.localLt){const e=[];return n.lt&&e.push(...n.lt.split("|")),n.localLt&&e.push(...n.localLt.split("|")),e.includes(t)}}return!1}function Dl(e){e.forEach((e=>{const t=`Found linkless \`\` element with text "${e.textContent}" but no matching \`\``,n=e.closest("[data-link-for]"),r=`Add a matching \`\` element, ${ls`use ${"[data-cite]"} to link to an external definition, or enable ${"[xref]"} for automatic cross-spec linking.`}${n?` This link is inside a \`data-link-for="${n.dataset.linkFor}"\` section — \`[=term=]\` links are scoped to that context. To link to a global concept instead, either add \`data-link-for=""\` on this \`\` or move it outside the scoped section.`:""}`;rs(t,Tl,{title:"Linking error: no matching ``",hint:r,elements:[e]})}))}var Ol=Object.freeze({__proto__:null,name:Tl,possibleExternalLinks:Rl,run:async function(e){const t=function(){const e=new es;for(const t of dc.keys()){const{result:n,duplicates:r}=Al(t);e.set(t,n),r.length>0&&ns(Ll.duplicateMsg(t),Tl,{title:Ll.duplicateTitle,elements:r})}return e}(),n=[],r=document.querySelectorAll("a[data-cite='']:not([data-no-link-to-dfn]), a:not([href]):not([data-cite]):not([data-no-link-to-dfn]):not(.logo):not(.externalDFN)");for(const e of r){if(!e.dataset?.linkType&&e.dataset?.xrefType){Rl.push(e);continue}const r=Pl(e,t);if(r){Nl(e,r,t)||Rl.push(e)}else""===e.dataset.cite?n.push(e):Rl.push(e)}Dl(n),function(e){const{shortName:t=""}=e,n=t?new RegExp(String.raw`^([?!])?${vr(t)}\b([^-])`,"i"):null,r=document.querySelectorAll("dfn[data-cite]:not([data-cite='']), a[data-cite]:not([data-cite=''])");for(const t of r){n&&(t.dataset.cite=(t.dataset.cite??"").replace(n,`$1${yl}$2`));const{key:r,isNormative:s}=Cl(t);r!==yl&&(s||e.normativeReferences.has(r)?(e.normativeReferences.add(r),e.informativeReferences.delete(r)):e.informativeReferences.add(r))}}(e),e.xref||Dl(Rl)}});const jl="xrefs",zl=3e5;async function Ml(){return await ir.openDB("xref",2,{upgrade(e){[...e.objectStoreNames].forEach((t=>e.deleteObjectStore(t)));e.createObjectStore(jl,{keyPath:"query.id"}).createIndex("byTerm","query.term",{unique:!1})}})}async function Ul(e){const t=new Map;if(await async function(){const e="XREF:LAST_VERSION_CHECK",t=parseInt(localStorage.getItem(e)??"",10),n=Date.now();if(!t)return localStorage.setItem(e,n.toString()),!1;if(n-tt}())return await async function(){try{await Ml().then((e=>e.clear(jl)))}catch(e){console.error(e)}}(),t;const n=new Set(e.map((e=>e.id)));try{const e=await Ml();let r=await e.transaction(jl).store.openCursor();for(;r;)n.has(r.key)&&t.set(r.key,r.value.result),r=await r.continue()}catch(e){console.error(e)}return t}const ql="core/xref",Wl={"web-platform":["HTML","INFRA","URL","WEBIDL","DOM","FETCH"]},Fl="https://respec.org/xref/",Bl=[];if(!document.querySelector("link[rel='preconnect'][href='https://respec.org']")){const e=Sr({hint:"preconnect",href:"https://respec.org"});document.head.appendChild(e)}function Hl(e){const t="xrefType"in e.dataset;let n=Gl(e,{isIDL:t});t||(n=n.toLowerCase());const r=function(e){const t=[];let n=e.closest("[data-cite]");for(;n;){const r=(n.dataset.cite??"").toLowerCase().replace(/[!?]/g,"").split(/\s+/).filter((e=>e));if(r.length){t.push(r);const e=Jl(r);e.length&&t.push(e)}if(n===e)break;n=n.parentElement?.closest("[data-cite]")??null}if(n!==e){const n=e.closest("section"),r=n?n.querySelectorAll("a.bibref"):[],s=[...new Set([...r].map((e=>e.textContent.toLowerCase())))];if(s.length){t.push(s);const e=Jl(s);e.length&&t.push(e)}}const r=function(e){const t=[],n=new Set;for(const r of e){const e=[...new Set(r).values().filter((e=>!n.has(e)))];e.forEach((e=>n.add(e))),t.push(e.sort())}return t}(t);return r}(e),s=function(e,t){if(t)return e.dataset.xrefType?e.dataset.xrefType.split("|"):["_IDL_"];return["_CONCEPT_"]}(e,t),i=function(e,t){if(e.dataset.xrefFor)return Nr(e.dataset.xrefFor);if(t){const t=e.closest("[data-xref-for]");if(t)return Nr(t.dataset.xrefFor??"")}return null}(e,t);return{id:"",term:n,types:s,...r.length&&{specs:r},..."string"==typeof i&&{for:i}}}function Gl(e,{isIDL:t=!1}={}){const{lt:n}=e.dataset;let r=n?n.split("|",1)[0]:e.textContent;return r=Nr(r),"the-empty-string"===r?"":(t||n||(r=r.replace(yr,"")),r)}function Vl(e){return e.replace(/-\d+$/,"")}function Jl(e){return[...new Set(e.map(Vl)).difference(new Set(e))]}function Kl(e,t,n,r){const{term:s,specs:i=[]}=t,{uri:o,shortname:a,spec:c,normative:l,type:u,for:d}=n,p=i.flat().includes(c)?c:a,h=new URL(o,"https://partial");let{pathname:f}=h;"/"===f&&(f="");const m={cite:p,citePath:f,citeFrag:h.hash.slice(1),linkType:u};d&&(m.linkFor=d[0]),h.origin&&"https://partial"!==h.origin&&(m.citeHref=h.href),Object.assign(e.dataset,m),function(e,t,n,r,s){const i=function(e){const t=e.closest(".normative"),n=e.closest(Cr);return!n||e===t||t&&n&&n.contains(t)}(e);if(!i)return void(s.normativeReferences.has(t)||s.informativeReferences.add(t));if(n){const e=s.informativeReferences.has(t)?s.informativeReferences.getCanonicalKey(t)??t:t;return s.normativeReferences.add(e),void s.informativeReferences.delete(e)}Bl.push({term:r,spec:t,element:e})}(e,p,l,s,r)}function Zl(e){const t=JSON.stringify(e,Object.keys(e).sort()),n=(new TextEncoder).encode(t);return crypto.subtle.digest("SHA-1",n).then(Yl)}function Yl(e){const t=new Uint8Array(e);return t.toHex?.()??[...t].map((e=>e.toString(16).padStart(2,"0"))).join("")}function Xl(e){const t=e.querySelectorAll("a[data-xref-for], a[data-xref-type], a[data-link-for]"),n=["data-xref-for","data-xref-type","data-link-for"];t.forEach((e=>{n.forEach((t=>e.removeAttribute(t)))}))}var Ql=Object.freeze({__proto__:null,API_URL:Fl,getTermFromElement:Gl,informativeRefsInNormative:Bl,name:ql,run:async function(e){if(!e.xref)return;const t=function(e){const t={url:new URL("search/",Fl).href,specs:null},n=Object.assign({},t);switch(Array.isArray(e)?"array":typeof e){case"boolean":break;case"string":{const t=e;t.toLowerCase()in Wl?Object.assign(n,{specs:Wl[t.toLowerCase()]}):r(t);break}case"array":Object.assign(n,{specs:e});break;case"object":{const t=e;if(Object.assign(n,t),t.profile){const e=t.profile.toLowerCase();if(e in Wl){const r=(t.specs??[]).concat(Wl[e]);Object.assign(n,{specs:r})}else r(t.profile)}break}default:{const t=ls`Expected: \`true\`, a profile name (e.g. \`"web-platform"\`), an array of spec shortnames (e.g. \`["FETCH", "DOM"]\`), or an object with \`url\`, \`specs\`, or \`profile\` properties. See ${"[xref]"}.`;ns(`Invalid value for \`xref\` configuration option. Received: "${e}".`,ql,{hint:t})}}return n;function r(e){ns(`Invalid profile "${e}" in \`respecConfig.xref\`. Please use one of the supported profiles: ${Ar(Object.keys(Wl),(e=>`"${e}"`))}.`,ql)}}(e.xref);if(t.specs){const e=document.body.dataset.cite?document.body.dataset.cite.split(/\s+/):[];document.body.dataset.cite=e.concat(t.specs).join(" ")}const n=Rl.concat(function(){const e=document.querySelectorAll(":is(a,dfn)[data-cite]:not([data-cite=''],[data-cite*='#'])"),t=document.querySelectorAll("dfn.externalDFN");return[...e].filter((e=>{if(""===e.textContent.trim())return!1;const t=e.closest("[data-cite]");return!t||""!==t.dataset.cite})).concat(...t)}());if(!n.length)return;const r=[];for(const e of n){const t=Hl(e);t.id=await Zl(t),r.push(t)}const s=await async function(e,t){const n=new Set,r=e.filter((e=>!n.has(e.id)&&(n.add(e.id)&&!0))),s=await Ul(r),i=r.filter((e=>!s.get(e.id))),o=await async function(e,t){if(!e.length)return new Map;const n={method:"POST",body:JSON.stringify({queries:e}),headers:{"Content-Type":"application/json"}},r=await fetch(t,n),s=await r.json();return new Map(s.results.map((({id:e,result:t})=>[e,t])))}(i,t);o.size&&await async function(e,t){try{const n=(await Ml()).transaction(jl,"readwrite");for(const r of e){const e=t.get(r.id);e?.length&&n.objectStore(jl).add({query:r,result:e})}await n.done}catch(e){console.error(e)}}(r,o);return new Map([...s,...o])}(r,t.url);!function(e,t,n,r){const s={ambiguous:new Map,notFound:new Map};for(let i=0,o=e.length;i{const r=new URL(Fl);return r.searchParams.set("term",e),t.for&&r.searchParams.set("for",t.for),r.searchParams.set("types",t.types.join(",")),n.length&&r.searchParams.set("specs",n.join(",")),r.href},r=(e,t)=>ls`[See search matches for "${t}"](${e}) or ${"[Learn about this error|#error-term-not-found]"}.`;for(const{query:e,elems:s}of t.values()){const t=e.specs?[...new Set(e.specs.flat())].sort():[],i=Gl(s[0]),o=n(i,e),a=Lr(t,(e=>`**[${e}]**`));let c=r(o,i);const l=s[0].parentElement?.closest("[data-cite]")??null,u=l?.dataset.cite?.replace(/`/g,"")??"";l&&l!==document.body&&u&&(c+=` A parent element has \`data-cite="${u}"\` — check that the spec shortname is correct.`);ns(`Couldn't find "**${i}**"${e.for?`, for **"${e.for}"**, `:""} in this document or other cited documents: ${a}.`,ql,{title:"No matching definition found.",elements:s,hint:c})}for(const{query:t,elems:s,results:i}of e.values()){const e=[...new Set(i.map((e=>e.shortname)))].sort(),o=Lr(e,(e=>`**[${e}]**`)),a=Gl(s[0]),c=n(a,t,e),l=t.for?`, for **"${t.for}"**, `:"",u=r(c,a),d=ls`To fix, use the ${"[data-cite]"} attribute to pick the one you mean from the appropriate specification.`+String.raw` ${u}`;ns(`The term "**${a}**"${l} is ambiguous because it's defined in ${o}.`,ql,{title:"Definition is ambiguous.",elements:s,hint:d})}}(s)}(n,r,s,e),hs("beforesave",Xl)}});const eu="headings",tu=864e5;async function nu(){return await ir.openDB("respec-headings",1,{upgrade(e){[...e.objectStoreNames].forEach((t=>e.deleteObjectStore(t))),e.createObjectStore(eu)}})}async function ru(e){const t=new Map;if(function(){const e=parseInt(localStorage.getItem("HEADINGS:LAST_CACHED")??"",10);return!isNaN(e)&&Date.now()-e>tu}())return await async function(){try{const e=await nu();await e.clear(eu),localStorage.removeItem("HEADINGS:LAST_CACHED")}catch(e){console.error(e)}}(),t;try{const n=(await nu()).transaction(eu);for(const r of e){const e=`${r.spec}#${r.id}`,s=await n.store.get(e);s&&t.set(e,s.result)}}catch(e){console.error(e)}return t}const su="core/xref-headings",iu="https://respec.org/xref/search/headings";async function ou(e,t=iu){if(!e.length)return new Map;const n=await ru(e),r=e.filter((e=>!n.has(`${e.spec}#${e.id}`)));if(!r.length)return n;try{const e=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({queries:r})});if(!e.ok){const t=`Failed to fetch heading texts (HTTP ${e.status}).`;return rs(t,su,{hint:"Cross-spec section links will fall back to spec titles."}),n}const{result:s=[]}=await e.json(),i=new Map(s.filter((e=>!e.error)).map((e=>[`${e.spec}#${e.id}`,{title:e.title,number:e.number||null}])));return await async function(e,t){try{const n=(await nu()).transaction(eu,"readwrite");for(const r of e){const e=`${r.spec}#${r.id}`,s=t.get(e);s&&n.objectStore(eu).put({query:r,result:s},e)}await n.done,localStorage.setItem("HEADINGS:LAST_CACHED",Date.now().toString())}catch(e){console.error(e)}}(r,i),new Map([...n,...i])}catch{return rs("Failed to fetch heading texts from respec.org.",su,{hint:"Cross-spec section links will fall back to spec titles."}),n}}function au(e,{title:t,number:n}){n?e.append(sr`${n} `,t):e.textContent=t}var cu=Object.freeze({__proto__:null,HEADINGS_API_URL:iu,fetchHeadingTexts:ou,name:su,run:async function(e){const t=document.querySelectorAll("a[data-cite-frag][data-matched-text]");if(!t.length)return;const n=function(e){const t="object"!=typeof e.xref||null===e.xref||Array.isArray(e.xref)?{}:e.xref;return"string"==typeof t.headingApiUrl?t.headingApiUrl:iu}(e),r=new Map,s=[];if(t.forEach((e=>{if(e.dataset.lt)e.textContent=e.dataset.lt,delete e.dataset.lt;else{const t=(e.dataset.cite??"").replace(/^[!?]/,""),n=e.dataset.citeFrag??"",i=`${t}#${n}`;s.push({elem:e,key:i}),r.has(i)||r.set(i,{spec:t,id:n})}})),!s.length)return;const i=await ou([...r.values()],n);s.forEach((({elem:e,key:t})=>{const n=i.get(t);n?.title&&(e.textContent="",au(e,n))}))},setHeadingContent:au});var lu=String.raw`ul.index{columns:30ch;column-gap:1.5em}
+          >`}}switch(r.type){case"text":return fl`${Pr(i)}`;case"number":case"float":return fl`${Pr(i)}`;case"bytes":case"hex":case"base64":return fl`${Pr(i)}`;default:return Pr(i)}}#i(e){const t=e.parentNode;return!!t&&t instanceof gr}serializeToken(e,t){const n=e.type,r=e.serialize();switch(n){case"COMMENT":return this.#o(r);case"CTLOP":return this.#a(r);case"=":case"/=":case"//=":case"/":case"//":case"=>":case"..":case"...":return this.#c(r);case"?":case"*":case"+":return t instanceof br?this.#l(r):r;default:return r}}#o(e){return e.replace(/(;[^\n]*)/g,'$1')}#a(e){return e.replace(/(\.\w+)/g,'$1')}#c(e){const t=e.match(/^(\s*)(.*?)(\s*)$/s);return t?`${t[1]}${t[2]}${t[3]}`:fl`${e}`}#l(e){const t=e.match(/^(\s*)(.*?)(\s*)$/s);return t?`${t[1]}${t[2]}${t[3]}`:fl`${e}`}}var gl=Object.freeze({__proto__:null,name:ul,run:async function(){const e=document.querySelectorAll("pre.cddl:not([data-no-cddl])");if(!e.length)return;const t=document.createElement("style");t.textContent=ll;const n=document.querySelector("head link, head > *:last-child");n?n.before(t):document.head.append(t);const r=e=>new ur(e).parse(),s={definitions:new Map,proseDfns:new Map,genericParams:new Map};var i,o,a,c;i=document,o=s.proseDfns,i.querySelectorAll(["dfn[data-dfn-type='cddl-type']","dfn[data-dfn-type='cddl-key']","dfn[data-dfn-type='cddl-value']"].join(", ")).forEach((e=>{const t=e.dataset.dfnType,n=e.dataset.dfnFor,r=e.textContent.trim(),s=n?`${pl(n)}-`:"",i=`cddl-${t.replace("cddl-","")}-${s}${pl(r)}`;e.id||=i,o.set(i,e.id),pc(e,[r])})),e.forEach((e=>function(e,t,n){const r=e.textContent;if(r.trim())try{const s=t(r),i=new ml(n),o=s.serialize(i),a=document.createElement("code");a.innerHTML=o,e.textContent="",e.append(a),e.classList.add("def","highlight"),qr(e,"cddl-block");const c=document.createElement("span");c.className="cddlHeader",c.innerHTML=`CDDL`;const l=Gc(".cddlHeader");c.append(l),e.prepend(c)}catch(t){ns(`CDDL processing error: ${t instanceof Error?t.message:String(t)}`,ul,{elements:[e],hint:'Check the CDDL syntax in the `
` block.'})}}(e,r,s))),a=document.body,c=s.definitions,a.querySelectorAll("[data-cddl-pending]").forEach((e=>{const t=`cddl-type:${e.dataset.cddlPending}`;if(c.has(t)){const n=c.get(t);if(!n)return;const r=a.ownerDocument.createElement("a");r.href=`#${n.id}`,r.className="cddl-name",r.dataset.linkType="cddl-type",r.textContent=e.textContent,e.replaceWith(r)}})),document.querySelectorAll("[data-cddl-pending]").forEach((e=>{rs(`No CDDL definition found for \`${e.getAttribute("data-cddl-pending")}\`.`,ul,{elements:[e],hint:"Check for typos in the type name."})})),function(e,t){e.querySelectorAll('a[data-link-type^="cddl-"]:not([href]):not([data-cite])').forEach((e=>{const n=e.getAttribute("data-link-type"),r=e.getAttribute("data-xref-for")||e.getAttribute("data-link-for")||"",s=e.textContent.trim();if("cddl-type"!==n&&"cddl-key"!==n&&"cddl-value"!==n)return;const i={"cddl-type":`cddl-type:${s}`,"cddl-key":`cddl-key:${r}/${s}`,"cddl-value":`cddl-value:${r}/${s}`}[n];if(t.has(i)){const n=t.get(i);if(!n)return;e.setAttribute("href",`#${n.id}`),e.classList.add("internalDFN")}else rs(`CDDL ${n}: no definition found for \`${s}\`${r?`, for \`${r}\`,`:""} in any \`
\` block.`,ul,{elements:[e]}),e.setAttribute("data-no-link-to-dfn","")}))}(document,s.definitions),function(e,t){t.forEach((t=>{const n=e.getElementById(t.id);"dfn"===n?.localName&&pc(n,[n.textContent.trim()])}))}(document,s.definitions),Vc(),hs("beforesave",(e=>{e.querySelectorAll("[data-cddl-pending]").forEach((e=>e.removeAttribute("data-cddl-pending")))}))}});const bl="core/data-cite",yl="__SPEC__";async function wl(e){const{key:t,frag:n,path:r,href:s}=e;let i="",o="";if(t===yl)i=document.location.href;else{const e=await ka(t);if(!e)return null;i=e.href??"",o=e.title}if(s)i=s;else{if(r){const e=r.startsWith("/")?`.${r}`:r;i=new URL(e,i).href}n&&(i=new URL(n,i).href)}return{href:i,title:o}}function vl(e,t,n){const{href:r,title:s}=t,i=!n.path&&!n.frag;switch(e.localName){case"a":{const t=e;if(""===t.textContent&&"the-empty-string"!==t.dataset.lt&&(t.textContent=s),t.href=r,i){const e=document.createElement("cite");t.replaceWith(e),e.append(t)}break}case"dfn":{const t=document.createElement("a");if(t.href=r,t.dataset.cite=n.key,t.dataset.citePath=n.path,t.dataset.citeFrag=n.frag,e.textContent?Vr(e,t):(t.textContent=s,e.append(t)),i){const n=document.createElement("cite");n.append(t),e.append(n)}if("export"in e.dataset){ns("Exporting a linked external definition is not allowed.",bl,{hint:"Please remove the `data-export` attribute.",elements:[e]}),delete e.dataset.export}e.classList.add("externalDFN"),e.dataset.noExport="";break}}}function kl(e){return t=>{const n=t.search(e);return-1!==n?t.substring(n):""}}const $l=kl("#"),xl=kl("/");function Cl(e){const{dataset:t}=e,{cite:n,citeFrag:r,citePath:s,citeHref:i}=t;if((n??"").startsWith("#")&&!r){const r=e.parentElement?.closest('[data-cite]:not([data-cite^="#"])')??null,{key:s,isNormative:i}=r?Cl(r):{key:yl,isNormative:!1};return t.cite=i?s:`?${s}`,t.citeFrag=(n??"").replace("#",""),Cl(e)}const o=r?`#${r}`:$l(n??""),a=s||xl(n??"").split("#")[0],{type:c}=Gr(n??"",e),l="normative"===c,u=/^[?|!]/.test(n??"");return{key:(n??"").split(/[/|#]/)[0].substring(Number(u)),isNormative:l,frag:o,path:a,href:i}}function Sl(e){const t=["data-cite","data-cite-frag","data-cite-path"];e.querySelectorAll("a[data-cite], dfn[data-cite]").forEach((e=>t.forEach((t=>e.removeAttribute(t)))))}var _l=Object.freeze({__proto__:null,THIS_SPEC:yl,name:bl,run:async function(){const e=document.querySelectorAll("dfn[data-cite]:not([data-cite='']), a[data-cite]:not([data-cite=''])");await async function(e){const t=e.map(Cl).map((async e=>({entry:e,result:await ka(e.key)}))),n=(await Promise.all(t)).filter((({result:e})=>null===e)).map((({entry:{key:e}})=>e)),r=await va(n);r&&Object.assign(ma,r)}([...e]);const t=new Map,n=new Map;for(const r of e){t.set(r,r.dataset.cite);const e=Cl(r);n.set(r,e)}for(const r of e){const e=t.get(r),s=n.get(r),i=await wl(s);if(i)r.dataset.citeFrag&&r.dataset.matchedText||!r.dataset.lt||"the-empty-string"===r.dataset.lt||""!==r.textContent||(r.textContent=r.dataset.lt,delete r.dataset.lt),vl(r,i,s);else{const t=`Couldn't find a match for "${e}"`;r.dataset.matchedText&&(r.textContent=r.dataset.matchedText),rs(t,bl,{elements:[r]})}}hs("beforesave",Sl)},toCiteDetails:Cl});const Tl="core/link-to-dfn",Rl=[],El={en:{duplicateMsg:e=>`Duplicate definition(s) of '${e}'`,duplicateTitle:"This is defined more than once in the document."},ja:{duplicateMsg:e=>`'${e}' の重複定義`,duplicateTitle:"この文書内で複数回定義されています."},de:{duplicateMsg:e=>`Mehrfache Definition von '${e}'`,duplicateTitle:"Das Dokument enthält mehrere Definitionen dieses Eintrags."},zh:{duplicateMsg:e=>`'${e}' 的重复定义`,duplicateTitle:"在文档中有重复的定义。"},cs:{duplicateMsg:e=>`Duplicitní definice '${e}'`,duplicateTitle:"Toto je v dokumentu definováno vícekrát."}},Ll=Ir(El);function Al(e){const t=new Map,n=[];for(const r of dc.get(e)??[]){const{dfnType:s="dfn"}=r.dataset,i=r.dataset.dfnFor?.split(",").map((e=>e.trim()))??[""];for(const o of i){if(t.has(o)&&t.get(o)?.has(s)){const e=t.get(o)?.get(s),i="dfn"===e?.localName,a="dfn"===r.localName,c=s===(e?.dataset.dfnType||"dfn"),l=!o&&!e?.dataset.dfnFor||e?.dataset.dfnFor?.split(",").map((e=>e.trim())).includes(o);if(i&&a&&c&&l){n.push(r);continue}}t.has(o)||t.set(o,new Map),t.get(o)?.set(s,r),("idl"in r.dataset||"dfn"!==s)&&t.get(o)?.set("idl",r),Fr(r,"dfn",e)}}return{result:t,duplicates:n}}function Pl(e,t){const n=function(e){const t=e.closest("[data-link-for]"),n=t?t.dataset.linkFor??"":"",r=Br(e).reduce(((e,r)=>{const s=r.split(".");2===s.length&&e.push({for:s[0],title:s[1]}),e.push({for:n,title:r}),t||e.push({for:r,title:r}),""!==n&&e.push({for:"",title:r});const i=r.replace(yr,"");return i!==r&&""!==i&&(e.push({for:n,title:i}),t||e.push({for:i,title:i}),""!==n&&e.push({for:"",title:i})),e}),[]);return r}(e),r=n.find((e=>t.has(e.title)&&t.get(e.title)?.has(e.for)));if(!r)return;const s=t.get(r.title)?.get(r.for),{linkType:i}=e.dataset;if(i){for(const e of i.split("|"))if(s?.get(e))return s.get(e);return s?.get("dfn")}{const e=r.for?"idl":"dfn";return s?.get(e)||s?.get("idl")}}function Nl(e,t,n){let r=!1;const{linkFor:s}=e.dataset,{dfnFor:i}=t.dataset;if(t.dataset.cite)e.dataset.cite=t.dataset.cite;else if(s&&!n.get(s)&&i&&!i.split(",").map((e=>e.trim())).includes(s))r=!0;else if(t.classList.contains("externalDFN")){const n=t.dataset.lt?t.dataset.lt.split("|"):[];e.dataset.lt=n[0]||t.textContent,r=!0}else"partial"!==e.dataset.idl?(e.href=`#${t.id}`,e.classList.add("internalDFN")):r=!0;return e.hasAttribute("data-link-type")||(e.dataset.linkType="idl"in t.dataset?"idl":"dfn"),function(e){if(e.closest("code,pre"))return!0;if(1!==e.childNodes.length)return!1;const[t]=e.childNodes;return"code"===t.localName}(t)&&function(e,t){const n=e.textContent.trim(),r=t.dataset.hasOwnProperty("idl"),s=Il(e)&&Il(t,n);r&&!s||Vr(e,document.createElement("code"))}(e,t),!r}function Il(e,t=""){if("a"===e.localName){if(!e.querySelector("code"))return!0}else{const{dataset:n}=e;if(e.textContent.trim()===t)return!0;if(n.title===t)return!0;if(n.lt||n.localLt){const e=[];return n.lt&&e.push(...n.lt.split("|")),n.localLt&&e.push(...n.localLt.split("|")),e.includes(t)}}return!1}function Dl(e){e.forEach((e=>{const t=`Found linkless \`\` element with text "${e.textContent}" but no matching \`\``,n=e.closest("[data-link-for]"),r=`Add a matching \`\` element, ${ls`use ${"[data-cite]"} to link to an external definition, or enable ${"[xref]"} for automatic cross-spec linking.`}${n?` This link is inside a \`data-link-for="${n.dataset.linkFor}"\` section — \`[=term=]\` links are scoped to that context. To link to a global concept instead, either add \`data-link-for=""\` on this \`\` or move it outside the scoped section.`:""}`;rs(t,Tl,{title:"Linking error: no matching ``",hint:r,elements:[e]})}))}var Ol=Object.freeze({__proto__:null,name:Tl,possibleExternalLinks:Rl,run:async function(e){const t=function(){const e=new es;for(const t of dc.keys()){const{result:n,duplicates:r}=Al(t);e.set(t,n),r.length>0&&ns(Ll.duplicateMsg(t),Tl,{title:Ll.duplicateTitle,elements:r})}return e}(),n=[],r=document.querySelectorAll("a[data-cite='']:not([data-no-link-to-dfn]), a:not([href]):not([data-cite]):not([data-no-link-to-dfn]):not(.logo):not(.externalDFN)");for(const e of r){if(!e.dataset?.linkType&&e.dataset?.xrefType){Rl.push(e);continue}const r=Pl(e,t);if(r){Nl(e,r,t)||Rl.push(e)}else""===e.dataset.cite?n.push(e):Rl.push(e)}Dl(n),function(e){const{shortName:t=""}=e,n=t?new RegExp(String.raw`^([?!])?${vr(t)}\b([^-])`,"i"):null,r=document.querySelectorAll("dfn[data-cite]:not([data-cite='']), a[data-cite]:not([data-cite=''])");for(const t of r){n&&(t.dataset.cite=(t.dataset.cite??"").replace(n,`$1${yl}$2`));const{key:r,isNormative:s}=Cl(t);r!==yl&&(s||e.normativeReferences.has(r)?(e.normativeReferences.add(r),e.informativeReferences.delete(r)):e.informativeReferences.add(r))}}(e),e.xref||Dl(Rl)}});const jl="xrefs",zl=3e5;async function Ml(){return await ir.openDB("xref",2,{upgrade(e){[...e.objectStoreNames].forEach((t=>e.deleteObjectStore(t)));e.createObjectStore(jl,{keyPath:"query.id"}).createIndex("byTerm","query.term",{unique:!1})}})}async function Ul(e){const t=new Map;if(await async function(){const e="XREF:LAST_VERSION_CHECK",t=parseInt(localStorage.getItem(e)??"",10),n=Date.now();if(!t)return localStorage.setItem(e,n.toString()),!1;if(n-tt}())return await async function(){try{await Ml().then((e=>e.clear(jl)))}catch(e){console.error(e)}}(),t;const n=new Set(e.map((e=>e.id)));try{const e=await Ml();let r=await e.transaction(jl).store.openCursor();for(;r;)n.has(r.key)&&t.set(r.key,r.value.result),r=await r.continue()}catch(e){console.error(e)}return t}const ql="core/xref",Wl={"web-platform":["HTML","INFRA","URL","WEBIDL","DOM","FETCH"]},Fl="https://respec.org/xref/",Bl=[];if(!document.querySelector("link[rel='preconnect'][href='https://respec.org']")){const e=Sr({hint:"preconnect",href:"https://respec.org"});document.head.appendChild(e)}function Hl(e){const t="xrefType"in e.dataset;let n=Gl(e,{isIDL:t});t||(n=n.toLowerCase());const r=function(e){const t=[];let n=e.closest("[data-cite]");for(;n;){const r=(n.dataset.cite??"").toLowerCase().replace(/[!?]/g,"").split(/\s+/).filter((e=>e));if(r.length){t.push(r);const e=Jl(r);e.length&&t.push(e)}if(n===e)break;n=n.parentElement?.closest("[data-cite]")??null}if(n!==e){const n=e.closest("section"),r=n?n.querySelectorAll("a.bibref"):[],s=[...new Set([...r].map((e=>e.textContent.toLowerCase())))];if(s.length){t.push(s);const e=Jl(s);e.length&&t.push(e)}}const r=function(e){const t=[],n=new Set;for(const r of e){const e=[...new Set(r).values().filter((e=>!n.has(e)))];e.forEach((e=>n.add(e))),t.push(e.sort())}return t}(t);return r}(e),s=function(e,t){if(t)return e.dataset.xrefType?e.dataset.xrefType.split("|"):["_IDL_"];return["_CONCEPT_"]}(e,t),i=function(e,t){if(e.dataset.xrefFor)return Nr(e.dataset.xrefFor);if(t){const t=e.closest("[data-xref-for]");if(t)return Nr(t.dataset.xrefFor??"")}return null}(e,t);return{id:"",term:n,types:s,...r.length&&{specs:r},..."string"==typeof i&&{for:i}}}function Gl(e,{isIDL:t=!1}={}){const{lt:n}=e.dataset;let r=n?n.split("|",1)[0]:e.textContent;return r=Nr(r),"the-empty-string"===r?"":(t||n||(r=r.replace(yr,"")),r)}function Vl(e){return e.replace(/-\d+$/,"")}function Jl(e){return[...new Set(e.map(Vl)).difference(new Set(e))]}function Kl(e,t,n,r){const{term:s,specs:i=[]}=t,{uri:o,shortname:a,spec:c,normative:l,type:u,for:d}=n,p=i.flat().includes(c)?c:a,h=new URL(o,"https://partial");let{pathname:f}=h;"/"===f&&(f="");const m={cite:p,citePath:f,citeFrag:h.hash.slice(1),linkType:u};d&&(m.linkFor=d[0]),h.origin&&"https://partial"!==h.origin&&(m.citeHref=h.href),Object.assign(e.dataset,m),function(e,t,n,r,s){const i=function(e){const t=e.closest(".normative"),n=e.closest(Cr);return!n||e===t||t&&n&&n.contains(t)}(e);if(!i)return void(s.normativeReferences.has(t)||s.informativeReferences.add(t));if(n){const e=s.informativeReferences.has(t)?s.informativeReferences.getCanonicalKey(t)??t:t;return s.normativeReferences.add(e),void s.informativeReferences.delete(e)}Bl.push({term:r,spec:t,element:e})}(e,p,l,s,r)}function Zl(e){const t=JSON.stringify(e,Object.keys(e).sort()),n=(new TextEncoder).encode(t);return crypto.subtle.digest("SHA-1",n).then(Yl)}function Yl(e){const t=new Uint8Array(e);return t.toHex?.()??[...t].map((e=>e.toString(16).padStart(2,"0"))).join("")}function Xl(e){const t=e.querySelectorAll("a[data-xref-for], a[data-xref-type], a[data-link-for]"),n=["data-xref-for","data-xref-type","data-link-for"];t.forEach((e=>{n.forEach((t=>e.removeAttribute(t)))}))}var Ql=Object.freeze({__proto__:null,API_URL:Fl,getTermFromElement:Gl,informativeRefsInNormative:Bl,name:ql,run:async function(e){if(!e.xref)return;const t=function(e){const t={url:new URL("search/",Fl).href,specs:null},n=Object.assign({},t);switch(Array.isArray(e)?"array":typeof e){case"boolean":break;case"string":{const t=e;t.toLowerCase()in Wl?Object.assign(n,{specs:Wl[t.toLowerCase()]}):r(t);break}case"array":Object.assign(n,{specs:e});break;case"object":{const t=e;if(Object.assign(n,t),t.profile){const e=t.profile.toLowerCase();if(e in Wl){const r=(t.specs??[]).concat(Wl[e]);Object.assign(n,{specs:r})}else r(t.profile)}break}default:{const t=ls`Expected: \`true\`, a profile name (e.g. \`"web-platform"\`), an array of spec shortnames (e.g. \`["FETCH", "DOM"]\`), or an object with \`url\`, \`specs\`, or \`profile\` properties. See ${"[xref]"}.`;ns(`Invalid value for \`xref\` configuration option. Received: "${e}".`,ql,{hint:t})}}return n;function r(e){ns(`Invalid profile "${e}" in \`respecConfig.xref\`. Please use one of the supported profiles: ${Ar(Object.keys(Wl),(e=>`"${e}"`))}.`,ql)}}(e.xref);if(t.specs){const e=document.body.dataset.cite?document.body.dataset.cite.split(/\s+/):[];document.body.dataset.cite=e.concat(t.specs).join(" ")}const n=Rl.concat(function(){const e=document.querySelectorAll(":is(a,dfn)[data-cite]:not([data-cite=''],[data-cite*='#'])"),t=document.querySelectorAll("dfn.externalDFN");return[...e].filter((e=>{if(""===e.textContent.trim())return!1;const t=e.closest("[data-cite]");return!t||""!==t.dataset.cite})).concat(...t)}());if(!n.length)return;const r=[];for(const e of n){const t=Hl(e);t.id=await Zl(t),r.push(t)}const s=await async function(e,t){const n=new Set,r=e.filter((e=>!n.has(e.id)&&(n.add(e.id)&&!0))),s=await Ul(r),i=r.filter((e=>!s.get(e.id))),o=await async function(e,t){if(!e.length)return new Map;const n={method:"POST",body:JSON.stringify({queries:e}),headers:{"Content-Type":"application/json"}},r=await fetch(t,n),s=await r.json();return new Map(s.results.map((({id:e,result:t})=>[e,t])))}(i,t);o.size&&await async function(e,t){try{const n=(await Ml()).transaction(jl,"readwrite");for(const r of e){const e=t.get(r.id);e?.length&&n.objectStore(jl).add({query:r,result:e})}await n.done}catch(e){console.error(e)}}(r,o);return new Map([...s,...o])}(r,t.url);!function(e,t,n,r){const s={ambiguous:new Map,notFound:new Map};for(let i=0,o=e.length;i{const r=new URL(Fl);return r.searchParams.set("term",e),t.for&&r.searchParams.set("for",t.for),r.searchParams.set("types",t.types.join(",")),n.length&&r.searchParams.set("specs",n.join(",")),r.href},r=(e,t)=>ls`[See search matches for "${t}"](${e}) or ${"[Learn about this error|#error-term-not-found]"}.`;for(const{query:e,elems:s}of t.values()){const t=e.specs?[...new Set(e.specs.flat())].sort():[],i=Gl(s[0]),o=n(i,e),a=Lr(t,(e=>`**[${e}]**`));let c=r(o,i);const l=s[0].parentElement?.closest("[data-cite]")??null,u=l?.dataset.cite?.replace(/`/g,"")??"";l&&l!==document.body&&u&&(c+=` A parent element has \`data-cite="${u}"\` — check that the spec shortname is correct.`);ns(`Couldn't find "**${i}**"${e.for?`, for **"${e.for}"**, `:""} in this document or other cited documents: ${a}.`,ql,{title:"No matching definition found.",elements:s,hint:c})}for(const{query:t,elems:s,results:i}of e.values()){const e=[...new Set(i.map((e=>e.shortname)))].sort(),o=Lr(e,(e=>`**[${e}]**`)),a=Gl(s[0]),c=n(a,t,e),l=t.for?`, for **"${t.for}"**, `:"",u=r(c,a),d=ls`To fix, use the ${"[data-cite]"} attribute to pick the one you mean from the appropriate specification.`+String.raw` ${u}`;ns(`The term "**${a}**"${l} is ambiguous because it's defined in ${o}.`,ql,{title:"Definition is ambiguous.",elements:s,hint:d})}}(s)}(n,r,s,e),hs("beforesave",Xl)}});const eu="headings",tu=864e5;async function nu(){return await ir.openDB("respec-headings",1,{upgrade(e){[...e.objectStoreNames].forEach((t=>e.deleteObjectStore(t))),e.createObjectStore(eu)}})}async function ru(e){const t=new Map;if(function(){const e=parseInt(localStorage.getItem("HEADINGS:LAST_CACHED")??"",10);return!isNaN(e)&&Date.now()-e>tu}())return await async function(){try{const e=await nu();await e.clear(eu),localStorage.removeItem("HEADINGS:LAST_CACHED")}catch(e){console.error(e)}}(),t;try{const n=(await nu()).transaction(eu);for(const r of e){const e=`${r.spec}#${r.id}`,s=await n.store.get(e);s&&t.set(e,s.result)}}catch(e){console.error(e)}return t}const su="core/xref-headings",iu="https://respec.org/xref/search/headings";async function ou(e,t=iu){if(!e.length)return new Map;const n=await ru(e),r=e.filter((e=>!n.has(`${e.spec}#${e.id}`)));if(!r.length)return n;try{const e=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({queries:r})});if(!e.ok){const t=`Failed to fetch heading texts (HTTP ${e.status}).`;return rs(t,su,{hint:"Cross-spec section links will fall back to spec titles."}),n}const{result:s=[]}=await e.json(),i=new Map(s.filter((e=>!e.error)).map((e=>[`${e.spec}#${e.id}`,{title:e.title,number:e.number||null}])));return await async function(e,t){try{const n=(await nu()).transaction(eu,"readwrite");for(const r of e){const e=`${r.spec}#${r.id}`,s=t.get(e);s&&n.objectStore(eu).put({query:r,result:s},e)}await n.done,localStorage.setItem("HEADINGS:LAST_CACHED",Date.now().toString())}catch(e){console.error(e)}}(r,i),new Map([...n,...i])}catch{return rs("Failed to fetch heading texts from respec.org.",su,{hint:"Cross-spec section links will fall back to spec titles."}),n}}function au(e,{title:t,number:n}){n?e.append(sr`${n} `,t):e.textContent=t}var cu=Object.freeze({__proto__:null,HEADINGS_API_URL:iu,fetchHeadingTexts:ou,name:su,run:async function(e){const t=document.querySelectorAll("a[data-cite-frag][data-matched-text]");if(!t.length)return;const n=function(e){const t="object"!=typeof e.xref||null===e.xref||Array.isArray(e.xref)?{}:e.xref;return"string"==typeof t.headingApiUrl?t.headingApiUrl:iu}(e),r=new Map,s=[];if(t.forEach((e=>{if(e.dataset.lt)e.textContent=e.dataset.lt,delete e.dataset.lt;else{const t=(e.dataset.cite??"").replace(/^[!?]/,""),n=e.dataset.citeFrag??"",i=`${t}#${n}`;s.push({elem:e,key:i}),r.has(i)||r.set(i,{spec:t,id:n})}})),!s.length)return;const i=await ou([...r.values()],n);s.forEach((({elem:e,key:t})=>{const n=i.get(t);n?.title&&(e.textContent="",au(e,n))}))},setHeadingContent:au});var lu=String.raw`ul.index{columns:30ch;column-gap:1.5em}
 ul.index li{list-style:inherit}
 ul.index li span{color:inherit;cursor:pointer;white-space:normal}
 #index-defined-here ul.index li{font-size:.9rem}
@@ -1500,5 +1503,5 @@ var[data-type]:hover::after,var[data-type]:hover::before{opacity:1}`;var up=Obje
           * Přidejte atribut \`class=\"lint-ignore\"\` k odkazu.
           * Použijte lokální normativní proxy pro definici, např. \`term\`
 
-        Pro úplné potlačení tohoto varování nastavte \`lint: { \"${Th}\": false }\` ve vaší \`respecConfig\`.`}}});var Lh=Object.freeze({__proto__:null,name:Rh,run:function(e){if(!e.lint?.[Th])return;const t="error"===e.lint[Th]?ns:rs;Bl.forEach((({term:e,spec:n,element:r})=>{r.classList.contains("lint-ignore")||t(Eh.msg(e,n),Rh,{title:"Normative reference to non-normative term.",elements:[r],hint:Eh.hint})}))}}),Ah=Object.freeze({__proto__:null,default:'(() => {\n// @ts-check\n\nif (document.respec) {\n  document.respec.ready.then(setupVarHighlighter);\n} else {\n  setupVarHighlighter();\n}\n\nfunction setupVarHighlighter() {\n  document\n    .querySelectorAll("var")\n    .forEach(varElem => varElem.addEventListener("click", highlightListener));\n}\n\n/**\n * @param {MouseEvent} ev\n */\nfunction highlightListener(ev) {\n  ev.stopPropagation();\n  const varElem = /** @type {HTMLElement} */ (ev.target);\n  const hightligtedElems = highlightVars(varElem);\n  const resetListener = () => {\n    const hlColor = getHighlightColor(varElem);\n    hightligtedElems.forEach(el => removeHighlight(el, hlColor));\n    [...HL_COLORS.keys()].forEach(key => HL_COLORS.set(key, true));\n  };\n  if (hightligtedElems.length) {\n    document.body.addEventListener("click", resetListener, { once: true });\n  }\n}\n\n// availability of highlight colors. colors from var.css\nconst HL_COLORS = new Map([\n  ["respec-hl-c1", true],\n  ["respec-hl-c2", true],\n  ["respec-hl-c3", true],\n  ["respec-hl-c4", true],\n  ["respec-hl-c5", true],\n  ["respec-hl-c6", true],\n  ["respec-hl-c7", true],\n]);\n\n/**\n * @param {HTMLElement} target\n */\nfunction getHighlightColor(target) {\n  // return current colors if applicable\n  const { value } = target.classList;\n  const re = /respec-hl-\\w+/;\n  const activeClass = re.test(value) && value.match(re);\n  if (activeClass) return activeClass[0];\n\n  // first color preference\n  if (HL_COLORS.get("respec-hl-c1") === true) return "respec-hl-c1";\n\n  // otherwise get some other available color\n  return [...HL_COLORS.keys()].find(c => HL_COLORS.get(c)) || "respec-hl-c1";\n}\n\n/**\n * @param {HTMLElement} varElem\n */\nfunction highlightVars(varElem) {\n  const textContent = norm(varElem.textContent);\n  const parent = /** @type {HTMLElement} */ (\n    varElem.closest(".algorithm, section")\n  );\n  if (!parent) return [];\n  const highlightColor = getHighlightColor(varElem);\n\n  const varsToHighlight = [...parent.querySelectorAll("var")].filter(\n    el =>\n      norm(el.textContent) === textContent &&\n      el.closest(".algorithm, section") === parent\n  );\n\n  // update availability of highlight color\n  const colorStatus = varsToHighlight[0].classList.contains("respec-hl");\n  HL_COLORS.set(highlightColor, colorStatus);\n\n  // highlight vars\n  if (colorStatus) {\n    varsToHighlight.forEach(el => removeHighlight(el, highlightColor));\n    return [];\n  } else {\n    varsToHighlight.forEach(el => addHighlight(el, highlightColor));\n  }\n  return varsToHighlight;\n}\n\n/**\n * @param {HTMLElement} el\n * @param {string} highlightColor\n */\nfunction removeHighlight(el, highlightColor) {\n  el.classList.remove("respec-hl", highlightColor);\n  // clean up empty class attributes so they don\'t come in export\n  if (!el.classList.length) el.removeAttribute("class");\n}\n\n/**\n * @param {HTMLElement} elem\n * @param {string} highlightColor\n */\nfunction addHighlight(elem, highlightColor) {\n  elem.classList.add("respec-hl", highlightColor);\n}\n\n/**\n * Same as `norm` from src/core/utils, but our build process doesn\'t allow\n * imports in runtime scripts, so duplicated here.\n * @param {string} str\n */\nfunction norm(str) {\n  return str.trim().replace(/\\s+/g, " ");\n}\n})()'}),Ph=Object.freeze({__proto__:null,default:'(() => {\n// @ts-check\nif (document.respec) {\n  document.respec.ready.then(setupPanel);\n} else {\n  setupPanel();\n}\n\nfunction setupPanel() {\n  const listener = panelListener();\n  document.body.addEventListener("keydown", listener);\n  document.body.addEventListener("click", listener);\n}\n\nfunction panelListener() {\n  /** @type {HTMLElement | null} */\n  let panel = null;\n  /**\n   * @param {KeyboardEvent|MouseEvent} event\n   */\n  return event => {\n    const { target, type } = event;\n\n    if (!(target instanceof HTMLElement)) return;\n\n    // For keys, we only care about Enter key to activate the panel\n    // otherwise it\'s activated via a click.\n    if (\n      type === "keydown" &&\n      /** @type {KeyboardEvent} */ (event).key !== "Enter"\n    )\n      return;\n\n    const action = deriveAction(event);\n\n    switch (action) {\n      case "show": {\n        hidePanel(panel);\n        /** @type {HTMLElement | null} */\n        const dfn = target.closest("dfn, .index-term");\n        if (!dfn?.id) break;\n        panel = document.getElementById(`dfn-panel-for-${dfn.id}`);\n        if (!panel) break;\n        const coords = deriveCoordinates(\n          /** @type {MouseEvent|KeyboardEvent} */ (event)\n        );\n        displayPanel(dfn, panel, coords);\n        break;\n      }\n      case "dock": {\n        if (panel) {\n          panel.style.left = "";\n          panel.style.top = "";\n          panel.classList.add("docked");\n        }\n        break;\n      }\n      case "hide": {\n        hidePanel(panel);\n        panel = null;\n        break;\n      }\n    }\n  };\n}\n\n/**\n * @param {MouseEvent|KeyboardEvent} event\n */\nfunction deriveCoordinates(event) {\n  const target = /** @type HTMLElement */ (event.target);\n\n  // We prevent synthetic AT clicks from putting\n  // the dialog in a weird place. The AT events sometimes\n  // lack coordinates, so they have clientX/Y = 0\n  const rect = target.getBoundingClientRect();\n  if (\n    event instanceof MouseEvent &&\n    event.clientX >= rect.left &&\n    event.clientY >= rect.top\n  ) {\n    // The event probably happened inside the bounding rect...\n    return { x: event.clientX, y: event.clientY };\n  }\n\n  // Offset to the middle of the element\n  const x = rect.x + rect.width / 2;\n  // Placed at the bottom of the element\n  const y = rect.y + rect.height;\n  return { x, y };\n}\n\n/**\n * @param {Event} event\n */\nfunction deriveAction(event) {\n  const target = /** @type {HTMLElement} */ (event.target);\n  const hitALink = !!target.closest("a");\n  if (target.closest("dfn:not([data-cite]), .index-term")) {\n    return hitALink ? "none" : "show";\n  }\n  if (target.closest(".dfn-panel")) {\n    if (hitALink) {\n      return target.classList.contains("self-link") ? "hide" : "dock";\n    }\n\n    const panel = /** @type {HTMLElement} */ (target.closest(".dfn-panel"));\n    return panel.classList.contains("docked") ? "hide" : "none";\n  }\n  if (document.querySelector(".dfn-panel:not([hidden])")) {\n    return "hide";\n  }\n  return "none";\n}\n\n/**\n * @param {HTMLElement} dfn\n * @param {HTMLElement} panel\n * @param {{ x: number, y: number }} clickPosition\n */\nfunction displayPanel(dfn, panel, { x, y }) {\n  panel.hidden = false;\n  // distance (px) between edge of panel and the pointing triangle (caret)\n  const MARGIN = 20;\n\n  const dfnRects = dfn.getClientRects();\n  // Find the `top` offset when the `dfn` can be spread across multiple lines\n  let closestTop = 0;\n  let minDiff = Infinity;\n  for (const rect of dfnRects) {\n    const { top, bottom } = rect;\n    const diffFromClickY = Math.abs((top + bottom) / 2 - y);\n    if (diffFromClickY < minDiff) {\n      minDiff = diffFromClickY;\n      closestTop = top;\n    }\n  }\n\n  const top = window.scrollY + closestTop + dfnRects[0].height;\n  const left = x - MARGIN;\n  panel.style.left = `${left}px`;\n  panel.style.top = `${top}px`;\n\n  // Find if the panel is flowing out of the window\n  const panelRect = panel.getBoundingClientRect();\n  const SCREEN_WIDTH = Math.min(window.innerWidth, window.screen.width);\n  if (panelRect.right > SCREEN_WIDTH) {\n    const newLeft = Math.max(MARGIN, x + MARGIN - panelRect.width);\n    const newCaretOffset = left - newLeft;\n    panel.style.left = `${newLeft}px`;\n    /** @type {HTMLElement | null} */\n    const caret = panel.querySelector(".caret");\n    if (caret) caret.style.left = `${newCaretOffset}px`;\n  }\n\n  // As it\'s a dialog, we trap focus.\n  // TODO: when  becomes a implemented, we should really\n  // use that.\n  trapFocus(panel, dfn);\n}\n\n/**\n * @param {HTMLElement} panel\n * @param {HTMLElement} dfn\n * @returns\n */\nfunction trapFocus(panel, dfn) {\n  /** @type NodeListOf elements */\n  const anchors = panel.querySelectorAll("a[href]");\n  // No need to trap focus\n  if (!anchors.length) return;\n\n  // Move focus to first anchor element\n  const first = anchors.item(0);\n  first.focus();\n\n  const trapListener = createTrapListener(anchors, panel, dfn);\n  panel.addEventListener("keydown", trapListener);\n\n  // Hiding the panel releases the trap\n  const mo = new MutationObserver(records => {\n    const [record] = records;\n    const target = /** @type HTMLElement */ (record.target);\n    if (target.hidden) {\n      panel.removeEventListener("keydown", trapListener);\n      mo.disconnect();\n    }\n  });\n  mo.observe(panel, { attributes: true, attributeFilter: ["hidden"] });\n}\n\n/**\n *\n * @param {NodeListOf} anchors\n * @param {HTMLElement} panel\n * @param {HTMLElement} dfn\n * @returns\n */\nfunction createTrapListener(anchors, panel, dfn) {\n  const lastIndex = anchors.length - 1;\n  let currentIndex = 0;\n  /**\n   * @param {KeyboardEvent} event\n   */\n  return event => {\n    switch (event.key) {\n      // Hitting "Tab" traps us in a nice loop around elements.\n      case "Tab": {\n        event.preventDefault();\n        currentIndex += event.shiftKey ? -1 : +1;\n        if (currentIndex < 0) {\n          currentIndex = lastIndex;\n        } else if (currentIndex > lastIndex) {\n          currentIndex = 0;\n        }\n        anchors.item(currentIndex).focus();\n        break;\n      }\n\n      // Hitting "Enter" on an anchor releases the trap.\n      case "Enter":\n        hidePanel(panel);\n        break;\n\n      // Hitting "Escape" returns focus to dfn.\n      case "Escape":\n        hidePanel(panel);\n        dfn.focus();\n        return;\n    }\n  };\n}\n\n/** @param {HTMLElement | null} panel */\nfunction hidePanel(panel) {\n  if (!panel) return;\n  panel.hidden = true;\n  panel.classList.remove("docked");\n}\n})()'})}();
+        Pro úplné potlačení tohoto varování nastavte \`lint: { \"${Th}\": false }\` ve vaší \`respecConfig\`.`}}});var Lh=Object.freeze({__proto__:null,name:Rh,run:function(e){if(!e.lint?.[Th])return;const t="error"===e.lint[Th]?ns:rs;Bl.forEach((({term:e,spec:n,element:r})=>{r.classList.contains("lint-ignore")||t(Eh.msg(e,n),Rh,{title:"Normative reference to non-normative term.",elements:[r],hint:Eh.hint})}))}}),Ah=Object.freeze({__proto__:null,default:'(() => {\n// @ts-check\r\n\r\nif (document.respec) {\r\n  document.respec.ready.then(setupVarHighlighter);\r\n} else {\r\n  setupVarHighlighter();\r\n}\r\n\r\nfunction setupVarHighlighter() {\r\n  document\r\n    .querySelectorAll("var")\r\n    .forEach(varElem => varElem.addEventListener("click", highlightListener));\r\n}\r\n\r\n/**\r\n * @param {MouseEvent} ev\r\n */\r\nfunction highlightListener(ev) {\r\n  ev.stopPropagation();\r\n  const varElem = /** @type {HTMLElement} */ (ev.target);\r\n  const hightligtedElems = highlightVars(varElem);\r\n  const resetListener = () => {\r\n    const hlColor = getHighlightColor(varElem);\r\n    hightligtedElems.forEach(el => removeHighlight(el, hlColor));\r\n    [...HL_COLORS.keys()].forEach(key => HL_COLORS.set(key, true));\r\n  };\r\n  if (hightligtedElems.length) {\r\n    document.body.addEventListener("click", resetListener, { once: true });\r\n  }\r\n}\r\n\r\n// availability of highlight colors. colors from var.css\r\nconst HL_COLORS = new Map([\r\n  ["respec-hl-c1", true],\r\n  ["respec-hl-c2", true],\r\n  ["respec-hl-c3", true],\r\n  ["respec-hl-c4", true],\r\n  ["respec-hl-c5", true],\r\n  ["respec-hl-c6", true],\r\n  ["respec-hl-c7", true],\r\n]);\r\n\r\n/**\r\n * @param {HTMLElement} target\r\n */\r\nfunction getHighlightColor(target) {\r\n  // return current colors if applicable\r\n  const { value } = target.classList;\r\n  const re = /respec-hl-\\w+/;\r\n  const activeClass = re.test(value) && value.match(re);\r\n  if (activeClass) return activeClass[0];\r\n\r\n  // first color preference\r\n  if (HL_COLORS.get("respec-hl-c1") === true) return "respec-hl-c1";\r\n\r\n  // otherwise get some other available color\r\n  return [...HL_COLORS.keys()].find(c => HL_COLORS.get(c)) || "respec-hl-c1";\r\n}\r\n\r\n/**\r\n * @param {HTMLElement} varElem\r\n */\r\nfunction highlightVars(varElem) {\r\n  const textContent = norm(varElem.textContent);\r\n  const parent = /** @type {HTMLElement} */ (\r\n    varElem.closest(".algorithm, section")\r\n  );\r\n  if (!parent) return [];\r\n  const highlightColor = getHighlightColor(varElem);\r\n\r\n  const varsToHighlight = [...parent.querySelectorAll("var")].filter(\r\n    el =>\r\n      norm(el.textContent) === textContent &&\r\n      el.closest(".algorithm, section") === parent\r\n  );\r\n\r\n  // update availability of highlight color\r\n  const colorStatus = varsToHighlight[0].classList.contains("respec-hl");\r\n  HL_COLORS.set(highlightColor, colorStatus);\r\n\r\n  // highlight vars\r\n  if (colorStatus) {\r\n    varsToHighlight.forEach(el => removeHighlight(el, highlightColor));\r\n    return [];\r\n  } else {\r\n    varsToHighlight.forEach(el => addHighlight(el, highlightColor));\r\n  }\r\n  return varsToHighlight;\r\n}\r\n\r\n/**\r\n * @param {HTMLElement} el\r\n * @param {string} highlightColor\r\n */\r\nfunction removeHighlight(el, highlightColor) {\r\n  el.classList.remove("respec-hl", highlightColor);\r\n  // clean up empty class attributes so they don\'t come in export\r\n  if (!el.classList.length) el.removeAttribute("class");\r\n}\r\n\r\n/**\r\n * @param {HTMLElement} elem\r\n * @param {string} highlightColor\r\n */\r\nfunction addHighlight(elem, highlightColor) {\r\n  elem.classList.add("respec-hl", highlightColor);\r\n}\r\n\r\n/**\r\n * Same as `norm` from src/core/utils, but our build process doesn\'t allow\r\n * imports in runtime scripts, so duplicated here.\r\n * @param {string} str\r\n */\r\nfunction norm(str) {\r\n  return str.trim().replace(/\\s+/g, " ");\r\n}\r\n})()'}),Ph=Object.freeze({__proto__:null,default:'(() => {\n// @ts-check\r\nif (document.respec) {\r\n  document.respec.ready.then(setupPanel);\r\n} else {\r\n  setupPanel();\r\n}\r\n\r\nfunction setupPanel() {\r\n  const listener = panelListener();\r\n  document.body.addEventListener("keydown", listener);\r\n  document.body.addEventListener("click", listener);\r\n}\r\n\r\nfunction panelListener() {\r\n  /** @type {HTMLElement | null} */\r\n  let panel = null;\r\n  /**\r\n   * @param {KeyboardEvent|MouseEvent} event\r\n   */\r\n  return event => {\r\n    const { target, type } = event;\r\n\r\n    if (!(target instanceof HTMLElement)) return;\r\n\r\n    // For keys, we only care about Enter key to activate the panel\r\n    // otherwise it\'s activated via a click.\r\n    if (\r\n      type === "keydown" &&\r\n      /** @type {KeyboardEvent} */ (event).key !== "Enter"\r\n    )\r\n      return;\r\n\r\n    const action = deriveAction(event);\r\n\r\n    switch (action) {\r\n      case "show": {\r\n        hidePanel(panel);\r\n        /** @type {HTMLElement | null} */\r\n        const dfn = target.closest("dfn, .index-term");\r\n        if (!dfn?.id) break;\r\n        panel = document.getElementById(`dfn-panel-for-${dfn.id}`);\r\n        if (!panel) break;\r\n        const coords = deriveCoordinates(\r\n          /** @type {MouseEvent|KeyboardEvent} */ (event)\r\n        );\r\n        displayPanel(dfn, panel, coords);\r\n        break;\r\n      }\r\n      case "dock": {\r\n        if (panel) {\r\n          panel.style.left = "";\r\n          panel.style.top = "";\r\n          panel.classList.add("docked");\r\n        }\r\n        break;\r\n      }\r\n      case "hide": {\r\n        hidePanel(panel);\r\n        panel = null;\r\n        break;\r\n      }\r\n    }\r\n  };\r\n}\r\n\r\n/**\r\n * @param {MouseEvent|KeyboardEvent} event\r\n */\r\nfunction deriveCoordinates(event) {\r\n  const target = /** @type HTMLElement */ (event.target);\r\n\r\n  // We prevent synthetic AT clicks from putting\r\n  // the dialog in a weird place. The AT events sometimes\r\n  // lack coordinates, so they have clientX/Y = 0\r\n  const rect = target.getBoundingClientRect();\r\n  if (\r\n    event instanceof MouseEvent &&\r\n    event.clientX >= rect.left &&\r\n    event.clientY >= rect.top\r\n  ) {\r\n    // The event probably happened inside the bounding rect...\r\n    return { x: event.clientX, y: event.clientY };\r\n  }\r\n\r\n  // Offset to the middle of the element\r\n  const x = rect.x + rect.width / 2;\r\n  // Placed at the bottom of the element\r\n  const y = rect.y + rect.height;\r\n  return { x, y };\r\n}\r\n\r\n/**\r\n * @param {Event} event\r\n */\r\nfunction deriveAction(event) {\r\n  const target = /** @type {HTMLElement} */ (event.target);\r\n  const hitALink = !!target.closest("a");\r\n  if (target.closest("dfn:not([data-cite]), .index-term")) {\r\n    return hitALink ? "none" : "show";\r\n  }\r\n  if (target.closest(".dfn-panel")) {\r\n    if (hitALink) {\r\n      return target.classList.contains("self-link") ? "hide" : "dock";\r\n    }\r\n\r\n    const panel = /** @type {HTMLElement} */ (target.closest(".dfn-panel"));\r\n    return panel.classList.contains("docked") ? "hide" : "none";\r\n  }\r\n  if (document.querySelector(".dfn-panel:not([hidden])")) {\r\n    return "hide";\r\n  }\r\n  return "none";\r\n}\r\n\r\n/**\r\n * @param {HTMLElement} dfn\r\n * @param {HTMLElement} panel\r\n * @param {{ x: number, y: number }} clickPosition\r\n */\r\nfunction displayPanel(dfn, panel, { x, y }) {\r\n  panel.hidden = false;\r\n  // distance (px) between edge of panel and the pointing triangle (caret)\r\n  const MARGIN = 20;\r\n\r\n  const dfnRects = dfn.getClientRects();\r\n  // Find the `top` offset when the `dfn` can be spread across multiple lines\r\n  let closestTop = 0;\r\n  let minDiff = Infinity;\r\n  for (const rect of dfnRects) {\r\n    const { top, bottom } = rect;\r\n    const diffFromClickY = Math.abs((top + bottom) / 2 - y);\r\n    if (diffFromClickY < minDiff) {\r\n      minDiff = diffFromClickY;\r\n      closestTop = top;\r\n    }\r\n  }\r\n\r\n  const top = window.scrollY + closestTop + dfnRects[0].height;\r\n  const left = x - MARGIN;\r\n  panel.style.left = `${left}px`;\r\n  panel.style.top = `${top}px`;\r\n\r\n  // Find if the panel is flowing out of the window\r\n  const panelRect = panel.getBoundingClientRect();\r\n  const SCREEN_WIDTH = Math.min(window.innerWidth, window.screen.width);\r\n  if (panelRect.right > SCREEN_WIDTH) {\r\n    const newLeft = Math.max(MARGIN, x + MARGIN - panelRect.width);\r\n    const newCaretOffset = left - newLeft;\r\n    panel.style.left = `${newLeft}px`;\r\n    /** @type {HTMLElement | null} */\r\n    const caret = panel.querySelector(".caret");\r\n    if (caret) caret.style.left = `${newCaretOffset}px`;\r\n  }\r\n\r\n  // As it\'s a dialog, we trap focus.\r\n  // TODO: when  becomes a implemented, we should really\r\n  // use that.\r\n  trapFocus(panel, dfn);\r\n}\r\n\r\n/**\r\n * @param {HTMLElement} panel\r\n * @param {HTMLElement} dfn\r\n * @returns\r\n */\r\nfunction trapFocus(panel, dfn) {\r\n  /** @type NodeListOf elements */\r\n  const anchors = panel.querySelectorAll("a[href]");\r\n  // No need to trap focus\r\n  if (!anchors.length) return;\r\n\r\n  // Move focus to first anchor element\r\n  const first = anchors.item(0);\r\n  first.focus();\r\n\r\n  const trapListener = createTrapListener(anchors, panel, dfn);\r\n  panel.addEventListener("keydown", trapListener);\r\n\r\n  // Hiding the panel releases the trap\r\n  const mo = new MutationObserver(records => {\r\n    const [record] = records;\r\n    const target = /** @type HTMLElement */ (record.target);\r\n    if (target.hidden) {\r\n      panel.removeEventListener("keydown", trapListener);\r\n      mo.disconnect();\r\n    }\r\n  });\r\n  mo.observe(panel, { attributes: true, attributeFilter: ["hidden"] });\r\n}\r\n\r\n/**\r\n *\r\n * @param {NodeListOf} anchors\r\n * @param {HTMLElement} panel\r\n * @param {HTMLElement} dfn\r\n * @returns\r\n */\r\nfunction createTrapListener(anchors, panel, dfn) {\r\n  const lastIndex = anchors.length - 1;\r\n  let currentIndex = 0;\r\n  /**\r\n   * @param {KeyboardEvent} event\r\n   */\r\n  return event => {\r\n    switch (event.key) {\r\n      // Hitting "Tab" traps us in a nice loop around elements.\r\n      case "Tab": {\r\n        event.preventDefault();\r\n        currentIndex += event.shiftKey ? -1 : +1;\r\n        if (currentIndex < 0) {\r\n          currentIndex = lastIndex;\r\n        } else if (currentIndex > lastIndex) {\r\n          currentIndex = 0;\r\n        }\r\n        anchors.item(currentIndex).focus();\r\n        break;\r\n      }\r\n\r\n      // Hitting "Enter" on an anchor releases the trap.\r\n      case "Enter":\r\n        hidePanel(panel);\r\n        break;\r\n\r\n      // Hitting "Escape" returns focus to dfn.\r\n      case "Escape":\r\n        hidePanel(panel);\r\n        dfn.focus();\r\n        return;\r\n    }\r\n  };\r\n}\r\n\r\n/** @param {HTMLElement | null} panel */\r\nfunction hidePanel(panel) {\r\n  if (!panel) return;\r\n  panel.hidden = true;\r\n  panel.classList.remove("docked");\r\n}\r\n})()'})}();
 //# sourceMappingURL=respec-w3c.js.map
diff --git a/builds/respec-w3c.js.map b/builds/respec-w3c.js.map
index 95ab1d7963..a9d2172a11 100644
--- a/builds/respec-w3c.js.map
+++ b/builds/respec-w3c.js.map
@@ -1 +1 @@
-{"version":3,"file":"respec-w3c.js","sources":["../src/core/l10n.js","../node_modules/idb/build/index.js","../node_modules/webidl2/lib/error.js","../node_modules/webidl2/lib/productions/base.js","../node_modules/webidl2/lib/validators/helpers.js","../node_modules/webidl2/lib/productions/array-base.js","../node_modules/webidl2/lib/productions/token.js","../node_modules/webidl2/lib/productions/extended-attributes.js","../node_modules/webidl2/lib/productions/type.js","../node_modules/webidl2/lib/productions/default.js","../node_modules/webidl2/lib/productions/argument.js","../node_modules/webidl2/lib/productions/operation.js","../node_modules/webidl2/lib/productions/attribute.js","../node_modules/webidl2/lib/productions/helpers.js","../node_modules/webidl2/lib/tokeniser.js","../node_modules/webidl2/lib/productions/enum.js","../node_modules/webidl2/lib/productions/includes.js","../node_modules/webidl2/lib/productions/typedef.js","../node_modules/webidl2/lib/productions/callback.js","../node_modules/webidl2/lib/productions/container.js","../node_modules/webidl2/lib/productions/constant.js","../node_modules/webidl2/lib/productions/iterable.js","../node_modules/webidl2/lib/productions/constructor.js","../node_modules/webidl2/lib/productions/interface.js","../node_modules/webidl2/lib/validators/interface.js","../node_modules/webidl2/lib/productions/mixin.js","../node_modules/webidl2/lib/productions/field.js","../node_modules/webidl2/lib/productions/dictionary.js","../node_modules/webidl2/lib/productions/namespace.js","../node_modules/webidl2/lib/productions/callback-interface.js","../node_modules/webidl2/lib/webidl2.js","../node_modules/webidl2/lib/writer.js","../node_modules/webidl2/lib/validator.js","../node_modules/cddlparser/dist/tokens.js","../node_modules/cddlparser/dist/ast.js","../node_modules/cddlparser/dist/errors.js","../node_modules/cddlparser/dist/utils.js","../node_modules/cddlparser/dist/lexer.js","../node_modules/cddlparser/dist/parser.js","../node_modules/sniffy-mimetype/index.js","../node_modules/marked/lib/marked.esm.js","../js/deps/builds/pluralize.js","../node_modules/hyperhtml/esm.js","../src/core/import-maps.js","../src/core/utils.js","../src/core/pubsubhub.js","../src/core/include-config.js","../src/core/exporter.js","../src/core/text-loader.js","../src/core/worker.js","../src/core/respec-global.js","../src/core/post-process.js","../src/core/pre-process.js","../src/core/base-runner.js","../src/core/override-configuration.js","../src/styles/ui.css.js","../src/core/markdown.js","../src/core/ui.js","../src/respec.js","../profiles/w3c.js","../src/core/location-hash.js","../src/w3c/group.js","../src/core/templates/show-link.js","../src/core/templates/show-logo.js","../src/core/templates/show-people.js","../src/w3c/templates/headers.js","../src/w3c/templates/cgbg-headers.js","../src/w3c/templates/sotd.js","../src/w3c/templates/cgbg-sotd.js","../src/w3c/headers.js","../src/core/defaults.js","../src/w3c/defaults.js","../src/styles/respec.css.js","../src/core/style.js","../src/w3c/style.js","../src/core/github.js","../src/core/sections.js","../src/core/data-include.js","../src/core/reindent.js","../src/core/title.js","../src/w3c/level.js","../src/w3c/abstract.js","../src/core/data-transform.js","../src/core/data-abbr.js","../src/styles/algorithms.css.js","../src/core/algorithms.js","../src/core/inline-idl-parser.js","../src/core/biblio-db.js","../src/core/biblio.js","../src/core/render-biblio.js","../src/core/inlines.js","../src/w3c/conformance.js","../src/core/dfn-validators.js","../src/core/dfn-map.js","../src/core/dfn.js","../src/core/pluralize.js","../src/styles/examples.css.js","../src/core/examples.js","../src/styles/issues-notes.css.js","../src/core/issues-notes.js","../src/core/best-practices.js","../src/core/figures.js","../src/core/tables.js","../src/core/dfn-finder.js","../src/core/clipboard.js","../src/core/webidl-clipboard.js","../src/styles/webidl.css.js","../src/core/webidl.js","../src/styles/cddl.css.js","../src/core/cddl.js","../src/core/data-cite.js","../src/core/link-to-dfn.js","../src/core/xref-db.js","../src/core/xref.js","../src/core/xref-headings-db.js","../src/core/xref-headings.js","../src/styles/dfn-index.css.js","../src/core/dfn-index.js","../src/core/contrib.js","../src/core/fix-headers.js","../src/core/webidl-index.js","../src/core/cddl-index.js","../src/core/structure.js","../src/core/informative.js","../src/core/id-headers.js","../src/styles/caniuse.css.js","../src/core/caniuse.js","../src/styles/implementation-status.css.js","../src/core/implementation-status.js","../src/styles/mdn-annotation.css.js","../src/core/mdn-annotation.js","../src/ui/save-html.js","../src/ui/search-specref.js","../src/ui/search-xref.js","../src/ui/about-respec.js","../src/core/seo.js","../src/w3c/seo.js","../src/styles/highlight.css.js","../src/core/highlight.js","../src/core/data-tests.js","../src/core/list-sorter.js","../src/styles/var.css.js","../src/core/highlight-vars.js","../src/styles/datatype.css.js","../src/core/data-type.js","../src/core/anchor-expander.js","../src/styles/dfn-panel.css.js","../src/core/dfn-panel.js","../src/core/custom-elements/rs-changelog.js","../src/core/custom-elements/index.js","../src/core/web-monetization.js","../src/core/dfn-contract.js","../src/core/before-save.js","../src/core/linter-rules/check-charset.js","../src/core/linter-rules/check-punctuation.js","../src/core/linter-rules/check-internal-slots.js","../src/core/linter-rules/local-refs-exist.js","../src/core/linter-rules/no-captionless-tables.js","../src/core/linter-rules/no-unused-dfns.js","../src/core/linter-rules/no-headingless-sections.js","../src/core/linter-rules/no-unused-vars.js","../src/core/linter-rules/no-dfn-in-abstract.js","../src/w3c/linter-rules/required-sections.js","../src/core/linter-rules/wpt-tests-exist.js","../src/core/linter-rules/no-http-props.js","../src/core/linter-rules/a11y.js","../src/core/linter-rules/informative-dfn.js"],"sourcesContent":["// @ts-check\n/**\n * Module core/l10n\n *\n * Looks at the lang attribute on the root element and uses it\n * to manage the config.l10n object so that other parts of the system can\n * localize their text.\n */\n\nexport const name = \"core/l10n\";\n\nconst html = document.documentElement;\n// Explicitly default lang and dir on  if not set.\n// We assume English and ltr as default for international standards.\nif (!html?.hasAttribute(\"lang\")) {\n  html.lang = \"en\";\n  if (!html.hasAttribute(\"dir\")) {\n    html.dir = \"ltr\";\n  }\n}\n\n/** @type {Record} */\nexport const l10n = {};\n\nexport const lang = html?.lang ?? \"en\";\n\n/**\n * @param {Conf} config\n */\nexport function run(config) {\n  config.l10n = l10n[lang] || l10n.en;\n}\n","const instanceOfAny = (object, constructors) => constructors.some((c) => object instanceof c);\n\nlet idbProxyableTypes;\nlet cursorAdvanceMethods;\n// This is a function to prevent it throwing up in node environments.\nfunction getIdbProxyableTypes() {\n    return (idbProxyableTypes ||\n        (idbProxyableTypes = [\n            IDBDatabase,\n            IDBObjectStore,\n            IDBIndex,\n            IDBCursor,\n            IDBTransaction,\n        ]));\n}\n// This is a function to prevent it throwing up in node environments.\nfunction getCursorAdvanceMethods() {\n    return (cursorAdvanceMethods ||\n        (cursorAdvanceMethods = [\n            IDBCursor.prototype.advance,\n            IDBCursor.prototype.continue,\n            IDBCursor.prototype.continuePrimaryKey,\n        ]));\n}\nconst transactionDoneMap = new WeakMap();\nconst transformCache = new WeakMap();\nconst reverseTransformCache = new WeakMap();\nfunction promisifyRequest(request) {\n    const promise = new Promise((resolve, reject) => {\n        const unlisten = () => {\n            request.removeEventListener('success', success);\n            request.removeEventListener('error', error);\n        };\n        const success = () => {\n            resolve(wrap(request.result));\n            unlisten();\n        };\n        const error = () => {\n            reject(request.error);\n            unlisten();\n        };\n        request.addEventListener('success', success);\n        request.addEventListener('error', error);\n    });\n    // This mapping exists in reverseTransformCache but doesn't exist in transformCache. This\n    // is because we create many promises from a single IDBRequest.\n    reverseTransformCache.set(promise, request);\n    return promise;\n}\nfunction cacheDonePromiseForTransaction(tx) {\n    // Early bail if we've already created a done promise for this transaction.\n    if (transactionDoneMap.has(tx))\n        return;\n    const done = new Promise((resolve, reject) => {\n        const unlisten = () => {\n            tx.removeEventListener('complete', complete);\n            tx.removeEventListener('error', error);\n            tx.removeEventListener('abort', error);\n        };\n        const complete = () => {\n            resolve();\n            unlisten();\n        };\n        const error = () => {\n            reject(tx.error || new DOMException('AbortError', 'AbortError'));\n            unlisten();\n        };\n        tx.addEventListener('complete', complete);\n        tx.addEventListener('error', error);\n        tx.addEventListener('abort', error);\n    });\n    // Cache it for later retrieval.\n    transactionDoneMap.set(tx, done);\n}\nlet idbProxyTraps = {\n    get(target, prop, receiver) {\n        if (target instanceof IDBTransaction) {\n            // Special handling for transaction.done.\n            if (prop === 'done')\n                return transactionDoneMap.get(target);\n            // Make tx.store return the only store in the transaction, or undefined if there are many.\n            if (prop === 'store') {\n                return receiver.objectStoreNames[1]\n                    ? undefined\n                    : receiver.objectStore(receiver.objectStoreNames[0]);\n            }\n        }\n        // Else transform whatever we get back.\n        return wrap(target[prop]);\n    },\n    set(target, prop, value) {\n        target[prop] = value;\n        return true;\n    },\n    has(target, prop) {\n        if (target instanceof IDBTransaction &&\n            (prop === 'done' || prop === 'store')) {\n            return true;\n        }\n        return prop in target;\n    },\n};\nfunction replaceTraps(callback) {\n    idbProxyTraps = callback(idbProxyTraps);\n}\nfunction wrapFunction(func) {\n    // Due to expected object equality (which is enforced by the caching in `wrap`), we\n    // only create one new func per func.\n    // Cursor methods are special, as the behaviour is a little more different to standard IDB. In\n    // IDB, you advance the cursor and wait for a new 'success' on the IDBRequest that gave you the\n    // cursor. It's kinda like a promise that can resolve with many values. That doesn't make sense\n    // with real promises, so each advance methods returns a new promise for the cursor object, or\n    // undefined if the end of the cursor has been reached.\n    if (getCursorAdvanceMethods().includes(func)) {\n        return function (...args) {\n            // Calling the original function with the proxy as 'this' causes ILLEGAL INVOCATION, so we use\n            // the original object.\n            func.apply(unwrap(this), args);\n            return wrap(this.request);\n        };\n    }\n    return function (...args) {\n        // Calling the original function with the proxy as 'this' causes ILLEGAL INVOCATION, so we use\n        // the original object.\n        return wrap(func.apply(unwrap(this), args));\n    };\n}\nfunction transformCachableValue(value) {\n    if (typeof value === 'function')\n        return wrapFunction(value);\n    // This doesn't return, it just creates a 'done' promise for the transaction,\n    // which is later returned for transaction.done (see idbObjectHandler).\n    if (value instanceof IDBTransaction)\n        cacheDonePromiseForTransaction(value);\n    if (instanceOfAny(value, getIdbProxyableTypes()))\n        return new Proxy(value, idbProxyTraps);\n    // Return the same value back if we're not going to transform it.\n    return value;\n}\nfunction wrap(value) {\n    // We sometimes generate multiple promises from a single IDBRequest (eg when cursoring), because\n    // IDB is weird and a single IDBRequest can yield many responses, so these can't be cached.\n    if (value instanceof IDBRequest)\n        return promisifyRequest(value);\n    // If we've already transformed this value before, reuse the transformed value.\n    // This is faster, but it also provides object equality.\n    if (transformCache.has(value))\n        return transformCache.get(value);\n    const newValue = transformCachableValue(value);\n    // Not all types are transformed.\n    // These may be primitive types, so they can't be WeakMap keys.\n    if (newValue !== value) {\n        transformCache.set(value, newValue);\n        reverseTransformCache.set(newValue, value);\n    }\n    return newValue;\n}\nconst unwrap = (value) => reverseTransformCache.get(value);\n\n/**\n * Open a database.\n *\n * @param name Name of the database.\n * @param version Schema version.\n * @param callbacks Additional callbacks.\n */\nfunction openDB(name, version, { blocked, upgrade, blocking, terminated } = {}) {\n    const request = indexedDB.open(name, version);\n    const openPromise = wrap(request);\n    if (upgrade) {\n        request.addEventListener('upgradeneeded', (event) => {\n            upgrade(wrap(request.result), event.oldVersion, event.newVersion, wrap(request.transaction), event);\n        });\n    }\n    if (blocked) {\n        request.addEventListener('blocked', (event) => blocked(\n        // Casting due to https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/1405\n        event.oldVersion, event.newVersion, event));\n    }\n    openPromise\n        .then((db) => {\n        if (terminated)\n            db.addEventListener('close', () => terminated());\n        if (blocking) {\n            db.addEventListener('versionchange', (event) => blocking(event.oldVersion, event.newVersion, event));\n        }\n    })\n        .catch(() => { });\n    return openPromise;\n}\n/**\n * Delete a database.\n *\n * @param name Name of the database.\n */\nfunction deleteDB(name, { blocked } = {}) {\n    const request = indexedDB.deleteDatabase(name);\n    if (blocked) {\n        request.addEventListener('blocked', (event) => blocked(\n        // Casting due to https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/1405\n        event.oldVersion, event));\n    }\n    return wrap(request).then(() => undefined);\n}\n\nconst readMethods = ['get', 'getKey', 'getAll', 'getAllKeys', 'count'];\nconst writeMethods = ['put', 'add', 'delete', 'clear'];\nconst cachedMethods = new Map();\nfunction getMethod(target, prop) {\n    if (!(target instanceof IDBDatabase &&\n        !(prop in target) &&\n        typeof prop === 'string')) {\n        return;\n    }\n    if (cachedMethods.get(prop))\n        return cachedMethods.get(prop);\n    const targetFuncName = prop.replace(/FromIndex$/, '');\n    const useIndex = prop !== targetFuncName;\n    const isWrite = writeMethods.includes(targetFuncName);\n    if (\n    // Bail if the target doesn't exist on the target. Eg, getAll isn't in Edge.\n    !(targetFuncName in (useIndex ? IDBIndex : IDBObjectStore).prototype) ||\n        !(isWrite || readMethods.includes(targetFuncName))) {\n        return;\n    }\n    const method = async function (storeName, ...args) {\n        // isWrite ? 'readwrite' : undefined gzipps better, but fails in Edge :(\n        const tx = this.transaction(storeName, isWrite ? 'readwrite' : 'readonly');\n        let target = tx.store;\n        if (useIndex)\n            target = target.index(args.shift());\n        // Must reject if op rejects.\n        // If it's a write operation, must reject if tx.done rejects.\n        // Must reject with op rejection first.\n        // Must resolve with op value.\n        // Must handle both promises (no unhandled rejections)\n        return (await Promise.all([\n            target[targetFuncName](...args),\n            isWrite && tx.done,\n        ]))[0];\n    };\n    cachedMethods.set(prop, method);\n    return method;\n}\nreplaceTraps((oldTraps) => ({\n    ...oldTraps,\n    get: (target, prop, receiver) => getMethod(target, prop) || oldTraps.get(target, prop, receiver),\n    has: (target, prop) => !!getMethod(target, prop) || oldTraps.has(target, prop),\n}));\n\nconst advanceMethodProps = ['continue', 'continuePrimaryKey', 'advance'];\nconst methodMap = {};\nconst advanceResults = new WeakMap();\nconst ittrProxiedCursorToOriginalProxy = new WeakMap();\nconst cursorIteratorTraps = {\n    get(target, prop) {\n        if (!advanceMethodProps.includes(prop))\n            return target[prop];\n        let cachedFunc = methodMap[prop];\n        if (!cachedFunc) {\n            cachedFunc = methodMap[prop] = function (...args) {\n                advanceResults.set(this, ittrProxiedCursorToOriginalProxy.get(this)[prop](...args));\n            };\n        }\n        return cachedFunc;\n    },\n};\nasync function* iterate(...args) {\n    // tslint:disable-next-line:no-this-assignment\n    let cursor = this;\n    if (!(cursor instanceof IDBCursor)) {\n        cursor = await cursor.openCursor(...args);\n    }\n    if (!cursor)\n        return;\n    cursor = cursor;\n    const proxiedCursor = new Proxy(cursor, cursorIteratorTraps);\n    ittrProxiedCursorToOriginalProxy.set(proxiedCursor, cursor);\n    // Map this double-proxy back to the original, so other cursor methods work.\n    reverseTransformCache.set(proxiedCursor, unwrap(cursor));\n    while (cursor) {\n        yield proxiedCursor;\n        // If one of the advancing methods was not called, call continue().\n        cursor = await (advanceResults.get(proxiedCursor) || cursor.continue());\n        advanceResults.delete(proxiedCursor);\n    }\n}\nfunction isIteratorProp(target, prop) {\n    return ((prop === Symbol.asyncIterator &&\n        instanceOfAny(target, [IDBIndex, IDBObjectStore, IDBCursor])) ||\n        (prop === 'iterate' && instanceOfAny(target, [IDBIndex, IDBObjectStore])));\n}\nreplaceTraps((oldTraps) => ({\n    ...oldTraps,\n    get(target, prop, receiver) {\n        if (isIteratorProp(target, prop))\n            return iterate;\n        return oldTraps.get(target, prop, receiver);\n    },\n    has(target, prop) {\n        return isIteratorProp(target, prop) || oldTraps.has(target, prop);\n    },\n}));\n\nexport { deleteDB, openDB, unwrap, wrap };\n","/**\n * @param {string} text\n */\nfunction lastLine(text) {\n  const splitted = text.split(\"\\n\");\n  return splitted[splitted.length - 1];\n}\n\nfunction appendIfExist(base, target) {\n  let result = base;\n  if (target) {\n    result += ` ${target}`;\n  }\n  return result;\n}\n\nfunction contextAsText(node) {\n  const hierarchy = [node];\n  while (node && node.parent) {\n    const { parent } = node;\n    hierarchy.unshift(parent);\n    node = parent;\n  }\n  return hierarchy.map((n) => appendIfExist(n.type, n.name)).join(\" -> \");\n}\n\n/**\n * @typedef {object} WebIDL2ErrorOptions\n * @property {\"error\" | \"warning\"} [level]\n * @property {Function} [autofix]\n * @property {string} [ruleName]\n *\n * @typedef {ReturnType} WebIDLErrorData\n *\n * @param {string} message error message\n * @param {*} position\n * @param {*} current\n * @param {*} message\n * @param {\"Syntax\" | \"Validation\"} kind error type\n * @param {WebIDL2ErrorOptions=} options\n */\nfunction error(\n  source,\n  position,\n  current,\n  message,\n  kind,\n  { level = \"error\", autofix, ruleName } = {},\n) {\n  /**\n   * @param {number} count\n   */\n  function sliceTokens(count) {\n    return count > 0\n      ? source.slice(position, position + count)\n      : source.slice(Math.max(position + count, 0), position);\n  }\n\n  /**\n   * @param {import(\"./tokeniser.js\").Token[]} inputs\n   * @param {object} [options]\n   * @param {boolean} [options.precedes]\n   * @returns\n   */\n  function tokensToText(inputs, { precedes } = {}) {\n    const text = inputs.map((t) => t.trivia + t.value).join(\"\");\n    const nextToken = source[position];\n    if (nextToken.type === \"eof\") {\n      return text;\n    }\n    if (precedes) {\n      return text + nextToken.trivia;\n    }\n    return text.slice(nextToken.trivia.length);\n  }\n\n  const maxTokens = 5; // arbitrary but works well enough\n  const line =\n    source[position].type !== \"eof\"\n      ? source[position].line\n      : source.length > 1\n        ? source[position - 1].line\n        : 1;\n\n  const precedingLastLine = lastLine(\n    tokensToText(sliceTokens(-maxTokens), { precedes: true }),\n  );\n\n  const subsequentTokens = sliceTokens(maxTokens);\n  const subsequentText = tokensToText(subsequentTokens);\n  const subsequentFirstLine = subsequentText.split(\"\\n\")[0];\n\n  const spaced = \" \".repeat(precedingLastLine.length) + \"^\";\n  const sourceContext = precedingLastLine + subsequentFirstLine + \"\\n\" + spaced;\n\n  const contextType = kind === \"Syntax\" ? \"since\" : \"inside\";\n  const inSourceName = source.name ? ` in ${source.name}` : \"\";\n  const grammaticalContext =\n    current && current.name\n      ? `, ${contextType} \\`${current.partial ? \"partial \" : \"\"}${contextAsText(\n          current,\n        )}\\``\n      : \"\";\n  const context = `${kind} error at line ${line}${inSourceName}${grammaticalContext}:\\n${sourceContext}`;\n  return {\n    message: `${context} ${message}`,\n    bareMessage: message,\n    context,\n    line,\n    sourceName: source.name,\n    level,\n    ruleName,\n    autofix,\n    input: subsequentText,\n    tokens: subsequentTokens,\n  };\n}\n\n/**\n * @param {string} message error message\n */\nexport function syntaxError(source, position, current, message) {\n  return error(source, position, current, message, \"Syntax\");\n}\n\n/**\n * @param {string} message error message\n * @param {WebIDL2ErrorOptions} [options]\n */\nexport function validationError(\n  token,\n  current,\n  ruleName,\n  message,\n  options = {},\n) {\n  options.ruleName = ruleName;\n  return error(\n    current.source,\n    token.index,\n    current,\n    message,\n    \"Validation\",\n    options,\n  );\n}\n","export class Base {\n  /**\n   * @param {object} initializer\n   * @param {Base[\"source\"]} initializer.source\n   * @param {Base[\"tokens\"]} initializer.tokens\n   */\n  constructor({ source, tokens }) {\n    Object.defineProperties(this, {\n      source: { value: source },\n      tokens: { value: tokens, writable: true },\n      parent: { value: null, writable: true },\n      this: { value: this }, // useful when escaping from proxy\n    });\n  }\n\n  toJSON() {\n    const json = { type: undefined, name: undefined, inheritance: undefined };\n    let proto = this;\n    while (proto !== Object.prototype) {\n      const descMap = Object.getOwnPropertyDescriptors(proto);\n      for (const [key, value] of Object.entries(descMap)) {\n        if (value.enumerable || value.get) {\n          // @ts-ignore - allow indexing here\n          json[key] = this[key];\n        }\n      }\n      proto = Object.getPrototypeOf(proto);\n    }\n    return json;\n  }\n}\n","/**\n * @typedef {import(\"../validator.js\").Definitions} Definitions\n * @typedef {import(\"../productions/dictionary.js\").Dictionary} Dictionary\n * @typedef {import(\"../../lib/productions/type\").Type} Type\n *\n * @param {Type} idlType\n * @param {Definitions} defs\n * @param {object} [options]\n * @param {boolean} [options.useNullableInner] use when the input idlType is nullable and you want to use its inner type\n * @return {{ reference: *, dictionary: Dictionary }} the type reference that ultimately includes dictionary.\n */\nexport function idlTypeIncludesDictionary(\n  idlType,\n  defs,\n  { useNullableInner } = {},\n) {\n  if (!idlType.union) {\n    const def = defs.unique.get(idlType.idlType);\n    if (!def) {\n      return;\n    }\n    if (def.type === \"typedef\") {\n      const { typedefIncludesDictionary } = defs.cache;\n      if (typedefIncludesDictionary.has(def)) {\n        // Note that this also halts when it met indeterminate state\n        // to prevent infinite recursion\n        return typedefIncludesDictionary.get(def);\n      }\n      defs.cache.typedefIncludesDictionary.set(def, undefined); // indeterminate state\n      const result = idlTypeIncludesDictionary(def.idlType, defs);\n      defs.cache.typedefIncludesDictionary.set(def, result);\n      if (result) {\n        return {\n          reference: idlType,\n          dictionary: result.dictionary,\n        };\n      }\n    }\n    if (def.type === \"dictionary\" && (useNullableInner || !idlType.nullable)) {\n      return {\n        reference: idlType,\n        dictionary: def,\n      };\n    }\n  }\n  for (const subtype of idlType.subtype) {\n    const result = idlTypeIncludesDictionary(subtype, defs);\n    if (result) {\n      if (subtype.union) {\n        return result;\n      }\n      return {\n        reference: subtype,\n        dictionary: result.dictionary,\n      };\n    }\n  }\n}\n\n/**\n * @param {Dictionary} dict dictionary type\n * @param {Definitions} defs\n * @return {boolean}\n */\nexport function dictionaryIncludesRequiredField(dict, defs) {\n  if (defs.cache.dictionaryIncludesRequiredField.has(dict)) {\n    return defs.cache.dictionaryIncludesRequiredField.get(dict);\n  }\n  // Set cached result to indeterminate to short-circuit circular definitions.\n  // The final result will be updated to true or false.\n  defs.cache.dictionaryIncludesRequiredField.set(dict, undefined);\n  let result = dict.members.some((field) => field.required);\n  if (!result && dict.inheritance) {\n    const superdict = defs.unique.get(dict.inheritance);\n    if (!superdict) {\n      // Assume required members in the supertype if it is unknown.\n      result = true;\n    } else if (dictionaryIncludesRequiredField(superdict, defs)) {\n      result = true;\n    }\n  }\n  defs.cache.dictionaryIncludesRequiredField.set(dict, result);\n  return result;\n}\n\n/**\n * For now this only checks the most frequent cases:\n * 1. direct inclusion of [EnforceRange]\n * 2. typedef of that\n *\n * More complex cases with dictionaries and records are not covered yet.\n *\n * @param {Type} idlType\n * @param {Definitions} defs\n */\nexport function idlTypeIncludesEnforceRange(idlType, defs) {\n  if (idlType.union) {\n    // TODO: This should ideally be checked too\n    return false;\n  }\n\n  if (idlType.extAttrs.some((e) => e.name === \"EnforceRange\")) {\n    return true;\n  }\n\n  const def = defs.unique.get(idlType.idlType);\n  if (def?.type !== \"typedef\") {\n    return false;\n  }\n\n  return def.idlType.extAttrs.some((e) => e.name === \"EnforceRange\");\n}\n","export class ArrayBase extends Array {\n  constructor({ source, tokens }) {\n    super();\n    Object.defineProperties(this, {\n      source: { value: source },\n      tokens: { value: tokens },\n      parent: { value: null, writable: true },\n    });\n  }\n}\n","import { Base } from \"./base.js\";\nimport { unescape } from \"./helpers.js\";\n\nexport class WrappedToken extends Base {\n  /**\n   * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n   * @param {string} type\n   */\n  static parser(tokeniser, type) {\n    return () => {\n      const value = tokeniser.consumeKind(type);\n      if (value) {\n        return new WrappedToken({\n          source: tokeniser.source,\n          tokens: { value },\n        });\n      }\n    };\n  }\n\n  get value() {\n    return unescape(this.tokens.value.value);\n  }\n\n  /** @param {import(\"../writer.js\").Writer} w */\n  write(w) {\n    return w.ts.wrap([\n      w.token(this.tokens.value),\n      w.token(this.tokens.separator),\n    ]);\n  }\n}\n\nexport class Eof extends WrappedToken {\n  /**\n   * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n   */\n  static parse(tokeniser) {\n    const value = tokeniser.consumeKind(\"eof\");\n    if (value) {\n      return new Eof({ source: tokeniser.source, tokens: { value } });\n    }\n  }\n\n  get type() {\n    return \"eof\";\n  }\n}\n","import { Base } from \"./base.js\";\nimport { ArrayBase } from \"./array-base.js\";\nimport { WrappedToken } from \"./token.js\";\nimport { list, argument_list, autoParenter, unescape } from \"./helpers.js\";\nimport { validationError } from \"../error.js\";\n\n/**\n * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n * @param {string} tokenName\n */\nfunction tokens(tokeniser, tokenName) {\n  return list(tokeniser, {\n    parser: WrappedToken.parser(tokeniser, tokenName),\n    listName: tokenName + \" list\",\n  });\n}\n\nconst extAttrValueSyntax = [\"identifier\", \"decimal\", \"integer\", \"string\"];\n\nconst shouldBeLegacyPrefixed = [\n  \"NoInterfaceObject\",\n  \"LenientSetter\",\n  \"LenientThis\",\n  \"TreatNonObjectAsNull\",\n  \"Unforgeable\",\n];\n\nconst renamedLegacies = new Map([\n  .../** @type {[string, string][]} */ (\n    shouldBeLegacyPrefixed.map((name) => [name, `Legacy${name}`])\n  ),\n  [\"NamedConstructor\", \"LegacyFactoryFunction\"],\n  [\"OverrideBuiltins\", \"LegacyOverrideBuiltIns\"],\n  [\"TreatNullAs\", \"LegacyNullToEmptyString\"],\n]);\n\n/**\n * This will allow a set of extended attribute values to be parsed.\n * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n */\nfunction extAttrListItems(tokeniser) {\n  for (const syntax of extAttrValueSyntax) {\n    const toks = tokens(tokeniser, syntax);\n    if (toks.length) {\n      return toks;\n    }\n  }\n  tokeniser.error(\n    `Expected identifiers, strings, decimals, or integers but none found`,\n  );\n}\n\nexport class ExtendedAttributeParameters extends Base {\n  /**\n   * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n   */\n  static parse(tokeniser) {\n    const tokens = { assign: tokeniser.consume(\"=\") };\n    const ret = autoParenter(\n      new ExtendedAttributeParameters({ source: tokeniser.source, tokens }),\n    );\n    ret.list = [];\n    if (tokens.assign) {\n      tokens.asterisk = tokeniser.consume(\"*\");\n      if (tokens.asterisk) {\n        return ret.this;\n      }\n      tokens.secondaryName = tokeniser.consumeKind(...extAttrValueSyntax);\n    }\n    tokens.open = tokeniser.consume(\"(\");\n    if (tokens.open) {\n      ret.list = ret.rhsIsList\n        ? // [Exposed=(Window,Worker)]\n          extAttrListItems(tokeniser)\n        : // [LegacyFactoryFunction=Audio(DOMString src)] or [Constructor(DOMString str)]\n          argument_list(tokeniser);\n      tokens.close =\n        tokeniser.consume(\")\") ||\n        tokeniser.error(\"Unexpected token in extended attribute argument list\");\n    } else if (tokens.assign && !tokens.secondaryName) {\n      tokeniser.error(\"No right hand side to extended attribute assignment\");\n    }\n    return ret.this;\n  }\n\n  get rhsIsList() {\n    return (\n      this.tokens.assign && !this.tokens.asterisk && !this.tokens.secondaryName\n    );\n  }\n\n  get rhsType() {\n    if (this.rhsIsList) {\n      return this.list[0].tokens.value.type + \"-list\";\n    }\n    if (this.tokens.asterisk) {\n      return \"*\";\n    }\n    if (this.tokens.secondaryName) {\n      return this.tokens.secondaryName.type;\n    }\n    return null;\n  }\n\n  /** @param {import(\"../writer.js\").Writer} w */\n  write(w) {\n    const { rhsType } = this;\n    return w.ts.wrap([\n      w.token(this.tokens.assign),\n      w.token(this.tokens.asterisk),\n      w.reference_token(this.tokens.secondaryName, this.parent),\n      w.token(this.tokens.open),\n      ...this.list.map((p) => {\n        return rhsType === \"identifier-list\"\n          ? w.identifier(p, this.parent)\n          : p.write(w);\n      }),\n      w.token(this.tokens.close),\n    ]);\n  }\n}\n\nexport class SimpleExtendedAttribute extends Base {\n  /**\n   * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n   */\n  static parse(tokeniser) {\n    const name = tokeniser.consumeKind(\"identifier\");\n    if (name) {\n      return new SimpleExtendedAttribute({\n        source: tokeniser.source,\n        tokens: { name },\n        params: ExtendedAttributeParameters.parse(tokeniser),\n      });\n    }\n  }\n\n  constructor({ source, tokens, params }) {\n    super({ source, tokens });\n    params.parent = this;\n    Object.defineProperty(this, \"params\", { value: params });\n  }\n\n  get type() {\n    return \"extended-attribute\";\n  }\n  get name() {\n    return this.tokens.name.value;\n  }\n  get rhs() {\n    const { rhsType: type, tokens, list } = this.params;\n    if (!type) {\n      return null;\n    }\n    const value = this.params.rhsIsList\n      ? list\n      : this.params.tokens.secondaryName\n        ? unescape(tokens.secondaryName.value)\n        : null;\n    return { type, value };\n  }\n  get arguments() {\n    const { rhsIsList, list } = this.params;\n    if (!list || rhsIsList) {\n      return [];\n    }\n    return list;\n  }\n\n  *validate(defs) {\n    const { name } = this;\n    if (name === \"LegacyNoInterfaceObject\") {\n      const message = `\\`[LegacyNoInterfaceObject]\\` extended attribute is an \\\nundesirable feature that may be removed from Web IDL in the future. Refer to the \\\n[relevant upstream PR](https://github.com/whatwg/webidl/pull/609) for more \\\ninformation.`;\n      yield validationError(\n        this.tokens.name,\n        this,\n        \"no-nointerfaceobject\",\n        message,\n        { level: \"warning\" },\n      );\n    } else if (renamedLegacies.has(name)) {\n      const message = `\\`[${name}]\\` extended attribute is a legacy feature \\\nthat is now renamed to \\`[${renamedLegacies.get(name)}]\\`. Refer to the \\\n[relevant upstream PR](https://github.com/whatwg/webidl/pull/870) for more \\\ninformation.`;\n      yield validationError(this.tokens.name, this, \"renamed-legacy\", message, {\n        level: \"warning\",\n        autofix: renameLegacyExtendedAttribute(this),\n      });\n    }\n    for (const arg of this.arguments) {\n      yield* arg.validate(defs);\n    }\n  }\n\n  /** @param {import(\"../writer.js\").Writer} w */\n  write(w) {\n    return w.ts.wrap([\n      w.ts.trivia(this.tokens.name.trivia),\n      w.ts.extendedAttribute(\n        w.ts.wrap([\n          w.ts.extendedAttributeReference(this.name),\n          this.params.write(w),\n        ]),\n      ),\n      w.token(this.tokens.separator),\n    ]);\n  }\n}\n\n/**\n * @param {SimpleExtendedAttribute} extAttr\n */\nfunction renameLegacyExtendedAttribute(extAttr) {\n  return () => {\n    const { name } = extAttr;\n    extAttr.tokens.name.value = renamedLegacies.get(name);\n    if (name === \"TreatNullAs\") {\n      extAttr.params.tokens = {};\n    }\n  };\n}\n\n// Note: we parse something simpler than the official syntax. It's all that ever\n// seems to be used\nexport class ExtendedAttributes extends ArrayBase {\n  /**\n   * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n   */\n  static parse(tokeniser) {\n    const tokens = {};\n    tokens.open = tokeniser.consume(\"[\");\n    const ret = new ExtendedAttributes({ source: tokeniser.source, tokens });\n    if (!tokens.open) return ret;\n    ret.push(\n      ...list(tokeniser, {\n        parser: SimpleExtendedAttribute.parse,\n        listName: \"extended attribute\",\n      }),\n    );\n    tokens.close =\n      tokeniser.consume(\"]\") ||\n      tokeniser.error(\n        \"Expected a closing token for the extended attribute list\",\n      );\n    if (!ret.length) {\n      tokeniser.unconsume(tokens.close.index);\n      tokeniser.error(\"An extended attribute list must not be empty\");\n    }\n    if (tokeniser.probe(\"[\")) {\n      tokeniser.error(\n        \"Illegal double extended attribute lists, consider merging them\",\n      );\n    }\n    return ret;\n  }\n\n  *validate(defs) {\n    for (const extAttr of this) {\n      yield* extAttr.validate(defs);\n    }\n  }\n\n  /** @param {import(\"../writer.js\").Writer} w */\n  write(w) {\n    if (!this.length) return \"\";\n    return w.ts.wrap([\n      w.token(this.tokens.open),\n      ...this.map((ea) => ea.write(w)),\n      w.token(this.tokens.close),\n    ]);\n  }\n}\n","import { Base } from \"./base.js\";\nimport {\n  unescape,\n  type_with_extended_attributes,\n  return_type,\n  primitive_type,\n  autoParenter,\n} from \"./helpers.js\";\nimport { stringTypes, typeNameKeywords } from \"../tokeniser.js\";\nimport { validationError } from \"../error.js\";\nimport { idlTypeIncludesDictionary } from \"../validators/helpers.js\";\nimport { ExtendedAttributes } from \"./extended-attributes.js\";\n\n/**\n * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n * @param {string} typeName\n */\nfunction generic_type(tokeniser, typeName) {\n  const base = tokeniser.consume(\n    \"FrozenArray\",\n    \"ObservableArray\",\n    \"Promise\",\n    \"async_sequence\",\n    \"sequence\",\n    \"record\",\n  );\n  if (!base) {\n    return;\n  }\n  const ret = autoParenter(\n    new Type({ source: tokeniser.source, tokens: { base } }),\n  );\n  ret.tokens.open =\n    tokeniser.consume(\"<\") ||\n    tokeniser.error(`No opening bracket after ${base.value}`);\n  switch (base.value) {\n    case \"Promise\": {\n      if (tokeniser.probe(\"[\"))\n        tokeniser.error(\"Promise type cannot have extended attribute\");\n      const subtype =\n        return_type(tokeniser, typeName) ||\n        tokeniser.error(\"Missing Promise subtype\");\n      ret.subtype.push(subtype);\n      break;\n    }\n    case \"async_sequence\":\n    case \"sequence\":\n    case \"FrozenArray\":\n    case \"ObservableArray\": {\n      const subtype =\n        type_with_extended_attributes(tokeniser, typeName) ||\n        tokeniser.error(`Missing ${base.value} subtype`);\n      ret.subtype.push(subtype);\n      break;\n    }\n    case \"record\": {\n      if (tokeniser.probe(\"[\"))\n        tokeniser.error(\"Record key cannot have extended attribute\");\n      const keyType =\n        tokeniser.consume(...stringTypes) ||\n        tokeniser.error(`Record key must be one of: ${stringTypes.join(\", \")}`);\n      const keyIdlType = new Type({\n        source: tokeniser.source,\n        tokens: { base: keyType },\n      });\n      keyIdlType.tokens.separator =\n        tokeniser.consume(\",\") ||\n        tokeniser.error(\"Missing comma after record key type\");\n      keyIdlType.type = typeName;\n      const valueType =\n        type_with_extended_attributes(tokeniser, typeName) ||\n        tokeniser.error(\"Error parsing generic type record\");\n      ret.subtype.push(keyIdlType, valueType);\n      break;\n    }\n  }\n  if (!ret.idlType) tokeniser.error(`Error parsing generic type ${base.value}`);\n  ret.tokens.close =\n    tokeniser.consume(\">\") ||\n    tokeniser.error(`Missing closing bracket after ${base.value}`);\n  return ret.this;\n}\n\n/**\n * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n */\nfunction type_suffix(tokeniser, obj) {\n  const nullable = tokeniser.consume(\"?\");\n  if (nullable) {\n    obj.tokens.nullable = nullable;\n  }\n  if (tokeniser.probe(\"?\")) tokeniser.error(\"Can't nullable more than once\");\n}\n\n/**\n * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n * @param {string} typeName\n */\nfunction single_type(tokeniser, typeName) {\n  let ret = generic_type(tokeniser, typeName) || primitive_type(tokeniser);\n  if (!ret) {\n    const base =\n      tokeniser.consumeKind(\"identifier\") ||\n      tokeniser.consume(...stringTypes, ...typeNameKeywords);\n    if (!base) {\n      return;\n    }\n    ret = new Type({ source: tokeniser.source, tokens: { base } });\n    if (tokeniser.probe(\"<\"))\n      tokeniser.error(`Unsupported generic type ${base.value}`);\n  }\n  if (ret.generic === \"Promise\" && tokeniser.probe(\"?\")) {\n    tokeniser.error(\"Promise type cannot be nullable\");\n  }\n  ret.type = typeName || null;\n  type_suffix(tokeniser, ret);\n  if (ret.nullable && ret.idlType === \"any\")\n    tokeniser.error(\"Type `any` cannot be made nullable\");\n  return ret;\n}\n\n/**\n * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n * @param {string} type\n */\nfunction union_type(tokeniser, type) {\n  const tokens = {};\n  tokens.open = tokeniser.consume(\"(\");\n  if (!tokens.open) return;\n  const ret = autoParenter(new Type({ source: tokeniser.source, tokens }));\n  ret.type = type || null;\n  while (true) {\n    const typ =\n      type_with_extended_attributes(tokeniser, type) ||\n      tokeniser.error(\"No type after open parenthesis or 'or' in union type\");\n    if (typ.idlType === \"any\")\n      tokeniser.error(\"Type `any` cannot be included in a union type\");\n    if (typ.generic === \"Promise\")\n      tokeniser.error(\"Type `Promise` cannot be included in a union type\");\n    ret.subtype.push(typ);\n    const or = tokeniser.consume(\"or\");\n    if (or) {\n      typ.tokens.separator = or;\n    } else break;\n  }\n  if (ret.idlType.length < 2) {\n    tokeniser.error(\n      \"At least two types are expected in a union type but found less\",\n    );\n  }\n  tokens.close =\n    tokeniser.consume(\")\") || tokeniser.error(\"Unterminated union type\");\n  type_suffix(tokeniser, ret);\n  return ret.this;\n}\n\nexport class Type extends Base {\n  /**\n   * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n   * @param {string} typeName\n   */\n  static parse(tokeniser, typeName) {\n    return single_type(tokeniser, typeName) || union_type(tokeniser, typeName);\n  }\n\n  constructor({ source, tokens }) {\n    super({ source, tokens });\n    Object.defineProperty(this, \"subtype\", { value: [], writable: true });\n    this.extAttrs = new ExtendedAttributes({ source, tokens: {} });\n  }\n\n  get generic() {\n    if (this.subtype.length && this.tokens.base) {\n      return this.tokens.base.value;\n    }\n    return \"\";\n  }\n  get nullable() {\n    return Boolean(this.tokens.nullable);\n  }\n  get union() {\n    return Boolean(this.subtype.length) && !this.tokens.base;\n  }\n  get idlType() {\n    if (this.subtype.length) {\n      return this.subtype;\n    }\n    // Adding prefixes/postfixes for \"unrestricted float\", etc.\n    const name = [this.tokens.prefix, this.tokens.base, this.tokens.postfix]\n      .filter((t) => t)\n      .map((t) => t.value)\n      .join(\" \");\n    return unescape(name);\n  }\n\n  *validate(defs) {\n    yield* this.extAttrs.validate(defs);\n\n    if (this.idlType === \"BufferSource\") {\n      // XXX: For now this is a hack. Consider moving parents' extAttrs into types as the spec says:\n      // https://webidl.spec.whatwg.org/#idl-annotated-types\n      for (const extAttrs of [this.extAttrs, this.parent?.extAttrs]) {\n        for (const extAttr of extAttrs) {\n          if (extAttr.name !== \"AllowShared\") {\n            continue;\n          }\n          const message = `\\`[AllowShared] BufferSource\\` is now replaced with AllowSharedBufferSource.`;\n          yield validationError(\n            this.tokens.base,\n            this,\n            \"migrate-allowshared\",\n            message,\n            { autofix: replaceAllowShared(this, extAttr, extAttrs) },\n          );\n        }\n      }\n    }\n\n    if (this.idlType === \"void\") {\n      const message = `\\`void\\` is now replaced by \\`undefined\\`. Refer to the \\\n[relevant GitHub issue](https://github.com/whatwg/webidl/issues/60) \\\nfor more information.`;\n      yield validationError(this.tokens.base, this, \"replace-void\", message, {\n        autofix: replaceVoid(this),\n      });\n    }\n\n    /*\n     * If a union is nullable, its subunions cannot include a dictionary\n     * If not, subunions may include dictionaries if each union is not nullable\n     */\n    const typedef = !this.union && defs.unique.get(this.idlType);\n    const target = this.union\n      ? this\n      : typedef && typedef.type === \"typedef\"\n        ? typedef.idlType\n        : undefined;\n    if (target && this.nullable) {\n      // do not allow any dictionary\n      const { reference } = idlTypeIncludesDictionary(target, defs) || {};\n      if (reference) {\n        const targetToken = (this.union ? reference : this).tokens.base;\n        const message = \"Nullable union cannot include a dictionary type.\";\n        yield validationError(\n          targetToken,\n          this,\n          \"no-nullable-union-dict\",\n          message,\n        );\n      }\n    } else {\n      // allow some dictionary\n      for (const subtype of this.subtype) {\n        yield* subtype.validate(defs);\n      }\n    }\n  }\n\n  /** @param {import(\"../writer.js\").Writer} w */\n  write(w) {\n    const type_body = () => {\n      if (this.union || this.generic) {\n        return w.ts.wrap([\n          w.token(this.tokens.base, w.ts.generic),\n          w.token(this.tokens.open),\n          ...this.subtype.map((t) => t.write(w)),\n          w.token(this.tokens.close),\n        ]);\n      }\n      const firstToken = this.tokens.prefix || this.tokens.base;\n      const prefix = this.tokens.prefix\n        ? [this.tokens.prefix.value, w.ts.trivia(this.tokens.base.trivia)]\n        : [];\n      const ref = w.reference(\n        w.ts.wrap([\n          ...prefix,\n          this.tokens.base.value,\n          w.token(this.tokens.postfix),\n        ]),\n        {\n          unescaped: /** @type {string} (because it's not union) */ (\n            this.idlType\n          ),\n          context: this,\n        },\n      );\n      return w.ts.wrap([w.ts.trivia(firstToken.trivia), ref]);\n    };\n    return w.ts.wrap([\n      this.extAttrs.write(w),\n      type_body(),\n      w.token(this.tokens.nullable),\n      w.token(this.tokens.separator),\n    ]);\n  }\n}\n\n/**\n * @param {Type} type\n * @param {import(\"./extended-attributes.js\").SimpleExtendedAttribute} extAttr\n * @param {ExtendedAttributes} extAttrs\n */\nfunction replaceAllowShared(type, extAttr, extAttrs) {\n  return () => {\n    const index = extAttrs.indexOf(extAttr);\n    extAttrs.splice(index, 1);\n    if (!extAttrs.length && type.tokens.base.trivia.match(/^\\s$/)) {\n      type.tokens.base.trivia = \"\"; // (let's not remove comments)\n    }\n\n    type.tokens.base.value = \"AllowSharedBufferSource\";\n  };\n}\n\n/**\n * @param {Type} type\n */\nfunction replaceVoid(type) {\n  return () => {\n    type.tokens.base.value = \"undefined\";\n  };\n}\n","import { Base } from \"./base.js\";\nimport { const_data, const_value } from \"./helpers.js\";\n\nexport class Default extends Base {\n  /**\n   * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n   */\n  static parse(tokeniser) {\n    const assign = tokeniser.consume(\"=\");\n    if (!assign) {\n      return null;\n    }\n    const def =\n      const_value(tokeniser) ||\n      tokeniser.consumeKind(\"string\") ||\n      tokeniser.consume(\"null\", \"[\", \"{\") ||\n      tokeniser.error(\"No value for default\");\n    const expression = [def];\n    if (def.value === \"[\") {\n      const close =\n        tokeniser.consume(\"]\") ||\n        tokeniser.error(\"Default sequence value must be empty\");\n      expression.push(close);\n    } else if (def.value === \"{\") {\n      const close =\n        tokeniser.consume(\"}\") ||\n        tokeniser.error(\"Default dictionary value must be empty\");\n      expression.push(close);\n    }\n    return new Default({\n      source: tokeniser.source,\n      tokens: { assign },\n      expression,\n    });\n  }\n\n  constructor({ source, tokens, expression }) {\n    super({ source, tokens });\n    expression.parent = this;\n    Object.defineProperty(this, \"expression\", { value: expression });\n  }\n\n  get type() {\n    return const_data(this.expression[0]).type;\n  }\n  get value() {\n    return const_data(this.expression[0]).value;\n  }\n  get negative() {\n    return const_data(this.expression[0]).negative;\n  }\n\n  /** @param {import(\"../writer.js\").Writer} w */\n  write(w) {\n    return w.ts.wrap([\n      w.token(this.tokens.assign),\n      ...this.expression.map((t) => w.token(t)),\n    ]);\n  }\n}\n","import { Base } from \"./base.js\";\nimport { Default } from \"./default.js\";\nimport { ExtendedAttributes } from \"./extended-attributes.js\";\nimport {\n  unescape,\n  type_with_extended_attributes,\n  autoParenter,\n  getFirstToken,\n} from \"./helpers.js\";\nimport { argumentNameKeywords, Tokeniser } from \"../tokeniser.js\";\nimport { validationError } from \"../error.js\";\nimport {\n  idlTypeIncludesDictionary,\n  dictionaryIncludesRequiredField,\n} from \"../validators/helpers.js\";\n\nexport class Argument extends Base {\n  /**\n   * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n   */\n  static parse(tokeniser) {\n    const start_position = tokeniser.position;\n    /** @type {Base[\"tokens\"]} */\n    const tokens = {};\n    const ret = autoParenter(\n      new Argument({ source: tokeniser.source, tokens }),\n    );\n    ret.extAttrs = ExtendedAttributes.parse(tokeniser);\n    tokens.optional = tokeniser.consume(\"optional\");\n    ret.idlType = type_with_extended_attributes(tokeniser, \"argument-type\");\n    if (!ret.idlType) {\n      return tokeniser.unconsume(start_position);\n    }\n    if (!tokens.optional) {\n      tokens.variadic = tokeniser.consume(\"...\");\n    }\n    tokens.name =\n      tokeniser.consumeKind(\"identifier\") ||\n      tokeniser.consume(...argumentNameKeywords);\n    if (!tokens.name) {\n      return tokeniser.unconsume(start_position);\n    }\n    ret.default = tokens.optional ? Default.parse(tokeniser) : null;\n    return ret.this;\n  }\n\n  get type() {\n    return \"argument\";\n  }\n  get optional() {\n    return !!this.tokens.optional;\n  }\n  get variadic() {\n    return !!this.tokens.variadic;\n  }\n  get name() {\n    return unescape(this.tokens.name.value);\n  }\n\n  /**\n   * @param {import(\"../validator.js\").Definitions} defs\n   */\n  *validate(defs) {\n    yield* this.extAttrs.validate(defs);\n    yield* this.idlType.validate(defs);\n    const result = idlTypeIncludesDictionary(this.idlType, defs, {\n      useNullableInner: true,\n    });\n    if (result) {\n      if (this.idlType.nullable) {\n        const message = `Dictionary arguments cannot be nullable.`;\n        yield validationError(\n          this.tokens.name,\n          this,\n          \"no-nullable-dict-arg\",\n          message,\n        );\n      } else if (!this.optional) {\n        if (\n          this.parent &&\n          !dictionaryIncludesRequiredField(result.dictionary, defs) &&\n          isLastRequiredArgument(this)\n        ) {\n          const message = `Dictionary argument must be optional if it has no required fields`;\n          yield validationError(\n            this.tokens.name,\n            this,\n            \"dict-arg-optional\",\n            message,\n            {\n              autofix: autofixDictionaryArgumentOptionality(this),\n            },\n          );\n        }\n      } else if (!this.default) {\n        const message = `Optional dictionary arguments must have a default value of \\`{}\\`.`;\n        yield validationError(\n          this.tokens.name,\n          this,\n          \"dict-arg-default\",\n          message,\n          {\n            autofix: autofixOptionalDictionaryDefaultValue(this),\n          },\n        );\n      }\n    }\n  }\n\n  /** @param {import(\"../writer.js\").Writer} w */\n  write(w) {\n    return w.ts.wrap([\n      this.extAttrs.write(w),\n      w.token(this.tokens.optional),\n      w.ts.type(this.idlType.write(w)),\n      w.token(this.tokens.variadic),\n      w.name_token(this.tokens.name, { data: this }),\n      this.default ? this.default.write(w) : \"\",\n      w.token(this.tokens.separator),\n    ]);\n  }\n}\n\n/**\n * @param {Argument} arg\n */\nfunction isLastRequiredArgument(arg) {\n  const list = arg.parent.arguments || arg.parent.list;\n  const index = list.indexOf(arg);\n  const requiredExists = list.slice(index + 1).some((a) => !a.optional);\n  return !requiredExists;\n}\n\n/**\n * @param {Argument} arg\n */\nfunction autofixDictionaryArgumentOptionality(arg) {\n  return () => {\n    const firstToken = getFirstToken(arg.idlType);\n    arg.tokens.optional = {\n      ...firstToken,\n      type: \"optional\",\n      value: \"optional\",\n    };\n    firstToken.trivia = \" \";\n    autofixOptionalDictionaryDefaultValue(arg)();\n  };\n}\n\n/**\n * @param {Argument} arg\n */\nfunction autofixOptionalDictionaryDefaultValue(arg) {\n  return () => {\n    arg.default = Default.parse(new Tokeniser(\" = {}\"));\n  };\n}\n","import { Base } from \"./base.js\";\nimport {\n  return_type,\n  argument_list,\n  unescape,\n  autoParenter,\n} from \"./helpers.js\";\nimport { validationError } from \"../error.js\";\n\nexport class Operation extends Base {\n  /**\n   * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n   * @param {object} [options]\n   * @param {import(\"../tokeniser.js\").Token} [options.special]\n   * @param {import(\"../tokeniser.js\").Token} [options.regular]\n   */\n  static parse(tokeniser, { special, regular } = {}) {\n    const tokens = { special };\n    const ret = autoParenter(\n      new Operation({ source: tokeniser.source, tokens }),\n    );\n    if (special && special.value === \"stringifier\") {\n      tokens.termination = tokeniser.consume(\";\");\n      if (tokens.termination) {\n        ret.arguments = [];\n        return ret;\n      }\n    }\n    if (!special && !regular) {\n      tokens.special = tokeniser.consume(\"getter\", \"setter\", \"deleter\");\n    }\n    ret.idlType =\n      return_type(tokeniser) || tokeniser.error(\"Missing return type\");\n    tokens.name =\n      tokeniser.consumeKind(\"identifier\") || tokeniser.consume(\"includes\");\n    tokens.open =\n      tokeniser.consume(\"(\") || tokeniser.error(\"Invalid operation\");\n    ret.arguments = argument_list(tokeniser);\n    tokens.close =\n      tokeniser.consume(\")\") || tokeniser.error(\"Unterminated operation\");\n    tokens.termination =\n      tokeniser.consume(\";\") ||\n      tokeniser.error(\"Unterminated operation, expected `;`\");\n    return ret.this;\n  }\n\n  get type() {\n    return \"operation\";\n  }\n  get name() {\n    const { name } = this.tokens;\n    if (!name) {\n      return \"\";\n    }\n    return unescape(name.value);\n  }\n  get special() {\n    if (!this.tokens.special) {\n      return \"\";\n    }\n    return this.tokens.special.value;\n  }\n\n  *validate(defs) {\n    yield* this.extAttrs.validate(defs);\n    if (!this.name && [\"\", \"static\"].includes(this.special)) {\n      const message = `Regular or static operations must have both a return type and an identifier.`;\n      yield validationError(this.tokens.open, this, \"incomplete-op\", message);\n    }\n    if (this.idlType) {\n      if (this.idlType.generic === \"async_sequence\") {\n        const message = `async_sequence types cannot be returned by an operation.`;\n        yield validationError(\n          this.idlType.tokens.base,\n          this,\n          \"async-sequence-idl-to-js\",\n          message,\n        );\n      }\n      yield* this.idlType.validate(defs);\n    }\n    for (const argument of this.arguments) {\n      yield* argument.validate(defs);\n    }\n  }\n\n  /** @param {import(\"../writer.js\").Writer} w */\n  write(w) {\n    const { parent } = this;\n    const body = this.idlType\n      ? [\n          w.ts.type(this.idlType.write(w)),\n          w.name_token(this.tokens.name, { data: this, parent }),\n          w.token(this.tokens.open),\n          w.ts.wrap(this.arguments.map((arg) => arg.write(w))),\n          w.token(this.tokens.close),\n        ]\n      : [];\n    return w.ts.definition(\n      w.ts.wrap([\n        this.extAttrs.write(w),\n        this.tokens.name\n          ? w.token(this.tokens.special)\n          : w.token(this.tokens.special, w.ts.nameless, { data: this, parent }),\n        ...body,\n        w.token(this.tokens.termination),\n      ]),\n      { data: this, parent },\n    );\n  }\n}\n","import { validationError } from \"../error.js\";\nimport {\n  idlTypeIncludesDictionary,\n  idlTypeIncludesEnforceRange,\n} from \"../validators/helpers.js\";\nimport { Base } from \"./base.js\";\nimport {\n  type_with_extended_attributes,\n  unescape,\n  autoParenter,\n} from \"./helpers.js\";\n\nexport class Attribute extends Base {\n  /**\n   * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n   * @param {object} [options]\n   * @param {import(\"../tokeniser.js\").Token} [options.special]\n   * @param {boolean} [options.noInherit]\n   * @param {boolean} [options.readonly]\n   */\n  static parse(\n    tokeniser,\n    { special, noInherit = false, readonly = false } = {},\n  ) {\n    const start_position = tokeniser.position;\n    const tokens = { special };\n    const ret = autoParenter(\n      new Attribute({ source: tokeniser.source, tokens }),\n    );\n    if (!special && !noInherit) {\n      tokens.special = tokeniser.consume(\"inherit\");\n    }\n    if (ret.special === \"inherit\" && tokeniser.probe(\"readonly\")) {\n      tokeniser.error(\"Inherited attributes cannot be read-only\");\n    }\n    tokens.readonly = tokeniser.consume(\"readonly\");\n    if (readonly && !tokens.readonly && tokeniser.probe(\"attribute\")) {\n      tokeniser.error(\"Attributes must be readonly in this context\");\n    }\n    tokens.base = tokeniser.consume(\"attribute\");\n    if (!tokens.base) {\n      tokeniser.unconsume(start_position);\n      return;\n    }\n    ret.idlType =\n      type_with_extended_attributes(tokeniser, \"attribute-type\") ||\n      tokeniser.error(\"Attribute lacks a type\");\n    tokens.name =\n      tokeniser.consumeKind(\"identifier\") ||\n      tokeniser.consume(\"async\", \"required\") ||\n      tokeniser.error(\"Attribute lacks a name\");\n    tokens.termination =\n      tokeniser.consume(\";\") ||\n      tokeniser.error(\"Unterminated attribute, expected `;`\");\n    return ret.this;\n  }\n\n  get type() {\n    return \"attribute\";\n  }\n  get special() {\n    if (!this.tokens.special) {\n      return \"\";\n    }\n    return this.tokens.special.value;\n  }\n  get readonly() {\n    return !!this.tokens.readonly;\n  }\n  get name() {\n    return unescape(this.tokens.name.value);\n  }\n\n  *validate(defs) {\n    yield* this.extAttrs.validate(defs);\n    yield* this.idlType.validate(defs);\n\n    if (\n      [\"async_sequence\", \"sequence\", \"record\"].includes(this.idlType.generic)\n    ) {\n      const message = `Attributes cannot accept ${this.idlType.generic} types.`;\n      yield validationError(\n        this.tokens.name,\n        this,\n        \"attr-invalid-type\",\n        message,\n      );\n    }\n\n    {\n      const { reference } = idlTypeIncludesDictionary(this.idlType, defs) || {};\n      if (reference) {\n        const targetToken = (this.idlType.union ? reference : this.idlType)\n          .tokens.base;\n        const message = \"Attributes cannot accept dictionary types.\";\n        yield validationError(targetToken, this, \"attr-invalid-type\", message);\n      }\n    }\n\n    if (this.readonly) {\n      if (idlTypeIncludesEnforceRange(this.idlType, defs)) {\n        const targetToken = this.idlType.tokens.base;\n        const message =\n          \"Readonly attributes cannot accept [EnforceRange] extended attribute.\";\n        yield validationError(targetToken, this, \"attr-invalid-type\", message);\n      }\n    }\n  }\n\n  /** @param {import(\"../writer.js\").Writer} w */\n  write(w) {\n    const { parent } = this;\n    return w.ts.definition(\n      w.ts.wrap([\n        this.extAttrs.write(w),\n        w.token(this.tokens.special),\n        w.token(this.tokens.readonly),\n        w.token(this.tokens.base),\n        w.ts.type(this.idlType.write(w)),\n        w.name_token(this.tokens.name, { data: this, parent }),\n        w.token(this.tokens.termination),\n      ]),\n      { data: this, parent },\n    );\n  }\n}\n","import { Type } from \"./type.js\";\nimport { Argument } from \"./argument.js\";\nimport {\n  ExtendedAttributes,\n  SimpleExtendedAttribute,\n} from \"./extended-attributes.js\";\nimport { Operation } from \"./operation.js\";\nimport { Attribute } from \"./attribute.js\";\nimport { Tokeniser } from \"../tokeniser.js\";\n\n/**\n * @param {string} identifier\n */\nexport function unescape(identifier) {\n  return identifier.startsWith(\"_\") ? identifier.slice(1) : identifier;\n}\n\n/**\n * Parses comma-separated list\n * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n * @param {object} args\n * @param {Function} args.parser parser function for each item\n * @param {boolean} [args.allowDangler] whether to allow dangling comma\n * @param {string} [args.listName] the name to be shown on error messages\n */\nexport function list(tokeniser, { parser, allowDangler, listName = \"list\" }) {\n  const first = parser(tokeniser);\n  if (!first) {\n    return [];\n  }\n  first.tokens.separator = tokeniser.consume(\",\");\n  const items = [first];\n  while (first.tokens.separator) {\n    const item = parser(tokeniser);\n    if (!item) {\n      if (!allowDangler) {\n        tokeniser.error(`Trailing comma in ${listName}`);\n      }\n      break;\n    }\n    item.tokens.separator = tokeniser.consume(\",\");\n    items.push(item);\n    if (!item.tokens.separator) break;\n  }\n  return items;\n}\n\n/**\n * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n */\nexport function const_value(tokeniser) {\n  return (\n    tokeniser.consumeKind(\"decimal\", \"integer\") ||\n    tokeniser.consume(\"true\", \"false\", \"Infinity\", \"-Infinity\", \"NaN\")\n  );\n}\n\n/**\n * @param {object} token\n * @param {string} token.type\n * @param {string} token.value\n */\nexport function const_data({ type, value }) {\n  switch (type) {\n    case \"decimal\":\n    case \"integer\":\n      return { type: \"number\", value };\n    case \"string\":\n      return { type: \"string\", value: value.slice(1, -1) };\n  }\n\n  switch (value) {\n    case \"true\":\n    case \"false\":\n      return { type: \"boolean\", value: value === \"true\" };\n    case \"Infinity\":\n    case \"-Infinity\":\n      return { type: \"Infinity\", negative: value.startsWith(\"-\") };\n    case \"[\":\n      return { type: \"sequence\", value: [] };\n    case \"{\":\n      return { type: \"dictionary\" };\n    default:\n      return { type: value };\n  }\n}\n\n/**\n * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n */\nexport function primitive_type(tokeniser) {\n  function integer_type() {\n    const prefix = tokeniser.consume(\"unsigned\");\n    const base = tokeniser.consume(\"short\", \"long\");\n    if (base) {\n      const postfix = tokeniser.consume(\"long\");\n      return new Type({ source, tokens: { prefix, base, postfix } });\n    }\n    if (prefix) tokeniser.error(\"Failed to parse integer type\");\n  }\n\n  function decimal_type() {\n    const prefix = tokeniser.consume(\"unrestricted\");\n    const base = tokeniser.consume(\"float\", \"double\");\n    if (base) {\n      return new Type({ source, tokens: { prefix, base } });\n    }\n    if (prefix) tokeniser.error(\"Failed to parse float type\");\n  }\n\n  const { source } = tokeniser;\n  const num_type = integer_type() || decimal_type();\n  if (num_type) return num_type;\n  const base = tokeniser.consume(\n    \"bigint\",\n    \"boolean\",\n    \"byte\",\n    \"octet\",\n    \"undefined\",\n  );\n  if (base) {\n    return new Type({ source, tokens: { base } });\n  }\n}\n\n/**\n * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n */\nexport function argument_list(tokeniser) {\n  return list(tokeniser, {\n    parser: Argument.parse,\n    listName: \"arguments list\",\n  });\n}\n\n/**\n * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n * @param {string=} typeName (TODO: See Type.type for more details)\n */\nexport function type_with_extended_attributes(tokeniser, typeName) {\n  const extAttrs = ExtendedAttributes.parse(tokeniser);\n  const ret = Type.parse(tokeniser, typeName);\n  if (ret) autoParenter(ret).extAttrs = extAttrs;\n  return ret;\n}\n\n/**\n * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n * @param {string=} typeName (TODO: See Type.type for more details)\n */\nexport function return_type(tokeniser, typeName) {\n  const typ = Type.parse(tokeniser, typeName || \"return-type\");\n  if (typ) {\n    return typ;\n  }\n  const voidToken = tokeniser.consume(\"void\");\n  if (voidToken) {\n    const ret = new Type({\n      source: tokeniser.source,\n      tokens: { base: voidToken },\n    });\n    ret.type = \"return-type\";\n    return ret;\n  }\n}\n\n/**\n * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n */\nexport function stringifier(tokeniser) {\n  const special = tokeniser.consume(\"stringifier\");\n  if (!special) return;\n  const member =\n    Attribute.parse(tokeniser, { special }) ||\n    Operation.parse(tokeniser, { special }) ||\n    tokeniser.error(\"Unterminated stringifier\");\n  return member;\n}\n\n/**\n * @param {string} str\n */\nexport function getLastIndentation(str) {\n  const lines = str.split(\"\\n\");\n  // the first line visually binds to the preceding token\n  if (lines.length) {\n    const match = lines[lines.length - 1].match(/^\\s+/);\n    if (match) {\n      return match[0];\n    }\n  }\n  return \"\";\n}\n\n/**\n * @param {string} parentTrivia\n */\nexport function getMemberIndentation(parentTrivia) {\n  const indentation = getLastIndentation(parentTrivia);\n  const indentCh = indentation.includes(\"\\t\") ? \"\\t\" : \"  \";\n  return indentation + indentCh;\n}\n\n/**\n * @param {import(\"./interface.js\").Interface} def\n */\nexport function autofixAddExposedWindow(def) {\n  return () => {\n    if (def.extAttrs.length) {\n      const tokeniser = new Tokeniser(\"Exposed=Window,\");\n      const exposed = SimpleExtendedAttribute.parse(tokeniser);\n      exposed.tokens.separator = tokeniser.consume(\",\");\n      const existing = def.extAttrs[0];\n      if (!/^\\s/.test(existing.tokens.name.trivia)) {\n        existing.tokens.name.trivia = ` ${existing.tokens.name.trivia}`;\n      }\n      def.extAttrs.unshift(exposed);\n    } else {\n      autoParenter(def).extAttrs = ExtendedAttributes.parse(\n        new Tokeniser(\"[Exposed=Window]\"),\n      );\n      const trivia = def.tokens.base.trivia;\n      def.extAttrs.tokens.open.trivia = trivia;\n      def.tokens.base.trivia = `\\n${getLastIndentation(trivia)}`;\n    }\n  };\n}\n\n/**\n * Get the first syntax token for the given IDL object.\n * @param {*} data\n */\nexport function getFirstToken(data) {\n  if (data.extAttrs.length) {\n    return data.extAttrs.tokens.open;\n  }\n  if (data.type === \"operation\" && !data.special) {\n    return getFirstToken(data.idlType);\n  }\n  const tokens = Object.values(data.tokens).sort((x, y) => x.index - y.index);\n  return tokens[0];\n}\n\n/**\n * @template T\n * @param {T[]} array\n * @param {(item: T) => boolean} predicate\n */\nexport function findLastIndex(array, predicate) {\n  const index = array.slice().reverse().findIndex(predicate);\n  if (index === -1) {\n    return index;\n  }\n  return array.length - index - 1;\n}\n\n/**\n * Returns a proxy that auto-assign `parent` field.\n * @template {Record} T\n * @param {T} data\n * @param {*} [parent] The object that will be assigned to `parent`.\n *                     If absent, it will be `data` by default.\n * @return {T}\n */\nexport function autoParenter(data, parent) {\n  if (!parent) {\n    // Defaults to `data` unless specified otherwise.\n    parent = data;\n  }\n  if (!data) {\n    // This allows `autoParenter(undefined)` which again allows\n    // `autoParenter(parse())` where the function may return nothing.\n    return data;\n  }\n  const proxy = new Proxy(data, {\n    get(target, p) {\n      const value = target[p];\n      if (Array.isArray(value) && p !== \"source\") {\n        // Wraps the array so that any added items will also automatically\n        // get their `parent` values.\n        return autoParenter(value, target);\n      }\n      return value;\n    },\n    set(target, p, value) {\n      // @ts-ignore https://github.com/microsoft/TypeScript/issues/47357\n      target[p] = value;\n      if (!value) {\n        return true;\n      } else if (Array.isArray(value)) {\n        // Assigning an array will add `parent` to its items.\n        for (const item of value) {\n          if (typeof item.parent !== \"undefined\") {\n            item.parent = parent;\n          }\n        }\n      } else if (typeof value.parent !== \"undefined\") {\n        value.parent = parent;\n      }\n      return true;\n    },\n  });\n  return proxy;\n}\n","import { syntaxError } from \"./error.js\";\nimport { unescape } from \"./productions/helpers.js\";\n\n// These regular expressions use the sticky flag so they will only match at\n// the current location (ie. the offset of lastIndex).\nconst tokenRe = {\n  // This expression uses a lookahead assertion to catch false matches\n  // against integers early.\n  decimal:\n    /-?(?=[0-9]*\\.|[0-9]+[eE])(([0-9]+\\.[0-9]*|[0-9]*\\.[0-9]+)([Ee][-+]?[0-9]+)?|[0-9]+[Ee][-+]?[0-9]+)/y,\n  integer: /-?(0([Xx][0-9A-Fa-f]+|[0-7]*)|[1-9][0-9]*)/y,\n  identifier: /[_-]?[A-Za-z][0-9A-Z_a-z-]*/y,\n  string: /\"[^\"]*\"/y,\n  whitespace: /[\\t\\n\\r ]+/y,\n  comment: /\\/\\/.*|\\/\\*[\\s\\S]*?\\*\\//y,\n  other: /[^\\t\\n\\r 0-9A-Za-z]/y,\n};\n\nexport const typeNameKeywords = [\n  \"ArrayBuffer\",\n  \"SharedArrayBuffer\",\n  \"DataView\",\n  \"Int8Array\",\n  \"Int16Array\",\n  \"Int32Array\",\n  \"Uint8Array\",\n  \"Uint16Array\",\n  \"Uint32Array\",\n  \"Uint8ClampedArray\",\n  \"BigInt64Array\",\n  \"BigUint64Array\",\n  \"Float16Array\",\n  \"Float32Array\",\n  \"Float64Array\",\n  \"any\",\n  \"object\",\n  \"symbol\",\n];\n\nexport const stringTypes = [\"ByteString\", \"DOMString\", \"USVString\"];\n\nexport const argumentNameKeywords = [\n  \"async\",\n  \"attribute\",\n  \"callback\",\n  \"const\",\n  \"constructor\",\n  \"deleter\",\n  \"dictionary\",\n  \"enum\",\n  \"getter\",\n  \"includes\",\n  \"inherit\",\n  \"interface\",\n  \"iterable\",\n  \"maplike\",\n  \"namespace\",\n  \"partial\",\n  \"required\",\n  \"setlike\",\n  \"setter\",\n  \"static\",\n  \"stringifier\",\n  \"typedef\",\n  \"unrestricted\",\n];\n\nconst nonRegexTerminals = [\n  \"-Infinity\",\n  \"FrozenArray\",\n  \"Infinity\",\n  \"NaN\",\n  \"ObservableArray\",\n  \"Promise\",\n  \"async_iterable\",\n  \"async_sequence\",\n  \"bigint\",\n  \"boolean\",\n  \"byte\",\n  \"double\",\n  \"false\",\n  \"float\",\n  \"long\",\n  \"mixin\",\n  \"null\",\n  \"octet\",\n  \"optional\",\n  \"or\",\n  \"readonly\",\n  \"record\",\n  \"sequence\",\n  \"short\",\n  \"true\",\n  \"undefined\",\n  \"unsigned\",\n  \"void\",\n].concat(argumentNameKeywords, stringTypes, typeNameKeywords);\n\nconst punctuations = [\n  \"(\",\n  \")\",\n  \",\",\n  \"...\",\n  \":\",\n  \";\",\n  \"<\",\n  \"=\",\n  \">\",\n  \"?\",\n  \"*\",\n  \"[\",\n  \"]\",\n  \"{\",\n  \"}\",\n];\n\nconst reserved = [\n  // \"constructor\" is now a keyword\n  \"_constructor\",\n  \"toString\",\n  \"_toString\",\n];\n\n/**\n * @typedef {ArrayItemType>} Token\n * @param {string} str\n */\nfunction tokenise(str) {\n  const tokens = [];\n  let lastCharIndex = 0;\n  let trivia = \"\";\n  let line = 1;\n  let index = 0;\n  while (lastCharIndex < str.length) {\n    const nextChar = str.charAt(lastCharIndex);\n    let result = -1;\n\n    if (/[\\t\\n\\r ]/.test(nextChar)) {\n      result = attemptTokenMatch(\"whitespace\", { noFlushTrivia: true });\n    } else if (nextChar === \"/\") {\n      result = attemptTokenMatch(\"comment\", { noFlushTrivia: true });\n    }\n\n    if (result !== -1) {\n      const currentTrivia = tokens.pop().value;\n      line += (currentTrivia.match(/\\n/g) || []).length;\n      trivia += currentTrivia;\n      index -= 1;\n    } else if (/[-0-9.A-Z_a-z]/.test(nextChar)) {\n      result = attemptTokenMatch(\"decimal\");\n      if (result === -1) {\n        result = attemptTokenMatch(\"integer\");\n      }\n      if (result === -1) {\n        result = attemptTokenMatch(\"identifier\");\n        const lastIndex = tokens.length - 1;\n        const token = tokens[lastIndex];\n        if (result !== -1) {\n          if (reserved.includes(token.value)) {\n            const message = `${unescape(\n              token.value,\n            )} is a reserved identifier and must not be used.`;\n            throw new WebIDLParseError(\n              syntaxError(tokens, lastIndex, null, message),\n            );\n          } else if (nonRegexTerminals.includes(token.value)) {\n            token.type = \"inline\";\n          }\n        }\n      }\n    } else if (nextChar === '\"') {\n      result = attemptTokenMatch(\"string\");\n    }\n\n    for (const punctuation of punctuations) {\n      if (str.startsWith(punctuation, lastCharIndex)) {\n        tokens.push({\n          type: \"inline\",\n          value: punctuation,\n          trivia,\n          line,\n          index,\n        });\n        trivia = \"\";\n        lastCharIndex += punctuation.length;\n        result = lastCharIndex;\n        break;\n      }\n    }\n\n    // other as the last try\n    if (result === -1) {\n      result = attemptTokenMatch(\"other\");\n    }\n    if (result === -1) {\n      throw new Error(\"Token stream not progressing\");\n    }\n    lastCharIndex = result;\n    index += 1;\n  }\n\n  // remaining trivia as eof\n  tokens.push({\n    type: \"eof\",\n    value: \"\",\n    trivia,\n    line,\n    index,\n  });\n\n  return tokens;\n\n  /**\n   * @param {keyof typeof tokenRe} type\n   * @param {object} options\n   * @param {boolean} [options.noFlushTrivia]\n   */\n  function attemptTokenMatch(type, { noFlushTrivia } = {}) {\n    const re = tokenRe[type];\n    re.lastIndex = lastCharIndex;\n    const result = re.exec(str);\n    if (result) {\n      tokens.push({ type, value: result[0], trivia, line, index });\n      if (!noFlushTrivia) {\n        trivia = \"\";\n      }\n      return re.lastIndex;\n    }\n    return -1;\n  }\n}\n\nexport class Tokeniser {\n  /**\n   * @param {string} idl\n   */\n  constructor(idl) {\n    this.source = tokenise(idl);\n    this.position = 0;\n  }\n\n  /**\n   * @param {string} message\n   * @return {never}\n   */\n  error(message) {\n    throw new WebIDLParseError(\n      syntaxError(this.source, this.position, this.current, message),\n    );\n  }\n\n  /**\n   * @param {string} type\n   */\n  probeKind(type) {\n    return (\n      this.source.length > this.position &&\n      this.source[this.position].type === type\n    );\n  }\n\n  /**\n   * @param {string} value\n   */\n  probe(value) {\n    return (\n      this.probeKind(\"inline\") && this.source[this.position].value === value\n    );\n  }\n\n  /**\n   * @param {...string} candidates\n   */\n  consumeKind(...candidates) {\n    for (const type of candidates) {\n      if (!this.probeKind(type)) continue;\n      const token = this.source[this.position];\n      this.position++;\n      return token;\n    }\n  }\n\n  /**\n   * @param {...string} candidates\n   */\n  consume(...candidates) {\n    if (!this.probeKind(\"inline\")) return;\n    const token = this.source[this.position];\n    for (const value of candidates) {\n      if (token.value !== value) continue;\n      this.position++;\n      return token;\n    }\n  }\n\n  /**\n   * @param {string} value\n   */\n  consumeIdentifier(value) {\n    if (!this.probeKind(\"identifier\")) {\n      return;\n    }\n    if (this.source[this.position].value !== value) {\n      return;\n    }\n    return this.consumeKind(\"identifier\");\n  }\n\n  /**\n   * @param {number} position\n   */\n  unconsume(position) {\n    this.position = position;\n  }\n}\n\nexport class WebIDLParseError extends Error {\n  /**\n   * @param {object} options\n   * @param {string} options.message\n   * @param {string} options.bareMessage\n   * @param {string} options.context\n   * @param {number} options.line\n   * @param {*} options.sourceName\n   * @param {string} options.input\n   * @param {*[]} options.tokens\n   */\n  constructor({\n    message,\n    bareMessage,\n    context,\n    line,\n    sourceName,\n    input,\n    tokens,\n  }) {\n    super(message);\n\n    this.name = \"WebIDLParseError\"; // not to be mangled\n    this.bareMessage = bareMessage;\n    this.context = context;\n    this.line = line;\n    this.sourceName = sourceName;\n    this.input = input;\n    this.tokens = tokens;\n  }\n}\n","import { list, unescape, autoParenter } from \"./helpers.js\";\nimport { WrappedToken } from \"./token.js\";\nimport { Base } from \"./base.js\";\n\nexport class EnumValue extends WrappedToken {\n  /**\n   * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n   */\n  static parse(tokeniser) {\n    const value = tokeniser.consumeKind(\"string\");\n    if (value) {\n      return new EnumValue({ source: tokeniser.source, tokens: { value } });\n    }\n  }\n\n  get type() {\n    return \"enum-value\";\n  }\n  get value() {\n    return super.value.slice(1, -1);\n  }\n\n  /** @param {import(\"../writer.js\").Writer} w */\n  write(w) {\n    const { parent } = this;\n    return w.ts.wrap([\n      w.ts.trivia(this.tokens.value.trivia),\n      w.ts.definition(\n        w.ts.wrap(['\"', w.ts.name(this.value, { data: this, parent }), '\"']),\n        { data: this, parent },\n      ),\n      w.token(this.tokens.separator),\n    ]);\n  }\n}\n\nexport class Enum extends Base {\n  /**\n   * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n   */\n  static parse(tokeniser) {\n    /** @type {Base[\"tokens\"]} */\n    const tokens = {};\n    tokens.base = tokeniser.consume(\"enum\");\n    if (!tokens.base) {\n      return;\n    }\n    tokens.name =\n      tokeniser.consumeKind(\"identifier\") ||\n      tokeniser.error(\"No name for enum\");\n    const ret = autoParenter(new Enum({ source: tokeniser.source, tokens }));\n    tokeniser.current = ret.this;\n    tokens.open = tokeniser.consume(\"{\") || tokeniser.error(\"Bodyless enum\");\n    ret.values = list(tokeniser, {\n      parser: EnumValue.parse,\n      allowDangler: true,\n      listName: \"enumeration\",\n    });\n    if (tokeniser.probeKind(\"string\")) {\n      tokeniser.error(\"No comma between enum values\");\n    }\n    tokens.close =\n      tokeniser.consume(\"}\") || tokeniser.error(\"Unexpected value in enum\");\n    if (!ret.values.length) {\n      tokeniser.error(\"No value in enum\");\n    }\n    tokens.termination =\n      tokeniser.consume(\";\") || tokeniser.error(\"No semicolon after enum\");\n    return ret.this;\n  }\n\n  get type() {\n    return \"enum\";\n  }\n  get name() {\n    return unescape(this.tokens.name.value);\n  }\n\n  /** @param {import(\"../writer.js\").Writer} w */\n  write(w) {\n    return w.ts.definition(\n      w.ts.wrap([\n        this.extAttrs.write(w),\n        w.token(this.tokens.base),\n        w.name_token(this.tokens.name, { data: this }),\n        w.token(this.tokens.open),\n        w.ts.wrap(this.values.map((v) => v.write(w))),\n        w.token(this.tokens.close),\n        w.token(this.tokens.termination),\n      ]),\n      { data: this },\n    );\n  }\n}\n","import { Base } from \"./base.js\";\nimport { unescape } from \"./helpers.js\";\n\nexport class Includes extends Base {\n  /**\n   * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n   */\n  static parse(tokeniser) {\n    const target = tokeniser.consumeKind(\"identifier\");\n    if (!target) {\n      return;\n    }\n    const tokens = { target };\n    tokens.includes = tokeniser.consume(\"includes\");\n    if (!tokens.includes) {\n      tokeniser.unconsume(target.index);\n      return;\n    }\n    tokens.mixin =\n      tokeniser.consumeKind(\"identifier\") ||\n      tokeniser.error(\"Incomplete includes statement\");\n    tokens.termination =\n      tokeniser.consume(\";\") ||\n      tokeniser.error(\"No terminating ; for includes statement\");\n    return new Includes({ source: tokeniser.source, tokens });\n  }\n\n  get type() {\n    return \"includes\";\n  }\n  get target() {\n    return unescape(this.tokens.target.value);\n  }\n  get includes() {\n    return unescape(this.tokens.mixin.value);\n  }\n\n  /** @param {import(\"../writer.js\").Writer} w */\n  write(w) {\n    return w.ts.definition(\n      w.ts.wrap([\n        this.extAttrs.write(w),\n        w.reference_token(this.tokens.target, this),\n        w.token(this.tokens.includes),\n        w.reference_token(this.tokens.mixin, this),\n        w.token(this.tokens.termination),\n      ]),\n      { data: this },\n    );\n  }\n}\n","import { Base } from \"./base.js\";\nimport {\n  type_with_extended_attributes,\n  unescape,\n  autoParenter,\n} from \"./helpers.js\";\n\nexport class Typedef extends Base {\n  /**\n   * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n   */\n  static parse(tokeniser) {\n    /** @type {Base[\"tokens\"]} */\n    const tokens = {};\n    const ret = autoParenter(new Typedef({ source: tokeniser.source, tokens }));\n    tokens.base = tokeniser.consume(\"typedef\");\n    if (!tokens.base) {\n      return;\n    }\n    ret.idlType =\n      type_with_extended_attributes(tokeniser, \"typedef-type\") ||\n      tokeniser.error(\"Typedef lacks a type\");\n    tokens.name =\n      tokeniser.consumeKind(\"identifier\") ||\n      tokeniser.error(\"Typedef lacks a name\");\n    tokeniser.current = ret.this;\n    tokens.termination =\n      tokeniser.consume(\";\") ||\n      tokeniser.error(\"Unterminated typedef, expected `;`\");\n    return ret.this;\n  }\n\n  get type() {\n    return \"typedef\";\n  }\n  get name() {\n    return unescape(this.tokens.name.value);\n  }\n\n  *validate(defs) {\n    yield* this.idlType.validate(defs);\n  }\n\n  /** @param {import(\"../writer.js\").Writer} w */\n  write(w) {\n    return w.ts.definition(\n      w.ts.wrap([\n        this.extAttrs.write(w),\n        w.token(this.tokens.base),\n        w.ts.type(this.idlType.write(w)),\n        w.name_token(this.tokens.name, { data: this }),\n        w.token(this.tokens.termination),\n      ]),\n      { data: this },\n    );\n  }\n}\n","import { Base } from \"./base.js\";\nimport {\n  return_type,\n  argument_list,\n  unescape,\n  autoParenter,\n} from \"./helpers.js\";\nimport { validationError } from \"../error.js\";\n\nexport class CallbackFunction extends Base {\n  /**\n   * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n   */\n  static parse(tokeniser, base) {\n    const tokens = { base };\n    const ret = autoParenter(\n      new CallbackFunction({ source: tokeniser.source, tokens }),\n    );\n    tokens.name =\n      tokeniser.consumeKind(\"identifier\") ||\n      tokeniser.error(\"Callback lacks a name\");\n    tokeniser.current = ret.this;\n    tokens.assign =\n      tokeniser.consume(\"=\") || tokeniser.error(\"Callback lacks an assignment\");\n    ret.idlType =\n      return_type(tokeniser) || tokeniser.error(\"Callback lacks a return type\");\n    tokens.open =\n      tokeniser.consume(\"(\") ||\n      tokeniser.error(\"Callback lacks parentheses for arguments\");\n    ret.arguments = argument_list(tokeniser);\n    tokens.close =\n      tokeniser.consume(\")\") || tokeniser.error(\"Unterminated callback\");\n    tokens.termination =\n      tokeniser.consume(\";\") ||\n      tokeniser.error(\"Unterminated callback, expected `;`\");\n    return ret.this;\n  }\n\n  get type() {\n    return \"callback\";\n  }\n  get name() {\n    return unescape(this.tokens.name.value);\n  }\n\n  *validate(defs) {\n    yield* this.extAttrs.validate(defs);\n    for (const arg of this.arguments) {\n      yield* arg.validate(defs);\n      if (arg.idlType.generic === \"async_sequence\") {\n        const message = `async_sequence types cannot be returned as a callback argument.`;\n        yield validationError(\n          arg.tokens.name,\n          arg,\n          \"async-sequence-idl-to-js\",\n          message,\n        );\n      }\n    }\n    yield* this.idlType.validate(defs);\n  }\n\n  /** @param {import(\"../writer.js\").Writer} w */\n  write(w) {\n    return w.ts.definition(\n      w.ts.wrap([\n        this.extAttrs.write(w),\n        w.token(this.tokens.base),\n        w.name_token(this.tokens.name, { data: this }),\n        w.token(this.tokens.assign),\n        w.ts.type(this.idlType.write(w)),\n        w.token(this.tokens.open),\n        ...this.arguments.map((arg) => arg.write(w)),\n        w.token(this.tokens.close),\n        w.token(this.tokens.termination),\n      ]),\n      { data: this },\n    );\n  }\n}\n","import { Base } from \"./base.js\";\nimport { ExtendedAttributes } from \"./extended-attributes.js\";\nimport { unescape, autoParenter } from \"./helpers.js\";\n\n/**\n * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n */\nfunction inheritance(tokeniser) {\n  const colon = tokeniser.consume(\":\");\n  if (!colon) {\n    return {};\n  }\n  const inheritance =\n    tokeniser.consumeKind(\"identifier\") ||\n    tokeniser.error(\"Inheritance lacks a type\");\n  return { colon, inheritance };\n}\n\n/**\n * Parser callback.\n * @callback ParserCallback\n * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n * @param {...*} args\n */\n\n/**\n * A parser callback and optional option object.\n * @typedef AllowedMember\n * @type {[ParserCallback, object?]}\n */\n\nexport class Container extends Base {\n  /**\n   * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n   * @param {*} instance TODO: This should be {T extends Container}, but see https://github.com/microsoft/TypeScript/issues/4628\n   * @param {*} args\n   */\n  static parse(tokeniser, instance, { inheritable, allowedMembers }) {\n    const { tokens, type } = instance;\n    tokens.name =\n      tokeniser.consumeKind(\"identifier\") ||\n      tokeniser.error(`Missing name in ${type}`);\n    tokeniser.current = instance;\n    instance = autoParenter(instance);\n    if (inheritable) {\n      Object.assign(tokens, inheritance(tokeniser));\n    }\n    tokens.open = tokeniser.consume(\"{\") || tokeniser.error(`Bodyless ${type}`);\n    instance.members = [];\n    while (true) {\n      tokens.close = tokeniser.consume(\"}\");\n      if (tokens.close) {\n        tokens.termination =\n          tokeniser.consume(\";\") ||\n          tokeniser.error(`Missing semicolon after ${type}`);\n        return instance.this;\n      }\n      const ea = ExtendedAttributes.parse(tokeniser);\n      let mem;\n      for (const [parser, ...args] of allowedMembers) {\n        mem = autoParenter(parser(tokeniser, ...args));\n        if (mem) {\n          break;\n        }\n      }\n      if (!mem) {\n        tokeniser.error(\"Unknown member\");\n      }\n      mem.extAttrs = ea;\n      instance.members.push(mem.this);\n    }\n  }\n\n  get partial() {\n    return !!this.tokens.partial;\n  }\n  get name() {\n    return unescape(this.tokens.name.value);\n  }\n  get inheritance() {\n    if (!this.tokens.inheritance) {\n      return null;\n    }\n    return unescape(this.tokens.inheritance.value);\n  }\n\n  *validate(defs) {\n    for (const member of this.members) {\n      if (member.validate) {\n        yield* member.validate(defs);\n      }\n    }\n  }\n\n  /** @param {import(\"../writer.js\").Writer} w */\n  write(w) {\n    const inheritance = () => {\n      if (!this.tokens.inheritance) {\n        return \"\";\n      }\n      return w.ts.wrap([\n        w.token(this.tokens.colon),\n        w.ts.trivia(this.tokens.inheritance.trivia),\n        w.ts.inheritance(\n          w.reference(this.tokens.inheritance.value, { context: this }),\n        ),\n      ]);\n    };\n\n    return w.ts.definition(\n      w.ts.wrap([\n        this.extAttrs.write(w),\n        w.token(this.tokens.callback),\n        w.token(this.tokens.partial),\n        w.token(this.tokens.base),\n        w.token(this.tokens.mixin),\n        w.name_token(this.tokens.name, { data: this }),\n        inheritance(),\n        w.token(this.tokens.open),\n        w.ts.wrap(this.members.map((m) => m.write(w))),\n        w.token(this.tokens.close),\n        w.token(this.tokens.termination),\n      ]),\n      { data: this },\n    );\n  }\n}\n","import { Base } from \"./base.js\";\nimport { Type } from \"./type.js\";\nimport {\n  const_data,\n  const_value,\n  primitive_type,\n  autoParenter,\n  unescape,\n} from \"./helpers.js\";\n\nexport class Constant extends Base {\n  /**\n   * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n   */\n  static parse(tokeniser) {\n    /** @type {Base[\"tokens\"]} */\n    const tokens = {};\n    tokens.base = tokeniser.consume(\"const\");\n    if (!tokens.base) {\n      return;\n    }\n    let idlType = primitive_type(tokeniser);\n    if (!idlType) {\n      const base =\n        tokeniser.consumeKind(\"identifier\") ||\n        tokeniser.error(\"Const lacks a type\");\n      idlType = new Type({ source: tokeniser.source, tokens: { base } });\n    }\n    if (tokeniser.probe(\"?\")) {\n      tokeniser.error(\"Unexpected nullable constant type\");\n    }\n    idlType.type = \"const-type\";\n    tokens.name =\n      tokeniser.consumeKind(\"identifier\") ||\n      tokeniser.error(\"Const lacks a name\");\n    tokens.assign =\n      tokeniser.consume(\"=\") || tokeniser.error(\"Const lacks value assignment\");\n    tokens.value =\n      const_value(tokeniser) || tokeniser.error(\"Const lacks a value\");\n    tokens.termination =\n      tokeniser.consume(\";\") ||\n      tokeniser.error(\"Unterminated const, expected `;`\");\n    const ret = new Constant({ source: tokeniser.source, tokens });\n    autoParenter(ret).idlType = idlType;\n    return ret;\n  }\n\n  get type() {\n    return \"const\";\n  }\n  get name() {\n    return unescape(this.tokens.name.value);\n  }\n  get value() {\n    return const_data(this.tokens.value);\n  }\n\n  /** @param {import(\"../writer.js\").Writer} w */\n  write(w) {\n    const { parent } = this;\n    return w.ts.definition(\n      w.ts.wrap([\n        this.extAttrs.write(w),\n        w.token(this.tokens.base),\n        w.ts.type(this.idlType.write(w)),\n        w.name_token(this.tokens.name, { data: this, parent }),\n        w.token(this.tokens.assign),\n        w.token(this.tokens.value),\n        w.token(this.tokens.termination),\n      ]),\n      { data: this, parent },\n    );\n  }\n}\n","import { validationError } from \"../error.js\";\nimport { Base } from \"./base.js\";\nimport {\n  type_with_extended_attributes,\n  autoParenter,\n  argument_list,\n} from \"./helpers.js\";\n\nexport class IterableLike extends Base {\n  /**\n   * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n   */\n  static parse(tokeniser) {\n    const start_position = tokeniser.position;\n    const ret = autoParenter(\n      new IterableLike({ source: tokeniser.source, tokens: {} }),\n    );\n    const { tokens } = ret;\n    tokens.readonly = tokeniser.consume(\"readonly\");\n    if (!tokens.readonly) {\n      tokens.async = tokeniser.consume(\"async\");\n    }\n    tokens.base = tokens.readonly\n      ? tokeniser.consume(\"maplike\", \"setlike\")\n      : tokens.async\n        ? tokeniser.consume(\"iterable\")\n        : tokeniser.consume(\"iterable\", \"async_iterable\", \"maplike\", \"setlike\");\n    if (!tokens.base) {\n      tokeniser.unconsume(start_position);\n      return;\n    }\n\n    const { type } = ret;\n    const secondTypeRequired = type === \"maplike\";\n    const secondTypeAllowed =\n      secondTypeRequired || type === \"iterable\" || type === \"async_iterable\";\n    const argumentAllowed =\n      type === \"async_iterable\" || (ret.async && type === \"iterable\");\n\n    tokens.open =\n      tokeniser.consume(\"<\") ||\n      tokeniser.error(`Missing less-than sign \\`<\\` in ${type} declaration`);\n    const first =\n      type_with_extended_attributes(tokeniser) ||\n      tokeniser.error(`Missing a type argument in ${type} declaration`);\n    ret.idlType = [first];\n    ret.arguments = [];\n\n    if (secondTypeAllowed) {\n      first.tokens.separator = tokeniser.consume(\",\");\n      if (first.tokens.separator) {\n        ret.idlType.push(type_with_extended_attributes(tokeniser));\n      } else if (secondTypeRequired) {\n        tokeniser.error(`Missing second type argument in ${type} declaration`);\n      }\n    }\n\n    tokens.close =\n      tokeniser.consume(\">\") ||\n      tokeniser.error(`Missing greater-than sign \\`>\\` in ${type} declaration`);\n\n    if (tokeniser.probe(\"(\")) {\n      if (argumentAllowed) {\n        tokens.argsOpen = tokeniser.consume(\"(\");\n        ret.arguments.push(...argument_list(tokeniser));\n        tokens.argsClose =\n          tokeniser.consume(\")\") ||\n          tokeniser.error(\"Unterminated async iterable argument list\");\n      } else {\n        tokeniser.error(`Arguments are only allowed for \\`async iterable\\``);\n      }\n    }\n\n    tokens.termination =\n      tokeniser.consume(\";\") ||\n      tokeniser.error(`Missing semicolon after ${type} declaration`);\n\n    return ret.this;\n  }\n\n  get type() {\n    return this.tokens.base.value;\n  }\n  get readonly() {\n    return !!this.tokens.readonly;\n  }\n  get async() {\n    return !!this.tokens.async;\n  }\n\n  *validate(defs) {\n    if (this.async && this.type === \"iterable\") {\n      const message = \"`async iterable` is now changed to `async_iterable`.\";\n      yield validationError(\n        this.tokens.async,\n        this,\n        \"obsolete-async-iterable-syntax\",\n        message,\n        {\n          autofix: autofixAsyncIterableSyntax(this),\n        },\n      );\n    }\n    for (const type of this.idlType) {\n      yield* type.validate(defs);\n    }\n    for (const argument of this.arguments) {\n      yield* argument.validate(defs);\n    }\n  }\n\n  /** @param {import(\"../writer.js\").Writer} w */\n  write(w) {\n    return w.ts.definition(\n      w.ts.wrap([\n        this.extAttrs.write(w),\n        w.token(this.tokens.readonly),\n        w.token(this.tokens.async),\n        w.token(this.tokens.base, w.ts.generic),\n        w.token(this.tokens.open),\n        w.ts.wrap(this.idlType.map((t) => t.write(w))),\n        w.token(this.tokens.close),\n        w.token(this.tokens.argsOpen),\n        w.ts.wrap(this.arguments.map((arg) => arg.write(w))),\n        w.token(this.tokens.argsClose),\n        w.token(this.tokens.termination),\n      ]),\n      { data: this, parent: this.parent },\n    );\n  }\n}\n\n/**\n * @param {IterableLike} iterableLike\n */\nfunction autofixAsyncIterableSyntax(iterableLike) {\n  return () => {\n    const async = iterableLike.tokens.async;\n    iterableLike.tokens.base = {\n      ...async,\n      type: \"async_iterable\",\n      value: \"async_iterable\",\n    };\n    delete iterableLike.tokens.async;\n  };\n}\n","import { Base } from \"./base.js\";\nimport { argument_list, autoParenter } from \"./helpers.js\";\n\nexport class Constructor extends Base {\n  /**\n   * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n   */\n  static parse(tokeniser) {\n    const base = tokeniser.consume(\"constructor\");\n    if (!base) {\n      return;\n    }\n    /** @type {Base[\"tokens\"]} */\n    const tokens = { base };\n    tokens.open =\n      tokeniser.consume(\"(\") ||\n      tokeniser.error(\"No argument list in constructor\");\n    const args = argument_list(tokeniser);\n    tokens.close =\n      tokeniser.consume(\")\") || tokeniser.error(\"Unterminated constructor\");\n    tokens.termination =\n      tokeniser.consume(\";\") ||\n      tokeniser.error(\"No semicolon after constructor\");\n    const ret = new Constructor({ source: tokeniser.source, tokens });\n    autoParenter(ret).arguments = args;\n    return ret;\n  }\n\n  get type() {\n    return \"constructor\";\n  }\n\n  *validate(defs) {\n    for (const argument of this.arguments) {\n      yield* argument.validate(defs);\n    }\n  }\n\n  /** @param {import(\"../writer.js\").Writer} w */\n  write(w) {\n    const { parent } = this;\n    return w.ts.definition(\n      w.ts.wrap([\n        this.extAttrs.write(w),\n        w.token(this.tokens.base, w.ts.nameless, { data: this, parent }),\n        w.token(this.tokens.open),\n        w.ts.wrap(this.arguments.map((arg) => arg.write(w))),\n        w.token(this.tokens.close),\n        w.token(this.tokens.termination),\n      ]),\n      { data: this, parent },\n    );\n  }\n}\n","import { Container } from \"./container.js\";\nimport { Attribute } from \"./attribute.js\";\nimport { Operation } from \"./operation.js\";\nimport { Constant } from \"./constant.js\";\nimport { IterableLike } from \"./iterable.js\";\nimport {\n  stringifier,\n  autofixAddExposedWindow,\n  getMemberIndentation,\n  getLastIndentation,\n  getFirstToken,\n  findLastIndex,\n  autoParenter,\n} from \"./helpers.js\";\nimport { validationError } from \"../error.js\";\nimport { checkInterfaceMemberDuplication } from \"../validators/interface.js\";\nimport { Constructor } from \"./constructor.js\";\nimport { Tokeniser } from \"../tokeniser.js\";\nimport { ExtendedAttributes } from \"./extended-attributes.js\";\n\n/**\n * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n */\nfunction static_member(tokeniser) {\n  const special = tokeniser.consume(\"static\");\n  if (!special) return;\n  const member =\n    Attribute.parse(tokeniser, { special }) ||\n    Operation.parse(tokeniser, { special }) ||\n    tokeniser.error(\"No body in static member\");\n  return member;\n}\n\nexport class Interface extends Container {\n  /**\n   * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n   * @param {import(\"../tokeniser.js\").Token} base\n   * @param {object} [options]\n   * @param {import(\"./container.js\").AllowedMember[]} [options.extMembers]\n   * @param {import(\"../tokeniser.js\").Token|null} [options.partial]\n   */\n  static parse(tokeniser, base, { extMembers = [], partial = null } = {}) {\n    const tokens = { partial, base };\n    return Container.parse(\n      tokeniser,\n      new Interface({ source: tokeniser.source, tokens }),\n      {\n        inheritable: !partial,\n        allowedMembers: [\n          ...extMembers,\n          [Constant.parse],\n          [Constructor.parse],\n          [static_member],\n          [stringifier],\n          [IterableLike.parse],\n          [Attribute.parse],\n          [Operation.parse],\n        ],\n      },\n    );\n  }\n\n  get type() {\n    return \"interface\";\n  }\n\n  *validate(defs) {\n    yield* this.extAttrs.validate(defs);\n    if (\n      !this.partial &&\n      this.extAttrs.every((extAttr) => extAttr.name !== \"Exposed\")\n    ) {\n      const message = `Interfaces must have \\`[Exposed]\\` extended attribute. \\\nTo fix, add, for example, \\`[Exposed=Window]\\`. Please also consider carefully \\\nif your interface should also be exposed in a Worker scope. Refer to the \\\n[WebIDL spec section on Exposed](https://heycam.github.io/webidl/#Exposed) \\\nfor more information.`;\n      yield validationError(\n        this.tokens.name,\n        this,\n        \"require-exposed\",\n        message,\n        {\n          autofix: autofixAddExposedWindow(this),\n        },\n      );\n    }\n    const oldConstructors = this.extAttrs.filter(\n      (extAttr) => extAttr.name === \"Constructor\",\n    );\n    for (const constructor of oldConstructors) {\n      const message = `Constructors should now be represented as a \\`constructor()\\` operation on the interface \\\ninstead of \\`[Constructor]\\` extended attribute. Refer to the \\\n[WebIDL spec section on constructor operations](https://heycam.github.io/webidl/#idl-constructors) \\\nfor more information.`;\n      yield validationError(\n        constructor.tokens.name,\n        this,\n        \"constructor-member\",\n        message,\n        {\n          autofix: autofixConstructor(this, constructor),\n        },\n      );\n    }\n\n    const isGlobal = this.extAttrs.some((extAttr) => extAttr.name === \"Global\");\n    if (isGlobal) {\n      const factoryFunctions = this.extAttrs.filter(\n        (extAttr) => extAttr.name === \"LegacyFactoryFunction\",\n      );\n      for (const named of factoryFunctions) {\n        const message = `Interfaces marked as \\`[Global]\\` cannot have factory functions.`;\n        yield validationError(\n          named.tokens.name,\n          this,\n          \"no-constructible-global\",\n          message,\n        );\n      }\n\n      const constructors = this.members.filter(\n        (member) => member.type === \"constructor\",\n      );\n      for (const named of constructors) {\n        const message = `Interfaces marked as \\`[Global]\\` cannot have constructors.`;\n        yield validationError(\n          named.tokens.base,\n          this,\n          \"no-constructible-global\",\n          message,\n        );\n      }\n    }\n\n    yield* super.validate(defs);\n    if (!this.partial) {\n      yield* checkInterfaceMemberDuplication(defs, this);\n    }\n  }\n}\n\nfunction autofixConstructor(interfaceDef, constructorExtAttr) {\n  interfaceDef = autoParenter(interfaceDef);\n  return () => {\n    const indentation = getLastIndentation(\n      interfaceDef.extAttrs.tokens.open.trivia,\n    );\n    const memberIndent = interfaceDef.members.length\n      ? getLastIndentation(getFirstToken(interfaceDef.members[0]).trivia)\n      : getMemberIndentation(indentation);\n    const constructorOp = Constructor.parse(\n      new Tokeniser(`\\n${memberIndent}constructor();`),\n    );\n    constructorOp.extAttrs = new ExtendedAttributes({\n      source: interfaceDef.source,\n      tokens: {},\n    });\n    autoParenter(constructorOp).arguments = constructorExtAttr.arguments;\n\n    const existingIndex = findLastIndex(\n      interfaceDef.members,\n      (m) => m.type === \"constructor\",\n    );\n    interfaceDef.members.splice(existingIndex + 1, 0, constructorOp);\n\n    const { close } = interfaceDef.tokens;\n    if (!close.trivia.includes(\"\\n\")) {\n      close.trivia += `\\n${indentation}`;\n    }\n\n    const { extAttrs } = interfaceDef;\n    const index = extAttrs.indexOf(constructorExtAttr);\n    const removed = extAttrs.splice(index, 1);\n    if (!extAttrs.length) {\n      extAttrs.tokens.open = extAttrs.tokens.close = undefined;\n    } else if (extAttrs.length === index) {\n      extAttrs[index - 1].tokens.separator = undefined;\n    } else if (!extAttrs[index].tokens.name.trivia.trim()) {\n      extAttrs[index].tokens.name.trivia = removed[0].tokens.name.trivia;\n    }\n  };\n}\n","import { validationError } from \"../error.js\";\n\n/**\n * @param {import(\"../validator.js\").Definitions} defs\n * @param {import(\"../productions/container.js\").Container} i\n */\nexport function* checkInterfaceMemberDuplication(defs, i) {\n  const opNames = groupOperationNames(i);\n  const partials = defs.partials.get(i.name) || [];\n  const mixins = defs.mixinMap.get(i.name) || [];\n  for (const ext of [...partials, ...mixins]) {\n    const additions = getOperations(ext);\n    const statics = additions.filter((a) => a.special === \"static\");\n    const nonstatics = additions.filter((a) => a.special !== \"static\");\n    yield* checkAdditions(statics, opNames.statics, ext, i);\n    yield* checkAdditions(nonstatics, opNames.nonstatics, ext, i);\n    statics.forEach((op) => opNames.statics.add(op.name));\n    nonstatics.forEach((op) => opNames.nonstatics.add(op.name));\n  }\n\n  /**\n   * @param {import(\"../productions/operation.js\").Operation[]} additions\n   * @param {Set} existings\n   * @param {import(\"../productions/container.js\").Container} ext\n   * @param {import(\"../productions/container.js\").Container} base\n   */\n  function* checkAdditions(additions, existings, ext, base) {\n    for (const addition of additions) {\n      const { name } = addition;\n      if (name && existings.has(name)) {\n        const isStatic = addition.special === \"static\" ? \"static \" : \"\";\n        const message = `The ${isStatic}operation \"${name}\" has already been defined for the base interface \"${base.name}\" either in itself or in a mixin`;\n        yield validationError(\n          addition.tokens.name,\n          ext,\n          \"no-cross-overload\",\n          message,\n        );\n      }\n    }\n  }\n\n  /**\n   * @param {import(\"../productions/container.js\").Container} i\n   * @returns {import(\"../productions/operation.js\").Operation[]}\n   */\n  function getOperations(i) {\n    return i.members.filter(({ type }) => type === \"operation\");\n  }\n\n  /**\n   * @param {import(\"../productions/container.js\").Container} i\n   */\n  function groupOperationNames(i) {\n    const ops = getOperations(i);\n    return {\n      statics: new Set(\n        ops.filter((op) => op.special === \"static\").map((op) => op.name),\n      ),\n      nonstatics: new Set(\n        ops.filter((op) => op.special !== \"static\").map((op) => op.name),\n      ),\n    };\n  }\n}\n","import { Container } from \"./container.js\";\nimport { Constant } from \"./constant.js\";\nimport { Attribute } from \"./attribute.js\";\nimport { Operation } from \"./operation.js\";\nimport { stringifier } from \"./helpers.js\";\n\nexport class Mixin extends Container {\n  /**\n   * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n   * @param {import(\"../tokeniser.js\").Token} base\n   * @param {object} [options]\n   * @param {import(\"./container.js\").AllowedMember[]} [options.extMembers]\n   * @param {import(\"../tokeniser.js\").Token} [options.partial]\n   */\n  static parse(tokeniser, base, { extMembers = [], partial } = {}) {\n    const tokens = { partial, base };\n    tokens.mixin = tokeniser.consume(\"mixin\");\n    if (!tokens.mixin) {\n      return;\n    }\n    return Container.parse(\n      tokeniser,\n      new Mixin({ source: tokeniser.source, tokens }),\n      {\n        allowedMembers: [\n          ...extMembers,\n          [Constant.parse],\n          [stringifier],\n          [Attribute.parse, { noInherit: true }],\n          [Operation.parse, { regular: true }],\n        ],\n      },\n    );\n  }\n\n  get type() {\n    return \"interface mixin\";\n  }\n}\n","import { Base } from \"./base.js\";\nimport {\n  unescape,\n  type_with_extended_attributes,\n  autoParenter,\n} from \"./helpers.js\";\nimport { ExtendedAttributes } from \"./extended-attributes.js\";\nimport { Default } from \"./default.js\";\n\nexport class Field extends Base {\n  /**\n   * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n   */\n  static parse(tokeniser) {\n    /** @type {Base[\"tokens\"]} */\n    const tokens = {};\n    const ret = autoParenter(new Field({ source: tokeniser.source, tokens }));\n    ret.extAttrs = ExtendedAttributes.parse(tokeniser);\n    tokens.required = tokeniser.consume(\"required\");\n    ret.idlType =\n      type_with_extended_attributes(tokeniser, \"dictionary-type\") ||\n      tokeniser.error(\"Dictionary member lacks a type\");\n    tokens.name =\n      tokeniser.consumeKind(\"identifier\") ||\n      tokeniser.error(\"Dictionary member lacks a name\");\n    ret.default = Default.parse(tokeniser);\n    if (tokens.required && ret.default)\n      tokeniser.error(\"Required member must not have a default\");\n    tokens.termination =\n      tokeniser.consume(\";\") ||\n      tokeniser.error(\"Unterminated dictionary member, expected `;`\");\n    return ret.this;\n  }\n\n  get type() {\n    return \"field\";\n  }\n  get name() {\n    return unescape(this.tokens.name.value);\n  }\n  get required() {\n    return !!this.tokens.required;\n  }\n\n  *validate(defs) {\n    yield* this.idlType.validate(defs);\n  }\n\n  /** @param {import(\"../writer.js\").Writer} w */\n  write(w) {\n    const { parent } = this;\n    return w.ts.definition(\n      w.ts.wrap([\n        this.extAttrs.write(w),\n        w.token(this.tokens.required),\n        w.ts.type(this.idlType.write(w)),\n        w.name_token(this.tokens.name, { data: this, parent }),\n        this.default ? this.default.write(w) : \"\",\n        w.token(this.tokens.termination),\n      ]),\n      { data: this, parent },\n    );\n  }\n}\n","import { Container } from \"./container.js\";\nimport { Field } from \"./field.js\";\n\nexport class Dictionary extends Container {\n  /**\n   * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n   * @param {object} [options]\n   * @param {import(\"./container.js\").AllowedMember[]} [options.extMembers]\n   * @param {import(\"../tokeniser.js\").Token} [options.partial]\n   */\n  static parse(tokeniser, { extMembers = [], partial } = {}) {\n    const tokens = { partial };\n    tokens.base = tokeniser.consume(\"dictionary\");\n    if (!tokens.base) {\n      return;\n    }\n    return Container.parse(\n      tokeniser,\n      new Dictionary({ source: tokeniser.source, tokens }),\n      {\n        inheritable: !partial,\n        allowedMembers: [...extMembers, [Field.parse]],\n      },\n    );\n  }\n\n  get type() {\n    return \"dictionary\";\n  }\n}\n","import { Container } from \"./container.js\";\nimport { Attribute } from \"./attribute.js\";\nimport { Operation } from \"./operation.js\";\nimport { validationError } from \"../error.js\";\nimport { autofixAddExposedWindow } from \"./helpers.js\";\nimport { Constant } from \"./constant.js\";\n\nexport class Namespace extends Container {\n  /**\n   * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n   * @param {object} [options]\n   * @param {import(\"./container.js\").AllowedMember[]} [options.extMembers]\n   * @param {import(\"../tokeniser.js\").Token} [options.partial]\n   */\n  static parse(tokeniser, { extMembers = [], partial } = {}) {\n    const tokens = { partial };\n    tokens.base = tokeniser.consume(\"namespace\");\n    if (!tokens.base) {\n      return;\n    }\n    return Container.parse(\n      tokeniser,\n      new Namespace({ source: tokeniser.source, tokens }),\n      {\n        allowedMembers: [\n          ...extMembers,\n          [Attribute.parse, { noInherit: true, readonly: true }],\n          [Constant.parse],\n          [Operation.parse, { regular: true }],\n        ],\n      },\n    );\n  }\n\n  get type() {\n    return \"namespace\";\n  }\n\n  *validate(defs) {\n    if (\n      !this.partial &&\n      this.extAttrs.every((extAttr) => extAttr.name !== \"Exposed\")\n    ) {\n      const message = `Namespaces must have [Exposed] extended attribute. \\\nTo fix, add, for example, [Exposed=Window]. Please also consider carefully \\\nif your namespace should also be exposed in a Worker scope. Refer to the \\\n[WebIDL spec section on Exposed](https://heycam.github.io/webidl/#Exposed) \\\nfor more information.`;\n      yield validationError(\n        this.tokens.name,\n        this,\n        \"require-exposed\",\n        message,\n        {\n          autofix: autofixAddExposedWindow(this),\n        },\n      );\n    }\n    yield* super.validate(defs);\n  }\n}\n","import { Container } from \"./container.js\";\nimport { Operation } from \"./operation.js\";\nimport { Constant } from \"./constant.js\";\n\nexport class CallbackInterface extends Container {\n  /**\n   * @param {import(\"../tokeniser.js\").Tokeniser} tokeniser\n   * @param {*} callback\n   * @param {object} [options]\n   * @param {import(\"./container.js\").AllowedMember[]} [options.extMembers]\n   */\n  static parse(tokeniser, callback, { extMembers = [] } = {}) {\n    const tokens = { callback };\n    tokens.base = tokeniser.consume(\"interface\");\n    if (!tokens.base) {\n      return;\n    }\n    return Container.parse(\n      tokeniser,\n      new CallbackInterface({ source: tokeniser.source, tokens }),\n      {\n        allowedMembers: [\n          ...extMembers,\n          [Constant.parse],\n          [Operation.parse, { regular: true }],\n        ],\n      },\n    );\n  }\n\n  get type() {\n    return \"callback interface\";\n  }\n}\n","import { Tokeniser } from \"./tokeniser.js\";\nimport { Enum } from \"./productions/enum.js\";\nimport { Includes } from \"./productions/includes.js\";\nimport { ExtendedAttributes } from \"./productions/extended-attributes.js\";\nimport { Typedef } from \"./productions/typedef.js\";\nimport { CallbackFunction } from \"./productions/callback.js\";\nimport { Interface } from \"./productions/interface.js\";\nimport { Mixin } from \"./productions/mixin.js\";\nimport { Dictionary } from \"./productions/dictionary.js\";\nimport { Namespace } from \"./productions/namespace.js\";\nimport { CallbackInterface } from \"./productions/callback-interface.js\";\nimport { autoParenter } from \"./productions/helpers.js\";\nimport { Eof } from \"./productions/token.js\";\n\n/** @typedef {'callbackInterface'|'dictionary'|'interface'|'mixin'|'namespace'} ExtendableInterfaces */\n/** @typedef {{ extMembers?: import(\"./productions/container.js\").AllowedMember[]}} Extension */\n/** @typedef {Partial>} Extensions */\n\n/**\n * Parser options.\n * @typedef {Object} ParserOptions\n * @property {string} [sourceName]\n * @property {boolean} [concrete]\n * @property {Function[]} [productions]\n * @property {Extensions} [extensions]\n */\n\n/**\n * @param {Tokeniser} tokeniser\n * @param {ParserOptions} options\n */\nfunction parseByTokens(tokeniser, options) {\n  const source = tokeniser.source;\n\n  function error(str) {\n    tokeniser.error(str);\n  }\n\n  function consume(...candidates) {\n    return tokeniser.consume(...candidates);\n  }\n\n  function callback() {\n    const callback = consume(\"callback\");\n    if (!callback) return;\n    if (tokeniser.probe(\"interface\")) {\n      return CallbackInterface.parse(tokeniser, callback, {\n        ...options?.extensions?.callbackInterface,\n      });\n    }\n    return CallbackFunction.parse(tokeniser, callback);\n  }\n\n  function interface_(opts) {\n    const base = consume(\"interface\");\n    if (!base) return;\n    return (\n      Mixin.parse(tokeniser, base, {\n        ...opts,\n        ...options?.extensions?.mixin,\n      }) ||\n      Interface.parse(tokeniser, base, {\n        ...opts,\n        ...options?.extensions?.interface,\n      }) ||\n      error(\"Interface has no proper body\")\n    );\n  }\n\n  function partial() {\n    const partial = consume(\"partial\");\n    if (!partial) return;\n    return (\n      Dictionary.parse(tokeniser, {\n        partial,\n        ...options?.extensions?.dictionary,\n      }) ||\n      interface_({ partial }) ||\n      Namespace.parse(tokeniser, {\n        partial,\n        ...options?.extensions?.namespace,\n      }) ||\n      error(\"Partial doesn't apply to anything\")\n    );\n  }\n\n  function definition() {\n    if (options.productions) {\n      for (const production of options.productions) {\n        const result = production(tokeniser);\n        if (result) {\n          return result;\n        }\n      }\n    }\n\n    return (\n      callback() ||\n      interface_() ||\n      partial() ||\n      Dictionary.parse(tokeniser, options?.extensions?.dictionary) ||\n      Enum.parse(tokeniser) ||\n      Typedef.parse(tokeniser) ||\n      Includes.parse(tokeniser) ||\n      Namespace.parse(tokeniser, options?.extensions?.namespace)\n    );\n  }\n\n  function definitions() {\n    if (!source.length) return [];\n    const defs = [];\n    while (true) {\n      const ea = ExtendedAttributes.parse(tokeniser);\n      const def = definition();\n      if (!def) {\n        if (ea.length) error(\"Stray extended attributes\");\n        break;\n      }\n      autoParenter(def).extAttrs = ea;\n      defs.push(def);\n    }\n    const eof = Eof.parse(tokeniser);\n    if (options.concrete) {\n      defs.push(eof);\n    }\n    return defs;\n  }\n\n  const res = definitions();\n  if (tokeniser.position < source.length) error(\"Unrecognised tokens\");\n  return res;\n}\n\n/**\n * @param {string} str\n * @param {ParserOptions} [options]\n */\nexport function parse(str, options = {}) {\n  const tokeniser = new Tokeniser(str);\n  if (typeof options.sourceName !== \"undefined\") {\n    // @ts-ignore (See Tokeniser.source in supplement.d.ts)\n    tokeniser.source.name = options.sourceName;\n  }\n  return parseByTokens(tokeniser, options);\n}\n","function noop(arg) {\n  return arg;\n}\n\nconst templates = {\n  wrap: (items) => items.join(\"\"),\n  trivia: noop,\n  name: noop,\n  reference: noop,\n  type: noop,\n  generic: noop,\n  nameless: noop,\n  inheritance: noop,\n  definition: noop,\n  extendedAttribute: noop,\n  extendedAttributeReference: noop,\n};\n\nexport class Writer {\n  constructor(ts) {\n    this.ts = Object.assign({}, templates, ts);\n  }\n\n  /**\n   * @param {string} raw\n   * @param {object} options\n   * @param {string} [options.unescaped]\n   * @param {import(\"./productions/base.js\").Base} [options.context]\n   * @returns\n   */\n  reference(raw, { unescaped, context }) {\n    if (!unescaped) {\n      unescaped = raw.startsWith(\"_\") ? raw.slice(1) : raw;\n    }\n    return this.ts.reference(raw, unescaped, context);\n  }\n\n  /**\n   * @param {import(\"./tokeniser.js\").Token} t\n   * @param {Function} wrapper\n   * @param {...any} args\n   * @returns\n   */\n  token(t, wrapper = noop, ...args) {\n    if (!t) {\n      return \"\";\n    }\n    const value = wrapper(t.value, ...args);\n    return this.ts.wrap([this.ts.trivia(t.trivia), value]);\n  }\n\n  reference_token(t, context) {\n    return this.token(t, this.reference.bind(this), { context });\n  }\n\n  name_token(t, arg) {\n    return this.token(t, this.ts.name, arg);\n  }\n\n  identifier(id, context) {\n    return this.ts.wrap([\n      this.reference_token(id.tokens.value, context),\n      this.token(id.tokens.separator),\n    ]);\n  }\n}\n\nexport function write(ast, { templates: ts = templates } = {}) {\n  ts = Object.assign({}, templates, ts);\n\n  const w = new Writer(ts);\n\n  return ts.wrap(ast.map((it) => it.write(w)));\n}\n","import { validationError as error } from \"./error.js\";\n\nfunction getMixinMap(all, unique) {\n  const map = new Map();\n  const includes = all.filter((def) => def.type === \"includes\");\n  for (const include of includes) {\n    const mixin = unique.get(include.includes);\n    if (!mixin) {\n      continue;\n    }\n    const array = map.get(include.target);\n    if (array) {\n      array.push(mixin);\n    } else {\n      map.set(include.target, [mixin]);\n    }\n  }\n  return map;\n}\n\n/**\n * @typedef {ReturnType} Definitions\n */\nfunction groupDefinitions(all) {\n  const unique = new Map();\n  const duplicates = new Set();\n  const partials = new Map();\n  for (const def of all) {\n    if (def.partial) {\n      const array = partials.get(def.name);\n      if (array) {\n        array.push(def);\n      } else {\n        partials.set(def.name, [def]);\n      }\n      continue;\n    }\n    if (!def.name) {\n      continue;\n    }\n    if (!unique.has(def.name)) {\n      unique.set(def.name, def);\n    } else {\n      duplicates.add(def);\n    }\n  }\n  return {\n    all,\n    unique,\n    partials,\n    duplicates,\n    mixinMap: getMixinMap(all, unique),\n    cache: {\n      typedefIncludesDictionary: new WeakMap(),\n      dictionaryIncludesRequiredField: new WeakMap(),\n    },\n  };\n}\n\nfunction* checkDuplicatedNames({ unique, duplicates }) {\n  for (const dup of duplicates) {\n    const { name } = dup;\n    const message = `The name \"${name}\" of type \"${\n      unique.get(name).type\n    }\" was already seen`;\n    yield error(dup.tokens.name, dup, \"no-duplicate\", message);\n  }\n}\n\nfunction* validateIterable(ast) {\n  const defs = groupDefinitions(ast);\n  for (const def of defs.all) {\n    if (def.validate) {\n      yield* def.validate(defs);\n    }\n  }\n  yield* checkDuplicatedNames(defs);\n}\n\n// Remove this once all of our support targets expose `.flat()` by default\nfunction flatten(array) {\n  if (array.flat) {\n    return array.flat();\n  }\n  return [].concat(...array);\n}\n\n/**\n * @param {import(\"./productions/base.js\").Base[]} ast\n * @return {import(\"./error.js\").WebIDLErrorData[]} validation errors\n */\nexport function validate(ast) {\n  return [...validateIterable(flatten(ast))];\n}\n","export var Tokens;\n(function (Tokens) {\n    Tokens[\"ILLEGAL\"] = \"ILLEGAL\";\n    Tokens[\"EOF\"] = \"EOF\";\n    Tokens[\"NL\"] = \"\\n\";\n    Tokens[\"SPACE\"] = \" \";\n    Tokens[\"UNDERSCORE\"] = \"_\";\n    Tokens[\"DOLLAR\"] = \"$\";\n    Tokens[\"ATSIGN\"] = \"@\";\n    Tokens[\"CARET\"] = \"^\";\n    Tokens[\"HASH\"] = \"#\";\n    Tokens[\"TILDE\"] = \"~\";\n    Tokens[\"AMPERSAND\"] = \"&\";\n    // Identifiers + literals,\n    Tokens[\"IDENT\"] = \"IDENT\";\n    Tokens[\"COMMENT\"] = \"COMMENT\";\n    Tokens[\"STRING\"] = \"STRING\";\n    Tokens[\"NUMBER\"] = \"NUMBER\";\n    Tokens[\"FLOAT\"] = \"FLOAT\";\n    Tokens[\"CTLOP\"] = \"CTLOP\";\n    Tokens[\"BYTES\"] = \"BYTES\";\n    Tokens[\"HEX\"] = \"HEX\";\n    Tokens[\"BASE64\"] = \"BASE64\";\n    // Operators,\n    Tokens[\"ASSIGN\"] = \"=\";\n    Tokens[\"ARROWMAP\"] = \"=>\";\n    Tokens[\"TCHOICE\"] = \"/\";\n    Tokens[\"GCHOICE\"] = \"//\";\n    Tokens[\"TCHOICEALT\"] = \"/=\";\n    Tokens[\"GCHOICEALT\"] = \"//=\";\n    Tokens[\"PLUS\"] = \"+\";\n    Tokens[\"MINUS\"] = \"-\";\n    Tokens[\"QUEST\"] = \"?\";\n    Tokens[\"ASTERISK\"] = \"*\";\n    // Ranges,\n    Tokens[\"INCLRANGE\"] = \"..\";\n    Tokens[\"EXCLRANGE\"] = \"...\";\n    // Delimiters,\n    Tokens[\"COMMA\"] = \",\";\n    Tokens[\"DOT\"] = \".\";\n    Tokens[\"COLON\"] = \":\";\n    Tokens[\"SEMICOLON\"] = \";\";\n    Tokens[\"LPAREN\"] = \"(\";\n    Tokens[\"RPAREN\"] = \")\";\n    Tokens[\"LBRACE\"] = \"{\";\n    Tokens[\"RBRACE\"] = \"}\";\n    Tokens[\"LBRACK\"] = \"[\";\n    Tokens[\"RBRACK\"] = \"]\";\n    Tokens[\"LT\"] = \"<\";\n    Tokens[\"GT\"] = \">\";\n    Tokens[\"QUOT\"] = \"\\\"\";\n})(Tokens || (Tokens = {}));\nconst reverseTokens = Object.entries(Tokens).reduce((acc, [key, value]) => {\n    acc[value] = key;\n    return acc;\n}, {});\nexport class Token {\n    type;\n    literal;\n    comments;\n    whitespace;\n    constructor(type, literal, comments = [], whitespace = \"\") {\n        this.type = type;\n        this.literal = literal;\n        this.comments = comments;\n        this.whitespace = whitespace;\n    }\n    serialize() {\n        let output = \"\";\n        for (const comment of this.comments) {\n            output += comment.serialize();\n        }\n        output += this.whitespace;\n        switch (this.type) {\n            case Tokens.IDENT:\n            case Tokens.COMMENT:\n            case Tokens.NUMBER:\n            case Tokens.FLOAT:\n                output += this.literal;\n                break;\n            case Tokens.STRING:\n                output += '\"' + this.literal + '\"';\n                break;\n            case Tokens.CTLOP:\n                output += \".\" + this.literal;\n                break;\n            case Tokens.BYTES:\n                output += \"'\" + this.literal + \"'\";\n                break;\n            case Tokens.HEX:\n                output += \"h'\" + this.literal + \"'\";\n                break;\n            case Tokens.BASE64:\n                output += \"b64'\" + this.literal + \"'\";\n                break;\n            case Tokens.EOF:\n                break;\n            default:\n                output += this.type.valueOf();\n        }\n        return output;\n    }\n    startWithSpaces() {\n        return this.whitespace !== \"\" || this.comments.length > 0;\n    }\n    toString(indent = 0) {\n        const indentStr = \"  \".repeat(indent);\n        const res = [\n            `${indentStr}${this.constructor.name}: ${reverseTokens[this.type]} (${this.type})`\n        ];\n        if (this.whitespace !== \"\") {\n            res.push(\"  \".repeat(indent + 1) + `whitespaces: ${this.whitespace.length}`);\n        }\n        if (this.literal !== \"\") {\n            res.push(\"  \".repeat(indent + 1) + `literal: ${this.literal}`);\n        }\n        for (const comment of this.comments) {\n            res.push(comment.toString(indent + 1));\n        }\n        return res.join(\"\\n\");\n    }\n    toJSON() {\n        return {\n            type: this.type,\n            literal: this.literal,\n            comments: this.comments?.map(c => c.toJSON()),\n            whitespace: this.whitespace\n        };\n    }\n}\n//# sourceMappingURL=tokens.js.map","import { Token, Tokens } from \"./tokens.js\";\nexport class Marker {\n    /**\n     * Serialize a Token.\n     *\n     * The function must handle whitespaces and comments that the Token\n     * contains.\n     */\n    serializeToken(token, node) {\n        return token.serialize();\n    }\n    /**\n     * Serialize a Value.\n     */\n    serializeValue(prefix, value, suffix, node) {\n        return prefix + value + suffix;\n    }\n    /**\n     * Serialize a typename or a groupname\n     */\n    serializeName(name, node) {\n        return name;\n    }\n    /**\n     * Wrapping markup for a node as a whole if needed\n     */\n    markupFor(node) {\n        return [null, null];\n    }\n}\n/**\n * Abstract base class for all nodes in the abstract syntax tree.\n */\nexport class CDDLNode {\n    parentNode = null;\n    serialize(marker) {\n        // Make sure that parentNode relationships are properly set\n        this.setChildrenParent();\n        if (marker) {\n            const markup = marker.markupFor(this);\n            let output = markup[0] !== null ? markup[0] : \"\";\n            output += this._serialize(marker);\n            output += markup[1] !== null ? markup[1] : \"\";\n            return output;\n        }\n        return this._serialize();\n    }\n    setChildrenParent() {\n        /**\n         * Initialize the parentNode links from children nodes to this node\n         * so that marker can access and adapt its behavior based on the\n         * current context.\n         */\n        for (const child of this.getChildren()) {\n            child.parentNode = this;\n            child.setChildrenParent();\n        }\n    }\n    getChildren() {\n        /**\n         * Return the list of children nodes attached to this node\n         */\n        return [];\n    }\n    _serializeToken(token, marker) {\n        if (!token) {\n            return \"\";\n        }\n        if (!marker) {\n            return token.serialize();\n        }\n        return marker.serializeToken(token, this);\n    }\n    toString(indent = 0) {\n        return \"  \".repeat(indent) + this.constructor.name;\n    }\n    toJSON() {\n        return {};\n    }\n}\n/**\n * A wrapped node is a node optionally enclosed in an open and close token.\n */\nexport class WrappedNode extends CDDLNode {\n    openToken = null;\n    closeToken = null;\n    serialize(marker) {\n        let output = \"\";\n        if (marker) {\n            const markup = marker.markupFor(this);\n            output += markup[0] !== null ? markup[0] : \"\";\n        }\n        output += this._serializeToken(this.openToken, marker);\n        output += this._serialize(marker);\n        output += this._serializeToken(this.closeToken, marker);\n        if (marker) {\n            const markup = marker.markupFor(this);\n            output += markup[1] !== null ? markup[1] : \"\";\n        }\n        return output;\n    }\n    toString(indent = 0) {\n        const res = [super.toString(indent)];\n        if (this.openToken) {\n            res.push(this.openToken.toString(indent + 1));\n        }\n        if (this.closeToken) {\n            res.push(this.closeToken.toString(indent + 1));\n        }\n        return res.join(\"\\n\");\n    }\n    toJSON() {\n        return Object.assign(super.toJSON(), {\n            openToken: this.openToken?.toJSON(),\n            closeToken: this.closeToken?.toJSON()\n        });\n    }\n}\n/**\n * A token node is a node that essentially represents a concrete token and/or\n * that may be part of a list.\n *\n * It stores the comments and whitespaces that may come *before* it, and an\n * optional separator token that may be used *after* it to separate it from\n * the next token in an underlying list.\n *\n * The separator remains null when the node is not part of a list, or not part\n * of a list that uses separators.\n *\n * A token node is a wrapped node if its openToken and closeToken properties\n * are set.\n */\nexport class TokenNode extends WrappedNode {\n    // Comments and whitespace *before* the node\n    comments = [];\n    whitespace = \"\";\n    separator = null;\n    /**\n     * Function may be useful in subclasses to output something\n     * before the comments and whitespace associated with the\n     * main token\n     */\n    _prestr(marker) {\n        return \"\";\n    }\n    serialize(marker) {\n        let output = this._prestr(marker);\n        for (const comment of this.comments) {\n            output += this._serializeToken(comment, marker);\n        }\n        output += this.whitespace;\n        output += super.serialize(marker);\n        output += this._serializeToken(this.separator, marker);\n        return output;\n    }\n    setComments(token) {\n        this.comments = token.comments;\n        this.whitespace = token.whitespace;\n    }\n    toString(indent = 0) {\n        const res = [super.toString(indent)];\n        for (const comment of this.comments) {\n            res.push(comment.toString(indent + 1));\n        }\n        if (this.whitespace !== \"\") {\n            res.push(\"  \".repeat(indent + 1) + `whitespaces: ${this.whitespace.length}`);\n        }\n        if (this.separator) {\n            res.push(this.separator.toString(indent + 1));\n        }\n        return res.join(\"\\n\");\n    }\n    toJSON() {\n        return Object.assign(super.toJSON(), {\n            comments: this.comments.map(c => c.toJSON()),\n            whitespace: this.whitespace,\n            separator: this.separator?.toJSON()\n        });\n    }\n}\n/**\n * Represents a set of CDDL rules\n */\nexport class CDDLTree extends TokenNode {\n    rules;\n    constructor(rules) {\n        super();\n        this.rules = rules;\n    }\n    getChildren() {\n        return this.rules;\n    }\n    _serialize(marker) {\n        return this.rules.map((item) => item.serialize(marker)).join(\"\");\n    }\n    toString(indent = 0) {\n        const res = [super.toString(indent)];\n        for (const rule of this.rules) {\n            res.push(rule.toString(indent + 1));\n        }\n        return res.join(\"\\n\");\n    }\n    toJSON() {\n        return Object.assign(super.toJSON(), {\n            rules: this.rules.map(r => r.toJSON())\n        });\n    }\n}\n/**\n * A group definition\n */\nexport class Rule extends CDDLNode {\n    name;\n    assign;\n    type;\n    constructor(name, assign, type) {\n        super();\n        this.name = name;\n        this.assign = assign;\n        this.type = type;\n    }\n    getChildren() {\n        return [this.name, this.type];\n    }\n    _serialize(marker) {\n        let output = this.name.serialize(marker);\n        output += this._serializeToken(this.assign, marker);\n        output += this.type.serialize(marker);\n        return output;\n    }\n    toString(indent = 0) {\n        return [\n            super.toString(indent),\n            this.name.toString(indent + 1),\n            this.assign.toString(indent + 1),\n            this.type.toString(indent + 1),\n        ].join(\"\\n\");\n    }\n    toJSON() {\n        return Object.assign(super.toJSON(), {\n            name: this.name.toJSON(),\n            assign: this.assign.toJSON(),\n            type: this.type.toJSON()\n        });\n    }\n}\n/**\n * A group entry\n */\nexport class GroupEntry extends TokenNode {\n    occurrence;\n    key;\n    type;\n    constructor(occurrence, key, type) {\n        super();\n        this.occurrence = occurrence;\n        this.key = key;\n        this.type = type;\n    }\n    getChildren() {\n        const children = [];\n        if (this.occurrence) {\n            children.push(this.occurrence);\n        }\n        if (this.key) {\n            children.push(this.key);\n        }\n        children.push(this.type);\n        return children;\n    }\n    _serialize(marker) {\n        let output = \"\";\n        if (this.occurrence) {\n            output += this.occurrence.serialize(marker);\n        }\n        if (this.key) {\n            output += this.key.serialize(marker);\n        }\n        output += this.type.serialize(marker);\n        return output;\n    }\n    /**\n     * Return true if GroupEntry can be converted to a proper Type.\n     */\n    isConvertibleToType() {\n        return (this.occurrence === null &&\n            this.key === null &&\n            (!(this.type instanceof Group) ||\n                this.type instanceof Array ||\n                this.type instanceof Map));\n    }\n    toString(indent = 0) {\n        const res = [super.toString(indent)];\n        if (this.occurrence) {\n            res.push(this.occurrence.toString(indent + 1));\n        }\n        if (this.key) {\n            res.push(this.key.toString(indent + 1));\n        }\n        res.push(this.type.toString(indent + 1));\n        return res.join(\"\\n\");\n    }\n    toJSON() {\n        return Object.assign(super.toJSON(), {\n            occurrence: this.occurrence?.toJSON(),\n            key: this.key?.toJSON(),\n            type: this.type.toJSON()\n        });\n    }\n}\n/**\n * A group, meaning a list of group choices wrapped in parentheses\n */\nexport class Group extends TokenNode {\n    groupChoices;\n    constructor(groupChoices) {\n        super();\n        this.groupChoices = groupChoices;\n    }\n    getChildren() {\n        return this.groupChoices;\n    }\n    _serialize(marker) {\n        return this.groupChoices.map((item) => item.serialize(marker)).join(\"\");\n    }\n    toString(indent = 0) {\n        const res = [super.toString(indent)];\n        for (const item of this.groupChoices) {\n            res.push(item.toString(indent + 1));\n        }\n        return res.join(\"\\n\");\n    }\n    toJSON() {\n        return Object.assign(super.toJSON(), {\n            groupChoices: this.groupChoices.map(g => g.toJSON())\n        });\n    }\n}\n/**\n * A map, meaning a list of group choices wrapped in curly braces\n */\nexport class Map extends Group {\n}\n/**\n * An array\n */\nexport class Array extends Group {\n}\n/**\n * A group choice\n */\nexport class GroupChoice extends TokenNode {\n    groupEntries;\n    constructor(groupEntries) {\n        super();\n        this.groupEntries = groupEntries;\n    }\n    getChildren() {\n        return this.groupEntries;\n    }\n    _serialize(marker) {\n        return this.groupEntries.map((item) => item.serialize(marker)).join(\"\");\n    }\n    toString(indent = 0) {\n        const res = [super.toString(indent)];\n        for (const item of this.groupEntries) {\n            res.push(item.toString(indent + 1));\n        }\n        return res.join(\"\\n\");\n    }\n    toJSON() {\n        return Object.assign(super.toJSON(), {\n            groupEntries: this.groupEntries.map(g => g.toJSON())\n        });\n    }\n}\n/**\n * A tag definition\n */\nexport class Tag extends TokenNode {\n    numericPart;\n    typePart;\n    constructor(numericPart = null, typePart = null) {\n        super();\n        this.numericPart = numericPart;\n        this.typePart = typePart;\n    }\n    getChildren() {\n        return this.typePart ? [this.typePart] : [];\n    }\n    _serialize(marker) {\n        let output = this._serializeToken(new Token(Tokens.HASH, \"\"), marker);\n        output += this._serializeToken(this.numericPart, marker);\n        output += this.typePart ? this.typePart.serialize(marker) : \"\";\n        return output;\n    }\n    toString(indent = 0) {\n        const res = [super.toString(indent) + \" (#)\"];\n        if (this.numericPart) {\n            res.push(this.numericPart.toString(indent + 1));\n        }\n        if (this.typePart) {\n            res.push(this.typePart.toString(indent + 1));\n        }\n        return res.join(\"\\n\");\n    }\n    toJSON() {\n        return Object.assign(super.toJSON(), {\n            numericPart: this.numericPart?.toJSON(),\n            typePart: this.typePart?.toJSON()\n        });\n    }\n}\n/**\n * Occurrence\n */\nexport class Occurrence extends TokenNode {\n    n;\n    m;\n    tokens;\n    constructor(n, m, tokens = []) {\n        super();\n        this.n = n;\n        this.m = m;\n        this.tokens = tokens;\n    }\n    _serialize(marker) {\n        return this.tokens.map((item) => this._serializeToken(item, marker)).join(\"\");\n    }\n    toString(indent = 0) {\n        const res = [super.toString(indent)];\n        for (const item of this.tokens) {\n            res.push(item.toString(indent + 1));\n        }\n        return res.join(\"\\n\");\n    }\n    toJSON() {\n        return Object.assign(super.toJSON(), {\n            n: this.n,\n            m: this.m,\n            tokens: this.tokens.map(t => t.toJSON())\n        });\n    }\n}\n/**\n * A value (number, text or bytes)\n */\nexport class Value extends TokenNode {\n    value;\n    type;\n    constructor(value, type) {\n        super();\n        this.value = value;\n        this.type = type;\n    }\n    _serialize(marker) {\n        let prefix = \"\";\n        let suffix = \"\";\n        if (this.type === \"text\") {\n            prefix = '\"';\n            suffix = '\"';\n        }\n        else if (this.type === \"bytes\") {\n            prefix = \"'\";\n            suffix = \"'\";\n        }\n        else if (this.type === \"hex\") {\n            prefix = \"h'\";\n            suffix = \"'\";\n        }\n        else if (this.type === \"base64\") {\n            prefix = \"b64'\";\n            suffix = \"'\";\n        }\n        if (!marker) {\n            return prefix + this.value + suffix;\n        }\n        return marker.serializeValue(prefix, this.value, suffix, this);\n    }\n    toString(indent = 0) {\n        return \"  \".repeat(indent) + `${this.constructor.name} (${this.type}): ${this.value}`;\n    }\n    toJSON() {\n        return Object.assign(super.toJSON(), {\n            value: this.value,\n            type: this.type\n        });\n    }\n}\n/**\n * A typename (or groupname)\n */\nexport class Typename extends TokenNode {\n    name;\n    unwrapped;\n    parameters;\n    constructor(name, unwrapped, parameters = null) {\n        super();\n        this.name = name;\n        this.unwrapped = unwrapped;\n        this.parameters = parameters;\n    }\n    getChildren() {\n        return this.parameters ? [this.parameters] : [];\n    }\n    _prestr(marker) {\n        return this._serializeToken(this.unwrapped, marker);\n    }\n    _serialize(marker) {\n        let output = \"\";\n        if (!marker) {\n            output = this.name;\n        }\n        else {\n            output += marker.serializeName(this.name, this);\n        }\n        if (this.parameters) {\n            output += this.parameters.serialize(marker);\n        }\n        return output;\n    }\n    toString(indent = 0) {\n        const res = [super.toString(indent), \"  \".repeat(indent + 1) + this.name];\n        if (this.unwrapped) {\n            res.push(this.unwrapped.toString(indent + 1));\n        }\n        if (this.parameters) {\n            res.push(this.parameters.toString(indent + 1));\n        }\n        return res.join(\"\\n\");\n    }\n    toJSON() {\n        return Object.assign(super.toJSON(), {\n            name: this.name,\n            unwrapped: this.unwrapped?.toJSON(),\n            parameters: this.parameters?.toJSON()\n        });\n    }\n}\n/**\n * A choice built from a group (or a groupname)\n */\nexport class ChoiceFrom extends TokenNode {\n    target;\n    constructor(target) {\n        super();\n        this.target = target;\n    }\n    getChildren() {\n        return [this.target];\n    }\n    _serialize(marker) {\n        let output = this._serializeToken(new Token(Tokens.AMPERSAND, \"\"), marker);\n        output += this.target.serialize(marker);\n        return output;\n    }\n    toString(indent = 0) {\n        const res = [\n            super.toString(indent) + \" (&)\",\n            this.target.toString(indent + 1),\n        ];\n        return res.join(\"\\n\");\n    }\n    toJSON() {\n        return Object.assign(super.toJSON(), {\n            target: this.target.toJSON()\n        });\n    }\n}\n/**\n * A Range is a specific kind of Type1.\n */\nexport class Range extends TokenNode {\n    min;\n    max;\n    rangeop;\n    constructor(min, max, rangeop) {\n        super();\n        this.min = min;\n        this.max = max;\n        this.rangeop = rangeop;\n    }\n    getChildren() {\n        return [this.min, this.max];\n    }\n    _serialize(marker) {\n        let output = this.min.serialize(marker);\n        output += this._serializeToken(this.rangeop, marker);\n        output += this.max.serialize(marker);\n        return output;\n    }\n    toString(indent = 0) {\n        const res = [\n            super.toString(indent),\n            this.min.toString(indent + 1),\n            this.rangeop.toString(indent + 1),\n            this.max.toString(indent + 1),\n        ];\n        return res.join(\"\\n\");\n    }\n    toJSON() {\n        return Object.assign(super.toJSON(), {\n            min: this.min.toJSON(),\n            max: this.max.toJSON(),\n            rangeop: this.rangeop.toJSON()\n        });\n    }\n}\n/**\n * An operator is a specific type of Type1\n */\nexport class Operator extends TokenNode {\n    type;\n    name;\n    controller;\n    constructor(type, name, controller) {\n        super();\n        this.type = type;\n        this.name = name;\n        this.controller = controller;\n    }\n    getChildren() {\n        return [this.type, this.controller];\n    }\n    _serialize(marker) {\n        let output = this.type.serialize(marker);\n        output += this._serializeToken(this.name, marker);\n        output += this.controller.serialize(marker);\n        return output;\n    }\n    toString(indent = 0) {\n        const res = [\n            super.toString(indent),\n            this.type.toString(indent + 1),\n            this.name.toString(indent + 1),\n            this.controller.toString(indent + 1),\n        ];\n        return res.join(\"\\n\");\n    }\n    toJSON() {\n        return Object.assign(super.toJSON(), {\n            type: this.type.toJSON(),\n            name: this.name.toJSON(),\n            controller: this.controller.toJSON()\n        });\n    }\n}\n/**\n * Memberkey\n */\nexport class Memberkey extends CDDLNode {\n    type;\n    hasCut;\n    hasColon;\n    tokens;\n    constructor(type, hasCut, hasColon, tokens = []) {\n        super();\n        this.type = type;\n        this.hasCut = hasCut;\n        this.hasColon = hasColon;\n        this.tokens = tokens;\n    }\n    getChildren() {\n        return [this.type];\n    }\n    _serialize(marker) {\n        let output = this.type.serialize(marker);\n        output += this.tokens.map((token) => this._serializeToken(token, marker)).join(\"\");\n        return output;\n    }\n    toString(indent = 0) {\n        const res = [super.toString(indent), this.type.toString(indent + 1)];\n        for (const item of this.tokens) {\n            res.push(item.toString(indent + 1));\n        }\n        return res.join(\"\\n\");\n    }\n    toJSON() {\n        return Object.assign(super.toJSON(), {\n            type: this.type.toJSON(),\n            hasCut: this.hasCut,\n            hasColon: this.hasColon,\n            tokens: this.tokens.map(t => t.toJSON())\n        });\n    }\n}\n/**\n * A Type is a list of Type1, each representing a possible choice.\n */\nexport class Type extends TokenNode {\n    types;\n    constructor(types) {\n        super();\n        this.types = types;\n    }\n    getChildren() {\n        return this.types;\n    }\n    _serialize(marker) {\n        return this.types.map((item) => item.serialize(marker)).join(\"\");\n    }\n    toString(indent = 0) {\n        const res = [super.toString(indent)];\n        for (const item of this.types) {\n            res.push(item.toString(indent + 1));\n        }\n        return res.join(\"\\n\");\n    }\n    toJSON() {\n        return Object.assign(super.toJSON(), {\n            types: this.types.map(t => t.toJSON())\n        });\n    }\n}\n/**\n * A set of generic parameters\n */\nexport class GenericParameters extends WrappedNode {\n    parameters;\n    constructor(parameters) {\n        super();\n        this.parameters = parameters;\n    }\n    getChildren() {\n        return this.parameters;\n    }\n    _serialize(marker) {\n        return this.parameters.map((item) => item.serialize(marker)).join(\"\");\n    }\n    toString(indent = 0) {\n        const res = [super.toString(indent)];\n        for (const item of this.parameters) {\n            res.push(item.toString(indent + 1));\n        }\n        return res.join(\"\\n\");\n    }\n    toJSON() {\n        return Object.assign(super.toJSON(), {\n            parameters: this.parameters.map(p => p.toJSON())\n        });\n    }\n}\n/**\n * A set of generic arguments\n */\nexport class GenericArguments extends WrappedNode {\n    parameters;\n    constructor(parameters) {\n        super();\n        this.parameters = parameters;\n    }\n    getChildren() {\n        return this.parameters;\n    }\n    _serialize(marker) {\n        return this.parameters.map((item) => item.serialize(marker)).join(\"\");\n    }\n    toString(indent = 0) {\n        const res = [super.toString(indent)];\n        for (const item of this.parameters) {\n            res.push(item.toString(indent + 1));\n        }\n        return res.join(\"\\n\");\n    }\n    toJSON() {\n        return Object.assign(super.toJSON(), {\n            parameters: this.parameters.map(p => p.toJSON())\n        });\n    }\n}\n//# sourceMappingURL=ast.js.map","export class ParserError extends Error {\n    /**\n     * CDDL tokenizer or parser error\n     */\n    constructor(message = \"A parsing error occurred\") {\n        super(message);\n        this.name = \"ParserError\";\n    }\n}\n//# sourceMappingURL=errors.js.map","export function isAlpha(ch) {\n    return (\"a\" <= ch && ch <= \"z\") || (\"A\" <= ch && ch <= \"Z\");\n}\nexport function isExtendedAlpha(ch) {\n    return isAlpha(ch) || \"@_$\".includes(ch);\n}\nexport function isUint(literal) {\n    return (literal.length > 0 &&\n        \"123456789\".includes(literal[0]) &&\n        [...literal].every(ch => \"0123456789\".includes(ch)));\n}\n//# sourceMappingURL=utils.js.map","import { Token, Tokens } from \"./tokens.js\";\nimport { isExtendedAlpha } from \"./utils.js\";\nimport { ParserError } from \"./errors.js\";\nexport class Location {\n    line;\n    position;\n    constructor(line = -1, position = -1) {\n        this.line = line;\n        this.position = position;\n    }\n}\nexport class Lexer {\n    input;\n    position = 0;\n    readPosition = 0;\n    ch = 0;\n    constructor(source) {\n        this.input = source;\n        this.readChar();\n    }\n    readChar() {\n        if (this.readPosition >= this.input.length) {\n            this.ch = 0;\n        }\n        else {\n            this.ch = this.input.charCodeAt(this.readPosition);\n        }\n        this.position = this.readPosition;\n        this.readPosition += 1;\n    }\n    _peekAtNextChar() {\n        if (this.readPosition >= this.input.length) {\n            return \"\";\n        }\n        return this.input[this.readPosition];\n    }\n    getLocation() {\n        const position = this.position - 2;\n        const sourceLines = this.input.split(\"\\n\");\n        let i = 0;\n        for (let line = 0; line < sourceLines.length; line++) {\n            const lineLength = sourceLines[line].length;\n            i += lineLength + 1;\n            if (i > position) {\n                const lineBegin = i - lineLength;\n                return new Location(line, position - lineBegin + 1);\n            }\n        }\n        return new Location(0, 0);\n    }\n    getLine(lineNumber) {\n        return this.input.split(\"\\n\")[lineNumber];\n    }\n    getLocationInfo() {\n        const loc = this.getLocation();\n        const line = loc.line >= 0 ? this.getLine(loc.line) : \"\";\n        let locationInfo = line + \"\\n\";\n        locationInfo += \" \".repeat(loc.position >= 0 ? loc.position : 0) + \"^\\n\";\n        locationInfo += \" \".repeat(loc.position >= 0 ? loc.position : 0) + \"|\\n\";\n        return locationInfo;\n    }\n    nextToken() {\n        const comments = this._readComments();\n        let whitespace = \"\";\n        if (comments.length > 0 && comments[comments.length - 1].literal === \"\") {\n            whitespace = comments[comments.length - 1].whitespace;\n            comments.pop();\n        }\n        let token = new Token(Tokens.ILLEGAL, \"\");\n        const literal = String.fromCharCode(this.ch);\n        let tokenRead = false;\n        if (literal === \"=\") {\n            if (this._peekAtNextChar() === \">\") {\n                this.readChar();\n                token = new Token(Tokens.ARROWMAP, \"\", comments, whitespace);\n            }\n            else {\n                token = new Token(Tokens.ASSIGN, \"\", comments, whitespace);\n            }\n        }\n        else if (literal === \"(\") {\n            token = new Token(Tokens.LPAREN, \"\", comments, whitespace);\n        }\n        else if (literal === \")\") {\n            token = new Token(Tokens.RPAREN, \"\", comments, whitespace);\n        }\n        else if (literal === \"{\") {\n            token = new Token(Tokens.LBRACE, \"\", comments, whitespace);\n        }\n        else if (literal === \"}\") {\n            token = new Token(Tokens.RBRACE, \"\", comments, whitespace);\n        }\n        else if (literal === \"[\") {\n            token = new Token(Tokens.LBRACK, \"\", comments, whitespace);\n        }\n        else if (literal === \"]\") {\n            token = new Token(Tokens.RBRACK, \"\", comments, whitespace);\n        }\n        else if (literal === \"<\") {\n            token = new Token(Tokens.LT, \"\", comments, whitespace);\n        }\n        else if (literal === \">\") {\n            token = new Token(Tokens.GT, \"\", comments, whitespace);\n        }\n        else if (literal === \"+\") {\n            token = new Token(Tokens.PLUS, \"\", comments, whitespace);\n        }\n        else if (literal === \",\") {\n            token = new Token(Tokens.COMMA, \"\", comments, whitespace);\n        }\n        else if (literal === \".\") {\n            if (this._peekAtNextChar() === \".\") {\n                this.readChar();\n                token = new Token(Tokens.INCLRANGE, \"\", comments, whitespace);\n                if (this._peekAtNextChar() === \".\") {\n                    this.readChar();\n                    token = new Token(Tokens.EXCLRANGE, \"\", comments, whitespace);\n                }\n            }\n            else if (isExtendedAlpha(this._peekAtNextChar())) {\n                this.readChar();\n                token = new Token(Tokens.CTLOP, this._readIdentifier(), comments, whitespace);\n                tokenRead = true;\n            }\n        }\n        else if (literal === \":\") {\n            token = new Token(Tokens.COLON, \"\", comments, whitespace);\n        }\n        else if (literal === \"?\") {\n            token = new Token(Tokens.QUEST, \"\", comments, whitespace);\n        }\n        else if (literal === \"/\") {\n            if (this._peekAtNextChar() === \"/\") {\n                this.readChar();\n                token = new Token(Tokens.GCHOICE, \"\", comments, whitespace);\n                if (this._peekAtNextChar() === \"=\") {\n                    this.readChar();\n                    token = new Token(Tokens.GCHOICEALT, \"\", comments, whitespace);\n                }\n            }\n            else if (this._peekAtNextChar() === \"=\") {\n                this.readChar();\n                token = new Token(Tokens.TCHOICEALT, \"\", comments, whitespace);\n            }\n            else {\n                token = new Token(Tokens.TCHOICE, \"\", comments, whitespace);\n            }\n        }\n        else if (literal === \"*\") {\n            token = new Token(Tokens.ASTERISK, \"\", comments, whitespace);\n        }\n        else if (literal === \"^\") {\n            token = new Token(Tokens.CARET, \"\", comments, whitespace);\n        }\n        else if (literal === \"#\") {\n            token = new Token(Tokens.HASH, \"\", comments, whitespace);\n        }\n        else if (literal === \"~\") {\n            token = new Token(Tokens.TILDE, \"\", comments, whitespace);\n        }\n        else if (literal === '\"') {\n            token = new Token(Tokens.STRING, this._readString(), comments, whitespace);\n        }\n        else if (literal === \"'\") {\n            token = new Token(Tokens.BYTES, this._readBytesString(), comments, whitespace);\n        }\n        else if (literal === \";\") {\n            token = new Token(Tokens.COMMENT, this._readComment(), comments, whitespace);\n            tokenRead = true;\n        }\n        else if (literal === \"&\") {\n            token = new Token(Tokens.AMPERSAND, \"\", comments, whitespace);\n        }\n        else if (this.ch === 0) {\n            token = new Token(Tokens.EOF, \"\", comments, whitespace);\n        }\n        else if (isExtendedAlpha(literal)) {\n            if (literal === \"b\" && this._peekAtNextChar() === \"6\") {\n                this.readChar();\n                this.readChar();\n                if (String.fromCharCode(this.ch) === \"4\" && this._peekAtNextChar() === \"'\") {\n                    this.readChar();\n                    token = new Token(Tokens.BASE64, this._readBytesString(), comments, whitespace);\n                }\n                else {\n                    // Looked like a b64 byte string, but that's just a regular identifier\n                    token = new Token(Tokens.IDENT, this._readIdentifier(\"b6\"), comments, whitespace);\n                    tokenRead = true;\n                }\n            }\n            else if (literal === \"h\" && this._peekAtNextChar() === \"'\") {\n                this.readChar();\n                token = new Token(Tokens.HEX, this._readBytesString(), comments, whitespace);\n            }\n            else {\n                token = new Token(Tokens.IDENT, this._readIdentifier(), comments, whitespace);\n                tokenRead = true;\n            }\n        }\n        else if (this._isDigit(literal) || literal === \"-\") {\n            // Numbers start with a digit or a minus\n            const numberOrFloat = this._readNumberOrFloat();\n            token = new Token(numberOrFloat.includes(\".\") ? Tokens.FLOAT : Tokens.NUMBER, numberOrFloat, comments, whitespace);\n            tokenRead = true;\n        }\n        if (!tokenRead) {\n            this.readChar();\n        }\n        return token;\n    }\n    _isDigit(char) {\n        return char >= \"0\" && char <= \"9\";\n    }\n    _readIdentifier(start = \"\") {\n        const position = this.position;\n        if (start === \"\" && !isExtendedAlpha(String.fromCharCode(this.ch))) {\n            throw this._tokenError(\"expected identifier, got nothing\");\n        }\n        while (isExtendedAlpha(String.fromCharCode(this.ch)) ||\n            this._isDigit(String.fromCharCode(this.ch)) ||\n            \"-. \".includes(String.fromCharCode(this.ch))) {\n            const ch = String.fromCharCode(this.ch);\n            if (ch === \" \")\n                break; // Handle space which might be in the list\n            if (\"-.\".includes(ch)) {\n                // Continue but identifier cannot end with these\n            }\n            this.readChar();\n        }\n        const identifier = start + this.input.substring(position, this.position);\n        if (identifier.endsWith(\"-\") || identifier.endsWith(\".\")) {\n            throw this._tokenError(`identifier cannot end with \\`-\\` or \\`.\\`, got \\`${identifier}\\``);\n        }\n        return identifier;\n    }\n    _readComment() {\n        const position = this.position;\n        while (this.ch && String.fromCharCode(this.ch) !== \"\\n\") {\n            this.readChar();\n        }\n        return this.input.substring(position, this.position);\n    }\n    _readString() {\n        const position = this.position;\n        // assert String.fromCharCode(this.ch) === '\"'\n        this.readChar(); // eat \"\n        while (String.fromCharCode(this.ch) !== '\"') {\n            if ((this.ch >= 0x20 && this.ch <= 0x21) ||\n                (this.ch >= 0x23 && this.ch <= 0x5b) ||\n                (this.ch >= 0x5d && this.ch <= 0x7e) ||\n                (this.ch >= 0x80 && this.ch <= 0x10fffd)) {\n                this.readChar();\n            }\n            else if (String.fromCharCode(this.ch) === \"\\\\\") {\n                this.readChar();\n                if ((this.ch >= 0x20 && this.ch <= 0x7e) ||\n                    (this.ch >= 0x80 && this.ch <= 0x10fffd)) {\n                    this.readChar();\n                }\n                else {\n                    throw this._tokenError(`invalid escape character in text string \\`${this.input.substring(position + 1, this.position)}\\``);\n                }\n            }\n            else if (this.ch === 0x0a) {\n                this.readChar();\n            }\n            else if (this.ch === 0x0d && this._peekAtNextChar().charCodeAt(0) === 0x0a) {\n                this.readChar();\n                this.readChar();\n            }\n            else {\n                throw this._tokenError(`invalid text string \\`${this.input.substring(position + 1, this.position)}\\``);\n            }\n        }\n        return this.input.substring(position + 1, this.position);\n    }\n    _readBytesString() {\n        const position = this.position;\n        // assert String.fromCharCode(this.ch) === \"'\"\n        this.readChar(); // eat '\n        while (String.fromCharCode(this.ch) !== \"'\") {\n            if ((this.ch >= 0x20 && this.ch <= 0x26) ||\n                (this.ch >= 0x28 && this.ch <= 0x5b) ||\n                (this.ch >= 0x5d && this.ch <= 0x10fffd)) {\n                this.readChar();\n            }\n            else if (String.fromCharCode(this.ch) === \"\\\\\") {\n                this.readChar();\n                if ((this.ch >= 0x20 && this.ch <= 0x7e) ||\n                    (this.ch >= 0x80 && this.ch <= 0x10fffd)) {\n                    this.readChar();\n                }\n                else {\n                    throw this._tokenError(`invalid escape character in byte string \\`${this.input.substring(position + 1, this.position)}\\``);\n                }\n            }\n            else if (this.ch === 0x0a) {\n                this.readChar();\n            }\n            else if (this.ch === 0x0d && this._peekAtNextChar().charCodeAt(0) === 0x0a) {\n                this.readChar();\n                this.readChar();\n            }\n            else {\n                throw this._tokenError(`invalid byte string \\`${this.input.substring(position + 1, this.position)}\\``);\n            }\n        }\n        return this.input.substring(position + 1, this.position);\n    }\n    _readNumberOrFloat() {\n        const position = this.position;\n        let dotFound = false;\n        if (String.fromCharCode(this.ch) === \"-\") {\n            this.readChar();\n        }\n        if (String.fromCharCode(this.ch) === \"0\") {\n            this.readChar();\n            if (String.fromCharCode(this.ch) === \"x\") {\n                // Hex number\n                this.readChar();\n                if (!\"0123456789ABCDEF\".includes(String.fromCharCode(this.ch).toUpperCase())) {\n                    throw this._tokenError(`expected hex number to contain hex digits, got \\`${this.input.substring(position, this.position)}\\``);\n                }\n                while (\"0123456789ABCDEF\".includes(String.fromCharCode(this.ch).toUpperCase())) {\n                    this.readChar();\n                }\n                if (String.fromCharCode(this.ch) === \".\") {\n                    dotFound = true;\n                    if (this._peekAtNextChar() === \".\") {\n                        return this.input.substring(position, this.position);\n                    }\n                    this.readChar();\n                    while (\"0123456789ABCDEF\".includes(String.fromCharCode(this.ch).toUpperCase())) {\n                        this.readChar();\n                    }\n                }\n                if (dotFound && String.fromCharCode(this.ch) !== \"p\") {\n                    throw this._tokenError(`expected hex number with fraction to have an exponent, got \\`${this.input.substring(position, this.position)}\\``);\n                }\n                if (String.fromCharCode(this.ch) === \"p\") {\n                    this.readChar();\n                    if (\"+-\".includes(String.fromCharCode(this.ch))) {\n                        this.readChar();\n                    }\n                    if (!this._isDigit(String.fromCharCode(this.ch))) {\n                        throw this._tokenError(`expected hex number with exponent to have a digit in exponent, got \\`${this.input.substring(position, this.position)}\\``);\n                    }\n                    while (this._isDigit(String.fromCharCode(this.ch))) {\n                        this.readChar();\n                    }\n                }\n            }\n            else if (String.fromCharCode(this.ch) === \"b\") {\n                // Binary number\n                this.readChar();\n                if (!\"01\".includes(String.fromCharCode(this.ch))) {\n                    throw this._tokenError(`expected binary number to have binary digits, got \\`${this.input.substring(position, this.position)}\\``);\n                }\n                while (\"01\".includes(String.fromCharCode(this.ch))) {\n                    this.readChar();\n                }\n            }\n            else if (String.fromCharCode(this.ch) === \".\") {\n                if (this._peekAtNextChar() === \".\") {\n                    return this.input.substring(position, this.position);\n                }\n                this.readChar();\n                if (!this._isDigit(String.fromCharCode(this.ch))) {\n                    throw this._tokenError(`expected number with fraction to have digits in fraction part, got \\`${this.input.substring(position, this.position)}\\``);\n                }\n                while (this._isDigit(String.fromCharCode(this.ch))) {\n                    this.readChar();\n                }\n                if (String.fromCharCode(this.ch) === \"e\") {\n                    this.readChar();\n                    if (\"+-\".includes(String.fromCharCode(this.ch))) {\n                        this.readChar();\n                    }\n                    if (!this._isDigit(String.fromCharCode(this.ch))) {\n                        throw this._tokenError(`expected number with exponent to have digits in exponent, got \\`${this.input.substring(position, this.position)}\\``);\n                    }\n                    while (this._isDigit(String.fromCharCode(this.ch))) {\n                        this.readChar();\n                    }\n                }\n            }\n        }\n        else {\n            while (this._isDigit(String.fromCharCode(this.ch))) {\n                this.readChar();\n            }\n            if (String.fromCharCode(this.ch) === \".\") {\n                if (this._peekAtNextChar() === \".\") {\n                    return this.input.substring(position, this.position);\n                }\n                this.readChar();\n                if (!this._isDigit(String.fromCharCode(this.ch))) {\n                    throw this._tokenError(`expected number with fraction to have digits in fraction part, got \\`${this.input.substring(position, this.position)}\\``);\n                }\n                while (this._isDigit(String.fromCharCode(this.ch))) {\n                    this.readChar();\n                }\n            }\n            if (String.fromCharCode(this.ch) === \"e\") {\n                this.readChar();\n                if (\"+-\".includes(String.fromCharCode(this.ch))) {\n                    this.readChar();\n                }\n                if (!this._isDigit(String.fromCharCode(this.ch))) {\n                    throw this._tokenError(`expected number with exponent to have digits in exponent, got \\`${this.input.substring(position, this.position)}\\``);\n                }\n                while (this._isDigit(String.fromCharCode(this.ch))) {\n                    this.readChar();\n                }\n            }\n        }\n        return this.input.substring(position, this.position);\n    }\n    _readWhitespace() {\n        const position = this.position;\n        while (\" \\t\\n\\r\".includes(String.fromCharCode(this.ch))) {\n            this.readChar();\n        }\n        return this.input.substring(position, this.position);\n    }\n    _readComments() {\n        const comments = [];\n        while (true) {\n            const whitespace = this._readWhitespace();\n            if (String.fromCharCode(this.ch) !== \";\") {\n                if (whitespace !== \"\") {\n                    const token = new Token(Tokens.COMMENT, \"\", [], whitespace);\n                    comments.push(token);\n                }\n                break;\n            }\n            const token = new Token(Tokens.COMMENT, this._readComment(), [], whitespace);\n            comments.push(token);\n        }\n        return comments;\n    }\n    _tokenError(message) {\n        const location = this.getLocation();\n        return new ParserError(`CDDL token error - line ${location.line + 1}: ${message}`);\n    }\n}\n//# sourceMappingURL=lexer.js.map","import { ParserError } from \"./errors.js\";\nimport { Lexer } from \"./lexer.js\";\nimport { Token, Tokens } from \"./tokens.js\";\nimport { isUint } from \"./utils.js\";\nimport { CDDLTree, CDDLNode, Rule, GroupEntry, Group, Map, Array, GroupChoice, Type, Typename, Value, Operator, Tag, Range, Memberkey, ChoiceFrom, Occurrence, GenericParameters, GenericArguments } from \"./ast.js\";\nconst NIL_TOKEN = new Token(Tokens.ILLEGAL, \"\");\nexport class Parser {\n    lexer;\n    curToken = NIL_TOKEN;\n    peekToken = NIL_TOKEN;\n    getCurToken() {\n        return this.curToken;\n    }\n    getPeekToken() {\n        return this.peekToken;\n    }\n    constructor(source) {\n        this.lexer = new Lexer(source);\n        this._nextToken();\n        this._nextToken();\n    }\n    parse() {\n        const rules = [];\n        while (this.getCurToken().type !== Tokens.EOF) {\n            const rule = this._parseRule();\n            rules.push(rule);\n        }\n        const tree = new CDDLTree(rules);\n        tree.separator = this._nextToken();\n        tree.setChildrenParent();\n        this._convertGroupDefinitions(tree);\n        return tree;\n    }\n    _parseRule() {\n        const typename = this._parseTypename(true, null);\n        const assign = this._nextToken();\n        let node;\n        if (assign.type === Tokens.ASSIGN || assign.type === Tokens.GCHOICEALT) {\n            const groupEntry = this._parseGroupEntry();\n            node = new Rule(typename, assign, groupEntry);\n        }\n        else if (assign.type === Tokens.TCHOICEALT) {\n            const ruleType = this._parseType();\n            if (!(ruleType instanceof Type)) {\n                throw new Error(\"Expected Type instance\");\n            }\n            node = new Rule(typename, assign, ruleType);\n        }\n        else {\n            throw this._parserError(`expected assignment (\\`=\\`, \\`/=\\`, \\`//=\\`) after \\`${typename.serialize().trim()}\\`, got \\`${assign.serialize().trim()}\\``);\n        }\n        return node;\n    }\n    _parseGroupEntry() {\n        const occurrence = this._parseOccurrence();\n        const looseType = this._parseType(true);\n        let node;\n        if (looseType instanceof Memberkey) {\n            const entryType = this._parseType(false);\n            if (!(entryType instanceof Type)) {\n                throw new Error(\"Expected Type instance\");\n            }\n            node = new GroupEntry(occurrence, looseType, entryType);\n        }\n        else {\n            node = new GroupEntry(occurrence, null, looseType);\n        }\n        return node;\n    }\n    _parseType(loose = false) {\n        const altTypes = [];\n        let type1 = this._parseType1(loose);\n        altTypes.push(type1);\n        if (loose && this.getCurToken().type === Tokens.CARET) {\n            const caretTokens = [];\n            caretTokens.push(this._nextToken());\n            if (this.getCurToken().type !== Tokens.ARROWMAP) {\n                throw this._parserError(`expected arrow map (\\`=>\\`), got \\`#{(this.getCurToken().serialize() + this.getPeekToken().serialize()).trim()}\\``);\n            }\n            caretTokens.push(this._nextToken());\n            const key = new Memberkey(type1, true, false, caretTokens);\n            return key;\n        }\n        if (loose && this.getCurToken().type === Tokens.ARROWMAP) {\n            const key = new Memberkey(type1, false, false, [this._nextToken()]);\n            return key;\n        }\n        if (loose && this.getCurToken().type === Tokens.COLON) {\n            const key = new Memberkey(type1, true, true, [this._nextToken()]);\n            return key;\n        }\n        while (this.getCurToken().type === Tokens.TCHOICE) {\n            // Record the separator with the previous type\n            type1.separator = this._nextToken();\n            type1 = this._parseType1();\n            altTypes.push(type1);\n        }\n        const node = new Type(altTypes);\n        return node;\n    }\n    _parseType1(loose = false) {\n        const type2 = this._parseType2(loose);\n        let node;\n        if (this.getCurToken().type === Tokens.INCLRANGE || this.getCurToken().type === Tokens.EXCLRANGE) {\n            const rangeop = this._nextToken();\n            const maxType = this._parseType2();\n            if (!(type2 instanceof Value || type2 instanceof Typename)) {\n                throw this._parserError(`expected range min to be a value or a typename, got \\`${type2.serialize().trim()}\\``);\n            }\n            if (!(maxType instanceof Value || maxType instanceof Typename)) {\n                throw this._parserError(`expected range max to be a value or a typename, got \\`${maxType.serialize().trim()}\\``);\n            }\n            node = new Range(type2, maxType, rangeop);\n        }\n        else if (this.getCurToken().type === Tokens.CTLOP) {\n            // Note: validation of operator name could be added here\n            const operator = this._nextToken();\n            const controlType = this._parseType2();\n            node = new Operator(type2, operator, controlType);\n        }\n        else {\n            node = type2;\n        }\n        return node;\n    }\n    _parseType2(loose = false) {\n        let node;\n        if (this.getCurToken().type === Tokens.LPAREN) {\n            const openToken = this._nextToken();\n            if (loose) {\n                node = this._parseGroup(false);\n            }\n            else {\n                const innerType = this._parseType();\n                if (!(innerType instanceof Type)) {\n                    throw new Error(\"Expected Type instance\");\n                }\n                node = innerType;\n            }\n            node.openToken = openToken;\n            if (this.getCurToken().type !== Tokens.RPAREN) {\n                throw this._parserError(`expected right parenthesis, got \\`${this.getCurToken().serialize().trim()}\\``);\n            }\n            node.closeToken = this._nextToken();\n        }\n        else if (this.getCurToken().type === Tokens.LBRACE) {\n            const openToken = this._nextToken();\n            node = this._parseGroup(true);\n            node.openToken = openToken;\n            if (this.getCurToken().type !== Tokens.RBRACE) {\n                throw this._parserError(`expected right brace, got \\`${this.getCurToken().serialize()}\\``);\n            }\n            node.closeToken = this._nextToken();\n        }\n        else if (this.getCurToken().type === Tokens.LBRACK) {\n            const openToken = this._nextToken();\n            const group = this._parseGroup(false);\n            node = new Array(group.groupChoices);\n            node.openToken = openToken;\n            if (this.getCurToken().type !== Tokens.RBRACK) {\n                throw this._parserError(`expected right bracket, got \\`${this.getCurToken().serialize().trim()}\\``);\n            }\n            node.closeToken = this._nextToken();\n        }\n        else if (this.getCurToken().type === Tokens.TILDE) {\n            const unwrapped = this._nextToken();\n            node = this._parseTypename(false, unwrapped);\n        }\n        else if (this.getCurToken().type === Tokens.AMPERSAND) {\n            const refToken = this._nextToken();\n            if (this.getCurToken().type === Tokens.LPAREN) {\n                const openToken = this._nextToken();\n                const group = this._parseGroup(false);\n                group.openToken = openToken;\n                if (this.getCurToken().type !== Tokens.RPAREN) {\n                    throw this._parserError(`expected right parenthesis, got \\`${this.getCurToken().serialize().trim()}\\``);\n                }\n                group.closeToken = this._nextToken();\n                node = new ChoiceFrom(group);\n            }\n            else {\n                const typename = this._parseTypename(false, null);\n                node = new ChoiceFrom(typename);\n            }\n            node.setComments(refToken);\n        }\n        else if (this.getCurToken().type === Tokens.HASH) {\n            const hashToken = this._nextToken();\n            if ((this.getCurToken().type === Tokens.NUMBER || this.getCurToken().type === Tokens.FLOAT) &&\n                !this.getCurToken().startWithSpaces()) {\n                const number = this._nextToken();\n                if (number.literal.length > 1 &&\n                    (number.literal[1] !== \".\" || number.literal.includes(\"e\"))) {\n                    throw this._parserError(`expected data item after \"#\" to match \\`DIGIT [\".\" uint]\\`, got \\`${this.getCurToken().serialize().trim()}\\``);\n                }\n                if (number.literal[0] === \"6\" &&\n                    this.getCurToken().type === Tokens.LPAREN &&\n                    !this.getCurToken().startWithSpaces()) {\n                    const type2 = this._parseType2();\n                    if (!(type2 instanceof Type)) {\n                        throw new Error(\"Expected Type instance\");\n                    }\n                    node = new Tag(number, type2);\n                }\n                else {\n                    node = new Tag(number);\n                }\n            }\n            else {\n                node = new Tag();\n            }\n            node.setComments(hashToken);\n        }\n        else if (this.getCurToken().type === Tokens.IDENT) {\n            node = this._parseTypename(false, null);\n        }\n        else if (this.getCurToken().type === Tokens.STRING) {\n            const value = this._nextToken();\n            node = new Value(value.literal, \"text\");\n            node.setComments(value);\n        }\n        else if (this.getCurToken().type === Tokens.BYTES) {\n            const value = this._nextToken();\n            node = new Value(value.literal, \"bytes\");\n            node.setComments(value);\n        }\n        else if (this.getCurToken().type === Tokens.HEX) {\n            const value = this._nextToken();\n            node = new Value(value.literal, \"hex\");\n            node.setComments(value);\n        }\n        else if (this.getCurToken().type === Tokens.BASE64) {\n            const value = this._nextToken();\n            node = new Value(value.literal, \"base64\");\n            node.setComments(value);\n        }\n        else if (this.getCurToken().type === Tokens.NUMBER) {\n            const value = this._nextToken();\n            node = new Value(value.literal, \"number\");\n            node.setComments(value);\n        }\n        else if (this.getCurToken().type === Tokens.FLOAT) {\n            const value = this._nextToken();\n            node = new Value(value.literal, \"number\");\n            node.setComments(value);\n        }\n        else {\n            throw this._parserError(`invalid type2 production, got \\`${this.getCurToken().serialize().trim()}\\``);\n        }\n        return node;\n    }\n    _parseGroup(isMap = false) {\n        const groupChoices = [];\n        while (true) {\n            if (this.getCurToken().type === Tokens.RPAREN ||\n                this.getCurToken().type === Tokens.RBRACE ||\n                this.getCurToken().type === Tokens.RBRACK) {\n                break;\n            }\n            const groupEntries = [];\n            while (this.getCurToken().type !== Tokens.GCHOICE) {\n                const groupEntry = this._parseGroupEntry();\n                groupEntries.push(groupEntry);\n                if (this.getCurToken().type === Tokens.COMMA) {\n                    groupEntry.separator = this._nextToken();\n                }\n                if (this.getCurToken().type === Tokens.RPAREN ||\n                    this.getCurToken().type === Tokens.RBRACE ||\n                    this.getCurToken().type === Tokens.RBRACK) {\n                    break;\n                }\n            }\n            const groupChoice = new GroupChoice(groupEntries);\n            groupChoices.push(groupChoice);\n            if (this.getCurToken().type === Tokens.RPAREN ||\n                this.getCurToken().type === Tokens.RBRACE ||\n                this.getCurToken().type === Tokens.RBRACK) {\n                break;\n            }\n            groupChoice.separator = this._nextToken();\n        }\n        let node;\n        if (isMap) {\n            node = new Map(groupChoices);\n        }\n        else {\n            node = new Group(groupChoices);\n        }\n        return node;\n    }\n    _parseOccurrence() {\n        const tokens = [];\n        let occurrence = null;\n        if (this.getCurToken().type === Tokens.QUEST ||\n            this.getCurToken().type === Tokens.ASTERISK ||\n            this.getCurToken().type === Tokens.PLUS) {\n            const n = this.getCurToken().type === Tokens.PLUS ? 1 : 0;\n            let m = Infinity;\n            if (this.getCurToken().type === Tokens.ASTERISK &&\n                this.getPeekToken().type === Tokens.NUMBER &&\n                isUint(this.getPeekToken().literal) &&\n                !this.getPeekToken().startWithSpaces()) {\n                tokens.push(this._nextToken());\n                m = parseInt(this.getCurToken().literal);\n            }\n            tokens.push(this._nextToken());\n            occurrence = new Occurrence(n, m, tokens);\n        }\n        else if (this.getCurToken().type === Tokens.NUMBER &&\n            isUint(this.getCurToken().literal) &&\n            this.getPeekToken().type === Tokens.ASTERISK &&\n            !this.getPeekToken().startWithSpaces()) {\n            const n = parseInt(this.getCurToken().literal);\n            let m = Infinity;\n            tokens.push(this._nextToken()); // eat \"n\"\n            tokens.push(this._nextToken()); // eat \"*\"\n            if (this.getCurToken().type === Tokens.NUMBER &&\n                isUint(this.getCurToken().literal) &&\n                !this.getCurToken().startWithSpaces()) {\n                m = parseInt(this.getCurToken().literal);\n                tokens.push(this._nextToken());\n            }\n            occurrence = new Occurrence(n, m, tokens);\n        }\n        return occurrence;\n    }\n    _parseTypename(definition = false, unwrapped = null) {\n        if (this.getCurToken().type !== Tokens.IDENT) {\n            throw this._parserError(`expected group identifier, got \\`${this.getCurToken().serialize().trim()}\\``);\n        }\n        const ident = this._nextToken();\n        let parameters;\n        if (definition) {\n            parameters = this._parseGenericParameters();\n        }\n        else {\n            parameters = this._parseGenericArguments();\n        }\n        const typename = new Typename(ident.literal, unwrapped, parameters);\n        typename.setComments(ident);\n        return typename;\n    }\n    _parseGenericParameters() {\n        if (this.getCurToken().type !== Tokens.LT || this.getCurToken().startWithSpaces()) {\n            return null;\n        }\n        const openToken = this._nextToken();\n        const parameters = [];\n        let name = this._parseTypename();\n        parameters.push(name);\n        while (this.getCurToken().type === Tokens.COMMA) {\n            name.separator = this._nextToken();\n            name = this._parseTypename();\n            parameters.push(name);\n        }\n        const node = new GenericParameters(parameters);\n        node.openToken = openToken;\n        if (this.getCurToken().type !== Tokens.GT) {\n            throw this._parserError(`expected \\`>\\` character to end generic production, got \\`${this.getCurToken().serialize().trim()}\\``);\n        }\n        node.closeToken = this._nextToken();\n        return node;\n    }\n    _parseGenericArguments() {\n        if (this.getCurToken().type !== Tokens.LT || this.getCurToken().startWithSpaces()) {\n            return null;\n        }\n        const openToken = this._nextToken();\n        const parameters = [];\n        let type1 = this._parseType1();\n        parameters.push(type1);\n        while (this.getCurToken().type === Tokens.COMMA) {\n            type1.separator = this._nextToken();\n            type1 = this._parseType1();\n            parameters.push(type1);\n        }\n        const node = new GenericArguments(parameters);\n        node.openToken = openToken;\n        if (this.getCurToken().type !== Tokens.GT) {\n            throw this._parserError(`expected \\`>\\` character to end generic production, got \\`${this.getCurToken().serialize().trim()}\\``);\n        }\n        node.closeToken = this._nextToken();\n        return node;\n    }\n    _convertGroupDefinitions(tree) {\n        const rulenames = new Set();\n        const typenames = new Set();\n        const groupnames = new Set();\n        const checkUnderlyingType = (type1) => {\n            if (type1 instanceof Value ||\n                type1 instanceof Map ||\n                type1 instanceof Array ||\n                type1 instanceof ChoiceFrom ||\n                type1 instanceof Tag) {\n                return \"type\";\n            }\n            if (type1 instanceof Range) {\n                return checkUnderlyingType(type1.min);\n            }\n            if (type1 instanceof Operator) {\n                return checkUnderlyingType(type1.type);\n            }\n            if (type1 instanceof Typename) {\n                const name = type1.name;\n                if (typenames.has(name) || this._isPreludeType(name)) {\n                    return \"type\";\n                }\n                if (groupnames.has(name)) {\n                    return \"group\";\n                }\n            }\n            return \"unknown\";\n        };\n        for (const rule of tree.rules) {\n            rulenames.add(rule.name.name);\n            if (typenames.size === 0) {\n                typenames.add(rule.name.name);\n            }\n            if (rule.type instanceof Type) {\n                typenames.add(rule.name.name);\n                continue;\n            }\n            if (rule.assign.type === Tokens.TCHOICEALT) {\n                typenames.add(rule.name.name);\n            }\n            if (rule.assign.type === Tokens.GCHOICEALT) {\n                groupnames.add(rule.name.name);\n            }\n            if (rule.type.type.types.length > 1 && rule.type.type.openToken === null) {\n                typenames.add(rule.name.name);\n            }\n            if (rule.type.occurrence !== null) {\n                groupnames.add(rule.name.name);\n            }\n            if (rule.type.key !== null) {\n                groupnames.add(rule.name.name);\n            }\n        }\n        const lookForKeys = (node) => {\n            if (node instanceof GroupEntry &&\n                node.key !== null &&\n                node.key.type instanceof Typename &&\n                !node.key.hasColon &&\n                rulenames.has(node.key.type.name)) {\n                typenames.add(node.key.type.name);\n            }\n            for (const child of node.getChildren()) {\n                lookForKeys(child);\n            }\n        };\n        lookForKeys(tree);\n        let updateFound = true;\n        while (updateFound) {\n            updateFound = false;\n            for (const rule of tree.rules) {\n                if (rule.type instanceof Type) {\n                    for (const type1 of rule.type.types) {\n                        if (type1 instanceof Typename && rulenames.has(type1.name)) {\n                            if (!typenames.has(type1.name)) {\n                                updateFound = true;\n                                typenames.add(type1.name);\n                            }\n                        }\n                    }\n                    continue;\n                }\n                if (typenames.has(rule.name.name)) {\n                    for (const type1 of rule.type.type.types) {\n                        if (type1 instanceof Typename && rulenames.has(type1.name)) {\n                            if (!typenames.has(type1.name)) {\n                                updateFound = true;\n                                typenames.add(type1.name);\n                            }\n                        }\n                    }\n                }\n                if (groupnames.has(rule.name.name)) {\n                    for (const type1 of rule.type.type.types) {\n                        if (type1 instanceof Typename && rulenames.has(type1.name)) {\n                            if (!groupnames.has(type1.name)) {\n                                updateFound = true;\n                                groupnames.add(type1.name);\n                            }\n                        }\n                    }\n                }\n                if (rule.assign.type === Tokens.ASSIGN) {\n                    const defTypes = new Set(rule.type.type.types.map((type1) => checkUnderlyingType(type1)));\n                    if (defTypes.has(\"type\") && defTypes.has(\"group\")) {\n                        throw new ParserError(`CDDL semantic error - rule \\`${rule.name.name}\\` targets a mix of type and group rules`);\n                    }\n                    if (defTypes.has(\"type\")) {\n                        if (!typenames.has(rule.name.name)) {\n                            updateFound = true;\n                            typenames.add(rule.name.name);\n                        }\n                    }\n                    else if (defTypes.has(\"group\")) {\n                        if (!groupnames.has(rule.name.name)) {\n                            updateFound = true;\n                            groupnames.add(rule.name.name);\n                        }\n                    }\n                }\n            }\n        }\n        const overlap = [...typenames].filter((name) => groupnames.has(name));\n        if (overlap.length > 0) {\n            const overlapStr = overlap.join(\", \");\n            throw new ParserError(`CDDL semantic error - mix of type and group definitions for ${overlapStr}`);\n        }\n        for (const rule of tree.rules) {\n            if (rule.type instanceof Type) {\n                continue;\n            }\n            if (typenames.has(rule.name.name)) {\n                if (!rule.type.isConvertibleToType()) {\n                    throw new ParserError(`CDDL semantic error - rule \\`${rule.name.name}\\` is a type definition but uses a group entry`);\n                }\n                rule.type = rule.type.type;\n            }\n        }\n    }\n    _isPreludeType(name) {\n        const preludeTypes = [\n            \"any\", \"uint\", \"nint\", \"int\", \"bstr\", \"bytes\", \"tstr\", \"text\", \"tdate\",\n            \"time\", \"number\", \"biguint\", \"bignint\", \"bigint\", \"integer\", \"unsigned\",\n            \"decfrac\", \"bigfloat\", \"eb64url\", \"eb64legacy\", \"eb16\", \"encoded-cbor\",\n            \"uri\", \"b64url\", \"b64legacy\", \"regexp\", \"mime-message\", \"cbor-any\",\n            \"float16\", \"float32\", \"float64\", \"float16-32\", \"float32-64\", \"float\",\n            \"false\", \"true\", \"bool\", \"nil\", \"null\", \"undefined\"\n        ];\n        return preludeTypes.includes(name);\n    }\n    _nextToken() {\n        const curToken = this.curToken;\n        this.curToken = this.peekToken;\n        this.peekToken = this.lexer.nextToken();\n        return curToken;\n    }\n    _parserError(message) {\n        const location = this.lexer.getLocation();\n        return new ParserError(`CDDL syntax error - line ${location.line + 1}: ${message}`);\n    }\n}\n//# sourceMappingURL=parser.js.map","/**\n * Implementation of MIMEType and MIME Type parser from\n * https://mimesniff.spec.whatwg.org/\n */\n\nconst HTTPTokenCodePoints = /^[!#$%&'*+-.^`|~\\w]+$/;\n\n// \"HTTP whitespace is U+000A LF, U+000D CR, U+0009 TAB or U+0020 SPACE.\"\n// eslint-disable-next-line no-control-regex\nconst HTTPWhiteSpace = /[\\u000A\\u000D\\u0009\\u0020]/u;\n\n// An HTTP quoted-string token code point is\n// U+0009 TAB,\n// a code point in the range U+0020 SPACE to U+007E (~), inclusive,\n// or a code point in the range U+0080 through U+00FF (ÿ), inclusive.\n// eslint-disable-next-line no-control-regex\nconst HTTPQuotedString = /^[\\u0009\\u{0020}-\\{u0073}\\u{0080}-\\u{00FF}]+$/u;\n\nexport class MIMEType {\n  constructor(input) {\n    const { type, subtype, params } = parseMimeType(input);\n    this.type = type.trim().toLowerCase();\n    this.subtype = subtype.trimEnd().toLowerCase();\n    this.parameters = new Map(Object.entries(params));\n  }\n\n  /**\n   * @see https://mimesniff.spec.whatwg.org/#mime-type-essence\n   */\n  get essence() {\n    return `${this.type}/${this.subtype}`;\n  }\n\n  toString() {\n    return serialize(this);\n  }\n};\n\nexport function isValidMimeType(text){\n  try {\n    parse(text);\n  } catch (e) {\n    return false;\n  }\n  return true;\n}\n\n/**\n * https://mimesniff.spec.whatwg.org/#serialize-a-mime-type\n */\nfunction serialize(mimeType) {\n  const { parameters, essence } = mimeType;\n  if (!parameters.size) {\n    return essence;\n  }\n  let paramStr = \";\";\n  for (const [key, value] of parameters.entries()) {\n    paramStr += key;\n    if (value !== null) {\n      if (HTTPTokenCodePoints.test(value)) {\n        paramStr += `=${value}`;\n      } else {\n        paramStr += `=\"${value}\"`;\n      }\n    } else {\n      // null or empty string\n      paramStr += '=\"\"';\n    }\n    paramStr += \";\";\n  }\n  // remove final \";\"\n  return mimeType.essence + paramStr.slice(0, -1);\n}\n\n/**\n * Implementation of https://mimesniff.spec.whatwg.org/#parse-a-mime-type\n * parser state machines if as follows, params and param values are optional and can be null:\n *\n * \"type\"\n *    -> \"subtype\"\n *      -> \"param-start\" (ignores white space)\n *         -> \"param-name\"\n *            -> \"param-value\"\n *              -> \"collect-quoted-string\"\n *                -> \"ignore-input-until-next-param\"\n *\n *\n *\n * @param {String} input\n */\nexport function parseMimeType(input) {\n  input = input.trim();\n  if (!input) {\n    throw new TypeError(\"Invalid input.\");\n  }\n\n  let type = \"\";\n  let subtype = \"\";\n  let paramName = \"\";\n  let paramValue = null;\n  let params = new Map();\n  let parserMode = \"type\";\n  let inputArray = Array.from(input); // retain unicode chars\n  for (let position = 0; position < inputArray.length; position++) {\n    const char = inputArray[position];\n    switch (parserMode) {\n      case \"type\":\n        if (char === \"/\") {\n          parserMode = \"subtype\";\n          continue;\n        }\n        type += char;\n        break;\n      case \"subtype\":\n        if (char === \";\") {\n          parserMode = \"param-start\";\n          continue;\n        }\n        subtype += char;\n        break;\n      case \"param-start\":\n        // Skip HTTP white space\n        if (HTTPWhiteSpace.test(char) || char === \";\") {\n          continue;\n        }\n        paramName += char;\n        parserMode = \"param-name\";\n        break;\n      case \"param-name\":\n        if (char === \"=\" || char === \";\") {\n          if (char === \"=\") {\n            parserMode = \"param-value\";\n            paramValue = null;\n            continue;\n          }\n          params.set(paramName.toLowerCase(), null);\n          paramName = \"\";\n          continue;\n        }\n        paramName += char;\n        break;\n      case \"param-value\":\n        if (char == '\"') {\n          parserMode = \"collect-quoted-string\";\n          continue;\n        }\n        if (char === \";\") {\n          paramValue = paramValue.trimEnd();\n          parserMode = \"param-start\";\n          storeParam(params, paramName, paramValue);\n          paramName = \"\";\n          continue;\n        }\n        paramValue = typeof paramValue === \"string\" ? paramValue + char : char;\n        break;\n      case \"collect-quoted-string\":\n        if (char === '\"') {\n          storeParam(params, paramName, paramValue);\n          parserMode = \"ignore-input-until-next-param\";\n          paramName = \"\";\n          paramValue = null;\n          continue;\n        }\n        if (char === \"\\\\\") {\n          continue;\n        }\n        paramValue = typeof paramValue === \"string\" ? paramValue + char : char;\n        break;\n      case \"ignore-input-until-next-param\":\n        if (char !== \";\") {\n          continue;\n        }\n        parserMode = \"param-start\";\n        break;\n      default:\n        throw new Error(\n          `State machine error - unknown parser mode: ${parserMode} `\n        );\n    }\n  }\n  if (paramName) {\n    storeParam(params, paramName, paramValue);\n  }\n  if (type.trim() === \"\" || !HTTPTokenCodePoints.test(type)) {\n    throw new TypeError(\"Invalid type\");\n  }\n  if (subtype.trim() === \"\" || !HTTPTokenCodePoints.test(subtype)) {\n    throw new TypeError(\"Invalid subtype\");\n  }\n  return {\n    type,\n    subtype,\n    params: Object.fromEntries(params.entries()),\n  };\n}\n\nfunction storeParam(params, paramName, paramValue) {\n  if (\n    (paramName &&\n      paramName !== \"\" &&\n      !params.has(paramName) &&\n      HTTPQuotedString.test(paramValue)) ||\n    paramValue === null\n  ) {\n    params.set(paramName.toLowerCase(), paramValue);\n  }\n}\n","/**\n * marked v18.0.3 - a markdown parser\n * Copyright (c) 2018-2026, MarkedJS. (MIT License)\n * Copyright (c) 2011-2018, Christopher Jeffrey. (MIT License)\n * https://github.com/markedjs/marked\n */\n\n/**\n * DO NOT EDIT THIS FILE\n * The code in this file is generated from files in ./src/\n */\n\nfunction z(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}var T=z();function G(l){T=l}var _={exec:()=>null};function d(l,e=\"\"){let t=typeof l==\"string\"?l:l.source,n={replace:(s,r)=>{let i=typeof r==\"string\"?r:r.source;return i=i.replace(m.caret,\"$1\"),t=t.replace(s,i),n},getRegex:()=>new RegExp(t,e)};return n}var Re=((l=\"\")=>{try{return!!new RegExp(\"(?<=1)(?/,blockquoteSetextReplace:/\\n {0,3}((?:=+|-+) *)(?=\\n|$)/g,blockquoteSetextReplace2:/^ {0,3}>[ \\t]?/gm,listReplaceNesting:/^ {1,4}(?=( {4})*[^ ])/g,listIsTask:/^\\[[ xX]\\] +\\S/,listReplaceTask:/^\\[[ xX]\\] +/,listTaskCheckbox:/\\[[ xX]\\]/,anyLine:/\\n.*\\n/,hrefBrackets:/^<(.*)>$/,tableDelimiter:/[:|]/,tableAlignChars:/^\\||\\| *$/g,tableRowBlankLine:/\\n[ \\t]*$/,tableAlignRight:/^ *-+: *$/,tableAlignCenter:/^ *:-+: *$/,tableAlignLeft:/^ *:-+ *$/,startATag:/^/i,startPreScriptTag:/^<(pre|code|kbd|script)(\\s|>)/i,endPreScriptTag:/^<\\/(pre|code|kbd|script)(\\s|>)/i,startAngleBracket:/^$/,pedanticHrefTitle:/^([^'\"]*[^\\s])\\s+(['\"])(.*)\\2/,unicodeAlphaNumeric:/[\\p{L}\\p{N}]/u,escapeTest:/[&<>\"']/,escapeReplace:/[&<>\"']/g,escapeTestNoEncode:/[<>\"']|&(?!(#\\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\\w+);)/,escapeReplaceNoEncode:/[<>\"']|&(?!(#\\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\\w+);)/g,caret:/(^|[^\\[])\\^/g,percentDecode:/%25/g,findPipe:/\\|/g,splitPipe:/ \\|/,slashPipe:/\\\\\\|/g,carriageReturn:/\\r\\n|\\r/g,spaceLine:/^ +$/gm,notSpaceStart:/^\\S*/,endingNewline:/\\n$/,listItemRegex:l=>new RegExp(`^( {0,3}${l})((?:[\t ][^\\\\n]*)?(?:\\\\n|$))`),nextBulletRegex:l=>new RegExp(`^ {0,${Math.min(3,l-1)}}(?:[*+-]|\\\\d{1,9}[.)])((?:[ \t][^\\\\n]*)?(?:\\\\n|$))`),hrRegex:l=>new RegExp(`^ {0,${Math.min(3,l-1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\\\* *){3,})(?:\\\\n+|$)`),fencesBeginRegex:l=>new RegExp(`^ {0,${Math.min(3,l-1)}}(?:\\`\\`\\`|~~~)`),headingBeginRegex:l=>new RegExp(`^ {0,${Math.min(3,l-1)}}#`),htmlBeginRegex:l=>new RegExp(`^ {0,${Math.min(3,l-1)}}<(?:[a-z].*>|!--)`,\"i\"),blockquoteBeginRegex:l=>new RegExp(`^ {0,${Math.min(3,l-1)}}>`)},Te=/^(?:[ \\t]*(?:\\n|$))+/,Oe=/^((?: {4}| {0,3}\\t)[^\\n]+(?:\\n(?:[ \\t]*(?:\\n|$))*)?)+/,we=/^ {0,3}(`{3,}(?=[^`\\n]*(?:\\n|$))|~{3,})([^\\n]*)(?:\\n|$)(?:|([\\s\\S]*?)(?:\\n|$))(?: {0,3}\\1[~`]* *(?=\\n|$)|$)/,I=/^ {0,3}((?:-[\\t ]*){3,}|(?:_[ \\t]*){3,}|(?:\\*[ \\t]*){3,})(?:\\n+|$)/,ye=/^ {0,3}(#{1,6})(?=\\s|$)(.*)(?:\\n+|$)/,Q=/ {0,3}(?:[*+-]|\\d{1,9}[.)])/,ie=/^(?!bull |blockCode|fences|blockquote|heading|html|table)((?:.|\\n(?!\\s*?\\n|bull |blockCode|fences|blockquote|heading|html|table))+?)\\n {0,3}(=+|-+) *(?:\\n+|$)/,oe=d(ie).replace(/bull/g,Q).replace(/blockCode/g,/(?: {4}| {0,3}\\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\\n>]+>\\n/).replace(/\\|table/g,\"\").getRegex(),Pe=d(ie).replace(/bull/g,Q).replace(/blockCode/g,/(?: {4}| {0,3}\\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\\n>]+>\\n/).replace(/table/g,/ {0,3}\\|?(?:[:\\- ]*\\|)+[\\:\\- ]*\\n/).getRegex(),j=/^([^\\n]+(?:\\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\\n)[^\\n]+)*)/,Se=/^[^\\n]+/,F=/(?!\\s*\\])(?:\\\\[\\s\\S]|[^\\[\\]\\\\])+/,$e=d(/^ {0,3}\\[(label)\\]: *(?:\\n[ \\t]*)?([^<\\s][^\\s]*|<.*?>)(?:(?: +(?:\\n[ \\t]*)?| *\\n[ \\t]*)(title))? *(?:\\n+|$)/).replace(\"label\",F).replace(\"title\",/(?:\"(?:\\\\\"?|[^\"\\\\])*\"|'[^'\\n]*(?:\\n[^'\\n]+)*\\n?'|\\([^()]*\\))/).getRegex(),Le=d(/^(bull)([ \\t][^\\n]+?)?(?:\\n|$)/).replace(/bull/g,Q).getRegex(),v=\"address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul\",U=/|$))/,_e=d(\"^ {0,3}(?:<(script|pre|style|textarea)[\\\\s>][\\\\s\\\\S]*?(?:[^\\\\n]*\\\\n+|$)|comment[^\\\\n]*(\\\\n+|$)|<\\\\?[\\\\s\\\\S]*?(?:\\\\?>\\\\n*|$)|\\\\n*|$)|\\\\n*|$)|)[\\\\s\\\\S]*?(?:(?:\\\\n[ \t]*)+\\\\n|$)|<(?!script|pre|style|textarea)([a-z][\\\\w-]*)(?:attribute)*? */?>(?=[ \\\\t]*(?:\\\\n|$))[\\\\s\\\\S]*?(?:(?:\\\\n[ \t]*)+\\\\n|$)|(?=[ \\\\t]*(?:\\\\n|$))[\\\\s\\\\S]*?(?:(?:\\\\n[ \t]*)+\\\\n|$))\",\"i\").replace(\"comment\",U).replace(\"tag\",v).replace(\"attribute\",/ +[a-zA-Z:_][\\w.:-]*(?: *= *\"[^\"\\n]*\"| *= *'[^'\\n]*'| *= *[^\\s\"'=<>`]+)?/).getRegex(),ae=d(j).replace(\"hr\",I).replace(\"heading\",\" {0,3}#{1,6}(?:\\\\s|$)\").replace(\"|lheading\",\"\").replace(\"|table\",\"\").replace(\"blockquote\",\" {0,3}>\").replace(\"fences\",\" {0,3}(?:`{3,}(?=[^`\\\\n]*\\\\n)|~{3,})[^\\\\n]*\\\\n\").replace(\"list\",\" {0,3}(?:[*+-]|1[.)])[ \\\\t]\").replace(\"html\",\")|<(?:script|pre|style|textarea|!--)\").replace(\"tag\",v).getRegex(),Me=d(/^( {0,3}> ?(paragraph|[^\\n]*)(?:\\n|$))+/).replace(\"paragraph\",ae).getRegex(),K={blockquote:Me,code:Oe,def:$e,fences:we,heading:ye,hr:I,html:_e,lheading:oe,list:Le,newline:Te,paragraph:ae,table:_,text:Se},re=d(\"^ *([^\\\\n ].*)\\\\n {0,3}((?:\\\\| *)?:?-+:? *(?:\\\\| *:?-+:? *)*(?:\\\\| *)?)(?:\\\\n((?:(?! *\\\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\\\n|$))*)\\\\n*|$)\").replace(\"hr\",I).replace(\"heading\",\" {0,3}#{1,6}(?:\\\\s|$)\").replace(\"blockquote\",\" {0,3}>\").replace(\"code\",\"(?: {4}| {0,3}\t)[^\\\\n]\").replace(\"fences\",\" {0,3}(?:`{3,}(?=[^`\\\\n]*\\\\n)|~{3,})[^\\\\n]*\\\\n\").replace(\"list\",\" {0,3}(?:[*+-]|1[.)])[ \\\\t]\").replace(\"html\",\")|<(?:script|pre|style|textarea|!--)\").replace(\"tag\",v).getRegex(),ze={...K,lheading:Pe,table:re,paragraph:d(j).replace(\"hr\",I).replace(\"heading\",\" {0,3}#{1,6}(?:\\\\s|$)\").replace(\"|lheading\",\"\").replace(\"table\",re).replace(\"blockquote\",\" {0,3}>\").replace(\"fences\",\" {0,3}(?:`{3,}(?=[^`\\\\n]*\\\\n)|~{3,})[^\\\\n]*\\\\n\").replace(\"list\",\" {0,3}(?:[*+-]|1[.)])[ \\\\t]\").replace(\"html\",\")|<(?:script|pre|style|textarea|!--)\").replace(\"tag\",v).getRegex()},Ee={...K,html:d(`^ *(?:comment *(?:\\\\n|\\\\s*$)|<(tag)[\\\\s\\\\S]+? *(?:\\\\n{2,}|\\\\s*$)|\\\\s]*)*?/?> *(?:\\\\n{2,}|\\\\s*$))`).replace(\"comment\",U).replace(/tag/g,\"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\\\b)\\\\w+(?!:|[^\\\\w\\\\s@]*@)\\\\b\").getRegex(),def:/^ *\\[([^\\]]+)\\]: *]+)>?(?: +([\"(][^\\n]+[\")]))? *(?:\\n+|$)/,heading:/^(#{1,6})(.*)(?:\\n+|$)/,fences:_,lheading:/^(.+?)\\n {0,3}(=+|-+) *(?:\\n+|$)/,paragraph:d(j).replace(\"hr\",I).replace(\"heading\",` *#{1,6} *[^\n]`).replace(\"lheading\",oe).replace(\"|table\",\"\").replace(\"blockquote\",\" {0,3}>\").replace(\"|fences\",\"\").replace(\"|list\",\"\").replace(\"|html\",\"\").replace(\"|tag\",\"\").getRegex()},Ae=/^\\\\([!\"#$%&'()*+,\\-./:;<=>?@\\[\\]\\\\^_`{|}~])/,Ce=/^(`+)([^`]|[^`][\\s\\S]*?[^`])\\1(?!`)/,le=/^( {2,}|\\\\)\\n(?!\\s*$)/,Ie=/^(`+|[^`])(?:(?= {2,}\\n)|[\\s\\S]*?(?:(?=[\\\\`+)[^`]+\\k(?!`))*?\\]\\((?:\\\\[\\s\\S]|[^\\\\\\(\\)]|\\((?:\\\\[\\s\\S]|[^\\\\\\(\\)])*\\))*\\)/).replace(\"precode-\",Re?\"(?`+)[^`]+\\k(?!`)/).replace(\"html\",/<(?! )[^<>]*?>/).getRegex(),pe=/^(?:\\*+(?:((?!\\*)punct)|([^\\s*]))?)|^_+(?:((?!_)punct)|([^\\s_]))?/,He=d(pe,\"u\").replace(/punct/g,E).getRegex(),Ze=d(pe,\"u\").replace(/punct/g,ue).getRegex(),ce=\"^[^_*]*?__[^_*]*?\\\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\\\*)punct(\\\\*+)(?=[\\\\s]|$)|notPunctSpace(\\\\*+)(?!\\\\*)(?=punctSpace|$)|(?!\\\\*)punctSpace(\\\\*+)(?=notPunctSpace)|[\\\\s](\\\\*+)(?!\\\\*)(?=punct)|(?!\\\\*)punct(\\\\*+)(?!\\\\*)(?=punct)|notPunctSpace(\\\\*+)(?=notPunctSpace)\",Ge=d(ce,\"gu\").replace(/notPunctSpace/g,W).replace(/punctSpace/g,H).replace(/punct/g,E).getRegex(),Ne=d(ce,\"gu\").replace(/notPunctSpace/g,qe).replace(/punctSpace/g,De).replace(/punct/g,ue).getRegex(),Qe=d(\"^[^_*]*?\\\\*\\\\*[^_*]*?_[^_*]*?(?=\\\\*\\\\*)|[^_]+(?=[^_])|(?!_)punct(_+)(?=[\\\\s]|$)|notPunctSpace(_+)(?!_)(?=punctSpace|$)|(?!_)punctSpace(_+)(?=notPunctSpace)|[\\\\s](_+)(?!_)(?=punct)|(?!_)punct(_+)(?!_)(?=punct)\",\"gu\").replace(/notPunctSpace/g,W).replace(/punctSpace/g,H).replace(/punct/g,E).getRegex(),je=d(/^~~?(?:((?!~)punct)|[^\\s~])/,\"u\").replace(/punct/g,E).getRegex(),Fe=\"^[^~]+(?=[^~])|(?!~)punct(~~?)(?=[\\\\s]|$)|notPunctSpace(~~?)(?!~)(?=punctSpace|$)|(?!~)punctSpace(~~?)(?=notPunctSpace)|[\\\\s](~~?)(?!~)(?=punct)|(?!~)punct(~~?)(?!~)(?=punct)|notPunctSpace(~~?)(?=notPunctSpace)\",Ue=d(Fe,\"gu\").replace(/notPunctSpace/g,W).replace(/punctSpace/g,H).replace(/punct/g,E).getRegex(),Ke=d(/\\\\(punct)/,\"gu\").replace(/punct/g,E).getRegex(),We=d(/^<(scheme:[^\\s\\x00-\\x1f<>]*|email)>/).replace(\"scheme\",/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace(\"email\",/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex(),Xe=d(U).replace(\"(?:-->|$)\",\"-->\").getRegex(),Je=d(\"^comment|^|^<[a-zA-Z][\\\\w-]*(?:attribute)*?\\\\s*/?>|^<\\\\?[\\\\s\\\\S]*?\\\\?>|^|^\").replace(\"comment\",Xe).replace(\"attribute\",/\\s+[a-zA-Z:_][\\w.:-]*(?:\\s*=\\s*\"[^\"]*\"|\\s*=\\s*'[^']*'|\\s*=\\s*[^\\s\"'=<>`]+)?/).getRegex(),q=/(?:\\[(?:\\\\[\\s\\S]|[^\\[\\]\\\\])*\\]|\\\\[\\s\\S]|`+(?!`)[^`]*?`+(?!`)|``+(?=\\])|[^\\[\\]\\\\`])*?/,Ve=d(/^!?\\[(label)\\]\\(\\s*(href)(?:(?:[ \\t]+(?:\\n[ \\t]*)?|\\n[ \\t]*)(title))?\\s*\\)/).replace(\"label\",q).replace(\"href\",/<(?:\\\\.|[^\\n<>\\\\])+>|[^ \\t\\n\\x00-\\x1f]*/).replace(\"title\",/\"(?:\\\\\"?|[^\"\\\\])*\"|'(?:\\\\'?|[^'\\\\])*'|\\((?:\\\\\\)?|[^)\\\\])*\\)/).getRegex(),he=d(/^!?\\[(label)\\]\\[(ref)\\]/).replace(\"label\",q).replace(\"ref\",F).getRegex(),ke=d(/^!?\\[(ref)\\](?:\\[\\])?/).replace(\"ref\",F).getRegex(),Ye=d(\"reflink|nolink(?!\\\\()\",\"g\").replace(\"reflink\",he).replace(\"nolink\",ke).getRegex(),se=/[hH][tT][tT][pP][sS]?|[fF][tT][pP]/,X={_backpedal:_,anyPunctuation:Ke,autolink:We,blockSkip:ve,br:le,code:Ce,del:_,delLDelim:_,delRDelim:_,emStrongLDelim:He,emStrongRDelimAst:Ge,emStrongRDelimUnd:Qe,escape:Ae,link:Ve,nolink:ke,punctuation:Be,reflink:he,reflinkSearch:Ye,tag:Je,text:Ie,url:_},et={...X,link:d(/^!?\\[(label)\\]\\((.*?)\\)/).replace(\"label\",q).getRegex(),reflink:d(/^!?\\[(label)\\]\\s*\\[([^\\]]*)\\]/).replace(\"label\",q).getRegex()},N={...X,emStrongRDelimAst:Ne,emStrongLDelim:Ze,delLDelim:je,delRDelim:Ue,url:d(/^((?:protocol):\\/\\/|www\\.)(?:[a-zA-Z0-9\\-]+\\.?)+[^\\s<]*|^email/).replace(\"protocol\",se).replace(\"email\",/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/).getRegex(),_backpedal:/(?:[^?!.,:;*_'\"~()&]+|\\([^)]*\\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'\"~)]+(?!$))+/,del:/^(~~?)(?=[^\\s~])((?:\\\\[\\s\\S]|[^\\\\])*?(?:\\\\[\\s\\S]|[^\\s~\\\\]))\\1(?=[^~]|$)/,text:d(/^([`~]+|[^`~])(?:(?= {2,}\\n)|(?=[a-zA-Z0-9.!#$%&'*+\\/=?_`{\\|}~-]+@)|[\\s\\S]*?(?:(?=[\\\\\":\">\",'\"':\""\",\"'\":\"'\"},de=l=>nt[l];function O(l,e){if(e){if(m.escapeTest.test(l))return l.replace(m.escapeReplace,de)}else if(m.escapeTestNoEncode.test(l))return l.replace(m.escapeReplaceNoEncode,de);return l}function J(l){try{l=encodeURI(l).replace(m.percentDecode,\"%\")}catch{return null}return l}function V(l,e){let t=l.replace(m.findPipe,(r,i,o)=>{let u=!1,a=i;for(;--a>=0&&o[a]===\"\\\\\";)u=!u;return u?\"|\":\" |\"}),n=t.split(m.splitPipe),s=0;if(n[0].trim()||n.shift(),n.length>0&&!n.at(-1)?.trim()&&n.pop(),e)if(n.length>e)n.splice(e);else for(;n.length=0&&m.blankLine.test(e[t]);)t--;return e.length-t<=2?l:e.slice(0,t+1).join(`\n`)}function ge(l,e){if(l.indexOf(e[1])===-1)return-1;let t=0;for(let n=0;n0?-2:-1}function fe(l,e=0){let t=e,n=\"\";for(let s of l)if(s===\"\t\"){let r=4-t%4;n+=\" \".repeat(r),t+=r}else n+=s,t++;return n}function me(l,e,t,n,s){let r=e.href,i=e.title||null,o=l[1].replace(s.other.outputLinkReplace,\"$1\");n.state.inLink=!0;let u={type:l[0].charAt(0)===\"!\"?\"image\":\"link\",raw:t,href:r,title:i,text:o,tokens:n.inlineTokens(o)};return n.state.inLink=!1,u}function rt(l,e,t){let n=l.match(t.other.indentCodeCompensation);if(n===null)return e;let s=n[1];return e.split(`\n`).map(r=>{let i=r.match(t.other.beginningSpace);if(i===null)return r;let[o]=i;return o.length>=s.length?r.slice(s.length):r}).join(`\n`)}var w=class{options;rules;lexer;constructor(e){this.options=e||T}space(e){let t=this.rules.block.newline.exec(e);if(t&&t[0].length>0)return{type:\"space\",raw:t[0]}}code(e){let t=this.rules.block.code.exec(e);if(t){let n=this.options.pedantic?t[0]:Y(t[0]),s=n.replace(this.rules.other.codeRemoveIndent,\"\");return{type:\"code\",raw:n,codeBlockStyle:\"indented\",text:s}}}fences(e){let t=this.rules.block.fences.exec(e);if(t){let n=t[0],s=rt(n,t[3]||\"\",this.rules);return{type:\"code\",raw:n,lang:t[2]?t[2].trim().replace(this.rules.inline.anyPunctuation,\"$1\"):t[2],text:s}}}heading(e){let t=this.rules.block.heading.exec(e);if(t){let n=t[2].trim();if(this.rules.other.endingHash.test(n)){let s=$(n,\"#\");(this.options.pedantic||!s||this.rules.other.endingSpaceChar.test(s))&&(n=s.trim())}return{type:\"heading\",raw:$(t[0],`\n`),depth:t[1].length,text:n,tokens:this.lexer.inline(n)}}}hr(e){let t=this.rules.block.hr.exec(e);if(t)return{type:\"hr\",raw:$(t[0],`\n`)}}blockquote(e){let t=this.rules.block.blockquote.exec(e);if(t){let n=$(t[0],`\n`).split(`\n`),s=\"\",r=\"\",i=[];for(;n.length>0;){let o=!1,u=[],a;for(a=0;a1,r={type:\"list\",raw:\"\",ordered:s,start:s?+n.slice(0,-1):\"\",loose:!1,items:[]};n=s?`\\\\d{1,9}\\\\${n.slice(-1)}`:`\\\\${n}`,this.options.pedantic&&(n=s?n:\"[*+-]\");let i=this.rules.other.listItemRegex(n),o=!1;for(;e;){let a=!1,c=\"\",p=\"\";if(!(t=i.exec(e))||this.rules.block.hr.test(e))break;c=t[0],e=e.substring(c.length);let k=fe(t[2].split(`\n`,1)[0],t[1].length),h=e.split(`\n`,1)[0],R=!k.trim(),f=0;if(this.options.pedantic?(f=2,p=k.trimStart()):R?f=t[1].length+1:(f=k.search(this.rules.other.nonSpaceChar),f=f>4?1:f,p=k.slice(f),f+=t[1].length),R&&this.rules.other.blankLine.test(h)&&(c+=h+`\n`,e=e.substring(h.length+1),a=!0),!a){let S=this.rules.other.nextBulletRegex(f),ee=this.rules.other.hrRegex(f),te=this.rules.other.fencesBeginRegex(f),ne=this.rules.other.headingBeginRegex(f),xe=this.rules.other.htmlBeginRegex(f),be=this.rules.other.blockquoteBeginRegex(f);for(;e;){let Z=e.split(`\n`,1)[0],C;if(h=Z,this.options.pedantic?(h=h.replace(this.rules.other.listReplaceNesting,\"  \"),C=h):C=h.replace(this.rules.other.tabCharGlobal,\"    \"),te.test(h)||ne.test(h)||xe.test(h)||be.test(h)||S.test(h)||ee.test(h))break;if(C.search(this.rules.other.nonSpaceChar)>=f||!h.trim())p+=`\n`+C.slice(f);else{if(R||k.replace(this.rules.other.tabCharGlobal,\"    \").search(this.rules.other.nonSpaceChar)>=4||te.test(k)||ne.test(k)||ee.test(k))break;p+=`\n`+h}R=!h.trim(),c+=Z+`\n`,e=e.substring(Z.length+1),k=C.slice(f)}}r.loose||(o?r.loose=!0:this.rules.other.doubleBlankLine.test(c)&&(o=!0)),r.items.push({type:\"list_item\",raw:c,task:!!this.options.gfm&&this.rules.other.listIsTask.test(p),loose:!1,text:p,tokens:[]}),r.raw+=c}let u=r.items.at(-1);if(u)u.raw=u.raw.trimEnd(),u.text=u.text.trimEnd();else return;r.raw=r.raw.trimEnd();for(let a of r.items){this.lexer.state.top=!1,a.tokens=this.lexer.blockTokens(a.text,[]);let c=a.tokens[0];if(a.task&&(c?.type===\"text\"||c?.type===\"paragraph\")){a.text=a.text.replace(this.rules.other.listReplaceTask,\"\"),c.raw=c.raw.replace(this.rules.other.listReplaceTask,\"\"),c.text=c.text.replace(this.rules.other.listReplaceTask,\"\");for(let k=this.lexer.inlineQueue.length-1;k>=0;k--)if(this.rules.other.listIsTask.test(this.lexer.inlineQueue[k].src)){this.lexer.inlineQueue[k].src=this.lexer.inlineQueue[k].src.replace(this.rules.other.listReplaceTask,\"\");break}let p=this.rules.other.listTaskCheckbox.exec(a.raw);if(p){let k={type:\"checkbox\",raw:p[0]+\" \",checked:p[0]!==\"[ ]\"};a.checked=k.checked,r.loose?a.tokens[0]&&[\"paragraph\",\"text\"].includes(a.tokens[0].type)&&\"tokens\"in a.tokens[0]&&a.tokens[0].tokens?(a.tokens[0].raw=k.raw+a.tokens[0].raw,a.tokens[0].text=k.raw+a.tokens[0].text,a.tokens[0].tokens.unshift(k)):a.tokens.unshift({type:\"paragraph\",raw:k.raw,text:k.raw,tokens:[k]}):a.tokens.unshift(k)}}else a.task&&(a.task=!1);if(!r.loose){let p=a.tokens.filter(h=>h.type===\"space\"),k=p.length>0&&p.some(h=>this.rules.other.anyLine.test(h.raw));r.loose=k}}if(r.loose)for(let a of r.items){a.loose=!0;for(let c of a.tokens)c.type===\"text\"&&(c.type=\"paragraph\")}return r}}html(e){let t=this.rules.block.html.exec(e);if(t){let n=Y(t[0]);return{type:\"html\",block:!0,raw:n,pre:t[1]===\"pre\"||t[1]===\"script\"||t[1]===\"style\",text:n}}}def(e){let t=this.rules.block.def.exec(e);if(t){let n=t[1].toLowerCase().replace(this.rules.other.multipleSpaceGlobal,\" \"),s=t[2]?t[2].replace(this.rules.other.hrefBrackets,\"$1\").replace(this.rules.inline.anyPunctuation,\"$1\"):\"\",r=t[3]?t[3].substring(1,t[3].length-1).replace(this.rules.inline.anyPunctuation,\"$1\"):t[3];return{type:\"def\",tag:n,raw:$(t[0],`\n`),href:s,title:r}}}table(e){let t=this.rules.block.table.exec(e);if(!t||!this.rules.other.tableDelimiter.test(t[2]))return;let n=V(t[1]),s=t[2].replace(this.rules.other.tableAlignChars,\"\").split(\"|\"),r=t[3]?.trim()?t[3].replace(this.rules.other.tableRowBlankLine,\"\").split(`\n`):[],i={type:\"table\",raw:$(t[0],`\n`),header:[],align:[],rows:[]};if(n.length===s.length){for(let o of s)this.rules.other.tableAlignRight.test(o)?i.align.push(\"right\"):this.rules.other.tableAlignCenter.test(o)?i.align.push(\"center\"):this.rules.other.tableAlignLeft.test(o)?i.align.push(\"left\"):i.align.push(null);for(let o=0;o({text:u,tokens:this.lexer.inline(u),header:!1,align:i.align[a]})));return i}}lheading(e){let t=this.rules.block.lheading.exec(e);if(t){let n=t[1].trim();return{type:\"heading\",raw:$(t[0],`\n`),depth:t[2].charAt(0)===\"=\"?1:2,text:n,tokens:this.lexer.inline(n)}}}paragraph(e){let t=this.rules.block.paragraph.exec(e);if(t){let n=t[1].charAt(t[1].length-1)===`\n`?t[1].slice(0,-1):t[1];return{type:\"paragraph\",raw:t[0],text:n,tokens:this.lexer.inline(n)}}}text(e){let t=this.rules.block.text.exec(e);if(t)return{type:\"text\",raw:t[0],text:t[0],tokens:this.lexer.inline(t[0])}}escape(e){let t=this.rules.inline.escape.exec(e);if(t)return{type:\"escape\",raw:t[0],text:t[1]}}tag(e){let t=this.rules.inline.tag.exec(e);if(t)return!this.lexer.state.inLink&&this.rules.other.startATag.test(t[0])?this.lexer.state.inLink=!0:this.lexer.state.inLink&&this.rules.other.endATag.test(t[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&this.rules.other.startPreScriptTag.test(t[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&this.rules.other.endPreScriptTag.test(t[0])&&(this.lexer.state.inRawBlock=!1),{type:\"html\",raw:t[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,block:!1,text:t[0]}}link(e){let t=this.rules.inline.link.exec(e);if(t){let n=t[2].trim();if(!this.options.pedantic&&this.rules.other.startAngleBracket.test(n)){if(!this.rules.other.endAngleBracket.test(n))return;let i=$(n.slice(0,-1),\"\\\\\");if((n.length-i.length)%2===0)return}else{let i=ge(t[2],\"()\");if(i===-2)return;if(i>-1){let u=(t[0].indexOf(\"!\")===0?5:4)+t[1].length+i;t[2]=t[2].substring(0,i),t[0]=t[0].substring(0,u).trim(),t[3]=\"\"}}let s=t[2],r=\"\";if(this.options.pedantic){let i=this.rules.other.pedanticHrefTitle.exec(s);i&&(s=i[1],r=i[3])}else r=t[3]?t[3].slice(1,-1):\"\";return s=s.trim(),this.rules.other.startAngleBracket.test(s)&&(this.options.pedantic&&!this.rules.other.endAngleBracket.test(n)?s=s.slice(1):s=s.slice(1,-1)),me(t,{href:s&&s.replace(this.rules.inline.anyPunctuation,\"$1\"),title:r&&r.replace(this.rules.inline.anyPunctuation,\"$1\")},t[0],this.lexer,this.rules)}}reflink(e,t){let n;if((n=this.rules.inline.reflink.exec(e))||(n=this.rules.inline.nolink.exec(e))){let s=(n[2]||n[1]).replace(this.rules.other.multipleSpaceGlobal,\" \"),r=t[s.toLowerCase()];if(!r){let i=n[0].charAt(0);return{type:\"text\",raw:i,text:i}}return me(n,r,n[0],this.lexer,this.rules)}}emStrong(e,t,n=\"\"){let s=this.rules.inline.emStrongLDelim.exec(e);if(!s||!s[1]&&!s[2]&&!s[3]&&!s[4]||s[4]&&n.match(this.rules.other.unicodeAlphaNumeric))return;if(!(s[1]||s[3]||\"\")||!n||this.rules.inline.punctuation.exec(n)){let i=[...s[0]].length-1,o,u,a=i,c=0,p=s[0][0]===\"*\"?this.rules.inline.emStrongRDelimAst:this.rules.inline.emStrongRDelimUnd;for(p.lastIndex=0,t=t.slice(-1*e.length+i);(s=p.exec(t))!==null;){if(o=s[1]||s[2]||s[3]||s[4]||s[5]||s[6],!o)continue;if(u=[...o].length,s[3]||s[4]){a+=u;continue}else if((s[5]||s[6])&&i%3&&!((i+u)%3)){c+=u;continue}if(a-=u,a>0)continue;u=Math.min(u,u+a+c);let k=[...s[0]][0].length,h=e.slice(0,i+s.index+k+u);if(Math.min(i,u)%2){let f=h.slice(1,-1);return{type:\"em\",raw:h,text:f,tokens:this.lexer.inlineTokens(f)}}let R=h.slice(2,-2);return{type:\"strong\",raw:h,text:R,tokens:this.lexer.inlineTokens(R)}}}}codespan(e){let t=this.rules.inline.code.exec(e);if(t){let n=t[2].replace(this.rules.other.newLineCharGlobal,\" \"),s=this.rules.other.nonSpaceChar.test(n),r=this.rules.other.startingSpaceChar.test(n)&&this.rules.other.endingSpaceChar.test(n);return s&&r&&(n=n.substring(1,n.length-1)),{type:\"codespan\",raw:t[0],text:n}}}br(e){let t=this.rules.inline.br.exec(e);if(t)return{type:\"br\",raw:t[0]}}del(e,t,n=\"\"){let s=this.rules.inline.delLDelim.exec(e);if(!s)return;if(!(s[1]||\"\")||!n||this.rules.inline.punctuation.exec(n)){let i=[...s[0]].length-1,o,u,a=i,c=this.rules.inline.delRDelim;for(c.lastIndex=0,t=t.slice(-1*e.length+i);(s=c.exec(t))!==null;){if(o=s[1]||s[2]||s[3]||s[4]||s[5]||s[6],!o||(u=[...o].length,u!==i))continue;if(s[3]||s[4]){a+=u;continue}if(a-=u,a>0)continue;u=Math.min(u,u+a);let p=[...s[0]][0].length,k=e.slice(0,i+s.index+p+u),h=k.slice(i,-i);return{type:\"del\",raw:k,text:h,tokens:this.lexer.inlineTokens(h)}}}}autolink(e){let t=this.rules.inline.autolink.exec(e);if(t){let n,s;return t[2]===\"@\"?(n=t[1],s=\"mailto:\"+n):(n=t[1],s=n),{type:\"link\",raw:t[0],text:n,href:s,tokens:[{type:\"text\",raw:n,text:n}]}}}url(e){let t;if(t=this.rules.inline.url.exec(e)){let n,s;if(t[2]===\"@\")n=t[0],s=\"mailto:\"+n;else{let r;do r=t[0],t[0]=this.rules.inline._backpedal.exec(t[0])?.[0]??\"\";while(r!==t[0]);n=t[0],t[1]===\"www.\"?s=\"http://\"+t[0]:s=t[0]}return{type:\"link\",raw:t[0],text:n,href:s,tokens:[{type:\"text\",raw:n,text:n}]}}}inlineText(e){let t=this.rules.inline.text.exec(e);if(t){let n=this.lexer.state.inRawBlock;return{type:\"text\",raw:t[0],text:t[0],escaped:n}}}};var x=class l{tokens;options;state;inlineQueue;tokenizer;constructor(e){this.tokens=[],this.tokens.links=Object.create(null),this.options=e||T,this.options.tokenizer=this.options.tokenizer||new w,this.tokenizer=this.options.tokenizer,this.tokenizer.options=this.options,this.tokenizer.lexer=this,this.inlineQueue=[],this.state={inLink:!1,inRawBlock:!1,top:!0};let t={other:m,block:B.normal,inline:A.normal};this.options.pedantic?(t.block=B.pedantic,t.inline=A.pedantic):this.options.gfm&&(t.block=B.gfm,this.options.breaks?t.inline=A.breaks:t.inline=A.gfm),this.tokenizer.rules=t}static get rules(){return{block:B,inline:A}}static lex(e,t){return new l(t).lex(e)}static lexInline(e,t){return new l(t).inlineTokens(e)}lex(e){e=e.replace(m.carriageReturn,`\n`),this.blockTokens(e,this.tokens);for(let t=0;t(r=o.call({lexer:this},e,t))?(e=e.substring(r.raw.length),t.push(r),!0):!1))continue;if(r=this.tokenizer.space(e)){e=e.substring(r.raw.length);let o=t.at(-1);r.raw.length===1&&o!==void 0?o.raw+=`\n`:t.push(r);continue}if(r=this.tokenizer.code(e)){e=e.substring(r.raw.length);let o=t.at(-1);o?.type===\"paragraph\"||o?.type===\"text\"?(o.raw+=(o.raw.endsWith(`\n`)?\"\":`\n`)+r.raw,o.text+=`\n`+r.text,this.inlineQueue.at(-1).src=o.text):t.push(r);continue}if(r=this.tokenizer.fences(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.heading(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.hr(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.blockquote(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.list(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.html(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.def(e)){e=e.substring(r.raw.length);let o=t.at(-1);o?.type===\"paragraph\"||o?.type===\"text\"?(o.raw+=(o.raw.endsWith(`\n`)?\"\":`\n`)+r.raw,o.text+=`\n`+r.raw,this.inlineQueue.at(-1).src=o.text):this.tokens.links[r.tag]||(this.tokens.links[r.tag]={href:r.href,title:r.title},t.push(r));continue}if(r=this.tokenizer.table(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.lheading(e)){e=e.substring(r.raw.length),t.push(r);continue}let i=e;if(this.options.extensions?.startBlock){let o=1/0,u=e.slice(1),a;this.options.extensions.startBlock.forEach(c=>{a=c.call({lexer:this},u),typeof a==\"number\"&&a>=0&&(o=Math.min(o,a))}),o<1/0&&o>=0&&(i=e.substring(0,o+1))}if(this.state.top&&(r=this.tokenizer.paragraph(i))){let o=t.at(-1);n&&o?.type===\"paragraph\"?(o.raw+=(o.raw.endsWith(`\n`)?\"\":`\n`)+r.raw,o.text+=`\n`+r.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=o.text):t.push(r),n=i.length!==e.length,e=e.substring(r.raw.length);continue}if(r=this.tokenizer.text(e)){e=e.substring(r.raw.length);let o=t.at(-1);o?.type===\"text\"?(o.raw+=(o.raw.endsWith(`\n`)?\"\":`\n`)+r.raw,o.text+=`\n`+r.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=o.text):t.push(r);continue}if(e){this.infiniteLoopError(e.charCodeAt(0));break}}return this.state.top=!0,t}inline(e,t=[]){return this.inlineQueue.push({src:e,tokens:t}),t}inlineTokens(e,t=[]){this.tokenizer.lexer=this;let n=e,s=null;if(this.tokens.links){let a=Object.keys(this.tokens.links);if(a.length>0)for(;(s=this.tokenizer.rules.inline.reflinkSearch.exec(n))!==null;)a.includes(s[0].slice(s[0].lastIndexOf(\"[\")+1,-1))&&(n=n.slice(0,s.index)+\"[\"+\"a\".repeat(s[0].length-2)+\"]\"+n.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex))}for(;(s=this.tokenizer.rules.inline.anyPunctuation.exec(n))!==null;)n=n.slice(0,s.index)+\"++\"+n.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);let r;for(;(s=this.tokenizer.rules.inline.blockSkip.exec(n))!==null;)r=s[2]?s[2].length:0,n=n.slice(0,s.index+r)+\"[\"+\"a\".repeat(s[0].length-r-2)+\"]\"+n.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);n=this.options.hooks?.emStrongMask?.call({lexer:this},n)??n;let i=!1,o=\"\",u=1/0;for(;e;){if(e.length(a=p.call({lexer:this},e,t))?(e=e.substring(a.raw.length),t.push(a),!0):!1))continue;if(a=this.tokenizer.escape(e)){e=e.substring(a.raw.length),t.push(a);continue}if(a=this.tokenizer.tag(e)){e=e.substring(a.raw.length),t.push(a);continue}if(a=this.tokenizer.link(e)){e=e.substring(a.raw.length),t.push(a);continue}if(a=this.tokenizer.reflink(e,this.tokens.links)){e=e.substring(a.raw.length);let p=t.at(-1);a.type===\"text\"&&p?.type===\"text\"?(p.raw+=a.raw,p.text+=a.text):t.push(a);continue}if(a=this.tokenizer.emStrong(e,n,o)){e=e.substring(a.raw.length),t.push(a);continue}if(a=this.tokenizer.codespan(e)){e=e.substring(a.raw.length),t.push(a);continue}if(a=this.tokenizer.br(e)){e=e.substring(a.raw.length),t.push(a);continue}if(a=this.tokenizer.del(e,n,o)){e=e.substring(a.raw.length),t.push(a);continue}if(a=this.tokenizer.autolink(e)){e=e.substring(a.raw.length),t.push(a);continue}if(!this.state.inLink&&(a=this.tokenizer.url(e))){e=e.substring(a.raw.length),t.push(a);continue}let c=e;if(this.options.extensions?.startInline){let p=1/0,k=e.slice(1),h;this.options.extensions.startInline.forEach(R=>{h=R.call({lexer:this},k),typeof h==\"number\"&&h>=0&&(p=Math.min(p,h))}),p<1/0&&p>=0&&(c=e.substring(0,p+1))}if(a=this.tokenizer.inlineText(c)){e=e.substring(a.raw.length),a.raw.slice(-1)!==\"_\"&&(o=a.raw.slice(-1)),i=!0;let p=t.at(-1);p?.type===\"text\"?(p.raw+=a.raw,p.text+=a.text):t.push(a);continue}if(e){this.infiniteLoopError(e.charCodeAt(0));break}}return t}infiniteLoopError(e){let t=\"Infinite loop on byte: \"+e;if(this.options.silent)console.error(t);else throw new Error(t)}};var y=class{options;parser;constructor(e){this.options=e||T}space(e){return\"\"}code({text:e,lang:t,escaped:n}){let s=(t||\"\").match(m.notSpaceStart)?.[0],r=e.replace(m.endingNewline,\"\")+`\n`;return s?'
'+(n?r:O(r,!0))+`
\n`:\"
\"+(n?r:O(r,!0))+`
\n`}blockquote({tokens:e}){return`
\n${this.parser.parse(e)}
\n`}html({text:e}){return e}def(e){return\"\"}heading({tokens:e,depth:t}){return`${this.parser.parseInline(e)}\n`}hr(e){return`
\n`}list(e){let t=e.ordered,n=e.start,s=\"\";for(let o=0;o\n`+s+\"\n`}listitem(e){return`
  • ${this.parser.parse(e.tokens)}
  • \n`}checkbox({checked:e}){return\" '}paragraph({tokens:e}){return`

    ${this.parser.parseInline(e)}

    \n`}table(e){let t=\"\",n=\"\";for(let r=0;r${s}`),`\n\n`+t+`\n`+s+`
    \n`}tablerow({text:e}){return`\n${e}\n`}tablecell(e){let t=this.parser.parseInline(e.tokens),n=e.header?\"th\":\"td\";return(e.align?`<${n} align=\"${e.align}\">`:`<${n}>`)+t+`\n`}strong({tokens:e}){return`${this.parser.parseInline(e)}`}em({tokens:e}){return`${this.parser.parseInline(e)}`}codespan({text:e}){return`${O(e,!0)}`}br(e){return\"
    \"}del({tokens:e}){return`${this.parser.parseInline(e)}`}link({href:e,title:t,tokens:n}){let s=this.parser.parseInline(n),r=J(e);if(r===null)return s;e=r;let i='
    \"+s+\"\",i}image({href:e,title:t,text:n,tokens:s}){s&&(n=this.parser.parseInline(s,this.parser.textRenderer));let r=J(e);if(r===null)return O(n);e=r;let i=`\"${O(n)}\"`;return\",i}text(e){return\"tokens\"in e&&e.tokens?this.parser.parseInline(e.tokens):\"escaped\"in e&&e.escaped?e.text:O(e.text)}};var L=class{strong({text:e}){return e}em({text:e}){return e}codespan({text:e}){return e}del({text:e}){return e}html({text:e}){return e}text({text:e}){return e}link({text:e}){return\"\"+e}image({text:e}){return\"\"+e}br(){return\"\"}checkbox({raw:e}){return e}};var b=class l{options;renderer;textRenderer;constructor(e){this.options=e||T,this.options.renderer=this.options.renderer||new y,this.renderer=this.options.renderer,this.renderer.options=this.options,this.renderer.parser=this,this.textRenderer=new L}static parse(e,t){return new l(t).parse(e)}static parseInline(e,t){return new l(t).parseInline(e)}parse(e){this.renderer.parser=this;let t=\"\";for(let n=0;n{let o=r[i].flat(1/0);n=n.concat(this.walkTokens(o,t))}):r.tokens&&(n=n.concat(this.walkTokens(r.tokens,t)))}}return n}use(...e){let t=this.defaults.extensions||{renderers:{},childTokens:{}};return e.forEach(n=>{let s={...n};if(s.async=this.defaults.async||s.async||!1,n.extensions&&(n.extensions.forEach(r=>{if(!r.name)throw new Error(\"extension name required\");if(\"renderer\"in r){let i=t.renderers[r.name];i?t.renderers[r.name]=function(...o){let u=r.renderer.apply(this,o);return u===!1&&(u=i.apply(this,o)),u}:t.renderers[r.name]=r.renderer}if(\"tokenizer\"in r){if(!r.level||r.level!==\"block\"&&r.level!==\"inline\")throw new Error(\"extension level must be 'block' or 'inline'\");let i=t[r.level];i?i.unshift(r.tokenizer):t[r.level]=[r.tokenizer],r.start&&(r.level===\"block\"?t.startBlock?t.startBlock.push(r.start):t.startBlock=[r.start]:r.level===\"inline\"&&(t.startInline?t.startInline.push(r.start):t.startInline=[r.start]))}\"childTokens\"in r&&r.childTokens&&(t.childTokens[r.name]=r.childTokens)}),s.extensions=t),n.renderer){let r=this.defaults.renderer||new y(this.defaults);for(let i in n.renderer){if(!(i in r))throw new Error(`renderer '${i}' does not exist`);if([\"options\",\"parser\"].includes(i))continue;let o=i,u=n.renderer[o],a=r[o];r[o]=(...c)=>{let p=u.apply(r,c);return p===!1&&(p=a.apply(r,c)),p||\"\"}}s.renderer=r}if(n.tokenizer){let r=this.defaults.tokenizer||new w(this.defaults);for(let i in n.tokenizer){if(!(i in r))throw new Error(`tokenizer '${i}' does not exist`);if([\"options\",\"rules\",\"lexer\"].includes(i))continue;let o=i,u=n.tokenizer[o],a=r[o];r[o]=(...c)=>{let p=u.apply(r,c);return p===!1&&(p=a.apply(r,c)),p}}s.tokenizer=r}if(n.hooks){let r=this.defaults.hooks||new P;for(let i in n.hooks){if(!(i in r))throw new Error(`hook '${i}' does not exist`);if([\"options\",\"block\"].includes(i))continue;let o=i,u=n.hooks[o],a=r[o];P.passThroughHooks.has(i)?r[o]=c=>{if(this.defaults.async&&P.passThroughHooksRespectAsync.has(i))return(async()=>{let k=await u.call(r,c);return a.call(r,k)})();let p=u.call(r,c);return a.call(r,p)}:r[o]=(...c)=>{if(this.defaults.async)return(async()=>{let k=await u.apply(r,c);return k===!1&&(k=await a.apply(r,c)),k})();let p=u.apply(r,c);return p===!1&&(p=a.apply(r,c)),p}}s.hooks=r}if(n.walkTokens){let r=this.defaults.walkTokens,i=n.walkTokens;s.walkTokens=function(o){let u=[];return u.push(i.call(this,o)),r&&(u=u.concat(r.call(this,o))),u}}this.defaults={...this.defaults,...s}}),this}setOptions(e){return this.defaults={...this.defaults,...e},this}lexer(e,t){return x.lex(e,t??this.defaults)}parser(e,t){return b.parse(e,t??this.defaults)}parseMarkdown(e){return(n,s)=>{let r={...s},i={...this.defaults,...r},o=this.onError(!!i.silent,!!i.async);if(this.defaults.async===!0&&r.async===!1)return o(new Error(\"marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise.\"));if(typeof n>\"u\"||n===null)return o(new Error(\"marked(): input parameter is undefined or null\"));if(typeof n!=\"string\")return o(new Error(\"marked(): input parameter is of type \"+Object.prototype.toString.call(n)+\", string expected\"));if(i.hooks&&(i.hooks.options=i,i.hooks.block=e),i.async)return(async()=>{let u=i.hooks?await i.hooks.preprocess(n):n,c=await(i.hooks?await i.hooks.provideLexer(e):e?x.lex:x.lexInline)(u,i),p=i.hooks?await i.hooks.processAllTokens(c):c;i.walkTokens&&await Promise.all(this.walkTokens(p,i.walkTokens));let h=await(i.hooks?await i.hooks.provideParser(e):e?b.parse:b.parseInline)(p,i);return i.hooks?await i.hooks.postprocess(h):h})().catch(o);try{i.hooks&&(n=i.hooks.preprocess(n));let a=(i.hooks?i.hooks.provideLexer(e):e?x.lex:x.lexInline)(n,i);i.hooks&&(a=i.hooks.processAllTokens(a)),i.walkTokens&&this.walkTokens(a,i.walkTokens);let p=(i.hooks?i.hooks.provideParser(e):e?b.parse:b.parseInline)(a,i);return i.hooks&&(p=i.hooks.postprocess(p)),p}catch(u){return o(u)}}}onError(e,t){return n=>{if(n.message+=`\nPlease report this to https://github.com/markedjs/marked.`,e){let s=\"

    An error occurred:

    \"+O(n.message+\"\",!0)+\"
    \";return t?Promise.resolve(s):s}if(t)return Promise.reject(n);throw n}}};var M=new D;function g(l,e){return M.parse(l,e)}g.options=g.setOptions=function(l){return M.setOptions(l),g.defaults=M.defaults,G(g.defaults),g};g.getDefaults=z;g.defaults=T;g.use=function(...l){return M.use(...l),g.defaults=M.defaults,G(g.defaults),g};g.walkTokens=function(l,e){return M.walkTokens(l,e)};g.parseInline=M.parseInline;g.Parser=b;g.parser=b.parse;g.Renderer=y;g.TextRenderer=L;g.Lexer=x;g.lexer=x.lex;g.Tokenizer=w;g.Hooks=P;g.parse=g;var jt=g.options,Ft=g.setOptions,Ut=g.use,Kt=g.walkTokens,Wt=g.parseInline,Xt=g,Jt=b.parse,Vt=x.lex;export{P as Hooks,x as Lexer,D as Marked,b as Parser,y as Renderer,L as TextRenderer,w as Tokenizer,T as defaults,z as getDefaults,Vt as lexer,g as marked,jt as options,Xt as parse,Wt as parseInline,Jt as parser,Ft as setOptions,Ut as use,Kt as walkTokens};\n//# sourceMappingURL=marked.esm.js.map\n","function getDefaultExportFromCjs (x) {\n\treturn x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;\n}\n\nfunction commonjsRequire(path) {\n\tthrow new Error('Could not dynamically require \"' + path + '\". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.');\n}\n\nvar pluralize$2 = {exports: {}};\n\n/* global define */\nvar pluralize$1 = pluralize$2.exports;\n\nvar hasRequiredPluralize;\n\nfunction requirePluralize () {\n\tif (hasRequiredPluralize) return pluralize$2.exports;\n\thasRequiredPluralize = 1;\n\t(function (module, exports) {\n\t\t(function (root, pluralize) {\n\t\t /* istanbul ignore else */\n\t\t if (typeof commonjsRequire === 'function' && 'object' === 'object' && 'object' === 'object') {\n\t\t // Node.\n\t\t module.exports = pluralize();\n\t\t } else {\n\t\t // Browser global.\n\t\t root.pluralize = pluralize();\n\t\t }\n\t\t})(pluralize$1, function () {\n\t\t // Rule storage - pluralize and singularize need to be run sequentially,\n\t\t // while other rules can be optimized using an object for instant lookups.\n\t\t var pluralRules = [];\n\t\t var singularRules = [];\n\t\t var uncountables = {};\n\t\t var irregularPlurals = {};\n\t\t var irregularSingles = {};\n\n\t\t /**\n\t\t * Sanitize a pluralization rule to a usable regular expression.\n\t\t *\n\t\t * @param {(RegExp|string)} rule\n\t\t * @return {RegExp}\n\t\t */\n\t\t function sanitizeRule (rule) {\n\t\t if (typeof rule === 'string') {\n\t\t return new RegExp('^' + rule + '$', 'i');\n\t\t }\n\n\t\t return rule;\n\t\t }\n\n\t\t /**\n\t\t * Pass in a word token to produce a function that can replicate the case on\n\t\t * another word.\n\t\t *\n\t\t * @param {string} word\n\t\t * @param {string} token\n\t\t * @return {Function}\n\t\t */\n\t\t function restoreCase (word, token) {\n\t\t // Tokens are an exact match.\n\t\t if (word === token) return token;\n\n\t\t // Lower cased words. E.g. \"hello\".\n\t\t if (word === word.toLowerCase()) return token.toLowerCase();\n\n\t\t // Upper cased words. E.g. \"WHISKY\".\n\t\t if (word === word.toUpperCase()) return token.toUpperCase();\n\n\t\t // Title cased words. E.g. \"Title\".\n\t\t if (word[0] === word[0].toUpperCase()) {\n\t\t return token.charAt(0).toUpperCase() + token.substr(1).toLowerCase();\n\t\t }\n\n\t\t // Lower cased words. E.g. \"test\".\n\t\t return token.toLowerCase();\n\t\t }\n\n\t\t /**\n\t\t * Interpolate a regexp string.\n\t\t *\n\t\t * @param {string} str\n\t\t * @param {Array} args\n\t\t * @return {string}\n\t\t */\n\t\t function interpolate (str, args) {\n\t\t return str.replace(/\\$(\\d{1,2})/g, function (match, index) {\n\t\t return args[index] || '';\n\t\t });\n\t\t }\n\n\t\t /**\n\t\t * Replace a word using a rule.\n\t\t *\n\t\t * @param {string} word\n\t\t * @param {Array} rule\n\t\t * @return {string}\n\t\t */\n\t\t function replace (word, rule) {\n\t\t return word.replace(rule[0], function (match, index) {\n\t\t var result = interpolate(rule[1], arguments);\n\n\t\t if (match === '') {\n\t\t return restoreCase(word[index - 1], result);\n\t\t }\n\n\t\t return restoreCase(match, result);\n\t\t });\n\t\t }\n\n\t\t /**\n\t\t * Sanitize a word by passing in the word and sanitization rules.\n\t\t *\n\t\t * @param {string} token\n\t\t * @param {string} word\n\t\t * @param {Array} rules\n\t\t * @return {string}\n\t\t */\n\t\t function sanitizeWord (token, word, rules) {\n\t\t // Empty string or doesn't need fixing.\n\t\t if (!token.length || uncountables.hasOwnProperty(token)) {\n\t\t return word;\n\t\t }\n\n\t\t var len = rules.length;\n\n\t\t // Iterate over the sanitization rules and use the first one to match.\n\t\t while (len--) {\n\t\t var rule = rules[len];\n\n\t\t if (rule[0].test(word)) return replace(word, rule);\n\t\t }\n\n\t\t return word;\n\t\t }\n\n\t\t /**\n\t\t * Replace a word with the updated word.\n\t\t *\n\t\t * @param {Object} replaceMap\n\t\t * @param {Object} keepMap\n\t\t * @param {Array} rules\n\t\t * @return {Function}\n\t\t */\n\t\t function replaceWord (replaceMap, keepMap, rules) {\n\t\t return function (word) {\n\t\t // Get the correct token and case restoration functions.\n\t\t var token = word.toLowerCase();\n\n\t\t // Check against the keep object map.\n\t\t if (keepMap.hasOwnProperty(token)) {\n\t\t return restoreCase(word, token);\n\t\t }\n\n\t\t // Check against the replacement map for a direct word replacement.\n\t\t if (replaceMap.hasOwnProperty(token)) {\n\t\t return restoreCase(word, replaceMap[token]);\n\t\t }\n\n\t\t // Run all the rules against the word.\n\t\t return sanitizeWord(token, word, rules);\n\t\t };\n\t\t }\n\n\t\t /**\n\t\t * Check if a word is part of the map.\n\t\t */\n\t\t function checkWord (replaceMap, keepMap, rules, bool) {\n\t\t return function (word) {\n\t\t var token = word.toLowerCase();\n\n\t\t if (keepMap.hasOwnProperty(token)) return true;\n\t\t if (replaceMap.hasOwnProperty(token)) return false;\n\n\t\t return sanitizeWord(token, token, rules) === token;\n\t\t };\n\t\t }\n\n\t\t /**\n\t\t * Pluralize or singularize a word based on the passed in count.\n\t\t *\n\t\t * @param {string} word The word to pluralize\n\t\t * @param {number} count How many of the word exist\n\t\t * @param {boolean} inclusive Whether to prefix with the number (e.g. 3 ducks)\n\t\t * @return {string}\n\t\t */\n\t\t function pluralize (word, count, inclusive) {\n\t\t var pluralized = count === 1\n\t\t ? pluralize.singular(word) : pluralize.plural(word);\n\n\t\t return (inclusive ? count + ' ' : '') + pluralized;\n\t\t }\n\n\t\t /**\n\t\t * Pluralize a word.\n\t\t *\n\t\t * @type {Function}\n\t\t */\n\t\t pluralize.plural = replaceWord(\n\t\t irregularSingles, irregularPlurals, pluralRules\n\t\t );\n\n\t\t /**\n\t\t * Check if a word is plural.\n\t\t *\n\t\t * @type {Function}\n\t\t */\n\t\t pluralize.isPlural = checkWord(\n\t\t irregularSingles, irregularPlurals, pluralRules\n\t\t );\n\n\t\t /**\n\t\t * Singularize a word.\n\t\t *\n\t\t * @type {Function}\n\t\t */\n\t\t pluralize.singular = replaceWord(\n\t\t irregularPlurals, irregularSingles, singularRules\n\t\t );\n\n\t\t /**\n\t\t * Check if a word is singular.\n\t\t *\n\t\t * @type {Function}\n\t\t */\n\t\t pluralize.isSingular = checkWord(\n\t\t irregularPlurals, irregularSingles, singularRules\n\t\t );\n\n\t\t /**\n\t\t * Add a pluralization rule to the collection.\n\t\t *\n\t\t * @param {(string|RegExp)} rule\n\t\t * @param {string} replacement\n\t\t */\n\t\t pluralize.addPluralRule = function (rule, replacement) {\n\t\t pluralRules.push([sanitizeRule(rule), replacement]);\n\t\t };\n\n\t\t /**\n\t\t * Add a singularization rule to the collection.\n\t\t *\n\t\t * @param {(string|RegExp)} rule\n\t\t * @param {string} replacement\n\t\t */\n\t\t pluralize.addSingularRule = function (rule, replacement) {\n\t\t singularRules.push([sanitizeRule(rule), replacement]);\n\t\t };\n\n\t\t /**\n\t\t * Add an uncountable word rule.\n\t\t *\n\t\t * @param {(string|RegExp)} word\n\t\t */\n\t\t pluralize.addUncountableRule = function (word) {\n\t\t if (typeof word === 'string') {\n\t\t uncountables[word.toLowerCase()] = true;\n\t\t return;\n\t\t }\n\n\t\t // Set singular and plural references for the word.\n\t\t pluralize.addPluralRule(word, '$0');\n\t\t pluralize.addSingularRule(word, '$0');\n\t\t };\n\n\t\t /**\n\t\t * Add an irregular word definition.\n\t\t *\n\t\t * @param {string} single\n\t\t * @param {string} plural\n\t\t */\n\t\t pluralize.addIrregularRule = function (single, plural) {\n\t\t plural = plural.toLowerCase();\n\t\t single = single.toLowerCase();\n\n\t\t irregularSingles[single] = plural;\n\t\t irregularPlurals[plural] = single;\n\t\t };\n\n\t\t /**\n\t\t * Irregular rules.\n\t\t */\n\t\t [\n\t\t // Pronouns.\n\t\t ['I', 'we'],\n\t\t ['me', 'us'],\n\t\t ['he', 'they'],\n\t\t ['she', 'they'],\n\t\t ['them', 'them'],\n\t\t ['myself', 'ourselves'],\n\t\t ['yourself', 'yourselves'],\n\t\t ['itself', 'themselves'],\n\t\t ['herself', 'themselves'],\n\t\t ['himself', 'themselves'],\n\t\t ['themself', 'themselves'],\n\t\t ['is', 'are'],\n\t\t ['was', 'were'],\n\t\t ['has', 'have'],\n\t\t ['this', 'these'],\n\t\t ['that', 'those'],\n\t\t // Words ending in with a consonant and `o`.\n\t\t ['echo', 'echoes'],\n\t\t ['dingo', 'dingoes'],\n\t\t ['volcano', 'volcanoes'],\n\t\t ['tornado', 'tornadoes'],\n\t\t ['torpedo', 'torpedoes'],\n\t\t // Ends with `us`.\n\t\t ['genus', 'genera'],\n\t\t ['viscus', 'viscera'],\n\t\t // Ends with `ma`.\n\t\t ['stigma', 'stigmata'],\n\t\t ['stoma', 'stomata'],\n\t\t ['dogma', 'dogmata'],\n\t\t ['lemma', 'lemmata'],\n\t\t ['schema', 'schemata'],\n\t\t ['anathema', 'anathemata'],\n\t\t // Other irregular rules.\n\t\t ['ox', 'oxen'],\n\t\t ['axe', 'axes'],\n\t\t ['die', 'dice'],\n\t\t ['yes', 'yeses'],\n\t\t ['foot', 'feet'],\n\t\t ['eave', 'eaves'],\n\t\t ['goose', 'geese'],\n\t\t ['tooth', 'teeth'],\n\t\t ['quiz', 'quizzes'],\n\t\t ['human', 'humans'],\n\t\t ['proof', 'proofs'],\n\t\t ['carve', 'carves'],\n\t\t ['valve', 'valves'],\n\t\t ['looey', 'looies'],\n\t\t ['thief', 'thieves'],\n\t\t ['groove', 'grooves'],\n\t\t ['pickaxe', 'pickaxes'],\n\t\t ['passerby', 'passersby']\n\t\t ].forEach(function (rule) {\n\t\t return pluralize.addIrregularRule(rule[0], rule[1]);\n\t\t });\n\n\t\t /**\n\t\t * Pluralization rules.\n\t\t */\n\t\t [\n\t\t [/s?$/i, 's'],\n\t\t [/[^\\u0000-\\u007F]$/i, '$0'],\n\t\t [/([^aeiou]ese)$/i, '$1'],\n\t\t [/(ax|test)is$/i, '$1es'],\n\t\t [/(alias|[^aou]us|t[lm]as|gas|ris)$/i, '$1es'],\n\t\t [/(e[mn]u)s?$/i, '$1s'],\n\t\t [/([^l]ias|[aeiou]las|[ejzr]as|[iu]am)$/i, '$1'],\n\t\t [/(alumn|syllab|vir|radi|nucle|fung|cact|stimul|termin|bacill|foc|uter|loc|strat)(?:us|i)$/i, '$1i'],\n\t\t [/(alumn|alg|vertebr)(?:a|ae)$/i, '$1ae'],\n\t\t [/(seraph|cherub)(?:im)?$/i, '$1im'],\n\t\t [/(her|at|gr)o$/i, '$1oes'],\n\t\t [/(agend|addend|millenni|dat|extrem|bacteri|desiderat|strat|candelabr|errat|ov|symposi|curricul|automat|quor)(?:a|um)$/i, '$1a'],\n\t\t [/(apheli|hyperbat|periheli|asyndet|noumen|phenomen|criteri|organ|prolegomen|hedr|automat)(?:a|on)$/i, '$1a'],\n\t\t [/sis$/i, 'ses'],\n\t\t [/(?:(kni|wi|li)fe|(ar|l|ea|eo|oa|hoo)f)$/i, '$1$2ves'],\n\t\t [/([^aeiouy]|qu)y$/i, '$1ies'],\n\t\t [/([^ch][ieo][ln])ey$/i, '$1ies'],\n\t\t [/(x|ch|ss|sh|zz)$/i, '$1es'],\n\t\t [/(matr|cod|mur|sil|vert|ind|append)(?:ix|ex)$/i, '$1ices'],\n\t\t [/\\b((?:tit)?m|l)(?:ice|ouse)$/i, '$1ice'],\n\t\t [/(pe)(?:rson|ople)$/i, '$1ople'],\n\t\t [/(child)(?:ren)?$/i, '$1ren'],\n\t\t [/eaux$/i, '$0'],\n\t\t [/m[ae]n$/i, 'men'],\n\t\t ['thou', 'you']\n\t\t ].forEach(function (rule) {\n\t\t return pluralize.addPluralRule(rule[0], rule[1]);\n\t\t });\n\n\t\t /**\n\t\t * Singularization rules.\n\t\t */\n\t\t [\n\t\t [/s$/i, ''],\n\t\t [/(ss)$/i, '$1'],\n\t\t [/(wi|kni|(?:after|half|high|low|mid|non|night|[^\\w]|^)li)ves$/i, '$1fe'],\n\t\t [/(ar|(?:wo|[ae])l|[eo][ao])ves$/i, '$1f'],\n\t\t [/ies$/i, 'y'],\n\t\t [/\\b([pl]|zomb|(?:neck|cross)?t|coll|faer|food|gen|goon|group|lass|talk|goal|cut)ies$/i, '$1ie'],\n\t\t [/\\b(mon|smil)ies$/i, '$1ey'],\n\t\t [/\\b((?:tit)?m|l)ice$/i, '$1ouse'],\n\t\t [/(seraph|cherub)im$/i, '$1'],\n\t\t [/(x|ch|ss|sh|zz|tto|go|cho|alias|[^aou]us|t[lm]as|gas|(?:her|at|gr)o|[aeiou]ris)(?:es)?$/i, '$1'],\n\t\t [/(analy|diagno|parenthe|progno|synop|the|empha|cri|ne)(?:sis|ses)$/i, '$1sis'],\n\t\t [/(movie|twelve|abuse|e[mn]u)s$/i, '$1'],\n\t\t [/(test)(?:is|es)$/i, '$1is'],\n\t\t [/(alumn|syllab|vir|radi|nucle|fung|cact|stimul|termin|bacill|foc|uter|loc|strat)(?:us|i)$/i, '$1us'],\n\t\t [/(agend|addend|millenni|dat|extrem|bacteri|desiderat|strat|candelabr|errat|ov|symposi|curricul|quor)a$/i, '$1um'],\n\t\t [/(apheli|hyperbat|periheli|asyndet|noumen|phenomen|criteri|organ|prolegomen|hedr|automat)a$/i, '$1on'],\n\t\t [/(alumn|alg|vertebr)ae$/i, '$1a'],\n\t\t [/(cod|mur|sil|vert|ind)ices$/i, '$1ex'],\n\t\t [/(matr|append)ices$/i, '$1ix'],\n\t\t [/(pe)(rson|ople)$/i, '$1rson'],\n\t\t [/(child)ren$/i, '$1'],\n\t\t [/(eau)x?$/i, '$1'],\n\t\t [/men$/i, 'man']\n\t\t ].forEach(function (rule) {\n\t\t return pluralize.addSingularRule(rule[0], rule[1]);\n\t\t });\n\n\t\t /**\n\t\t * Uncountable rules.\n\t\t */\n\t\t [\n\t\t // Singular words with no plurals.\n\t\t 'adulthood',\n\t\t 'advice',\n\t\t 'agenda',\n\t\t 'aid',\n\t\t 'aircraft',\n\t\t 'alcohol',\n\t\t 'ammo',\n\t\t 'analytics',\n\t\t 'anime',\n\t\t 'athletics',\n\t\t 'audio',\n\t\t 'bison',\n\t\t 'blood',\n\t\t 'bream',\n\t\t 'buffalo',\n\t\t 'butter',\n\t\t 'carp',\n\t\t 'cash',\n\t\t 'chassis',\n\t\t 'chess',\n\t\t 'clothing',\n\t\t 'cod',\n\t\t 'commerce',\n\t\t 'cooperation',\n\t\t 'corps',\n\t\t 'debris',\n\t\t 'diabetes',\n\t\t 'digestion',\n\t\t 'elk',\n\t\t 'energy',\n\t\t 'equipment',\n\t\t 'excretion',\n\t\t 'expertise',\n\t\t 'firmware',\n\t\t 'flounder',\n\t\t 'fun',\n\t\t 'gallows',\n\t\t 'garbage',\n\t\t 'graffiti',\n\t\t 'hardware',\n\t\t 'headquarters',\n\t\t 'health',\n\t\t 'herpes',\n\t\t 'highjinks',\n\t\t 'homework',\n\t\t 'housework',\n\t\t 'information',\n\t\t 'jeans',\n\t\t 'justice',\n\t\t 'kudos',\n\t\t 'labour',\n\t\t 'literature',\n\t\t 'machinery',\n\t\t 'mackerel',\n\t\t 'mail',\n\t\t 'media',\n\t\t 'mews',\n\t\t 'moose',\n\t\t 'music',\n\t\t 'mud',\n\t\t 'manga',\n\t\t 'news',\n\t\t 'only',\n\t\t 'personnel',\n\t\t 'pike',\n\t\t 'plankton',\n\t\t 'pliers',\n\t\t 'police',\n\t\t 'pollution',\n\t\t 'premises',\n\t\t 'rain',\n\t\t 'research',\n\t\t 'rice',\n\t\t 'salmon',\n\t\t 'scissors',\n\t\t 'series',\n\t\t 'sewage',\n\t\t 'shambles',\n\t\t 'shrimp',\n\t\t 'software',\n\t\t 'species',\n\t\t 'staff',\n\t\t 'swine',\n\t\t 'tennis',\n\t\t 'traffic',\n\t\t 'transportation',\n\t\t 'trout',\n\t\t 'tuna',\n\t\t 'wealth',\n\t\t 'welfare',\n\t\t 'whiting',\n\t\t 'wildebeest',\n\t\t 'wildlife',\n\t\t 'you',\n\t\t /pok[eé]mon$/i,\n\t\t // Regexes.\n\t\t /[^aeiou]ese$/i, // \"chinese\", \"japanese\"\n\t\t /deer$/i, // \"deer\", \"reindeer\"\n\t\t /fish$/i, // \"fish\", \"blowfish\", \"angelfish\"\n\t\t /measles$/i,\n\t\t /o[iu]s$/i, // \"carnivorous\"\n\t\t /pox$/i, // \"chickpox\", \"smallpox\"\n\t\t /sheep$/i\n\t\t ].forEach(pluralize.addUncountableRule);\n\n\t\t return pluralize;\n\t\t}); \n\t} (pluralize$2));\n\treturn pluralize$2.exports;\n}\n\nvar pluralizeExports = requirePluralize();\nvar pluralize = /*@__PURE__*/getDefaultExportFromCjs(pluralizeExports);\n\nexport { pluralize as default };\n","/*! (c) Andrea Giammarchi (ISC) */var hyperHTML=function(N){\"use strict\";var t={};try{t.WeakMap=WeakMap}catch(e){t.WeakMap=function(t,e){var n=e.defineProperty,r=e.hasOwnProperty,i=a.prototype;return i.delete=function(e){return this.has(e)&&delete e[this._]},i.get=function(e){return this.has(e)?e[this._]:void 0},i.has=function(e){return r.call(e,this._)},i.set=function(e,t){return n(e,this._,{configurable:!0,value:t}),this},a;function a(e){n(this,\"_\",{value:\"_@ungap/weakmap\"+t++}),e&&e.forEach(o,this)}function o(e){this.set(e[0],e[1])}}(Math.random(),Object)}var s=t.WeakMap,i={};try{i.WeakSet=WeakSet}catch(e){!function(e,t){var n=r.prototype;function r(){t(this,\"_\",{value:\"_@ungap/weakmap\"+e++})}n.add=function(e){return this.has(e)||t(e,this._,{value:!0,configurable:!0}),this},n.has=function(e){return this.hasOwnProperty.call(e,this._)},n.delete=function(e){return this.has(e)&&delete e[this._]},i.WeakSet=r}(Math.random(),Object.defineProperty)}function m(e,t,n,r,i,a){for(var o=(\"selectedIndex\"in t),u=o;ro;)--c;l=u+r-c;var m=Array(l),y=s[c];for(--n;y;){for(var b=y.newi,w=y.oldi;b>>0;n\"+e+\"\",r.querySelectorAll(t)):(r.innerHTML=e,r.childNodes)),n},function(e,t){return(\"svg\"===t?function(e){var t=H(O),n=H(\"div\");return n.innerHTML=''+e+\"\",F(t,n.firstChild.childNodes),t}:M)(e)});function F(e,t){for(var n=t.length;n--;)e.appendChild(t[0])}function H(e){return e===O?S.createDocumentFragment():S.createElementNS(\"http://www.w3.org/1999/xhtml\",e)}var I,z,V,Z,G,q,B,J,K,Q,U=(z=\"appendChild\",V=\"cloneNode\",Z=\"createTextNode\",q=(G=\"importNode\")in(I=N),(B=I.createDocumentFragment())[z](I[Z](\"g\")),B[z](I[Z](\"\")),(q?I[G](B,!0):B[V](!0)).childNodes.length<2?function e(t,n){for(var r=t[V](),i=t.childNodes||[],a=i.length,o=0;n&&o

    ',J[K].childNodes[0].getAttribute(Q)==Y)||(Y=\"_dt: \"+Y.slice(1,-1)+\";\",ee=!0)}catch(e){}var te=\"\\x3c!--\"+Y+\"--\\x3e\",ne=8,re=1,ie=3,ae=/^(?:style|textarea)$/i,oe=/^(?:area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr)$/i;var ue=\" \\\\f\\\\n\\\\r\\\\t\",ce=\"[^\"+ue+\"\\\\/>\\\"'=]+\",le=\"[\"+ue+\"]+\"+ce,se=\"<([A-Za-z]+[A-Za-z0-9:._-]*)((?:\",fe=\"(?:\\\\s*=\\\\s*(?:'[^']*?'|\\\"[^\\\"]*?\\\"|<[^>]*?>|\"+ce.replace(\"\\\\/\",\"\")+\"))?)\",he=new RegExp(se+le+fe+\"+)([\"+ue+\"]*/?>)\",\"g\"),de=new RegExp(se+le+fe+\"*)([\"+ue+\"]*/>)\",\"g\"),ve=new RegExp(\"(\"+le+\"\\\\s*=\\\\s*)(['\\\"]?)\"+te+\"\\\\2\",\"gi\");function pe(e,t,n,r){return\"<\"+t+n.replace(ve,ge)+r}function ge(e,t,n){return t+(n||'\"')+Y+(n||'\"')}function me(e,t,n){return oe.test(t)?e:\"<\"+t+n+\">\"}var ye=ee?function(e,t){var n=t.join(\" \");return t.slice.call(e,0).sort(function(e,t){return n.indexOf(e.name)<=n.indexOf(t.name)?-1:1})}:function(e,t){return t.slice.call(e,0)};function be(e,t,n,r){for(var i=e.childNodes,a=i.length,o=0;o str.replace(/[\\\\^$.*+?()[\\]{}|]/g, \"\\\\$&\"));\n\n/**\n * Hashes a string from char code. Can return a negative number.\n * Based on https://gist.github.com/hyamamoto/fd435505d29ebfa3d9716fd2be8d42f0\n * @param {String} text\n */\nfunction hashString(text) {\n let hash = 0;\n for (const char of text) {\n hash = (Math.imul(31, hash) + char.charCodeAt(0)) | 0;\n }\n return String(hash);\n}\n\n// https://stackoverflow.com/a/58633686\nexport const ISODate = new Intl.DateTimeFormat([\"sv-SE\"], {\n timeZone: \"UTC\",\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n});\n\n// We use an \"Australian Date\" because it omits the \",\"\n// after the day of the month, which is required by the W3C.\nconst dateLang =\n docLang === \"en\" || docLang.startsWith(\"en-\") ? \"en-AU\" : docLang;\nexport const W3CDate = new Intl.DateTimeFormat(dateLang, {\n timeZone: \"UTC\",\n year: \"numeric\",\n month: \"long\",\n day: dateLang === \"en-AU\" ? \"2-digit\" : \"numeric\",\n});\n\n/** CSS selector for matching elements that are non-normative */\nexport const nonNormativeSelector =\n \".informative, .note, .issue, .example, .ednote, .practice, .introductory\";\n\n/**\n * Creates a link element that represents a resource hint.\n *\n * @param {ResourceHintOption} opts Configure the resource hint.\n * @return {HTMLLinkElement} A link element ready to use.\n */\nexport function createResourceHint(opts) {\n const url = new URL(opts.href, document.baseURI);\n const linkElem = document.createElement(\"link\");\n let { href } = url;\n linkElem.rel = opts.hint;\n switch (linkElem.rel) {\n case \"dns-prefetch\":\n case \"preconnect\":\n href = url.origin;\n if (opts.corsMode || url.origin !== document.location.origin) {\n linkElem.crossOrigin = opts.corsMode || \"anonymous\";\n }\n break;\n case \"preload\":\n if (\"as\" in opts) {\n linkElem.setAttribute(\"as\", opts.as || \"\");\n }\n if (opts.corsMode) {\n linkElem.crossOrigin = opts.corsMode;\n }\n break;\n }\n linkElem.href = href;\n if (!opts.dontRemove) {\n linkElem.classList.add(\"removeOnSave\");\n }\n return linkElem;\n}\n\n// RESPEC STUFF\n/**\n * @param {Document | Element} doc\n */\nexport function removeReSpec(doc) {\n doc.querySelectorAll(\".remove, script[data-requiremodule]\").forEach(elem => {\n elem.remove();\n });\n}\n\n/**\n * Adds error class to each element while emitting a warning\n * @param {HTMLElement} elem\n * @param {String} msg message to show in warning\n * @param {String=} title error message to add on each element\n */\nfunction markAsOffending(elem, msg, title) {\n elem.classList.add(\"respec-offending-element\");\n if (!elem.hasAttribute(\"title\")) {\n elem.setAttribute(\"title\", title || msg);\n }\n if (!elem.id) {\n addId(elem, \"respec-offender\");\n }\n}\n\n// STRING HELPERS\n/**\n * @param {\"conjunction\"|\"disjunction\"} type\n * @param {\"long\"|\"narrow\"} style\n */\nfunction joinFactory(type, style = \"long\") {\n const formatter = new Intl.ListFormat(docLang, { style, type });\n /**\n * @template T\n * @param {string[]} items\n * @param {(value: string, index: number, array: string[]) => any} [mapper]\n */\n return (items, mapper) => {\n let elemCount = 0;\n return formatter.formatToParts(items).map(({ type, value }) => {\n if (type === \"element\" && mapper) {\n return mapper(value, elemCount++, items);\n }\n return value;\n });\n };\n}\n\n/**\n * Takes an array and returns a string that separates each of its items with the\n * proper commas and \"and\". The second argument is a mapping function that can\n * convert the items before they are joined.\n */\nconst conjunction = joinFactory(\"conjunction\");\nconst disjunction = joinFactory(\"disjunction\");\n\n/**\n *\n * @param {string[]} items\n * @param {(value: string, index: number, array: string[]) => string} [mapper]\n */\nexport function joinAnd(items, mapper) {\n return conjunction(items, mapper).join(\"\");\n}\n\n/**\n *\n * @param {string[]} items\n * @param {(value: string, index: number, array: string[]) => string} [mapper]\n */\nexport function joinOr(items, mapper) {\n return disjunction(items, mapper).join(\"\");\n}\n\n/**\n * Takes a string, applies some XML escapes, and returns the escaped string.\n * @param {string} str\n */\nexport function xmlEscape(str) {\n return str\n .replace(/&/g, \"&\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/, [lang: string]: Record }} T\n * @param {T} localizationStrings\n * @returns {T['en']}\n */\nexport function getIntlData(localizationStrings, lang = docLang) {\n lang = lang.toLowerCase();\n // Proxy return type is a known bug:\n // https://github.com/Microsoft/TypeScript/issues/20846\n // @ts-expect-error\n return new Proxy(localizationStrings, {\n /** @param {string} key */\n get(data, key) {\n const result = getIntlDataForKey(data, key, lang) || data.en[key];\n if (!result) {\n throw new Error(`No l10n data for key: \"${key}\"`);\n }\n return result;\n },\n });\n}\n\n/**\n * @template {Record>} T\n * @param {T} localizationStrings\n * @param {string} key\n */\nexport function getIntlDataForKey(localizationStrings, key, lang = docLang) {\n lang = lang.toLowerCase();\n const shortLang = lang.match(/^(\\w{2,3})-.+$/)?.[1] ?? \"\";\n return (\n localizationStrings[lang]?.[key] || localizationStrings[shortLang]?.[key]\n );\n}\n\n// --- DATE HELPERS -------------------------------------------------------------------------------\n/**\n * Takes a Date object and an optional separator and returns the year,month,day\n * representation with the custom separator (defaulting to none) and proper\n * 0-padding.\n * @param {Date} date\n */\nexport function concatDate(date, sep = \"\") {\n return ISODate.format(date).replace(dashes, sep);\n}\n\n/**\n * Checks if a date is in expected format used by ReSpec (yyyy-mm-dd)\n * @param {string} rawDate\n */\nexport function isValidConfDate(rawDate) {\n const date = /\\d{4}-\\d{2}-\\d{2}/.test(rawDate)\n ? new Date(rawDate)\n : \"Invalid Date\";\n return date.toString() !== \"Invalid Date\";\n}\n\n/**\n * Given an object, it converts it to a key value pair separated by (\"=\", configurable) and a delimiter (\" ,\" configurable).\n * @example {\"foo\": \"bar\", \"baz\": 1} becomes \"foo=bar, baz=1\"\n * @param {Record} obj\n */\nexport function toKeyValuePairs(obj, delimiter = \", \", separator = \"=\") {\n return Array.from(Object.entries(obj))\n .map(([key, value]) => `${key}${separator}${JSON.stringify(value)}`)\n .join(delimiter);\n}\n\n// STYLE HELPERS\n/**\n * Take a document and either a link or an array of links to CSS and appends a\n * `` element to the head pointing to each.\n * @param {Document} doc\n * @param {string | string[]} urls\n */\nexport function linkCSS(doc, urls) {\n const stylesArray = /** @type {string[]} */ ([]).concat(urls);\n const frag = stylesArray\n .map(url => {\n const link = doc.createElement(\"link\");\n link.rel = \"stylesheet\";\n link.href = url;\n return link;\n })\n .reduce((elem, nextLink) => {\n elem.appendChild(nextLink);\n return elem;\n }, doc.createDocumentFragment());\n doc.head.appendChild(frag);\n}\n\n// TRANSFORMATIONS\n\n/**\n * Run list of transforms over content and return result.\n *\n * Please note that this is a legacy method that is only kept in order to\n * maintain compatibility with RSv1. It is therefore not tested and not actively\n * supported.\n * @this {any}\n * @param {string} content\n * @param {string} [flist] List of global function names.\n * @param {...any} funcArgs Arguments to pass to each function.\n */\nexport function runTransforms(content, flist, ...funcArgs) {\n const args = [this, content, ...funcArgs];\n if (flist) {\n const methods = flist.split(/\\s+/);\n for (const meth of methods) {\n const method = /** @type {any} */ (window)[meth];\n if (method) {\n // the initial call passed |this| directly, so we keep it that way\n try {\n content = method.apply(this, args);\n } catch (e) {\n const msg = `call to \\`${meth}()\\` failed with: ${e}.`;\n const hint = \"See developer console for stack trace.\";\n showWarning(msg, \"utils/runTransforms\", {\n hint,\n cause: /** @type {Error} */ (e),\n });\n }\n }\n }\n }\n return content;\n}\n\n/**\n * Cached request handler\n * @param {RequestInfo} input\n * @param {number} maxAge cache expiration duration in ms. defaults to 24 hours\n * @return {Promise}\n * if a cached response is available and it's not stale, return it\n * else: request from network, cache and return fresh response.\n * If network fails, return a stale cached version if exists (else throw)\n */\nexport async function fetchAndCache(input, maxAge = 24 * 60 * 60 * 1000) {\n const request = new Request(input);\n const url = new URL(request.url);\n\n // use data from cache data if valid and render\n let cache;\n let cachedResponse;\n if (\"caches\" in window) {\n try {\n cache = await caches.open(url.origin);\n cachedResponse = await cache.match(request);\n if (\n cachedResponse &&\n new Date(cachedResponse.headers.get(\"Expires\") ?? \"\") > new Date()\n ) {\n return cachedResponse;\n }\n } catch (err) {\n console.error(\"Failed to use Cache API.\", err);\n }\n }\n\n // otherwise fetch new data and cache\n const response = await fetch(request);\n if (!response.ok) {\n if (cachedResponse) {\n // return stale version\n console.warn(`Returning a stale cached response for ${url}`);\n return cachedResponse;\n }\n }\n\n // cache response\n if (cache && response.ok) {\n const clonedResponse = response.clone();\n const customHeaders = new Headers(response.headers);\n const expiryDate = new Date(Date.now() + maxAge);\n customHeaders.set(\"Expires\", expiryDate.toISOString());\n const cacheResponse = new Response(await clonedResponse.blob(), {\n headers: customHeaders,\n });\n // put in cache, and forget it (there is no recovery if it throws, but that's ok).\n await cache.put(request, cacheResponse).catch(console.error);\n }\n return response;\n}\n\n// --- DOM HELPERS -------------------------------\n\n/**\n * Separates each item with proper commas.\n * @template T\n * @param {T[]} array\n * @param {(item: T, index: number, array: T[]) => any} [mapper]\n */\nexport function htmlJoinComma(array, mapper = item => item) {\n const items = array.map(mapper);\n const joined = items.slice(0, -1).map(item => html`${item}, `);\n return html`${joined}${items[items.length - 1]}`;\n}\n/**\n *\n * @param {string[]} array\n * @param {(item: any, index: number, array: string[]) => any[]} [mapper]\n */\nexport function htmlJoinAnd(array, mapper) {\n /** @type {any[]} */\n const result = /** @type {any} */ ([]).concat(conjunction(array, mapper));\n return result.map(item => (typeof item === \"string\" ? html`${item}` : item));\n}\n\n/**\n * Creates and sets an ID to an element (elem) by hashing the text content.\n *\n * @param {HTMLElement} elem element to hash from\n * @param {String} prefix prefix to prepend to the generated id\n */\nexport function addHashId(elem, prefix = \"\") {\n const text = norm(elem.textContent);\n const hash = hashString(text);\n return addId(elem, prefix, hash);\n}\n\n/**\n * Converts a string to a slug suitable for use in an HTML id attribute:\n * lowercases (unless noLC is true), decomposes Unicode, strips diacritics,\n * replaces runs of non-word characters with \"-\", and trims leading/trailing\n * hyphens.\n * @param {string} txt\n * @param {boolean} [noLC] - when true, skip lowercasing\n * @returns {string}\n */\nexport function toId(txt, noLC = false) {\n return (noLC ? txt : txt.toLowerCase())\n .trim()\n .normalize(\"NFD\")\n .replace(/[\\u0300-\\u036f]/g, \"\")\n .replace(/\\W+/gim, \"-\")\n .replace(/^-+/, \"\")\n .replace(/-+$/, \"\");\n}\n\n/**\n * Creates and sets an ID to an element (elem) using a specific prefix if\n * provided, and a specific text if given.\n * @param {HTMLElement} elem element\n * @param {String} pfx prefix\n * @param {String} txt text\n * @param {Boolean} noLC do not convert to lowercase\n * @returns {String} generated (or existing) id for element\n */\nexport function addId(elem, pfx = \"\", txt = \"\", noLC = false) {\n if (elem.id) {\n return elem.id;\n }\n if (!txt) {\n txt = (elem.title ? elem.title : elem.textContent).trim();\n }\n let id = toId(txt, noLC);\n\n if (!id) {\n id = \"generatedID\";\n } else if (/\\.$/.test(id) || !/^[a-z]/i.test(pfx || id)) {\n id = `x${id}`; // trailing . doesn't play well with jQuery\n }\n if (pfx) {\n id = `${pfx}-${id}`;\n }\n if (elem.ownerDocument.getElementById(id)) {\n let i = 0;\n let nextId = `${id}-${i}`;\n while (elem.ownerDocument.getElementById(nextId)) {\n i += 1;\n nextId = `${id}-${i}`;\n }\n id = nextId;\n }\n elem.id = id;\n return id;\n}\n\n/**\n * Returns all the descendant text nodes of an element.\n * @param {Node} el\n * @param {string[]} exclusions node localName to exclude\n * @param {object} options\n * @param {boolean} options.wsNodes return only whitespace-only nodes.\n * @returns {Text[]}\n */\nexport function getTextNodes(el, exclusions = [], options = { wsNodes: true }) {\n const exclusionQuery = exclusions.join(\", \");\n /**\n * @param {Text} node\n */\n const acceptNodeFn = node => {\n if (!options.wsNodes && !node.data.trim()) {\n return NodeFilter.FILTER_REJECT;\n }\n if (exclusionQuery && node.parentElement?.closest(exclusionQuery)) {\n return NodeFilter.FILTER_REJECT;\n }\n return NodeFilter.FILTER_ACCEPT;\n };\n const nodeIterator = document.createNodeIterator(\n el,\n NodeFilter.SHOW_TEXT,\n /** @type {NodeFilter} */ ({ acceptNode: acceptNodeFn })\n );\n /** @type {Text[]} */\n const textNodes = [];\n let node;\n while ((node = nodeIterator.nextNode())) {\n textNodes.push(/** @type {Text} */ (node));\n }\n return textNodes;\n}\n\n/**\n * For any element, returns an array of title strings that applies the algorithm\n * used for determining the actual title of a `` element (but can apply to\n * other as well).\n *\n * This method now *prefers* the `data-lt` attribute for the list of titles.\n * That attribute is added by this method to `` elements, so subsequent\n * calls to this method will return the `data-lt` based list.\n * @param {HTMLElement} elem\n * @returns {String[]} array of title strings\n */\nexport function getDfnTitles(elem) {\n const titleSet = new Set();\n // data-lt-noDefault avoid using the text content of a definition\n // in the definition list.\n // ltNodefault is === \"data-lt-noDefault\"... someone screwed up 😖\n const normText = \"ltNodefault\" in elem.dataset ? \"\" : norm(elem.textContent);\n const child = /** @type {HTMLElement | undefined} */ (elem.children[0]);\n if (elem.dataset.lt) {\n // prefer @data-lt for the list of title aliases\n elem.dataset.lt\n .split(\"|\")\n .map(item => norm(item))\n .forEach(item => titleSet.add(item));\n } else if (\n elem.childNodes.length === 1 &&\n elem.getElementsByTagName(\"abbr\").length === 1 &&\n child &&\n child.title\n ) {\n titleSet.add(child.title);\n } else if (elem.textContent === '\"\"') {\n titleSet.add(\"the-empty-string\");\n }\n\n titleSet.add(normText);\n titleSet.delete(\"\");\n\n // We could have done this with @data-lt (as the logic is same), but if\n // @data-lt was not present, we would end up using @data-local-lt as element's\n // id (in other words, we prefer textContent over @data-local-lt for dfn id)\n if (elem.dataset.localLt) {\n const localLt = elem.dataset.localLt.split(\"|\");\n localLt.forEach(item => titleSet.add(norm(item)));\n }\n\n const titles = [...titleSet];\n return titles;\n}\n\n/**\n * For an element (usually ), returns an array of targets that element might\n * refer to, in the object structure:\n * @typedef {object} LinkTarget\n * @property {string} for\n * @property {string} title\n *\n * For an element like:\n *

    Int3.member

    \n * we'll return:\n * * {for: \"int2\", title: \"int3.member\"}\n * * {for: \"int3\", title: \"member\"}\n * * {for: \"\", title: \"int3.member\"}\n * @param {HTMLElement} elem\n * @returns {LinkTarget[]}\n */\nexport function getLinkTargets(elem) {\n /** @type {HTMLElement | null} */\n const linkForElem = elem.closest(\"[data-link-for]\");\n const linkFor = linkForElem ? (linkForElem.dataset.linkFor ?? \"\") : \"\";\n const titles = getDfnTitles(elem);\n /** @type {LinkTarget[]} */\n const results = titles.reduce((result, title) => {\n // supports legacy Foo.Bar() definitions\n const split = title.split(\".\");\n if (split.length === 2) {\n // If there are multiple '.'s, this won't match an\n // Interface/member pair anyway.\n result.push({ for: split[0], title: split[1] });\n }\n result.push({ for: linkFor, title });\n if (!linkForElem) result.push({ for: title, title });\n\n // Finally, we can try to match without link for\n if (linkFor !== \"\") result.push({ for: \"\", title });\n\n // Possessive suffix fallback: \"term's\" / \"term\\u2019s\" → \"term\"\n const stripped = title.replace(POSSESSIVE_SUFFIX, \"\");\n if (stripped !== title && stripped !== \"\") {\n result.push({ for: linkFor, title: stripped });\n if (!linkForElem) result.push({ for: stripped, title: stripped });\n if (linkFor !== \"\") result.push({ for: \"\", title: stripped });\n }\n return result;\n }, /** @type {LinkTarget[]} */ ([]));\n return results;\n}\n\n/**\n * Changes name of a DOM Element\n * @param {Element} elem element to rename\n * @param {String} newName new element name\n * @param {Object} options\n * @param {boolean} options.copyAttributes\n *\n * @returns {Element} new renamed element\n */\nexport function renameElement(\n elem,\n newName,\n options = { copyAttributes: true }\n) {\n if (elem.localName === newName) return elem;\n const newElement = elem.ownerDocument.createElement(newName);\n // copy attributes\n if (options.copyAttributes) {\n for (const { name, value } of elem.attributes) {\n newElement.setAttribute(name, value);\n }\n }\n // copy child nodes\n newElement.append(...elem.childNodes);\n elem.replaceWith(newElement);\n return newElement;\n}\n\n/**\n * @param {string} ref\n * @param {HTMLElement} element\n */\nexport function refTypeFromContext(ref, element) {\n const closestInformative = element.closest(nonNormativeSelector);\n let isInformative = false;\n if (closestInformative) {\n // check if parent is not normative\n isInformative =\n !element.closest(\".normative\") ||\n !closestInformative.querySelector(\".normative\");\n }\n // prefixes `!` and `?` override section behavior\n if (ref.startsWith(\"!\")) {\n if (isInformative) {\n // A (forced) normative reference in informative section is illegal\n return { type: \"informative\", illegal: true };\n }\n isInformative = false;\n } else if (ref.startsWith(\"?\")) {\n isInformative = true;\n }\n const type = isInformative ? \"informative\" : \"normative\";\n return { type, illegal: false };\n}\n\n/**\n * Wraps inner contents with the wrapper node\n * @param {Node} outer outer node to be modified\n * @param {Element} wrapper wrapper node to be appended\n */\nexport function wrapInner(outer, wrapper) {\n wrapper.append(...outer.childNodes);\n outer.appendChild(wrapper);\n return outer;\n}\n\n/**\n * @param {Element} element\n */\nexport function getPreviousSections(element) {\n /** @type {Element[]} */\n const sections = [];\n for (const previous of iteratePreviousElements(element)) {\n if (previous.localName === \"section\") {\n sections.push(previous);\n }\n }\n return sections;\n}\n\n/**\n * @param {Element} element\n */\nfunction* iteratePreviousElements(element) {\n let previous = element;\n while (previous.previousElementSibling) {\n previous = previous.previousElementSibling;\n yield previous;\n }\n}\n/**\n * Applies the selector for all its ancestors.\n * @param {Element} element\n * @param {string} selector\n */\nexport function parents(element, selector) {\n /** @type {Element[]} */\n const list = [];\n let parent = element.parentElement;\n while (parent) {\n const closest = parent.closest(selector);\n if (!closest) {\n break;\n }\n list.push(closest);\n parent = closest.parentElement;\n }\n return list;\n}\n\n/**\n * Calculates indentation when the element starts after a newline. The value\n * will be empty if no newline or any non-whitespace exists after one.\n * @param {Element} element\n *\n * @example `
    ` returns \" \" (4 spaces).\n */\nexport function getElementIndentation(element) {\n const { previousSibling } = element;\n if (!previousSibling || previousSibling.nodeType !== Node.TEXT_NODE) {\n return \"\";\n }\n const index = (previousSibling.textContent ?? \"\").lastIndexOf(\"\\n\");\n if (index === -1) {\n return \"\";\n }\n const slice = (previousSibling.textContent ?? \"\").slice(index + 1);\n if (/\\S/.test(slice)) {\n return \"\";\n }\n return slice;\n}\n\n/**\n * Generates simple ids. The id's increment after it yields.\n *\n * @param {String} namespace A string like \"highlight\".\n * @param {number} counter A number, which can start at a given value.\n */\nexport function msgIdGenerator(namespace, counter = 0) {\n /** @returns {Generator} */\n /**\n * @param {string} namespace\n * @param {number} counter\n */\n function* idGenerator(namespace, counter) {\n while (true) {\n yield `${namespace}:${counter}`;\n counter++;\n }\n }\n const gen = idGenerator(namespace, counter);\n return () => {\n return gen.next().value;\n };\n}\n\n/** @extends {Set} */\nexport class InsensitiveStringSet extends Set {\n /**\n * @param {Array} [keys] Optional, initial keys\n */\n constructor(keys = []) {\n super();\n for (const key of keys) {\n this.add(key);\n }\n }\n /**\n * @param {string} key\n */\n add(key) {\n if (!this.has(key) && !this.getCanonicalKey(key)) {\n return super.add(key);\n }\n return this;\n }\n /**\n * @param {string} key\n */\n has(key) {\n return (\n super.has(key) ||\n [...this.keys()].some(\n existingKey => existingKey.toLowerCase() === key.toLowerCase()\n )\n );\n }\n /**\n * @param {string} key\n */\n delete(key) {\n return super.has(key)\n ? super.delete(key)\n : super.delete(this.getCanonicalKey(key) ?? key);\n }\n /**\n * @param {string} key\n */\n getCanonicalKey(key) {\n return super.has(key)\n ? key\n : [...this.keys()].find(\n existingKey => existingKey.toLowerCase() === key.toLowerCase()\n );\n }\n}\n\n/**\n * @param {HTMLElement} node\n */\nexport function makeSafeCopy(node) {\n const clone = node.cloneNode(true);\n clone.querySelectorAll(\"[id]\").forEach(elem => elem.removeAttribute(\"id\"));\n clone.querySelectorAll(\"dfn\").forEach(dfn => {\n renameElement(dfn, \"span\", { copyAttributes: false });\n });\n if (clone.hasAttribute(\"id\")) clone.removeAttribute(\"id\");\n removeCommentNodes(clone);\n return clone;\n}\n\n/**\n * @param {Node} node\n */\nexport function removeCommentNodes(node) {\n const walker = document.createTreeWalker(node, NodeFilter.SHOW_COMMENT);\n for (const comment of [...walkTree(walker)]) {\n /** @type {ChildNode} */ (comment).remove();\n }\n}\n\n/**\n * @param {TreeWalker} walker\n * @return {IterableIterator}\n */\nfunction* walkTree(walker) {\n while (walker.nextNode()) {\n yield walker.currentNode;\n }\n}\n\n/**\n * @template ValueType\n * @extends {Map}\n */\nexport class CaseInsensitiveMap extends Map {\n /**\n * @param {Array<[string, ValueType]>} [entries]\n */\n constructor(entries = []) {\n super();\n entries.forEach(([key, elem]) => {\n this.set(key, elem);\n });\n return this;\n }\n /**\n * @param {String} key\n * @param {ValueType} value\n */\n set(key, value) {\n super.set(key.toLowerCase(), value);\n return this;\n }\n /**\n * @param {String} key\n */\n get(key) {\n return super.get(key.toLowerCase());\n }\n /**\n * @param {String} key\n */\n has(key) {\n return super.has(key.toLowerCase());\n }\n /**\n * @param {String} key\n */\n delete(key) {\n return super.delete(key.toLowerCase());\n }\n}\n\nexport class RespecError extends Error {\n /**\n * @param {Parameters[0]} message\n * @param {Parameters[1]} plugin\n * @param {Parameters[2] & { isWarning: boolean }} options\n */\n constructor(message, plugin, options) {\n super(message, { ...(options.cause && { cause: options.cause }) });\n const name = options.isWarning ? \"ReSpecWarning\" : \"ReSpecError\";\n Object.assign(this, { message, plugin, name, ...options });\n if (options.elements) {\n options.elements.forEach(elem =>\n markAsOffending(elem, message, options.title)\n );\n }\n }\n\n toJSON() {\n const { message, name, stack } = this;\n // @ts-expect-error https://github.com/microsoft/TypeScript/issues/26792\n const { plugin, hint, elements, title, details } = this;\n return {\n message,\n name,\n plugin,\n hint,\n elements,\n title,\n details,\n stack,\n ...(this.cause instanceof Error && {\n cause: {\n name: this.cause.name,\n message: this.cause.message,\n stack: this.cause.stack,\n },\n }),\n };\n }\n}\n\n/**\n * @param {string} message\n * @param {string} pluginName Name of plugin that caused the error.\n * @param {object} [options]\n * @param {string} [options.hint] How to solve the error?\n * @param {HTMLElement[]} [options.elements] Offending elements.\n * @param {string} [options.title] Title attribute for offending elements. Can be a shorter form of the message.\n * @param {string} [options.details] Any further details/context.\n * @param {Error} [options.cause] The error that caused this one.\n */\nexport function showError(message, pluginName, options = {}) {\n const opts = { ...options, isWarning: false };\n pub(\"error\", new RespecError(message, pluginName, opts));\n}\n\n/**\n * @param {string} message\n * @param {string} pluginName Name of plugin that caused the error.\n * @param {object} [options]\n * @param {string} [options.hint] How to solve the error?\n * @param {HTMLElement[]} [options.elements] Offending elements.\n * @param {string} [options.title] Title attribute for offending elements. Can be a shorter form of the message.\n * @param {string} [options.details] Any further details/context.\n * @param {Error} [options.cause] The error that caused this one.\n */\nexport function showWarning(message, pluginName, options = {}) {\n const opts = { ...options, isWarning: true };\n pub(\"warn\", new RespecError(message, pluginName, opts));\n}\n\n/**\n * Creates showError, showWarning and amendConfiguration utilities for\n * use in custom pre-process and post-process plugins.\n * @param {string} pluginName\n */\nexport function makePluginUtils(pluginName) {\n /** @typedef {Parameters[2]} Options */\n return {\n /** @type {(configUpdates: Record) => void} */\n amendConfiguration: configUpdates =>\n pub(\"amend-user-config\", configUpdates),\n /** @type {(message: string, options?: Options) => void} */\n showError: (msg, options) => showError(msg, pluginName, options),\n /** @type {(message: string, options?: Options) => void} */\n showWarning: (msg, options) => showWarning(msg, pluginName, options),\n };\n}\n\n/**\n * Makes a string `coded`.\n *\n * @param {string} item\n * @returns {string}\n */\nexport function toMDCode(item) {\n return item ? `\\`${item}\\`` : \"\";\n}\n\n/**\n * Joins an array of strings, wrapping each string in back-ticks (`) for inline markdown code.\n *\n * @param {string[]} array\n * @param {object} options\n * @param {boolean} options.quotes Surround each item in quotes\n */\nexport function codedJoinOr(array, { quotes } = { quotes: false }) {\n return joinOr(array, quotes ? s => toMDCode(addQuotes(s)) : toMDCode);\n}\n\n/**\n * Wraps in back-ticks ` for code.\n *\n * @param {string[]} array\n * @param {object} options\n * @param {boolean} options.quotes Surround each item in quotes\n */\nexport function codedJoinAnd(array, { quotes } = { quotes: false }) {\n return joinAnd(array, quotes ? s => toMDCode(addQuotes(s)) : toMDCode);\n}\n\n/** @param {string} item */\nfunction addQuotes(item) {\n return String(item) ? `\"${item}\"` : \"\";\n}\n\n/**\n * Tagged template string, helps with linking to documentation.\n * Things inside [squareBrackets] are considered direct links to the documentation.\n * To alias something, one can use a \"|\", like [respecConfig|#respec-configuration].\n * @param {TemplateStringsArray} strings\n * @param {string[]} keys\n */\nexport function docLink(strings, ...keys) {\n const linkifiedStr = strings\n .map((s, i) => {\n const key = keys[i];\n if (!key) {\n return s;\n }\n // Linkables are wrapped in square brackets\n if (!key.startsWith(\"[\") && !key.endsWith(\"]\")) {\n return s + key;\n }\n\n const [linkingText, href] = key.slice(1, -1).split(\"|\");\n if (href) {\n const url = new URL(href, \"https://respec.org/docs/\");\n return `${s}[${linkingText}](${url})`;\n }\n return `${s}[\\`${linkingText}\\`](https://respec.org/docs/#${linkingText})`;\n })\n .join(\"\");\n return reindent(linkifiedStr);\n}\n\n/**\n * Takes a text string, trims it, splits it into lines,\n * finds the common indentation level, and then de-indents every line\n * by that common indentation level.\n *\n * @param {string} text - The text to be re-indented.\n * @returns {string} The re-indented text.\n */\nexport function reindent(text) {\n if (!text) {\n return text;\n }\n const lines = text.trimEnd().split(\"\\n\");\n while (lines.length && !lines[0].trim()) {\n lines.shift();\n }\n const indents = lines.filter(s => s.trim()).map(s => s.search(/[^\\s]/));\n const leastIndent = Math.min(...indents);\n return lines.map(s => s.slice(leastIndent)).join(\"\\n\");\n}\n","// @ts-check\n/**\n * Module core/pubsubhub\n *\n * Returns a singleton that can be used for message broadcasting\n * and message receiving. Replaces legacy \"msg\" code in ReSpec.\n */\nexport const name = \"core/pubsubhub\";\n\nimport { showError } from \"./utils.js\";\n\nconst subscriptions = new EventTarget();\n\n/**\n *\n * @param {EventTopic} topic\n * @param {any} detail\n */\nexport function pub(topic, detail) {\n subscriptions.dispatchEvent(new CustomEvent(topic, { detail }));\n if (window.parent === window.self) {\n return;\n }\n // If this is an iframe, postMessage parent (used in testing).\n const args = String(JSON.stringify(detail?.stack || detail));\n window.parent.postMessage({ topic, args }, window.parent.location.origin);\n}\n\n/**\n * Subscribes to a message type.\n * @param {EventTopic} topic The topic to subscribe to\n * @param {Function} cb Callback function\n * @param {Object} [options]\n * @param {Boolean} [options.once] Add prop \"once\" for single notification.\n * @return {void}\n */\nexport function sub(topic, cb, options = { once: false }) {\n /** @param {CustomEvent} ev */\n /**\n * @param {CustomEvent} ev\n */\n const listener = async ev => {\n try {\n await cb(ev.detail);\n } catch (err) {\n const error = /** @type {Error} */ (err);\n const msg = `Error in handler for topic \"${topic}\": ${error.message}`;\n showError(msg, `sub:${topic}`, { cause: error });\n }\n };\n subscriptions.addEventListener(topic, /** @type {any} */ (listener), options);\n}\n","// @ts-check\n// Module core/include-config\n// Inject's the document's configuration into the head as JSON.\nimport { sub } from \"./pubsubhub.js\";\nexport const name = \"core/include-config\";\n\nconst removeList = [\"githubToken\", \"githubUser\"];\n\n/**\n * @param {Conf} config\n */\nexport function run(config) {\n /** @type {Record} */\n const userConfig = {};\n /**\n * @param {Record} newValues\n */\n const amendConfig = newValues => Object.assign(userConfig, newValues);\n\n amendConfig(\n /** @type {Record} */ (/** @type {unknown} */ (config))\n );\n sub(\"amend-user-config\", amendConfig);\n\n sub(\"end-all\", () => {\n const script = document.createElement(\"script\");\n script.id = \"initialUserConfig\";\n script.type = \"application/json\";\n for (const prop of removeList) {\n if (prop in userConfig) delete userConfig[prop];\n }\n script.innerHTML = JSON.stringify(userConfig, null, 2);\n document.head.appendChild(script);\n });\n}\n","// @ts-check\n/**\n * module: core/exporter\n * Exports a ReSpec document, based on mime type, so it can be saved, etc.\n * Also performs cleanup, removing things that shouldn't be in published documents.\n * That is, elements that have a \"removeOnSave\" css class.\n */\n\nimport { removeCommentNodes, removeReSpec } from \"./utils.js\";\nimport { html } from \"./import-maps.js\";\nimport { pub } from \"./pubsubhub.js\";\n\nconst mimeTypes = new Map([\n [\"text/html\", \"html\"],\n [\"application/xml\", \"xml\"],\n]);\n\n/**\n * Creates a dataURI from a ReSpec document. It also cleans up the document\n * removing various things.\n *\n * @param {String} mimeType mimetype. one of `mimeTypes` above\n * @param {Document} doc document to export. useful for testing purposes\n * @returns a stringified data-uri of document that can be saved.\n */\nexport function rsDocToDataURL(mimeType, doc = document) {\n const format = mimeTypes.get(mimeType);\n if (!format) {\n const validTypes = [...mimeTypes.values()].join(\", \");\n const msg = `Invalid format: ${mimeType}. Expected one of: ${validTypes}.`;\n throw new TypeError(msg);\n }\n const data = serialize(format, doc);\n const encodedString = encodeURIComponent(data);\n return `data:${mimeType};charset=utf-8,${encodedString}`;\n}\n\n/**\n * @param {string} format\n * @param {Document} doc\n */\nexport function serialize(format, doc) {\n const cloneDoc = doc.cloneNode(true);\n cleanup(cloneDoc);\n let result = \"\";\n switch (format) {\n case \"xml\":\n result = new XMLSerializer().serializeToString(cloneDoc);\n break;\n default: {\n prettify(cloneDoc);\n if (cloneDoc.doctype) {\n result += new XMLSerializer().serializeToString(cloneDoc.doctype);\n }\n result += cloneDoc.documentElement.outerHTML;\n }\n }\n return result;\n}\n\n/**\n * @param {Document} cloneDoc\n */\nfunction cleanup(cloneDoc) {\n const { head, body, documentElement } = cloneDoc;\n removeCommentNodes(cloneDoc);\n\n cloneDoc\n .querySelectorAll(\".removeOnSave, #toc-nav\")\n .forEach((/** @type {Element} */ elem) => elem.remove());\n body.classList.remove(\"toc-sidebar\", \"toc-inline\");\n removeReSpec(documentElement);\n\n const insertions = cloneDoc.createDocumentFragment();\n\n // Move meta viewport, as it controls the rendering on mobile.\n const metaViewport = cloneDoc.querySelector(\"meta[name='viewport']\");\n if (metaViewport && head.firstChild !== metaViewport) {\n insertions.appendChild(metaViewport);\n }\n\n // Move charset to near top, as it needs to be in the first 512 bytes.\n /** @type {HTMLMetaElement} */\n const metaCharset =\n cloneDoc.querySelector(\"meta[charset], meta[content*='charset=']\") ||\n html``;\n insertions.appendChild(metaCharset);\n\n // Add meta generator\n const respecVersion = `ReSpec ${window.respecVersion || \"Developer Channel\"}`;\n const metaGenerator = html`\n \n `;\n\n insertions.appendChild(metaGenerator);\n head.prepend(insertions);\n pub(\"beforesave\", documentElement);\n}\n\n/** @param {Document} cloneDoc */\nfunction prettify(cloneDoc) {\n cloneDoc.querySelectorAll(\"style\").forEach(el => {\n el.innerHTML = `\\n${el.innerHTML}\\n`;\n });\n cloneDoc.querySelectorAll(\"head > *\").forEach(el => {\n el.outerHTML = `\\n${el.outerHTML}`;\n });\n}\n","/**\n * @param {string} path\n */\nexport async function fetchBase(path) {\n const response = await fetch(new URL(`../../${path}`, import.meta.url));\n return await response.text();\n}\n","// @ts-check\n/**\n * Module core/worker\n *\n * Exports a Web Worker for ReSpec, allowing for\n * multi-threaded processing of things.\n */\nexport const name = \"core/worker\";\n\n// Opportunistically preload syntax highlighter, which is used by the worker\nimport { createResourceHint } from \"./utils.js\";\nimport { fetchBase } from \"./text-loader.js\";\n\n// Derive the highlight URL from the ReSpec bundle location. In the IIFE bundle,\n// import.meta.url resolves to the script element's src (captured at load time).\nconst highlightHref = new URL(\"respec-highlight.js\", import.meta.url).href;\n\n// Canonical production URL used as the importScripts() fallback. This differs\n// from highlightHref because in source-module mode (dev server, headless tests)\n// import.meta.url resolves to the module file rather than the bundle, making\n// the derived URL wrong. The production URL is always correct for importScripts.\nconst PRODUCTION_HIGHLIGHT_URL =\n \"https://www.w3.org/Tools/respec/respec-highlight\";\n\n/** @type ResourceHintOption */\nconst hint = {\n hint: \"preload\",\n href: highlightHref,\n as: \"script\",\n};\nconst link = createResourceHint(hint);\ndocument.head.appendChild(link);\n\nasync function loadWorkerScript() {\n try {\n return (await import(\"text!../../worker/respec-worker.js\")).default;\n } catch {\n return fetchBase(\"worker/respec-worker.js\");\n }\n}\n\n/**\n * Fetch the highlight script in the main thread so it can be inlined directly\n * into the worker blob. This avoids importScripts() from a blob worker, which\n * Firefox blocks for cross-origin URLs in some environments.\n * Falls back to injecting the URL for importScripts() if the fetch fails\n * (e.g. cross-origin without CORS headers in production).\n */\nasync function fetchHighlightScript() {\n try {\n const response = await fetch(highlightHref);\n if (response.ok) return await response.text();\n } catch {\n // Network unavailable or CORS error — fall back to URL injection\n }\n return null;\n}\n\nasync function createWorker() {\n const [workerScript, highlightScript] = await Promise.all([\n loadWorkerScript(),\n fetchHighlightScript(),\n ]);\n\n // Inline the highlight script if fetched (no importScripts needed).\n // Fall back to the production URL for importScripts() if the fetch failed.\n const preamble =\n highlightScript !== null\n ? `${highlightScript}\\n`\n : `self.RESPEC_HIGHLIGHT_URL = \"${PRODUCTION_HIGHLIGHT_URL}\";\\n`;\n\n const blob = new Blob([preamble, workerScript], {\n type: \"application/javascript\",\n });\n return new Worker(URL.createObjectURL(blob));\n}\n\nexport const workerPromise = createWorker();\n","// @ts-check\n/**\n * This module adds a `respec` object to the `document` with the following\n * readonly properties:\n * - version: returns version of ReSpec Script.\n * - ready: returns a promise that settles when ReSpec finishes processing.\n * - worker: returns a promise that resolves to the ReSpec Web Worker.\n *\n */\nimport { serialize } from \"../core/exporter.js\";\nimport { sub } from \"./pubsubhub.js\";\nimport { workerPromise } from \"./worker.js\";\n\nexport const name = \"core/respec-global\";\n\nclass ReSpec {\n constructor() {\n /** @type {Promise} */\n this._respecDonePromise = new Promise(resolve => {\n sub(\"end-all\", () => resolve(), { once: true });\n });\n\n /** @type {any[]} */\n this.errors = [];\n /** @type {any[]} */\n this.warnings = [];\n\n sub(\"error\", (/** @type {any} */ rsError) => {\n console.error(rsError, rsError.toJSON());\n this.errors.push(rsError);\n });\n sub(\"warn\", (/** @type {any} */ rsError) => {\n console.warn(rsError, rsError.toJSON());\n this.warnings.push(rsError);\n });\n }\n\n get version() {\n return window.respecVersion;\n }\n\n get ready() {\n return this._respecDonePromise;\n }\n\n get worker() {\n return workerPromise;\n }\n\n async toHTML() {\n return serialize(\"html\", document);\n }\n}\n\nexport function init() {\n const respec = new ReSpec();\n Object.defineProperty(document, \"respec\", { value: respec });\n}\n","// @ts-check\n/**\n * Module core/post-process\n *\n * Corresponds to respecConfig.postProcess and config.afterEnd.\n * - postProcess: an array of functions that get called\n * after processing finishes. This is not recommended and the feature is not\n * tested. Use with care, if you know what you're doing. Chances are you really\n * want to be using a new module with your own profile.\n * - afterEnd: final thing that is called.\n */\nimport { makePluginUtils, showError } from \"./utils.js\";\n\nexport const name = \"core/post-process\";\n\nconst TIMEOUT = 15000;\n\n/**\n * @param {Promise | void} task\n * @param {string} label\n */\nfunction withTimeout(task, label) {\n return new Promise((resolve, reject) => {\n const timerId = setTimeout(() => {\n reject(new Error(`${label} timed out.`));\n }, TIMEOUT);\n Promise.resolve(task)\n .then(resolve, reject)\n .finally(() => {\n clearTimeout(timerId);\n });\n });\n}\n\n/**\n * @param {Conf} config\n */\nexport async function run(config) {\n if (Array.isArray(config.postProcess)) {\n const functions = config.postProcess.filter((/** @type {any} */ f) => {\n const isFunction = typeof f === \"function\";\n if (!isFunction) {\n const msg = \"Every item in `postProcess` must be a JS function.\";\n showError(msg, name);\n }\n return isFunction;\n });\n for (const [i, f] of functions.entries()) {\n const fnName = `${name}/${f.name || `[${i}]`}`;\n const utils = makePluginUtils(fnName);\n try {\n await withTimeout(\n f(config, document, utils),\n `postProcess function \"${fnName}\"`\n );\n } catch (err) {\n const msg = `Function ${fnName} threw an error during \\`postProcess\\`.`;\n const hint = \"See developer console.\";\n showError(msg, name, { hint, cause: /** @type {Error} */ (err) });\n }\n }\n }\n if (typeof config.afterEnd === \"function\") {\n try {\n await withTimeout(config.afterEnd(config, document), \"config.afterEnd\");\n } catch (err) {\n const msg = \"Function afterEnd threw an error.\";\n const hint = \"See developer console.\";\n showError(msg, name, { hint, cause: /** @type {Error} */ (err) });\n }\n }\n}\n","// @ts-check\n/**\n * Module core/pre-process\n *\n * Corresponds to respecConfig.preProcess.\n * - preProcess: an array of functions that get called\n * before anything else happens. This is not recommended and the feature is not\n * tested. Use with care, if you know what you're doing. Chances are you really\n * want to be using a new module with your own profile\n */\nimport { makePluginUtils, showError } from \"./utils.js\";\n\nexport const name = \"core/pre-process\";\n\nconst TIMEOUT = 15000;\n\n/**\n * @param {Conf} config\n */\nexport async function run(config) {\n if (Array.isArray(config.preProcess)) {\n const functions = config.preProcess.filter((/** @type {any} */ f) => {\n const isFunction = typeof f === \"function\";\n if (!isFunction) {\n const msg = \"Every item in `preProcess` must be a JS function.\";\n showError(msg, name);\n }\n return isFunction;\n });\n for (const [i, f] of functions.entries()) {\n const fnName = `${name}/${f.name || `[${i}]`}`;\n const utils = makePluginUtils(fnName);\n try {\n await new Promise((resolve, reject) => {\n const timerId = setTimeout(() => {\n reject(new Error(`preProcess function \"${fnName}\" timed out.`));\n }, TIMEOUT);\n Promise.resolve()\n .then(() => f(config, document, utils))\n .then(resolve, reject)\n .finally(() => {\n clearTimeout(timerId);\n });\n });\n } catch (err) {\n const msg = `Function ${fnName} threw an error during \\`preProcess\\`.`;\n const hint = \"See developer console.\";\n showError(msg, name, { hint, cause: /** @type {Error} */ (err) });\n }\n }\n }\n}\n","// @ts-check\n// Module core/base-runner\n// The module in charge of running the whole processing pipeline.\nimport { run as includeConfig } from \"./include-config.js\";\nimport { init as initReSpecGlobal } from \"./respec-global.js\";\nimport { run as overrideConfig } from \"./override-configuration.js\";\nimport { run as postProcess } from \"./post-process.js\";\nimport { run as preProcess } from \"./pre-process.js\";\nimport { pub } from \"./pubsubhub.js\";\nimport { removeReSpec } from \"./utils.js\";\n\nexport const name = \"core/base-runner\";\n\n/**\n * @param {ReSpecPlugin[]} plugs\n */\nexport async function runAll(plugs) {\n initReSpecGlobal();\n\n pub(\"start-all\", respecConfig);\n includeConfig(respecConfig);\n overrideConfig(respecConfig);\n performance.mark(`${name}-start`);\n await preProcess(respecConfig);\n\n const runnables = plugs.filter(p => isRunnableModule(p));\n runnables.forEach(\n plug => !plug.name && console.warn(\"Plugin lacks name:\", plug)\n );\n await executePreparePass(runnables, respecConfig);\n await executeRunPass(runnables, respecConfig);\n pub(\"plugins-done\", respecConfig);\n\n await postProcess(respecConfig);\n pub(\"end-all\", undefined);\n removeReSpec(document);\n performance.mark(`${name}-end`);\n performance.measure(name, `${name}-start`, `${name}-end`);\n}\n\n/**\n * @param {ReSpecPlugin} plug\n */\nfunction isRunnableModule(plug) {\n return plug && (plug.run || plug.Plugin);\n}\n\n/**\n * @param {ReSpecPlugin[]} runnables\n * @param {Conf} config\n */\nasync function executePreparePass(runnables, config) {\n for (const plug of runnables.filter(p => p.prepare)) {\n try {\n await plug.prepare?.(config);\n } catch (err) {\n console.error(err);\n }\n }\n}\n\n/**\n * @param {ReSpecPlugin[]} runnables\n * @param {Conf} config\n */\nasync function executeRunPass(runnables, config) {\n for (const plug of runnables) {\n const name = plug.name || \"\";\n\n try {\n // eslint-disable-next-line no-async-promise-executor\n await new Promise(async (resolve, reject) => {\n const timerId = setTimeout(() => {\n const msg = `Plugin ${name} took too long.`;\n console.error(msg, plug);\n reject(new Error(msg));\n }, 15000);\n\n performance.mark(`${name}-start`);\n try {\n if (plug.Plugin) {\n await new plug.Plugin(config).run();\n resolve(undefined);\n } else if (plug.run) {\n await plug.run(config);\n resolve(undefined);\n }\n } catch (err) {\n reject(err);\n } finally {\n clearTimeout(timerId);\n performance.mark(`${name}-end`);\n performance.measure(name, `${name}-start`, `${name}-end`);\n }\n });\n } catch (error) {\n console.error(error);\n }\n }\n}\n","// @ts-check\n// Module core/override-configuration\n// A helper module that makes it possible to override settings specified in respecConfig\n// by passing them as a query string. This is useful when you just want to make a few\n// tweaks to a document before generating the snapshot, without mucking with the source.\n// For example, you can change the status and date by appending:\n// ?specStatus=LC&publishDate=2012-03-15\nimport { pub } from \"./pubsubhub.js\";\n\nexport const name = \"core/override-configuration\";\n\n/**\n * @param {Conf} config\n */\nexport function run(config) {\n const params = new URLSearchParams(document.location.search);\n const overrideEntries = Array.from(params)\n .filter(([key, value]) => !!key && !!value)\n .map(([codedKey, codedValue]) => {\n const key = decodeURIComponent(codedKey);\n const decodedValue = decodeURIComponent(codedValue.replace(/%3D/g, \"=\"));\n let value;\n try {\n value = JSON.parse(decodedValue);\n } catch {\n value = decodedValue;\n }\n return [key, value];\n });\n const overrideProps = Object.fromEntries(overrideEntries);\n Object.assign(config, overrideProps);\n pub(\"amend-user-config\", overrideProps);\n}\n","const css = String.raw;\n\n// Prettier ignore only to keep code indented from level 0.\n// prettier-ignore\nexport default css`\n.respec-modal .close-button {\n position: absolute;\n z-index: inherit;\n padding: 0.2em;\n font-weight: bold;\n cursor: pointer;\n margin-left: 5px;\n border: none;\n background: transparent;\n}\n\n#respec-ui {\n position: fixed;\n display: flex;\n flex-direction: row-reverse;\n top: 20px;\n right: 20px;\n width: 202px;\n text-align: right;\n z-index: 9000;\n}\n\n\n#respec-pill,\n.respec-info-button {\n height: 2.4em;\n background: #fff;\n background: var(--bg, #fff);\n color: rgb(120, 120, 120);\n color: var(--tocnav-normal-text, rgb(120, 120, 120));\n border: 1px solid #ccc;\n box-shadow: 1px 1px 8px 0 rgba(100, 100, 100, 0.5);\n box-shadow: 1px 1px 8px 0 var(--tocsidebar-shadow, rgba(100, 100, 100, 0.5));\n padding: 0.2em 0em;\n}\n\n.respec-info-button {\n border: none;\n opacity: 0.75;\n border-radius: 2em;\n margin-right: 1em;\n min-width: 3.5em;\n will-change: opacity;\n}\n\n.respec-info-button:focus,\n.respec-info-button:hover {\n opacity: 1;\n transition: opacity 0.2s;\n}\n\n#respec-pill {\n width: 4.8em;\n}\n\n#respec-pill:not(:disabled) {\n animation: respec-fadein 0.6s ease-in-out;\n}\n\n@keyframes respec-fadein {\n from {\n margin-top: -1.2em;\n border-radius: 50%;\n border: 0.2em solid rgba(100, 100, 100, 0.5);\n box-shadow: none;\n height: 4.8em;\n }\n to {\n margin-top: 0;\n border: 1px solid #ccc;\n border-radius: 0;\n box-shadow: 1px 1px 8px 0 rgba(100, 100, 100, 0.5);\n height: 2.4em;\n }\n}\n\n#respec-pill:disabled {\n margin-top: -1.2em;\n position: relative;\n border: none;\n box-shadow: none;\n border-radius: 50%;\n width: 4.8em;\n height: 4.8em;\n padding: 0;\n}\n\n#respec-pill:disabled::after {\n position: absolute;\n content: '';\n inset: -0.2em;\n border-radius: 50%;\n border: 0.2em solid rgba(100, 100, 100, 0.5);\n border-left: 0.2em solid transparent;\n animation: respec-spin 0.5s infinite linear;\n}\n\n@media (prefers-reduced-motion) {\n #respec-pill:not(:disabled) {\n animation: none;\n }\n\n #respec-pill:disabled::after {\n animation: none;\n border-left: 0.2em solid rgba(100, 100, 100, 0.5);\n }\n}\n\n@keyframes respec-spin {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n}\n\n.respec-hidden {\n visibility: hidden;\n opacity: 0;\n transition: visibility 0s 0.2s, opacity 0.2s linear;\n}\n\n.respec-visible {\n visibility: visible;\n opacity: 1;\n transition: opacity 0.2s linear;\n}\n\n#respec-pill:hover,\n#respec-pill:focus {\n color: rgb(0, 0, 0);\n background-color: rgb(245, 245, 245);\n transition: color 0.2s;\n}\n\n#respec-menu {\n position: absolute;\n margin: 0;\n padding: 0;\n font-family: sans-serif;\n background: var(--bg, #fff);\n color: var(--text, black);\n box-shadow: 1px 1px 8px 0 rgba(100, 100, 100, 0.5);\n width: 200px;\n display: none;\n text-align: left;\n margin-top: 32px;\n font-size: 0.8em;\n}\n\n#respec-menu:not([hidden]) {\n display: block;\n}\n\n#respec-menu li {\n list-style-type: none;\n margin: 0;\n padding: 0;\n}\n\n.respec-save-buttons {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(47%, 2fr));\n grid-gap: 0.5cm;\n padding: 0.5cm;\n}\n\n.respec-save-button:link {\n padding-top: 16px;\n color: var(--def-text, white);\n background: var(--def-bg, rgb(42, 90, 168));\n justify-self: stretch;\n height: 1cm;\n text-decoration: none;\n text-align: center;\n font-size: inherit;\n border: none;\n border-radius: 0.2cm;\n}\n\n.respec-save-button:link:hover {\n color: var(--def-text, white);\n background: var(--defrow-border, rgb(42, 90, 168));\n padding: 0;\n margin: 0;\n border: 0;\n padding-top: 16px;\n}\n\n.respec-save-button:link:focus {\n background: var(--tocnav-active-bg, #193766);\n color: var(--tocnav-active-text, black);\n}\n\n#respec-ui button:focus,\n#respec-pill:focus,\n.respec-option:focus {\n outline: 0;\n outline-style: none;\n}\n\n#respec-pill-error {\n background-color: red;\n color: white;\n}\n\n#respec-pill-warning {\n background-color: orange;\n color: white;\n}\n\n.respec-warning-list,\n.respec-error-list {\n margin: 0;\n padding: 0;\n font-family: sans-serif;\n font-size: 0.85em;\n}\n\n.respec-warning-list {\n background-color: rgb(255, 251, 230);\n}\n\n:is(.respec-warning-list,.respec-error-list) > li {\n list-style-type: none;\n margin: 0;\n padding: .5em 0;\n padding-left: 2em;\n padding-right: .5em;\n}\n\n:is(.respec-warning-list,.respec-error-list) > li + li {\n margin-top: 0.5rem;\n}\n\n:is(.respec-warning-list,.respec-error-list) > li:before {\n position: absolute;\n left: .4em;\n}\n\n:is(.respec-warning-list,.respec-error-list) p {\n padding: 0;\n margin: 0;\n}\n\n.respec-warning-list > li {\n color: rgb(92, 59, 0);\n border-bottom: thin solid rgb(255, 245, 194);\n}\n\n.respec-error-list,\n.respec-error-list li {\n background-color: rgb(255, 240, 240);\n}\n\n.respec-warning-list > li::before {\n content: \"⚠️\";\n}\n\n.respec-error-list > li::before {\n content: \"💥\";\n}\n\n.respec-error-list > li {\n color: rgb(92, 59, 0);\n border-bottom: thin solid rgb(255, 215, 215);\n}\n\n:is(.respec-warning-list,.respec-error-list) > li li {\n list-style: disc;\n}\n\n#respec-overlay {\n display: block;\n position: fixed;\n z-index: 10000;\n top: 0px;\n left: 0px;\n height: 100%;\n width: 100%;\n background: #000;\n}\n\n.respec-show-overlay {\n transition: opacity 0.2s linear;\n opacity: 0.5;\n}\n\n.respec-hide-overlay {\n transition: opacity 0.2s linear;\n opacity: 0;\n}\n\n.respec-modal {\n display: block;\n position: fixed;\n z-index: 11000;\n top: 10%;\n background: var(--bg, #fff);\n color: var(--text, black);\n border: 5px solid #666;\n border-color: var(--tocsidebar-shadow, #666);\n min-width: 20%;\n padding: 0;\n max-height: 80%;\n overflow-y: auto;\n margin: 0 -0.5cm;\n left: 20%;\n max-width: 75%;\n min-width: 60%;\n}\n\n\n.respec-modal h3 {\n margin: 0;\n padding: 0.2em;\n left: 0 !important;\n text-align: center;\n background: var(--tocsidebar-shadow, #ddd);\n color: var(--text, black);\n font-size: 1em;\n}\n\n#respec-menu button.respec-option {\n background: var(--bg, white);\n color: var(--text, black);\n border: none;\n width: 100%;\n text-align: left;\n font-size: inherit;\n padding: 1.2em 1.2em;\n}\n\n#respec-menu button.respec-option:hover {\n background-color: var(--tocnav-hover-bg, #eee);\n color: var(--tocnav-hover-text, black);\n}\n\n.respec-cmd-icon {\n padding-right: 0.5em;\n}\n\n#respec-ui button.respec-option:first-child {\n margin-top: 0;\n}\n#respec-ui button.respec-option:last-child {\n border: none;\n border-radius: inherit;\n margin-bottom: 0;\n}\n\n.respec-button-copy-paste {\n position: absolute;\n height: 28px;\n width: 40px;\n cursor: pointer;\n background-image: linear-gradient(#fcfcfc, #eee);\n border: 1px solid rgb(144, 184, 222);\n border-left: 0;\n border-radius: 0px 0px 3px 0;\n -webkit-user-select: none;\n user-select: none;\n -webkit-appearance: none;\n top: 0;\n left: 127px;\n}\n\n@media print {\n #respec-ui {\n display: none;\n }\n}\n\n.respec-iframe {\n width: 100%;\n min-height: 550px;\n height: 100%;\n overflow: hidden;\n padding: 0;\n margin: 0;\n border: 0;\n}\n\n.respec-iframe:not(.ready) {\n background: url(\"https://respec.org/xref/loader.gif\") no-repeat center;\n}\n\n.respec-iframe + a[href] {\n font-size: 0.9rem;\n float: right;\n margin: 0 0.5em 0.5em;\n border-bottom-width: 1px;\n}\n\np:is(.respec-hint,.respec-occurrences) {\n display: block;\n margin-top: 0.5em;\n}\n\n.respec-plugin {\n text-align: right;\n color: rgb(120, 120, 120, .5);\n font-size: 0.6em;\n}\n`;\n","// @ts-check\n/**\n * Module core/markdown\n * Handles the optional markdown processing.\n *\n * Markdown support is optional. It is enabled by setting the `format`\n * property of the configuration object to \"markdown.\"\n *\n * We use marked for parsing Markdown:\n * https://github.com/markedjs/marked\n *\n */\n\nimport { getElementIndentation } from \"./utils.js\";\nimport { marked } from \"./import-maps.js\";\n\nexport const name = \"core/markdown\";\n\nconst gtEntity = />/gm;\nconst ampEntity = /&/gm;\n\nclass Renderer extends marked.Renderer {\n /**\n * @param {import('marked').Tokens.Code} token\n * @returns {string}\n */\n // @ts-expect-error - our token signature is compatible at runtime; marked's d.ts is minified\n code(token) {\n const { text: code, lang: infoString = \"\" } = token;\n const { language, ...metaData } = Renderer.parseInfoString(infoString);\n\n // regex to check whether the language is webidl\n if (/(^webidl$)/i.test(language)) {\n return `
    ${code}
    `;\n }\n\n const html = super\n .code(/** @type {any} */ ({ ...token, lang: language }))\n .replace(`class=\"language-`, `class=\"`);\n\n const { example, illegalExample } = metaData;\n if (!example && !illegalExample) return html;\n\n const title = example || illegalExample;\n const className = `${language} ${example ? \"example\" : \"illegal-example\"}`;\n return html.replace(\"
    \", `
    `);\n  }\n\n  /**\n   * @param {import('marked').Tokens.Image} token\n   */\n  image(token) {\n    const { href, title, text } = token;\n    if (!title) {\n      return super.image(token);\n    }\n    const html = String.raw;\n    return html`\n      
    \n \"${text}\"\n
    ${title}
    \n
    \n `;\n }\n\n /**\n * @param {string} infoString\n */\n static parseInfoString(infoString) {\n const firstSpace = infoString.search(/\\s/);\n if (firstSpace === -1) {\n return { language: infoString };\n }\n\n const language = infoString.slice(0, firstSpace);\n const metaDataStr = infoString.slice(firstSpace + 1);\n let metaData;\n if (metaDataStr) {\n try {\n metaData = JSON.parse(`{ ${metaDataStr} }`);\n } catch (error) {\n console.error(error);\n }\n }\n\n return { language, ...metaData };\n }\n\n /**\n * @param {import('marked').Tokens.Heading} token\n */\n heading(token) {\n const text = this.parser.parseInline(token.tokens);\n const level = token.depth;\n const headingWithIdRegex = /(.+)\\s+{#([\\w-]+)}$/;\n const match = text.match(headingWithIdRegex);\n if (match) {\n const [, textContent, id] = match;\n return `${textContent}`;\n }\n return super.heading(token);\n }\n}\n\n/** @type {import('marked').MarkedOptions} */\nconst config = {\n gfm: true,\n renderer: /** @type {any} */ (new Renderer()),\n};\n\n/**\n * Normalize indentation by stripping the leading whitespace determined from\n * the first non-empty line. This handles mixed-indentation HTML content where\n * some lines (e.g., from rendered markdown inside sections) may have less\n * indentation than the outer HTML structure. marked v16 is stricter: HTML\n * indented 4+ spaces is treated as a code block, so we must strip outer\n * indentation even when inner content has lines at column 0.\n * @param {string} text\n */\nfunction normalizeIndent(text) {\n if (!text) return text;\n const lines = text.trimEnd().split(\"\\n\");\n const firstNonEmpty = lines.findIndex(l => l.trim());\n if (firstNonEmpty === -1) return \"\";\n const nonEmptyLines = lines.slice(firstNonEmpty);\n const firstIndent = nonEmptyLines[0].search(/[^\\s]/);\n if (firstIndent < 1) return nonEmptyLines.join(\"\\n\");\n const prefix = \" \".repeat(firstIndent);\n return nonEmptyLines\n .map(s => (s.startsWith(prefix) ? s.slice(firstIndent) : s))\n .join(\"\\n\");\n}\n\n/**\n * @param {string} text\n * @param {object} options\n * @param {boolean} options.inline\n */\nexport function markdownToHtml(text, options = { inline: false }) {\n const normalizedLeftPad = normalizeIndent(text);\n // As markdown is pulled from HTML, > and & are already escaped and\n // so blockquotes aren't picked up by the parser. This fixes it.\n const potentialMarkdown = normalizedLeftPad\n .replace(gtEntity, \">\")\n .replace(ampEntity, \"&\");\n\n const result = options.inline\n ? marked.parseInline(potentialMarkdown, config)\n : marked.parse(potentialMarkdown, config);\n return result;\n}\n\n/**\n * @param {string} selector\n * @return {(el: Element) => Element[]}\n */\nfunction convertElements(selector) {\n return element => {\n const elements = element.querySelectorAll(selector);\n elements.forEach(convertElement);\n return Array.from(elements);\n };\n}\n\n/**\n * @param {Element} element\n */\nfunction convertElement(element) {\n for (const pre of element.getElementsByTagName(\"pre\")) {\n // HTML parser implicitly removes a newline after
    \n    // which breaks reindentation algorithm\n    pre.prepend(\"\\n\");\n  }\n  element.innerHTML = markdownToHtml(element.innerHTML);\n}\n\n/**\n * CommonMark requires additional empty newlines between markdown and HTML lines.\n * This function adds them as a backward compatibility workaround.\n * @param {HTMLElement} element\n * @param {string} selector\n */\nfunction workaroundBlockLevelMarkdown(element, selector) {\n  /** @type {NodeListOf} */\n  const elements = element.querySelectorAll(selector);\n  for (const element of elements) {\n    const { innerHTML } = element;\n    if (/^<\\w/.test(innerHTML.trimStart())) {\n      // if the block content starts with HTML-like format\n      // then assume it doesn't need a workaround\n      continue;\n    }\n    // Double newlines are needed to be parsed as Markdown\n    const lines = innerHTML.split(\"\\n\");\n    const firstTwo = lines.slice(0, 2).join(\"\\n\");\n    const lastTwo = lines.slice(-2).join(\"\\n\");\n    if (firstTwo.trim()) {\n      element.prepend(\"\\n\\n\");\n    }\n    if (lastTwo.trim()) {\n      // keep the indentation of the end tag\n      const indentation = getElementIndentation(element);\n      element.append(`\\n\\n${indentation}`);\n    }\n  }\n}\n\n/**\n * @param {Iterable} elements\n */\nfunction substituteWithTextNodes(elements) {\n  Array.from(elements).forEach(element => {\n    element.replaceWith(element.textContent);\n  });\n}\n\nconst processMDSections = convertElements(\"[data-format='markdown']:not(body)\");\nconst blockLevelElements =\n  \"[data-format=markdown], section, div, address, article, aside, figure, header, main\";\n\n/**\n * @param {Conf} conf\n */\nexport function run(conf) {\n  const hasMDSections = !!document.querySelector(\n    \"[data-format=markdown]:not(body)\"\n  );\n  const isMDFormat = conf.format === \"markdown\";\n  if (!isMDFormat && !hasMDSections) {\n    return; // Nothing to be done\n  }\n  // Only has markdown-format sections\n  if (!isMDFormat) {\n    processMDSections(document.body);\n    return;\n  }\n  // We transplant the UI to do the markdown processing\n  const rsUI = document.getElementById(\"respec-ui\");\n  rsUI?.remove();\n  // The new body will replace the old body\n  const newBody = document.body.cloneNode(true);\n  // Marked expects markdown be flush against the left margin\n  // so we need to normalize the inner text of some block\n  // elements.\n  workaroundBlockLevelMarkdown(newBody, blockLevelElements);\n  convertElement(newBody);\n  // Remove links where class .nolinks\n  substituteWithTextNodes(newBody.querySelectorAll(\".nolinks a[href]\"));\n  // Frankenstein the whole thing back together\n  if (rsUI) newBody.append(rsUI);\n  document.body.replaceWith(newBody);\n}\n","// @ts-check\n// Module core/ui\n// Handles the ReSpec UI\n// XXX TODO\n//  - look at other UI things to add\n//      - list issues\n//      - lint: validator, link checker, check WebIDL, ID references\n//      - save to GitHub\n//  - make a release candidate that people can test\n//  - once we have something decent, merge, ship as 3.2.0\nimport { html, pluralize } from \"./import-maps.js\";\nimport { reindent, xmlEscape } from \"./utils.js\";\nimport css from \"../styles/ui.css.js\";\nimport { markdownToHtml } from \"./markdown.js\";\nimport { sub } from \"./pubsubhub.js\";\nexport const name = \"core/ui\";\n\n// Opportunistically inserts the style, with the chance to reduce some FOUC\ninsertStyle();\n\nfunction insertStyle() {\n  const styleElement = document.createElement(\"style\");\n  styleElement.id = \"respec-ui-styles\";\n  styleElement.textContent = css;\n  styleElement.classList.add(\"removeOnSave\");\n  document.head.appendChild(styleElement);\n  return styleElement;\n}\n\n/**\n * @param {Element | null | undefined} elem\n * @param {Map} ariaMap\n */\nfunction ariaDecorate(elem, ariaMap) {\n  if (!elem) {\n    return;\n  }\n  Array.from(ariaMap).forEach(([name, value]) => {\n    elem.setAttribute(`aria-${name}`, value);\n  });\n}\n\nconst respecUI = html``;\nconst menu = html``;\nconst closeButton = html` ui.closeModal()}\n  title=\"Close\"\n>\n  ❌\n`;\nwindow.addEventListener(\"load\", () => trapFocus(menu));\n/** @type {HTMLElement | null} */\nlet modal;\n/** @type {HTMLElement | null} */\nlet overlay;\n/** @type {any[]} */\nconst errors = [];\n/** @type {any[]} */\nconst warnings = [];\n/** @type {Record} */\nconst buttons = {};\n\nsub(\"start-all\", () => document.body.prepend(respecUI), { once: true });\nsub(\"end-all\", () => document.body.prepend(respecUI), { once: true });\n\nconst respecPill = html``;\nrespecUI.appendChild(respecPill);\nrespecPill.addEventListener(\n  \"click\",\n  /** @param {MouseEvent} e */ e => {\n    e.stopPropagation();\n    respecPill.setAttribute(\"aria-expanded\", String(menu.hidden));\n    toggleMenu();\n    menu.querySelector(\"li:first-child button\").focus();\n  }\n);\n\ndocument.documentElement.addEventListener(\"click\", () => {\n  if (!menu.hidden) {\n    toggleMenu();\n  }\n});\nrespecUI.appendChild(menu);\n\nmenu.addEventListener(\n  \"keydown\",\n  /** @param {KeyboardEvent} e */ e => {\n    if (e.key === \"Escape\" && !menu.hidden) {\n      respecPill.setAttribute(\"aria-expanded\", String(menu.hidden));\n      toggleMenu();\n      respecPill.focus();\n    }\n  }\n);\n\nfunction toggleMenu() {\n  menu.classList.toggle(\"respec-hidden\");\n  menu.classList.toggle(\"respec-visible\");\n  menu.hidden = !menu.hidden;\n}\n\n/**\n * @param {Element} element\n */\nfunction trapFocus(element) {\n  /** @type {NodeListOf} */\n  const focusableEls = element.querySelectorAll(\n    \"a[href]:not([disabled]), button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled])\"\n  );\n  const firstFocusableEl = focusableEls[0];\n  const lastFocusableEl = focusableEls[focusableEls.length - 1];\n  if (firstFocusableEl) {\n    firstFocusableEl.focus();\n  }\n  element.addEventListener(\"keydown\", e => {\n    const keyEvent = /** @type {KeyboardEvent} */ (e);\n    if (keyEvent.key !== \"Tab\") {\n      return;\n    }\n    // shift + tab\n    if (keyEvent.shiftKey) {\n      if (document.activeElement === firstFocusableEl) {\n        lastFocusableEl.focus();\n        keyEvent.preventDefault();\n      }\n    }\n    // tab\n    else if (document.activeElement === lastFocusableEl) {\n      firstFocusableEl.focus();\n      keyEvent.preventDefault();\n    }\n  });\n}\n\nconst ariaMap = new Map([\n  [\"controls\", \"respec-menu\"],\n  [\"expanded\", \"false\"],\n  [\"haspopup\", \"true\"],\n  [\"label\", \"ReSpec Menu\"],\n]);\nariaDecorate(respecPill, ariaMap);\n\n/**\n * @param {RespecError | string} err\n * @param {(RespecError | string)[]} arr\n * @param {string} butName\n * @param {string} title\n */\nfunction errWarn(err, arr, butName, title) {\n  arr.push(err);\n  if (!buttons.hasOwnProperty(butName)) {\n    buttons[butName] = createWarnButton(butName, arr, title);\n    respecUI.appendChild(buttons[butName]);\n  }\n  const button = buttons[butName];\n  button.textContent = String(arr.length);\n  const label = arr.length === 1 ? pluralize.singular(title) : title;\n  const ariaMap = new Map([[\"label\", `${arr.length} ${label}`]]);\n  ariaDecorate(button, ariaMap);\n}\n\n/**\n * @param {string} butName\n * @param {any[]} arr\n * @param {string} title\n */\nfunction createWarnButton(butName, arr, title) {\n  const buttonId = `respec-pill-${butName}`;\n  const button = html``;\n  button.addEventListener(\"click\", () => {\n    button.setAttribute(\"aria-expanded\", \"true\");\n    const ol = html`
      `;\n for (const err of arr) {\n const fragment = document\n .createRange()\n .createContextualFragment(rsErrorToHTML(err));\n const li = document.createElement(\"li\");\n // if it's only a single element, just copy the contents into li\n if (fragment.firstElementChild === fragment.lastElementChild) {\n li.append(\n .../** @type {Element} */ (fragment.firstElementChild).childNodes\n );\n // Otherwise, take everything.\n } else {\n li.appendChild(fragment);\n }\n ol.appendChild(li);\n }\n ui.freshModal(title, ol, button);\n });\n const ariaMap = new Map([\n [\"expanded\", \"false\"],\n [\"haspopup\", \"true\"],\n [\"controls\", `respec-pill-${butName}-modal`],\n ]);\n ariaDecorate(button, ariaMap);\n return button;\n}\n\nexport const ui = {\n show() {\n try {\n respecUI.hidden = false;\n } catch (err) {\n console.error(err);\n }\n },\n hide() {\n respecUI.hidden = true;\n },\n enable() {\n respecPill.removeAttribute(\"disabled\");\n },\n /**\n * @param {string} _keyShort shortcut key. unused - kept for backward compatibility.\n * @param {string} label\n * @param {Function} handler\n * @param {string} icon\n */\n addCommand(label, handler, _keyShort, icon) {\n icon = icon || \"\";\n const id = `respec-button-${label.toLowerCase().replace(/\\s+/, \"-\")}`;\n const button = html``;\n const menuItem = html`
    1. ${button}
    2. `;\n menuItem.addEventListener(\"click\", handler);\n menu.appendChild(menuItem);\n return button;\n },\n /** @param {RespecError | string} rsError */\n error(rsError) {\n errWarn(rsError, errors, \"error\", \"ReSpec Errors\");\n },\n /** @param {RespecError | string} rsError */\n warning(rsError) {\n errWarn(rsError, warnings, \"warning\", \"ReSpec Warnings\");\n },\n /** @param {Element} [owner] */\n closeModal(owner) {\n if (overlay) {\n const overlayElem = overlay;\n overlayElem.classList.remove(\"respec-show-overlay\");\n overlayElem.classList.add(\"respec-hide-overlay\");\n overlayElem.addEventListener(\"transitionend\", () => {\n overlayElem.remove();\n overlay = null;\n });\n }\n if (owner) {\n owner.setAttribute(\"aria-expanded\", \"false\");\n }\n if (!modal) return;\n modal.remove();\n modal = null;\n respecPill.focus();\n },\n /**\n * @param {string} title\n * @param {Node} content\n * @param {HTMLElement} currentOwner\n */\n freshModal(title, content, currentOwner) {\n if (modal) modal.remove();\n if (overlay) overlay.remove();\n overlay = html`
      `;\n const id = `${currentOwner.id}-modal`;\n const headingId = `${id}-heading`;\n modal = html`\n ${closeButton}\n

      ${title}

      \n
      ${content}
      \n `;\n const ariaMap = new Map([[\"labelledby\", headingId]]);\n ariaDecorate(modal, ariaMap);\n document.body.append(\n /** @type {HTMLElement} */ (overlay),\n /** @type {HTMLElement} */ (modal)\n );\n /** @type {HTMLElement} */ (overlay).addEventListener(\"click\", () =>\n this.closeModal(currentOwner)\n );\n /** @type {HTMLElement} */ (overlay).classList.toggle(\n \"respec-show-overlay\"\n );\n /** @type {HTMLElement} */ (modal).hidden = false;\n trapFocus(/** @type {HTMLElement} */ (modal));\n },\n};\ndocument.addEventListener(\"keydown\", ev => {\n if (ev.key === \"Escape\") {\n ui.closeModal();\n }\n});\nwindow.respecUI = ui;\nsub(\n \"error\",\n /** @param {RespecError | string} details */ details => ui.error(details)\n);\nsub(\n \"warn\",\n /** @param {RespecError | string} details */ details => ui.warning(details)\n);\n\n/**\n * @param {RespecError | string} err\n */\nfunction rsErrorToHTML(err) {\n if (typeof err === \"string\") {\n return err;\n }\n\n const plugin = err.plugin\n ? `

      (plugin: \"${err.plugin}\")

      `\n : \"\";\n\n const hint = err.hint\n ? `\\n${markdownToHtml(\n `

      How to fix: ${reindent(\n err.hint\n )}`,\n {\n inline: !err.hint.includes(\"\\n\"),\n }\n )}\\n`\n : \"\";\n const elements = Array.isArray(err.elements)\n ? `

      Occurred ${\n err.elements.length\n } times at:

      \n ${markdownToHtml(err.elements.map(generateMarkdownLink).join(\"\\n\"))}`\n : \"\";\n const details = err.details\n ? `\\n\\n
      \\n${err.details}\\n
      \\n`\n : \"\";\n const msg = markdownToHtml(`**${xmlEscape(err.message)}**`, { inline: true });\n const result = `${msg}${hint}${elements}${details}${plugin}`;\n return result;\n}\n\n/**\n * @param {Element} element\n */\nfunction generateMarkdownLink(element) {\n return `* [\\`<${element.localName}>\\`](#${element.id}) element`;\n}\n","import { runAll } from \"./core/base-runner.js\";\nimport { ui } from \"./core/ui.js\";\n\n// In case everything else fails, we want the error\nwindow.addEventListener(\"error\", ev => {\n console.error(ev.error, ev.message, ev);\n});\n\nexport async function run(plugins) {\n try {\n ui.show();\n await domReady();\n await runAll(plugins);\n } finally {\n ui.enable();\n }\n}\n\nasync function domReady() {\n if (document.readyState === \"loading\") {\n await new Promise(resolve =>\n document.addEventListener(\"DOMContentLoaded\", resolve)\n );\n }\n}\n","import * as ReSpec from \"../src/respec.js\";\n\nconst modules = [\n // order is significant\n import(\"../src/core/location-hash.js\"),\n import(\"../src/core/l10n.js\"),\n import(\"../src/w3c/group.js\"),\n import(\"../src/w3c/defaults.js\"),\n import(\"../src/core/style.js\"),\n import(\"../src/w3c/style.js\"),\n import(\"../src/core/github.js\"),\n import(\"../src/core/data-include.js\"),\n import(\"../src/core/markdown.js\"),\n import(\"../src/core/reindent.js\"),\n import(\"../src/core/title.js\"),\n import(\"../src/w3c/level.js\"),\n import(\"../src/w3c/headers.js\"),\n import(\"../src/w3c/abstract.js\"),\n import(\"../src/core/data-transform.js\"),\n import(\"../src/core/data-abbr.js\"),\n import(\"../src/core/algorithms.js\"),\n import(\"../src/core/inlines.js\"),\n import(\"../src/w3c/conformance.js\"),\n import(\"../src/core/dfn.js\"),\n import(\"../src/core/pluralize.js\"),\n import(\"../src/core/examples.js\"),\n import(\"../src/core/issues-notes.js\"),\n import(\"../src/core/best-practices.js\"),\n import(\"../src/core/figures.js\"),\n import(\"../src/core/tables.js\"),\n import(\"../src/core/webidl.js\"),\n import(\"../src/core/cddl.js\"),\n import(\"../src/core/biblio.js\"),\n import(\"../src/core/link-to-dfn.js\"),\n import(\"../src/core/xref.js\"),\n import(\"../src/core/data-cite.js\"),\n import(\"../src/core/xref-headings.js\"),\n import(\"../src/core/render-biblio.js\"),\n import(\"../src/core/dfn-index.js\"),\n import(\"../src/core/contrib.js\"),\n import(\"../src/core/sections.js\"),\n import(\"../src/core/fix-headers.js\"),\n import(\"../src/core/webidl-index.js\"),\n import(\"../src/core/cddl-index.js\"),\n import(\"../src/core/structure.js\"),\n import(\"../src/core/informative.js\"),\n import(\"../src/core/id-headers.js\"),\n import(\"../src/core/caniuse.js\"),\n import(\"../src/core/implementation-status.js\"),\n import(\"../src/core/mdn-annotation.js\"),\n import(\"../src/ui/save-html.js\"),\n import(\"../src/ui/search-specref.js\"),\n import(\"../src/ui/search-xref.js\"),\n import(\"../src/ui/about-respec.js\"),\n import(\"../src/core/seo.js\"),\n import(\"../src/w3c/seo.js\"),\n import(\"../src/core/highlight.js\"),\n import(\"../src/core/webidl-clipboard.js\"),\n import(\"../src/core/data-tests.js\"),\n import(\"../src/core/list-sorter.js\"),\n import(\"../src/core/highlight-vars.js\"),\n import(\"../src/core/data-type.js\"),\n import(\"../src/core/anchor-expander.js\"),\n import(\"../src/core/dfn-panel.js\"),\n import(\"../src/core/custom-elements/index.js\"),\n import(\"../src/core/web-monetization.js\"),\n import(\"../src/core/dfn-contract.js\"),\n import(\"../src/core/before-save.js\"),\n /* Linters must be the last thing to run */\n import(\"../src/core/linter-rules/check-charset.js\"),\n import(\"../src/core/linter-rules/check-punctuation.js\"),\n import(\"../src/core/linter-rules/check-internal-slots.js\"),\n import(\"../src/core/linter-rules/local-refs-exist.js\"),\n import(\"../src/core/linter-rules/no-captionless-tables.js\"),\n import(\"../src/core/linter-rules/no-unused-dfns.js\"),\n import(\"../src/core/linter-rules/no-headingless-sections.js\"),\n import(\"../src/core/linter-rules/no-unused-vars.js\"),\n import(\"../src/core/linter-rules/no-dfn-in-abstract.js\"),\n import(\"../src/w3c/linter-rules/required-sections.js\"),\n import(\"../src/core/linter-rules/wpt-tests-exist.js\"),\n import(\"../src/core/linter-rules/no-http-props.js\"),\n import(\"../src/core/linter-rules/a11y.js\"),\n import(\"../src/core/linter-rules/informative-dfn.js\"),\n];\n\nPromise.all(modules)\n .then(plugins => ReSpec.run(plugins))\n .catch(err => console.error(err));\n","// @ts-check\n// Module core/location-hash\n// As ReSpec injects a bunch of stuff async, the scroll position is not always\n// at the right place when we are done processing. The purpose of this module\n// is to reset window's location hash, which will cause the browser to scroll\n// the window to the correct point in the document when processing is done.\n\nexport const name = \"core/location-hash\";\n\nexport function run() {\n if (!window.location.hash) {\n return;\n }\n\n // We have to use .then() here because otherwise we would get stuck\n // awaiting this plugin to finish.\n document.respec.ready.then(() => {\n const hash = decodeURIComponent(window.location.hash).slice(1);\n\n let newHash = hash;\n /** @type {HTMLElement|null} */\n const element = document.getElementById(newHash);\n\n // Check if hash contains any non-word character.\n const isLegacyFrag = /\\W/.test(newHash);\n\n // Allow some degree of recovery for legacy fragments format.\n // See https://github.com/speced/respec/issues/1353\n if (!element && isLegacyFrag) {\n const id = newHash\n // Replace all non-word characters with a dash.\n .replace(/[\\W]+/gim, \"-\")\n // Remove any leading dashes.\n .replace(/^-+/, \"\")\n // Remove any trailing dashes.\n .replace(/-+$/, \"\");\n\n /** @type {HTMLElement|null} */\n const updatedElement = document.getElementById(id);\n if (updatedElement) {\n newHash = id;\n }\n }\n window.location.hash = `#${newHash}`;\n });\n}\n","// @ts-check\n/**\n * @module w3c/group\n * The purpose of this module is to fetch and set the working group configuration details.\n */\nimport { docLink, fetchAndCache, showError } from \"../core/utils.js\";\n\nexport const name = \"w3c/group\";\n\nconst W3C_GROUPS_API = \"https://respec.org/w3c/groups/\";\n\n/**\n * Fetches the group configuration details and adds them to the document's configuration.\n * @param {Conf} conf The document configuration object.\n * @return {Promise} Resolves after setting the group configuration details.\n */\nexport async function run(conf) {\n if (!conf.group) {\n return;\n }\n\n const { group } = conf;\n const groupDetails = Array.isArray(group)\n ? await getMultipleGroupDetails(group)\n : await getGroupDetails(group);\n Object.assign(conf, groupDetails);\n}\n\n/**\n * Fetches configuration details for multiple groups concurrently.\n * @param {string[]} groups An array of group identifiers.\n * @return {Promise} Resolves to an object containing the configuration details for each group.\n */\nasync function getMultipleGroupDetails(groups) {\n const details = await Promise.all(groups.map(getGroupDetails));\n /** @type {{ [key in keyof GroupDetails]: GroupDetails[key][] }} */\n const result = {\n wg: [],\n wgId: [],\n wgURI: [],\n wgPatentURI: [],\n wgPatentPolicy: [],\n groupType: [],\n };\n for (const groupDetails of details.filter(Boolean)) {\n for (const key of Object.keys(result)) {\n /** @type {Record} */ (result)[key].push(\n /** @type {Record} */ (groupDetails)[key]\n );\n }\n }\n return result;\n}\n\n/**\n * Fetches configuration details for a single group.\n * @param {string} group A group identifier.\n * @return {Promise} Resolves to an object containing the group's configuration details, or undefined if the group could not be fetched.\n */\nasync function getGroupDetails(group) {\n let type = \"\";\n let shortname = group;\n if (group.includes(\"/\")) {\n [type, shortname] = group.split(\"/\", 2);\n }\n const url = new URL(`${shortname}/${type}`, W3C_GROUPS_API);\n const res = await fetchAndCache(url.href);\n if (res.ok) {\n const json = await res.json();\n const {\n id: wgId,\n name: wg,\n patentURI: wgPatentURI,\n patentPolicy: wgPatentPolicy,\n type: groupType,\n wgURI,\n } = json;\n return { wg, wgId, wgURI, wgPatentURI, wgPatentPolicy, groupType };\n }\n\n const text = await res.text();\n let message = `Failed to fetch group details (HTTP: ${res.status}).`;\n let hint;\n if (res.status === 409) {\n [message, hint] = text.split(\"\\n\", 2);\n } else if (res.status === 404) {\n hint = docLink`See the list of [supported group names](https://respec.org/w3c/groups/) to use with the ${\"[group]\"} configuration option.`;\n }\n showError(message, name, { hint });\n}\n","// @ts-check\nimport { html } from \"../../core/import-maps.js\";\nimport { showWarning } from \"../../core/utils.js\";\n\nconst name = \"core/templates/show-link\";\n\n/**\n * @param {object} link\n * @param {string} link.key\n * @param {string} [link.class]\n * @param {LinkData[]} [link.data]\n */\nexport default function showLink(link) {\n if (!link.key) {\n const msg =\n \"Found a link without `key` attribute in the configuration. See dev console.\";\n showWarning(msg, name);\n console.warn(msg, link);\n return;\n }\n return html`\n
      ${link.key}
      \n ${link.data ? link.data.map(showLinkData) : showLinkData(link)}\n `;\n}\n\n/**\n * @typedef {object} LinkData\n * @property {string} [LinkData.class]\n * @property {string} [LinkData.href]\n * @property {string} [LinkData.value]\n * @param {LinkData} data\n */\nfunction showLinkData(data) {\n return html`
      \n ${data.href\n ? html`${data.value || data.href}`\n : data.value}\n
      `;\n}\n","// @ts-check\nimport { docLink, showError } from \"../../core/utils.js\";\nimport { html } from \"../../core/import-maps.js\";\n\nconst name = \"core/templates/show-logo\";\n\n/**\n * Logo mapper. Takes a logo structure and converts it to HTML.\n *\n * @param {ReSpecLogo} logo\n * @param {number} index\n */\nexport default function showLogo(logo, index) {\n /** @type {HTMLAnchorElement} */\n const a = html`\n `;\n if (!logo.alt) {\n const src = logo.src ? `, with \\`src\\` ${logo.src}, ` : \"\";\n const msg = `Logo at index ${index}${src} is missing required \"\\`alt\\`\" property.`;\n const hint = docLink`Add the missing \"\\`alt\\`\" property describing the logo. See ${\"[logos]\"} for more information.`;\n showError(msg, name, { hint, elements: [a] });\n }\n if (!logo.src) {\n const msg = `Logo at index ${index} is missing \"\\`src\\`\" property.`;\n const hint = docLink`The \\`src\\` property is required on every logo. See ${\"[logos]\"} for more information.`;\n showError(msg, name, { hint, elements: [a] });\n }\n return a;\n}\n","// @ts-check\n\nconst name = \"core/templates/show-people\";\n\nimport {\n W3CDate,\n getIntlData,\n isValidConfDate,\n showError,\n showWarning,\n} from \"../../core/utils.js\";\nimport { html } from \"../../core/import-maps.js\";\n\n/** @satisfies {Record} */\nconst localizationStrings = {\n en: {\n until(date) {\n return html` Until ${date} `;\n },\n },\n es: {\n until(date) {\n return html` Hasta ${date} `;\n },\n },\n ko: {\n until(date) {\n return html` ${date} 이전 `;\n },\n },\n ja: {\n until(date) {\n return html` ${date} 以前 `;\n },\n },\n de: {\n until(date) {\n return html` bis ${date} `;\n },\n },\n zh: {\n until(date) {\n return html` 直到 ${date} `;\n },\n },\n};\nconst l10n = getIntlData(localizationStrings);\n\nconst orcidIcon = () =>\n html`\n \n \n \n `;\n\n/**\n * @param {Conf} conf\n * @param {\"editors\" | \"authors\" | \"formerEditors\"} propName - the name of the property of the people to render.\n */\nexport default function showPeople(conf, propName) {\n const people = conf[propName];\n if (!Array.isArray(people) || !people.length) return; // nothing to show...\n\n const validatePerson = personValidator(propName);\n return people.filter(validatePerson).map(personToHTML);\n}\n\n/**\n * @param {Person} person\n */\nfunction personToHTML(person) {\n // The following are treated as opt-in HTML by hyperHTML\n // we need to deprecate this!\n const personName = [person.name];\n const company = [person.company];\n const editorId = person.w3cid || null;\n const contents = [];\n if (person.mailto) {\n person.url = `mailto:${person.mailto}`;\n }\n if (person.url) {\n const url = new URL(person.url, document.location.href);\n const classList =\n url.protocol === \"mailto:\"\n ? \"ed_mailto u-email email p-name\"\n : \"u-url url p-name fn\";\n contents.push(\n html`${personName}`\n );\n } else {\n contents.push(html`${personName}`);\n }\n if (person.orcid) {\n contents.push(\n html`${orcidIcon()}`\n );\n }\n if (person.company) {\n const hCard = \"p-org org h-org\";\n const companyElem = person.companyURL\n ? html`${company}`\n : html`${company}`;\n contents.push(html` (${companyElem})`);\n }\n if (person.note) {\n contents.push(document.createTextNode(` (${person.note})`));\n }\n if (person.extras) {\n contents.push(...person.extras.map(extra => html`, ${renderExtra(extra)}`));\n }\n const { retiredDate } = person;\n if (person.retiredDate) {\n const time = html`