import {
	d as Extension,
	P as Plugin,
	a as PluginKey,
	Z as combineTransactionSteps,
	_ as getChangedRanges,
	$ as findChildrenInRange,
	a0 as Slice,
	F as Fragment,
	S as Selection,
	a1 as SelectionRange,
	T as TextSelection,
	a2 as keydownHandler,
	D as DecorationSet,
	A as Decoration,
	b as NodeSelection,
	a3 as Transform,
	a4 as AllSelection,
	R as Mark,
	N as Node3,
	Q as DOMParser$1,
	a5 as ReplaceStep,
	a6 as findParentNode,
	a7 as Mapping,
	a8 as DOMSerializer,
	q as callOrReturn,
	s as getExtensionField,
	m as mergeAttributes,
	a9 as index_default,
	aa as index_default$1,
	ab as index_default$2,
	ac as index_default$3,
	ad as index_default$4,
	o as dropPoint,
	ae as getMarkRange,
	Y as posToDOMRect,
	x as redo$1,
	y as undo$1,
	v as history,
	af as findChildren,
	ag as closeHistory,
	u as gapCursor,
	ah as getSchema,
	ai as createDocument,
	E as Editor,
	aj as Node$1,
	ak as keymap,
	i as isNodeSelection,
	O as extensions_exports,
	J as Text,
	L as Link,
	al as findParentNodeClosestToPos,
	am as selectionToInsertionEnd,
	an as ReplaceAroundStep,
} from "./index-Bs--Ol2m.js";
import { g as getDefaultExportFromCjs } from "./_commonjsHelpers-B85MJLTf.js";
import { importShared } from "./__federation_fn_import-hlt2XzeI.js";
import { j as jsxRuntimeExports } from "./jsx-runtime-CvJTHeKY.js";

const scriptRel = /* @__PURE__ */ (function detectScriptRel() {
	const relList = typeof document !== "undefined" && document.createElement("link").relList;
	return relList && relList.supports && relList.supports("modulepreload") ? "modulepreload" : "preload";
})();
const assetsURL = function (dep) {
	return "/" + dep;
};
const seen = {};
const __vitePreload = function preload(baseModule, deps, importerUrl) {
	let promise = Promise.resolve();
	if (true && deps && deps.length > 0) {
		let allSettled2 = function (promises) {
			return Promise.all(
				promises.map((p) =>
					Promise.resolve(p).then(
						(value) => ({ status: "fulfilled", value }),
						(reason) => ({ status: "rejected", reason }),
					),
				),
			);
		};
		document.getElementsByTagName("link");
		const cspNonceMeta = document.querySelector("meta[property=csp-nonce]");
		const cspNonce = cspNonceMeta?.nonce || cspNonceMeta?.getAttribute("nonce");
		promise = allSettled2(
			deps.map((dep) => {
				dep = assetsURL(dep);
				if (dep in seen) return;
				seen[dep] = true;
				const isCss = dep.endsWith(".css");
				const cssSelector = isCss ? '[rel="stylesheet"]' : "";
				if (document.querySelector(`link[href="${dep}"]${cssSelector}`)) {
					return;
				}
				const link = document.createElement("link");
				link.rel = isCss ? "stylesheet" : scriptRel;
				if (!isCss) {
					link.as = "script";
				}
				link.crossOrigin = "";
				link.href = dep;
				if (cspNonce) {
					link.setAttribute("nonce", cspNonce);
				}
				document.head.appendChild(link);
				if (isCss) {
					return new Promise((res, rej) => {
						link.addEventListener("load", res);
						link.addEventListener("error", () => rej(new Error(`Unable to preload CSS for ${dep}`)));
					});
				}
			}),
		);
	}
	function handlePreloadError(err) {
		const e = new Event("vite:preloadError", {
			cancelable: true,
		});
		e.payload = err;
		window.dispatchEvent(e);
		if (!e.defaultPrevented) {
			throw err;
		}
	}
	return promise.then((res) => {
		for (const item of res || []) {
			if (item.status !== "rejected") continue;
			handlePreloadError(item.reason);
		}
		return baseModule().catch(handlePreloadError);
	});
};

// Unique ID creation requires a high quality random # generator. In the browser we therefore
// require the crypto API and do not support built-in fallback to lower quality random number
// generators (like Math.random()).
var getRandomValues$1;
var rnds8 = new Uint8Array(16);
function rng() {
	// lazy load so that environments that need to polyfill have a chance to do so
	if (!getRandomValues$1) {
		// getRandomValues needs to be invoked in a context where "this" is a Crypto implementation. Also,
		// find the complete implementation of crypto (msCrypto) on IE11.
		getRandomValues$1 =
			(typeof crypto !== "undefined" && crypto.getRandomValues && crypto.getRandomValues.bind(crypto)) ||
			(typeof msCrypto !== "undefined" && typeof msCrypto.getRandomValues === "function" && msCrypto.getRandomValues.bind(msCrypto));

		if (!getRandomValues$1) {
			throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");
		}
	}

	return getRandomValues$1(rnds8);
}

const REGEX = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;

function validate(uuid) {
	return typeof uuid === "string" && REGEX.test(uuid);
}

/**
 * Convert array of 16 byte values to UUID string format of the form:
 * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
 */

var byteToHex = [];

for (var i$2 = 0; i$2 < 256; ++i$2) {
	byteToHex.push((i$2 + 0x100).toString(16).substr(1));
}

function stringify$2(arr) {
	var offset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
	// Note: Be careful editing this code!  It's been tuned for performance
	// and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434
	var uuid = (
		byteToHex[arr[offset + 0]] +
		byteToHex[arr[offset + 1]] +
		byteToHex[arr[offset + 2]] +
		byteToHex[arr[offset + 3]] +
		"-" +
		byteToHex[arr[offset + 4]] +
		byteToHex[arr[offset + 5]] +
		"-" +
		byteToHex[arr[offset + 6]] +
		byteToHex[arr[offset + 7]] +
		"-" +
		byteToHex[arr[offset + 8]] +
		byteToHex[arr[offset + 9]] +
		"-" +
		byteToHex[arr[offset + 10]] +
		byteToHex[arr[offset + 11]] +
		byteToHex[arr[offset + 12]] +
		byteToHex[arr[offset + 13]] +
		byteToHex[arr[offset + 14]] +
		byteToHex[arr[offset + 15]]
	).toLowerCase(); // Consistency check for valid UUID.  If this throws, it's likely due to one
	// of the following:
	// - One or more input array values don't map to a hex octet (leading to
	// "undefined" in the uuid)
	// - Invalid input values for the RFC `version` or `variant` fields

	if (!validate(uuid)) {
		throw TypeError("Stringified UUID is invalid");
	}

	return uuid;
}

function v4(options, buf, offset) {
	options = options || {};
	var rnds = options.random || (options.rng || rng)(); // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`

	rnds[6] = (rnds[6] & 0x0f) | 0x40;
	rnds[8] = (rnds[8] & 0x3f) | 0x80; // Copy bytes to buffer, if provided

	return stringify$2(rnds);
}

function pt(t, o = JSON.stringify) {
	const r = {};
	return t.filter((n) => {
		const e = o(n);
		return Object.prototype.hasOwnProperty.call(r, e) ? false : (r[e] = true);
	});
}
function dt$1(t) {
	const o = t.filter((n, e) => t.indexOf(n) !== e);
	return pt(o);
}
const Q$2 = Extension.create({
	name: "uniqueID",
	// we’ll set a very high priority to make sure this runs first
	// and is compatible with `appendTransaction` hooks of other extensions
	priority: 1e4,
	addOptions() {
		return {
			attributeName: "id",
			types: [],
			setIdAttribute: false,
			generateID: () => {
				if (typeof window < "u" && window.__TEST_OPTIONS) {
					const t = window.__TEST_OPTIONS;
					return (t.mockID === void 0 ? (t.mockID = 0) : t.mockID++, t.mockID.toString());
				}
				return v4();
			},
			filterTransaction: null,
		};
	},
	addGlobalAttributes() {
		return [
			{
				types: this.options.types,
				attributes: {
					[this.options.attributeName]: {
						default: null,
						parseHTML: (t) => t.getAttribute(`data-${this.options.attributeName}`),
						renderHTML: (t) => {
							const o = {
								[`data-${this.options.attributeName}`]: t[this.options.attributeName],
							};
							return this.options.setIdAttribute ?
									{
										...o,
										id: t[this.options.attributeName],
									}
								:	o;
						},
					},
				},
			},
		];
	},
	// check initial content for missing ids
	// onCreate() {
	//   // Don’t do this when the collaboration extension is active
	//   // because this may update the content, so Y.js tries to merge these changes.
	//   // This leads to empty block nodes.
	//   // See: https://github.com/ueberdosis/tiptap/issues/2400
	//   if (
	//     this.editor.extensionManager.extensions.find(
	//       (extension) => extension.name === "collaboration"
	//     )
	//   ) {
	//     return;
	//   }
	//   const { view, state } = this.editor;
	//   const { tr, doc } = state;
	//   const { types, attributeName, generateID } = this.options;
	//   const nodesWithoutId = findChildren(doc, (node) => {
	//     return (
	//       types.includes(node.type.name) && node.attrs[attributeName] === null
	//     );
	//   });
	//   nodesWithoutId.forEach(({ node, pos }) => {
	//     tr.setNodeMarkup(pos, undefined, {
	//       ...node.attrs,
	//       [attributeName]: generateID(),
	//     });
	//   });
	//   tr.setMeta("addToHistory", false);
	//   view.dispatch(tr);
	// },
	addProseMirrorPlugins() {
		let t = null,
			o = false;
		return [
			new Plugin({
				key: new PluginKey("uniqueID"),
				appendTransaction: (r, n, e) => {
					const s = r.some((g) => g.docChanged) && !n.doc.eq(e.doc),
						i =
							this.options.filterTransaction &&
							r.some((g) => {
								var C, d;
								return !((d = (C = this.options).filterTransaction) != null && d.call(C, g));
							});
					if (!s || i) return;
					const { tr: l } = e,
						{ types: a, attributeName: c, generateID: h } = this.options,
						f = combineTransactionSteps(n.doc, r),
						{ mapping: p } = f;
					if (
						(getChangedRanges(f).forEach(({ newRange: g }) => {
							const C = findChildrenInRange(e.doc, g, (y) => a.includes(y.type.name)),
								d = C.map(({ node: y }) => y.attrs[c]).filter((y) => y !== null),
								w = dt$1(d);
							C.forEach(({ node: y, pos: m }) => {
								var z;
								const b = (z = l.doc.nodeAt(m)) == null ? void 0 : z.attrs[c];
								if (b === null) {
									const U = n.doc.type.createAndFill().content;
									if (n.doc.content.findDiffStart(U) === null) {
										const q = JSON.parse(JSON.stringify(e.doc.toJSON()));
										if (((q.content[0].content[0].attrs.id = "initialBlockId"), JSON.stringify(q.content) === JSON.stringify(U.toJSON()))) {
											l.setNodeMarkup(m, void 0, {
												...y.attrs,
												[c]: "initialBlockId",
											});
											return;
										}
									}
									l.setNodeMarkup(m, void 0, {
										...y.attrs,
										[c]: h(),
									});
									return;
								}
								const { deleted: k } = p.invert().mapResult(m);
								k &&
									w.includes(b) &&
									l.setNodeMarkup(m, void 0, {
										...y.attrs,
										[c]: h(),
									});
							});
						}),
						!!l.steps.length)
					)
						return (l.setMeta("uniqueID", true), l);
				},
				// we register a global drag handler to track the current drag source element
				view(r) {
					const n = (e) => {
						let s;
						t = !((s = r.dom.parentElement) === null || s === void 0) && s.contains(e.target) ? r.dom.parentElement : null;
					};
					return (
						window.addEventListener("dragstart", n),
						{
							destroy() {
								window.removeEventListener("dragstart", n);
							},
						}
					);
				},
				props: {
					// `handleDOMEvents` is called before `transformPasted` so we can do
					// some checks before. However, `transformPasted` only runs when
					// editor content is pasted - not external content.
					handleDOMEvents: {
						// only create new ids for dropped content while holding `alt`
						// or content is dragged from another editor
						drop: (r, n) => {
							let e;
							return (t !== r.dom.parentElement || ((e = n.dataTransfer) === null || e === void 0 ? void 0 : e.effectAllowed) === "copy" ? (o = true) : (o = false), (t = null), false);
						},
						// always create new ids on pasted content
						paste: () => ((o = true), false),
					},
					// we’ll remove ids for every pasted node
					// so we can create a new one within `appendTransaction`
					transformPasted: (r) => {
						if (!o) return r;
						const { types: n, attributeName: e } = this.options,
							s = (i) => {
								const l = [];
								return (
									i.forEach((a) => {
										if (a.isText) {
											l.push(a);
											return;
										}
										if (!n.includes(a.type.name)) {
											l.push(a.copy(s(a.content)));
											return;
										}
										const c = a.type.create(
											{
												...a.attrs,
												[e]: null,
											},
											s(a.content),
											a.marks,
										);
										l.push(c);
									}),
									Fragment.from(l)
								);
							};
						return ((o = false), new Slice(s(r.content), r.openStart, r.openEnd));
					},
				},
			}),
		];
	},
});
function K$2(t) {
	return t.type === "link";
}
function X$2(t) {
	return typeof t != "string" && t.type === "link";
}
function B$2(t) {
	return typeof t != "string" && t.type === "text";
}
function S$1(t) {
	var o, r, n, e, s;
	return (
		R$1(t) ? { ...t }
		: v$2(t) ?
			{
				type: "tableCell",
				content: [].concat(t.content),
				props: {
					backgroundColor: ((o = t.props) == null ? void 0 : o.backgroundColor) ?? "default",
					textColor: ((r = t.props) == null ? void 0 : r.textColor) ?? "default",
					textAlignment: ((n = t.props) == null ? void 0 : n.textAlignment) ?? "left",
					colspan: ((e = t.props) == null ? void 0 : e.colspan) ?? 1,
					rowspan: ((s = t.props) == null ? void 0 : s.rowspan) ?? 1,
				},
			}
		:	{
				type: "tableCell",
				content: [].concat(t),
				props: {
					backgroundColor: "default",
					textColor: "default",
					textAlignment: "left",
					colspan: 1,
					rowspan: 1,
				},
			}
	);
}
function v$2(t) {
	return t != null && typeof t != "string" && !Array.isArray(t) && t.type === "tableCell";
}
function R$1(t) {
	return v$2(t) && t.props !== void 0 && t.content !== void 0;
}
function A$3(t) {
	return R$1(t) ? (t.props.colspan ?? 1) : 1;
}
function D$3(t) {
	return R$1(t) ? (t.props.rowspan ?? 1) : 1;
}
class O extends Error {
	constructor(o) {
		super(`Unreachable case: ${o}`);
	}
}
function At$2(t, o = true) {
	const { "data-test": r, ...n } = t;
	if (Object.keys(n).length > 0 && o) throw new Error("Object must be empty " + JSON.stringify(t));
}
function Y$1(t, o) {
	const r = t.resolve(o);
	if (r.nodeAfter && r.nodeAfter.type.isInGroup("bnBlock"))
		return {
			posBeforeNode: r.pos,
			node: r.nodeAfter,
		};
	let n = r.depth,
		e = r.node(n);
	for (; n > 0; ) {
		if (e.type.isInGroup("bnBlock"))
			return {
				posBeforeNode: r.before(n),
				node: e,
			};
		(n--, (e = r.node(n)));
	}
	const s = [];
	(t.descendants((l, a) => {
		l.type.isInGroup("bnBlock") && s.push(a);
	}),
		console.warn(`Position ${o} is not within a blockContainer node.`));
	const i = t.resolve(s.find((l) => l >= o) || s[s.length - 1]);
	return {
		posBeforeNode: i.pos,
		node: i.nodeAfter,
	};
}
function $$4(t, o) {
	if (!t.type.isInGroup("bnBlock")) throw new Error(`Attempted to get bnBlock node at position but found node of different type ${t.type.name}`);
	const r = t,
		n = o,
		e = n + r.nodeSize,
		s = {
			node: r,
			beforePos: n,
			afterPos: e,
		};
	if (r.type.name === "blockContainer") {
		let i, l;
		if (
			(r.forEach((a, c) => {
				if (a.type.spec.group === "blockContent") {
					const h = a,
						f = n + c + 1,
						p = f + a.nodeSize;
					i = {
						node: h,
						beforePos: f,
						afterPos: p,
					};
				} else if (a.type.name === "blockGroup") {
					const h = a,
						f = n + c + 1,
						p = f + a.nodeSize;
					l = {
						node: h,
						beforePos: f,
						afterPos: p,
					};
				}
			}),
			!i)
		)
			throw new Error(`blockContainer node does not contain a blockContent node in its children: ${r}`);
		return {
			isBlockContainer: true,
			bnBlock: s,
			blockContent: i,
			childContainer: l,
			blockNoteType: i.node.type.name,
		};
	} else {
		if (!s.node.type.isInGroup("childContainer")) throw new Error(`bnBlock node is not in the childContainer group: ${s.node}`);
		return {
			isBlockContainer: false,
			bnBlock: s,
			childContainer: s,
			blockNoteType: s.node.type.name,
		};
	}
}
function Z(t) {
	return $$4(t.node, t.posBeforeNode);
}
function It$1(t) {
	if (!t.nodeAfter) throw new Error(`Attempted to get blockContainer node at position ${t.pos} but a node at this position does not exist`);
	return $$4(t.nodeAfter, t.pos);
}
function Tt$2(t) {
	const o = Y$1(t.doc, t.selection.anchor);
	return Z(o);
}
function Ot$2(t) {
	const o = Y$1(t.doc, t.selection.anchor);
	return Z(o);
}
function ht(t) {
	return "doc" in t ? t.doc.type.schema : t.type.schema;
}
function G(t) {
	return t.cached.blockNoteEditor;
}
function J$1(t) {
	return G(t).schema;
}
function _$1(t) {
	return J$1(t).blockSchema;
}
function H$2(t) {
	return J$1(t).inlineContentSchema;
}
function N$1(t) {
	return J$1(t).styleSchema;
}
function j$1(t) {
	return G(t).blockCache;
}
function gt(t, o, r) {
	var s, i;
	const n = {
			type: "tableContent",
			columnWidths: [],
			headerRows: void 0,
			headerCols: void 0,
			rows: [],
		},
		e = [];
	t.content.forEach((l, a, c) => {
		const h = {
			cells: [],
		};
		(c === 0 &&
			l.content.forEach((f) => {
				let p = f.attrs.colwidth;
				(p == null && (p = new Array(f.attrs.colspan ?? 1).fill(void 0)), n.columnWidths.push(...p));
			}),
			(h.cells = l.content.content.map(
				(f, p) => (
					e[c] || (e[c] = []),
					(e[c][p] = f.type.name === "tableHeader"),
					{
						type: "tableCell",
						content: f.content.content
							.map((g) => F$2(g, o, r))
							.reduce((g, C) => {
								if (!g.length) return C;
								const d = g[g.length - 1],
									w = C[0];
								return w && B$2(d) && B$2(w) && JSON.stringify(d.styles) === JSON.stringify(w.styles) ?
										((d.text +=
											`
` + w.text),
										g.push(...C.slice(1)),
										g)
									:	(g.push(...C), g);
							}, []),
						props: {
							colspan: f.attrs.colspan,
							rowspan: f.attrs.rowspan,
							backgroundColor: f.attrs.backgroundColor,
							textColor: f.attrs.textColor,
							textAlignment: f.attrs.textAlignment,
						},
					}
				),
			)),
			n.rows.push(h));
	});
	for (let l = 0; l < e.length; l++) (s = e[l]) != null && s.every((a) => a) && (n.headerRows = (n.headerRows ?? 0) + 1);
	for (let l = 0; l < ((i = e[0]) == null ? void 0 : i.length); l++) e != null && e.every((a) => a[l]) && (n.headerCols = (n.headerCols ?? 0) + 1);
	return n;
}
function F$2(t, o, r) {
	const n = [];
	let e;
	return (
		t.content.forEach((s) => {
			if (s.type.name === "hardBreak") {
				if (e)
					if (B$2(e))
						e.text += `
`;
					else if (K$2(e))
						e.content[e.content.length - 1].text += `
`;
					else throw new Error("unexpected");
				else
					e = {
						type: "text",
						text: `
`,
						styles: {},
					};
				return;
			}
			if (s.type.name !== "link" && s.type.name !== "text") {
				if (!o[s.type.name]) {
					console.warn("unrecognized inline content type", s.type.name);
					return;
				}
				(e && (n.push(e), (e = void 0)), n.push(wt$1(s, o, r)));
				return;
			}
			const i = {};
			let l;
			for (const a of s.marks)
				if (a.type.name === "link") l = a;
				else {
					const c = r[a.type.name];
					if (!c) {
						if (a.type.spec.blocknoteIgnore) continue;
						throw new Error(`style ${a.type.name} not found in styleSchema`);
					}
					if (c.propSchema === "boolean") i[c.type] = true;
					else if (c.propSchema === "string") i[c.type] = a.attrs.stringValue;
					else throw new O(c.propSchema);
				}
			e ?
				B$2(e) ?
					l ?
						(n.push(e),
						(e = {
							type: "link",
							href: l.attrs.href,
							content: [
								{
									type: "text",
									text: s.textContent,
									styles: i,
								},
							],
						}))
					: JSON.stringify(e.styles) === JSON.stringify(i) ? (e.text += s.textContent)
					: (n.push(e),
						(e = {
							type: "text",
							text: s.textContent,
							styles: i,
						}))
				:	K$2(e) &&
					(l ?
						e.href === l.attrs.href ?
							JSON.stringify(e.content[e.content.length - 1].styles) === JSON.stringify(i) ?
								(e.content[e.content.length - 1].text += s.textContent)
							:	e.content.push({
									type: "text",
									text: s.textContent,
									styles: i,
								})
						:	(n.push(e),
							(e = {
								type: "link",
								href: l.attrs.href,
								content: [
									{
										type: "text",
										text: s.textContent,
										styles: i,
									},
								],
							}))
					:	(n.push(e),
						(e = {
							type: "text",
							text: s.textContent,
							styles: i,
						})))
			: l ?
				(e = {
					type: "link",
					href: l.attrs.href,
					content: [
						{
							type: "text",
							text: s.textContent,
							styles: i,
						},
					],
				})
			:	(e = {
					type: "text",
					text: s.textContent,
					styles: i,
				});
		}),
		e && n.push(e),
		n
	);
}
function wt$1(t, o, r) {
	if (t.type.name === "text" || t.type.name === "link") throw new Error("unexpected");
	const n = {},
		e = o[t.type.name];
	for (const [l, a] of Object.entries(t.attrs)) {
		if (!e) throw Error("ic node is of an unrecognized type: " + t.type.name);
		const c = e.propSchema;
		l in c && (n[l] = a);
	}
	let s;
	return (
		e.content === "styled" ? (s = F$2(t, o, r)) : (s = void 0),
		{
			type: t.type.name,
			props: n,
			content: s,
		}
	);
}
function L$2(t, o, r = _$1(o), n = H$2(o), e = N$1(o), s = j$1(o)) {
	var C;
	if (!t.type.isInGroup("bnBlock")) throw Error("Node should be a bnBlock, but is instead: " + t.type.name);
	const i = s == null ? void 0 : s.get(t);
	if (i) return i;
	const l = $$4(t, 0);
	let a = l.bnBlock.node.attrs.id;
	a === null && (a = Q$2.options.generateID());
	const c = r[l.blockNoteType];
	if (!c) throw Error("Block is of an unrecognized type: " + l.blockNoteType);
	const h = {};
	for (const [d, w] of Object.entries({
		...t.attrs,
		...(l.isBlockContainer ? l.blockContent.node.attrs : {}),
	})) {
		const y = c.propSchema;
		d in y && !(y[d].default === void 0 && w === void 0) && (h[d] = w);
	}
	const f = r[l.blockNoteType],
		p = [];
	(C = l.childContainer) == null ||
		C.node.forEach((d) => {
			p.push(L$2(d, o, r, n, e, s));
		});
	let u;
	if (f.content === "inline") {
		if (!l.isBlockContainer) throw new Error("impossible");
		u = F$2(l.blockContent.node, n, e);
	} else if (f.content === "table") {
		if (!l.isBlockContainer) throw new Error("impossible");
		u = gt(l.blockContent.node, n, e);
	} else if (f.content === "none") u = void 0;
	else throw new O(f.content);
	const g = {
		id: a,
		type: f.type,
		props: h,
		content: u,
		children: p,
	};
	return (s == null || s.set(t, g), g);
}
function St$1(t, o = ht(t), r = _$1(o), n = H$2(o), e = N$1(o), s = j$1(o)) {
	const i = [];
	return (t.firstChild && t.firstChild.descendants((l) => (i.push(L$2(l, o, r, n, e, s)), false)), i);
}
function Dt$2(t, o, r = _$1(o), n = H$2(o), e = N$1(o), s = j$1(o)) {
	function i(l, a, c) {
		if (l.type.name !== "blockGroup") throw new Error("unexpected");
		const h = [];
		let f, p;
		return (
			l.forEach((u, g, C) => {
				if (u.type.name !== "blockContainer") throw new Error("unexpected");
				if (u.childCount === 0) return;
				if (u.childCount === 0 || u.childCount > 2) throw new Error("unexpected, blockContainer.childCount: " + u.childCount);
				const d = C === 0,
					w = C === l.childCount - 1;
				if (u.firstChild.type.name === "blockGroup") {
					if (!d) throw new Error("unexpected");
					const k = i(u.firstChild, Math.max(0, a - 1), w ? Math.max(0, c - 1) : 0);
					((f = k.blockCutAtStart), w && (p = k.blockCutAtEnd), h.push(...k.blocks));
					return;
				}
				const y = L$2(u, o, r, n, e, s),
					m = u.childCount > 1 ? u.child(1) : void 0;
				let b = [];
				if (m) {
					const k = i(
						m,
						0,
						// TODO: can this be anything other than 0?
						w ? Math.max(0, c - 1) : 0,
					);
					((b = k.blocks), w && (p = k.blockCutAtEnd));
				}
				(w && !m && c > 1 && (p = y.id),
					d && a > 1 && (f = y.id),
					h.push({
						...y,
						children: b,
					}));
			}),
			{ blocks: h, blockCutAtStart: f, blockCutAtEnd: p }
		);
	}
	if (t.content.childCount === 0)
		return {
			blocks: [],
			blockCutAtStart: void 0,
			blockCutAtEnd: void 0,
		};
	if (t.content.childCount !== 1) throw new Error("slice must be a single block, did you forget includeParents=true?");
	return i(t.content.firstChild, Math.max(t.openStart - 1, 0), Math.max(t.openEnd - 1, 0));
}
function x(t) {
	const { height: o, width: r } = tt(t),
		n = new Array(o).fill(false).map(() => new Array(r).fill(null)),
		e = (s, i) => {
			for (let l = s; l < o; l++) for (let a = i; a < r; a++) if (!n[l][a]) return { row: l, col: a };
			throw new Error("Unable to create occupancy grid for table, no more available cells");
		};
	for (let s = 0; s < t.content.rows.length; s++)
		for (let i = 0; i < t.content.rows[s].cells.length; i++) {
			const l = S$1(t.content.rows[s].cells[i]),
				a = D$3(l),
				c = A$3(l),
				{ row: h, col: f } = e(s, i);
			for (let p = h; p < h + a; p++)
				for (let u = f; u < f + c; u++) {
					if (n[p][u]) throw new Error(`Unable to create occupancy grid for table, cell at ${p},${u} is already occupied`);
					n[p][u] = {
						row: s,
						col: i,
						rowspan: a,
						colspan: c,
						cell: l,
					};
				}
		}
	return n;
}
function I$2(t) {
	const o = /* @__PURE__ */ new Set();
	return t.map((r) => ({
		cells: r.map((n) => (o.has(n.row + ":" + n.col) ? false : (o.add(n.row + ":" + n.col), n.cell))).filter((n) => n !== false),
	}));
}
function E$1(t, o, r = x(o)) {
	for (let n = 0; n < r.length; n++)
		for (let e = 0; e < r[n].length; e++) {
			const s = r[n][e];
			if (s && s.row === t.row && s.col === t.col) return { row: n, col: e, cell: s.cell };
		}
	throw new Error(`Unable to resolve relative table cell indices for table, cell at ${t.row},${t.col} is not occupied`);
}
function tt(t) {
	const o = t.content.rows.length;
	let r = 0;
	return (
		t.content.rows.forEach((n) => {
			let e = 0;
			(n.cells.forEach((s) => {
				e += A$3(s);
			}),
				(r = Math.max(r, e)));
		}),
		{ height: o, width: r }
	);
}
function et(t, o, r = x(o)) {
	var e;
	const n = (e = r[t.row]) == null ? void 0 : e[t.col];
	if (n)
		return {
			row: n.row,
			col: n.col,
			cell: n.cell,
		};
}
function yt$1(t, o) {
	var s;
	const r = x(t);
	if (o < 0 || o >= r.length) return [];
	let n = 0;
	for (let i = 0; i < o; i++) {
		const l = (s = r[n]) == null ? void 0 : s[0];
		if (!l) return [];
		n += l.rowspan;
	}
	const e = new Array(r[0].length)
		.fill(false)
		.map((i, l) => et({ row: n, col: l }, t, r))
		.filter((i) => i !== void 0);
	return e.filter((i, l) => e.findIndex((a) => a.row === i.row && a.col === i.col) === l);
}
function Ct$1(t, o) {
	var s;
	const r = x(t);
	if (o < 0 || o >= r[0].length) return [];
	let n = 0;
	for (let i = 0; i < o; i++) {
		const l = (s = r[0]) == null ? void 0 : s[n];
		if (!l) return [];
		n += l.colspan;
	}
	const e = new Array(r.length)
		.fill(false)
		.map((i, l) => et({ row: l, col: n }, t, r))
		.filter((i) => i !== void 0);
	return e.filter((i, l) => e.findIndex((a) => a.row === i.row && a.col === i.col) === l);
}
function Mt$2(t, o, r, n = x(t)) {
	const { col: e } = E$1(
			{
				row: 0,
				col: o,
			},
			t,
			n,
		),
		{ col: s } = E$1(
			{
				row: 0,
				col: r,
			},
			t,
			n,
		);
	return (
		n.forEach((i) => {
			const [l] = i.splice(e, 1);
			i.splice(s, 0, l);
		}),
		I$2(n)
	);
}
function Pt$2(t, o, r, n = x(t)) {
	const { row: e } = E$1(
			{
				row: o,
				col: 0,
			},
			t,
			n,
		),
		{ row: s } = E$1(
			{
				row: r,
				col: 0,
			},
			t,
			n,
		),
		[i] = n.splice(e, 1);
	return (n.splice(s, 0, i), I$2(n));
}
function M(t) {
	return (
		t ?
			v$2(t) ? M(t.content)
			: typeof t == "string" ? t.length === 0
			: Array.isArray(t) ?
				t.every((o) =>
					typeof o == "string" ? o.length === 0
					: B$2(o) ? o.text.length === 0
					: X$2(o) ?
						typeof o.content == "string" ?
							o.content.length === 0
						:	o.content.every((r) => r.text.length === 0)
					:	false,
				)
			:	false
		:	true
	);
}
function Rt$2(t, o, r = x(t)) {
	if (o === "columns") {
		let s = 0;
		for (let i = r[0].length - 1; i >= 0 && r.every((a) => M(a[i].cell) && a[i].colspan === 1); i--) s++;
		for (let i = r.length - 1; i >= 0; i--) {
			const l = Math.max(r[i].length - s, 1);
			r[i] = r[i].slice(0, l);
		}
		return I$2(r);
	}
	let n = 0;
	for (let s = r.length - 1; s >= 0 && r[s].every((l) => M(l.cell) && l.rowspan === 1); s--) n++;
	const e = Math.min(n, r.length - 1);
	return (r.splice(r.length - e, e), I$2(r));
}
function $t$2(t, o, r, n = x(t)) {
	const { width: e, height: s } = tt(t);
	if (o === "columns")
		n.forEach((i, l) => {
			if (r >= 0)
				for (let a = 0; a < r; a++)
					i.push({
						row: l,
						col: Math.max(...i.map((c) => c.col)) + 1,
						rowspan: 1,
						colspan: 1,
						cell: S$1(""),
					});
			else i.splice(e + r, -1 * r);
		});
	else if (r > 0)
		for (let i = 0; i < r; i++) {
			const l = new Array(e).fill(null).map((a, c) => ({
				row: s + i,
				col: c,
				rowspan: 1,
				colspan: 1,
				cell: S$1(""),
			}));
			n.push(l);
		}
	else r < 0 && n.splice(s + r, -1 * r);
	return I$2(n);
}
function Jt$2(t, o, r) {
	const n = yt$1(t, r);
	if (!n.some((a) => D$3(a.cell) > 1)) return true;
	let s = r,
		i = r;
	return (
		n.forEach((a) => {
			const c = D$3(a.cell);
			((s = Math.max(s, a.row + c - 1)), (i = Math.min(i, a.row)));
		}),
		o < r ? r === s : r === i
	);
}
function _t$3(t, o, r) {
	const n = Ct$1(t, r);
	if (!n.some((a) => A$3(a.cell) > 1)) return true;
	let s = r,
		i = r;
	return (
		n.forEach((a) => {
			const c = A$3(a.cell);
			((s = Math.max(s, a.col + c - 1)), (i = Math.min(i, a.col)));
		}),
		o < r ? r === s : r === i
	);
}
function Ht$2(t, o, r) {
	const n = E$1(t, r),
		e = E$1(o, r);
	return n.col === e.col;
}
function V$1(t, o, r, n) {
	const e = [];
	for (const [i, l] of Object.entries(t.styles || {})) {
		const a = r[i];
		if (!a) throw new Error(`style ${i} not found in styleSchema`);
		if (a.propSchema === "boolean") l && e.push(o.mark(i));
		else if (a.propSchema === "string") l && e.push(o.mark(i, { stringValue: l }));
		else throw new O(a.propSchema);
	}
	return (
		!n || !o.nodes[n].spec.code ?
			t.text
				.split(/(\n)/g)
				.filter((i) => i.length > 0)
				.map((i) =>
					(
						i ===
						`
`
					) ?
						o.nodes.hardBreak.createChecked()
					:	o.text(i, e),
				)
		: t.text.length > 0 ? [o.text(t.text, e)]
		: []
	);
}
function mt(t, o, r) {
	const n = o.marks.link.create({
		href: t.href,
	});
	return P$1(t.content, o, r).map((e) => {
		if (e.type.name === "text") return e.mark([...e.marks, n]);
		if (e.type.name === "hardBreak") return e;
		throw new Error("unexpected node type");
	});
}
function P$1(t, o, r, n) {
	const e = [];
	if (typeof t == "string") return (e.push(...V$1({ text: t, styles: {} }, o, r, n)), e);
	for (const s of t) e.push(...V$1(s, o, r, n));
	return e;
}
function T$1(t, o, r, n = N$1(o)) {
	const e = [];
	for (const s of t)
		typeof s == "string" ? e.push(...P$1(s, o, n, r))
		: X$2(s) ? e.push(...mt(s, o, n))
		: B$2(s) ? e.push(...P$1([s], o, n, r))
		: e.push(nt(s, o, n));
	return e;
}
function kt$1(t, o, r = N$1(o)) {
	const n = [],
		e = new Array(t.headerRows ?? 0).fill(true),
		s = new Array(t.headerCols ?? 0).fill(true),
		i = t.columnWidths ?? [];
	for (let l = 0; l < t.rows.length; l++) {
		const a = t.rows[l],
			c = [],
			h = e[l];
		for (let p = 0; p < a.cells.length; p++) {
			const u = a.cells[p],
				g = s[p],
				C = void 0;
			let d = null;
			const w = E$1(
				{
					row: l,
					col: p,
				},
				{ content: t },
			);
			let y = i[w.col] ? [i[w.col]] : null;
			if (u)
				if (typeof u == "string") d = o.text(u);
				else if (v$2(u)) {
					u.content && (d = T$1(u.content, o, "tableParagraph", r));
					const b = A$3(u);
					b > 1 && (y = new Array(b).fill(false).map((k, W) => i[w.col + W] ?? void 0));
				} else d = T$1(u, o, "tableParagraph", r);
			const m = o.nodes[g || h ? "tableHeader" : "tableCell"].createChecked(
				{
					...(v$2(u) ? u.props : {}),
					colwidth: y,
				},
				o.nodes.tableParagraph.createChecked(C, d),
			);
			c.push(m);
		}
		const f = o.nodes.tableRow.createChecked({}, c);
		n.push(f);
	}
	return n;
}
function nt(t, o, r) {
	let n,
		e = t.type;
	if ((e === void 0 && (e = "paragraph"), !o.nodes[e])) throw new Error(`node type ${e} not found in schema`);
	if (!t.content) n = o.nodes[e].createChecked(t.props);
	else if (typeof t.content == "string") {
		const s = T$1([t.content], o, e, r);
		n = o.nodes[e].createChecked(t.props, s);
	} else if (Array.isArray(t.content)) {
		const s = T$1(t.content, o, e, r);
		n = o.nodes[e].createChecked(t.props, s);
	} else if (t.content.type === "tableContent") {
		const s = kt$1(t.content, o, r);
		n = o.nodes[e].createChecked(t.props, s);
	} else throw new O(t.content.type);
	return n;
}
function bt$1(t, o, r = N$1(o)) {
	let n = t.id;
	n === void 0 && (n = Q$2.options.generateID());
	const e = [];
	if (t.children) for (const i of t.children) e.push(bt$1(i, o, r));
	if (
		!t.type || // can happen if block.type is not defined (this should create the default node)
		o.nodes[t.type].isInGroup("blockContent")
	) {
		const i = nt(t, o, r),
			l = e.length > 0 ? o.nodes.blockGroup.createChecked({}, e) : void 0;
		return o.nodes.blockContainer.createChecked(
			{
				id: n,
				...t.props,
			},
			l ? [i, l] : i,
		);
	} else {
		if (o.nodes[t.type].isInGroup("bnBlock"))
			return o.nodes[t.type].createChecked(
				{
					id: n,
					...t.props,
				},
				e,
			);
		throw new Error(`block type ${t.type} doesn't match blockContent or bnBlock group`);
	}
}

//#region src/tablemap.ts
let readFromCache;
let addToCache;
if (typeof WeakMap != "undefined") {
	let cache = /* @__PURE__ */ new WeakMap();
	readFromCache = (key) => cache.get(key);
	addToCache = (key, value) => {
		cache.set(key, value);
		return value;
	};
} else {
	const cache = [];
	const cacheSize = 10;
	let cachePos = 0;
	readFromCache = (key) => {
		for (let i = 0; i < cache.length; i += 2) if (cache[i] == key) return cache[i + 1];
	};
	addToCache = (key, value) => {
		if (cachePos == cacheSize) cachePos = 0;
		cache[cachePos++] = key;
		return (cache[cachePos++] = value);
	};
}
/**
 * A table map describes the structure of a given table. To avoid
 * recomputing them all the time, they are cached per table node. To
 * be able to do that, positions saved in the map are relative to the
 * start of the table, rather than the start of the document.
 *
 * @public
 */
var TableMap = class {
	constructor(width, height, map, problems) {
		this.width = width;
		this.height = height;
		this.map = map;
		this.problems = problems;
	}
	findCell(pos) {
		for (let i = 0; i < this.map.length; i++) {
			const curPos = this.map[i];
			if (curPos != pos) continue;
			const left = i % this.width;
			const top = (i / this.width) | 0;
			let right = left + 1;
			let bottom = top + 1;
			for (let j = 1; right < this.width && this.map[i + j] == curPos; j++) right++;
			for (let j = 1; bottom < this.height && this.map[i + this.width * j] == curPos; j++) bottom++;
			return {
				left,
				top,
				right,
				bottom,
			};
		}
		throw new RangeError(`No cell with offset ${pos} found`);
	}
	colCount(pos) {
		for (let i = 0; i < this.map.length; i++) if (this.map[i] == pos) return i % this.width;
		throw new RangeError(`No cell with offset ${pos} found`);
	}
	nextCell(pos, axis, dir) {
		const { left, right, top, bottom } = this.findCell(pos);
		if (axis == "horiz") {
			if (dir < 0 ? left == 0 : right == this.width) return null;
			return this.map[top * this.width + (dir < 0 ? left - 1 : right)];
		} else {
			if (dir < 0 ? top == 0 : bottom == this.height) return null;
			return this.map[left + this.width * (dir < 0 ? top - 1 : bottom)];
		}
	}
	rectBetween(a, b) {
		const { left: leftA, right: rightA, top: topA, bottom: bottomA } = this.findCell(a);
		const { left: leftB, right: rightB, top: topB, bottom: bottomB } = this.findCell(b);
		return {
			left: Math.min(leftA, leftB),
			top: Math.min(topA, topB),
			right: Math.max(rightA, rightB),
			bottom: Math.max(bottomA, bottomB),
		};
	}
	cellsInRect(rect) {
		const result = [];
		const seen = {};
		for (let row = rect.top; row < rect.bottom; row++)
			for (let col = rect.left; col < rect.right; col++) {
				const index = row * this.width + col;
				const pos = this.map[index];
				if (seen[pos]) continue;
				seen[pos] = true;
				if ((col == rect.left && col && this.map[index - 1] == pos) || (row == rect.top && row && this.map[index - this.width] == pos)) continue;
				result.push(pos);
			}
		return result;
	}
	positionAt(row, col, table) {
		for (let i = 0, rowStart = 0; ; i++) {
			const rowEnd = rowStart + table.child(i).nodeSize;
			if (i == row) {
				let index = col + row * this.width;
				const rowEndIndex = (row + 1) * this.width;
				while (index < rowEndIndex && this.map[index] < rowStart) index++;
				return index == rowEndIndex ? rowEnd - 1 : this.map[index];
			}
			rowStart = rowEnd;
		}
	}
	static get(table) {
		return readFromCache(table) || addToCache(table, computeMap(table));
	}
};
function computeMap(table) {
	if (table.type.spec.tableRole != "table") throw new RangeError("Not a table node: " + table.type.name);
	const width = findWidth(table),
		height = table.childCount;
	const map = [];
	let mapPos = 0;
	let problems = null;
	const colWidths = [];
	for (let i = 0, e = width * height; i < e; i++) map[i] = 0;
	for (let row = 0, pos = 0; row < height; row++) {
		const rowNode = table.child(row);
		pos++;
		for (let i = 0; ; i++) {
			while (mapPos < map.length && map[mapPos] != 0) mapPos++;
			if (i == rowNode.childCount) break;
			const cellNode = rowNode.child(i);
			const { colspan, rowspan, colwidth } = cellNode.attrs;
			for (let h = 0; h < rowspan; h++) {
				if (h + row >= height) {
					(problems || (problems = [])).push({
						type: "overlong_rowspan",
						pos,
						n: rowspan - h,
					});
					break;
				}
				const start = mapPos + h * width;
				for (let w = 0; w < colspan; w++) {
					if (map[start + w] == 0) map[start + w] = pos;
					else
						(problems || (problems = [])).push({
							type: "collision",
							row,
							pos,
							n: colspan - w,
						});
					const colW = colwidth && colwidth[w];
					if (colW) {
						const widthIndex = ((start + w) % width) * 2,
							prev = colWidths[widthIndex];
						if (prev == null || (prev != colW && colWidths[widthIndex + 1] == 1)) {
							colWidths[widthIndex] = colW;
							colWidths[widthIndex + 1] = 1;
						} else if (prev == colW) colWidths[widthIndex + 1]++;
					}
				}
			}
			mapPos += colspan;
			pos += cellNode.nodeSize;
		}
		const expectedPos = (row + 1) * width;
		let missing = 0;
		while (mapPos < expectedPos) if (map[mapPos++] == 0) missing++;
		if (missing)
			(problems || (problems = [])).push({
				type: "missing",
				row,
				n: missing,
			});
		pos++;
	}
	if (width === 0 || height === 0) (problems || (problems = [])).push({ type: "zero_sized" });
	const tableMap = new TableMap(width, height, map, problems);
	let badWidths = false;
	for (let i = 0; !badWidths && i < colWidths.length; i += 2) if (colWidths[i] != null && colWidths[i + 1] < height) badWidths = true;
	if (badWidths) findBadColWidths(tableMap, colWidths, table);
	return tableMap;
}
function findWidth(table) {
	let width = -1;
	let hasRowSpan = false;
	for (let row = 0; row < table.childCount; row++) {
		const rowNode = table.child(row);
		let rowWidth = 0;
		if (hasRowSpan)
			for (let j = 0; j < row; j++) {
				const prevRow = table.child(j);
				for (let i = 0; i < prevRow.childCount; i++) {
					const cell = prevRow.child(i);
					if (j + cell.attrs.rowspan > row) rowWidth += cell.attrs.colspan;
				}
			}
		for (let i = 0; i < rowNode.childCount; i++) {
			const cell = rowNode.child(i);
			rowWidth += cell.attrs.colspan;
			if (cell.attrs.rowspan > 1) hasRowSpan = true;
		}
		if (width == -1) width = rowWidth;
		else if (width != rowWidth) width = Math.max(width, rowWidth);
	}
	return width;
}
function findBadColWidths(map, colWidths, table) {
	if (!map.problems) map.problems = [];
	const seen = {};
	for (let i = 0; i < map.map.length; i++) {
		const pos = map.map[i];
		if (seen[pos]) continue;
		seen[pos] = true;
		const node = table.nodeAt(pos);
		if (!node) throw new RangeError(`No cell with offset ${pos} found`);
		let updated = null;
		const attrs = node.attrs;
		for (let j = 0; j < attrs.colspan; j++) {
			const colWidth = colWidths[((i + j) % map.width) * 2];
			if (colWidth != null && (!attrs.colwidth || attrs.colwidth[j] != colWidth)) (updated || (updated = freshColWidth(attrs)))[j] = colWidth;
		}
		if (updated)
			map.problems.unshift({
				type: "colwidth mismatch",
				pos,
				colwidth: updated,
			});
	}
}
function freshColWidth(attrs) {
	if (attrs.colwidth) return attrs.colwidth.slice();
	const result = [];
	for (let i = 0; i < attrs.colspan; i++) result.push(0);
	return result;
}
/**
 * @public
 */
function tableNodeTypes(schema) {
	let result = schema.cached.tableNodeTypes;
	if (!result) {
		result = schema.cached.tableNodeTypes = {};
		for (const name in schema.nodes) {
			const type = schema.nodes[name],
				role = type.spec.tableRole;
			if (role) result[role] = type;
		}
	}
	return result;
}

//#endregion
//#region src/util.ts
/**
 * @public
 */
const tableEditingKey = new PluginKey("selectingCells");
/**
 * @public
 */
function cellAround($pos) {
	for (let d = $pos.depth - 1; d > 0; d--) if ($pos.node(d).type.spec.tableRole == "row") return $pos.node(0).resolve($pos.before(d + 1));
	return null;
}
function cellWrapping($pos) {
	for (let d = $pos.depth; d > 0; d--) {
		const role = $pos.node(d).type.spec.tableRole;
		if (role === "cell" || role === "header_cell") return $pos.node(d);
	}
	return null;
}
/**
 * @public
 */
function isInTable(state) {
	const $head = state.selection.$head;
	for (let d = $head.depth; d > 0; d--) if ($head.node(d).type.spec.tableRole == "row") return true;
	return false;
}
/**
 * @internal
 */
function selectionCell(state) {
	const sel = state.selection;
	if ("$anchorCell" in sel && sel.$anchorCell) return sel.$anchorCell.pos > sel.$headCell.pos ? sel.$anchorCell : sel.$headCell;
	else if ("node" in sel && sel.node && sel.node.type.spec.tableRole == "cell") return sel.$anchor;
	const $cell = cellAround(sel.$head) || cellNear(sel.$head);
	if ($cell) return $cell;
	throw new RangeError(`No cell found around position ${sel.head}`);
}
/**
 * @public
 */
function cellNear($pos) {
	for (let after = $pos.nodeAfter, pos = $pos.pos; after; after = after.firstChild, pos++) {
		const role = after.type.spec.tableRole;
		if (role == "cell" || role == "header_cell") return $pos.doc.resolve(pos);
	}
	for (let before = $pos.nodeBefore, pos = $pos.pos; before; before = before.lastChild, pos--) {
		const role = before.type.spec.tableRole;
		if (role == "cell" || role == "header_cell") return $pos.doc.resolve(pos - before.nodeSize);
	}
}
/**
 * @public
 */
function pointsAtCell($pos) {
	return $pos.parent.type.spec.tableRole == "row" && !!$pos.nodeAfter;
}
/**
 * @public
 */
function moveCellForward($pos) {
	return $pos.node(0).resolve($pos.pos + $pos.nodeAfter.nodeSize);
}
/**
 * @internal
 */
function inSameTable($cellA, $cellB) {
	return $cellA.depth == $cellB.depth && $cellA.pos >= $cellB.start(-1) && $cellA.pos <= $cellB.end(-1);
}
/**
 * @public
 */
function nextCell($pos, axis, dir) {
	const table = $pos.node(-1);
	const map = TableMap.get(table);
	const tableStart = $pos.start(-1);
	const moved = map.nextCell($pos.pos - tableStart, axis, dir);
	return moved == null ? null : $pos.node(0).resolve(tableStart + moved);
}
/**
 * @public
 */
function removeColSpan(attrs, pos, n = 1) {
	const result = {
		...attrs,
		colspan: attrs.colspan - n,
	};
	if (result.colwidth) {
		result.colwidth = result.colwidth.slice();
		result.colwidth.splice(pos, n);
		if (!result.colwidth.some((w) => w > 0)) result.colwidth = null;
	}
	return result;
}
/**
 * @public
 */
function addColSpan(attrs, pos, n = 1) {
	const result = {
		...attrs,
		colspan: attrs.colspan + n,
	};
	if (result.colwidth) {
		result.colwidth = result.colwidth.slice();
		for (let i = 0; i < n; i++) result.colwidth.splice(pos, 0, 0);
	}
	return result;
}
/**
 * @public
 */
function columnIsHeader(map, table, col) {
	const headerCell = tableNodeTypes(table.type.schema).header_cell;
	for (let row = 0; row < map.height; row++) if (table.nodeAt(map.map[col + row * map.width]).type != headerCell) return false;
	return true;
}

//#endregion
//#region src/cellselection.ts
/**
 * A [`Selection`](http://prosemirror.net/docs/ref/#state.Selection)
 * subclass that represents a cell selection spanning part of a table.
 * With the plugin enabled, these will be created when the user
 * selects across cells, and will be drawn by giving selected cells a
 * `selectedCell` CSS class.
 *
 * @public
 */
var CellSelection = class CellSelection extends Selection {
	constructor($anchorCell, $headCell = $anchorCell) {
		const table = $anchorCell.node(-1);
		const map = TableMap.get(table);
		const tableStart = $anchorCell.start(-1);
		const rect = map.rectBetween($anchorCell.pos - tableStart, $headCell.pos - tableStart);
		const doc = $anchorCell.node(0);
		const cells = map.cellsInRect(rect).filter((p) => p != $headCell.pos - tableStart);
		cells.unshift($headCell.pos - tableStart);
		const ranges = cells.map((pos) => {
			const cell = table.nodeAt(pos);
			if (!cell) throw new RangeError(`No cell with offset ${pos} found`);
			const from = tableStart + pos + 1;
			return new SelectionRange(doc.resolve(from), doc.resolve(from + cell.content.size));
		});
		super(ranges[0].$from, ranges[0].$to, ranges);
		this.$anchorCell = $anchorCell;
		this.$headCell = $headCell;
	}
	map(doc, mapping) {
		const $anchorCell = doc.resolve(mapping.map(this.$anchorCell.pos));
		const $headCell = doc.resolve(mapping.map(this.$headCell.pos));
		if (pointsAtCell($anchorCell) && pointsAtCell($headCell) && inSameTable($anchorCell, $headCell)) {
			const tableChanged = this.$anchorCell.node(-1) != $anchorCell.node(-1);
			if (tableChanged && this.isRowSelection()) return CellSelection.rowSelection($anchorCell, $headCell);
			else if (tableChanged && this.isColSelection()) return CellSelection.colSelection($anchorCell, $headCell);
			else return new CellSelection($anchorCell, $headCell);
		}
		return TextSelection.between($anchorCell, $headCell);
	}
	content() {
		const table = this.$anchorCell.node(-1);
		const map = TableMap.get(table);
		const tableStart = this.$anchorCell.start(-1);
		const rect = map.rectBetween(this.$anchorCell.pos - tableStart, this.$headCell.pos - tableStart);
		const seen = {};
		const rows = [];
		for (let row = rect.top; row < rect.bottom; row++) {
			const rowContent = [];
			for (let index = row * map.width + rect.left, col = rect.left; col < rect.right; col++, index++) {
				const pos = map.map[index];
				if (seen[pos]) continue;
				seen[pos] = true;
				const cellRect = map.findCell(pos);
				let cell = table.nodeAt(pos);
				if (!cell) throw new RangeError(`No cell with offset ${pos} found`);
				const extraLeft = rect.left - cellRect.left;
				const extraRight = cellRect.right - rect.right;
				if (extraLeft > 0 || extraRight > 0) {
					let attrs = cell.attrs;
					if (extraLeft > 0) attrs = removeColSpan(attrs, 0, extraLeft);
					if (extraRight > 0) attrs = removeColSpan(attrs, attrs.colspan - extraRight, extraRight);
					if (cellRect.left < rect.left) {
						cell = cell.type.createAndFill(attrs);
						if (!cell) throw new RangeError(`Could not create cell with attrs ${JSON.stringify(attrs)}`);
					} else cell = cell.type.create(attrs, cell.content);
				}
				if (cellRect.top < rect.top || cellRect.bottom > rect.bottom) {
					const attrs = {
						...cell.attrs,
						rowspan: Math.min(cellRect.bottom, rect.bottom) - Math.max(cellRect.top, rect.top),
					};
					if (cellRect.top < rect.top) cell = cell.type.createAndFill(attrs);
					else cell = cell.type.create(attrs, cell.content);
				}
				rowContent.push(cell);
			}
			rows.push(table.child(row).copy(Fragment.from(rowContent)));
		}
		const fragment = this.isColSelection() && this.isRowSelection() ? table : rows;
		return new Slice(Fragment.from(fragment), 1, 1);
	}
	replace(tr, content = Slice.empty) {
		const mapFrom = tr.steps.length,
			ranges = this.ranges;
		for (let i = 0; i < ranges.length; i++) {
			const { $from, $to } = ranges[i],
				mapping = tr.mapping.slice(mapFrom);
			tr.replace(mapping.map($from.pos), mapping.map($to.pos), i ? Slice.empty : content);
		}
		const sel = Selection.findFrom(tr.doc.resolve(tr.mapping.slice(mapFrom).map(this.to)), -1);
		if (sel) tr.setSelection(sel);
	}
	replaceWith(tr, node) {
		this.replace(tr, new Slice(Fragment.from(node), 0, 0));
	}
	forEachCell(f) {
		const table = this.$anchorCell.node(-1);
		const map = TableMap.get(table);
		const tableStart = this.$anchorCell.start(-1);
		const cells = map.cellsInRect(map.rectBetween(this.$anchorCell.pos - tableStart, this.$headCell.pos - tableStart));
		for (let i = 0; i < cells.length; i++) f(table.nodeAt(cells[i]), tableStart + cells[i]);
	}
	isColSelection() {
		const anchorTop = this.$anchorCell.index(-1);
		const headTop = this.$headCell.index(-1);
		if (Math.min(anchorTop, headTop) > 0) return false;
		const anchorBottom = anchorTop + this.$anchorCell.nodeAfter.attrs.rowspan;
		const headBottom = headTop + this.$headCell.nodeAfter.attrs.rowspan;
		return Math.max(anchorBottom, headBottom) == this.$headCell.node(-1).childCount;
	}
	static colSelection($anchorCell, $headCell = $anchorCell) {
		const table = $anchorCell.node(-1);
		const map = TableMap.get(table);
		const tableStart = $anchorCell.start(-1);
		const anchorRect = map.findCell($anchorCell.pos - tableStart);
		const headRect = map.findCell($headCell.pos - tableStart);
		const doc = $anchorCell.node(0);
		if (anchorRect.top <= headRect.top) {
			if (anchorRect.top > 0) $anchorCell = doc.resolve(tableStart + map.map[anchorRect.left]);
			if (headRect.bottom < map.height) $headCell = doc.resolve(tableStart + map.map[map.width * (map.height - 1) + headRect.right - 1]);
		} else {
			if (headRect.top > 0) $headCell = doc.resolve(tableStart + map.map[headRect.left]);
			if (anchorRect.bottom < map.height) $anchorCell = doc.resolve(tableStart + map.map[map.width * (map.height - 1) + anchorRect.right - 1]);
		}
		return new CellSelection($anchorCell, $headCell);
	}
	isRowSelection() {
		const table = this.$anchorCell.node(-1);
		const map = TableMap.get(table);
		const tableStart = this.$anchorCell.start(-1);
		const anchorLeft = map.colCount(this.$anchorCell.pos - tableStart);
		const headLeft = map.colCount(this.$headCell.pos - tableStart);
		if (Math.min(anchorLeft, headLeft) > 0) return false;
		const anchorRight = anchorLeft + this.$anchorCell.nodeAfter.attrs.colspan;
		const headRight = headLeft + this.$headCell.nodeAfter.attrs.colspan;
		return Math.max(anchorRight, headRight) == map.width;
	}
	eq(other) {
		return other instanceof CellSelection && other.$anchorCell.pos == this.$anchorCell.pos && other.$headCell.pos == this.$headCell.pos;
	}
	static rowSelection($anchorCell, $headCell = $anchorCell) {
		const table = $anchorCell.node(-1);
		const map = TableMap.get(table);
		const tableStart = $anchorCell.start(-1);
		const anchorRect = map.findCell($anchorCell.pos - tableStart);
		const headRect = map.findCell($headCell.pos - tableStart);
		const doc = $anchorCell.node(0);
		if (anchorRect.left <= headRect.left) {
			if (anchorRect.left > 0) $anchorCell = doc.resolve(tableStart + map.map[anchorRect.top * map.width]);
			if (headRect.right < map.width) $headCell = doc.resolve(tableStart + map.map[map.width * (headRect.top + 1) - 1]);
		} else {
			if (headRect.left > 0) $headCell = doc.resolve(tableStart + map.map[headRect.top * map.width]);
			if (anchorRect.right < map.width) $anchorCell = doc.resolve(tableStart + map.map[map.width * (anchorRect.top + 1) - 1]);
		}
		return new CellSelection($anchorCell, $headCell);
	}
	toJSON() {
		return {
			type: "cell",
			anchor: this.$anchorCell.pos,
			head: this.$headCell.pos,
		};
	}
	static fromJSON(doc, json) {
		return new CellSelection(doc.resolve(json.anchor), doc.resolve(json.head));
	}
	static create(doc, anchorCell, headCell = anchorCell) {
		return new CellSelection(doc.resolve(anchorCell), doc.resolve(headCell));
	}
	getBookmark() {
		return new CellBookmark(this.$anchorCell.pos, this.$headCell.pos);
	}
};
CellSelection.prototype.visible = false;
Selection.jsonID("cell", CellSelection);
/**
 * @public
 */
var CellBookmark = class CellBookmark {
	constructor(anchor, head) {
		this.anchor = anchor;
		this.head = head;
	}
	map(mapping) {
		return new CellBookmark(mapping.map(this.anchor), mapping.map(this.head));
	}
	resolve(doc) {
		const $anchorCell = doc.resolve(this.anchor),
			$headCell = doc.resolve(this.head);
		if (
			$anchorCell.parent.type.spec.tableRole == "row" &&
			$headCell.parent.type.spec.tableRole == "row" &&
			$anchorCell.index() < $anchorCell.parent.childCount &&
			$headCell.index() < $headCell.parent.childCount &&
			inSameTable($anchorCell, $headCell)
		)
			return new CellSelection($anchorCell, $headCell);
		else return Selection.near($headCell, 1);
	}
};
function drawCellSelection(state) {
	if (!(state.selection instanceof CellSelection)) return null;
	const cells = [];
	state.selection.forEachCell((node, pos) => {
		cells.push(Decoration.node(pos, pos + node.nodeSize, { class: "selectedCell" }));
	});
	return DecorationSet.create(state.doc, cells);
}
function isCellBoundarySelection({ $from, $to }) {
	if ($from.pos == $to.pos || $from.pos < $to.pos - 6) return false;
	let afterFrom = $from.pos;
	let beforeTo = $to.pos;
	let depth = $from.depth;
	for (; depth >= 0; depth--, afterFrom++) if ($from.after(depth + 1) < $from.end(depth)) break;
	for (let d = $to.depth; d >= 0; d--, beforeTo--) if ($to.before(d + 1) > $to.start(d)) break;
	return afterFrom == beforeTo && /row|table/.test($from.node(depth).type.spec.tableRole);
}
function isTextSelectionAcrossCells({ $from, $to }) {
	let fromCellBoundaryNode;
	let toCellBoundaryNode;
	for (let i = $from.depth; i > 0; i--) {
		const node = $from.node(i);
		if (node.type.spec.tableRole === "cell" || node.type.spec.tableRole === "header_cell") {
			fromCellBoundaryNode = node;
			break;
		}
	}
	for (let i = $to.depth; i > 0; i--) {
		const node = $to.node(i);
		if (node.type.spec.tableRole === "cell" || node.type.spec.tableRole === "header_cell") {
			toCellBoundaryNode = node;
			break;
		}
	}
	return fromCellBoundaryNode !== toCellBoundaryNode && $to.parentOffset === 0;
}
function normalizeSelection(state, tr, allowTableNodeSelection) {
	const sel = (tr || state).selection;
	const doc = (tr || state).doc;
	let normalize;
	let role;
	if (sel instanceof NodeSelection && (role = sel.node.type.spec.tableRole)) {
		if (role == "cell" || role == "header_cell") normalize = CellSelection.create(doc, sel.from);
		else if (role == "row") {
			const $cell = doc.resolve(sel.from + 1);
			normalize = CellSelection.rowSelection($cell, $cell);
		} else if (!allowTableNodeSelection) {
			const map = TableMap.get(sel.node);
			const start = sel.from + 1;
			const lastCell = start + map.map[map.width * map.height - 1];
			normalize = CellSelection.create(doc, start + 1, lastCell);
		}
	} else if (sel instanceof TextSelection && isCellBoundarySelection(sel)) normalize = TextSelection.create(doc, sel.from);
	else if (sel instanceof TextSelection && isTextSelectionAcrossCells(sel)) normalize = TextSelection.create(doc, sel.$from.start(), sel.$from.end());
	if (normalize) (tr || (tr = state.tr)).setSelection(normalize);
	return tr;
}

//#endregion
//#region src/fixtables.ts
/**
 * @public
 */
const fixTablesKey = new PluginKey("fix-tables");
/**
 * Helper for iterating through the nodes in a document that changed
 * compared to the given previous document. Useful for avoiding
 * duplicate work on each transaction.
 *
 * @public
 */
function changedDescendants(old, cur, offset, f) {
	const oldSize = old.childCount,
		curSize = cur.childCount;
	outer: for (let i = 0, j = 0; i < curSize; i++) {
		const child = cur.child(i);
		for (let scan = j, e = Math.min(oldSize, i + 3); scan < e; scan++)
			if (old.child(scan) == child) {
				j = scan + 1;
				offset += child.nodeSize;
				continue outer;
			}
		f(child, offset);
		if (j < oldSize && old.child(j).sameMarkup(child)) changedDescendants(old.child(j), child, offset + 1, f);
		else child.nodesBetween(0, child.content.size, f, offset + 1);
		offset += child.nodeSize;
	}
}
/**
 * Inspect all tables in the given state's document and return a
 * transaction that fixes them, if necessary. If `oldState` was
 * provided, that is assumed to hold a previous, known-good state,
 * which will be used to avoid re-scanning unchanged parts of the
 * document.
 *
 * @public
 */
function fixTables(state, oldState) {
	let tr;
	const check = (node, pos) => {
		if (node.type.spec.tableRole == "table") tr = fixTable(state, node, pos, tr);
	};
	if (!oldState) state.doc.descendants(check);
	else if (oldState.doc != state.doc) changedDescendants(oldState.doc, state.doc, 0, check);
	return tr;
}
function fixTable(state, table, tablePos, tr) {
	const map = TableMap.get(table);
	if (!map.problems) return tr;
	if (!tr) tr = state.tr;
	const mustAdd = [];
	for (let i = 0; i < map.height; i++) mustAdd.push(0);
	for (let i = 0; i < map.problems.length; i++) {
		const prob = map.problems[i];
		if (prob.type == "collision") {
			const cell = table.nodeAt(prob.pos);
			if (!cell) continue;
			const attrs = cell.attrs;
			for (let j = 0; j < attrs.rowspan; j++) mustAdd[prob.row + j] += prob.n;
			tr.setNodeMarkup(tr.mapping.map(tablePos + 1 + prob.pos), null, removeColSpan(attrs, attrs.colspan - prob.n, prob.n));
		} else if (prob.type == "missing") mustAdd[prob.row] += prob.n;
		else if (prob.type == "overlong_rowspan") {
			const cell = table.nodeAt(prob.pos);
			if (!cell) continue;
			tr.setNodeMarkup(tr.mapping.map(tablePos + 1 + prob.pos), null, {
				...cell.attrs,
				rowspan: cell.attrs.rowspan - prob.n,
			});
		} else if (prob.type == "colwidth mismatch") {
			const cell = table.nodeAt(prob.pos);
			if (!cell) continue;
			tr.setNodeMarkup(tr.mapping.map(tablePos + 1 + prob.pos), null, {
				...cell.attrs,
				colwidth: prob.colwidth,
			});
		} else if (prob.type == "zero_sized") {
			const pos = tr.mapping.map(tablePos);
			tr.delete(pos, pos + table.nodeSize);
		}
	}
	let first, last;
	for (let i = 0; i < mustAdd.length; i++)
		if (mustAdd[i]) {
			if (first == null) first = i;
			last = i;
		}
	for (let i = 0, pos = tablePos + 1; i < map.height; i++) {
		const row = table.child(i);
		const end = pos + row.nodeSize;
		const add = mustAdd[i];
		if (add > 0) {
			let role = "cell";
			if (row.firstChild) role = row.firstChild.type.spec.tableRole;
			const nodes = [];
			for (let j = 0; j < add; j++) {
				const node = tableNodeTypes(state.schema)[role].createAndFill();
				if (node) nodes.push(node);
			}
			const side = (i == 0 || first == i - 1) && last == i ? pos + 1 : end - 1;
			tr.insert(tr.mapping.map(side), nodes);
		}
		pos = end;
	}
	return tr.setMeta(fixTablesKey, { fixTables: true });
}

//#endregion
//#region src/commands.ts
/**
 * Helper to get the selected rectangle in a table, if any. Adds table
 * map, table node, and table start offset to the object for
 * convenience.
 *
 * @public
 */
function selectedRect(state) {
	const sel = state.selection;
	const $pos = selectionCell(state);
	const table = $pos.node(-1);
	const tableStart = $pos.start(-1);
	const map = TableMap.get(table);
	return {
		...(sel instanceof CellSelection ? map.rectBetween(sel.$anchorCell.pos - tableStart, sel.$headCell.pos - tableStart) : map.findCell($pos.pos - tableStart)),
		tableStart,
		map,
		table,
	};
}
/**
 * Add a column at the given position in a table.
 *
 * @public
 */
function addColumn(tr, { map, tableStart, table }, col) {
	let refColumn = col > 0 ? -1 : 0;
	if (columnIsHeader(map, table, col + refColumn)) refColumn = col == 0 || col == map.width ? null : 0;
	for (let row = 0; row < map.height; row++) {
		const index = row * map.width + col;
		if (col > 0 && col < map.width && map.map[index - 1] == map.map[index]) {
			const pos = map.map[index];
			const cell = table.nodeAt(pos);
			tr.setNodeMarkup(tr.mapping.map(tableStart + pos), null, addColSpan(cell.attrs, col - map.colCount(pos)));
			row += cell.attrs.rowspan - 1;
		} else {
			const type = refColumn == null ? tableNodeTypes(table.type.schema).cell : table.nodeAt(map.map[index + refColumn]).type;
			const pos = map.positionAt(row, col, table);
			tr.insert(tr.mapping.map(tableStart + pos), type.createAndFill());
		}
	}
	return tr;
}
/**
 * Command to add a column before the column with the selection.
 *
 * @public
 */
function addColumnBefore(state, dispatch) {
	if (!isInTable(state)) return false;
	if (dispatch) {
		const rect = selectedRect(state);
		dispatch(addColumn(state.tr, rect, rect.left));
	}
	return true;
}
/**
 * Command to add a column after the column with the selection.
 *
 * @public
 */
function addColumnAfter(state, dispatch) {
	if (!isInTable(state)) return false;
	if (dispatch) {
		const rect = selectedRect(state);
		dispatch(addColumn(state.tr, rect, rect.right));
	}
	return true;
}
/**
 * @public
 */
function removeColumn(tr, { map, table, tableStart }, col) {
	const mapStart = tr.mapping.maps.length;
	for (let row = 0; row < map.height; ) {
		const index = row * map.width + col;
		const pos = map.map[index];
		const cell = table.nodeAt(pos);
		const attrs = cell.attrs;
		if ((col > 0 && map.map[index - 1] == pos) || (col < map.width - 1 && map.map[index + 1] == pos))
			tr.setNodeMarkup(tr.mapping.slice(mapStart).map(tableStart + pos), null, removeColSpan(attrs, col - map.colCount(pos)));
		else {
			const start = tr.mapping.slice(mapStart).map(tableStart + pos);
			tr.delete(start, start + cell.nodeSize);
		}
		row += attrs.rowspan;
	}
}
/**
 * Command function that removes the selected columns from a table.
 *
 * @public
 */
function deleteColumn(state, dispatch) {
	if (!isInTable(state)) return false;
	if (dispatch) {
		const rect = selectedRect(state);
		const tr = state.tr;
		if (rect.left == 0 && rect.right == rect.map.width) return false;
		for (let i = rect.right - 1; ; i--) {
			removeColumn(tr, rect, i);
			if (i == rect.left) break;
			const table = rect.tableStart ? tr.doc.nodeAt(rect.tableStart - 1) : tr.doc;
			if (!table) throw new RangeError("No table found");
			rect.table = table;
			rect.map = TableMap.get(table);
		}
		dispatch(tr);
	}
	return true;
}
/**
 * @public
 */
function rowIsHeader(map, table, row) {
	var _table$nodeAt;
	const headerCell = tableNodeTypes(table.type.schema).header_cell;
	for (let col = 0; col < map.width; col++)
		if (((_table$nodeAt = table.nodeAt(map.map[col + row * map.width])) === null || _table$nodeAt === void 0 ? void 0 : _table$nodeAt.type) != headerCell) return false;
	return true;
}
/**
 * @public
 */
function addRow(tr, { map, tableStart, table }, row) {
	let rowPos = tableStart;
	for (let i = 0; i < row; i++) rowPos += table.child(i).nodeSize;
	const cells = [];
	let refRow = row > 0 ? -1 : 0;
	if (rowIsHeader(map, table, row + refRow)) refRow = row == 0 || row == map.height ? null : 0;
	for (let col = 0, index = map.width * row; col < map.width; col++, index++)
		if (row > 0 && row < map.height && map.map[index] == map.map[index - map.width]) {
			const pos = map.map[index];
			const attrs = table.nodeAt(pos).attrs;
			tr.setNodeMarkup(tableStart + pos, null, {
				...attrs,
				rowspan: attrs.rowspan + 1,
			});
			col += attrs.colspan - 1;
		} else {
			var _table$nodeAt2;
			const type =
				refRow == null ? tableNodeTypes(table.type.schema).cell
				: (_table$nodeAt2 = table.nodeAt(map.map[index + refRow * map.width])) === null || _table$nodeAt2 === void 0 ? void 0
				: _table$nodeAt2.type;
			const node = type === null || type === void 0 ? void 0 : type.createAndFill();
			if (node) cells.push(node);
		}
	tr.insert(rowPos, tableNodeTypes(table.type.schema).row.create(null, cells));
	return tr;
}
/**
 * Add a table row before the selection.
 *
 * @public
 */
function addRowBefore(state, dispatch) {
	if (!isInTable(state)) return false;
	if (dispatch) {
		const rect = selectedRect(state);
		dispatch(addRow(state.tr, rect, rect.top));
	}
	return true;
}
/**
 * Add a table row after the selection.
 *
 * @public
 */
function addRowAfter(state, dispatch) {
	if (!isInTable(state)) return false;
	if (dispatch) {
		const rect = selectedRect(state);
		dispatch(addRow(state.tr, rect, rect.bottom));
	}
	return true;
}
/**
 * @public
 */
function removeRow(tr, { map, table, tableStart }, row) {
	let rowPos = 0;
	for (let i = 0; i < row; i++) rowPos += table.child(i).nodeSize;
	const nextRow = rowPos + table.child(row).nodeSize;
	const mapFrom = tr.mapping.maps.length;
	tr.delete(rowPos + tableStart, nextRow + tableStart);
	const seen = /* @__PURE__ */ new Set();
	for (let col = 0, index = row * map.width; col < map.width; col++, index++) {
		const pos = map.map[index];
		if (seen.has(pos)) continue;
		seen.add(pos);
		if (row > 0 && pos == map.map[index - map.width]) {
			const attrs = table.nodeAt(pos).attrs;
			tr.setNodeMarkup(tr.mapping.slice(mapFrom).map(pos + tableStart), null, {
				...attrs,
				rowspan: attrs.rowspan - 1,
			});
			col += attrs.colspan - 1;
		} else if (row < map.height && pos == map.map[index + map.width]) {
			const cell = table.nodeAt(pos);
			const attrs = cell.attrs;
			const copy = cell.type.create(
				{
					...attrs,
					rowspan: cell.attrs.rowspan - 1,
				},
				cell.content,
			);
			const newPos = map.positionAt(row + 1, col, table);
			tr.insert(tr.mapping.slice(mapFrom).map(tableStart + newPos), copy);
			col += attrs.colspan - 1;
		}
	}
}
/**
 * Remove the selected rows from a table.
 *
 * @public
 */
function deleteRow(state, dispatch) {
	if (!isInTable(state)) return false;
	if (dispatch) {
		const rect = selectedRect(state),
			tr = state.tr;
		if (rect.top == 0 && rect.bottom == rect.map.height) return false;
		for (let i = rect.bottom - 1; ; i--) {
			removeRow(tr, rect, i);
			if (i == rect.top) break;
			const table = rect.tableStart ? tr.doc.nodeAt(rect.tableStart - 1) : tr.doc;
			if (!table) throw new RangeError("No table found");
			rect.table = table;
			rect.map = TableMap.get(rect.table);
		}
		dispatch(tr);
	}
	return true;
}
function isEmpty$1(cell) {
	const c = cell.content;
	return c.childCount == 1 && c.child(0).isTextblock && c.child(0).childCount == 0;
}
function cellsOverlapRectangle({ width, height, map }, rect) {
	let indexTop = rect.top * width + rect.left,
		indexLeft = indexTop;
	let indexBottom = (rect.bottom - 1) * width + rect.left,
		indexRight = indexTop + (rect.right - rect.left - 1);
	for (let i = rect.top; i < rect.bottom; i++) {
		if ((rect.left > 0 && map[indexLeft] == map[indexLeft - 1]) || (rect.right < width && map[indexRight] == map[indexRight + 1])) return true;
		indexLeft += width;
		indexRight += width;
	}
	for (let i = rect.left; i < rect.right; i++) {
		if ((rect.top > 0 && map[indexTop] == map[indexTop - width]) || (rect.bottom < height && map[indexBottom] == map[indexBottom + width])) return true;
		indexTop++;
		indexBottom++;
	}
	return false;
}
/**
 * Merge the selected cells into a single cell. Only available when
 * the selected cells' outline forms a rectangle.
 *
 * @public
 */
function mergeCells(state, dispatch) {
	const sel = state.selection;
	if (!(sel instanceof CellSelection) || sel.$anchorCell.pos == sel.$headCell.pos) return false;
	const rect = selectedRect(state),
		{ map } = rect;
	if (cellsOverlapRectangle(map, rect)) return false;
	if (dispatch) {
		const tr = state.tr;
		const seen = {};
		let content = Fragment.empty;
		let mergedPos;
		let mergedCell;
		for (let row = rect.top; row < rect.bottom; row++)
			for (let col = rect.left; col < rect.right; col++) {
				const cellPos = map.map[row * map.width + col];
				const cell = rect.table.nodeAt(cellPos);
				if (seen[cellPos] || !cell) continue;
				seen[cellPos] = true;
				if (mergedPos == null) {
					mergedPos = cellPos;
					mergedCell = cell;
				} else {
					if (!isEmpty$1(cell)) content = content.append(cell.content);
					const mapped = tr.mapping.map(cellPos + rect.tableStart);
					tr.delete(mapped, mapped + cell.nodeSize);
				}
			}
		if (mergedPos == null || mergedCell == null) return true;
		tr.setNodeMarkup(mergedPos + rect.tableStart, null, {
			...addColSpan(mergedCell.attrs, mergedCell.attrs.colspan, rect.right - rect.left - mergedCell.attrs.colspan),
			rowspan: rect.bottom - rect.top,
		});
		if (content.size > 0) {
			const end = mergedPos + 1 + mergedCell.content.size;
			const start = isEmpty$1(mergedCell) ? mergedPos + 1 : end;
			tr.replaceWith(start + rect.tableStart, end + rect.tableStart, content);
		}
		tr.setSelection(new CellSelection(tr.doc.resolve(mergedPos + rect.tableStart)));
		dispatch(tr);
	}
	return true;
}
/**
 * Split a selected cell, whose rowpan or colspan is greater than one,
 * into smaller cells. Use the first cell type for the new cells.
 *
 * @public
 */
function splitCell(state, dispatch) {
	const nodeTypes = tableNodeTypes(state.schema);
	return splitCellWithType(({ node }) => {
		return nodeTypes[node.type.spec.tableRole];
	})(state, dispatch);
}
/**
 * Split a selected cell, whose rowpan or colspan is greater than one,
 * into smaller cells with the cell type (th, td) returned by getType function.
 *
 * @public
 */
function splitCellWithType(getCellType) {
	return (state, dispatch) => {
		const sel = state.selection;
		let cellNode;
		let cellPos;
		if (!(sel instanceof CellSelection)) {
			var _cellAround;
			cellNode = cellWrapping(sel.$from);
			if (!cellNode) return false;
			cellPos = (_cellAround = cellAround(sel.$from)) === null || _cellAround === void 0 ? void 0 : _cellAround.pos;
		} else {
			if (sel.$anchorCell.pos != sel.$headCell.pos) return false;
			cellNode = sel.$anchorCell.nodeAfter;
			cellPos = sel.$anchorCell.pos;
		}
		if (cellNode == null || cellPos == null) return false;
		if (cellNode.attrs.colspan == 1 && cellNode.attrs.rowspan == 1) return false;
		if (dispatch) {
			let baseAttrs = cellNode.attrs;
			const attrs = [];
			const colwidth = baseAttrs.colwidth;
			if (baseAttrs.rowspan > 1)
				baseAttrs = {
					...baseAttrs,
					rowspan: 1,
				};
			if (baseAttrs.colspan > 1)
				baseAttrs = {
					...baseAttrs,
					colspan: 1,
				};
			const rect = selectedRect(state),
				tr = state.tr;
			for (let i = 0; i < rect.right - rect.left; i++)
				attrs.push(
					colwidth ?
						{
							...baseAttrs,
							colwidth: colwidth && colwidth[i] ? [colwidth[i]] : null,
						}
					:	baseAttrs,
				);
			let lastCell;
			for (let row = rect.top; row < rect.bottom; row++) {
				let pos = rect.map.positionAt(row, rect.left, rect.table);
				if (row == rect.top) pos += cellNode.nodeSize;
				for (let col = rect.left, i = 0; col < rect.right; col++, i++) {
					if (col == rect.left && row == rect.top) continue;
					tr.insert(
						(lastCell = tr.mapping.map(pos + rect.tableStart, 1)),
						getCellType({
							node: cellNode,
							row,
							col,
						}).createAndFill(attrs[i]),
					);
				}
			}
			tr.setNodeMarkup(
				cellPos,
				getCellType({
					node: cellNode,
					row: rect.top,
					col: rect.left,
				}),
				attrs[0],
			);
			if (sel instanceof CellSelection) tr.setSelection(new CellSelection(tr.doc.resolve(sel.$anchorCell.pos), lastCell ? tr.doc.resolve(lastCell) : void 0));
			dispatch(tr);
		}
		return true;
	};
}
function deprecated_toggleHeader(type) {
	return function (state, dispatch) {
		if (!isInTable(state)) return false;
		if (dispatch) {
			const types = tableNodeTypes(state.schema);
			const rect = selectedRect(state),
				tr = state.tr;
			const cells = rect.map.cellsInRect(
				type == "column" ?
					{
						left: rect.left,
						top: 0,
						right: rect.right,
						bottom: rect.map.height,
					}
				: type == "row" ?
					{
						left: 0,
						top: rect.top,
						right: rect.map.width,
						bottom: rect.bottom,
					}
				:	rect,
			);
			const nodes = cells.map((pos) => rect.table.nodeAt(pos));
			for (let i = 0; i < cells.length; i++) if (nodes[i].type == types.header_cell) tr.setNodeMarkup(rect.tableStart + cells[i], types.cell, nodes[i].attrs);
			if (tr.steps.length === 0) for (let i = 0; i < cells.length; i++) tr.setNodeMarkup(rect.tableStart + cells[i], types.header_cell, nodes[i].attrs);
			dispatch(tr);
		}
		return true;
	};
}
function isHeaderEnabledByType(type, rect, types) {
	const cellPositions = rect.map.cellsInRect({
		left: 0,
		top: 0,
		right: type == "row" ? rect.map.width : 1,
		bottom: type == "column" ? rect.map.height : 1,
	});
	for (let i = 0; i < cellPositions.length; i++) {
		const cell = rect.table.nodeAt(cellPositions[i]);
		if (cell && cell.type !== types.header_cell) return false;
	}
	return true;
}
/**
 * Toggles between row/column header and normal cells (Only applies to first row/column).
 * For deprecated behavior pass `useDeprecatedLogic` in options with true.
 *
 * @public
 */
function toggleHeader(type, options) {
	options = options || { useDeprecatedLogic: false };
	if (options.useDeprecatedLogic) return deprecated_toggleHeader(type);
	return function (state, dispatch) {
		if (!isInTable(state)) return false;
		if (dispatch) {
			const types = tableNodeTypes(state.schema);
			const rect = selectedRect(state),
				tr = state.tr;
			const isHeaderRowEnabled = isHeaderEnabledByType("row", rect, types);
			const isHeaderColumnEnabled = isHeaderEnabledByType("column", rect, types);
			const selectionStartsAt =
				(
					type === "column" ? isHeaderRowEnabled
					: type === "row" ? isHeaderColumnEnabled
					: false
				) ?
					1
				:	0;
			const cellsRect =
				type == "column" ?
					{
						left: 0,
						top: selectionStartsAt,
						right: 1,
						bottom: rect.map.height,
					}
				: type == "row" ?
					{
						left: selectionStartsAt,
						top: 0,
						right: rect.map.width,
						bottom: 1,
					}
				:	rect;
			const newType =
				type == "column" ?
					isHeaderColumnEnabled ? types.cell
					:	types.header_cell
				: type == "row" ?
					isHeaderRowEnabled ? types.cell
					:	types.header_cell
				:	types.cell;
			rect.map.cellsInRect(cellsRect).forEach((relativeCellPos) => {
				const cellPos = relativeCellPos + rect.tableStart;
				const cell = tr.doc.nodeAt(cellPos);
				if (cell) tr.setNodeMarkup(cellPos, newType, cell.attrs);
			});
			dispatch(tr);
		}
		return true;
	};
}
/**
 * Toggles whether the selected row contains header cells.
 *
 * @public
 */
toggleHeader("row", { useDeprecatedLogic: true });
/**
 * Toggles whether the selected column contains header cells.
 *
 * @public
 */
toggleHeader("column", { useDeprecatedLogic: true });
/**
 * Toggles whether the selected cells are header cells.
 *
 * @public
 */
toggleHeader("cell", { useDeprecatedLogic: true });
function findNextCell($cell, dir) {
	if (dir < 0) {
		const before = $cell.nodeBefore;
		if (before) return $cell.pos - before.nodeSize;
		for (let row = $cell.index(-1) - 1, rowEnd = $cell.before(); row >= 0; row--) {
			const rowNode = $cell.node(-1).child(row);
			const lastChild = rowNode.lastChild;
			if (lastChild) return rowEnd - 1 - lastChild.nodeSize;
			rowEnd -= rowNode.nodeSize;
		}
	} else {
		if ($cell.index() < $cell.parent.childCount - 1) return $cell.pos + $cell.nodeAfter.nodeSize;
		const table = $cell.node(-1);
		for (let row = $cell.indexAfter(-1), rowStart = $cell.after(); row < table.childCount; row++) {
			const rowNode = table.child(row);
			if (rowNode.childCount) return rowStart + 1;
			rowStart += rowNode.nodeSize;
		}
	}
	return null;
}
/**
 * Returns a command for selecting the next (direction=1) or previous
 * (direction=-1) cell in a table.
 *
 * @public
 */
function goToNextCell(direction) {
	return function (state, dispatch) {
		if (!isInTable(state)) return false;
		const cell = findNextCell(selectionCell(state), direction);
		if (cell == null) return false;
		if (dispatch) {
			const $cell = state.doc.resolve(cell);
			dispatch(state.tr.setSelection(TextSelection.between($cell, moveCellForward($cell))).scrollIntoView());
		}
		return true;
	};
}
/**
 * Deletes the content of the selected cells, if they are not empty.
 *
 * @public
 */
function deleteCellSelection(state, dispatch) {
	const sel = state.selection;
	if (!(sel instanceof CellSelection)) return false;
	if (dispatch) {
		const tr = state.tr;
		const baseContent = tableNodeTypes(state.schema).cell.createAndFill().content;
		sel.forEachCell((cell, pos) => {
			if (!cell.content.eq(baseContent)) tr.replace(tr.mapping.map(pos + 1), tr.mapping.map(pos + cell.nodeSize - 1), new Slice(baseContent, 0, 0));
		});
		if (tr.docChanged) dispatch(tr);
	}
	return true;
}

//#endregion
//#region src/copypaste.ts
/**
 * Get a rectangular area of cells from a slice, or null if the outer
 * nodes of the slice aren't table cells or rows.
 *
 * @internal
 */
function pastedCells(slice) {
	if (slice.size === 0) return null;
	let { content, openStart, openEnd } = slice;
	while (content.childCount == 1 && ((openStart > 0 && openEnd > 0) || content.child(0).type.spec.tableRole == "table")) {
		openStart--;
		openEnd--;
		content = content.child(0).content;
	}
	const first = content.child(0);
	const role = first.type.spec.tableRole;
	const schema = first.type.schema,
		rows = [];
	if (role == "row")
		for (let i = 0; i < content.childCount; i++) {
			let cells = content.child(i).content;
			const left = i ? 0 : Math.max(0, openStart - 1);
			const right = i < content.childCount - 1 ? 0 : Math.max(0, openEnd - 1);
			if (left || right) cells = fitSlice(tableNodeTypes(schema).row, new Slice(cells, left, right)).content;
			rows.push(cells);
		}
	else if (role == "cell" || role == "header_cell") rows.push(openStart || openEnd ? fitSlice(tableNodeTypes(schema).row, new Slice(content, openStart, openEnd)).content : content);
	else return null;
	return ensureRectangular(schema, rows);
}
function ensureRectangular(schema, rows) {
	const widths = [];
	for (let i = 0; i < rows.length; i++) {
		const row = rows[i];
		for (let j = row.childCount - 1; j >= 0; j--) {
			const { rowspan, colspan } = row.child(j).attrs;
			for (let r = i; r < i + rowspan; r++) widths[r] = (widths[r] || 0) + colspan;
		}
	}
	let width = 0;
	for (let r = 0; r < widths.length; r++) width = Math.max(width, widths[r]);
	for (let r = 0; r < widths.length; r++) {
		if (r >= rows.length) rows.push(Fragment.empty);
		if (widths[r] < width) {
			const empty = tableNodeTypes(schema).cell.createAndFill();
			const cells = [];
			for (let i = widths[r]; i < width; i++) cells.push(empty);
			rows[r] = rows[r].append(Fragment.from(cells));
		}
	}
	return {
		height: rows.length,
		width,
		rows,
	};
}
function fitSlice(nodeType, slice) {
	const node = nodeType.createAndFill();
	return new Transform(node).replace(0, node.content.size, slice).doc;
}
/**
 * Clip or extend (repeat) the given set of cells to cover the given
 * width and height. Will clip rowspan/colspan cells at the edges when
 * they stick out.
 *
 * @internal
 */
function clipCells({ width, height, rows }, newWidth, newHeight) {
	if (width != newWidth) {
		const added = [];
		const newRows = [];
		for (let row = 0; row < rows.length; row++) {
			const frag = rows[row],
				cells = [];
			for (let col = added[row] || 0, i = 0; col < newWidth; i++) {
				let cell = frag.child(i % frag.childCount);
				if (col + cell.attrs.colspan > newWidth) cell = cell.type.createChecked(removeColSpan(cell.attrs, cell.attrs.colspan, col + cell.attrs.colspan - newWidth), cell.content);
				cells.push(cell);
				col += cell.attrs.colspan;
				for (let j = 1; j < cell.attrs.rowspan; j++) added[row + j] = (added[row + j] || 0) + cell.attrs.colspan;
			}
			newRows.push(Fragment.from(cells));
		}
		rows = newRows;
		width = newWidth;
	}
	if (height != newHeight) {
		const newRows = [];
		for (let row = 0, i = 0; row < newHeight; row++, i++) {
			const cells = [],
				source = rows[i % height];
			for (let j = 0; j < source.childCount; j++) {
				let cell = source.child(j);
				if (row + cell.attrs.rowspan > newHeight)
					cell = cell.type.create(
						{
							...cell.attrs,
							rowspan: Math.max(1, newHeight - cell.attrs.rowspan),
						},
						cell.content,
					);
				cells.push(cell);
			}
			newRows.push(Fragment.from(cells));
		}
		rows = newRows;
		height = newHeight;
	}
	return {
		width,
		height,
		rows,
	};
}
function growTable(tr, map, table, start, width, height, mapFrom) {
	const schema = tr.doc.type.schema;
	const types = tableNodeTypes(schema);
	let empty;
	let emptyHead;
	if (width > map.width)
		for (let row = 0, rowEnd = 0; row < map.height; row++) {
			const rowNode = table.child(row);
			rowEnd += rowNode.nodeSize;
			const cells = [];
			let add;
			if (rowNode.lastChild == null || rowNode.lastChild.type == types.cell) add = empty || (empty = types.cell.createAndFill());
			else add = emptyHead || (emptyHead = types.header_cell.createAndFill());
			for (let i = map.width; i < width; i++) cells.push(add);
			tr.insert(tr.mapping.slice(mapFrom).map(rowEnd - 1 + start), cells);
		}
	if (height > map.height) {
		const cells = [];
		for (let i = 0, start$1 = (map.height - 1) * map.width; i < Math.max(map.width, width); i++) {
			const header = i >= map.width ? false : table.nodeAt(map.map[start$1 + i]).type == types.header_cell;
			cells.push(header ? emptyHead || (emptyHead = types.header_cell.createAndFill()) : empty || (empty = types.cell.createAndFill()));
		}
		const emptyRow = types.row.create(null, Fragment.from(cells)),
			rows = [];
		for (let i = map.height; i < height; i++) rows.push(emptyRow);
		tr.insert(tr.mapping.slice(mapFrom).map(start + table.nodeSize - 2), rows);
	}
	return !!(empty || emptyHead);
}
function isolateHorizontal(tr, map, table, start, left, right, top, mapFrom) {
	if (top == 0 || top == map.height) return false;
	let found = false;
	for (let col = left; col < right; col++) {
		const index = top * map.width + col,
			pos = map.map[index];
		if (map.map[index - map.width] == pos) {
			found = true;
			const cell = table.nodeAt(pos);
			const { top: cellTop, left: cellLeft } = map.findCell(pos);
			tr.setNodeMarkup(tr.mapping.slice(mapFrom).map(pos + start), null, {
				...cell.attrs,
				rowspan: top - cellTop,
			});
			tr.insert(
				tr.mapping.slice(mapFrom).map(map.positionAt(top, cellLeft, table)),
				cell.type.createAndFill({
					...cell.attrs,
					rowspan: cellTop + cell.attrs.rowspan - top,
				}),
			);
			col += cell.attrs.colspan - 1;
		}
	}
	return found;
}
function isolateVertical(tr, map, table, start, top, bottom, left, mapFrom) {
	if (left == 0 || left == map.width) return false;
	let found = false;
	for (let row = top; row < bottom; row++) {
		const index = row * map.width + left,
			pos = map.map[index];
		if (map.map[index - 1] == pos) {
			found = true;
			const cell = table.nodeAt(pos);
			const cellLeft = map.colCount(pos);
			const updatePos = tr.mapping.slice(mapFrom).map(pos + start);
			tr.setNodeMarkup(updatePos, null, removeColSpan(cell.attrs, left - cellLeft, cell.attrs.colspan - (left - cellLeft)));
			tr.insert(updatePos + cell.nodeSize, cell.type.createAndFill(removeColSpan(cell.attrs, 0, left - cellLeft)));
			row += cell.attrs.rowspan - 1;
		}
	}
	return found;
}
/**
 * Insert the given set of cells (as returned by `pastedCells`) into a
 * table, at the position pointed at by rect.
 *
 * @internal
 */
function insertCells(state, dispatch, tableStart, rect, cells) {
	let table = tableStart ? state.doc.nodeAt(tableStart - 1) : state.doc;
	if (!table) throw new Error("No table found");
	let map = TableMap.get(table);
	const { top, left } = rect;
	const right = left + cells.width,
		bottom = top + cells.height;
	const tr = state.tr;
	let mapFrom = 0;
	function recomp() {
		table = tableStart ? tr.doc.nodeAt(tableStart - 1) : tr.doc;
		if (!table) throw new Error("No table found");
		map = TableMap.get(table);
		mapFrom = tr.mapping.maps.length;
	}
	if (growTable(tr, map, table, tableStart, right, bottom, mapFrom)) recomp();
	if (isolateHorizontal(tr, map, table, tableStart, left, right, top, mapFrom)) recomp();
	if (isolateHorizontal(tr, map, table, tableStart, left, right, bottom, mapFrom)) recomp();
	if (isolateVertical(tr, map, table, tableStart, top, bottom, left, mapFrom)) recomp();
	if (isolateVertical(tr, map, table, tableStart, top, bottom, right, mapFrom)) recomp();
	for (let row = top; row < bottom; row++) {
		const from = map.positionAt(row, left, table),
			to = map.positionAt(row, right, table);
		tr.replace(tr.mapping.slice(mapFrom).map(from + tableStart), tr.mapping.slice(mapFrom).map(to + tableStart), new Slice(cells.rows[row - top], 0, 0));
	}
	recomp();
	tr.setSelection(new CellSelection(tr.doc.resolve(tableStart + map.positionAt(top, left, table)), tr.doc.resolve(tableStart + map.positionAt(bottom - 1, right - 1, table))));
	dispatch(tr);
}

//#endregion
//#region src/input.ts
const handleKeyDown = keydownHandler({
	"ArrowLeft": arrow$4("horiz", -1),
	"ArrowRight": arrow$4("horiz", 1),
	"ArrowUp": arrow$4("vert", -1),
	"ArrowDown": arrow$4("vert", 1),
	"Shift-ArrowLeft": shiftArrow("horiz", -1),
	"Shift-ArrowRight": shiftArrow("horiz", 1),
	"Shift-ArrowUp": shiftArrow("vert", -1),
	"Shift-ArrowDown": shiftArrow("vert", 1),
	"Backspace": deleteCellSelection,
	"Mod-Backspace": deleteCellSelection,
	"Delete": deleteCellSelection,
	"Mod-Delete": deleteCellSelection,
});
function maybeSetSelection(state, dispatch, selection) {
	if (selection.eq(state.selection)) return false;
	if (dispatch) dispatch(state.tr.setSelection(selection).scrollIntoView());
	return true;
}
/**
 * @internal
 */
function arrow$4(axis, dir) {
	return (state, dispatch, view) => {
		if (!view) return false;
		const sel = state.selection;
		if (sel instanceof CellSelection) return maybeSetSelection(state, dispatch, Selection.near(sel.$headCell, dir));
		if (axis != "horiz" && !sel.empty) return false;
		const end = atEndOfCell(view, axis, dir);
		if (end == null) return false;
		if (axis == "horiz") return maybeSetSelection(state, dispatch, Selection.near(state.doc.resolve(sel.head + dir), dir));
		else {
			const $cell = state.doc.resolve(end);
			const $next = nextCell($cell, axis, dir);
			let newSel;
			if ($next) newSel = Selection.near($next, 1);
			else if (dir < 0) newSel = Selection.near(state.doc.resolve($cell.before(-1)), -1);
			else newSel = Selection.near(state.doc.resolve($cell.after(-1)), 1);
			return maybeSetSelection(state, dispatch, newSel);
		}
	};
}
function shiftArrow(axis, dir) {
	return (state, dispatch, view) => {
		if (!view) return false;
		const sel = state.selection;
		let cellSel;
		if (sel instanceof CellSelection) cellSel = sel;
		else {
			const end = atEndOfCell(view, axis, dir);
			if (end == null) return false;
			cellSel = new CellSelection(state.doc.resolve(end));
		}
		const $head = nextCell(cellSel.$headCell, axis, dir);
		if (!$head) return false;
		return maybeSetSelection(state, dispatch, new CellSelection(cellSel.$anchorCell, $head));
	};
}
function handleTripleClick(view, pos) {
	const doc = view.state.doc,
		$cell = cellAround(doc.resolve(pos));
	if (!$cell) return false;
	view.dispatch(view.state.tr.setSelection(new CellSelection($cell)));
	return true;
}
/**
 * @public
 */
function handlePaste(view, _, slice) {
	if (!isInTable(view.state)) return false;
	let cells = pastedCells(slice);
	const sel = view.state.selection;
	if (sel instanceof CellSelection) {
		if (!cells)
			cells = {
				width: 1,
				height: 1,
				rows: [Fragment.from(fitSlice(tableNodeTypes(view.state.schema).cell, slice))],
			};
		const table = sel.$anchorCell.node(-1);
		const start = sel.$anchorCell.start(-1);
		const rect = TableMap.get(table).rectBetween(sel.$anchorCell.pos - start, sel.$headCell.pos - start);
		cells = clipCells(cells, rect.right - rect.left, rect.bottom - rect.top);
		insertCells(view.state, view.dispatch, start, rect, cells);
		return true;
	} else if (cells) {
		const $cell = selectionCell(view.state);
		const start = $cell.start(-1);
		insertCells(view.state, view.dispatch, start, TableMap.get($cell.node(-1)).findCell($cell.pos - start), cells);
		return true;
	} else return false;
}
function handleMouseDown$1(view, startEvent) {
	var _cellUnderMouse;
	if (startEvent.button != 0) return;
	if (startEvent.ctrlKey || startEvent.metaKey) return;
	const startDOMCell = domInCell(view, startEvent.target);
	let $anchor;
	if (startEvent.shiftKey && view.state.selection instanceof CellSelection) {
		setCellSelection(view.state.selection.$anchorCell, startEvent);
		startEvent.preventDefault();
	} else if (
		startEvent.shiftKey &&
		startDOMCell &&
		($anchor = cellAround(view.state.selection.$anchor)) != null &&
		((_cellUnderMouse = cellUnderMouse(view, startEvent)) === null || _cellUnderMouse === void 0 ? void 0 : _cellUnderMouse.pos) != $anchor.pos
	) {
		setCellSelection($anchor, startEvent);
		startEvent.preventDefault();
	} else if (!startDOMCell) return;
	function setCellSelection($anchor$1, event) {
		let $head = cellUnderMouse(view, event);
		const starting = tableEditingKey.getState(view.state) == null;
		if (!$head || !inSameTable($anchor$1, $head))
			if (starting) $head = $anchor$1;
			else return;
		const selection = new CellSelection($anchor$1, $head);
		if (starting || !view.state.selection.eq(selection)) {
			const tr = view.state.tr.setSelection(selection);
			if (starting) tr.setMeta(tableEditingKey, $anchor$1.pos);
			view.dispatch(tr);
		}
	}
	function stop() {
		view.root.removeEventListener("mouseup", stop);
		view.root.removeEventListener("dragstart", stop);
		view.root.removeEventListener("mousemove", move);
		if (tableEditingKey.getState(view.state) != null) view.dispatch(view.state.tr.setMeta(tableEditingKey, -1));
	}
	function move(_event) {
		const event = _event;
		const anchor = tableEditingKey.getState(view.state);
		let $anchor$1;
		if (anchor != null) $anchor$1 = view.state.doc.resolve(anchor);
		else if (domInCell(view, event.target) != startDOMCell) {
			$anchor$1 = cellUnderMouse(view, startEvent);
			if (!$anchor$1) return stop();
		}
		if ($anchor$1) setCellSelection($anchor$1, event);
	}
	view.root.addEventListener("mouseup", stop);
	view.root.addEventListener("dragstart", stop);
	view.root.addEventListener("mousemove", move);
}
function atEndOfCell(view, axis, dir) {
	if (!(view.state.selection instanceof TextSelection)) return null;
	const { $head } = view.state.selection;
	for (let d = $head.depth - 1; d >= 0; d--) {
		const parent = $head.node(d);
		if ((dir < 0 ? $head.index(d) : $head.indexAfter(d)) != (dir < 0 ? 0 : parent.childCount)) return null;
		if (parent.type.spec.tableRole == "cell" || parent.type.spec.tableRole == "header_cell") {
			const cellPos = $head.before(d);
			const dirStr =
				axis == "vert" ?
					dir > 0 ?
						"down"
					:	"up"
				: dir > 0 ? "right"
				: "left";
			return view.endOfTextblock(dirStr) ? cellPos : null;
		}
	}
	return null;
}
function domInCell(view, dom) {
	for (; dom && dom != view.dom; dom = dom.parentNode) if (dom.nodeName == "TD" || dom.nodeName == "TH") return dom;
	return null;
}
function cellUnderMouse(view, event) {
	const mousePos = view.posAtCoords({
		left: event.clientX,
		top: event.clientY,
	});
	if (!mousePos) return null;
	let { inside, pos } = mousePos;
	return (inside >= 0 && cellAround(view.state.doc.resolve(inside))) || cellAround(view.state.doc.resolve(pos));
}

//#endregion
//#region src/tableview.ts
/**
 * @public
 */
var TableView = class {
	constructor(node, defaultCellMinWidth) {
		this.node = node;
		this.defaultCellMinWidth = defaultCellMinWidth;
		this.dom = document.createElement("div");
		this.dom.className = "tableWrapper";
		this.table = this.dom.appendChild(document.createElement("table"));
		this.table.style.setProperty("--default-cell-min-width", `${defaultCellMinWidth}px`);
		this.colgroup = this.table.appendChild(document.createElement("colgroup"));
		updateColumnsOnResize(node, this.colgroup, this.table, defaultCellMinWidth);
		this.contentDOM = this.table.appendChild(document.createElement("tbody"));
	}
	update(node) {
		if (node.type != this.node.type) return false;
		this.node = node;
		updateColumnsOnResize(node, this.colgroup, this.table, this.defaultCellMinWidth);
		return true;
	}
	ignoreMutation(record) {
		return record.type == "attributes" && (record.target == this.table || this.colgroup.contains(record.target));
	}
};
/**
 * @public
 */
function updateColumnsOnResize(node, colgroup, table, defaultCellMinWidth, overrideCol, overrideValue) {
	let totalWidth = 0;
	let fixedWidth = true;
	let nextDOM = colgroup.firstChild;
	const row = node.firstChild;
	if (!row) return;
	for (let i = 0, col = 0; i < row.childCount; i++) {
		const { colspan, colwidth } = row.child(i).attrs;
		for (let j = 0; j < colspan; j++, col++) {
			const hasWidth = overrideCol == col ? overrideValue : colwidth && colwidth[j];
			const cssWidth = hasWidth ? hasWidth + "px" : "";
			totalWidth += hasWidth || defaultCellMinWidth;
			if (!hasWidth) fixedWidth = false;
			if (!nextDOM) {
				const col$1 = document.createElement("col");
				col$1.style.width = cssWidth;
				colgroup.appendChild(col$1);
			} else {
				if (nextDOM.style.width != cssWidth) nextDOM.style.width = cssWidth;
				nextDOM = nextDOM.nextSibling;
			}
		}
	}
	while (nextDOM) {
		var _nextDOM$parentNode;
		const after = nextDOM.nextSibling;
		(_nextDOM$parentNode = nextDOM.parentNode) === null || _nextDOM$parentNode === void 0 || _nextDOM$parentNode.removeChild(nextDOM);
		nextDOM = after;
	}
	if (fixedWidth) {
		table.style.width = totalWidth + "px";
		table.style.minWidth = "";
	} else {
		table.style.width = "";
		table.style.minWidth = totalWidth + "px";
	}
}

//#endregion
//#region src/columnresizing.ts
/**
 * @public
 */
const columnResizingPluginKey = new PluginKey("tableColumnResizing");
/**
 * @public
 */
function columnResizing({ handleWidth = 5, cellMinWidth = 25, defaultCellMinWidth = 100, View = TableView, lastColumnResizable = true } = {}) {
	const plugin = new Plugin({
		key: columnResizingPluginKey,
		state: {
			init(_, state) {
				var _plugin$spec;
				const nodeViews =
					(_plugin$spec = plugin.spec) === null || _plugin$spec === void 0 || (_plugin$spec = _plugin$spec.props) === null || _plugin$spec === void 0 ? void 0 : _plugin$spec.nodeViews;
				const tableName = tableNodeTypes(state.schema).table.name;
				if (View && nodeViews)
					nodeViews[tableName] = (node, view) => {
						return new View(node, defaultCellMinWidth, view);
					};
				return new ResizeState(-1, false);
			},
			apply(tr, prev) {
				return prev.apply(tr);
			},
		},
		props: {
			attributes: (state) => {
				const pluginState = columnResizingPluginKey.getState(state);
				return pluginState && pluginState.activeHandle > -1 ? { class: "resize-cursor" } : {};
			},
			handleDOMEvents: {
				mousemove: (view, event) => {
					handleMouseMove(view, event, handleWidth, lastColumnResizable);
				},
				mouseleave: (view) => {
					handleMouseLeave(view);
				},
				mousedown: (view, event) => {
					handleMouseDown(view, event, cellMinWidth, defaultCellMinWidth);
				},
			},
			decorations: (state) => {
				const pluginState = columnResizingPluginKey.getState(state);
				if (pluginState && pluginState.activeHandle > -1) return handleDecorations(state, pluginState.activeHandle);
			},
			nodeViews: {},
		},
	});
	return plugin;
}
/**
 * @public
 */
var ResizeState = class ResizeState {
	constructor(activeHandle, dragging) {
		this.activeHandle = activeHandle;
		this.dragging = dragging;
	}
	apply(tr) {
		const state = this;
		const action = tr.getMeta(columnResizingPluginKey);
		if (action && action.setHandle != null) return new ResizeState(action.setHandle, false);
		if (action && action.setDragging !== void 0) return new ResizeState(state.activeHandle, action.setDragging);
		if (state.activeHandle > -1 && tr.docChanged) {
			let handle = tr.mapping.map(state.activeHandle, -1);
			if (!pointsAtCell(tr.doc.resolve(handle))) handle = -1;
			return new ResizeState(handle, state.dragging);
		}
		return state;
	}
};
function handleMouseMove(view, event, handleWidth, lastColumnResizable) {
	if (!view.editable) return;
	const pluginState = columnResizingPluginKey.getState(view.state);
	if (!pluginState) return;
	if (!pluginState.dragging) {
		const target = domCellAround(event.target);
		let cell = -1;
		if (target) {
			const { left, right } = target.getBoundingClientRect();
			if (event.clientX - left <= handleWidth) cell = edgeCell(view, event, "left", handleWidth);
			else if (right - event.clientX <= handleWidth) cell = edgeCell(view, event, "right", handleWidth);
		}
		if (cell != pluginState.activeHandle) {
			if (!lastColumnResizable && cell !== -1) {
				const $cell = view.state.doc.resolve(cell);
				const table = $cell.node(-1);
				const map = TableMap.get(table);
				const tableStart = $cell.start(-1);
				if (map.colCount($cell.pos - tableStart) + $cell.nodeAfter.attrs.colspan - 1 == map.width - 1) return;
			}
			updateHandle(view, cell);
		}
	}
}
function handleMouseLeave(view) {
	if (!view.editable) return;
	const pluginState = columnResizingPluginKey.getState(view.state);
	if (pluginState && pluginState.activeHandle > -1 && !pluginState.dragging) updateHandle(view, -1);
}
function handleMouseDown(view, event, cellMinWidth, defaultCellMinWidth) {
	var _view$dom$ownerDocume;
	if (!view.editable) return false;
	const win = (_view$dom$ownerDocume = view.dom.ownerDocument.defaultView) !== null && _view$dom$ownerDocume !== void 0 ? _view$dom$ownerDocume : window;
	const pluginState = columnResizingPluginKey.getState(view.state);
	if (!pluginState || pluginState.activeHandle == -1 || pluginState.dragging) return false;
	const cell = view.state.doc.nodeAt(pluginState.activeHandle);
	const width = currentColWidth(view, pluginState.activeHandle, cell.attrs);
	view.dispatch(
		view.state.tr.setMeta(columnResizingPluginKey, {
			setDragging: {
				startX: event.clientX,
				startWidth: width,
			},
		}),
	);
	function finish(event$1) {
		win.removeEventListener("mouseup", finish);
		win.removeEventListener("mousemove", move);
		const pluginState$1 = columnResizingPluginKey.getState(view.state);
		if (pluginState$1 === null || pluginState$1 === void 0 ? void 0 : pluginState$1.dragging) {
			updateColumnWidth(view, pluginState$1.activeHandle, draggedWidth(pluginState$1.dragging, event$1, cellMinWidth));
			view.dispatch(view.state.tr.setMeta(columnResizingPluginKey, { setDragging: null }));
		}
	}
	function move(event$1) {
		if (!event$1.which) return finish(event$1);
		const pluginState$1 = columnResizingPluginKey.getState(view.state);
		if (!pluginState$1) return;
		if (pluginState$1.dragging) {
			const dragged = draggedWidth(pluginState$1.dragging, event$1, cellMinWidth);
			displayColumnWidth(view, pluginState$1.activeHandle, dragged, defaultCellMinWidth);
		}
	}
	displayColumnWidth(view, pluginState.activeHandle, width, defaultCellMinWidth);
	win.addEventListener("mouseup", finish);
	win.addEventListener("mousemove", move);
	event.preventDefault();
	return true;
}
function currentColWidth(view, cellPos, { colspan, colwidth }) {
	const width = colwidth && colwidth[colwidth.length - 1];
	if (width) return width;
	const dom = view.domAtPos(cellPos);
	let domWidth = dom.node.childNodes[dom.offset].offsetWidth,
		parts = colspan;
	if (colwidth) {
		for (let i = 0; i < colspan; i++)
			if (colwidth[i]) {
				domWidth -= colwidth[i];
				parts--;
			}
	}
	return domWidth / parts;
}
function domCellAround(target) {
	while (target && target.nodeName != "TD" && target.nodeName != "TH") target = target.classList && target.classList.contains("ProseMirror") ? null : target.parentNode;
	return target;
}
function edgeCell(view, event, side, handleWidth) {
	const offset = side == "right" ? -handleWidth : handleWidth;
	const found = view.posAtCoords({
		left: event.clientX + offset,
		top: event.clientY,
	});
	if (!found) return -1;
	const { pos } = found;
	const $cell = cellAround(view.state.doc.resolve(pos));
	if (!$cell) return -1;
	if (side == "right") return $cell.pos;
	const map = TableMap.get($cell.node(-1)),
		start = $cell.start(-1);
	const index = map.map.indexOf($cell.pos - start);
	return index % map.width == 0 ? -1 : start + map.map[index - 1];
}
function draggedWidth(dragging, event, resizeMinWidth) {
	const offset = event.clientX - dragging.startX;
	return Math.max(resizeMinWidth, dragging.startWidth + offset);
}
function updateHandle(view, value) {
	view.dispatch(view.state.tr.setMeta(columnResizingPluginKey, { setHandle: value }));
}
function updateColumnWidth(view, cell, width) {
	const $cell = view.state.doc.resolve(cell);
	const table = $cell.node(-1),
		map = TableMap.get(table),
		start = $cell.start(-1);
	const col = map.colCount($cell.pos - start) + $cell.nodeAfter.attrs.colspan - 1;
	const tr = view.state.tr;
	for (let row = 0; row < map.height; row++) {
		const mapIndex = row * map.width + col;
		if (row && map.map[mapIndex] == map.map[mapIndex - map.width]) continue;
		const pos = map.map[mapIndex];
		const attrs = table.nodeAt(pos).attrs;
		const index = attrs.colspan == 1 ? 0 : col - map.colCount(pos);
		if (attrs.colwidth && attrs.colwidth[index] == width) continue;
		const colwidth = attrs.colwidth ? attrs.colwidth.slice() : zeroes(attrs.colspan);
		colwidth[index] = width;
		tr.setNodeMarkup(start + pos, null, {
			...attrs,
			colwidth,
		});
	}
	if (tr.docChanged) view.dispatch(tr);
}
function displayColumnWidth(view, cell, width, defaultCellMinWidth) {
	const $cell = view.state.doc.resolve(cell);
	const table = $cell.node(-1),
		start = $cell.start(-1);
	const col = TableMap.get(table).colCount($cell.pos - start) + $cell.nodeAfter.attrs.colspan - 1;
	let dom = view.domAtPos($cell.start(-1)).node;
	while (dom && dom.nodeName != "TABLE") dom = dom.parentNode;
	if (!dom) return;
	updateColumnsOnResize(table, dom.firstChild, dom, defaultCellMinWidth, col, width);
}
function zeroes(n) {
	return Array(n).fill(0);
}
function handleDecorations(state, cell) {
	const decorations = [];
	const $cell = state.doc.resolve(cell);
	const table = $cell.node(-1);
	if (!table) return DecorationSet.empty;
	const map = TableMap.get(table);
	const start = $cell.start(-1);
	const col = map.colCount($cell.pos - start) + $cell.nodeAfter.attrs.colspan - 1;
	for (let row = 0; row < map.height; row++) {
		const index = col + row * map.width;
		if ((col == map.width - 1 || map.map[index] != map.map[index + 1]) && (row == 0 || map.map[index] != map.map[index - map.width])) {
			var _columnResizingPlugin;
			const cellPos = map.map[index];
			const pos = start + cellPos + table.nodeAt(cellPos).nodeSize - 1;
			const dom = document.createElement("div");
			dom.className = "column-resize-handle";
			if ((_columnResizingPlugin = columnResizingPluginKey.getState(state)) === null || _columnResizingPlugin === void 0 ? void 0 : _columnResizingPlugin.dragging)
				decorations.push(Decoration.node(start + cellPos, start + cellPos + table.nodeAt(cellPos).nodeSize, { class: "column-resize-dragging" }));
			decorations.push(Decoration.widget(pos, dom));
		}
	}
	return DecorationSet.create(state.doc, decorations);
}

//#endregion
//#region src/index.ts
/**
 * Creates a [plugin](http://prosemirror.net/docs/ref/#state.Plugin)
 * that, when added to an editor, enables cell-selection, handles
 * cell-based copy/paste, and makes sure tables stay well-formed (each
 * row has the same width, and cells don't overlap).
 *
 * You should probably put this plugin near the end of your array of
 * plugins, since it handles mouse and arrow key events in tables
 * rather broadly, and other plugins, like the gap cursor or the
 * column-width dragging plugin, might want to get a turn first to
 * perform more specific behavior.
 *
 * @public
 */
function tableEditing({ allowTableNodeSelection = false } = {}) {
	return new Plugin({
		key: tableEditingKey,
		state: {
			init() {
				return null;
			},
			apply(tr, cur) {
				const set = tr.getMeta(tableEditingKey);
				if (set != null) return set == -1 ? null : set;
				if (cur == null || !tr.docChanged) return cur;
				const { deleted, pos } = tr.mapping.mapResult(cur);
				return deleted ? null : pos;
			},
		},
		props: {
			decorations: drawCellSelection,
			handleDOMEvents: { mousedown: handleMouseDown$1 },
			createSelectionBetween(view) {
				return tableEditingKey.getState(view.state) != null ? view.state.selection : null;
			},
			handleTripleClick,
			handleKeyDown,
			handlePaste,
		},
		appendTransaction(_, oldState, state) {
			return normalizeSelection(state, fixTables(state, oldState), allowTableNodeSelection);
		},
	});
}

/**
 * Utility module to work with key-value stores.
 *
 * @module map
 */

/**
 * @template K
 * @template V
 * @typedef {Map<K,V>} GlobalMap
 */

/**
 * Creates a new Map instance.
 *
 * @function
 * @return {Map<any, any>}
 *
 * @function
 */
const create$9 = () => new Map();

/**
 * Copy a Map object into a fresh Map object.
 *
 * @function
 * @template K,V
 * @param {Map<K,V>} m
 * @return {Map<K,V>}
 */
const copy = (m) => {
	const r = create$9();
	m.forEach((v, k) => {
		r.set(k, v);
	});
	return r;
};

/**
 * Get map property. Create T if property is undefined and set T on map.
 *
 * ```js
 * const listeners = map.setIfUndefined(events, 'eventName', set.create)
 * listeners.add(listener)
 * ```
 *
 * @function
 * @template {Map<any, any>} MAP
 * @template {MAP extends Map<any,infer V> ? function():V : unknown} CF
 * @param {MAP} map
 * @param {MAP extends Map<infer K,any> ? K : unknown} key
 * @param {CF} createT
 * @return {ReturnType<CF>}
 */
const setIfUndefined = (map, key, createT) => {
	let set = map.get(key);
	if (set === undefined) {
		map.set(key, (set = createT()));
	}
	return set;
};

/**
 * Creates an Array and populates it with the content of all key-value pairs using the `f(value, key)` function.
 *
 * @function
 * @template K
 * @template V
 * @template R
 * @param {Map<K,V>} m
 * @param {function(V,K):R} f
 * @return {Array<R>}
 */
const map$3 = (m, f) => {
	const res = [];
	for (const [key, value] of m) {
		res.push(f(value, key));
	}
	return res;
};

/**
 * Tests whether any key-value pairs pass the test implemented by `f(value, key)`.
 *
 * @todo should rename to some - similarly to Array.some
 *
 * @function
 * @template K
 * @template V
 * @param {Map<K,V>} m
 * @param {function(V,K):boolean} f
 * @return {boolean}
 */
const any = (m, f) => {
	for (const [key, value] of m) {
		if (f(value, key)) {
			return true;
		}
	}
	return false;
};

/**
 * Utility module to work with sets.
 *
 * @module set
 */

const create$8 = () => new Set();

/**
 * Utility module to work with Arrays.
 *
 * @module array
 */

/**
 * Return the last element of an array. The element must exist
 *
 * @template L
 * @param {ArrayLike<L>} arr
 * @return {L}
 */
const last = (arr) => arr[arr.length - 1];

/**
 * Append elements from src to dest
 *
 * @template M
 * @param {Array<M>} dest
 * @param {Array<M>} src
 */
const appendTo = (dest, src) => {
	for (let i = 0; i < src.length; i++) {
		dest.push(src[i]);
	}
};

/**
 * Transforms something array-like to an actual Array.
 *
 * @function
 * @template T
 * @param {ArrayLike<T>|Iterable<T>} arraylike
 * @return {T}
 */
const from = Array.from;

/**
 * True iff condition holds on every element in the Array.
 *
 * @function
 * @template {ArrayLike<any>} ARR
 *
 * @param {ARR} arr
 * @param {ARR extends ArrayLike<infer S> ? ((value:S, index:number, arr:ARR) => boolean) : any} f
 * @return {boolean}
 */
const every$1 = (arr, f) => {
	for (let i = 0; i < arr.length; i++) {
		if (!f(arr[i], i, arr)) {
			return false;
		}
	}
	return true;
};

/**
 * True iff condition holds on some element in the Array.
 *
 * @function
 * @template {ArrayLike<any>} ARR
 *
 * @param {ARR} arr
 * @param {ARR extends ArrayLike<infer S> ? ((value:S, index:number, arr:ARR) => boolean) : never} f
 * @return {boolean}
 */
const some = (arr, f) => {
	for (let i = 0; i < arr.length; i++) {
		if (f(arr[i], i, arr)) {
			return true;
		}
	}
	return false;
};

/**
 * @template T
 * @param {number} len
 * @param {function(number, Array<T>):T} f
 * @return {Array<T>}
 */
const unfold = (len, f) => {
	const array = new Array(len);
	for (let i = 0; i < len; i++) {
		array[i] = f(i, array);
	}
	return array;
};

const isArray = Array.isArray;

/**
 * Observable class prototype.
 *
 * @module observable
 */

/**
 * Handles named events.
 * @experimental
 *
 * This is basically a (better typed) duplicate of Observable, which will replace Observable in the
 * next release.
 *
 * @template {{[key in keyof EVENTS]: function(...any):void}} EVENTS
 */
class ObservableV2 {
	constructor() {
		/**
		 * Some desc.
		 * @type {Map<string, Set<any>>}
		 */
		this._observers = create$9();
	}

	/**
	 * @template {keyof EVENTS & string} NAME
	 * @param {NAME} name
	 * @param {EVENTS[NAME]} f
	 */
	on(name, f) {
		setIfUndefined(this._observers, /** @type {string} */ (name), create$8).add(f);
		return f;
	}

	/**
	 * @template {keyof EVENTS & string} NAME
	 * @param {NAME} name
	 * @param {EVENTS[NAME]} f
	 */
	once(name, f) {
		/**
		 * @param  {...any} args
		 */
		const _f = (...args) => {
			this.off(name, /** @type {any} */ (_f));
			f(...args);
		};
		this.on(name, /** @type {any} */ (_f));
	}

	/**
	 * @template {keyof EVENTS & string} NAME
	 * @param {NAME} name
	 * @param {EVENTS[NAME]} f
	 */
	off(name, f) {
		const observers = this._observers.get(name);
		if (observers !== undefined) {
			observers.delete(f);
			if (observers.size === 0) {
				this._observers.delete(name);
			}
		}
	}

	/**
	 * Emit a named event. All registered event listeners that listen to the
	 * specified name will receive the event.
	 *
	 * @todo This should catch exceptions
	 *
	 * @template {keyof EVENTS & string} NAME
	 * @param {NAME} name The event name.
	 * @param {Parameters<EVENTS[NAME]>} args The arguments that are applied to the event listener.
	 */
	emit(name, args) {
		// copy all listeners to an array first to make sure that no event is emitted to listeners that are subscribed while the event handler is called.
		return from((this._observers.get(name) || create$9()).values()).forEach((f) => f(...args));
	}

	destroy() {
		this._observers = create$9();
	}
}
/* c8 ignore end */

/**
 * Common Math expressions.
 *
 * @module math
 */

const floor$1 = Math.floor;
const abs = Math.abs;

/**
 * @function
 * @param {number} a
 * @param {number} b
 * @return {number} The smaller element of a and b
 */
const min$1 = (a, b) => (a < b ? a : b);

/**
 * @function
 * @param {number} a
 * @param {number} b
 * @return {number} The bigger element of a and b
 */
const max$1 = (a, b) => (a > b ? a : b);

/**
 * Check whether n is negative, while considering the -0 edge case. While `-0 < 0` is false, this
 * function returns true for -0,-1,,.. and returns false for 0,1,2,...
 * @param {number} n
 * @return {boolean} Wether n is negative. This function also distinguishes between -0 and +0
 */
const isNegativeZero = (n) => (n !== 0 ? n < 0 : 1 / n < 0);

/* eslint-env browser */

/**
 * Binary data constants.
 *
 * @module binary
 */

/**
 * n-th bit activated.
 *
 * @type {number}
 */
const BIT1 = 1;
const BIT2 = 2;
const BIT3 = 4;
const BIT4 = 8;
const BIT6 = 32;
const BIT7 = 64;
const BIT8 = 128;
const BIT30 = 1 << 29;
const BITS5 = 31;
const BITS6 = 63;
const BITS7 = 127;
/**
 * @type {number}
 */
const BITS31 = 0x7fffffff;

/**
 * Utility helpers for working with numbers.
 *
 * @module number
 */

const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER;
const MIN_SAFE_INTEGER = Number.MIN_SAFE_INTEGER;

/* c8 ignore next */
const isInteger$1 = Number.isInteger || ((num) => typeof num === "number" && isFinite(num) && floor$1(num) === num);

/**
 * Utility module to work with strings.
 *
 * @module string
 */

const fromCharCode = String.fromCharCode;

/**
 * @param {string} s
 * @return {string}
 */
const toLowerCase = (s) => s.toLowerCase();

const trimLeftRegex = /^\s*/g;

/**
 * @param {string} s
 * @return {string}
 */
const trimLeft = (s) => s.replace(trimLeftRegex, "");

const fromCamelCaseRegex = /([A-Z])/g;

/**
 * @param {string} s
 * @param {string} separator
 * @return {string}
 */
const fromCamelCase = (s, separator) => trimLeft(s.replace(fromCamelCaseRegex, (match) => `${separator}${toLowerCase(match)}`));

/**
 * @param {string} str
 * @return {Uint8Array<ArrayBuffer>}
 */
const _encodeUtf8Polyfill = (str) => {
	const encodedString = unescape(encodeURIComponent(str));
	const len = encodedString.length;
	const buf = new Uint8Array(len);
	for (let i = 0; i < len; i++) {
		buf[i] = /** @type {number} */ (encodedString.codePointAt(i));
	}
	return buf;
};

/* c8 ignore next */
const utf8TextEncoder = /** @type {TextEncoder} */ (typeof TextEncoder !== "undefined" ? new TextEncoder() : null);

/**
 * @param {string} str
 * @return {Uint8Array<ArrayBuffer>}
 */
const _encodeUtf8Native = (str) => utf8TextEncoder.encode(str);

/**
 * @param {string} str
 * @return {Uint8Array}
 */
/* c8 ignore next */
const encodeUtf8 = utf8TextEncoder ? _encodeUtf8Native : _encodeUtf8Polyfill;

/* c8 ignore next */
let utf8TextDecoder = typeof TextDecoder === "undefined" ? null : new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });

/* c8 ignore start */
if (utf8TextDecoder && utf8TextDecoder.decode(new Uint8Array()).length === 1) {
	// Safari doesn't handle BOM correctly.
	// This fixes a bug in Safari 13.0.5 where it produces a BOM the first time it is called.
	// utf8TextDecoder.decode(new Uint8Array()).length === 1 on the first call and
	// utf8TextDecoder.decode(new Uint8Array()).length === 1 on the second call
	// Another issue is that from then on no BOM chars are recognized anymore
	/* c8 ignore next */
	utf8TextDecoder = null;
}

/**
 * @param {string} source
 * @param {number} n
 */
const repeat = (source, n) => unfold(n, () => source).join("");

/**
 * Efficient schema-less binary encoding with support for variable length encoding.
 *
 * Use [lib0/encoding] with [lib0/decoding]. Every encoding function has a corresponding decoding function.
 *
 * Encodes numbers in little-endian order (least to most significant byte order)
 * and is compatible with Golang's binary encoding (https://golang.org/pkg/encoding/binary/)
 * which is also used in Protocol Buffers.
 *
 * ```js
 * // encoding step
 * const encoder = encoding.createEncoder()
 * encoding.writeVarUint(encoder, 256)
 * encoding.writeVarString(encoder, 'Hello world!')
 * const buf = encoding.toUint8Array(encoder)
 * ```
 *
 * ```js
 * // decoding step
 * const decoder = decoding.createDecoder(buf)
 * decoding.readVarUint(decoder) // => 256
 * decoding.readVarString(decoder) // => 'Hello world!'
 * decoding.hasContent(decoder) // => false - all data is read
 * ```
 *
 * @module encoding
 */

/**
 * A BinaryEncoder handles the encoding to an Uint8Array.
 */
class Encoder {
	constructor() {
		this.cpos = 0;
		this.cbuf = new Uint8Array(100);
		/**
		 * @type {Array<Uint8Array>}
		 */
		this.bufs = [];
	}
}

/**
 * @function
 * @return {Encoder}
 */
const createEncoder = () => new Encoder();

/**
 * @param {function(Encoder):void} f
 */
const encode = (f) => {
	const encoder = createEncoder();
	f(encoder);
	return toUint8Array(encoder);
};

/**
 * The current length of the encoded data.
 *
 * @function
 * @param {Encoder} encoder
 * @return {number}
 */
const length = (encoder) => {
	let len = encoder.cpos;
	for (let i = 0; i < encoder.bufs.length; i++) {
		len += encoder.bufs[i].length;
	}
	return len;
};

/**
 * Transform to Uint8Array.
 *
 * @function
 * @param {Encoder} encoder
 * @return {Uint8Array<ArrayBuffer>} The created ArrayBuffer.
 */
const toUint8Array = (encoder) => {
	const uint8arr = new Uint8Array(length(encoder));
	let curPos = 0;
	for (let i = 0; i < encoder.bufs.length; i++) {
		const d = encoder.bufs[i];
		uint8arr.set(d, curPos);
		curPos += d.length;
	}
	uint8arr.set(new Uint8Array(encoder.cbuf.buffer, 0, encoder.cpos), curPos);
	return uint8arr;
};

/**
 * Verify that it is possible to write `len` bytes wtihout checking. If
 * necessary, a new Buffer with the required length is attached.
 *
 * @param {Encoder} encoder
 * @param {number} len
 */
const verifyLen = (encoder, len) => {
	const bufferLen = encoder.cbuf.length;
	if (bufferLen - encoder.cpos < len) {
		encoder.bufs.push(new Uint8Array(encoder.cbuf.buffer, 0, encoder.cpos));
		encoder.cbuf = new Uint8Array(max$1(bufferLen, len) * 2);
		encoder.cpos = 0;
	}
};

/**
 * Write one byte to the encoder.
 *
 * @function
 * @param {Encoder} encoder
 * @param {number} num The byte that is to be encoded.
 */
const write = (encoder, num) => {
	const bufferLen = encoder.cbuf.length;
	if (encoder.cpos === bufferLen) {
		encoder.bufs.push(encoder.cbuf);
		encoder.cbuf = new Uint8Array(bufferLen * 2);
		encoder.cpos = 0;
	}
	encoder.cbuf[encoder.cpos++] = num;
};

/**
 * Write one byte as an unsigned integer.
 *
 * @function
 * @param {Encoder} encoder
 * @param {number} num The number that is to be encoded.
 */
const writeUint8 = write;

/**
 * Write a variable length unsigned integer. Max encodable integer is 2^53.
 *
 * @function
 * @param {Encoder} encoder
 * @param {number} num The number that is to be encoded.
 */
const writeVarUint = (encoder, num) => {
	while (num > BITS7) {
		write(encoder, BIT8 | (BITS7 & num));
		num = floor$1(num / 128); // shift >>> 7
	}
	write(encoder, BITS7 & num);
};

/**
 * Write a variable length integer.
 *
 * We use the 7th bit instead for signaling that this is a negative number.
 *
 * @function
 * @param {Encoder} encoder
 * @param {number} num The number that is to be encoded.
 */
const writeVarInt = (encoder, num) => {
	const isNegative = isNegativeZero(num);
	if (isNegative) {
		num = -num;
	}
	//             |- whether to continue reading         |- whether is negative     |- number
	write(encoder, (num > BITS6 ? BIT8 : 0) | (isNegative ? BIT7 : 0) | (BITS6 & num));
	num = floor$1(num / 64); // shift >>> 6
	// We don't need to consider the case of num === 0 so we can use a different
	// pattern here than above.
	while (num > 0) {
		write(encoder, (num > BITS7 ? BIT8 : 0) | (BITS7 & num));
		num = floor$1(num / 128); // shift >>> 7
	}
};

/**
 * A cache to store strings temporarily
 */
const _strBuffer = new Uint8Array(30000);
const _maxStrBSize = _strBuffer.length / 3;

/**
 * Write a variable length string.
 *
 * @function
 * @param {Encoder} encoder
 * @param {String} str The string that is to be encoded.
 */
const _writeVarStringNative = (encoder, str) => {
	if (str.length < _maxStrBSize) {
		// We can encode the string into the existing buffer
		/* c8 ignore next */
		const written = utf8TextEncoder.encodeInto(str, _strBuffer).written || 0;
		writeVarUint(encoder, written);
		for (let i = 0; i < written; i++) {
			write(encoder, _strBuffer[i]);
		}
	} else {
		writeVarUint8Array(encoder, encodeUtf8(str));
	}
};

/**
 * Write a variable length string.
 *
 * @function
 * @param {Encoder} encoder
 * @param {String} str The string that is to be encoded.
 */
const _writeVarStringPolyfill = (encoder, str) => {
	const encodedString = unescape(encodeURIComponent(str));
	const len = encodedString.length;
	writeVarUint(encoder, len);
	for (let i = 0; i < len; i++) {
		write(encoder, /** @type {number} */ (encodedString.codePointAt(i)));
	}
};

/**
 * Write a variable length string.
 *
 * @function
 * @param {Encoder} encoder
 * @param {String} str The string that is to be encoded.
 */
/* c8 ignore next */
const writeVarString = utf8TextEncoder && /** @type {any} */ (utf8TextEncoder).encodeInto ? _writeVarStringNative : _writeVarStringPolyfill;

/**
 * Append fixed-length Uint8Array to the encoder.
 *
 * @function
 * @param {Encoder} encoder
 * @param {Uint8Array} uint8Array
 */
const writeUint8Array = (encoder, uint8Array) => {
	const bufferLen = encoder.cbuf.length;
	const cpos = encoder.cpos;
	const leftCopyLen = min$1(bufferLen - cpos, uint8Array.length);
	const rightCopyLen = uint8Array.length - leftCopyLen;
	encoder.cbuf.set(uint8Array.subarray(0, leftCopyLen), cpos);
	encoder.cpos += leftCopyLen;
	if (rightCopyLen > 0) {
		// Still something to write, write right half..
		// Append new buffer
		encoder.bufs.push(encoder.cbuf);
		// must have at least size of remaining buffer
		encoder.cbuf = new Uint8Array(max$1(bufferLen * 2, rightCopyLen));
		// copy array
		encoder.cbuf.set(uint8Array.subarray(leftCopyLen));
		encoder.cpos = rightCopyLen;
	}
};

/**
 * Append an Uint8Array to Encoder.
 *
 * @function
 * @param {Encoder} encoder
 * @param {Uint8Array} uint8Array
 */
const writeVarUint8Array = (encoder, uint8Array) => {
	writeVarUint(encoder, uint8Array.byteLength);
	writeUint8Array(encoder, uint8Array);
};

/**
 * Create an DataView of the next `len` bytes. Use it to write data after
 * calling this function.
 *
 * ```js
 * // write float32 using DataView
 * const dv = writeOnDataView(encoder, 4)
 * dv.setFloat32(0, 1.1)
 * // read float32 using DataView
 * const dv = readFromDataView(encoder, 4)
 * dv.getFloat32(0) // => 1.100000023841858 (leaving it to the reader to find out why this is the correct result)
 * ```
 *
 * @param {Encoder} encoder
 * @param {number} len
 * @return {DataView}
 */
const writeOnDataView = (encoder, len) => {
	verifyLen(encoder, len);
	const dview = new DataView(encoder.cbuf.buffer, encoder.cpos, len);
	encoder.cpos += len;
	return dview;
};

/**
 * @param {Encoder} encoder
 * @param {number} num
 */
const writeFloat32 = (encoder, num) => writeOnDataView(encoder, 4).setFloat32(0, num, false);

/**
 * @param {Encoder} encoder
 * @param {number} num
 */
const writeFloat64 = (encoder, num) => writeOnDataView(encoder, 8).setFloat64(0, num, false);

/**
 * @param {Encoder} encoder
 * @param {bigint} num
 */
const writeBigInt64 = (encoder, num) => /** @type {any} */ (writeOnDataView(encoder, 8)).setBigInt64(0, num, false);

const floatTestBed = new DataView(new ArrayBuffer(4));
/**
 * Check if a number can be encoded as a 32 bit float.
 *
 * @param {number} num
 * @return {boolean}
 */
const isFloat32 = (num) => {
	floatTestBed.setFloat32(0, num);
	return floatTestBed.getFloat32(0) === num;
};

/**
 * @typedef {Array<AnyEncodable>} AnyEncodableArray
 */

/**
 * @typedef {undefined|null|number|bigint|boolean|string|{[k:string]:AnyEncodable}|AnyEncodableArray|Uint8Array} AnyEncodable
 */

/**
 * Encode data with efficient binary format.
 *
 * Differences to JSON:
 * • Transforms data to a binary format (not to a string)
 * • Encodes undefined, NaN, and ArrayBuffer (these can't be represented in JSON)
 * • Numbers are efficiently encoded either as a variable length integer, as a
 *   32 bit float, as a 64 bit float, or as a 64 bit bigint.
 *
 * Encoding table:
 *
 * | Data Type           | Prefix   | Encoding Method    | Comment |
 * | ------------------- | -------- | ------------------ | ------- |
 * | undefined           | 127      |                    | Functions, symbol, and everything that cannot be identified is encoded as undefined |
 * | null                | 126      |                    | |
 * | integer             | 125      | writeVarInt        | Only encodes 32 bit signed integers |
 * | float32             | 124      | writeFloat32       | |
 * | float64             | 123      | writeFloat64       | |
 * | bigint              | 122      | writeBigInt64      | |
 * | boolean (false)     | 121      |                    | True and false are different data types so we save the following byte |
 * | boolean (true)      | 120      |                    | - 0b01111000 so the last bit determines whether true or false |
 * | string              | 119      | writeVarString     | |
 * | object<string,any>  | 118      | custom             | Writes {length} then {length} key-value pairs |
 * | array<any>          | 117      | custom             | Writes {length} then {length} json values |
 * | Uint8Array          | 116      | writeVarUint8Array | We use Uint8Array for any kind of binary data |
 *
 * Reasons for the decreasing prefix:
 * We need the first bit for extendability (later we may want to encode the
 * prefix with writeVarUint). The remaining 7 bits are divided as follows:
 * [0-30]   the beginning of the data range is used for custom purposes
 *          (defined by the function that uses this library)
 * [31-127] the end of the data range is used for data encoding by
 *          lib0/encoding.js
 *
 * @param {Encoder} encoder
 * @param {AnyEncodable} data
 */
const writeAny = (encoder, data) => {
	switch (typeof data) {
		case "string":
			// TYPE 119: STRING
			write(encoder, 119);
			writeVarString(encoder, data);
			break;
		case "number":
			if (isInteger$1(data) && abs(data) <= BITS31) {
				// TYPE 125: INTEGER
				write(encoder, 125);
				writeVarInt(encoder, data);
			} else if (isFloat32(data)) {
				// TYPE 124: FLOAT32
				write(encoder, 124);
				writeFloat32(encoder, data);
			} else {
				// TYPE 123: FLOAT64
				write(encoder, 123);
				writeFloat64(encoder, data);
			}
			break;
		case "bigint":
			// TYPE 122: BigInt
			write(encoder, 122);
			writeBigInt64(encoder, data);
			break;
		case "object":
			if (data === null) {
				// TYPE 126: null
				write(encoder, 126);
			} else if (isArray(data)) {
				// TYPE 117: Array
				write(encoder, 117);
				writeVarUint(encoder, data.length);
				for (let i = 0; i < data.length; i++) {
					writeAny(encoder, data[i]);
				}
			} else if (data instanceof Uint8Array) {
				// TYPE 116: ArrayBuffer
				write(encoder, 116);
				writeVarUint8Array(encoder, data);
			} else {
				// TYPE 118: Object
				write(encoder, 118);
				const keys = Object.keys(data);
				writeVarUint(encoder, keys.length);
				for (let i = 0; i < keys.length; i++) {
					const key = keys[i];
					writeVarString(encoder, key);
					writeAny(encoder, data[key]);
				}
			}
			break;
		case "boolean":
			// TYPE 120/121: boolean (true/false)
			write(encoder, data ? 120 : 121);
			break;
		default:
			// TYPE 127: undefined
			write(encoder, 127);
	}
};

/**
 * Now come a few stateful encoder that have their own classes.
 */

/**
 * Basic Run Length Encoder - a basic compression implementation.
 *
 * Encodes [1,1,1,7] to [1,3,7,1] (3 times 1, 1 time 7). This encoder might do more harm than good if there are a lot of values that are not repeated.
 *
 * It was originally used for image compression. Cool .. article http://csbruce.com/cbm/transactor/pdfs/trans_v7_i06.pdf
 *
 * @note T must not be null!
 *
 * @template T
 */
class RleEncoder extends Encoder {
	/**
	 * @param {function(Encoder, T):void} writer
	 */
	constructor(writer) {
		super();
		/**
		 * The writer
		 */
		this.w = writer;
		/**
		 * Current state
		 * @type {T|null}
		 */
		this.s = null;
		this.count = 0;
	}

	/**
	 * @param {T} v
	 */
	write(v) {
		if (this.s === v) {
			this.count++;
		} else {
			if (this.count > 0) {
				// flush counter, unless this is the first value (count = 0)
				writeVarUint(this, this.count - 1); // since count is always > 0, we can decrement by one. non-standard encoding ftw
			}
			this.count = 1;
			// write first value
			this.w(this, v);
			this.s = v;
		}
	}
}

/**
 * @param {UintOptRleEncoder} encoder
 */
const flushUintOptRleEncoder = (encoder) => {
	if (encoder.count > 0) {
		// flush counter, unless this is the first value (count = 0)
		// case 1: just a single value. set sign to positive
		// case 2: write several values. set sign to negative to indicate that there is a length coming
		writeVarInt(encoder.encoder, encoder.count === 1 ? encoder.s : -encoder.s);
		if (encoder.count > 1) {
			writeVarUint(encoder.encoder, encoder.count - 2); // since count is always > 1, we can decrement by one. non-standard encoding ftw
		}
	}
};

/**
 * Optimized Rle encoder that does not suffer from the mentioned problem of the basic Rle encoder.
 *
 * Internally uses VarInt encoder to write unsigned integers. If the input occurs multiple times, we write
 * write it as a negative number. The UintOptRleDecoder then understands that it needs to read a count.
 *
 * Encodes [1,2,3,3,3] as [1,2,-3,3] (once 1, once 2, three times 3)
 */
class UintOptRleEncoder {
	constructor() {
		this.encoder = new Encoder();
		/**
		 * @type {number}
		 */
		this.s = 0;
		this.count = 0;
	}

	/**
	 * @param {number} v
	 */
	write(v) {
		if (this.s === v) {
			this.count++;
		} else {
			flushUintOptRleEncoder(this);
			this.count = 1;
			this.s = v;
		}
	}

	/**
	 * Flush the encoded state and transform this to a Uint8Array.
	 *
	 * Note that this should only be called once.
	 */
	toUint8Array() {
		flushUintOptRleEncoder(this);
		return toUint8Array(this.encoder);
	}
}

/**
 * @param {IntDiffOptRleEncoder} encoder
 */
const flushIntDiffOptRleEncoder = (encoder) => {
	if (encoder.count > 0) {
		//          31 bit making up the diff | wether to write the counter
		// const encodedDiff = encoder.diff << 1 | (encoder.count === 1 ? 0 : 1)
		const encodedDiff = encoder.diff * 2 + (encoder.count === 1 ? 0 : 1);
		// flush counter, unless this is the first value (count = 0)
		// case 1: just a single value. set first bit to positive
		// case 2: write several values. set first bit to negative to indicate that there is a length coming
		writeVarInt(encoder.encoder, encodedDiff);
		if (encoder.count > 1) {
			writeVarUint(encoder.encoder, encoder.count - 2); // since count is always > 1, we can decrement by one. non-standard encoding ftw
		}
	}
};

/**
 * A combination of the IntDiffEncoder and the UintOptRleEncoder.
 *
 * The count approach is similar to the UintDiffOptRleEncoder, but instead of using the negative bitflag, it encodes
 * in the LSB whether a count is to be read. Therefore this Encoder only supports 31 bit integers!
 *
 * Encodes [1, 2, 3, 2] as [3, 1, 6, -1] (more specifically [(1 << 1) | 1, (3 << 0) | 0, -1])
 *
 * Internally uses variable length encoding. Contrary to normal UintVar encoding, the first byte contains:
 * * 1 bit that denotes whether the next value is a count (LSB)
 * * 1 bit that denotes whether this value is negative (MSB - 1)
 * * 1 bit that denotes whether to continue reading the variable length integer (MSB)
 *
 * Therefore, only five bits remain to encode diff ranges.
 *
 * Use this Encoder only when appropriate. In most cases, this is probably a bad idea.
 */
class IntDiffOptRleEncoder {
	constructor() {
		this.encoder = new Encoder();
		/**
		 * @type {number}
		 */
		this.s = 0;
		this.count = 0;
		this.diff = 0;
	}

	/**
	 * @param {number} v
	 */
	write(v) {
		if (this.diff === v - this.s) {
			this.s = v;
			this.count++;
		} else {
			flushIntDiffOptRleEncoder(this);
			this.count = 1;
			this.diff = v - this.s;
			this.s = v;
		}
	}

	/**
	 * Flush the encoded state and transform this to a Uint8Array.
	 *
	 * Note that this should only be called once.
	 */
	toUint8Array() {
		flushIntDiffOptRleEncoder(this);
		return toUint8Array(this.encoder);
	}
}

/**
 * Optimized String Encoder.
 *
 * Encoding many small strings in a simple Encoder is not very efficient. The function call to decode a string takes some time and creates references that must be eventually deleted.
 * In practice, when decoding several million small strings, the GC will kick in more and more often to collect orphaned string objects (or maybe there is another reason?).
 *
 * This string encoder solves the above problem. All strings are concatenated and written as a single string using a single encoding call.
 *
 * The lengths are encoded using a UintOptRleEncoder.
 */
class StringEncoder {
	constructor() {
		/**
		 * @type {Array<string>}
		 */
		this.sarr = [];
		this.s = "";
		this.lensE = new UintOptRleEncoder();
	}

	/**
	 * @param {string} string
	 */
	write(string) {
		this.s += string;
		if (this.s.length > 19) {
			this.sarr.push(this.s);
			this.s = "";
		}
		this.lensE.write(string.length);
	}

	toUint8Array() {
		const encoder = new Encoder();
		this.sarr.push(this.s);
		this.s = "";
		writeVarString(encoder, this.sarr.join(""));
		writeUint8Array(encoder, this.lensE.toUint8Array());
		return toUint8Array(encoder);
	}
}

/**
 * Error helpers.
 *
 * @module error
 */

/**
 * @param {string} s
 * @return {Error}
 */
/* c8 ignore next */
const create$7 = (s) => new Error(s);

/**
 * @throws {Error}
 * @return {never}
 */
/* c8 ignore next 3 */
const methodUnimplemented = () => {
	throw create$7("Method unimplemented");
};

/**
 * @throws {Error}
 * @return {never}
 */
/* c8 ignore next 3 */
const unexpectedCase = () => {
	throw create$7("Unexpected case");
};

/**
 * Efficient schema-less binary decoding with support for variable length encoding.
 *
 * Use [lib0/decoding] with [lib0/encoding]. Every encoding function has a corresponding decoding function.
 *
 * Encodes numbers in little-endian order (least to most significant byte order)
 * and is compatible with Golang's binary encoding (https://golang.org/pkg/encoding/binary/)
 * which is also used in Protocol Buffers.
 *
 * ```js
 * // encoding step
 * const encoder = encoding.createEncoder()
 * encoding.writeVarUint(encoder, 256)
 * encoding.writeVarString(encoder, 'Hello world!')
 * const buf = encoding.toUint8Array(encoder)
 * ```
 *
 * ```js
 * // decoding step
 * const decoder = decoding.createDecoder(buf)
 * decoding.readVarUint(decoder) // => 256
 * decoding.readVarString(decoder) // => 'Hello world!'
 * decoding.hasContent(decoder) // => false - all data is read
 * ```
 *
 * @module decoding
 */

const errorUnexpectedEndOfArray = create$7("Unexpected end of array");
const errorIntegerOutOfRange = create$7("Integer out of Range");

/**
 * A Decoder handles the decoding of an Uint8Array.
 * @template {ArrayBufferLike} [Buf=ArrayBufferLike]
 */
class Decoder {
	/**
	 * @param {Uint8Array<Buf>} uint8Array Binary data to decode
	 */
	constructor(uint8Array) {
		/**
		 * Decoding target.
		 *
		 * @type {Uint8Array<Buf>}
		 */
		this.arr = uint8Array;
		/**
		 * Current decoding position.
		 *
		 * @type {number}
		 */
		this.pos = 0;
	}
}

/**
 * @function
 * @template {ArrayBufferLike} Buf
 * @param {Uint8Array<Buf>} uint8Array
 * @return {Decoder<Buf>}
 */
const createDecoder = (uint8Array) => new Decoder(uint8Array);

/**
 * @function
 * @param {Decoder} decoder
 * @return {boolean}
 */
const hasContent = (decoder) => decoder.pos !== decoder.arr.length;

/**
 * Create an Uint8Array view of the next `len` bytes and advance the position by `len`.
 *
 * Important: The Uint8Array still points to the underlying ArrayBuffer. Make sure to discard the result as soon as possible to prevent any memory leaks.
 *            Use `buffer.copyUint8Array` to copy the result into a new Uint8Array.
 *
 * @function
 * @template {ArrayBufferLike} Buf
 * @param {Decoder<Buf>} decoder The decoder instance
 * @param {number} len The length of bytes to read
 * @return {Uint8Array<Buf>}
 */
const readUint8Array = (decoder, len) => {
	const view = new Uint8Array(decoder.arr.buffer, decoder.pos + decoder.arr.byteOffset, len);
	decoder.pos += len;
	return view;
};

/**
 * Read variable length Uint8Array.
 *
 * Important: The Uint8Array still points to the underlying ArrayBuffer. Make sure to discard the result as soon as possible to prevent any memory leaks.
 *            Use `buffer.copyUint8Array` to copy the result into a new Uint8Array.
 *
 * @function
 * @template {ArrayBufferLike} Buf
 * @param {Decoder<Buf>} decoder
 * @return {Uint8Array<Buf>}
 */
const readVarUint8Array = (decoder) => readUint8Array(decoder, readVarUint(decoder));

/**
 * Read one byte as unsigned integer.
 * @function
 * @param {Decoder} decoder The decoder instance
 * @return {number} Unsigned 8-bit integer
 */
const readUint8 = (decoder) => decoder.arr[decoder.pos++];

/**
 * Read unsigned integer (32bit) with variable length.
 * 1/8th of the storage is used as encoding overhead.
 *  * numbers < 2^7 is stored in one bytlength
 *  * numbers < 2^14 is stored in two bylength
 *
 * @function
 * @param {Decoder} decoder
 * @return {number} An unsigned integer.length
 */
const readVarUint = (decoder) => {
	let num = 0;
	let mult = 1;
	const len = decoder.arr.length;
	while (decoder.pos < len) {
		const r = decoder.arr[decoder.pos++];
		// num = num | ((r & binary.BITS7) << len)
		num = num + (r & BITS7) * mult; // shift $r << (7*#iterations) and add it to num
		mult *= 128; // next iteration, shift 7 "more" to the left
		if (r < BIT8) {
			return num;
		}
		/* c8 ignore start */
		if (num > MAX_SAFE_INTEGER) {
			throw errorIntegerOutOfRange;
		}
		/* c8 ignore stop */
	}
	throw errorUnexpectedEndOfArray;
};

/**
 * Read signed integer (32bit) with variable length.
 * 1/8th of the storage is used as encoding overhead.
 *  * numbers < 2^7 is stored in one bytlength
 *  * numbers < 2^14 is stored in two bylength
 * @todo This should probably create the inverse ~num if number is negative - but this would be a breaking change.
 *
 * @function
 * @param {Decoder} decoder
 * @return {number} An unsigned integer.length
 */
const readVarInt = (decoder) => {
	let r = decoder.arr[decoder.pos++];
	let num = r & BITS6;
	let mult = 64;
	const sign = (r & BIT7) > 0 ? -1 : 1;
	if ((r & BIT8) === 0) {
		// don't continue reading
		return sign * num;
	}
	const len = decoder.arr.length;
	while (decoder.pos < len) {
		r = decoder.arr[decoder.pos++];
		// num = num | ((r & binary.BITS7) << len)
		num = num + (r & BITS7) * mult;
		mult *= 128;
		if (r < BIT8) {
			return sign * num;
		}
		/* c8 ignore start */
		if (num > MAX_SAFE_INTEGER) {
			throw errorIntegerOutOfRange;
		}
		/* c8 ignore stop */
	}
	throw errorUnexpectedEndOfArray;
};

/**
 * We don't test this function anymore as we use native decoding/encoding by default now.
 * Better not modify this anymore..
 *
 * Transforming utf8 to a string is pretty expensive. The code performs 10x better
 * when String.fromCodePoint is fed with all characters as arguments.
 * But most environments have a maximum number of arguments per functions.
 * For effiency reasons we apply a maximum of 10000 characters at once.
 *
 * @function
 * @param {Decoder} decoder
 * @return {String} The read String.
 */
/* c8 ignore start */
const _readVarStringPolyfill = (decoder) => {
	let remainingLen = readVarUint(decoder);
	if (remainingLen === 0) {
		return "";
	} else {
		let encodedString = String.fromCodePoint(readUint8(decoder)); // remember to decrease remainingLen
		if (--remainingLen < 100) {
			// do not create a Uint8Array for small strings
			while (remainingLen--) {
				encodedString += String.fromCodePoint(readUint8(decoder));
			}
		} else {
			while (remainingLen > 0) {
				const nextLen = remainingLen < 10000 ? remainingLen : 10000;
				// this is dangerous, we create a fresh array view from the existing buffer
				const bytes = decoder.arr.subarray(decoder.pos, decoder.pos + nextLen);
				decoder.pos += nextLen;
				// Starting with ES5.1 we can supply a generic array-like object as arguments
				encodedString += String.fromCodePoint.apply(null, /** @type {any} */ (bytes));
				remainingLen -= nextLen;
			}
		}
		return decodeURIComponent(escape(encodedString));
	}
};
/* c8 ignore stop */

/**
 * @function
 * @param {Decoder} decoder
 * @return {String} The read String
 */
const _readVarStringNative = (decoder) => /** @type any */ (utf8TextDecoder).decode(readVarUint8Array(decoder));

/**
 * Read string of variable length
 * * varUint is used to store the length of the string
 *
 * @function
 * @param {Decoder} decoder
 * @return {String} The read String
 *
 */
/* c8 ignore next */
const readVarString = utf8TextDecoder ? _readVarStringNative : _readVarStringPolyfill;

/**
 * @param {Decoder} decoder
 * @param {number} len
 * @return {DataView}
 */
const readFromDataView = (decoder, len) => {
	const dv = new DataView(decoder.arr.buffer, decoder.arr.byteOffset + decoder.pos, len);
	decoder.pos += len;
	return dv;
};

/**
 * @param {Decoder} decoder
 */
const readFloat32 = (decoder) => readFromDataView(decoder, 4).getFloat32(0, false);

/**
 * @param {Decoder} decoder
 */
const readFloat64 = (decoder) => readFromDataView(decoder, 8).getFloat64(0, false);

/**
 * @param {Decoder} decoder
 */
const readBigInt64 = (decoder) => /** @type {any} */ (readFromDataView(decoder, 8)).getBigInt64(0, false);

/**
 * @type {Array<function(Decoder):any>}
 */
const readAnyLookupTable = [
	(decoder) => undefined, // CASE 127: undefined
	(decoder) => null, // CASE 126: null
	readVarInt, // CASE 125: integer
	readFloat32, // CASE 124: float32
	readFloat64, // CASE 123: float64
	readBigInt64, // CASE 122: bigint
	(decoder) => false, // CASE 121: boolean (false)
	(decoder) => true, // CASE 120: boolean (true)
	readVarString, // CASE 119: string
	(decoder) => {
		// CASE 118: object<string,any>
		const len = readVarUint(decoder);
		/**
		 * @type {Object<string,any>}
		 */
		const obj = {};
		for (let i = 0; i < len; i++) {
			const key = readVarString(decoder);
			obj[key] = readAny(decoder);
		}
		return obj;
	},
	(decoder) => {
		// CASE 117: array<any>
		const len = readVarUint(decoder);
		const arr = [];
		for (let i = 0; i < len; i++) {
			arr.push(readAny(decoder));
		}
		return arr;
	},
	readVarUint8Array, // CASE 116: Uint8Array
];

/**
 * @param {Decoder} decoder
 */
const readAny = (decoder) => readAnyLookupTable[127 - readUint8(decoder)](decoder);

/**
 * T must not be null.
 *
 * @template T
 */
class RleDecoder extends Decoder {
	/**
	 * @param {Uint8Array} uint8Array
	 * @param {function(Decoder):T} reader
	 */
	constructor(uint8Array, reader) {
		super(uint8Array);
		/**
		 * The reader
		 */
		this.reader = reader;
		/**
		 * Current state
		 * @type {T|null}
		 */
		this.s = null;
		this.count = 0;
	}

	read() {
		if (this.count === 0) {
			this.s = this.reader(this);
			if (hasContent(this)) {
				this.count = readVarUint(this) + 1; // see encoder implementation for the reason why this is incremented
			} else {
				this.count = -1; // read the current value forever
			}
		}
		this.count--;
		return /** @type {T} */ (this.s);
	}
}

class UintOptRleDecoder extends Decoder {
	/**
	 * @param {Uint8Array} uint8Array
	 */
	constructor(uint8Array) {
		super(uint8Array);
		/**
		 * @type {number}
		 */
		this.s = 0;
		this.count = 0;
	}

	read() {
		if (this.count === 0) {
			this.s = readVarInt(this);
			// if the sign is negative, we read the count too, otherwise count is 1
			const isNegative = isNegativeZero(this.s);
			this.count = 1;
			if (isNegative) {
				this.s = -this.s;
				this.count = readVarUint(this) + 2;
			}
		}
		this.count--;
		return /** @type {number} */ (this.s);
	}
}

class IntDiffOptRleDecoder extends Decoder {
	/**
	 * @param {Uint8Array} uint8Array
	 */
	constructor(uint8Array) {
		super(uint8Array);
		/**
		 * @type {number}
		 */
		this.s = 0;
		this.count = 0;
		this.diff = 0;
	}

	/**
	 * @return {number}
	 */
	read() {
		if (this.count === 0) {
			const diff = readVarInt(this);
			// if the first bit is set, we read more data
			const hasCount = diff & 1;
			this.diff = floor$1(diff / 2); // shift >> 1
			this.count = 1;
			if (hasCount) {
				this.count = readVarUint(this) + 2;
			}
		}
		this.s += this.diff;
		this.count--;
		return this.s;
	}
}

class StringDecoder {
	/**
	 * @param {Uint8Array} uint8Array
	 */
	constructor(uint8Array) {
		this.decoder = new UintOptRleDecoder(uint8Array);
		this.str = readVarString(this.decoder);
		/**
		 * @type {number}
		 */
		this.spos = 0;
	}

	/**
	 * @return {string}
	 */
	read() {
		const end = this.spos + this.decoder.read();
		const res = this.str.slice(this.spos, end);
		this.spos = end;
		return res;
	}
}

/* eslint-env browser */

const getRandomValues = crypto.getRandomValues.bind(crypto);

/**
 * Isomorphic module for true random numbers / buffers / uuids.
 *
 * Attention: falls back to Math.random if the browser does not support crypto.
 *
 * @module random
 */

const rand = Math.random;

const uint32 = () => getRandomValues(new Uint32Array(1))[0];

/**
 * @template T
 * @param {Array<T>} arr
 * @return {T}
 */
const oneOf$1 = (arr) => arr[floor$1(rand() * arr.length)];

// @ts-ignore
const uuidv4Template = [1e7] + -1e3 + -4e3 + -8e3 + -1e11;

/**
 * @return {string}
 */
const uuidv4 = () => uuidv4Template.replace(/[018]/g, /** @param {number} c */ (c) => (c ^ (uint32() & (15 >> (c / 4)))).toString(16));

/**
 * Utility module to work with time.
 *
 * @module time
 */

/**
 * Return current unix time.
 *
 * @return {number}
 */
const getUnixTime = Date.now;

/**
 * Utility helpers to work with promises.
 *
 * @module promise
 */

/**
 * @template T
 * @callback PromiseResolve
 * @param {T|PromiseLike<T>} [result]
 */

/**
 * @template T
 * @param {function(PromiseResolve<T>,function(Error):void):any} f
 * @return {Promise<T>}
 */
const create$6 = (f) => /** @type {Promise<T>} */ (new Promise(f));

/**
 * `Promise.all` wait for all promises in the array to resolve and return the result
 * @template {unknown[] | []} PS
 *
 * @param {PS} ps
 * @return {Promise<{ -readonly [P in keyof PS]: Awaited<PS[P]> }>}
 */
Promise.all.bind(Promise);

/**
 * Often used conditions.
 *
 * @module conditions
 */

/**
 * @template T
 * @param {T|null|undefined} v
 * @return {T|null}
 */
/* c8 ignore next */
const undefinedToNull = (v) => (v === undefined ? null : v);

/* eslint-env browser */

/**
 * Isomorphic variable storage.
 *
 * Uses LocalStorage in the browser and falls back to in-memory storage.
 *
 * @module storage
 */

/* c8 ignore start */
class VarStoragePolyfill {
	constructor() {
		this.map = new Map();
	}

	/**
	 * @param {string} key
	 * @param {any} newValue
	 */
	setItem(key, newValue) {
		this.map.set(key, newValue);
	}

	/**
	 * @param {string} key
	 */
	getItem(key) {
		return this.map.get(key);
	}
}
/* c8 ignore stop */

/**
 * @type {any}
 */
let _localStorage = new VarStoragePolyfill();
let usePolyfill = true;

/* c8 ignore start */
try {
	// if the same-origin rule is violated, accessing localStorage might thrown an error
	if (typeof localStorage !== "undefined" && localStorage) {
		_localStorage = localStorage;
		usePolyfill = false;
	}
} catch (e) {}
/* c8 ignore stop */

/**
 * This is basically localStorage in browser, or a polyfill in nodejs
 */
/* c8 ignore next */
const varStorage = _localStorage;

const EqualityTraitSymbol = Symbol("Equality");

/**
 * @typedef {{ [EqualityTraitSymbol]:(other:EqualityTrait)=>boolean }} EqualityTrait
 */

/**
 *
 * Utility function to compare any two objects.
 *
 * Note that it is expected that the first parameter is more specific than the latter one.
 *
 * @example js
 *     class X { [traits.EqualityTraitSymbol] (other) { return other === this }  }
 *     class X2 { [traits.EqualityTraitSymbol] (other) { return other === this }, x2 () { return 2 }  }
 *     // this is fine
 *     traits.equals(new X2(), new X())
 *     // this is not, because the left type is less specific than the right one
 *     traits.equals(new X(), new X2())
 *
 * @template {EqualityTrait} T
 * @param {NoInfer<T>} a
 * @param {T} b
 * @return {boolean}
 */
const equals = (a, b) => a === b || !!a?.[EqualityTraitSymbol]?.(b) || false;

/**
 * @param {any} o
 * @return {o is { [k:string]:any }}
 */
const isObject$1 = (o) => typeof o === "object";

/**
 * Object.assign
 */
const assign = Object.assign;

/**
 * @param {Object<string,any>} obj
 */
const keys$1 = Object.keys;

/**
 * @template V
 * @param {{[k:string]:V}} obj
 * @param {function(V,string):any} f
 */
const forEach = (obj, f) => {
	for (const key in obj) {
		f(obj[key], key);
	}
};

/**
 * @param {Object<string,any>} obj
 * @return {number}
 */
const size$3 = (obj) => keys$1(obj).length;

/**
 * @param {Object|null|undefined} obj
 */
const isEmpty = (obj) => {
	// eslint-disable-next-line no-unreachable-loop
	for (const _k in obj) {
		return false;
	}
	return true;
};

/**
 * @template {{ [key:string|number|symbol]: any }} T
 * @param {T} obj
 * @param {(v:T[keyof T],k:keyof T)=>boolean} f
 * @return {boolean}
 */
const every = (obj, f) => {
	for (const key in obj) {
		if (!f(obj[key], key)) {
			return false;
		}
	}
	return true;
};

/**
 * Calls `Object.prototype.hasOwnProperty`.
 *
 * @param {any} obj
 * @param {string|number|symbol} key
 * @return {boolean}
 */
const hasProperty$1 = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key);

/**
 * @param {Object<string,any>} a
 * @param {Object<string,any>} b
 * @return {boolean}
 */
const equalFlat = (a, b) => a === b || (size$3(a) === size$3(b) && every(a, (val, key) => (val !== undefined || hasProperty$1(b, key)) && equals(b[key], val)));

/**
 * Make an object immutable. This hurts performance and is usually not needed if you perform good
 * coding practices.
 */
const freeze = Object.freeze;

/**
 * Make an object and all its children immutable.
 * This *really* hurts performance and is usually not needed if you perform good coding practices.
 *
 * @template {any} T
 * @param {T} o
 * @return {Readonly<T>}
 */
const deepFreeze = (o) => {
	for (const key in o) {
		const c = o[key];
		if (typeof c === "object" || typeof c === "function") {
			deepFreeze(o[key]);
		}
	}
	return freeze(o);
};

/**
 * Common functions and function call helpers.
 *
 * @module function
 */

/**
 * Calls all functions in `fs` with args. Only throws after all functions were called.
 *
 * @param {Array<function>} fs
 * @param {Array<any>} args
 */
const callAll = (fs, args, i = 0) => {
	try {
		for (; i < fs.length; i++) {
			fs[i](...args);
		}
	} finally {
		if (i < fs.length) {
			callAll(fs, args, i + 1);
		}
	}
};

/**
 * @template A
 *
 * @param {A} a
 * @return {A}
 */
const id = (a) => a;

/* c8 ignore start */

/**
 * @param {any} a
 * @param {any} b
 * @return {boolean}
 */
const equalityDeep = (a, b) => {
	if (a === b) {
		return true;
	}
	if (a == null || b == null || (a.constructor !== b.constructor && (a.constructor || Object) !== (b.constructor || Object))) {
		return false;
	}
	if (a[EqualityTraitSymbol] != null) {
		return a[EqualityTraitSymbol](b);
	}
	switch (a.constructor) {
		case ArrayBuffer:
			a = new Uint8Array(a);
			b = new Uint8Array(b);
		// eslint-disable-next-line no-fallthrough
		case Uint8Array: {
			if (a.byteLength !== b.byteLength) {
				return false;
			}
			for (let i = 0; i < a.length; i++) {
				if (a[i] !== b[i]) {
					return false;
				}
			}
			break;
		}
		case Set: {
			if (a.size !== b.size) {
				return false;
			}
			for (const value of a) {
				if (!b.has(value)) {
					return false;
				}
			}
			break;
		}
		case Map: {
			if (a.size !== b.size) {
				return false;
			}
			for (const key of a.keys()) {
				if (!b.has(key) || !equalityDeep(a.get(key), b.get(key))) {
					return false;
				}
			}
			break;
		}
		case undefined:
		case Object:
			if (size$3(a) !== size$3(b)) {
				return false;
			}
			for (const key in a) {
				if (!hasProperty$1(a, key) || !equalityDeep(a[key], b[key])) {
					return false;
				}
			}
			break;
		case Array:
			if (a.length !== b.length) {
				return false;
			}
			for (let i = 0; i < a.length; i++) {
				if (!equalityDeep(a[i], b[i])) {
					return false;
				}
			}
			break;
		default:
			return false;
	}
	return true;
};

/**
 * @template V
 * @template {V} OPTS
 *
 * @param {V} value
 * @param {Array<OPTS>} options
 */
// @ts-ignore
const isOneOf = (value, options) => options.includes(value);

var define_process_env_default = {};
const isNode$1 =
	typeof process !== "undefined" &&
	process.release &&
	/node|io\.js/.test(process.release.name) &&
	Object.prototype.toString.call(typeof process !== "undefined" ? process : 0) === "[object process]";
const isBrowser = typeof window !== "undefined" && typeof document !== "undefined" && !isNode$1;
let params;
const computeParams = () => {
	if (params === void 0) {
		if (isNode$1) {
			params = create$9();
			const pargs = process.argv;
			let currParamName = null;
			for (let i = 0; i < pargs.length; i++) {
				const parg = pargs[i];
				if (parg[0] === "-") {
					if (currParamName !== null) {
						params.set(currParamName, "");
					}
					currParamName = parg;
				} else {
					if (currParamName !== null) {
						params.set(currParamName, parg);
						currParamName = null;
					}
				}
			}
			if (currParamName !== null) {
				params.set(currParamName, "");
			}
		} else if (typeof location === "object") {
			params = create$9();
			(location.search || "?")
				.slice(1)
				.split("&")
				.forEach((kv) => {
					if (kv.length !== 0) {
						const [key, value] = kv.split("=");
						params.set(`--${fromCamelCase(key, "-")}`, value);
						params.set(`-${fromCamelCase(key, "-")}`, value);
					}
				});
		} else {
			params = create$9();
		}
	}
	return params;
};
const hasParam = (name) => computeParams().has(name);
const getVariable = (name) => (isNode$1 ? undefinedToNull(define_process_env_default[name.toUpperCase().replaceAll("-", "_")]) : undefinedToNull(varStorage.getItem(name)));
const hasConf = (name) => hasParam("--" + name) || getVariable(name) !== null;
const production = hasConf("production");
const forceColor = isNode$1 && isOneOf(define_process_env_default.FORCE_COLOR, ["true", "1", "2"]);
const supportsColor =
	forceColor ||
	(!hasParam("--no-colors") && // @todo deprecate --no-colors
		!hasConf("no-color") &&
		(!isNode$1 || process.stdout.isTTY) &&
		(!isNode$1 || hasParam("--color") || getVariable("COLORTERM") !== null || (getVariable("TERM") || "").includes("color")));

/**
 * Utility functions to work with buffers (Uint8Array).
 *
 * @module buffer
 */

/**
 * @param {number} len
 */
const createUint8ArrayFromLen = (len) => new Uint8Array(len);

/* c8 ignore start */
/**
 * @param {Uint8Array} bytes
 * @return {string}
 */
const toBase64Browser = (bytes) => {
	let s = "";
	for (let i = 0; i < bytes.byteLength; i++) {
		s += fromCharCode(bytes[i]);
	}
	// eslint-disable-next-line no-undef
	return btoa(s);
};
/* c8 ignore stop */

/**
 * @param {Uint8Array} bytes
 * @return {string}
 */
const toBase64Node = (bytes) => Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString("base64");

/* c8 ignore next */
const toBase64 = isBrowser ? toBase64Browser : toBase64Node;

/**
 * Copy the content of an Uint8Array view to a new ArrayBuffer.
 *
 * @param {Uint8Array} uint8Array
 * @return {Uint8Array}
 */
const copyUint8Array = (uint8Array) => {
	const newBuf = createUint8ArrayFromLen(uint8Array.byteLength);
	newBuf.set(uint8Array);
	return newBuf;
};

/**
 * Encode anything as a UInt8Array. It's a pun on typescripts's `any` type.
 * See encoding.writeAny for more information.
 *
 * @param {any} data
 * @return {Uint8Array}
 */
const encodeAny = (data) => encode((encoder) => writeAny(encoder, data));

/**
 * Working with value pairs.
 *
 * @module pair
 */

/**
 * @template L,R
 */
class Pair {
	/**
	 * @param {L} left
	 * @param {R} right
	 */
	constructor(left, right) {
		this.left = left;
		this.right = right;
	}
}

/**
 * @template L,R
 * @param {L} left
 * @param {R} right
 * @return {Pair<L,R>}
 */
const create$5 = (left, right) => new Pair(left, right);

/**
 * Fast Pseudo Random Number Generators.
 *
 * Given a seed a PRNG generates a sequence of numbers that cannot be reasonably predicted.
 * Two PRNGs must generate the same random sequence of numbers if  given the same seed.
 *
 * @module prng
 */

/**
 * Generates a single random bool.
 *
 * @param {PRNG} gen A random number generator.
 * @return {Boolean} A random boolean
 */
const bool = (gen) => gen.next() >= 0.5;

/**
 * Generates a random integer with 53 bit resolution.
 *
 * @param {PRNG} gen A random number generator.
 * @param {Number} min The lower bound of the allowed return values (inclusive).
 * @param {Number} max The upper bound of the allowed return values (inclusive).
 * @return {Number} A random integer on [min, max]
 */
const int53 = (gen, min, max) => floor$1(gen.next() * (max + 1 - min) + min);

/**
 * Generates a random integer with 32 bit resolution.
 *
 * @param {PRNG} gen A random number generator.
 * @param {Number} min The lower bound of the allowed return values (inclusive).
 * @param {Number} max The upper bound of the allowed return values (inclusive).
 * @return {Number} A random integer on [min, max]
 */
const int32 = (gen, min, max) => floor$1(gen.next() * (max + 1 - min) + min);

/**
 * @deprecated
 * Optimized version of prng.int32. It has the same precision as prng.int32, but should be preferred when
 * openaring on smaller ranges.
 *
 * @param {PRNG} gen A random number generator.
 * @param {Number} min The lower bound of the allowed return values (inclusive).
 * @param {Number} max The upper bound of the allowed return values (inclusive). The max inclusive number is `binary.BITS31-1`
 * @return {Number} A random integer on [min, max]
 */
const int31 = (gen, min, max) => int32(gen, min, max);

/**
 * @param {PRNG} gen
 * @return {string} A single letter (a-z)
 */
const letter = (gen) => fromCharCode(int31(gen, 97, 122));

/**
 * @param {PRNG} gen
 * @param {number} [minLen=0]
 * @param {number} [maxLen=20]
 * @return {string} A random word (0-20 characters) without spaces consisting of letters (a-z)
 */
const word = (gen, minLen = 0, maxLen = 20) => {
	const len = int31(gen, minLen, maxLen);
	let str = "";
	for (let i = 0; i < len; i++) {
		str += letter(gen);
	}
	return str;
};

/**
 * Returns one element of a given array.
 *
 * @param {PRNG} gen A random number generator.
 * @param {Array<T>} array Non empty Array of possible values.
 * @return {T} One of the values of the supplied Array.
 * @template T
 */
const oneOf = (gen, array) => array[int31(gen, 0, array.length - 1)];
/* c8 ignore stop */

/**
 * @experimental WIP
 *
 * Simple & efficient schemas for your data.
 */

/**
 * @typedef {string|number|bigint|boolean|null|undefined|symbol} Primitive
 */

/**
 * @typedef {{ [k:string|number|symbol]: any }} AnyObject
 */

/**
 * @template T
 * @typedef {T extends Schema<infer X> ? X : T} Unwrap
 */

/**
 * @template T
 * @typedef {T extends Schema<infer X> ? X : T} TypeOf
 */

/**
 * @template {readonly unknown[]} T
 * @typedef {T extends readonly [Schema<infer First>, ...infer Rest] ? [First, ...UnwrapArray<Rest>] : [] } UnwrapArray
 */

/**
 * @template T
 * @typedef {T extends Schema<infer S> ? Schema<S> : never} CastToSchema
 */

/**
 * @template {unknown[]} Arr
 * @typedef {Arr extends [...unknown[], infer L] ? L : never} TupleLast
 */

/**
 * @template {unknown[]} Arr
 * @typedef {Arr extends [...infer Fs, unknown] ? Fs : never} TuplePop
 */

/**
 * @template {readonly unknown[]} T
 * @typedef {T extends []
 *   ? {}
 *   : T extends [infer First]
 *   ? First
 *   : T extends [infer First, ...infer Rest]
 *   ? First & Intersect<Rest>
 *   : never
 * } Intersect
 */

const schemaSymbol = Symbol("0schema");

class ValidationError {
	constructor() {
		/**
		 * Reverse errors
		 * @type {Array<{ path: string?, expected: string, has: string, message: string? }>}
		 */
		this._rerrs = [];
	}

	/**
	 * @param {string?} path
	 * @param {string} expected
	 * @param {string} has
	 * @param {string?} message
	 */
	extend(path, expected, has, message = null) {
		this._rerrs.push({ path, expected, has, message });
	}

	toString() {
		const s = [];
		for (let i = this._rerrs.length - 1; i > 0; i--) {
			const r = this._rerrs[i];
			/* c8 ignore next */
			s.push(repeat(" ", (this._rerrs.length - i) * 2) + `${r.path != null ? `[${r.path}] ` : ""}${r.has} doesn't match ${r.expected}. ${r.message}`);
		}
		return s.join("\n");
	}
}

/**
 * @param {any} a
 * @param {any} b
 * @return {boolean}
 */
const shapeExtends = (a, b) => {
	if (a === b) return true;
	if (a == null || b == null || a.constructor !== b.constructor) return false;
	if (a[EqualityTraitSymbol]) return equals(a, b); // last resort: check equality (do this before array and obj check which don't implement the equality trait)
	if (isArray(a)) {
		return every$1(a, (aitem) => some(b, (bitem) => shapeExtends(aitem, bitem)));
	} else if (isObject$1(a)) {
		return every(a, (aitem, akey) => shapeExtends(aitem, b[akey]));
	}
	/* c8 ignore next */
	return false;
};

/**
 * @template T
 * @implements {equalityTraits.EqualityTrait}
 */
let Schema$1 = class Schema {
	// this.shape must not be defined on Schema. Otherwise typecheck on metatypes (e.g. $$object) won't work as expected anymore
	/**
	 * If true, the more things are added to the shape the more objects this schema will accept (e.g.
	 * union). By default, the more objects are added, the the fewer objects this schema will accept.
	 * @protected
	 */
	static _dilutes = false;

	/**
	 * @param {Schema<any>} other
	 */
	extends(other) {
		let [a, b] = [/** @type {any} */ (this).shape, /** @type {any} */ (other).shape];
		if (/** @type {typeof Schema<any>} */ (this.constructor)._dilutes) [b, a] = [a, b];
		return shapeExtends(a, b);
	}

	/**
	 * Overwrite this when necessary. By default, we only check the `shape` property which every shape
	 * should have.
	 * @param {Schema<any>} other
	 */
	equals(other) {
		// @ts-ignore
		return this.constructor === other.constructor && equalityDeep(this.shape, other.shape);
	}

	[schemaSymbol]() {
		return true;
	}

	/**
	 * @param {object} other
	 */
	[EqualityTraitSymbol](other) {
		return this.equals(/** @type {any} */ (other));
	}

	/**
	 * Use `schema.validate(obj)` with a typed parameter that is already of typed to be an instance of
	 * Schema. Validate will check the structure of the parameter and return true iff the instance
	 * really is an instance of Schema.
	 *
	 * @param {T} o
	 * @return {boolean}
	 */
	validate(o) {
		return this.check(o);
	}

	/* c8 ignore start */
	/**
	 * Similar to validate, but this method accepts untyped parameters.
	 *
	 * @param {any} _o
	 * @param {ValidationError} [_err]
	 * @return {_o is T}
	 */
	check(_o, _err) {
		methodUnimplemented();
	}
	/* c8 ignore stop */

	/**
	 * @type {Schema<T?>}
	 */
	get nullable() {
		// @ts-ignore
		return $union(this, $null);
	}

	/**
	 * @type {$Optional<Schema<T>>}
	 */
	get optional() {
		return new $Optional(/** @type {Schema<T>} */ (this));
	}

	/**
	 * Cast a variable to a specific type. Returns the casted value, or throws an exception otherwise.
	 * Use this if you know that the type is of a specific type and you just want to convince the type
	 * system.
	 *
	 * **Do not rely on these error messages!**
	 * Performs an assertion check only if not in a production environment.
	 *
	 * @template OO
	 * @param {OO} o
	 * @return {Extract<OO, T> extends never ? T : (OO extends Array<never> ? T : Extract<OO,T>)}
	 */
	cast(o) {
		assert(o, this);
		return /** @type {any} */ (o);
	}

	/**
	 * EXPECTO PATRONUM!! 🪄
	 * This function protects against type errors. Though it may not work in the real world.
	 *
	 * "After all this time?"
	 * "Always." - Snape, talking about type safety
	 *
	 * Ensures that a variable is a a specific type. Returns the value, or throws an exception if the assertion check failed.
	 * Use this if you know that the type is of a specific type and you just want to convince the type
	 * system.
	 *
	 * Can be useful when defining lambdas: `s.lambda(s.$number, s.$void).expect((n) => n + 1)`
	 *
	 * **Do not rely on these error messages!**
	 * Performs an assertion check if not in a production environment.
	 *
	 * @param {T} o
	 * @return {o extends T ? T : never}
	 */
	expect(o) {
		assert(o, this);
		return o;
	}
};

/**
 * @template {(new (...args:any[]) => any) | ((...args:any[]) => any)} Constr
 * @typedef {Constr extends ((...args:any[]) => infer T) ? T : (Constr extends (new (...args:any[]) => any) ? InstanceType<Constr> : never)} Instance
 */

/**
 * @template {(new (...args:any[]) => any) | ((...args:any[]) => any)} C
 * @extends {Schema<Instance<C>>}
 */
class $ConstructedBy extends Schema$1 {
	/**
	 * @param {C} c
	 * @param {((o:Instance<C>)=>boolean)|null} check
	 */
	constructor(c, check) {
		super();
		this.shape = c;
		this._c = check;
	}

	/**
	 * @param {any} o
	 * @param {ValidationError} [err]
	 * @return {o is C extends ((...args:any[]) => infer T) ? T : (C extends (new (...args:any[]) => any) ? InstanceType<C> : never)} o
	 */
	check(o, err = undefined) {
		const c = o?.constructor === this.shape && (this._c == null || this._c(o));
		/* c8 ignore next */
		!c && err?.extend(null, this.shape.name, o?.constructor.name, o?.constructor !== this.shape ? "Constructor match failed" : "Check failed");
		return c;
	}
}

/**
 * @template {(new (...args:any[]) => any) | ((...args:any[]) => any)} C
 * @param {C} c
 * @param {((o:Instance<C>) => boolean)|null} check
 * @return {CastToSchema<$ConstructedBy<C>>}
 */
const $constructedBy = (c, check = null) => new $ConstructedBy(c, check);
$constructedBy($ConstructedBy);

/**
 * Check custom properties on any object. You may want to overwrite the generated Schema<any>.
 *
 * @extends {Schema<any>}
 */
class $Custom extends Schema$1 {
	/**
	 * @param {(o:any) => boolean} check
	 */
	constructor(check) {
		super();
		/**
		 * @type {(o:any) => boolean}
		 */
		this.shape = check;
	}

	/**
	 * @param {any} o
	 * @param {ValidationError} err
	 * @return {o is any}
	 */
	check(o, err) {
		const c = this.shape(o);
		/* c8 ignore next */
		!c && err?.extend(null, "custom prop", o?.constructor.name, "failed to check custom prop");
		return c;
	}
}

/**
 * @param {(o:any) => boolean} check
 * @return {Schema<any>}
 */
const $custom = (check) => new $Custom(check);
$constructedBy($Custom);

/**
 * @template {Primitive} T
 * @extends {Schema<T>}
 */
class $Literal extends Schema$1 {
	/**
	 * @param {Array<T>} literals
	 */
	constructor(literals) {
		super();
		this.shape = literals;
	}

	/**
	 *
	 * @param {any} o
	 * @param {ValidationError} [err]
	 * @return {o is T}
	 */
	check(o, err) {
		const c = this.shape.some((a) => a === o);
		/* c8 ignore next */
		!c && err?.extend(null, this.shape.join(" | "), o.toString());
		return c;
	}
}

/**
 * @template {Primitive[]} T
 * @param {T} literals
 * @return {CastToSchema<$Literal<T[number]>>}
 */
const $literal = (...literals) => new $Literal(literals);
const $$literal = $constructedBy($Literal);

/**
 * @template {Array<string|Schema<string|number>>} Ts
 * @typedef {Ts extends [] ? `` : (Ts extends [infer T] ? (Unwrap<T> extends (string|number) ? Unwrap<T> : never) : (Ts extends [infer T1, ...infer Rest] ? `${Unwrap<T1> extends (string|number) ? Unwrap<T1> : never}${Rest extends Array<string|Schema<string|number>> ? CastStringTemplateArgsToTemplate<Rest> : never}` : never))} CastStringTemplateArgsToTemplate
 */

/**
 * @param {string} str
 * @return {string}
 */
const _regexEscape = /** @type {any} */ (RegExp).escape || /** @type {(str:string) => string} */ ((str) => str.replace(/[().|&,$^[\]]/g, (s) => "\\" + s));

/**
 * @param {string|Schema<any>} s
 * @return {string[]}
 */
const _schemaStringTemplateToRegex = (s) => {
	if ($string.check(s)) {
		return [_regexEscape(s)];
	}
	if ($$literal.check(s)) {
		return /** @type {Array<string|number>} */ (s.shape).map((v) => v + "");
	}
	if ($$number.check(s)) {
		return ["[+-]?\\d+.?\\d*"];
	}
	if ($$string.check(s)) {
		return [".*"];
	}
	if ($$union.check(s)) {
		return s.shape.map(_schemaStringTemplateToRegex).flat(1);
	}
	/* c8 ignore next 2 */
	// unexpected schema structure (only supports unions and string in literal types)
	unexpectedCase();
};

/**
 * @template {Array<string|Schema<string|number>>} T
 * @extends {Schema<CastStringTemplateArgsToTemplate<T>>}
 */
class $StringTemplate extends Schema$1 {
	/**
	 * @param {T} shape
	 */
	constructor(shape) {
		super();
		this.shape = shape;
		this._r = new RegExp(
			"^" +
				shape
					.map(_schemaStringTemplateToRegex)
					.map((opts) => `(${opts.join("|")})`)
					.join("") +
				"$",
		);
	}

	/**
	 * @param {any} o
	 * @param {ValidationError} [err]
	 * @return {o is CastStringTemplateArgsToTemplate<T>}
	 */
	check(o, err) {
		const c = this._r.exec(o) != null;
		/* c8 ignore next */
		!c && err?.extend(null, this._r.toString(), o.toString(), "String doesn't match string template.");
		return c;
	}
}
$constructedBy($StringTemplate);

const isOptionalSymbol = Symbol("optional");
/**
 * @template {Schema<any>} S
 * @extends Schema<Unwrap<S>|undefined>
 */
class $Optional extends Schema$1 {
	/**
	 * @param {S} shape
	 */
	constructor(shape) {
		super();
		this.shape = shape;
	}

	/**
	 * @param {any} o
	 * @param {ValidationError} [err]
	 * @return {o is (Unwrap<S>|undefined)}
	 */
	check(o, err) {
		const c = o === undefined || this.shape.check(o);
		/* c8 ignore next */
		!c && err?.extend(null, "undefined (optional)", "()");
		return c;
	}

	get [isOptionalSymbol]() {
		return true;
	}
}
const $$optional = $constructedBy($Optional);

/**
 * @extends Schema<never>
 */
class $Never extends Schema$1 {
	/**
	 * @param {any} _o
	 * @param {ValidationError} [err]
	 * @return {_o is never}
	 */
	check(_o, err) {
		/* c8 ignore next */
		err?.extend(null, "never", typeof _o);
		return false;
	}
}
$constructedBy($Never);

/**
 * @template {{ [key: string|symbol|number]: Schema<any> }} S
 * @typedef {{ [Key in keyof S as S[Key] extends $Optional<Schema<any>> ? Key : never]?: S[Key] extends $Optional<Schema<infer Type>> ? Type : never } & { [Key in keyof S as S[Key] extends $Optional<Schema<any>> ? never : Key]: S[Key] extends Schema<infer Type> ? Type : never }} $ObjectToType
 */

/**
 * @template {{[key:string|symbol|number]: Schema<any>}} S
 * @extends {Schema<$ObjectToType<S>>}
 */
class $Object extends Schema$1 {
	/**
	 * @param {S} shape
	 * @param {boolean} partial
	 */
	constructor(shape, partial = false) {
		super();
		/**
		 * @type {S}
		 */
		this.shape = shape;
		this._isPartial = partial;
	}

	static _dilutes = true;

	/**
	 * @type {Schema<Partial<$ObjectToType<S>>>}
	 */
	get partial() {
		return new $Object(this.shape, true);
	}

	/**
	 * @param {any} o
	 * @param {ValidationError} err
	 * @return {o is $ObjectToType<S>}
	 */
	check(o, err) {
		if (o == null) {
			/* c8 ignore next */
			err?.extend(null, "object", "null");
			return false;
		}
		return every(this.shape, (vv, vk) => {
			const c = (this._isPartial && !hasProperty$1(o, vk)) || vv.check(o[vk], err);
			!c && err?.extend(vk.toString(), vv.toString(), typeof o[vk], "Object property does not match");
			return c;
		});
	}
}

/**
 * @template S
 * @typedef {Schema<{ [Key in keyof S as S[Key] extends $Optional<Schema<any>> ? Key : never]?: S[Key] extends $Optional<Schema<infer Type>> ? Type : never } & { [Key in keyof S as S[Key] extends $Optional<Schema<any>> ? never : Key]: S[Key] extends Schema<infer Type> ? Type : never }>} _ObjectDefToSchema
 */

// I used an explicit type annotation instead of $ObjectToType, so that the user doesn't see the
// weird type definitions when inspecting type definions.
/**
 * @template {{ [key:string|symbol|number]: Schema<any> }} S
 * @param {S} def
 * @return {_ObjectDefToSchema<S> extends Schema<infer S> ? Schema<{ [K in keyof S]: S[K] }> : never}
 */
const $object = (def) => /** @type {any} */ (new $Object(def));
const $$object = $constructedBy($Object);
/**
 * @type {Schema<{[key:string]: any}>}
 */
const $objectAny = $custom((o) => o != null && (o.constructor === Object || o.constructor == null));

/**
 * @template {Schema<string|number|symbol>} Keys
 * @template {Schema<any>} Values
 * @extends {Schema<{ [key in Unwrap<Keys>]: Unwrap<Values> }>}
 */
class $Record extends Schema$1 {
	/**
	 * @param {Keys} keys
	 * @param {Values} values
	 */
	constructor(keys, values) {
		super();
		this.shape = {
			keys,
			values,
		};
	}

	/**
	 * @param {any} o
	 * @param {ValidationError} err
	 * @return {o is { [key in Unwrap<Keys>]: Unwrap<Values> }}
	 */
	check(o, err) {
		return (
			o != null &&
			every(o, (vv, vk) => {
				const ck = this.shape.keys.check(vk, err);
				/* c8 ignore next */
				!ck && err?.extend(vk + "", "Record", typeof o, ck ? "Key doesn't match schema" : "Value doesn't match value");
				return ck && this.shape.values.check(vv, err);
			})
		);
	}
}

/**
 * @template {Schema<string|number|symbol>} Keys
 * @template {Schema<any>} Values
 * @param {Keys} keys
 * @param {Values} values
 * @return {CastToSchema<$Record<Keys,Values>>}
 */
const $record = (keys, values) => new $Record(keys, values);
const $$record = $constructedBy($Record);

/**
 * @template {Schema<any>[]} S
 * @extends {Schema<{ [Key in keyof S]: S[Key] extends Schema<infer Type> ? Type : never }>}
 */
class $Tuple extends Schema$1 {
	/**
	 * @param {S} shape
	 */
	constructor(shape) {
		super();
		this.shape = shape;
	}

	/**
	 * @param {any} o
	 * @param {ValidationError} err
	 * @return {o is { [K in keyof S]: S[K] extends Schema<infer Type> ? Type : never }}
	 */
	check(o, err) {
		return (
			o != null &&
			every(this.shape, (vv, vk) => {
				const c = /** @type {Schema<any>} */ (vv).check(o[vk], err);
				/* c8 ignore next */
				!c && err?.extend(vk.toString(), "Tuple", typeof vv);
				return c;
			})
		);
	}
}

/**
 * @template {Array<Schema<any>>} T
 * @param {T} def
 * @return {CastToSchema<$Tuple<T>>}
 */
const $tuple = (...def) => new $Tuple(def);
$constructedBy($Tuple);

/**
 * @template {Schema<any>} S
 * @extends {Schema<Array<S extends Schema<infer T> ? T : never>>}
 */
class $Array extends Schema$1 {
	/**
	 * @param {Array<S>} v
	 */
	constructor(v) {
		super();
		/**
		 * @type {Schema<S extends Schema<infer T> ? T : never>}
		 */
		this.shape = v.length === 1 ? v[0] : new $Union(v);
	}

	/**
	 * @param {any} o
	 * @param {ValidationError} [err]
	 * @return {o is Array<S extends Schema<infer T> ? T : never>} o
	 */
	check(o, err) {
		const c = isArray(o) && every$1(o, (oi) => this.shape.check(oi));
		/* c8 ignore next */
		!c && err?.extend(null, "Array", "");
		return c;
	}
}

/**
 * @template {Array<Schema<any>>} T
 * @param {T} def
 * @return {Schema<Array<T extends Array<Schema<infer S>> ? S : never>>}
 */
const $array = (...def) => new $Array(def);
const $$array = $constructedBy($Array);
/**
 * @type {Schema<Array<any>>}
 */
const $arrayAny = $custom((o) => isArray(o));

/**
 * @template T
 * @extends {Schema<T>}
 */
class $InstanceOf extends Schema$1 {
	/**
	 * @param {new (...args:any) => T} constructor
	 * @param {((o:T) => boolean)|null} check
	 */
	constructor(constructor, check) {
		super();
		this.shape = constructor;
		this._c = check;
	}

	/**
	 * @param {any} o
	 * @param {ValidationError} err
	 * @return {o is T}
	 */
	check(o, err) {
		const c = o instanceof this.shape && (this._c == null || this._c(o));
		/* c8 ignore next */
		!c && err?.extend(null, this.shape.name, o?.constructor.name);
		return c;
	}
}

/**
 * @template T
 * @param {new (...args:any) => T} c
 * @param {((o:T) => boolean)|null} check
 * @return {Schema<T>}
 */
const $instanceOf = (c, check = null) => new $InstanceOf(c, check);
$constructedBy($InstanceOf);

const $$schema = $instanceOf(Schema$1);

/**
 * @template {Schema<any>[]} Args
 * @typedef {(...args:UnwrapArray<TuplePop<Args>>)=>Unwrap<TupleLast<Args>>} _LArgsToLambdaDef
 */

/**
 * @template {Array<Schema<any>>} Args
 * @extends {Schema<_LArgsToLambdaDef<Args>>}
 */
class $Lambda extends Schema$1 {
	/**
	 * @param {Args} args
	 */
	constructor(args) {
		super();
		this.len = args.length - 1;
		this.args = $tuple(...args.slice(-1));
		this.res = args[this.len];
	}

	/**
	 * @param {any} f
	 * @param {ValidationError} err
	 * @return {f is _LArgsToLambdaDef<Args>}
	 */
	check(f, err) {
		const c = f.constructor === Function && f.length <= this.len;
		/* c8 ignore next */
		!c && err?.extend(null, "function", typeof f);
		return c;
	}
}
const $$lambda = $constructedBy($Lambda);

/**
 * @type {Schema<Function>}
 */
const $function = $custom((o) => typeof o === "function");

/**
 * @template {Array<Schema<any>>} T
 * @extends {Schema<Intersect<UnwrapArray<T>>>}
 */
class $Intersection extends Schema$1 {
	/**
	 * @param {T} v
	 */
	constructor(v) {
		super();
		/**
		 * @type {T}
		 */
		this.shape = v;
	}

	/**
	 * @param {any} o
	 * @param {ValidationError} [err]
	 * @return {o is Intersect<UnwrapArray<T>>}
	 */
	check(o, err) {
		// @ts-ignore
		const c = every$1(this.shape, (check) => check.check(o, err));
		/* c8 ignore next */
		!c && err?.extend(null, "Intersectinon", typeof o);
		return c;
	}
}
$constructedBy($Intersection, (o) => o.shape.length > 0); // Intersection with length=0 is considered "any"

/**
 * @template S
 * @extends {Schema<S>}
 */
class $Union extends Schema$1 {
	static _dilutes = true;

	/**
	 * @param {Array<Schema<S>>} v
	 */
	constructor(v) {
		super();
		this.shape = v;
	}

	/**
	 * @param {any} o
	 * @param {ValidationError} [err]
	 * @return {o is S}
	 */
	check(o, err) {
		const c = some(this.shape, (vv) => vv.check(o, err));
		err?.extend(null, "Union", typeof o);
		return c;
	}
}

/**
 * @template {Array<any>} T
 * @param {T} schemas
 * @return {CastToSchema<$Union<Unwrap<ReadSchema<T>>>>}
 */
const $union = (...schemas) =>
	schemas.findIndex(($s) => $$union.check($s)) >= 0 ?
		$union(
			...schemas
				.map(($s) => $$3($s))
				.map(($s) => ($$union.check($s) ? $s.shape : [$s]))
				.flat(1),
		)
	: schemas.length === 1 ? schemas[0]
	: new $Union(schemas);
const $$union = /** @type {Schema<$Union<any>>} */ ($constructedBy($Union));

const _t$2 = () => true;
/**
 * @type {Schema<any>}
 */
const $any = $custom(_t$2);
const $$any = /** @type {Schema<Schema<any>>} */ ($constructedBy($Custom, (o) => o.shape === _t$2));

/**
 * @type {Schema<bigint>}
 */
const $bigint = $custom((o) => typeof o === "bigint");
const $$bigint = /** @type {Schema<Schema<BigInt>>} */ ($custom((o) => o === $bigint));

/**
 * @type {Schema<symbol>}
 */
const $symbol = $custom((o) => typeof o === "symbol");
/** @type {Schema<Schema<Symbol>>} */ ($custom((o) => o === $symbol));

/**
 * @type {Schema<number>}
 */
const $number = $custom((o) => typeof o === "number");
const $$number = /** @type {Schema<Schema<number>>} */ ($custom((o) => o === $number));

/**
 * @type {Schema<string>}
 */
const $string = $custom((o) => typeof o === "string");
const $$string = /** @type {Schema<Schema<string>>} */ ($custom((o) => o === $string));

/**
 * @type {Schema<boolean>}
 */
const $boolean = $custom((o) => typeof o === "boolean");
const $$boolean = /** @type {Schema<Schema<Boolean>>} */ ($custom((o) => o === $boolean));

/**
 * @type {Schema<undefined>}
 */
const $undefined = $literal(undefined);
/** @type {Schema<Schema<undefined>>} */ ($constructedBy($Literal, (o) => o.shape.length === 1 && o.shape[0] === undefined));

/**
 * @type {Schema<void>}
 */
$literal(undefined);

const $null = $literal(null);
const $$null = /** @type {Schema<Schema<null>>} */ ($constructedBy($Literal, (o) => o.shape.length === 1 && o.shape[0] === null));

$constructedBy(Uint8Array);
/** @type {Schema<Schema<Uint8Array>>} */ ($constructedBy($ConstructedBy, (o) => o.shape === Uint8Array));

/**
 * @type {Schema<Primitive>}
 */
const $primitive = $union($number, $string, $null, $undefined, $bigint, $boolean, $symbol);

/**
 * @typedef {JSON[]} JSONArray
 */
/**
 * @typedef {Primitive|JSONArray|{ [key:string]:JSON }} JSON
 */
/**
 * @type {Schema<null|number|string|boolean|JSON[]|{[key:string]:JSON}>}
 */
(
	() => {
		const $jsonArr = /** @type {$Array<$any>} */ ($array($any));
		const $jsonRecord = /** @type {$Record<$string,$any>} */ ($record($string, $any));
		const $json = $union($number, $string, $null, $boolean, $jsonArr, $jsonRecord);
		$jsonArr.shape = $json;
		$jsonRecord.shape.values = $json;
		return $json;
	}
)();

/**
 * @template {any} IN
 * @typedef {IN extends Schema<any> ? IN
 *   : (IN extends string|number|boolean|null ? Schema<IN>
 *     : (IN extends new (...args:any[])=>any ? Schema<InstanceType<IN>>
 *       : (IN extends any[] ? Schema<{ [K in keyof IN]: Unwrap<ReadSchema<IN[K]>> }[number]>
 *       : (IN extends object ? (_ObjectDefToSchema<{[K in keyof IN]:ReadSchema<IN[K]>}> extends Schema<infer S> ? Schema<{ [K in keyof S]: S[K] }> : never)
 *         : never)
 *         )
 *       )
 *     )
 * } ReadSchemaOld
 */

/**
 * @template {any} IN
 * @typedef {[Extract<IN,Schema<any>>,Extract<IN,string|number|boolean|null>,Extract<IN,new (...args:any[])=>any>,Extract<IN,any[]>,Extract<Exclude<IN,Schema<any>|string|number|boolean|null|(new (...args:any[])=>any)|any[]>,object>] extends [infer Schemas, infer Primitives, infer Constructors, infer Arrs, infer Obj]
 *   ? Schema<
 *       (Schemas extends Schema<infer S> ? S : never)
 *     | Primitives
 *     | (Constructors extends new (...args:any[])=>any ? InstanceType<Constructors> : never)
 *     | (Arrs extends any[] ? { [K in keyof Arrs]: Unwrap<ReadSchema<Arrs[K]>> }[number] : never)
 *     | (Obj extends object ? Unwrap<(_ObjectDefToSchema<{[K in keyof Obj]:ReadSchema<Obj[K]>}> extends Schema<infer S> ? Schema<{ [K in keyof S]: S[K] }> : never)> : never)>
 *   : never
 * } ReadSchema
 */

/**
 * @typedef {ReadSchema<{x:42}|{y:99}|Schema<string>|[1,2,{}]>} Q
 */

/**
 * @template IN
 * @param {IN} o
 * @return {ReadSchema<IN>}
 */
const $$3 = (o) => {
	if ($$schema.check(o)) {
		return /** @type {any} */ (o);
	} else if ($objectAny.check(o)) {
		/**
		 * @type {any}
		 */
		const o2 = {};
		for (const k in o) {
			o2[k] = $$3(o[k]);
		}
		return /** @type {any} */ ($object(o2));
	} else if ($arrayAny.check(o)) {
		return /** @type {any} */ ($union(...o.map($$3)));
	} else if ($primitive.check(o)) {
		return /** @type {any} */ ($literal(o));
	} else if ($function.check(o)) {
		return /** @type {any} */ ($constructedBy(/** @type {any} */ (o)));
	}
	/* c8 ignore next */
	unexpectedCase();
};

/* c8 ignore start */
/**
 * Assert that a variable is of this specific type.
 * The assertion check is only performed in non-production environments.
 *
 * @type {<T>(o:any,schema:Schema<T>) => asserts o is T}
 */
const assert =
	production ?
		() => {}
	:	(o, schema) => {
			const err = new ValidationError();
			if (!schema.check(o, err)) {
				throw create$7(`Expected value to be of type ${schema.constructor.name}.\n${err.toString()}`);
			}
		};
/* c8 ignore end */

/**
 * @template In
 * @template Out
 * @typedef {{ if: Schema<In>, h: (o:In,state?:any)=>Out }} Pattern
 */

/**
 * @template {Pattern<any,any>} P
 * @template In
 * @typedef {ReturnType<Extract<P,Pattern<In extends number ? number : (In extends string ? string : In),any>>['h']>} PatternMatchResult
 */

/**
 * @todo move this to separate library
 * @template {any} [State=undefined]
 * @template {Pattern<any,any>} [Patterns=never]
 */
class PatternMatcher {
	/**
	 * @param {Schema<State>} [$state]
	 */
	constructor($state) {
		/**
		 * @type {Array<Patterns>}
		 */
		this.patterns = [];
		this.$state = $state;
	}

	/**
	 * @template P
	 * @template R
	 * @param {P} pattern
	 * @param {(o:NoInfer<Unwrap<ReadSchema<P>>>,s:State)=>R} handler
	 * @return {PatternMatcher<State,Patterns|Pattern<Unwrap<ReadSchema<P>>,R>>}
	 */
	if(pattern, handler) {
		// @ts-ignore
		this.patterns.push({ if: $$3(pattern), h: handler });
		// @ts-ignore
		return this;
	}

	/**
	 * @template R
	 * @param {(o:any,s:State)=>R} h
	 */
	else(h) {
		return this.if($any, h);
	}

	/**
	 * @return {State extends undefined
	 *   ? <In extends Unwrap<Patterns['if']>>(o:In,state?:undefined)=>PatternMatchResult<Patterns,In>
	 *   : <In extends Unwrap<Patterns['if']>>(o:In,state:State)=>PatternMatchResult<Patterns,In>}
	 */
	done() {
		// @ts-ignore
		return /** @type {any} */ (o, s) => {
			for (let i = 0; i < this.patterns.length; i++) {
				const p = this.patterns[i];
				if (p.if.check(o)) {
					// @ts-ignore
					return p.h(o, s);
				}
			}
			throw create$7("Unhandled pattern");
		};
	}
}

/**
 * @template [State=undefined]
 * @param {State} [state]
 * @return {PatternMatcher<State extends undefined ? undefined : Unwrap<ReadSchema<State>>>}
 */
const match = (state) => new PatternMatcher(/** @type {any} */ (state));

/**
 * Helper function to generate a (non-exhaustive) sample set from a gives schema.
 *
 * @type {<T>(o:T,gen:prng.PRNG)=>T}
 */
const _random = /** @type {any} */ (
	match(/** @type {Schema<prng.PRNG>} */ ($any))
		.if($$number, (_o, gen) => int53(gen, MIN_SAFE_INTEGER, MAX_SAFE_INTEGER))
		.if($$string, (_o, gen) => word(gen))
		.if($$boolean, (_o, gen) => bool(gen))
		.if($$bigint, (_o, gen) => BigInt(int53(gen, MIN_SAFE_INTEGER, MAX_SAFE_INTEGER)))
		.if($$union, (o, gen) => random(gen, oneOf(gen, o.shape)))
		.if($$object, (o, gen) => {
			/**
			 * @type {any}
			 */
			const res = {};
			for (const k in o.shape) {
				let prop = o.shape[k];
				if ($$optional.check(prop)) {
					if (bool(gen)) {
						continue;
					}
					prop = prop.shape;
				}
				res[k] = _random(prop, gen);
			}
			return res;
		})
		.if($$array, (o, gen) => {
			const arr = [];
			const n = int32(gen, 0, 42);
			for (let i = 0; i < n; i++) {
				arr.push(random(gen, o.shape));
			}
			return arr;
		})
		.if($$literal, (o, gen) => {
			return oneOf(gen, o.shape);
		})
		.if($$null, (o, gen) => {
			return null;
		})
		.if($$lambda, (o, gen) => {
			const res = random(gen, o.res);
			return () => res;
		})
		.if($$any, (o, gen) => random(gen, oneOf(gen, [$number, $string, $null, $undefined, $bigint, $boolean, $array($number), $record($union("a", "b", "c"), $number)])))
		.if($$record, (o, gen) => {
			/**
			 * @type {any}
			 */
			const res = {};
			const keysN = int53(gen, 0, 3);
			for (let i = 0; i < keysN; i++) {
				const key = random(gen, o.shape.keys);
				const val = random(gen, o.shape.values);
				res[key] = val;
			}
			return res;
		})
		.done()
);

/**
 * @template S
 * @param {prng.PRNG} gen
 * @param {S} schema
 * @return {Unwrap<ReadSchema<S>>}
 */
const random = (gen, schema) => /** @type {any} */ (_random($$3(schema), gen));

/* eslint-env browser */

/* c8 ignore start */
/**
 * @type {Document}
 */
const doc = /** @type {Document} */ (typeof document !== "undefined" ? document : {});

/**
 * @type {$.Schema<DocumentFragment>}
 */
$custom((el) => el.nodeType === DOCUMENT_FRAGMENT_NODE);

/** @type {DOMParser} */ (typeof DOMParser !== "undefined" ? new DOMParser() : null);

/**
 * @type {$.Schema<Element>}
 */
$custom((el) => el.nodeType === ELEMENT_NODE);

/**
 * @type {$.Schema<Text>}
 */
$custom((el) => el.nodeType === TEXT_NODE);

/**
 * @param {Map<string,string>} m
 * @return {string}
 */
const mapToStyleString = (m) => map$3(m, (value, key) => `${key}:${value};`).join("");

const ELEMENT_NODE = doc.ELEMENT_NODE;
const TEXT_NODE = doc.TEXT_NODE;
const DOCUMENT_NODE = doc.DOCUMENT_NODE;
const DOCUMENT_FRAGMENT_NODE = doc.DOCUMENT_FRAGMENT_NODE;

/**
 * @type {$.Schema<Node>}
 */
$custom((el) => el.nodeType === DOCUMENT_NODE);
/* c8 ignore stop */

/* global requestIdleCallback, requestAnimationFrame, cancelIdleCallback, cancelAnimationFrame */

/**
 * @typedef {Object} TimeoutObject
 * @property {function} TimeoutObject.destroy
 */

/**
 * @param {function(number):void} clearFunction
 */
const createTimeoutClass = (clearFunction) =>
	class TT {
		/**
		 * @param {number} timeoutId
		 */
		constructor(timeoutId) {
			this._ = timeoutId;
		}

		destroy() {
			clearFunction(this._);
		}
	};

const Timeout = createTimeoutClass(clearTimeout);

/**
 * @param {number} timeout
 * @param {function} callback
 * @return {TimeoutObject}
 */
const timeout = (timeout, callback) => new Timeout(setTimeout(callback, timeout));

/**
 * Utility module to work with EcmaScript Symbols.
 *
 * @module symbol
 */

/**
 * Return fresh symbol.
 */
const create$4 = Symbol;

const BOLD = create$4();
const UNBOLD = create$4();
const BLUE = create$4();
const GREY = create$4();
const GREEN = create$4();
const RED = create$4();
const PURPLE = create$4();
const ORANGE = create$4();
const UNCOLOR = create$4();

/* c8 ignore start */
/**
 * @param {Array<undefined|string|Symbol|Object|number|function():any>} args
 * @return {Array<string|object|number|undefined>}
 */
const computeNoColorLoggingArgs = (args) => {
	if (args.length === 1 && args[0]?.constructor === Function) {
		args = /** @type {Array<string|Symbol|Object|number>} */ (/** @type {[function]} */ (args)[0]());
	}
	const strBuilder = [];
	const logArgs = [];
	// try with formatting until we find something unsupported
	let i = 0;
	for (; i < args.length; i++) {
		const arg = args[i];
		if (arg === undefined) {
			break;
		} else if (arg.constructor === String || arg.constructor === Number) {
			strBuilder.push(arg);
		} else if (arg.constructor === Object) {
			break;
		}
	}
	if (i > 0) {
		// create logArgs with what we have so far
		logArgs.push(strBuilder.join(""));
	}
	// append the rest
	for (; i < args.length; i++) {
		const arg = args[i];
		if (!(arg instanceof Symbol)) {
			logArgs.push(arg);
		}
	}
	return logArgs;
};
/* c8 ignore stop */

/**
 * Isomorphic logging module with support for colors!
 *
 * @module logging
 */

/**
 * @type {Object<Symbol,pair.Pair<string,string>>}
 */
const _browserStyleMap = {
	[BOLD]: create$5("font-weight", "bold"),
	[UNBOLD]: create$5("font-weight", "normal"),
	[BLUE]: create$5("color", "blue"),
	[GREEN]: create$5("color", "green"),
	[GREY]: create$5("color", "grey"),
	[RED]: create$5("color", "red"),
	[PURPLE]: create$5("color", "purple"),
	[ORANGE]: create$5("color", "orange"), // not well supported in chrome when debugging node with inspector - TODO: deprecate
	[UNCOLOR]: create$5("color", "black"),
};

/**
 * @param {Array<string|Symbol|Object|number|function():any>} args
 * @return {Array<string|object|number>}
 */
/* c8 ignore start */
const computeBrowserLoggingArgs = (args) => {
	if (args.length === 1 && args[0]?.constructor === Function) {
		args = /** @type {Array<string|Symbol|Object|number>} */ (/** @type {[function]} */ (args)[0]());
	}
	const strBuilder = [];
	const styles = [];
	const currentStyle = create$9();
	/**
	 * @type {Array<string|Object|number>}
	 */
	let logArgs = [];
	// try with formatting until we find something unsupported
	let i = 0;
	for (; i < args.length; i++) {
		const arg = args[i];
		// @ts-ignore
		const style = _browserStyleMap[arg];
		if (style !== undefined) {
			currentStyle.set(style.left, style.right);
		} else {
			if (arg === undefined) {
				break;
			}
			if (arg.constructor === String || arg.constructor === Number) {
				const style = mapToStyleString(currentStyle);
				if (i > 0 || style.length > 0) {
					strBuilder.push("%c" + arg);
					styles.push(style);
				} else {
					strBuilder.push(arg);
				}
			} else {
				break;
			}
		}
	}
	if (i > 0) {
		// create logArgs with what we have so far
		logArgs = styles;
		logArgs.unshift(strBuilder.join(""));
	}
	// append the rest
	for (; i < args.length; i++) {
		const arg = args[i];
		if (!(arg instanceof Symbol)) {
			logArgs.push(arg);
		}
	}
	return logArgs;
};
/* c8 ignore stop */

/* c8 ignore start */
const computeLoggingArgs = supportsColor ? computeBrowserLoggingArgs : computeNoColorLoggingArgs;
/* c8 ignore stop */

/**
 * @param {Array<string|Symbol|Object|number>} args
 */
const print = (...args) => {
	console.log(...computeLoggingArgs(args));
	/* c8 ignore next */
	vconsoles.forEach((vc) => vc.print(args));
};

/* c8 ignore start */
/**
 * @param {Array<string|Symbol|Object|number>} args
 */
const warn = (...args) => {
	console.warn(...computeLoggingArgs(args));
	args.unshift(ORANGE);
	vconsoles.forEach((vc) => vc.print(args));
};

const vconsoles = create$8();

/**
 * Utility module to create and manipulate Iterators.
 *
 * @module iterator
 */

/**
 * @template T
 * @param {function():IteratorResult<T>} next
 * @return {IterableIterator<T>}
 */
const createIterator = (next) => ({
	/**
	 * @return {IterableIterator<T>}
	 */
	[Symbol.iterator]() {
		return this;
	},
	// @ts-ignore
	next,
});

/**
 * @template T
 * @param {Iterator<T>} iterator
 * @param {function(T):boolean} filter
 */
const iteratorFilter = (iterator, filter) =>
	createIterator(() => {
		let res;
		do {
			res = iterator.next();
		} while (!res.done && !filter(res.value));
		return res;
	});

/**
 * @template T,M
 * @param {Iterator<T>} iterator
 * @param {function(T):M} fmap
 */
const iteratorMap = (iterator, fmap) =>
	createIterator(() => {
		const { done, value } = iterator.next();
		return { done, value: done ? undefined : fmap(value) };
	});

class DeleteItem {
	/**
	 * @param {number} clock
	 * @param {number} len
	 */
	constructor(clock, len) {
		/**
		 * @type {number}
		 */
		this.clock = clock;
		/**
		 * @type {number}
		 */
		this.len = len;
	}
}

/**
 * We no longer maintain a DeleteStore. DeleteSet is a temporary object that is created when needed.
 * - When created in a transaction, it must only be accessed after sorting, and merging
 *   - This DeleteSet is send to other clients
 * - We do not create a DeleteSet when we send a sync message. The DeleteSet message is created directly from StructStore
 * - We read a DeleteSet as part of a sync/update message. In this case the DeleteSet is already sorted and merged.
 */
class DeleteSet {
	constructor() {
		/**
		 * @type {Map<number,Array<DeleteItem>>}
		 */
		this.clients = new Map();
	}
}

/**
 * Iterate over all structs that the DeleteSet gc's.
 *
 * @param {Transaction} transaction
 * @param {DeleteSet} ds
 * @param {function(GC|Item):void} f
 *
 * @function
 */
const iterateDeletedStructs = (transaction, ds, f) =>
	ds.clients.forEach((deletes, clientid) => {
		const structs = /** @type {Array<GC|Item>} */ (transaction.doc.store.clients.get(clientid));
		if (structs != null) {
			const lastStruct = structs[structs.length - 1];
			const clockState = lastStruct.id.clock + lastStruct.length;
			for (let i = 0, del = deletes[i]; i < deletes.length && del.clock < clockState; del = deletes[++i]) {
				iterateStructs(transaction, structs, del.clock, del.len, f);
			}
		}
	});

/**
 * @param {Array<DeleteItem>} dis
 * @param {number} clock
 * @return {number|null}
 *
 * @private
 * @function
 */
const findIndexDS = (dis, clock) => {
	let left = 0;
	let right = dis.length - 1;
	while (left <= right) {
		const midindex = floor$1((left + right) / 2);
		const mid = dis[midindex];
		const midclock = mid.clock;
		if (midclock <= clock) {
			if (clock < midclock + mid.len) {
				return midindex;
			}
			left = midindex + 1;
		} else {
			right = midindex - 1;
		}
	}
	return null;
};

/**
 * @param {DeleteSet} ds
 * @param {ID} id
 * @return {boolean}
 *
 * @private
 * @function
 */
const isDeleted = (ds, id) => {
	const dis = ds.clients.get(id.client);
	return dis !== undefined && findIndexDS(dis, id.clock) !== null;
};

/**
 * @param {DeleteSet} ds
 *
 * @private
 * @function
 */
const sortAndMergeDeleteSet = (ds) => {
	ds.clients.forEach((dels) => {
		dels.sort((a, b) => a.clock - b.clock);
		// merge items without filtering or splicing the array
		// i is the current pointer
		// j refers to the current insert position for the pointed item
		// try to merge dels[i] into dels[j-1] or set dels[j]=dels[i]
		let i, j;
		for (i = 1, j = 1; i < dels.length; i++) {
			const left = dels[j - 1];
			const right = dels[i];
			if (left.clock + left.len >= right.clock) {
				dels[j - 1] = new DeleteItem(left.clock, max$1(left.len, right.clock + right.len - left.clock));
			} else {
				if (j < i) {
					dels[j] = right;
				}
				j++;
			}
		}
		dels.length = j;
	});
};

/**
 * @param {Array<DeleteSet>} dss
 * @return {DeleteSet} A fresh DeleteSet
 */
const mergeDeleteSets = (dss) => {
	const merged = new DeleteSet();
	for (let dssI = 0; dssI < dss.length; dssI++) {
		dss[dssI].clients.forEach((delsLeft, client) => {
			if (!merged.clients.has(client)) {
				// Write all missing keys from current ds and all following.
				// If merged already contains `client` current ds has already been added.
				/**
				 * @type {Array<DeleteItem>}
				 */
				const dels = delsLeft.slice();
				for (let i = dssI + 1; i < dss.length; i++) {
					appendTo(dels, dss[i].clients.get(client) || []);
				}
				merged.clients.set(client, dels);
			}
		});
	}
	sortAndMergeDeleteSet(merged);
	return merged;
};

/**
 * @param {DeleteSet} ds
 * @param {number} client
 * @param {number} clock
 * @param {number} length
 *
 * @private
 * @function
 */
const addToDeleteSet = (ds, client, clock, length) => {
	setIfUndefined(ds.clients, client, () => /** @type {Array<DeleteItem>} */ ([])).push(new DeleteItem(clock, length));
};

const createDeleteSet = () => new DeleteSet();

/**
 * @param {StructStore} ss
 * @return {DeleteSet} Merged and sorted DeleteSet
 *
 * @private
 * @function
 */
const createDeleteSetFromStructStore = (ss) => {
	const ds = createDeleteSet();
	ss.clients.forEach((structs, client) => {
		/**
		 * @type {Array<DeleteItem>}
		 */
		const dsitems = [];
		for (let i = 0; i < structs.length; i++) {
			const struct = structs[i];
			if (struct.deleted) {
				const clock = struct.id.clock;
				let len = struct.length;
				if (i + 1 < structs.length) {
					for (let next = structs[i + 1]; i + 1 < structs.length && next.deleted; next = structs[++i + 1]) {
						len += next.length;
					}
				}
				dsitems.push(new DeleteItem(clock, len));
			}
		}
		if (dsitems.length > 0) {
			ds.clients.set(client, dsitems);
		}
	});
	return ds;
};

/**
 * @param {DSEncoderV1 | DSEncoderV2} encoder
 * @param {DeleteSet} ds
 *
 * @private
 * @function
 */
const writeDeleteSet = (encoder, ds) => {
	writeVarUint(encoder.restEncoder, ds.clients.size);

	// Ensure that the delete set is written in a deterministic order
	from(ds.clients.entries())
		.sort((a, b) => b[0] - a[0])
		.forEach(([client, dsitems]) => {
			encoder.resetDsCurVal();
			writeVarUint(encoder.restEncoder, client);
			const len = dsitems.length;
			writeVarUint(encoder.restEncoder, len);
			for (let i = 0; i < len; i++) {
				const item = dsitems[i];
				encoder.writeDsClock(item.clock);
				encoder.writeDsLen(item.len);
			}
		});
};

/**
 * @param {DSDecoderV1 | DSDecoderV2} decoder
 * @return {DeleteSet}
 *
 * @private
 * @function
 */
const readDeleteSet = (decoder) => {
	const ds = new DeleteSet();
	const numClients = readVarUint(decoder.restDecoder);
	for (let i = 0; i < numClients; i++) {
		decoder.resetDsCurVal();
		const client = readVarUint(decoder.restDecoder);
		const numberOfDeletes = readVarUint(decoder.restDecoder);
		if (numberOfDeletes > 0) {
			const dsField = setIfUndefined(ds.clients, client, () => /** @type {Array<DeleteItem>} */ ([]));
			for (let i = 0; i < numberOfDeletes; i++) {
				dsField.push(new DeleteItem(decoder.readDsClock(), decoder.readDsLen()));
			}
		}
	}
	return ds;
};

/**
 * @todo YDecoder also contains references to String and other Decoders. Would make sense to exchange YDecoder.toUint8Array for YDecoder.DsToUint8Array()..
 */

/**
 * @param {DSDecoderV1 | DSDecoderV2} decoder
 * @param {Transaction} transaction
 * @param {StructStore} store
 * @return {Uint8Array|null} Returns a v2 update containing all deletes that couldn't be applied yet; or null if all deletes were applied successfully.
 *
 * @private
 * @function
 */
const readAndApplyDeleteSet = (decoder, transaction, store) => {
	const unappliedDS = new DeleteSet();
	const numClients = readVarUint(decoder.restDecoder);
	for (let i = 0; i < numClients; i++) {
		decoder.resetDsCurVal();
		const client = readVarUint(decoder.restDecoder);
		const numberOfDeletes = readVarUint(decoder.restDecoder);
		const structs = store.clients.get(client) || [];
		const state = getState(store, client);
		for (let i = 0; i < numberOfDeletes; i++) {
			const clock = decoder.readDsClock();
			const clockEnd = clock + decoder.readDsLen();
			if (clock < state) {
				if (state < clockEnd) {
					addToDeleteSet(unappliedDS, client, state, clockEnd - state);
				}
				let index = findIndexSS(structs, clock);
				/**
				 * We can ignore the case of GC and Delete structs, because we are going to skip them
				 * @type {Item}
				 */
				// @ts-ignore
				let struct = structs[index];
				// split the first item if necessary
				if (!struct.deleted && struct.id.clock < clock) {
					structs.splice(index + 1, 0, splitItem(transaction, struct, clock - struct.id.clock));
					index++; // increase we now want to use the next struct
				}
				while (index < structs.length) {
					// @ts-ignore
					struct = structs[index++];
					if (struct.id.clock < clockEnd) {
						if (!struct.deleted) {
							if (clockEnd < struct.id.clock + struct.length) {
								structs.splice(index, 0, splitItem(transaction, struct, clockEnd - struct.id.clock));
							}
							struct.delete(transaction);
						}
					} else {
						break;
					}
				}
			} else {
				addToDeleteSet(unappliedDS, client, clock, clockEnd - clock);
			}
		}
	}
	if (unappliedDS.clients.size > 0) {
		const ds = new UpdateEncoderV2();
		writeVarUint(ds.restEncoder, 0); // encode 0 structs
		writeDeleteSet(ds, unappliedDS);
		return ds.toUint8Array();
	}
	return null;
};

/**
 * @module Y
 */

const generateNewClientId = uint32;

/**
 * @typedef {Object} DocOpts
 * @property {boolean} [DocOpts.gc=true] Disable garbage collection (default: gc=true)
 * @property {function(Item):boolean} [DocOpts.gcFilter] Will be called before an Item is garbage collected. Return false to keep the Item.
 * @property {string} [DocOpts.guid] Define a globally unique identifier for this document
 * @property {string | null} [DocOpts.collectionid] Associate this document with a collection. This only plays a role if your provider has a concept of collection.
 * @property {any} [DocOpts.meta] Any kind of meta information you want to associate with this document. If this is a subdocument, remote peers will store the meta information as well.
 * @property {boolean} [DocOpts.autoLoad] If a subdocument, automatically load document. If this is a subdocument, remote peers will load the document as well automatically.
 * @property {boolean} [DocOpts.shouldLoad] Whether the document should be synced by the provider now. This is toggled to true when you call ydoc.load()
 */

/**
 * @typedef {Object} DocEvents
 * @property {function(Doc):void} DocEvents.destroy
 * @property {function(Doc):void} DocEvents.load
 * @property {function(boolean, Doc):void} DocEvents.sync
 * @property {function(Uint8Array, any, Doc, Transaction):void} DocEvents.update
 * @property {function(Uint8Array, any, Doc, Transaction):void} DocEvents.updateV2
 * @property {function(Doc):void} DocEvents.beforeAllTransactions
 * @property {function(Transaction, Doc):void} DocEvents.beforeTransaction
 * @property {function(Transaction, Doc):void} DocEvents.beforeObserverCalls
 * @property {function(Transaction, Doc):void} DocEvents.afterTransaction
 * @property {function(Transaction, Doc):void} DocEvents.afterTransactionCleanup
 * @property {function(Doc, Array<Transaction>):void} DocEvents.afterAllTransactions
 * @property {function({ loaded: Set<Doc>, added: Set<Doc>, removed: Set<Doc> }, Doc, Transaction):void} DocEvents.subdocs
 */

/**
 * A Yjs instance handles the state of shared data.
 * @extends ObservableV2<DocEvents>
 */
class Doc extends ObservableV2 {
	/**
	 * @param {DocOpts} opts configuration
	 */
	constructor({ guid = uuidv4(), collectionid = null, gc = true, gcFilter = () => true, meta = null, autoLoad = false, shouldLoad = true } = {}) {
		super();
		this.gc = gc;
		this.gcFilter = gcFilter;
		this.clientID = generateNewClientId();
		this.guid = guid;
		this.collectionid = collectionid;
		/**
		 * @type {Map<string, AbstractType<YEvent<any>>>}
		 */
		this.share = new Map();
		this.store = new StructStore();
		/**
		 * @type {Transaction | null}
		 */
		this._transaction = null;
		/**
		 * @type {Array<Transaction>}
		 */
		this._transactionCleanups = [];
		/**
		 * @type {Set<Doc>}
		 */
		this.subdocs = new Set();
		/**
		 * If this document is a subdocument - a document integrated into another document - then _item is defined.
		 * @type {Item?}
		 */
		this._item = null;
		this.shouldLoad = shouldLoad;
		this.autoLoad = autoLoad;
		this.meta = meta;
		/**
		 * This is set to true when the persistence provider loaded the document from the database or when the `sync` event fires.
		 * Note that not all providers implement this feature. Provider authors are encouraged to fire the `load` event when the doc content is loaded from the database.
		 *
		 * @type {boolean}
		 */
		this.isLoaded = false;
		/**
		 * This is set to true when the connection provider has successfully synced with a backend.
		 * Note that when using peer-to-peer providers this event may not provide very useful.
		 * Also note that not all providers implement this feature. Provider authors are encouraged to fire
		 * the `sync` event when the doc has been synced (with `true` as a parameter) or if connection is
		 * lost (with false as a parameter).
		 */
		this.isSynced = false;
		this.isDestroyed = false;
		/**
		 * Promise that resolves once the document has been loaded from a persistence provider.
		 */
		this.whenLoaded = create$6((resolve) => {
			this.on("load", () => {
				this.isLoaded = true;
				resolve(this);
			});
		});
		const provideSyncedPromise = () =>
			create$6((resolve) => {
				/**
				 * @param {boolean} isSynced
				 */
				const eventHandler = (isSynced) => {
					if (isSynced === undefined || isSynced === true) {
						this.off("sync", eventHandler);
						resolve();
					}
				};
				this.on("sync", eventHandler);
			});
		this.on("sync", (isSynced) => {
			if (isSynced === false && this.isSynced) {
				this.whenSynced = provideSyncedPromise();
			}
			this.isSynced = isSynced === undefined || isSynced === true;
			if (this.isSynced && !this.isLoaded) {
				this.emit("load", [this]);
			}
		});
		/**
		 * Promise that resolves once the document has been synced with a backend.
		 * This promise is recreated when the connection is lost.
		 * Note the documentation about the `isSynced` property.
		 */
		this.whenSynced = provideSyncedPromise();
	}

	/**
	 * Notify the parent document that you request to load data into this subdocument (if it is a subdocument).
	 *
	 * `load()` might be used in the future to request any provider to load the most current data.
	 *
	 * It is safe to call `load()` multiple times.
	 */
	load() {
		const item = this._item;
		if (item !== null && !this.shouldLoad) {
			transact(
				/** @type {any} */ (item.parent).doc,
				(transaction) => {
					transaction.subdocsLoaded.add(this);
				},
				null,
				true,
			);
		}
		this.shouldLoad = true;
	}

	getSubdocs() {
		return this.subdocs;
	}

	getSubdocGuids() {
		return new Set(from(this.subdocs).map((doc) => doc.guid));
	}

	/**
	 * Changes that happen inside of a transaction are bundled. This means that
	 * the observer fires _after_ the transaction is finished and that all changes
	 * that happened inside of the transaction are sent as one message to the
	 * other peers.
	 *
	 * @template T
	 * @param {function(Transaction):T} f The function that should be executed as a transaction
	 * @param {any} [origin] Origin of who started the transaction. Will be stored on transaction.origin
	 * @return T
	 *
	 * @public
	 */
	transact(f, origin = null) {
		return transact(this, f, origin);
	}

	/**
	 * Define a shared data type.
	 *
	 * Multiple calls of `ydoc.get(name, TypeConstructor)` yield the same result
	 * and do not overwrite each other. I.e.
	 * `ydoc.get(name, Y.Array) === ydoc.get(name, Y.Array)`
	 *
	 * After this method is called, the type is also available on `ydoc.share.get(name)`.
	 *
	 * *Best Practices:*
	 * Define all types right after the Y.Doc instance is created and store them in a separate object.
	 * Also use the typed methods `getText(name)`, `getArray(name)`, ..
	 *
	 * @template {typeof AbstractType<any>} Type
	 * @example
	 *   const ydoc = new Y.Doc(..)
	 *   const appState = {
	 *     document: ydoc.getText('document')
	 *     comments: ydoc.getArray('comments')
	 *   }
	 *
	 * @param {string} name
	 * @param {Type} TypeConstructor The constructor of the type definition. E.g. Y.Text, Y.Array, Y.Map, ...
	 * @return {InstanceType<Type>} The created type. Constructed with TypeConstructor
	 *
	 * @public
	 */
	get(name, TypeConstructor = /** @type {any} */ (AbstractType)) {
		const type = setIfUndefined(this.share, name, () => {
			// @ts-ignore
			const t = new TypeConstructor();
			t._integrate(this, null);
			return t;
		});
		const Constr = type.constructor;
		if (TypeConstructor !== AbstractType && Constr !== TypeConstructor) {
			if (Constr === AbstractType) {
				// @ts-ignore
				const t = new TypeConstructor();
				t._map = type._map;
				type._map.forEach(
					/** @param {Item?} n */ (n) => {
						for (; n !== null; n = n.left) {
							// @ts-ignore
							n.parent = t;
						}
					},
				);
				t._start = type._start;
				for (let n = t._start; n !== null; n = n.right) {
					n.parent = t;
				}
				t._length = type._length;
				this.share.set(name, t);
				t._integrate(this, null);
				return /** @type {InstanceType<Type>} */ (t);
			} else {
				throw new Error(`Type with the name ${name} has already been defined with a different constructor`);
			}
		}
		return /** @type {InstanceType<Type>} */ (type);
	}

	/**
	 * @template T
	 * @param {string} [name]
	 * @return {YArray<T>}
	 *
	 * @public
	 */
	getArray(name = "") {
		return /** @type {YArray<T>} */ (this.get(name, YArray));
	}

	/**
	 * @param {string} [name]
	 * @return {YText}
	 *
	 * @public
	 */
	getText(name = "") {
		return this.get(name, YText);
	}

	/**
	 * @template T
	 * @param {string} [name]
	 * @return {YMap<T>}
	 *
	 * @public
	 */
	getMap(name = "") {
		return /** @type {YMap<T>} */ (this.get(name, YMap));
	}

	/**
	 * @param {string} [name]
	 * @return {YXmlElement}
	 *
	 * @public
	 */
	getXmlElement(name = "") {
		return /** @type {YXmlElement<{[key:string]:string}>} */ (this.get(name, YXmlElement));
	}

	/**
	 * @param {string} [name]
	 * @return {YXmlFragment}
	 *
	 * @public
	 */
	getXmlFragment(name = "") {
		return this.get(name, YXmlFragment);
	}

	/**
	 * Converts the entire document into a js object, recursively traversing each yjs type
	 * Doesn't log types that have not been defined (using ydoc.getType(..)).
	 *
	 * @deprecated Do not use this method and rather call toJSON directly on the shared types.
	 *
	 * @return {Object<string, any>}
	 */
	toJSON() {
		/**
		 * @type {Object<string, any>}
		 */
		const doc = {};

		this.share.forEach((value, key) => {
			doc[key] = value.toJSON();
		});

		return doc;
	}

	/**
	 * Emit `destroy` event and unregister all event handlers.
	 */
	destroy() {
		this.isDestroyed = true;
		from(this.subdocs).forEach((subdoc) => subdoc.destroy());
		const item = this._item;
		if (item !== null) {
			this._item = null;
			const content = /** @type {ContentDoc} */ (item.content);
			content.doc = new Doc({ guid: this.guid, ...content.opts, shouldLoad: false });
			content.doc._item = item;
			transact(
				/** @type {any} */ (item).parent.doc,
				(transaction) => {
					const doc = content.doc;
					if (!item.deleted) {
						transaction.subdocsAdded.add(doc);
					}
					transaction.subdocsRemoved.add(this);
				},
				null,
				true,
			);
		}
		// @ts-ignore
		this.emit("destroyed", [true]); // DEPRECATED!
		this.emit("destroy", [this]);
		super.destroy();
	}
}

class DSDecoderV1 {
	/**
	 * @param {decoding.Decoder} decoder
	 */
	constructor(decoder) {
		this.restDecoder = decoder;
	}

	resetDsCurVal() {
		// nop
	}

	/**
	 * @return {number}
	 */
	readDsClock() {
		return readVarUint(this.restDecoder);
	}

	/**
	 * @return {number}
	 */
	readDsLen() {
		return readVarUint(this.restDecoder);
	}
}

class UpdateDecoderV1 extends DSDecoderV1 {
	/**
	 * @return {ID}
	 */
	readLeftID() {
		return createID(readVarUint(this.restDecoder), readVarUint(this.restDecoder));
	}

	/**
	 * @return {ID}
	 */
	readRightID() {
		return createID(readVarUint(this.restDecoder), readVarUint(this.restDecoder));
	}

	/**
	 * Read the next client id.
	 * Use this in favor of readID whenever possible to reduce the number of objects created.
	 */
	readClient() {
		return readVarUint(this.restDecoder);
	}

	/**
	 * @return {number} info An unsigned 8-bit integer
	 */
	readInfo() {
		return readUint8(this.restDecoder);
	}

	/**
	 * @return {string}
	 */
	readString() {
		return readVarString(this.restDecoder);
	}

	/**
	 * @return {boolean} isKey
	 */
	readParentInfo() {
		return readVarUint(this.restDecoder) === 1;
	}

	/**
	 * @return {number} info An unsigned 8-bit integer
	 */
	readTypeRef() {
		return readVarUint(this.restDecoder);
	}

	/**
	 * Write len of a struct - well suited for Opt RLE encoder.
	 *
	 * @return {number} len
	 */
	readLen() {
		return readVarUint(this.restDecoder);
	}

	/**
	 * @return {any}
	 */
	readAny() {
		return readAny(this.restDecoder);
	}

	/**
	 * @return {Uint8Array}
	 */
	readBuf() {
		return copyUint8Array(readVarUint8Array(this.restDecoder));
	}

	/**
	 * Legacy implementation uses JSON parse. We use any-decoding in v2.
	 *
	 * @return {any}
	 */
	readJSON() {
		return JSON.parse(readVarString(this.restDecoder));
	}

	/**
	 * @return {string}
	 */
	readKey() {
		return readVarString(this.restDecoder);
	}
}

class DSDecoderV2 {
	/**
	 * @param {decoding.Decoder} decoder
	 */
	constructor(decoder) {
		/**
		 * @private
		 */
		this.dsCurrVal = 0;
		this.restDecoder = decoder;
	}

	resetDsCurVal() {
		this.dsCurrVal = 0;
	}

	/**
	 * @return {number}
	 */
	readDsClock() {
		this.dsCurrVal += readVarUint(this.restDecoder);
		return this.dsCurrVal;
	}

	/**
	 * @return {number}
	 */
	readDsLen() {
		const diff = readVarUint(this.restDecoder) + 1;
		this.dsCurrVal += diff;
		return diff;
	}
}

class UpdateDecoderV2 extends DSDecoderV2 {
	/**
	 * @param {decoding.Decoder} decoder
	 */
	constructor(decoder) {
		super(decoder);
		/**
		 * List of cached keys. If the keys[id] does not exist, we read a new key
		 * from stringEncoder and push it to keys.
		 *
		 * @type {Array<string>}
		 */
		this.keys = [];
		readVarUint(decoder); // read feature flag - currently unused
		this.keyClockDecoder = new IntDiffOptRleDecoder(readVarUint8Array(decoder));
		this.clientDecoder = new UintOptRleDecoder(readVarUint8Array(decoder));
		this.leftClockDecoder = new IntDiffOptRleDecoder(readVarUint8Array(decoder));
		this.rightClockDecoder = new IntDiffOptRleDecoder(readVarUint8Array(decoder));
		this.infoDecoder = new RleDecoder(readVarUint8Array(decoder), readUint8);
		this.stringDecoder = new StringDecoder(readVarUint8Array(decoder));
		this.parentInfoDecoder = new RleDecoder(readVarUint8Array(decoder), readUint8);
		this.typeRefDecoder = new UintOptRleDecoder(readVarUint8Array(decoder));
		this.lenDecoder = new UintOptRleDecoder(readVarUint8Array(decoder));
	}

	/**
	 * @return {ID}
	 */
	readLeftID() {
		return new ID(this.clientDecoder.read(), this.leftClockDecoder.read());
	}

	/**
	 * @return {ID}
	 */
	readRightID() {
		return new ID(this.clientDecoder.read(), this.rightClockDecoder.read());
	}

	/**
	 * Read the next client id.
	 * Use this in favor of readID whenever possible to reduce the number of objects created.
	 */
	readClient() {
		return this.clientDecoder.read();
	}

	/**
	 * @return {number} info An unsigned 8-bit integer
	 */
	readInfo() {
		return /** @type {number} */ (this.infoDecoder.read());
	}

	/**
	 * @return {string}
	 */
	readString() {
		return this.stringDecoder.read();
	}

	/**
	 * @return {boolean}
	 */
	readParentInfo() {
		return this.parentInfoDecoder.read() === 1;
	}

	/**
	 * @return {number} An unsigned 8-bit integer
	 */
	readTypeRef() {
		return this.typeRefDecoder.read();
	}

	/**
	 * Write len of a struct - well suited for Opt RLE encoder.
	 *
	 * @return {number}
	 */
	readLen() {
		return this.lenDecoder.read();
	}

	/**
	 * @return {any}
	 */
	readAny() {
		return readAny(this.restDecoder);
	}

	/**
	 * @return {Uint8Array}
	 */
	readBuf() {
		return readVarUint8Array(this.restDecoder);
	}

	/**
	 * This is mainly here for legacy purposes.
	 *
	 * Initial we incoded objects using JSON. Now we use the much faster lib0/any-encoder. This method mainly exists for legacy purposes for the v1 encoder.
	 *
	 * @return {any}
	 */
	readJSON() {
		return readAny(this.restDecoder);
	}

	/**
	 * @return {string}
	 */
	readKey() {
		const keyClock = this.keyClockDecoder.read();
		if (keyClock < this.keys.length) {
			return this.keys[keyClock];
		} else {
			const key = this.stringDecoder.read();
			this.keys.push(key);
			return key;
		}
	}
}

class DSEncoderV1 {
	constructor() {
		this.restEncoder = createEncoder();
	}

	toUint8Array() {
		return toUint8Array(this.restEncoder);
	}

	resetDsCurVal() {
		// nop
	}

	/**
	 * @param {number} clock
	 */
	writeDsClock(clock) {
		writeVarUint(this.restEncoder, clock);
	}

	/**
	 * @param {number} len
	 */
	writeDsLen(len) {
		writeVarUint(this.restEncoder, len);
	}
}

class UpdateEncoderV1 extends DSEncoderV1 {
	/**
	 * @param {ID} id
	 */
	writeLeftID(id) {
		writeVarUint(this.restEncoder, id.client);
		writeVarUint(this.restEncoder, id.clock);
	}

	/**
	 * @param {ID} id
	 */
	writeRightID(id) {
		writeVarUint(this.restEncoder, id.client);
		writeVarUint(this.restEncoder, id.clock);
	}

	/**
	 * Use writeClient and writeClock instead of writeID if possible.
	 * @param {number} client
	 */
	writeClient(client) {
		writeVarUint(this.restEncoder, client);
	}

	/**
	 * @param {number} info An unsigned 8-bit integer
	 */
	writeInfo(info) {
		writeUint8(this.restEncoder, info);
	}

	/**
	 * @param {string} s
	 */
	writeString(s) {
		writeVarString(this.restEncoder, s);
	}

	/**
	 * @param {boolean} isYKey
	 */
	writeParentInfo(isYKey) {
		writeVarUint(this.restEncoder, isYKey ? 1 : 0);
	}

	/**
	 * @param {number} info An unsigned 8-bit integer
	 */
	writeTypeRef(info) {
		writeVarUint(this.restEncoder, info);
	}

	/**
	 * Write len of a struct - well suited for Opt RLE encoder.
	 *
	 * @param {number} len
	 */
	writeLen(len) {
		writeVarUint(this.restEncoder, len);
	}

	/**
	 * @param {any} any
	 */
	writeAny(any) {
		writeAny(this.restEncoder, any);
	}

	/**
	 * @param {Uint8Array} buf
	 */
	writeBuf(buf) {
		writeVarUint8Array(this.restEncoder, buf);
	}

	/**
	 * @param {any} embed
	 */
	writeJSON(embed) {
		writeVarString(this.restEncoder, JSON.stringify(embed));
	}

	/**
	 * @param {string} key
	 */
	writeKey(key) {
		writeVarString(this.restEncoder, key);
	}
}

class DSEncoderV2 {
	constructor() {
		this.restEncoder = createEncoder(); // encodes all the rest / non-optimized
		this.dsCurrVal = 0;
	}

	toUint8Array() {
		return toUint8Array(this.restEncoder);
	}

	resetDsCurVal() {
		this.dsCurrVal = 0;
	}

	/**
	 * @param {number} clock
	 */
	writeDsClock(clock) {
		const diff = clock - this.dsCurrVal;
		this.dsCurrVal = clock;
		writeVarUint(this.restEncoder, diff);
	}

	/**
	 * @param {number} len
	 */
	writeDsLen(len) {
		if (len === 0) {
			unexpectedCase();
		}
		writeVarUint(this.restEncoder, len - 1);
		this.dsCurrVal += len;
	}
}

class UpdateEncoderV2 extends DSEncoderV2 {
	constructor() {
		super();
		/**
		 * @type {Map<string,number>}
		 */
		this.keyMap = new Map();
		/**
		 * Refers to the next unique key-identifier to me used.
		 * See writeKey method for more information.
		 *
		 * @type {number}
		 */
		this.keyClock = 0;
		this.keyClockEncoder = new IntDiffOptRleEncoder();
		this.clientEncoder = new UintOptRleEncoder();
		this.leftClockEncoder = new IntDiffOptRleEncoder();
		this.rightClockEncoder = new IntDiffOptRleEncoder();
		this.infoEncoder = new RleEncoder(writeUint8);
		this.stringEncoder = new StringEncoder();
		this.parentInfoEncoder = new RleEncoder(writeUint8);
		this.typeRefEncoder = new UintOptRleEncoder();
		this.lenEncoder = new UintOptRleEncoder();
	}

	toUint8Array() {
		const encoder = createEncoder();
		writeVarUint(encoder, 0); // this is a feature flag that we might use in the future
		writeVarUint8Array(encoder, this.keyClockEncoder.toUint8Array());
		writeVarUint8Array(encoder, this.clientEncoder.toUint8Array());
		writeVarUint8Array(encoder, this.leftClockEncoder.toUint8Array());
		writeVarUint8Array(encoder, this.rightClockEncoder.toUint8Array());
		writeVarUint8Array(encoder, toUint8Array(this.infoEncoder));
		writeVarUint8Array(encoder, this.stringEncoder.toUint8Array());
		writeVarUint8Array(encoder, toUint8Array(this.parentInfoEncoder));
		writeVarUint8Array(encoder, this.typeRefEncoder.toUint8Array());
		writeVarUint8Array(encoder, this.lenEncoder.toUint8Array());
		// @note The rest encoder is appended! (note the missing var)
		writeUint8Array(encoder, toUint8Array(this.restEncoder));
		return toUint8Array(encoder);
	}

	/**
	 * @param {ID} id
	 */
	writeLeftID(id) {
		this.clientEncoder.write(id.client);
		this.leftClockEncoder.write(id.clock);
	}

	/**
	 * @param {ID} id
	 */
	writeRightID(id) {
		this.clientEncoder.write(id.client);
		this.rightClockEncoder.write(id.clock);
	}

	/**
	 * @param {number} client
	 */
	writeClient(client) {
		this.clientEncoder.write(client);
	}

	/**
	 * @param {number} info An unsigned 8-bit integer
	 */
	writeInfo(info) {
		this.infoEncoder.write(info);
	}

	/**
	 * @param {string} s
	 */
	writeString(s) {
		this.stringEncoder.write(s);
	}

	/**
	 * @param {boolean} isYKey
	 */
	writeParentInfo(isYKey) {
		this.parentInfoEncoder.write(isYKey ? 1 : 0);
	}

	/**
	 * @param {number} info An unsigned 8-bit integer
	 */
	writeTypeRef(info) {
		this.typeRefEncoder.write(info);
	}

	/**
	 * Write len of a struct - well suited for Opt RLE encoder.
	 *
	 * @param {number} len
	 */
	writeLen(len) {
		this.lenEncoder.write(len);
	}

	/**
	 * @param {any} any
	 */
	writeAny(any) {
		writeAny(this.restEncoder, any);
	}

	/**
	 * @param {Uint8Array} buf
	 */
	writeBuf(buf) {
		writeVarUint8Array(this.restEncoder, buf);
	}

	/**
	 * This is mainly here for legacy purposes.
	 *
	 * Initial we incoded objects using JSON. Now we use the much faster lib0/any-encoder. This method mainly exists for legacy purposes for the v1 encoder.
	 *
	 * @param {any} embed
	 */
	writeJSON(embed) {
		writeAny(this.restEncoder, embed);
	}

	/**
	 * Property keys are often reused. For example, in y-prosemirror the key `bold` might
	 * occur very often. For a 3d application, the key `position` might occur very often.
	 *
	 * We cache these keys in a Map and refer to them via a unique number.
	 *
	 * @param {string} key
	 */
	writeKey(key) {
		const clock = this.keyMap.get(key);
		if (clock === undefined) {
			/**
			 * @todo uncomment to introduce this feature finally
			 *
			 * Background. The ContentFormat object was always encoded using writeKey, but the decoder used to use readString.
			 * Furthermore, I forgot to set the keyclock. So everything was working fine.
			 *
			 * However, this feature here is basically useless as it is not being used (it actually only consumes extra memory).
			 *
			 * I don't know yet how to reintroduce this feature..
			 *
			 * Older clients won't be able to read updates when we reintroduce this feature. So this should probably be done using a flag.
			 *
			 */
			// this.keyMap.set(key, this.keyClock)
			this.keyClockEncoder.write(this.keyClock++);
			this.stringEncoder.write(key);
		} else {
			this.keyClockEncoder.write(clock);
		}
	}
}

/**
 * @module encoding
 */
/*
 * We use the first five bits in the info flag for determining the type of the struct.
 *
 * 0: GC
 * 1: Item with Deleted content
 * 2: Item with JSON content
 * 3: Item with Binary content
 * 4: Item with String content
 * 5: Item with Embed content (for richtext content)
 * 6: Item with Format content (a formatting marker for richtext content)
 * 7: Item with Type
 */

/**
 * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
 * @param {Array<GC|Item>} structs All structs by `client`
 * @param {number} client
 * @param {number} clock write structs starting with `ID(client,clock)`
 *
 * @function
 */
const writeStructs = (encoder, structs, client, clock) => {
	// write first id
	clock = max$1(clock, structs[0].id.clock); // make sure the first id exists
	const startNewStructs = findIndexSS(structs, clock);
	// write # encoded structs
	writeVarUint(encoder.restEncoder, structs.length - startNewStructs);
	encoder.writeClient(client);
	writeVarUint(encoder.restEncoder, clock);
	const firstStruct = structs[startNewStructs];
	// write first struct with an offset
	firstStruct.write(encoder, clock - firstStruct.id.clock);
	for (let i = startNewStructs + 1; i < structs.length; i++) {
		structs[i].write(encoder, 0);
	}
};

/**
 * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
 * @param {StructStore} store
 * @param {Map<number,number>} _sm
 *
 * @private
 * @function
 */
const writeClientsStructs = (encoder, store, _sm) => {
	// we filter all valid _sm entries into sm
	const sm = new Map();
	_sm.forEach((clock, client) => {
		// only write if new structs are available
		if (getState(store, client) > clock) {
			sm.set(client, clock);
		}
	});
	getStateVector(store).forEach((_clock, client) => {
		if (!_sm.has(client)) {
			sm.set(client, 0);
		}
	});
	// write # states that were updated
	writeVarUint(encoder.restEncoder, sm.size);
	// Write items with higher client ids first
	// This heavily improves the conflict algorithm.
	from(sm.entries())
		.sort((a, b) => b[0] - a[0])
		.forEach(([client, clock]) => {
			writeStructs(encoder, /** @type {Array<GC|Item>} */ (store.clients.get(client)), client, clock);
		});
};

/**
 * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder The decoder object to read data from.
 * @param {Doc} doc
 * @return {Map<number, { i: number, refs: Array<Item | GC> }>}
 *
 * @private
 * @function
 */
const readClientsStructRefs = (decoder, doc) => {
	/**
	 * @type {Map<number, { i: number, refs: Array<Item | GC> }>}
	 */
	const clientRefs = create$9();
	const numOfStateUpdates = readVarUint(decoder.restDecoder);
	for (let i = 0; i < numOfStateUpdates; i++) {
		const numberOfStructs = readVarUint(decoder.restDecoder);
		/**
		 * @type {Array<GC|Item>}
		 */
		const refs = new Array(numberOfStructs);
		const client = decoder.readClient();
		let clock = readVarUint(decoder.restDecoder);
		// const start = performance.now()
		clientRefs.set(client, { i: 0, refs });
		for (let i = 0; i < numberOfStructs; i++) {
			const info = decoder.readInfo();
			switch (BITS5 & info) {
				case 0: {
					// GC
					const len = decoder.readLen();
					refs[i] = new GC(createID(client, clock), len);
					clock += len;
					break;
				}
				case 10: {
					// Skip Struct (nothing to apply)
					// @todo we could reduce the amount of checks by adding Skip struct to clientRefs so we know that something is missing.
					const len = readVarUint(decoder.restDecoder);
					refs[i] = new Skip(createID(client, clock), len);
					clock += len;
					break;
				}
				default: {
					// Item with content
					/**
					 * The optimized implementation doesn't use any variables because inlining variables is faster.
					 * Below a non-optimized version is shown that implements the basic algorithm with
					 * a few comments
					 */
					const cantCopyParentInfo = (info & (BIT7 | BIT8)) === 0;
					// If parent = null and neither left nor right are defined, then we know that `parent` is child of `y`
					// and we read the next string as parentYKey.
					// It indicates how we store/retrieve parent from `y.share`
					// @type {string|null}
					const struct = new Item$1(
						createID(client, clock),
						null, // left
						(info & BIT8) === BIT8 ? decoder.readLeftID() : null, // origin
						null, // right
						(info & BIT7) === BIT7 ? decoder.readRightID() : null, // right origin
						cantCopyParentInfo ?
							decoder.readParentInfo() ?
								doc.get(decoder.readString())
							:	decoder.readLeftID()
						:	null, // parent
						cantCopyParentInfo && (info & BIT6) === BIT6 ? decoder.readString() : null, // parentSub
						readItemContent(decoder, info), // item content
					);
					/* A non-optimized implementation of the above algorithm:

          // The item that was originally to the left of this item.
          const origin = (info & binary.BIT8) === binary.BIT8 ? decoder.readLeftID() : null
          // The item that was originally to the right of this item.
          const rightOrigin = (info & binary.BIT7) === binary.BIT7 ? decoder.readRightID() : null
          const cantCopyParentInfo = (info & (binary.BIT7 | binary.BIT8)) === 0
          const hasParentYKey = cantCopyParentInfo ? decoder.readParentInfo() : false
          // If parent = null and neither left nor right are defined, then we know that `parent` is child of `y`
          // and we read the next string as parentYKey.
          // It indicates how we store/retrieve parent from `y.share`
          // @type {string|null}
          const parentYKey = cantCopyParentInfo && hasParentYKey ? decoder.readString() : null

          const struct = new Item(
            createID(client, clock),
            null, // left
            origin, // origin
            null, // right
            rightOrigin, // right origin
            cantCopyParentInfo && !hasParentYKey ? decoder.readLeftID() : (parentYKey !== null ? doc.get(parentYKey) : null), // parent
            cantCopyParentInfo && (info & binary.BIT6) === binary.BIT6 ? decoder.readString() : null, // parentSub
            readItemContent(decoder, info) // item content
          )
          */
					refs[i] = struct;
					clock += struct.length;
				}
			}
		}
		// console.log('time to read: ', performance.now() - start) // @todo remove
	}
	return clientRefs;
};

/**
 * Resume computing structs generated by struct readers.
 *
 * While there is something to do, we integrate structs in this order
 * 1. top element on stack, if stack is not empty
 * 2. next element from current struct reader (if empty, use next struct reader)
 *
 * If struct causally depends on another struct (ref.missing), we put next reader of
 * `ref.id.client` on top of stack.
 *
 * At some point we find a struct that has no causal dependencies,
 * then we start emptying the stack.
 *
 * It is not possible to have circles: i.e. struct1 (from client1) depends on struct2 (from client2)
 * depends on struct3 (from client1). Therefore the max stack size is equal to `structReaders.length`.
 *
 * This method is implemented in a way so that we can resume computation if this update
 * causally depends on another update.
 *
 * @param {Transaction} transaction
 * @param {StructStore} store
 * @param {Map<number, { i: number, refs: (GC | Item)[] }>} clientsStructRefs
 * @return { null | { update: Uint8Array, missing: Map<number,number> } }
 *
 * @private
 * @function
 */
const integrateStructs = (transaction, store, clientsStructRefs) => {
	/**
	 * @type {Array<Item | GC>}
	 */
	const stack = [];
	// sort them so that we take the higher id first, in case of conflicts the lower id will probably not conflict with the id from the higher user.
	let clientsStructRefsIds = from(clientsStructRefs.keys()).sort((a, b) => a - b);
	if (clientsStructRefsIds.length === 0) {
		return null;
	}
	const getNextStructTarget = () => {
		if (clientsStructRefsIds.length === 0) {
			return null;
		}
		let nextStructsTarget = /** @type {{i:number,refs:Array<GC|Item>}} */ (clientsStructRefs.get(clientsStructRefsIds[clientsStructRefsIds.length - 1]));
		while (nextStructsTarget.refs.length === nextStructsTarget.i) {
			clientsStructRefsIds.pop();
			if (clientsStructRefsIds.length > 0) {
				nextStructsTarget = /** @type {{i:number,refs:Array<GC|Item>}} */ (clientsStructRefs.get(clientsStructRefsIds[clientsStructRefsIds.length - 1]));
			} else {
				return null;
			}
		}
		return nextStructsTarget;
	};
	let curStructsTarget = getNextStructTarget();
	if (curStructsTarget === null) {
		return null;
	}

	/**
	 * @type {StructStore}
	 */
	const restStructs = new StructStore();
	const missingSV = new Map();
	/**
	 * @param {number} client
	 * @param {number} clock
	 */
	const updateMissingSv = (client, clock) => {
		const mclock = missingSV.get(client);
		if (mclock == null || mclock > clock) {
			missingSV.set(client, clock);
		}
	};
	/**
	 * @type {GC|Item}
	 */
	let stackHead = /** @type {any} */ (curStructsTarget).refs[/** @type {any} */ (curStructsTarget).i++];
	// caching the state because it is used very often
	const state = new Map();

	const addStackToRestSS = () => {
		for (const item of stack) {
			const client = item.id.client;
			const inapplicableItems = clientsStructRefs.get(client);
			if (inapplicableItems) {
				// decrement because we weren't able to apply previous operation
				inapplicableItems.i--;
				restStructs.clients.set(client, inapplicableItems.refs.slice(inapplicableItems.i));
				clientsStructRefs.delete(client);
				inapplicableItems.i = 0;
				inapplicableItems.refs = [];
			} else {
				// item was the last item on clientsStructRefs and the field was already cleared. Add item to restStructs and continue
				restStructs.clients.set(client, [item]);
			}
			// remove client from clientsStructRefsIds to prevent users from applying the same update again
			clientsStructRefsIds = clientsStructRefsIds.filter((c) => c !== client);
		}
		stack.length = 0;
	};

	// iterate over all struct readers until we are done
	while (true) {
		if (stackHead.constructor !== Skip) {
			const localClock = setIfUndefined(state, stackHead.id.client, () => getState(store, stackHead.id.client));
			const offset = localClock - stackHead.id.clock;
			if (offset < 0) {
				// update from the same client is missing
				stack.push(stackHead);
				updateMissingSv(stackHead.id.client, stackHead.id.clock - 1);
				// hid a dead wall, add all items from stack to restSS
				addStackToRestSS();
			} else {
				const missing = stackHead.getMissing(transaction, store);
				if (missing !== null) {
					stack.push(stackHead);
					// get the struct reader that has the missing struct
					/**
					 * @type {{ refs: Array<GC|Item>, i: number }}
					 */
					const structRefs = clientsStructRefs.get(/** @type {number} */ (missing)) || { refs: [], i: 0 };
					if (structRefs.refs.length === structRefs.i) {
						// This update message causally depends on another update message that doesn't exist yet
						updateMissingSv(/** @type {number} */ (missing), getState(store, missing));
						addStackToRestSS();
					} else {
						stackHead = structRefs.refs[structRefs.i++];
						continue;
					}
				} else if (offset === 0 || offset < stackHead.length) {
					// all fine, apply the stackhead
					stackHead.integrate(transaction, offset);
					state.set(stackHead.id.client, stackHead.id.clock + stackHead.length);
				}
			}
		}
		// iterate to next stackHead
		if (stack.length > 0) {
			stackHead = /** @type {GC|Item} */ (stack.pop());
		} else if (curStructsTarget !== null && curStructsTarget.i < curStructsTarget.refs.length) {
			stackHead = /** @type {GC|Item} */ (curStructsTarget.refs[curStructsTarget.i++]);
		} else {
			curStructsTarget = getNextStructTarget();
			if (curStructsTarget === null) {
				// we are done!
				break;
			} else {
				stackHead = /** @type {GC|Item} */ (curStructsTarget.refs[curStructsTarget.i++]);
			}
		}
	}
	if (restStructs.clients.size > 0) {
		const encoder = new UpdateEncoderV2();
		writeClientsStructs(encoder, restStructs, new Map());
		// write empty deleteset
		// writeDeleteSet(encoder, new DeleteSet())
		writeVarUint(encoder.restEncoder, 0); // => no need for an extra function call, just write 0 deletes
		return { missing: missingSV, update: encoder.toUint8Array() };
	}
	return null;
};

/**
 * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
 * @param {Transaction} transaction
 *
 * @private
 * @function
 */
const writeStructsFromTransaction = (encoder, transaction) => writeClientsStructs(encoder, transaction.doc.store, transaction.beforeState);

/**
 * Read and apply a document update.
 *
 * This function has the same effect as `applyUpdate` but accepts a decoder.
 *
 * @param {decoding.Decoder} decoder
 * @param {Doc} ydoc
 * @param {any} [transactionOrigin] This will be stored on `transaction.origin` and `.on('update', (update, origin))`
 * @param {UpdateDecoderV1 | UpdateDecoderV2} [structDecoder]
 *
 * @function
 */
const readUpdateV2 = (decoder, ydoc, transactionOrigin, structDecoder = new UpdateDecoderV2(decoder)) =>
	transact(
		ydoc,
		(transaction) => {
			// force that transaction.local is set to non-local
			transaction.local = false;
			let retry = false;
			const doc = transaction.doc;
			const store = doc.store;
			// let start = performance.now()
			const ss = readClientsStructRefs(structDecoder, doc);
			// console.log('time to read structs: ', performance.now() - start) // @todo remove
			// start = performance.now()
			// console.log('time to merge: ', performance.now() - start) // @todo remove
			// start = performance.now()
			const restStructs = integrateStructs(transaction, store, ss);
			const pending = store.pendingStructs;
			if (pending) {
				// check if we can apply something
				for (const [client, clock] of pending.missing) {
					if (clock < getState(store, client)) {
						retry = true;
						break;
					}
				}
				if (restStructs) {
					// merge restStructs into store.pending
					for (const [client, clock] of restStructs.missing) {
						const mclock = pending.missing.get(client);
						if (mclock == null || mclock > clock) {
							pending.missing.set(client, clock);
						}
					}
					pending.update = mergeUpdatesV2([pending.update, restStructs.update]);
				}
			} else {
				store.pendingStructs = restStructs;
			}
			// console.log('time to integrate: ', performance.now() - start) // @todo remove
			// start = performance.now()
			const dsRest = readAndApplyDeleteSet(structDecoder, transaction, store);
			if (store.pendingDs) {
				// @todo we could make a lower-bound state-vector check as we do above
				const pendingDSUpdate = new UpdateDecoderV2(createDecoder(store.pendingDs));
				readVarUint(pendingDSUpdate.restDecoder); // read 0 structs, because we only encode deletes in pendingdsupdate
				const dsRest2 = readAndApplyDeleteSet(pendingDSUpdate, transaction, store);
				if (dsRest && dsRest2) {
					// case 1: ds1 != null && ds2 != null
					store.pendingDs = mergeUpdatesV2([dsRest, dsRest2]);
				} else {
					// case 2: ds1 != null
					// case 3: ds2 != null
					// case 4: ds1 == null && ds2 == null
					store.pendingDs = dsRest || dsRest2;
				}
			} else {
				// Either dsRest == null && pendingDs == null OR dsRest != null
				store.pendingDs = dsRest;
			}
			// console.log('time to cleanup: ', performance.now() - start) // @todo remove
			// start = performance.now()

			// console.log('time to resume delete readers: ', performance.now() - start) // @todo remove
			// start = performance.now()
			if (retry) {
				const update = /** @type {{update: Uint8Array}} */ (store.pendingStructs).update;
				store.pendingStructs = null;
				applyUpdateV2(transaction.doc, update);
			}
		},
		transactionOrigin,
		false,
	);

/**
 * Apply a document update created by, for example, `y.on('update', update => ..)` or `update = encodeStateAsUpdate()`.
 *
 * This function has the same effect as `readUpdate` but accepts an Uint8Array instead of a Decoder.
 *
 * @param {Doc} ydoc
 * @param {Uint8Array} update
 * @param {any} [transactionOrigin] This will be stored on `transaction.origin` and `.on('update', (update, origin))`
 * @param {typeof UpdateDecoderV1 | typeof UpdateDecoderV2} [YDecoder]
 *
 * @function
 */
const applyUpdateV2 = (ydoc, update, transactionOrigin, YDecoder = UpdateDecoderV2) => {
	const decoder = createDecoder(update);
	readUpdateV2(decoder, ydoc, transactionOrigin, new YDecoder(decoder));
};

/**
 * Apply a document update created by, for example, `y.on('update', update => ..)` or `update = encodeStateAsUpdate()`.
 *
 * This function has the same effect as `readUpdate` but accepts an Uint8Array instead of a Decoder.
 *
 * @param {Doc} ydoc
 * @param {Uint8Array} update
 * @param {any} [transactionOrigin] This will be stored on `transaction.origin` and `.on('update', (update, origin))`
 *
 * @function
 */
const applyUpdate = (ydoc, update, transactionOrigin) => applyUpdateV2(ydoc, update, transactionOrigin, UpdateDecoderV1);

/**
 * Write all the document as a single update message. If you specify the state of the remote client (`targetStateVector`) it will
 * only write the operations that are missing.
 *
 * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
 * @param {Doc} doc
 * @param {Map<number,number>} [targetStateVector] The state of the target that receives the update. Leave empty to write all known structs
 *
 * @function
 */
const writeStateAsUpdate = (encoder, doc, targetStateVector = new Map()) => {
	writeClientsStructs(encoder, doc.store, targetStateVector);
	writeDeleteSet(encoder, createDeleteSetFromStructStore(doc.store));
};

/**
 * Write all the document as a single update message that can be applied on the remote document. If you specify the state of the remote client (`targetState`) it will
 * only write the operations that are missing.
 *
 * Use `writeStateAsUpdate` instead if you are working with lib0/encoding.js#Encoder
 *
 * @param {Doc} doc
 * @param {Uint8Array} [encodedTargetStateVector] The state of the target that receives the update. Leave empty to write all known structs
 * @param {UpdateEncoderV1 | UpdateEncoderV2} [encoder]
 * @return {Uint8Array}
 *
 * @function
 */
const encodeStateAsUpdateV2 = (doc, encodedTargetStateVector = new Uint8Array([0]), encoder = new UpdateEncoderV2()) => {
	const targetStateVector = decodeStateVector(encodedTargetStateVector);
	writeStateAsUpdate(encoder, doc, targetStateVector);
	const updates = [encoder.toUint8Array()];
	// also add the pending updates (if there are any)
	if (doc.store.pendingDs) {
		updates.push(doc.store.pendingDs);
	}
	if (doc.store.pendingStructs) {
		updates.push(diffUpdateV2(doc.store.pendingStructs.update, encodedTargetStateVector));
	}
	if (updates.length > 1) {
		if (encoder.constructor === UpdateEncoderV1) {
			return mergeUpdates(updates.map((update, i) => (i === 0 ? update : convertUpdateFormatV2ToV1(update))));
		} else if (encoder.constructor === UpdateEncoderV2) {
			return mergeUpdatesV2(updates);
		}
	}
	return updates[0];
};

/**
 * Write all the document as a single update message that can be applied on the remote document. If you specify the state of the remote client (`targetState`) it will
 * only write the operations that are missing.
 *
 * Use `writeStateAsUpdate` instead if you are working with lib0/encoding.js#Encoder
 *
 * @param {Doc} doc
 * @param {Uint8Array} [encodedTargetStateVector] The state of the target that receives the update. Leave empty to write all known structs
 * @return {Uint8Array}
 *
 * @function
 */
const encodeStateAsUpdate = (doc, encodedTargetStateVector) => encodeStateAsUpdateV2(doc, encodedTargetStateVector, new UpdateEncoderV1());

/**
 * Read state vector from Decoder and return as Map
 *
 * @param {DSDecoderV1 | DSDecoderV2} decoder
 * @return {Map<number,number>} Maps `client` to the number next expected `clock` from that client.
 *
 * @function
 */
const readStateVector = (decoder) => {
	const ss = new Map();
	const ssLength = readVarUint(decoder.restDecoder);
	for (let i = 0; i < ssLength; i++) {
		const client = readVarUint(decoder.restDecoder);
		const clock = readVarUint(decoder.restDecoder);
		ss.set(client, clock);
	}
	return ss;
};

/**
 * Read decodedState and return State as Map.
 *
 * @param {Uint8Array} decodedState
 * @return {Map<number,number>} Maps `client` to the number next expected `clock` from that client.
 *
 * @function
 */
// export const decodeStateVectorV2 = decodedState => readStateVector(new DSDecoderV2(decoding.createDecoder(decodedState)))

/**
 * Read decodedState and return State as Map.
 *
 * @param {Uint8Array} decodedState
 * @return {Map<number,number>} Maps `client` to the number next expected `clock` from that client.
 *
 * @function
 */
const decodeStateVector = (decodedState) => readStateVector(new DSDecoderV1(createDecoder(decodedState)));

/**
 * @param {DSEncoderV1 | DSEncoderV2} encoder
 * @param {Map<number,number>} sv
 * @function
 */
const writeStateVector = (encoder, sv) => {
	writeVarUint(encoder.restEncoder, sv.size);
	from(sv.entries())
		.sort((a, b) => b[0] - a[0])
		.forEach(([client, clock]) => {
			writeVarUint(encoder.restEncoder, client); // @todo use a special client decoder that is based on mapping
			writeVarUint(encoder.restEncoder, clock);
		});
	return encoder;
};

/**
 * @param {DSEncoderV1 | DSEncoderV2} encoder
 * @param {Doc} doc
 *
 * @function
 */
const writeDocumentStateVector = (encoder, doc) => writeStateVector(encoder, getStateVector(doc.store));

/**
 * Encode State as Uint8Array.
 *
 * @param {Doc|Map<number,number>} doc
 * @param {DSEncoderV1 | DSEncoderV2} [encoder]
 * @return {Uint8Array}
 *
 * @function
 */
const encodeStateVectorV2 = (doc, encoder = new DSEncoderV2()) => {
	if (doc instanceof Map) {
		writeStateVector(encoder, doc);
	} else {
		writeDocumentStateVector(encoder, doc);
	}
	return encoder.toUint8Array();
};

/**
 * Encode State as Uint8Array.
 *
 * @param {Doc|Map<number,number>} doc
 * @return {Uint8Array}
 *
 * @function
 */
const encodeStateVector = (doc) => encodeStateVectorV2(doc, new DSEncoderV1());

/**
 * General event handler implementation.
 *
 * @template ARG0, ARG1
 *
 * @private
 */
class EventHandler {
	constructor() {
		/**
		 * @type {Array<function(ARG0, ARG1):void>}
		 */
		this.l = [];
	}
}

/**
 * @template ARG0,ARG1
 * @returns {EventHandler<ARG0,ARG1>}
 *
 * @private
 * @function
 */
const createEventHandler = () => new EventHandler();

/**
 * Adds an event listener that is called when
 * {@link EventHandler#callEventListeners} is called.
 *
 * @template ARG0,ARG1
 * @param {EventHandler<ARG0,ARG1>} eventHandler
 * @param {function(ARG0,ARG1):void} f The event handler.
 *
 * @private
 * @function
 */
const addEventHandlerListener = (eventHandler, f) => eventHandler.l.push(f);

/**
 * Removes an event listener.
 *
 * @template ARG0,ARG1
 * @param {EventHandler<ARG0,ARG1>} eventHandler
 * @param {function(ARG0,ARG1):void} f The event handler that was added with
 *                     {@link EventHandler#addEventListener}
 *
 * @private
 * @function
 */
const removeEventHandlerListener = (eventHandler, f) => {
	const l = eventHandler.l;
	const len = l.length;
	eventHandler.l = l.filter((g) => f !== g);
	if (len === eventHandler.l.length) {
		console.error("[yjs] Tried to remove event handler that doesn't exist.");
	}
};

/**
 * Call all event listeners that were added via
 * {@link EventHandler#addEventListener}.
 *
 * @template ARG0,ARG1
 * @param {EventHandler<ARG0,ARG1>} eventHandler
 * @param {ARG0} arg0
 * @param {ARG1} arg1
 *
 * @private
 * @function
 */
const callEventHandlerListeners = (eventHandler, arg0, arg1) => callAll(eventHandler.l, [arg0, arg1]);

class ID {
	/**
	 * @param {number} client client id
	 * @param {number} clock unique per client id, continuous number
	 */
	constructor(client, clock) {
		/**
		 * Client id
		 * @type {number}
		 */
		this.client = client;
		/**
		 * unique per client id, continuous number
		 * @type {number}
		 */
		this.clock = clock;
	}
}

/**
 * @param {ID | null} a
 * @param {ID | null} b
 * @return {boolean}
 *
 * @function
 */
const compareIDs = (a, b) => a === b || (a !== null && b !== null && a.client === b.client && a.clock === b.clock);

/**
 * @param {number} client
 * @param {number} clock
 *
 * @private
 * @function
 */
const createID = (client, clock) => new ID(client, clock);

/**
 * The top types are mapped from y.share.get(keyname) => type.
 * `type` does not store any information about the `keyname`.
 * This function finds the correct `keyname` for `type` and throws otherwise.
 *
 * @param {AbstractType<any>} type
 * @return {string}
 *
 * @private
 * @function
 */
const findRootTypeKey = (type) => {
	// @ts-ignore _y must be defined, otherwise unexpected case
	for (const [key, value] of type.doc.share.entries()) {
		if (value === type) {
			return key;
		}
	}
	throw unexpectedCase();
};

/**
 * Check if `parent` is a parent of `child`.
 *
 * @param {AbstractType<any>} parent
 * @param {Item|null} child
 * @return {Boolean} Whether `parent` is a parent of `child`.
 *
 * @private
 * @function
 */
const isParentOf = (parent, child) => {
	while (child !== null) {
		if (child.parent === parent) {
			return true;
		}
		child = /** @type {AbstractType<any>} */ (child.parent)._item;
	}
	return false;
};

/**
 * A relative position is based on the Yjs model and is not affected by document changes.
 * E.g. If you place a relative position before a certain character, it will always point to this character.
 * If you place a relative position at the end of a type, it will always point to the end of the type.
 *
 * A numeric position is often unsuited for user selections, because it does not change when content is inserted
 * before or after.
 *
 * ```Insert(0, 'x')('a|bc') = 'xa|bc'``` Where | is the relative position.
 *
 * One of the properties must be defined.
 *
 * @example
 *   // Current cursor position is at position 10
 *   const relativePosition = createRelativePositionFromIndex(yText, 10)
 *   // modify yText
 *   yText.insert(0, 'abc')
 *   yText.delete(3, 10)
 *   // Compute the cursor position
 *   const absolutePosition = createAbsolutePositionFromRelativePosition(y, relativePosition)
 *   absolutePosition.type === yText // => true
 *   console.log('cursor location is ' + absolutePosition.index) // => cursor location is 3
 *
 */
class RelativePosition {
	/**
	 * @param {ID|null} type
	 * @param {string|null} tname
	 * @param {ID|null} item
	 * @param {number} assoc
	 */
	constructor(type, tname, item, assoc = 0) {
		/**
		 * @type {ID|null}
		 */
		this.type = type;
		/**
		 * @type {string|null}
		 */
		this.tname = tname;
		/**
		 * @type {ID | null}
		 */
		this.item = item;
		/**
		 * A relative position is associated to a specific character. By default
		 * assoc >= 0, the relative position is associated to the character
		 * after the meant position.
		 * I.e. position 1 in 'ab' is associated to character 'b'.
		 *
		 * If assoc < 0, then the relative position is associated to the character
		 * before the meant position.
		 *
		 * @type {number}
		 */
		this.assoc = assoc;
	}
}

/**
 * @param {any} json
 * @return {RelativePosition}
 *
 * @function
 */
const createRelativePositionFromJSON = (json) =>
	new RelativePosition(
		json.type == null ? null : createID(json.type.client, json.type.clock),
		json.tname ?? null,
		json.item == null ? null : createID(json.item.client, json.item.clock),
		json.assoc == null ? 0 : json.assoc,
	);

class AbsolutePosition {
	/**
	 * @param {AbstractType<any>} type
	 * @param {number} index
	 * @param {number} [assoc]
	 */
	constructor(type, index, assoc = 0) {
		/**
		 * @type {AbstractType<any>}
		 */
		this.type = type;
		/**
		 * @type {number}
		 */
		this.index = index;
		this.assoc = assoc;
	}
}

/**
 * @param {AbstractType<any>} type
 * @param {number} index
 * @param {number} [assoc]
 *
 * @function
 */
const createAbsolutePosition = (type, index, assoc = 0) => new AbsolutePosition(type, index, assoc);

/**
 * @param {AbstractType<any>} type
 * @param {ID|null} item
 * @param {number} [assoc]
 *
 * @function
 */
const createRelativePosition$1 = (type, item, assoc) => {
	let typeid = null;
	let tname = null;
	if (type._item === null) {
		tname = findRootTypeKey(type);
	} else {
		typeid = createID(type._item.id.client, type._item.id.clock);
	}
	return new RelativePosition(typeid, tname, item, assoc);
};

/**
 * Create a relativePosition based on a absolute position.
 *
 * @param {AbstractType<any>} type The base type (e.g. YText or YArray).
 * @param {number} index The absolute position.
 * @param {number} [assoc]
 * @return {RelativePosition}
 *
 * @function
 */
const createRelativePositionFromTypeIndex = (type, index, assoc = 0) => {
	let t = type._start;
	if (assoc < 0) {
		// associated to the left character or the beginning of a type, increment index if possible.
		if (index === 0) {
			return createRelativePosition$1(type, null, assoc);
		}
		index--;
	}
	while (t !== null) {
		if (!t.deleted && t.countable) {
			if (t.length > index) {
				// case 1: found position somewhere in the linked list
				return createRelativePosition$1(type, createID(t.id.client, t.id.clock + index), assoc);
			}
			index -= t.length;
		}
		if (t.right === null && assoc < 0) {
			// left-associated position, return last available id
			return createRelativePosition$1(type, t.lastId, assoc);
		}
		t = t.right;
	}
	return createRelativePosition$1(type, null, assoc);
};

/**
 * @param {StructStore} store
 * @param {ID} id
 */
const getItemWithOffset = (store, id) => {
	const item = getItem(store, id);
	const diff = id.clock - item.id.clock;
	return {
		item,
		diff,
	};
};

/**
 * Transform a relative position to an absolute position.
 *
 * If you want to share the relative position with other users, you should set
 * `followUndoneDeletions` to false to get consistent results across all clients.
 *
 * When calculating the absolute position, we try to follow the "undone deletions". This yields
 * better results for the user who performed undo. However, only the user who performed the undo
 * will get the better results, the other users don't know which operations recreated a deleted
 * range of content. There is more information in this ticket: https://github.com/yjs/yjs/issues/638
 *
 * @param {RelativePosition} rpos
 * @param {Doc} doc
 * @param {boolean} followUndoneDeletions - whether to follow undone deletions - see https://github.com/yjs/yjs/issues/638
 * @return {AbsolutePosition|null}
 *
 * @function
 */
const createAbsolutePositionFromRelativePosition = (rpos, doc, followUndoneDeletions = true) => {
	const store = doc.store;
	const rightID = rpos.item;
	const typeID = rpos.type;
	const tname = rpos.tname;
	const assoc = rpos.assoc;
	let type = null;
	let index = 0;
	if (rightID !== null) {
		if (getState(store, rightID.client) <= rightID.clock) {
			return null;
		}
		const res = followUndoneDeletions ? followRedone(store, rightID) : getItemWithOffset(store, rightID);
		const right = res.item;
		if (!(right instanceof Item$1)) {
			return null;
		}
		type = /** @type {AbstractType<any>} */ (right.parent);
		if (type._item === null || !type._item.deleted) {
			index = right.deleted || !right.countable ? 0 : res.diff + (assoc >= 0 ? 0 : 1); // adjust position based on left association if necessary
			let n = right.left;
			while (n !== null) {
				if (!n.deleted && n.countable) {
					index += n.length;
				}
				n = n.left;
			}
		}
	} else {
		if (tname !== null) {
			type = doc.get(tname);
		} else if (typeID !== null) {
			if (getState(store, typeID.client) <= typeID.clock) {
				// type does not exist yet
				return null;
			}
			const { item } = followUndoneDeletions ? followRedone(store, typeID) : { item: getItem(store, typeID) };
			if (item instanceof Item$1 && item.content instanceof ContentType) {
				type = item.content.type;
			} else {
				// struct is garbage collected
				return null;
			}
		} else {
			throw unexpectedCase();
		}
		if (assoc >= 0) {
			index = type._length;
		} else {
			index = 0;
		}
	}
	return createAbsolutePosition(type, index, rpos.assoc);
};

/**
 * @param {RelativePosition|null} a
 * @param {RelativePosition|null} b
 * @return {boolean}
 *
 * @function
 */
const compareRelativePositions = (a, b) => a === b || (a !== null && b !== null && a.tname === b.tname && compareIDs(a.item, b.item) && compareIDs(a.type, b.type) && a.assoc === b.assoc);

class Snapshot {
	/**
	 * @param {DeleteSet} ds
	 * @param {Map<number,number>} sv state map
	 */
	constructor(ds, sv) {
		/**
		 * @type {DeleteSet}
		 */
		this.ds = ds;
		/**
		 * State Map
		 * @type {Map<number,number>}
		 */
		this.sv = sv;
	}
}

/**
 * @param {DeleteSet} ds
 * @param {Map<number,number>} sm
 * @return {Snapshot}
 */
const createSnapshot = (ds, sm) => new Snapshot(ds, sm);

/**
 * @param {Doc} doc
 * @return {Snapshot}
 */
const snapshot = (doc) => createSnapshot(createDeleteSetFromStructStore(doc.store), getStateVector(doc.store));

/**
 * @param {Item} item
 * @param {Snapshot|undefined} snapshot
 *
 * @protected
 * @function
 */
const isVisible$1 = (item, snapshot) =>
	snapshot === undefined ? !item.deleted : snapshot.sv.has(item.id.client) && (snapshot.sv.get(item.id.client) || 0) > item.id.clock && !isDeleted(snapshot.ds, item.id);

/**
 * @param {Transaction} transaction
 * @param {Snapshot} snapshot
 */
const splitSnapshotAffectedStructs = (transaction, snapshot) => {
	const meta = setIfUndefined(transaction.meta, splitSnapshotAffectedStructs, create$8);
	const store = transaction.doc.store;
	// check if we already split for this snapshot
	if (!meta.has(snapshot)) {
		snapshot.sv.forEach((clock, client) => {
			if (clock < getState(store, client)) {
				getItemCleanStart(transaction, createID(client, clock));
			}
		});
		iterateDeletedStructs(transaction, snapshot.ds, (_item) => {});
		meta.add(snapshot);
	}
};

class StructStore {
	constructor() {
		/**
		 * @type {Map<number,Array<GC|Item>>}
		 */
		this.clients = new Map();
		/**
		 * @type {null | { missing: Map<number, number>, update: Uint8Array }}
		 */
		this.pendingStructs = null;
		/**
		 * @type {null | Uint8Array}
		 */
		this.pendingDs = null;
	}
}

/**
 * Return the states as a Map<client,clock>.
 * Note that clock refers to the next expected clock id.
 *
 * @param {StructStore} store
 * @return {Map<number,number>}
 *
 * @public
 * @function
 */
const getStateVector = (store) => {
	const sm = new Map();
	store.clients.forEach((structs, client) => {
		const struct = structs[structs.length - 1];
		sm.set(client, struct.id.clock + struct.length);
	});
	return sm;
};

/**
 * @param {StructStore} store
 * @param {number} client
 * @return {number}
 *
 * @public
 * @function
 */
const getState = (store, client) => {
	const structs = store.clients.get(client);
	if (structs === undefined) {
		return 0;
	}
	const lastStruct = structs[structs.length - 1];
	return lastStruct.id.clock + lastStruct.length;
};

/**
 * @param {StructStore} store
 * @param {GC|Item} struct
 *
 * @private
 * @function
 */
const addStruct = (store, struct) => {
	let structs = store.clients.get(struct.id.client);
	if (structs === undefined) {
		structs = [];
		store.clients.set(struct.id.client, structs);
	} else {
		const lastStruct = structs[structs.length - 1];
		if (lastStruct.id.clock + lastStruct.length !== struct.id.clock) {
			throw unexpectedCase();
		}
	}
	structs.push(struct);
};

/**
 * Perform a binary search on a sorted array
 * @param {Array<Item|GC>} structs
 * @param {number} clock
 * @return {number}
 *
 * @private
 * @function
 */
const findIndexSS = (structs, clock) => {
	let left = 0;
	let right = structs.length - 1;
	let mid = structs[right];
	let midclock = mid.id.clock;
	if (midclock === clock) {
		return right;
	}
	// @todo does it even make sense to pivot the search?
	// If a good split misses, it might actually increase the time to find the correct item.
	// Currently, the only advantage is that search with pivoting might find the item on the first try.
	let midindex = floor$1((clock / (midclock + mid.length - 1)) * right); // pivoting the search
	while (left <= right) {
		mid = structs[midindex];
		midclock = mid.id.clock;
		if (midclock <= clock) {
			if (clock < midclock + mid.length) {
				return midindex;
			}
			left = midindex + 1;
		} else {
			right = midindex - 1;
		}
		midindex = floor$1((left + right) / 2);
	}
	// Always check state before looking for a struct in StructStore
	// Therefore the case of not finding a struct is unexpected
	throw unexpectedCase();
};

/**
 * Expects that id is actually in store. This function throws or is an infinite loop otherwise.
 *
 * @param {StructStore} store
 * @param {ID} id
 * @return {GC|Item}
 *
 * @private
 * @function
 */
const find$1 = (store, id) => {
	/**
	 * @type {Array<GC|Item>}
	 */
	// @ts-ignore
	const structs = store.clients.get(id.client);
	return structs[findIndexSS(structs, id.clock)];
};

/**
 * Expects that id is actually in store. This function throws or is an infinite loop otherwise.
 * @private
 * @function
 */
const getItem = /** @type {function(StructStore,ID):Item} */ (find$1);

/**
 * @param {Transaction} transaction
 * @param {Array<Item|GC>} structs
 * @param {number} clock
 */
const findIndexCleanStart = (transaction, structs, clock) => {
	const index = findIndexSS(structs, clock);
	const struct = structs[index];
	if (struct.id.clock < clock && struct instanceof Item$1) {
		structs.splice(index + 1, 0, splitItem(transaction, struct, clock - struct.id.clock));
		return index + 1;
	}
	return index;
};

/**
 * Expects that id is actually in store. This function throws or is an infinite loop otherwise.
 *
 * @param {Transaction} transaction
 * @param {ID} id
 * @return {Item}
 *
 * @private
 * @function
 */
const getItemCleanStart = (transaction, id) => {
	const structs = /** @type {Array<Item>} */ (transaction.doc.store.clients.get(id.client));
	return structs[findIndexCleanStart(transaction, structs, id.clock)];
};

/**
 * Expects that id is actually in store. This function throws or is an infinite loop otherwise.
 *
 * @param {Transaction} transaction
 * @param {StructStore} store
 * @param {ID} id
 * @return {Item}
 *
 * @private
 * @function
 */
const getItemCleanEnd = (transaction, store, id) => {
	/**
	 * @type {Array<Item>}
	 */
	// @ts-ignore
	const structs = store.clients.get(id.client);
	const index = findIndexSS(structs, id.clock);
	const struct = structs[index];
	if (id.clock !== struct.id.clock + struct.length - 1 && struct.constructor !== GC) {
		structs.splice(index + 1, 0, splitItem(transaction, struct, id.clock - struct.id.clock + 1));
	}
	return struct;
};

/**
 * Replace `item` with `newitem` in store
 * @param {StructStore} store
 * @param {GC|Item} struct
 * @param {GC|Item} newStruct
 *
 * @private
 * @function
 */
const replaceStruct = (store, struct, newStruct) => {
	const structs = /** @type {Array<GC|Item>} */ (store.clients.get(struct.id.client));
	structs[findIndexSS(structs, struct.id.clock)] = newStruct;
};

/**
 * Iterate over a range of structs
 *
 * @param {Transaction} transaction
 * @param {Array<Item|GC>} structs
 * @param {number} clockStart Inclusive start
 * @param {number} len
 * @param {function(GC|Item):void} f
 *
 * @function
 */
const iterateStructs = (transaction, structs, clockStart, len, f) => {
	if (len === 0) {
		return;
	}
	const clockEnd = clockStart + len;
	let index = findIndexCleanStart(transaction, structs, clockStart);
	let struct;
	do {
		struct = structs[index++];
		if (clockEnd < struct.id.clock + struct.length) {
			findIndexCleanStart(transaction, structs, clockEnd);
		}
		f(struct);
	} while (index < structs.length && structs[index].id.clock < clockEnd);
};

/**
 * A transaction is created for every change on the Yjs model. It is possible
 * to bundle changes on the Yjs model in a single transaction to
 * minimize the number on messages sent and the number of observer calls.
 * If possible the user of this library should bundle as many changes as
 * possible. Here is an example to illustrate the advantages of bundling:
 *
 * @example
 * const ydoc = new Y.Doc()
 * const map = ydoc.getMap('map')
 * // Log content when change is triggered
 * map.observe(() => {
 *   console.log('change triggered')
 * })
 * // Each change on the map type triggers a log message:
 * map.set('a', 0) // => "change triggered"
 * map.set('b', 0) // => "change triggered"
 * // When put in a transaction, it will trigger the log after the transaction:
 * ydoc.transact(() => {
 *   map.set('a', 1)
 *   map.set('b', 1)
 * }) // => "change triggered"
 *
 * @public
 */
class Transaction {
	/**
	 * @param {Doc} doc
	 * @param {any} origin
	 * @param {boolean} local
	 */
	constructor(doc, origin, local) {
		/**
		 * The Yjs instance.
		 * @type {Doc}
		 */
		this.doc = doc;
		/**
		 * Describes the set of deleted items by ids
		 * @type {DeleteSet}
		 */
		this.deleteSet = new DeleteSet();
		/**
		 * Holds the state before the transaction started.
		 * @type {Map<Number,Number>}
		 */
		this.beforeState = getStateVector(doc.store);
		/**
		 * Holds the state after the transaction.
		 * @type {Map<Number,Number>}
		 */
		this.afterState = new Map();
		/**
		 * All types that were directly modified (property added or child
		 * inserted/deleted). New types are not included in this Set.
		 * Maps from type to parentSubs (`item.parentSub = null` for YArray)
		 * @type {Map<AbstractType<YEvent<any>>,Set<String|null>>}
		 */
		this.changed = new Map();
		/**
		 * Stores the events for the types that observe also child elements.
		 * It is mainly used by `observeDeep`.
		 * @type {Map<AbstractType<YEvent<any>>,Array<YEvent<any>>>}
		 */
		this.changedParentTypes = new Map();
		/**
		 * @type {Array<AbstractStruct>}
		 */
		this._mergeStructs = [];
		/**
		 * @type {any}
		 */
		this.origin = origin;
		/**
		 * Stores meta information on the transaction
		 * @type {Map<any,any>}
		 */
		this.meta = new Map();
		/**
		 * Whether this change originates from this doc.
		 * @type {boolean}
		 */
		this.local = local;
		/**
		 * @type {Set<Doc>}
		 */
		this.subdocsAdded = new Set();
		/**
		 * @type {Set<Doc>}
		 */
		this.subdocsRemoved = new Set();
		/**
		 * @type {Set<Doc>}
		 */
		this.subdocsLoaded = new Set();
		/**
		 * @type {boolean}
		 */
		this._needFormattingCleanup = false;
	}
}

/**
 * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
 * @param {Transaction} transaction
 * @return {boolean} Whether data was written.
 */
const writeUpdateMessageFromTransaction = (encoder, transaction) => {
	if (transaction.deleteSet.clients.size === 0 && !any(transaction.afterState, (clock, client) => transaction.beforeState.get(client) !== clock)) {
		return false;
	}
	sortAndMergeDeleteSet(transaction.deleteSet);
	writeStructsFromTransaction(encoder, transaction);
	writeDeleteSet(encoder, transaction.deleteSet);
	return true;
};

/**
 * If `type.parent` was added in current transaction, `type` technically
 * did not change, it was just added and we should not fire events for `type`.
 *
 * @param {Transaction} transaction
 * @param {AbstractType<YEvent<any>>} type
 * @param {string|null} parentSub
 */
const addChangedTypeToTransaction = (transaction, type, parentSub) => {
	const item = type._item;
	if (item === null || (item.id.clock < (transaction.beforeState.get(item.id.client) || 0) && !item.deleted)) {
		setIfUndefined(transaction.changed, type, create$8).add(parentSub);
	}
};

/**
 * @param {Array<AbstractStruct>} structs
 * @param {number} pos
 * @return {number} # of merged structs
 */
const tryToMergeWithLefts = (structs, pos) => {
	let right = structs[pos];
	let left = structs[pos - 1];
	let i = pos;
	for (; i > 0; right = left, left = structs[--i - 1]) {
		if (left.deleted === right.deleted && left.constructor === right.constructor) {
			if (left.mergeWith(right)) {
				if (right instanceof Item$1 && right.parentSub !== null && /** @type {AbstractType<any>} */ (right.parent)._map.get(right.parentSub) === right) {
					/** @type {AbstractType<any>} */ (right.parent)._map.set(right.parentSub, /** @type {Item} */ (left));
				}
				continue;
			}
		}
		break;
	}
	const merged = pos - i;
	if (merged) {
		// remove all merged structs from the array
		structs.splice(pos + 1 - merged, merged);
	}
	return merged;
};

/**
 * @param {DeleteSet} ds
 * @param {StructStore} store
 * @param {function(Item):boolean} gcFilter
 */
const tryGcDeleteSet = (ds, store, gcFilter) => {
	for (const [client, deleteItems] of ds.clients.entries()) {
		const structs = /** @type {Array<GC|Item>} */ (store.clients.get(client));
		for (let di = deleteItems.length - 1; di >= 0; di--) {
			const deleteItem = deleteItems[di];
			const endDeleteItemClock = deleteItem.clock + deleteItem.len;
			for (let si = findIndexSS(structs, deleteItem.clock), struct = structs[si]; si < structs.length && struct.id.clock < endDeleteItemClock; struct = structs[++si]) {
				const struct = structs[si];
				if (deleteItem.clock + deleteItem.len <= struct.id.clock) {
					break;
				}
				if (struct instanceof Item$1 && struct.deleted && !struct.keep && gcFilter(struct)) {
					struct.gc(store, false);
				}
			}
		}
	}
};

/**
 * @param {DeleteSet} ds
 * @param {StructStore} store
 */
const tryMergeDeleteSet = (ds, store) => {
	// try to merge deleted / gc'd items
	// merge from right to left for better efficiency and so we don't miss any merge targets
	ds.clients.forEach((deleteItems, client) => {
		const structs = /** @type {Array<GC|Item>} */ (store.clients.get(client));
		for (let di = deleteItems.length - 1; di >= 0; di--) {
			const deleteItem = deleteItems[di];
			// start with merging the item next to the last deleted item
			const mostRightIndexToCheck = min$1(structs.length - 1, 1 + findIndexSS(structs, deleteItem.clock + deleteItem.len - 1));
			for (let si = mostRightIndexToCheck, struct = structs[si]; si > 0 && struct.id.clock >= deleteItem.clock; struct = structs[si]) {
				si -= 1 + tryToMergeWithLefts(structs, si);
			}
		}
	});
};

/**
 * @param {Array<Transaction>} transactionCleanups
 * @param {number} i
 */
const cleanupTransactions = (transactionCleanups, i) => {
	if (i < transactionCleanups.length) {
		const transaction = transactionCleanups[i];
		const doc = transaction.doc;
		const store = doc.store;
		const ds = transaction.deleteSet;
		const mergeStructs = transaction._mergeStructs;
		try {
			sortAndMergeDeleteSet(ds);
			transaction.afterState = getStateVector(transaction.doc.store);
			doc.emit("beforeObserverCalls", [transaction, doc]);
			/**
			 * An array of event callbacks.
			 *
			 * Each callback is called even if the other ones throw errors.
			 *
			 * @type {Array<function():void>}
			 */
			const fs = [];
			// observe events on changed types
			transaction.changed.forEach((subs, itemtype) =>
				fs.push(() => {
					if (itemtype._item === null || !itemtype._item.deleted) {
						itemtype._callObserver(transaction, subs);
					}
				}),
			);
			fs.push(() => {
				// deep observe events
				transaction.changedParentTypes.forEach((events, type) => {
					// We need to think about the possibility that the user transforms the
					// Y.Doc in the event.
					if (type._dEH.l.length > 0 && (type._item === null || !type._item.deleted)) {
						events = events.filter((event) => event.target._item === null || !event.target._item.deleted);
						events.forEach((event) => {
							event.currentTarget = type;
							// path is relative to the current target
							event._path = null;
						});
						// sort events by path length so that top-level events are fired first.
						events.sort((event1, event2) => event1.path.length - event2.path.length);
						fs.push(() => {
							// We don't need to check for events.length
							// because we know it has at least one element
							callEventHandlerListeners(type._dEH, events, transaction);
						});
					}
				});
				fs.push(() => doc.emit("afterTransaction", [transaction, doc]));
				fs.push(() => {
					if (transaction._needFormattingCleanup) {
						cleanupYTextAfterTransaction(transaction);
					}
				});
			});
			callAll(fs, []);
		} finally {
			// Replace deleted items with ItemDeleted / GC.
			// This is where content is actually remove from the Yjs Doc.
			if (doc.gc) {
				tryGcDeleteSet(ds, store, doc.gcFilter);
			}
			tryMergeDeleteSet(ds, store);

			// on all affected store.clients props, try to merge
			transaction.afterState.forEach((clock, client) => {
				const beforeClock = transaction.beforeState.get(client) || 0;
				if (beforeClock !== clock) {
					const structs = /** @type {Array<GC|Item>} */ (store.clients.get(client));
					// we iterate from right to left so we can safely remove entries
					const firstChangePos = max$1(findIndexSS(structs, beforeClock), 1);
					for (let i = structs.length - 1; i >= firstChangePos; ) {
						i -= 1 + tryToMergeWithLefts(structs, i);
					}
				}
			});
			// try to merge mergeStructs
			// @todo: it makes more sense to transform mergeStructs to a DS, sort it, and merge from right to left
			//        but at the moment DS does not handle duplicates
			for (let i = mergeStructs.length - 1; i >= 0; i--) {
				const { client, clock } = mergeStructs[i].id;
				const structs = /** @type {Array<GC|Item>} */ (store.clients.get(client));
				const replacedStructPos = findIndexSS(structs, clock);
				if (replacedStructPos + 1 < structs.length) {
					if (tryToMergeWithLefts(structs, replacedStructPos + 1) > 1) {
						continue; // no need to perform next check, both are already merged
					}
				}
				if (replacedStructPos > 0) {
					tryToMergeWithLefts(structs, replacedStructPos);
				}
			}
			if (!transaction.local && transaction.afterState.get(doc.clientID) !== transaction.beforeState.get(doc.clientID)) {
				print(ORANGE, BOLD, "[yjs] ", UNBOLD, RED, "Changed the client-id because another client seems to be using it.");
				doc.clientID = generateNewClientId();
			}
			// @todo Merge all the transactions into one and provide send the data as a single update message
			doc.emit("afterTransactionCleanup", [transaction, doc]);
			if (doc._observers.has("update")) {
				const encoder = new UpdateEncoderV1();
				const hasContent = writeUpdateMessageFromTransaction(encoder, transaction);
				if (hasContent) {
					doc.emit("update", [encoder.toUint8Array(), transaction.origin, doc, transaction]);
				}
			}
			if (doc._observers.has("updateV2")) {
				const encoder = new UpdateEncoderV2();
				const hasContent = writeUpdateMessageFromTransaction(encoder, transaction);
				if (hasContent) {
					doc.emit("updateV2", [encoder.toUint8Array(), transaction.origin, doc, transaction]);
				}
			}
			const { subdocsAdded, subdocsLoaded, subdocsRemoved } = transaction;
			if (subdocsAdded.size > 0 || subdocsRemoved.size > 0 || subdocsLoaded.size > 0) {
				subdocsAdded.forEach((subdoc) => {
					subdoc.clientID = doc.clientID;
					if (subdoc.collectionid == null) {
						subdoc.collectionid = doc.collectionid;
					}
					doc.subdocs.add(subdoc);
				});
				subdocsRemoved.forEach((subdoc) => doc.subdocs.delete(subdoc));
				doc.emit("subdocs", [{ loaded: subdocsLoaded, added: subdocsAdded, removed: subdocsRemoved }, doc, transaction]);
				subdocsRemoved.forEach((subdoc) => subdoc.destroy());
			}

			if (transactionCleanups.length <= i + 1) {
				doc._transactionCleanups = [];
				doc.emit("afterAllTransactions", [doc, transactionCleanups]);
			} else {
				cleanupTransactions(transactionCleanups, i + 1);
			}
		}
	}
};

/**
 * Implements the functionality of `y.transact(()=>{..})`
 *
 * @template T
 * @param {Doc} doc
 * @param {function(Transaction):T} f
 * @param {any} [origin=true]
 * @return {T}
 *
 * @function
 */
const transact = (doc, f, origin = null, local = true) => {
	const transactionCleanups = doc._transactionCleanups;
	let initialCall = false;
	/**
	 * @type {any}
	 */
	let result = null;
	if (doc._transaction === null) {
		initialCall = true;
		doc._transaction = new Transaction(doc, origin, local);
		transactionCleanups.push(doc._transaction);
		if (transactionCleanups.length === 1) {
			doc.emit("beforeAllTransactions", [doc]);
		}
		doc.emit("beforeTransaction", [doc._transaction, doc]);
	}
	try {
		result = f(doc._transaction);
	} finally {
		if (initialCall) {
			const finishCleanup = doc._transaction === transactionCleanups[0];
			doc._transaction = null;
			if (finishCleanup) {
				// The first transaction ended, now process observer calls.
				// Observer call may create new transactions for which we need to call the observers and do cleanup.
				// We don't want to nest these calls, so we execute these calls one after
				// another.
				// Also we need to ensure that all cleanups are called, even if the
				// observes throw errors.
				// This file is full of hacky try {} finally {} blocks to ensure that an
				// event can throw errors and also that the cleanup is called.
				cleanupTransactions(transactionCleanups, 0);
			}
		}
	}
	return result;
};

class StackItem {
	/**
	 * @param {DeleteSet} deletions
	 * @param {DeleteSet} insertions
	 */
	constructor(deletions, insertions) {
		this.insertions = insertions;
		this.deletions = deletions;
		/**
		 * Use this to save and restore metadata like selection range
		 */
		this.meta = new Map();
	}
}
/**
 * @param {Transaction} tr
 * @param {UndoManager} um
 * @param {StackItem} stackItem
 */
const clearUndoManagerStackItem = (tr, um, stackItem) => {
	iterateDeletedStructs(tr, stackItem.deletions, (item) => {
		if (item instanceof Item$1 && um.scope.some((type) => type === tr.doc || isParentOf(/** @type {AbstractType<any>} */ (type), item))) {
			keepItem(item, false);
		}
	});
};

/**
 * @param {UndoManager} undoManager
 * @param {Array<StackItem>} stack
 * @param {'undo'|'redo'} eventType
 * @return {StackItem?}
 */
const popStackItem = (undoManager, stack, eventType) => {
	/**
	 * Keep a reference to the transaction so we can fire the event with the changedParentTypes
	 * @type {any}
	 */
	let _tr = null;
	const doc = undoManager.doc;
	const scope = undoManager.scope;
	transact(
		doc,
		(transaction) => {
			while (stack.length > 0 && undoManager.currStackItem === null) {
				const store = doc.store;
				const stackItem = /** @type {StackItem} */ (stack.pop());
				/**
				 * @type {Set<Item>}
				 */
				const itemsToRedo = new Set();
				/**
				 * @type {Array<Item>}
				 */
				const itemsToDelete = [];
				let performedChange = false;
				iterateDeletedStructs(transaction, stackItem.insertions, (struct) => {
					if (struct instanceof Item$1) {
						if (struct.redone !== null) {
							let { item, diff } = followRedone(store, struct.id);
							if (diff > 0) {
								item = getItemCleanStart(transaction, createID(item.id.client, item.id.clock + diff));
							}
							struct = item;
						}
						if (!struct.deleted && scope.some((type) => type === transaction.doc || isParentOf(/** @type {AbstractType<any>} */ (type), /** @type {Item} */ (struct)))) {
							itemsToDelete.push(struct);
						}
					}
				});
				iterateDeletedStructs(transaction, stackItem.deletions, (struct) => {
					if (
						struct instanceof Item$1 &&
						scope.some((type) => type === transaction.doc || isParentOf(/** @type {AbstractType<any>} */ (type), struct)) &&
						// Never redo structs in stackItem.insertions because they were created and deleted in the same capture interval.
						!isDeleted(stackItem.insertions, struct.id)
					) {
						itemsToRedo.add(struct);
					}
				});
				itemsToRedo.forEach((struct) => {
					performedChange = redoItem(transaction, struct, itemsToRedo, stackItem.insertions, undoManager.ignoreRemoteMapChanges, undoManager) !== null || performedChange;
				});
				// We want to delete in reverse order so that children are deleted before
				// parents, so we have more information available when items are filtered.
				for (let i = itemsToDelete.length - 1; i >= 0; i--) {
					const item = itemsToDelete[i];
					if (undoManager.deleteFilter(item)) {
						item.delete(transaction);
						performedChange = true;
					}
				}
				undoManager.currStackItem = performedChange ? stackItem : null;
			}
			transaction.changed.forEach((subProps, type) => {
				// destroy search marker if necessary
				if (subProps.has(null) && type._searchMarker) {
					type._searchMarker.length = 0;
				}
			});
			_tr = transaction;
		},
		undoManager,
	);
	const res = undoManager.currStackItem;
	if (res != null) {
		const changedParentTypes = _tr.changedParentTypes;
		undoManager.emit("stack-item-popped", [{ stackItem: res, type: eventType, changedParentTypes, origin: undoManager }, undoManager]);
		undoManager.currStackItem = null;
	}
	return res;
};

/**
 * @typedef {Object} UndoManagerOptions
 * @property {number} [UndoManagerOptions.captureTimeout=500]
 * @property {function(Transaction):boolean} [UndoManagerOptions.captureTransaction] Do not capture changes of a Transaction if result false.
 * @property {function(Item):boolean} [UndoManagerOptions.deleteFilter=()=>true] Sometimes
 * it is necessary to filter what an Undo/Redo operation can delete. If this
 * filter returns false, the type/item won't be deleted even it is in the
 * undo/redo scope.
 * @property {Set<any>} [UndoManagerOptions.trackedOrigins=new Set([null])]
 * @property {boolean} [ignoreRemoteMapChanges] Experimental. By default, the UndoManager will never overwrite remote changes. Enable this property to enable overwriting remote changes on key-value changes (Y.Map, properties on Y.Xml, etc..).
 * @property {Doc} [doc] The document that this UndoManager operates on. Only needed if typeScope is empty.
 */

/**
 * @typedef {Object} StackItemEvent
 * @property {StackItem} StackItemEvent.stackItem
 * @property {any} StackItemEvent.origin
 * @property {'undo'|'redo'} StackItemEvent.type
 * @property {Map<AbstractType<YEvent<any>>,Array<YEvent<any>>>} StackItemEvent.changedParentTypes
 */

/**
 * Fires 'stack-item-added' event when a stack item was added to either the undo- or
 * the redo-stack. You may store additional stack information via the
 * metadata property on `event.stackItem.meta` (it is a `Map` of metadata properties).
 * Fires 'stack-item-popped' event when a stack item was popped from either the
 * undo- or the redo-stack. You may restore the saved stack information from `event.stackItem.meta`.
 *
 * @extends {ObservableV2<{'stack-item-added':function(StackItemEvent, UndoManager):void, 'stack-item-popped': function(StackItemEvent, UndoManager):void, 'stack-cleared': function({ undoStackCleared: boolean, redoStackCleared: boolean }):void, 'stack-item-updated': function(StackItemEvent, UndoManager):void }>}
 */
class UndoManager extends ObservableV2 {
	/**
	 * @param {Doc|AbstractType<any>|Array<AbstractType<any>>} typeScope Limits the scope of the UndoManager. If this is set to a ydoc instance, all changes on that ydoc will be undone. If set to a specific type, only changes on that type or its children will be undone. Also accepts an array of types.
	 * @param {UndoManagerOptions} options
	 */
	constructor(
		typeScope,
		{
			captureTimeout = 500,
			captureTransaction = (_tr) => true,
			deleteFilter = () => true,
			trackedOrigins = new Set([null]),
			ignoreRemoteMapChanges = false,
			doc = /** @type {Doc} */ (
				isArray(typeScope) ? typeScope[0].doc
				: typeScope instanceof Doc ? typeScope
				: typeScope.doc
			),
		} = {},
	) {
		super();
		/**
		 * @type {Array<AbstractType<any> | Doc>}
		 */
		this.scope = [];
		this.doc = doc;
		this.addToScope(typeScope);
		this.deleteFilter = deleteFilter;
		trackedOrigins.add(this);
		this.trackedOrigins = trackedOrigins;
		this.captureTransaction = captureTransaction;
		/**
		 * @type {Array<StackItem>}
		 */
		this.undoStack = [];
		/**
		 * @type {Array<StackItem>}
		 */
		this.redoStack = [];
		/**
		 * Whether the client is currently undoing (calling UndoManager.undo)
		 *
		 * @type {boolean}
		 */
		this.undoing = false;
		this.redoing = false;
		/**
		 * The currently popped stack item if UndoManager.undoing or UndoManager.redoing
		 *
		 * @type {StackItem|null}
		 */
		this.currStackItem = null;
		this.lastChange = 0;
		this.ignoreRemoteMapChanges = ignoreRemoteMapChanges;
		this.captureTimeout = captureTimeout;
		/**
		 * @param {Transaction} transaction
		 */
		this.afterTransactionHandler = (transaction) => {
			// Only track certain transactions
			if (
				!this.captureTransaction(transaction) ||
				!this.scope.some((type) => transaction.changedParentTypes.has(/** @type {AbstractType<any>} */ (type)) || type === this.doc) ||
				(!this.trackedOrigins.has(transaction.origin) && (!transaction.origin || !this.trackedOrigins.has(transaction.origin.constructor)))
			) {
				return;
			}
			const undoing = this.undoing;
			const redoing = this.redoing;
			const stack = undoing ? this.redoStack : this.undoStack;
			if (undoing) {
				this.stopCapturing(); // next undo should not be appended to last stack item
			} else if (!redoing) {
				// neither undoing nor redoing: delete redoStack
				this.clear(false, true);
			}
			const insertions = new DeleteSet();
			transaction.afterState.forEach((endClock, client) => {
				const startClock = transaction.beforeState.get(client) || 0;
				const len = endClock - startClock;
				if (len > 0) {
					addToDeleteSet(insertions, client, startClock, len);
				}
			});
			const now = getUnixTime();
			let didAdd = false;
			if (this.lastChange > 0 && now - this.lastChange < this.captureTimeout && stack.length > 0 && !undoing && !redoing) {
				// append change to last stack op
				const lastOp = stack[stack.length - 1];
				lastOp.deletions = mergeDeleteSets([lastOp.deletions, transaction.deleteSet]);
				lastOp.insertions = mergeDeleteSets([lastOp.insertions, insertions]);
			} else {
				// create a new stack op
				stack.push(new StackItem(transaction.deleteSet, insertions));
				didAdd = true;
			}
			if (!undoing && !redoing) {
				this.lastChange = now;
			}
			// make sure that deleted structs are not gc'd
			iterateDeletedStructs(
				transaction,
				transaction.deleteSet,
				/** @param {Item|GC} item */ (item) => {
					if (item instanceof Item$1 && this.scope.some((type) => type === transaction.doc || isParentOf(/** @type {AbstractType<any>} */ (type), item))) {
						keepItem(item, true);
					}
				},
			);
			/**
			 * @type {[StackItemEvent, UndoManager]}
			 */
			const changeEvent = [{ stackItem: stack[stack.length - 1], origin: transaction.origin, type: undoing ? "redo" : "undo", changedParentTypes: transaction.changedParentTypes }, this];
			if (didAdd) {
				this.emit("stack-item-added", changeEvent);
			} else {
				this.emit("stack-item-updated", changeEvent);
			}
		};
		this.doc.on("afterTransaction", this.afterTransactionHandler);
		this.doc.on("destroy", () => {
			this.destroy();
		});
	}

	/**
	 * Extend the scope.
	 *
	 * @param {Array<AbstractType<any> | Doc> | AbstractType<any> | Doc} ytypes
	 */
	addToScope(ytypes) {
		const tmpSet = new Set(this.scope);
		ytypes = isArray(ytypes) ? ytypes : [ytypes];
		ytypes.forEach((ytype) => {
			if (!tmpSet.has(ytype)) {
				tmpSet.add(ytype);
				if (ytype instanceof AbstractType ? ytype.doc !== this.doc : ytype !== this.doc) warn("[yjs#509] Not same Y.Doc"); // use MultiDocUndoManager instead. also see https://github.com/yjs/yjs/issues/509
				this.scope.push(ytype);
			}
		});
	}

	/**
	 * @param {any} origin
	 */
	addTrackedOrigin(origin) {
		this.trackedOrigins.add(origin);
	}

	/**
	 * @param {any} origin
	 */
	removeTrackedOrigin(origin) {
		this.trackedOrigins.delete(origin);
	}

	clear(clearUndoStack = true, clearRedoStack = true) {
		if ((clearUndoStack && this.canUndo()) || (clearRedoStack && this.canRedo())) {
			this.doc.transact((tr) => {
				if (clearUndoStack) {
					this.undoStack.forEach((item) => clearUndoManagerStackItem(tr, this, item));
					this.undoStack = [];
				}
				if (clearRedoStack) {
					this.redoStack.forEach((item) => clearUndoManagerStackItem(tr, this, item));
					this.redoStack = [];
				}
				this.emit("stack-cleared", [{ undoStackCleared: clearUndoStack, redoStackCleared: clearRedoStack }]);
			});
		}
	}

	/**
	 * UndoManager merges Undo-StackItem if they are created within time-gap
	 * smaller than `options.captureTimeout`. Call `um.stopCapturing()` so that the next
	 * StackItem won't be merged.
	 *
	 *
	 * @example
	 *     // without stopCapturing
	 *     ytext.insert(0, 'a')
	 *     ytext.insert(1, 'b')
	 *     um.undo()
	 *     ytext.toString() // => '' (note that 'ab' was removed)
	 *     // with stopCapturing
	 *     ytext.insert(0, 'a')
	 *     um.stopCapturing()
	 *     ytext.insert(0, 'b')
	 *     um.undo()
	 *     ytext.toString() // => 'a' (note that only 'b' was removed)
	 *
	 */
	stopCapturing() {
		this.lastChange = 0;
	}

	/**
	 * Undo last changes on type.
	 *
	 * @return {StackItem?} Returns StackItem if a change was applied
	 */
	undo() {
		this.undoing = true;
		let res;
		try {
			res = popStackItem(this, this.undoStack, "undo");
		} finally {
			this.undoing = false;
		}
		return res;
	}

	/**
	 * Redo last undo operation.
	 *
	 * @return {StackItem?} Returns StackItem if a change was applied
	 */
	redo() {
		this.redoing = true;
		let res;
		try {
			res = popStackItem(this, this.redoStack, "redo");
		} finally {
			this.redoing = false;
		}
		return res;
	}

	/**
	 * Are undo steps available?
	 *
	 * @return {boolean} `true` if undo is possible
	 */
	canUndo() {
		return this.undoStack.length > 0;
	}

	/**
	 * Are redo steps available?
	 *
	 * @return {boolean} `true` if redo is possible
	 */
	canRedo() {
		return this.redoStack.length > 0;
	}

	destroy() {
		this.trackedOrigins.delete(this);
		this.doc.off("afterTransaction", this.afterTransactionHandler);
		super.destroy();
	}
}

/**
 * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
 */
function* lazyStructReaderGenerator(decoder) {
	const numOfStateUpdates = readVarUint(decoder.restDecoder);
	for (let i = 0; i < numOfStateUpdates; i++) {
		const numberOfStructs = readVarUint(decoder.restDecoder);
		const client = decoder.readClient();
		let clock = readVarUint(decoder.restDecoder);
		for (let i = 0; i < numberOfStructs; i++) {
			const info = decoder.readInfo();
			// @todo use switch instead of ifs
			if (info === 10) {
				const len = readVarUint(decoder.restDecoder);
				yield new Skip(createID(client, clock), len);
				clock += len;
			} else if ((BITS5 & info) !== 0) {
				const cantCopyParentInfo = (info & (BIT7 | BIT8)) === 0;
				// If parent = null and neither left nor right are defined, then we know that `parent` is child of `y`
				// and we read the next string as parentYKey.
				// It indicates how we store/retrieve parent from `y.share`
				// @type {string|null}
				const struct = new Item$1(
					createID(client, clock),
					null, // left
					(info & BIT8) === BIT8 ? decoder.readLeftID() : null, // origin
					null, // right
					(info & BIT7) === BIT7 ? decoder.readRightID() : null, // right origin
					// @ts-ignore Force writing a string here.
					cantCopyParentInfo ?
						decoder.readParentInfo() ?
							decoder.readString()
						:	decoder.readLeftID()
					:	null, // parent
					cantCopyParentInfo && (info & BIT6) === BIT6 ? decoder.readString() : null, // parentSub
					readItemContent(decoder, info), // item content
				);
				yield struct;
				clock += struct.length;
			} else {
				const len = decoder.readLen();
				yield new GC(createID(client, clock), len);
				clock += len;
			}
		}
	}
}

class LazyStructReader {
	/**
	 * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
	 * @param {boolean} filterSkips
	 */
	constructor(decoder, filterSkips) {
		this.gen = lazyStructReaderGenerator(decoder);
		/**
		 * @type {null | Item | Skip | GC}
		 */
		this.curr = null;
		this.done = false;
		this.filterSkips = filterSkips;
		this.next();
	}

	/**
	 * @return {Item | GC | Skip |null}
	 */
	next() {
		// ignore "Skip" structs
		do {
			this.curr = this.gen.next().value || null;
		} while (this.filterSkips && this.curr !== null && this.curr.constructor === Skip);
		return this.curr;
	}
}

class LazyStructWriter {
	/**
	 * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
	 */
	constructor(encoder) {
		this.currClient = 0;
		this.startClock = 0;
		this.written = 0;
		this.encoder = encoder;
		/**
		 * We want to write operations lazily, but also we need to know beforehand how many operations we want to write for each client.
		 *
		 * This kind of meta-information (#clients, #structs-per-client-written) is written to the restEncoder.
		 *
		 * We fragment the restEncoder and store a slice of it per-client until we know how many clients there are.
		 * When we flush (toUint8Array) we write the restEncoder using the fragments and the meta-information.
		 *
		 * @type {Array<{ written: number, restEncoder: Uint8Array }>}
		 */
		this.clientStructs = [];
	}
}

/**
 * @param {Array<Uint8Array>} updates
 * @return {Uint8Array}
 */
const mergeUpdates = (updates) => mergeUpdatesV2(updates, UpdateDecoderV1, UpdateEncoderV1);

/**
 * This method is intended to slice any kind of struct and retrieve the right part.
 * It does not handle side-effects, so it should only be used by the lazy-encoder.
 *
 * @param {Item | GC | Skip} left
 * @param {number} diff
 * @return {Item | GC}
 */
const sliceStruct = (left, diff) => {
	if (left.constructor === GC) {
		const { client, clock } = left.id;
		return new GC(createID(client, clock + diff), left.length - diff);
	} else if (left.constructor === Skip) {
		const { client, clock } = left.id;
		return new Skip(createID(client, clock + diff), left.length - diff);
	} else {
		const leftItem = /** @type {Item} */ (left);
		const { client, clock } = leftItem.id;
		return new Item$1(createID(client, clock + diff), null, createID(client, clock + diff - 1), null, leftItem.rightOrigin, leftItem.parent, leftItem.parentSub, leftItem.content.splice(diff));
	}
};

/**
 *
 * This function works similarly to `readUpdateV2`.
 *
 * @param {Array<Uint8Array>} updates
 * @param {typeof UpdateDecoderV1 | typeof UpdateDecoderV2} [YDecoder]
 * @param {typeof UpdateEncoderV1 | typeof UpdateEncoderV2} [YEncoder]
 * @return {Uint8Array}
 */
const mergeUpdatesV2 = (updates, YDecoder = UpdateDecoderV2, YEncoder = UpdateEncoderV2) => {
	if (updates.length === 1) {
		return updates[0];
	}
	const updateDecoders = updates.map((update) => new YDecoder(createDecoder(update)));
	let lazyStructDecoders = updateDecoders.map((decoder) => new LazyStructReader(decoder, true));

	/**
	 * @todo we don't need offset because we always slice before
	 * @type {null | { struct: Item | GC | Skip, offset: number }}
	 */
	let currWrite = null;

	const updateEncoder = new YEncoder();
	// write structs lazily
	const lazyStructEncoder = new LazyStructWriter(updateEncoder);

	// Note: We need to ensure that all lazyStructDecoders are fully consumed
	// Note: Should merge document updates whenever possible - even from different updates
	// Note: Should handle that some operations cannot be applied yet ()

	while (true) {
		// Write higher clients first ⇒ sort by clientID & clock and remove decoders without content
		lazyStructDecoders = lazyStructDecoders.filter((dec) => dec.curr !== null);
		lazyStructDecoders.sort(
			/** @type {function(any,any):number} */ (dec1, dec2) => {
				if (dec1.curr.id.client === dec2.curr.id.client) {
					const clockDiff = dec1.curr.id.clock - dec2.curr.id.clock;
					if (clockDiff === 0) {
						// @todo remove references to skip since the structDecoders must filter Skips.
						return (
							dec1.curr.constructor === dec2.curr.constructor ? 0
							: dec1.curr.constructor === Skip ? 1
							: -1
						); // we are filtering skips anyway.
					} else {
						return clockDiff;
					}
				} else {
					return dec2.curr.id.client - dec1.curr.id.client;
				}
			},
		);
		if (lazyStructDecoders.length === 0) {
			break;
		}
		const currDecoder = lazyStructDecoders[0];
		// write from currDecoder until the next operation is from another client or if filler-struct
		// then we need to reorder the decoders and find the next operation to write
		const firstClient = /** @type {Item | GC} */ (currDecoder.curr).id.client;

		if (currWrite !== null) {
			let curr = /** @type {Item | GC | null} */ (currDecoder.curr);
			let iterated = false;

			// iterate until we find something that we haven't written already
			// remember: first the high client-ids are written
			while (curr !== null && curr.id.clock + curr.length <= currWrite.struct.id.clock + currWrite.struct.length && curr.id.client >= currWrite.struct.id.client) {
				curr = currDecoder.next();
				iterated = true;
			}
			if (
				curr === null || // current decoder is empty
				curr.id.client !== firstClient || // check whether there is another decoder that has has updates from `firstClient`
				(iterated && curr.id.clock > currWrite.struct.id.clock + currWrite.struct.length) // the above while loop was used and we are potentially missing updates
			) {
				continue;
			}

			if (firstClient !== currWrite.struct.id.client) {
				writeStructToLazyStructWriter(lazyStructEncoder, currWrite.struct, currWrite.offset);
				currWrite = { struct: curr, offset: 0 };
				currDecoder.next();
			} else {
				if (currWrite.struct.id.clock + currWrite.struct.length < curr.id.clock) {
					// @todo write currStruct & set currStruct = Skip(clock = currStruct.id.clock + currStruct.length, length = curr.id.clock - self.clock)
					if (currWrite.struct.constructor === Skip) {
						// extend existing skip
						currWrite.struct.length = curr.id.clock + curr.length - currWrite.struct.id.clock;
					} else {
						writeStructToLazyStructWriter(lazyStructEncoder, currWrite.struct, currWrite.offset);
						const diff = curr.id.clock - currWrite.struct.id.clock - currWrite.struct.length;
						/**
						 * @type {Skip}
						 */
						const struct = new Skip(createID(firstClient, currWrite.struct.id.clock + currWrite.struct.length), diff);
						currWrite = { struct, offset: 0 };
					}
				} else {
					// if (currWrite.struct.id.clock + currWrite.struct.length >= curr.id.clock) {
					const diff = currWrite.struct.id.clock + currWrite.struct.length - curr.id.clock;
					if (diff > 0) {
						if (currWrite.struct.constructor === Skip) {
							// prefer to slice Skip because the other struct might contain more information
							currWrite.struct.length -= diff;
						} else {
							curr = sliceStruct(curr, diff);
						}
					}
					if (!currWrite.struct.mergeWith(/** @type {any} */ (curr))) {
						writeStructToLazyStructWriter(lazyStructEncoder, currWrite.struct, currWrite.offset);
						currWrite = { struct: curr, offset: 0 };
						currDecoder.next();
					}
				}
			}
		} else {
			currWrite = { struct: /** @type {Item | GC} */ (currDecoder.curr), offset: 0 };
			currDecoder.next();
		}
		for (
			let next = currDecoder.curr;
			next !== null && next.id.client === firstClient && next.id.clock === currWrite.struct.id.clock + currWrite.struct.length && next.constructor !== Skip;
			next = currDecoder.next()
		) {
			writeStructToLazyStructWriter(lazyStructEncoder, currWrite.struct, currWrite.offset);
			currWrite = { struct: next, offset: 0 };
		}
	}
	if (currWrite !== null) {
		writeStructToLazyStructWriter(lazyStructEncoder, currWrite.struct, currWrite.offset);
		currWrite = null;
	}
	finishLazyStructWriting(lazyStructEncoder);

	const dss = updateDecoders.map((decoder) => readDeleteSet(decoder));
	const ds = mergeDeleteSets(dss);
	writeDeleteSet(updateEncoder, ds);
	return updateEncoder.toUint8Array();
};

/**
 * @param {Uint8Array} update
 * @param {Uint8Array} sv
 * @param {typeof UpdateDecoderV1 | typeof UpdateDecoderV2} [YDecoder]
 * @param {typeof UpdateEncoderV1 | typeof UpdateEncoderV2} [YEncoder]
 */
const diffUpdateV2 = (update, sv, YDecoder = UpdateDecoderV2, YEncoder = UpdateEncoderV2) => {
	const state = decodeStateVector(sv);
	const encoder = new YEncoder();
	const lazyStructWriter = new LazyStructWriter(encoder);
	const decoder = new YDecoder(createDecoder(update));
	const reader = new LazyStructReader(decoder, false);
	while (reader.curr) {
		const curr = reader.curr;
		const currClient = curr.id.client;
		const svClock = state.get(currClient) || 0;
		if (reader.curr.constructor === Skip) {
			// the first written struct shouldn't be a skip
			reader.next();
			continue;
		}
		if (curr.id.clock + curr.length > svClock) {
			writeStructToLazyStructWriter(lazyStructWriter, curr, max$1(svClock - curr.id.clock, 0));
			reader.next();
			while (reader.curr && reader.curr.id.client === currClient) {
				writeStructToLazyStructWriter(lazyStructWriter, reader.curr, 0);
				reader.next();
			}
		} else {
			// read until something new comes up
			while (reader.curr && reader.curr.id.client === currClient && reader.curr.id.clock + reader.curr.length <= svClock) {
				reader.next();
			}
		}
	}
	finishLazyStructWriting(lazyStructWriter);
	// write ds
	const ds = readDeleteSet(decoder);
	writeDeleteSet(encoder, ds);
	return encoder.toUint8Array();
};

/**
 * @param {LazyStructWriter} lazyWriter
 */
const flushLazyStructWriter = (lazyWriter) => {
	if (lazyWriter.written > 0) {
		lazyWriter.clientStructs.push({ written: lazyWriter.written, restEncoder: toUint8Array(lazyWriter.encoder.restEncoder) });
		lazyWriter.encoder.restEncoder = createEncoder();
		lazyWriter.written = 0;
	}
};

/**
 * @param {LazyStructWriter} lazyWriter
 * @param {Item | GC} struct
 * @param {number} offset
 */
const writeStructToLazyStructWriter = (lazyWriter, struct, offset) => {
	// flush curr if we start another client
	if (lazyWriter.written > 0 && lazyWriter.currClient !== struct.id.client) {
		flushLazyStructWriter(lazyWriter);
	}
	if (lazyWriter.written === 0) {
		lazyWriter.currClient = struct.id.client;
		// write next client
		lazyWriter.encoder.writeClient(struct.id.client);
		// write startClock
		writeVarUint(lazyWriter.encoder.restEncoder, struct.id.clock + offset);
	}
	struct.write(lazyWriter.encoder, offset);
	lazyWriter.written++;
};
/**
 * Call this function when we collected all parts and want to
 * put all the parts together. After calling this method,
 * you can continue using the UpdateEncoder.
 *
 * @param {LazyStructWriter} lazyWriter
 */
const finishLazyStructWriting = (lazyWriter) => {
	flushLazyStructWriter(lazyWriter);

	// this is a fresh encoder because we called flushCurr
	const restEncoder = lazyWriter.encoder.restEncoder;

	/**
	 * Now we put all the fragments together.
	 * This works similarly to `writeClientsStructs`
	 */

	// write # states that were updated - i.e. the clients
	writeVarUint(restEncoder, lazyWriter.clientStructs.length);

	for (let i = 0; i < lazyWriter.clientStructs.length; i++) {
		const partStructs = lazyWriter.clientStructs[i];
		/**
		 * Works similarly to `writeStructs`
		 */
		// write # encoded structs
		writeVarUint(restEncoder, partStructs.written);
		// write the rest of the fragment
		writeUint8Array(restEncoder, partStructs.restEncoder);
	}
};

/**
 * @param {Uint8Array} update
 * @param {function(Item|GC|Skip):Item|GC|Skip} blockTransformer
 * @param {typeof UpdateDecoderV2 | typeof UpdateDecoderV1} YDecoder
 * @param {typeof UpdateEncoderV2 | typeof UpdateEncoderV1 } YEncoder
 */
const convertUpdateFormat = (update, blockTransformer, YDecoder, YEncoder) => {
	const updateDecoder = new YDecoder(createDecoder(update));
	const lazyDecoder = new LazyStructReader(updateDecoder, false);
	const updateEncoder = new YEncoder();
	const lazyWriter = new LazyStructWriter(updateEncoder);
	for (let curr = lazyDecoder.curr; curr !== null; curr = lazyDecoder.next()) {
		writeStructToLazyStructWriter(lazyWriter, blockTransformer(curr), 0);
	}
	finishLazyStructWriting(lazyWriter);
	const ds = readDeleteSet(updateDecoder);
	writeDeleteSet(updateEncoder, ds);
	return updateEncoder.toUint8Array();
};

/**
 * @param {Uint8Array} update
 */
const convertUpdateFormatV2ToV1 = (update) => convertUpdateFormat(update, id, UpdateDecoderV2, UpdateEncoderV1);

const errorComputeChanges = "You must not compute changes after the event-handler fired.";

/**
 * @template {AbstractType<any>} T
 * YEvent describes the changes on a YType.
 */
class YEvent {
	/**
	 * @param {T} target The changed type.
	 * @param {Transaction} transaction
	 */
	constructor(target, transaction) {
		/**
		 * The type on which this event was created on.
		 * @type {T}
		 */
		this.target = target;
		/**
		 * The current target on which the observe callback is called.
		 * @type {AbstractType<any>}
		 */
		this.currentTarget = target;
		/**
		 * The transaction that triggered this event.
		 * @type {Transaction}
		 */
		this.transaction = transaction;
		/**
		 * @type {Object|null}
		 */
		this._changes = null;
		/**
		 * @type {null | Map<string, { action: 'add' | 'update' | 'delete', oldValue: any }>}
		 */
		this._keys = null;
		/**
		 * @type {null | Array<{ insert?: string | Array<any> | object | AbstractType<any>, retain?: number, delete?: number, attributes?: Object<string, any> }>}
		 */
		this._delta = null;
		/**
		 * @type {Array<string|number>|null}
		 */
		this._path = null;
	}

	/**
	 * Computes the path from `y` to the changed type.
	 *
	 * @todo v14 should standardize on path: Array<{parent, index}> because that is easier to work with.
	 *
	 * The following property holds:
	 * @example
	 *   let type = y
	 *   event.path.forEach(dir => {
	 *     type = type.get(dir)
	 *   })
	 *   type === event.target // => true
	 */
	get path() {
		return this._path || (this._path = getPathTo(this.currentTarget, this.target));
	}

	/**
	 * Check if a struct is deleted by this event.
	 *
	 * In contrast to change.deleted, this method also returns true if the struct was added and then deleted.
	 *
	 * @param {AbstractStruct} struct
	 * @return {boolean}
	 */
	deletes(struct) {
		return isDeleted(this.transaction.deleteSet, struct.id);
	}

	/**
	 * @type {Map<string, { action: 'add' | 'update' | 'delete', oldValue: any }>}
	 */
	get keys() {
		if (this._keys === null) {
			if (this.transaction.doc._transactionCleanups.length === 0) {
				throw create$7(errorComputeChanges);
			}
			const keys = new Map();
			const target = this.target;
			const changed = /** @type Set<string|null> */ (this.transaction.changed.get(target));
			changed.forEach((key) => {
				if (key !== null) {
					const item = /** @type {Item} */ (target._map.get(key));
					/**
					 * @type {'delete' | 'add' | 'update'}
					 */
					let action;
					let oldValue;
					if (this.adds(item)) {
						let prev = item.left;
						while (prev !== null && this.adds(prev)) {
							prev = prev.left;
						}
						if (this.deletes(item)) {
							if (prev !== null && this.deletes(prev)) {
								action = "delete";
								oldValue = last(prev.content.getContent());
							} else {
								return;
							}
						} else {
							if (prev !== null && this.deletes(prev)) {
								action = "update";
								oldValue = last(prev.content.getContent());
							} else {
								action = "add";
								oldValue = undefined;
							}
						}
					} else {
						if (this.deletes(item)) {
							action = "delete";
							oldValue = last(/** @type {Item} */ item.content.getContent());
						} else {
							return; // nop
						}
					}
					keys.set(key, { action, oldValue });
				}
			});
			this._keys = keys;
		}
		return this._keys;
	}

	/**
	 * This is a computed property. Note that this can only be safely computed during the
	 * event call. Computing this property after other changes happened might result in
	 * unexpected behavior (incorrect computation of deltas). A safe way to collect changes
	 * is to store the `changes` or the `delta` object. Avoid storing the `transaction` object.
	 *
	 * @type {Array<{insert?: string | Array<any> | object | AbstractType<any>, retain?: number, delete?: number, attributes?: Object<string, any>}>}
	 */
	get delta() {
		return this.changes.delta;
	}

	/**
	 * Check if a struct is added by this event.
	 *
	 * In contrast to change.deleted, this method also returns true if the struct was added and then deleted.
	 *
	 * @param {AbstractStruct} struct
	 * @return {boolean}
	 */
	adds(struct) {
		return struct.id.clock >= (this.transaction.beforeState.get(struct.id.client) || 0);
	}

	/**
	 * This is a computed property. Note that this can only be safely computed during the
	 * event call. Computing this property after other changes happened might result in
	 * unexpected behavior (incorrect computation of deltas). A safe way to collect changes
	 * is to store the `changes` or the `delta` object. Avoid storing the `transaction` object.
	 *
	 * @type {{added:Set<Item>,deleted:Set<Item>,keys:Map<string,{action:'add'|'update'|'delete',oldValue:any}>,delta:Array<{insert?:Array<any>|string, delete?:number, retain?:number}>}}
	 */
	get changes() {
		let changes = this._changes;
		if (changes === null) {
			if (this.transaction.doc._transactionCleanups.length === 0) {
				throw create$7(errorComputeChanges);
			}
			const target = this.target;
			const added = create$8();
			const deleted = create$8();
			/**
			 * @type {Array<{insert:Array<any>}|{delete:number}|{retain:number}>}
			 */
			const delta = [];
			changes = {
				added,
				deleted,
				delta,
				keys: this.keys,
			};
			const changed = /** @type Set<string|null> */ (this.transaction.changed.get(target));
			if (changed.has(null)) {
				/**
				 * @type {any}
				 */
				let lastOp = null;
				const packOp = () => {
					if (lastOp) {
						delta.push(lastOp);
					}
				};
				for (let item = target._start; item !== null; item = item.right) {
					if (item.deleted) {
						if (this.deletes(item) && !this.adds(item)) {
							if (lastOp === null || lastOp.delete === undefined) {
								packOp();
								lastOp = { delete: 0 };
							}
							lastOp.delete += item.length;
							deleted.add(item);
						} // else nop
					} else {
						if (this.adds(item)) {
							if (lastOp === null || lastOp.insert === undefined) {
								packOp();
								lastOp = { insert: [] };
							}
							lastOp.insert = lastOp.insert.concat(item.content.getContent());
							added.add(item);
						} else {
							if (lastOp === null || lastOp.retain === undefined) {
								packOp();
								lastOp = { retain: 0 };
							}
							lastOp.retain += item.length;
						}
					}
				}
				if (lastOp !== null && lastOp.retain === undefined) {
					packOp();
				}
			}
			this._changes = changes;
		}
		return /** @type {any} */ (changes);
	}
}

/**
 * Compute the path from this type to the specified target.
 *
 * @example
 *   // `child` should be accessible via `type.get(path[0]).get(path[1])..`
 *   const path = type.getPathTo(child)
 *   // assuming `type instanceof YArray`
 *   console.log(path) // might look like => [2, 'key1']
 *   child === type.get(path[0]).get(path[1])
 *
 * @param {AbstractType<any>} parent
 * @param {AbstractType<any>} child target
 * @return {Array<string|number>} Path to the target
 *
 * @private
 * @function
 */
const getPathTo = (parent, child) => {
	const path = [];
	while (child._item !== null && child !== parent) {
		if (child._item.parentSub !== null) {
			// parent is map-ish
			path.unshift(child._item.parentSub);
		} else {
			// parent is array-ish
			let i = 0;
			let c = /** @type {AbstractType<any>} */ (child._item.parent)._start;
			while (c !== child._item && c !== null) {
				if (!c.deleted && c.countable) {
					i += c.length;
				}
				c = c.right;
			}
			path.unshift(i);
		}
		child = /** @type {AbstractType<any>} */ (child._item.parent);
	}
	return path;
};

/**
 * https://docs.yjs.dev/getting-started/working-with-shared-types#caveats
 */
const warnPrematureAccess = () => {
	warn("Invalid access: Add Yjs type to a document before reading data.");
};

const maxSearchMarker = 80;

/**
 * A unique timestamp that identifies each marker.
 *
 * Time is relative,.. this is more like an ever-increasing clock.
 *
 * @type {number}
 */
let globalSearchMarkerTimestamp = 0;

class ArraySearchMarker {
	/**
	 * @param {Item} p
	 * @param {number} index
	 */
	constructor(p, index) {
		p.marker = true;
		this.p = p;
		this.index = index;
		this.timestamp = globalSearchMarkerTimestamp++;
	}
}

/**
 * @param {ArraySearchMarker} marker
 */
const refreshMarkerTimestamp = (marker) => {
	marker.timestamp = globalSearchMarkerTimestamp++;
};

/**
 * This is rather complex so this function is the only thing that should overwrite a marker
 *
 * @param {ArraySearchMarker} marker
 * @param {Item} p
 * @param {number} index
 */
const overwriteMarker = (marker, p, index) => {
	marker.p.marker = false;
	marker.p = p;
	p.marker = true;
	marker.index = index;
	marker.timestamp = globalSearchMarkerTimestamp++;
};

/**
 * @param {Array<ArraySearchMarker>} searchMarker
 * @param {Item} p
 * @param {number} index
 */
const markPosition = (searchMarker, p, index) => {
	if (searchMarker.length >= maxSearchMarker) {
		// override oldest marker (we don't want to create more objects)
		const marker = searchMarker.reduce((a, b) => (a.timestamp < b.timestamp ? a : b));
		overwriteMarker(marker, p, index);
		return marker;
	} else {
		// create new marker
		const pm = new ArraySearchMarker(p, index);
		searchMarker.push(pm);
		return pm;
	}
};

/**
 * Search marker help us to find positions in the associative array faster.
 *
 * They speed up the process of finding a position without much bookkeeping.
 *
 * A maximum of `maxSearchMarker` objects are created.
 *
 * This function always returns a refreshed marker (updated timestamp)
 *
 * @param {AbstractType<any>} yarray
 * @param {number} index
 */
const findMarker = (yarray, index) => {
	if (yarray._start === null || index === 0 || yarray._searchMarker === null) {
		return null;
	}
	const marker = yarray._searchMarker.length === 0 ? null : yarray._searchMarker.reduce((a, b) => (abs(index - a.index) < abs(index - b.index) ? a : b));
	let p = yarray._start;
	let pindex = 0;
	if (marker !== null) {
		p = marker.p;
		pindex = marker.index;
		refreshMarkerTimestamp(marker); // we used it, we might need to use it again
	}
	// iterate to right if possible
	while (p.right !== null && pindex < index) {
		if (!p.deleted && p.countable) {
			if (index < pindex + p.length) {
				break;
			}
			pindex += p.length;
		}
		p = p.right;
	}
	// iterate to left if necessary (might be that pindex > index)
	while (p.left !== null && pindex > index) {
		p = p.left;
		if (!p.deleted && p.countable) {
			pindex -= p.length;
		}
	}
	// we want to make sure that p can't be merged with left, because that would screw up everything
	// in that cas just return what we have (it is most likely the best marker anyway)
	// iterate to left until p can't be merged with left
	while (p.left !== null && p.left.id.client === p.id.client && p.left.id.clock + p.left.length === p.id.clock) {
		p = p.left;
		if (!p.deleted && p.countable) {
			pindex -= p.length;
		}
	}

	// @todo remove!
	// assure position
	// {
	//   let start = yarray._start
	//   let pos = 0
	//   while (start !== p) {
	//     if (!start.deleted && start.countable) {
	//       pos += start.length
	//     }
	//     start = /** @type {Item} */ (start.right)
	//   }
	//   if (pos !== pindex) {
	//     debugger
	//     throw new Error('Gotcha position fail!')
	//   }
	// }
	// if (marker) {
	//   if (window.lengths == null) {
	//     window.lengths = []
	//     window.getLengths = () => window.lengths.sort((a, b) => a - b)
	//   }
	//   window.lengths.push(marker.index - pindex)
	//   console.log('distance', marker.index - pindex, 'len', p && p.parent.length)
	// }
	if (marker !== null && abs(marker.index - pindex) < /** @type {YText|YArray<any>} */ (p.parent).length / maxSearchMarker) {
		// adjust existing marker
		overwriteMarker(marker, p, pindex);
		return marker;
	} else {
		// create new marker
		return markPosition(yarray._searchMarker, p, pindex);
	}
};

/**
 * Update markers when a change happened.
 *
 * This should be called before doing a deletion!
 *
 * @param {Array<ArraySearchMarker>} searchMarker
 * @param {number} index
 * @param {number} len If insertion, len is positive. If deletion, len is negative.
 */
const updateMarkerChanges = (searchMarker, index, len) => {
	for (let i = searchMarker.length - 1; i >= 0; i--) {
		const m = searchMarker[i];
		if (len > 0) {
			/**
			 * @type {Item|null}
			 */
			let p = m.p;
			p.marker = false;
			// Ideally we just want to do a simple position comparison, but this will only work if
			// search markers don't point to deleted items for formats.
			// Iterate marker to prev undeleted countable position so we know what to do when updating a position
			while (p && (p.deleted || !p.countable)) {
				p = p.left;
				if (p && !p.deleted && p.countable) {
					// adjust position. the loop should break now
					m.index -= p.length;
				}
			}
			if (p === null || p.marker === true) {
				// remove search marker if updated position is null or if position is already marked
				searchMarker.splice(i, 1);
				continue;
			}
			m.p = p;
			p.marker = true;
		}
		if (index < m.index || (len > 0 && index === m.index)) {
			// a simple index <= m.index check would actually suffice
			m.index = max$1(index, m.index + len);
		}
	}
};

/**
 * Call event listeners with an event. This will also add an event to all
 * parents (for `.observeDeep` handlers).
 *
 * @template EventType
 * @param {AbstractType<EventType>} type
 * @param {Transaction} transaction
 * @param {EventType} event
 */
const callTypeObservers = (type, transaction, event) => {
	const changedType = type;
	const changedParentTypes = transaction.changedParentTypes;
	while (true) {
		// @ts-ignore
		setIfUndefined(changedParentTypes, type, () => []).push(event);
		if (type._item === null) {
			break;
		}
		type = /** @type {AbstractType<any>} */ (type._item.parent);
	}
	callEventHandlerListeners(changedType._eH, event, transaction);
};

/**
 * @template EventType
 * Abstract Yjs Type class
 */
class AbstractType {
	constructor() {
		/**
		 * @type {Item|null}
		 */
		this._item = null;
		/**
		 * @type {Map<string,Item>}
		 */
		this._map = new Map();
		/**
		 * @type {Item|null}
		 */
		this._start = null;
		/**
		 * @type {Doc|null}
		 */
		this.doc = null;
		this._length = 0;
		/**
		 * Event handlers
		 * @type {EventHandler<EventType,Transaction>}
		 */
		this._eH = createEventHandler();
		/**
		 * Deep event handlers
		 * @type {EventHandler<Array<YEvent<any>>,Transaction>}
		 */
		this._dEH = createEventHandler();
		/**
		 * @type {null | Array<ArraySearchMarker>}
		 */
		this._searchMarker = null;
	}

	/**
	 * @return {AbstractType<any>|null}
	 */
	get parent() {
		return this._item ? /** @type {AbstractType<any>} */ (this._item.parent) : null;
	}

	/**
	 * Integrate this type into the Yjs instance.
	 *
	 * * Save this struct in the os
	 * * This type is sent to other client
	 * * Observer functions are fired
	 *
	 * @param {Doc} y The Yjs instance
	 * @param {Item|null} item
	 */
	_integrate(y, item) {
		this.doc = y;
		this._item = item;
	}

	/**
	 * @return {AbstractType<EventType>}
	 */
	_copy() {
		throw methodUnimplemented();
	}

	/**
	 * Makes a copy of this data type that can be included somewhere else.
	 *
	 * Note that the content is only readable _after_ it has been included somewhere in the Ydoc.
	 *
	 * @return {AbstractType<EventType>}
	 */
	clone() {
		throw methodUnimplemented();
	}

	/**
	 * @param {UpdateEncoderV1 | UpdateEncoderV2} _encoder
	 */
	_write(_encoder) {}

	/**
	 * The first non-deleted item
	 */
	get _first() {
		let n = this._start;
		while (n !== null && n.deleted) {
			n = n.right;
		}
		return n;
	}

	/**
	 * Creates YEvent and calls all type observers.
	 * Must be implemented by each type.
	 *
	 * @param {Transaction} transaction
	 * @param {Set<null|string>} _parentSubs Keys changed on this type. `null` if list was modified.
	 */
	_callObserver(transaction, _parentSubs) {
		if (!transaction.local && this._searchMarker) {
			this._searchMarker.length = 0;
		}
	}

	/**
	 * Observe all events that are created on this type.
	 *
	 * @param {function(EventType, Transaction):void} f Observer function
	 */
	observe(f) {
		addEventHandlerListener(this._eH, f);
	}

	/**
	 * Observe all events that are created by this type and its children.
	 *
	 * @param {function(Array<YEvent<any>>,Transaction):void} f Observer function
	 */
	observeDeep(f) {
		addEventHandlerListener(this._dEH, f);
	}

	/**
	 * Unregister an observer function.
	 *
	 * @param {function(EventType,Transaction):void} f Observer function
	 */
	unobserve(f) {
		removeEventHandlerListener(this._eH, f);
	}

	/**
	 * Unregister an observer function.
	 *
	 * @param {function(Array<YEvent<any>>,Transaction):void} f Observer function
	 */
	unobserveDeep(f) {
		removeEventHandlerListener(this._dEH, f);
	}

	/**
	 * @abstract
	 * @return {any}
	 */
	toJSON() {}
}

/**
 * @param {AbstractType<any>} type
 * @param {number} start
 * @param {number} end
 * @return {Array<any>}
 *
 * @private
 * @function
 */
const typeListSlice = (type, start, end) => {
	type.doc ?? warnPrematureAccess();
	if (start < 0) {
		start = type._length + start;
	}
	if (end < 0) {
		end = type._length + end;
	}
	let len = end - start;
	const cs = [];
	let n = type._start;
	while (n !== null && len > 0) {
		if (n.countable && !n.deleted) {
			const c = n.content.getContent();
			if (c.length <= start) {
				start -= c.length;
			} else {
				for (let i = start; i < c.length && len > 0; i++) {
					cs.push(c[i]);
					len--;
				}
				start = 0;
			}
		}
		n = n.right;
	}
	return cs;
};

/**
 * @param {AbstractType<any>} type
 * @return {Array<any>}
 *
 * @private
 * @function
 */
const typeListToArray = (type) => {
	type.doc ?? warnPrematureAccess();
	const cs = [];
	let n = type._start;
	while (n !== null) {
		if (n.countable && !n.deleted) {
			const c = n.content.getContent();
			for (let i = 0; i < c.length; i++) {
				cs.push(c[i]);
			}
		}
		n = n.right;
	}
	return cs;
};

/**
 * @param {AbstractType<any>} type
 * @param {Snapshot} snapshot
 * @return {Array<any>}
 *
 * @private
 * @function
 */
const typeListToArraySnapshot = (type, snapshot) => {
	const cs = [];
	let n = type._start;
	while (n !== null) {
		if (n.countable && isVisible$1(n, snapshot)) {
			const c = n.content.getContent();
			for (let i = 0; i < c.length; i++) {
				cs.push(c[i]);
			}
		}
		n = n.right;
	}
	return cs;
};

/**
 * Executes a provided function on once on every element of this YArray.
 *
 * @param {AbstractType<any>} type
 * @param {function(any,number,any):void} f A function to execute on every element of this YArray.
 *
 * @private
 * @function
 */
const typeListForEach = (type, f) => {
	let index = 0;
	let n = type._start;
	type.doc ?? warnPrematureAccess();
	while (n !== null) {
		if (n.countable && !n.deleted) {
			const c = n.content.getContent();
			for (let i = 0; i < c.length; i++) {
				f(c[i], index++, type);
			}
		}
		n = n.right;
	}
};

/**
 * @template C,R
 * @param {AbstractType<any>} type
 * @param {function(C,number,AbstractType<any>):R} f
 * @return {Array<R>}
 *
 * @private
 * @function
 */
const typeListMap = (type, f) => {
	/**
	 * @type {Array<any>}
	 */
	const result = [];
	typeListForEach(type, (c, i) => {
		result.push(f(c, i, type));
	});
	return result;
};

/**
 * @param {AbstractType<any>} type
 * @return {IterableIterator<any>}
 *
 * @private
 * @function
 */
const typeListCreateIterator = (type) => {
	let n = type._start;
	/**
	 * @type {Array<any>|null}
	 */
	let currentContent = null;
	let currentContentIndex = 0;
	return {
		[Symbol.iterator]() {
			return this;
		},
		next: () => {
			// find some content
			if (currentContent === null) {
				while (n !== null && n.deleted) {
					n = n.right;
				}
				// check if we reached the end, no need to check currentContent, because it does not exist
				if (n === null) {
					return {
						done: true,
						value: undefined,
					};
				}
				// we found n, so we can set currentContent
				currentContent = n.content.getContent();
				currentContentIndex = 0;
				n = n.right; // we used the content of n, now iterate to next
			}
			const value = currentContent[currentContentIndex++];
			// check if we need to empty currentContent
			if (currentContent.length <= currentContentIndex) {
				currentContent = null;
			}
			return {
				done: false,
				value,
			};
		},
	};
};

/**
 * @param {AbstractType<any>} type
 * @param {number} index
 * @return {any}
 *
 * @private
 * @function
 */
const typeListGet = (type, index) => {
	type.doc ?? warnPrematureAccess();
	const marker = findMarker(type, index);
	let n = type._start;
	if (marker !== null) {
		n = marker.p;
		index -= marker.index;
	}
	for (; n !== null; n = n.right) {
		if (!n.deleted && n.countable) {
			if (index < n.length) {
				return n.content.getContent()[index];
			}
			index -= n.length;
		}
	}
};

/**
 * @param {Transaction} transaction
 * @param {AbstractType<any>} parent
 * @param {Item?} referenceItem
 * @param {Array<Object<string,any>|Array<any>|boolean|number|null|string|Uint8Array>} content
 *
 * @private
 * @function
 */
const typeListInsertGenericsAfter = (transaction, parent, referenceItem, content) => {
	let left = referenceItem;
	const doc = transaction.doc;
	const ownClientId = doc.clientID;
	const store = doc.store;
	const right = referenceItem === null ? parent._start : referenceItem.right;
	/**
	 * @type {Array<Object|Array<any>|number|null>}
	 */
	let jsonContent = [];
	const packJsonContent = () => {
		if (jsonContent.length > 0) {
			left = new Item$1(createID(ownClientId, getState(store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, new ContentAny(jsonContent));
			left.integrate(transaction, 0);
			jsonContent = [];
		}
	};
	content.forEach((c) => {
		if (c === null) {
			jsonContent.push(c);
		} else {
			switch (c.constructor) {
				case Number:
				case Object:
				case Boolean:
				case Array:
				case String:
					jsonContent.push(c);
					break;
				default:
					packJsonContent();
					switch (c.constructor) {
						case Uint8Array:
						case ArrayBuffer:
							left = new Item$1(
								createID(ownClientId, getState(store, ownClientId)),
								left,
								left && left.lastId,
								right,
								right && right.id,
								parent,
								null,
								new ContentBinary(new Uint8Array(/** @type {Uint8Array} */ (c))),
							);
							left.integrate(transaction, 0);
							break;
						case Doc:
							left = new Item$1(
								createID(ownClientId, getState(store, ownClientId)),
								left,
								left && left.lastId,
								right,
								right && right.id,
								parent,
								null,
								new ContentDoc(/** @type {Doc} */ (c)),
							);
							left.integrate(transaction, 0);
							break;
						default:
							if (c instanceof AbstractType) {
								left = new Item$1(createID(ownClientId, getState(store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, new ContentType(c));
								left.integrate(transaction, 0);
							} else {
								throw new Error("Unexpected content type in insert operation");
							}
					}
			}
		}
	});
	packJsonContent();
};

const lengthExceeded = () => create$7("Length exceeded!");

/**
 * @param {Transaction} transaction
 * @param {AbstractType<any>} parent
 * @param {number} index
 * @param {Array<Object<string,any>|Array<any>|number|null|string|Uint8Array>} content
 *
 * @private
 * @function
 */
const typeListInsertGenerics = (transaction, parent, index, content) => {
	if (index > parent._length) {
		throw lengthExceeded();
	}
	if (index === 0) {
		if (parent._searchMarker) {
			updateMarkerChanges(parent._searchMarker, index, content.length);
		}
		return typeListInsertGenericsAfter(transaction, parent, null, content);
	}
	const startIndex = index;
	const marker = findMarker(parent, index);
	let n = parent._start;
	if (marker !== null) {
		n = marker.p;
		index -= marker.index;
		// we need to iterate one to the left so that the algorithm works
		if (index === 0) {
			// @todo refactor this as it actually doesn't consider formats
			n = n.prev; // important! get the left undeleted item so that we can actually decrease index
			index += n && n.countable && !n.deleted ? n.length : 0;
		}
	}
	for (; n !== null; n = n.right) {
		if (!n.deleted && n.countable) {
			if (index <= n.length) {
				if (index < n.length) {
					// insert in-between
					getItemCleanStart(transaction, createID(n.id.client, n.id.clock + index));
				}
				break;
			}
			index -= n.length;
		}
	}
	if (parent._searchMarker) {
		updateMarkerChanges(parent._searchMarker, startIndex, content.length);
	}
	return typeListInsertGenericsAfter(transaction, parent, n, content);
};

/**
 * Pushing content is special as we generally want to push after the last item. So we don't have to update
 * the search marker.
 *
 * @param {Transaction} transaction
 * @param {AbstractType<any>} parent
 * @param {Array<Object<string,any>|Array<any>|number|null|string|Uint8Array>} content
 *
 * @private
 * @function
 */
const typeListPushGenerics = (transaction, parent, content) => {
	// Use the marker with the highest index and iterate to the right.
	const marker = (parent._searchMarker || []).reduce((maxMarker, currMarker) => (currMarker.index > maxMarker.index ? currMarker : maxMarker), { index: 0, p: parent._start });
	let n = marker.p;
	if (n) {
		while (n.right) {
			n = n.right;
		}
	}
	return typeListInsertGenericsAfter(transaction, parent, n, content);
};

/**
 * @param {Transaction} transaction
 * @param {AbstractType<any>} parent
 * @param {number} index
 * @param {number} length
 *
 * @private
 * @function
 */
const typeListDelete = (transaction, parent, index, length) => {
	if (length === 0) {
		return;
	}
	const startIndex = index;
	const startLength = length;
	const marker = findMarker(parent, index);
	let n = parent._start;
	if (marker !== null) {
		n = marker.p;
		index -= marker.index;
	}
	// compute the first item to be deleted
	for (; n !== null && index > 0; n = n.right) {
		if (!n.deleted && n.countable) {
			if (index < n.length) {
				getItemCleanStart(transaction, createID(n.id.client, n.id.clock + index));
			}
			index -= n.length;
		}
	}
	// delete all items until done
	while (length > 0 && n !== null) {
		if (!n.deleted) {
			if (length < n.length) {
				getItemCleanStart(transaction, createID(n.id.client, n.id.clock + length));
			}
			n.delete(transaction);
			length -= n.length;
		}
		n = n.right;
	}
	if (length > 0) {
		throw lengthExceeded();
	}
	if (parent._searchMarker) {
		updateMarkerChanges(parent._searchMarker, startIndex, -startLength + length /* in case we remove the above exception */);
	}
};

/**
 * @param {Transaction} transaction
 * @param {AbstractType<any>} parent
 * @param {string} key
 *
 * @private
 * @function
 */
const typeMapDelete = (transaction, parent, key) => {
	const c = parent._map.get(key);
	if (c !== undefined) {
		c.delete(transaction);
	}
};

/**
 * @param {Transaction} transaction
 * @param {AbstractType<any>} parent
 * @param {string} key
 * @param {Object|number|null|Array<any>|string|Uint8Array|AbstractType<any>} value
 *
 * @private
 * @function
 */
const typeMapSet = (transaction, parent, key, value) => {
	const left = parent._map.get(key) || null;
	const doc = transaction.doc;
	const ownClientId = doc.clientID;
	let content;
	if (value == null) {
		content = new ContentAny([value]);
	} else {
		switch (value.constructor) {
			case Number:
			case Object:
			case Boolean:
			case Array:
			case String:
			case Date:
			case BigInt:
				content = new ContentAny([value]);
				break;
			case Uint8Array:
				content = new ContentBinary(/** @type {Uint8Array} */ (value));
				break;
			case Doc:
				content = new ContentDoc(/** @type {Doc} */ (value));
				break;
			default:
				if (value instanceof AbstractType) {
					content = new ContentType(value);
				} else {
					throw new Error("Unexpected content type");
				}
		}
	}
	new Item$1(createID(ownClientId, getState(doc.store, ownClientId)), left, left && left.lastId, null, null, parent, key, content).integrate(transaction, 0);
};

/**
 * @param {AbstractType<any>} parent
 * @param {string} key
 * @return {Object<string,any>|number|null|Array<any>|string|Uint8Array|AbstractType<any>|undefined}
 *
 * @private
 * @function
 */
const typeMapGet = (parent, key) => {
	parent.doc ?? warnPrematureAccess();
	const val = parent._map.get(key);
	return val !== undefined && !val.deleted ? val.content.getContent()[val.length - 1] : undefined;
};

/**
 * @param {AbstractType<any>} parent
 * @return {Object<string,Object<string,any>|number|null|Array<any>|string|Uint8Array|AbstractType<any>|undefined>}
 *
 * @private
 * @function
 */
const typeMapGetAll = (parent) => {
	/**
	 * @type {Object<string,any>}
	 */
	const res = {};
	parent.doc ?? warnPrematureAccess();
	parent._map.forEach((value, key) => {
		if (!value.deleted) {
			res[key] = value.content.getContent()[value.length - 1];
		}
	});
	return res;
};

/**
 * @param {AbstractType<any>} parent
 * @param {string} key
 * @return {boolean}
 *
 * @private
 * @function
 */
const typeMapHas = (parent, key) => {
	parent.doc ?? warnPrematureAccess();
	const val = parent._map.get(key);
	return val !== undefined && !val.deleted;
};

/**
 * @param {AbstractType<any>} parent
 * @param {Snapshot} snapshot
 * @return {Object<string,Object<string,any>|number|null|Array<any>|string|Uint8Array|AbstractType<any>|undefined>}
 *
 * @private
 * @function
 */
const typeMapGetAllSnapshot = (parent, snapshot) => {
	/**
	 * @type {Object<string,any>}
	 */
	const res = {};
	parent._map.forEach((value, key) => {
		/**
		 * @type {Item|null}
		 */
		let v = value;
		while (v !== null && (!snapshot.sv.has(v.id.client) || v.id.clock >= (snapshot.sv.get(v.id.client) || 0))) {
			v = v.left;
		}
		if (v !== null && isVisible$1(v, snapshot)) {
			res[key] = v.content.getContent()[v.length - 1];
		}
	});
	return res;
};

/**
 * @param {AbstractType<any> & { _map: Map<string, Item> }} type
 * @return {IterableIterator<Array<any>>}
 *
 * @private
 * @function
 */
const createMapIterator = (type) => {
	type.doc ?? warnPrematureAccess();
	return iteratorFilter(type._map.entries(), /** @param {any} entry */ (entry) => !entry[1].deleted);
};

/**
 * @module YArray
 */

/**
 * Event that describes the changes on a YArray
 * @template T
 * @extends YEvent<YArray<T>>
 */
class YArrayEvent extends YEvent {}

/**
 * A shared Array implementation.
 * @template T
 * @extends AbstractType<YArrayEvent<T>>
 * @implements {Iterable<T>}
 */
class YArray extends AbstractType {
	constructor() {
		super();
		/**
		 * @type {Array<any>?}
		 * @private
		 */
		this._prelimContent = [];
		/**
		 * @type {Array<ArraySearchMarker>}
		 */
		this._searchMarker = [];
	}

	/**
	 * Construct a new YArray containing the specified items.
	 * @template {Object<string,any>|Array<any>|number|null|string|Uint8Array} T
	 * @param {Array<T>} items
	 * @return {YArray<T>}
	 */
	static from(items) {
		/**
		 * @type {YArray<T>}
		 */
		const a = new YArray();
		a.push(items);
		return a;
	}

	/**
	 * Integrate this type into the Yjs instance.
	 *
	 * * Save this struct in the os
	 * * This type is sent to other client
	 * * Observer functions are fired
	 *
	 * @param {Doc} y The Yjs instance
	 * @param {Item} item
	 */
	_integrate(y, item) {
		super._integrate(y, item);
		this.insert(0, /** @type {Array<any>} */ (this._prelimContent));
		this._prelimContent = null;
	}

	/**
	 * @return {YArray<T>}
	 */
	_copy() {
		return new YArray();
	}

	/**
	 * Makes a copy of this data type that can be included somewhere else.
	 *
	 * Note that the content is only readable _after_ it has been included somewhere in the Ydoc.
	 *
	 * @return {YArray<T>}
	 */
	clone() {
		/**
		 * @type {YArray<T>}
		 */
		const arr = new YArray();
		arr.insert(
			0,
			this.toArray().map((el) => (el instanceof AbstractType ? /** @type {typeof el} */ (el.clone()) : el)),
		);
		return arr;
	}

	get length() {
		this.doc ?? warnPrematureAccess();
		return this._length;
	}

	/**
	 * Creates YArrayEvent and calls observers.
	 *
	 * @param {Transaction} transaction
	 * @param {Set<null|string>} parentSubs Keys changed on this type. `null` if list was modified.
	 */
	_callObserver(transaction, parentSubs) {
		super._callObserver(transaction, parentSubs);
		callTypeObservers(this, transaction, new YArrayEvent(this, transaction));
	}

	/**
	 * Inserts new content at an index.
	 *
	 * Important: This function expects an array of content. Not just a content
	 * object. The reason for this "weirdness" is that inserting several elements
	 * is very efficient when it is done as a single operation.
	 *
	 * @example
	 *  // Insert character 'a' at position 0
	 *  yarray.insert(0, ['a'])
	 *  // Insert numbers 1, 2 at position 1
	 *  yarray.insert(1, [1, 2])
	 *
	 * @param {number} index The index to insert content at.
	 * @param {Array<T>} content The array of content
	 */
	insert(index, content) {
		if (this.doc !== null) {
			transact(this.doc, (transaction) => {
				typeListInsertGenerics(transaction, this, index, /** @type {any} */ (content));
			});
		} else {
			/** @type {Array<any>} */ (this._prelimContent).splice(index, 0, ...content);
		}
	}

	/**
	 * Appends content to this YArray.
	 *
	 * @param {Array<T>} content Array of content to append.
	 *
	 * @todo Use the following implementation in all types.
	 */
	push(content) {
		if (this.doc !== null) {
			transact(this.doc, (transaction) => {
				typeListPushGenerics(transaction, this, /** @type {any} */ (content));
			});
		} else {
			/** @type {Array<any>} */ (this._prelimContent).push(...content);
		}
	}

	/**
	 * Prepends content to this YArray.
	 *
	 * @param {Array<T>} content Array of content to prepend.
	 */
	unshift(content) {
		this.insert(0, content);
	}

	/**
	 * Deletes elements starting from an index.
	 *
	 * @param {number} index Index at which to start deleting elements
	 * @param {number} length The number of elements to remove. Defaults to 1.
	 */
	delete(index, length = 1) {
		if (this.doc !== null) {
			transact(this.doc, (transaction) => {
				typeListDelete(transaction, this, index, length);
			});
		} else {
			/** @type {Array<any>} */ (this._prelimContent).splice(index, length);
		}
	}

	/**
	 * Returns the i-th element from a YArray.
	 *
	 * @param {number} index The index of the element to return from the YArray
	 * @return {T}
	 */
	get(index) {
		return typeListGet(this, index);
	}

	/**
	 * Transforms this YArray to a JavaScript Array.
	 *
	 * @return {Array<T>}
	 */
	toArray() {
		return typeListToArray(this);
	}

	/**
	 * Returns a portion of this YArray into a JavaScript Array selected
	 * from start to end (end not included).
	 *
	 * @param {number} [start]
	 * @param {number} [end]
	 * @return {Array<T>}
	 */
	slice(start = 0, end = this.length) {
		return typeListSlice(this, start, end);
	}

	/**
	 * Transforms this Shared Type to a JSON object.
	 *
	 * @return {Array<any>}
	 */
	toJSON() {
		return this.map((c) => (c instanceof AbstractType ? c.toJSON() : c));
	}

	/**
	 * Returns an Array with the result of calling a provided function on every
	 * element of this YArray.
	 *
	 * @template M
	 * @param {function(T,number,YArray<T>):M} f Function that produces an element of the new Array
	 * @return {Array<M>} A new array with each element being the result of the
	 *                 callback function
	 */
	map(f) {
		return typeListMap(this, /** @type {any} */ (f));
	}

	/**
	 * Executes a provided function once on every element of this YArray.
	 *
	 * @param {function(T,number,YArray<T>):void} f A function to execute on every element of this YArray.
	 */
	forEach(f) {
		typeListForEach(this, f);
	}

	/**
	 * @return {IterableIterator<T>}
	 */
	[Symbol.iterator]() {
		return typeListCreateIterator(this);
	}

	/**
	 * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
	 */
	_write(encoder) {
		encoder.writeTypeRef(YArrayRefID);
	}
}

/**
 * @param {UpdateDecoderV1 | UpdateDecoderV2} _decoder
 *
 * @private
 * @function
 */
const readYArray = (_decoder) => new YArray();

/**
 * @module YMap
 */

/**
 * @template T
 * @extends YEvent<YMap<T>>
 * Event that describes the changes on a YMap.
 */
class YMapEvent extends YEvent {
	/**
	 * @param {YMap<T>} ymap The YArray that changed.
	 * @param {Transaction} transaction
	 * @param {Set<any>} subs The keys that changed.
	 */
	constructor(ymap, transaction, subs) {
		super(ymap, transaction);
		this.keysChanged = subs;
	}
}

/**
 * @template MapType
 * A shared Map implementation.
 *
 * @extends AbstractType<YMapEvent<MapType>>
 * @implements {Iterable<[string, MapType]>}
 */
class YMap extends AbstractType {
	/**
	 *
	 * @param {Iterable<readonly [string, any]>=} entries - an optional iterable to initialize the YMap
	 */
	constructor(entries) {
		super();
		/**
		 * @type {Map<string,any>?}
		 * @private
		 */
		this._prelimContent = null;

		if (entries === undefined) {
			this._prelimContent = new Map();
		} else {
			this._prelimContent = new Map(entries);
		}
	}

	/**
	 * Integrate this type into the Yjs instance.
	 *
	 * * Save this struct in the os
	 * * This type is sent to other client
	 * * Observer functions are fired
	 *
	 * @param {Doc} y The Yjs instance
	 * @param {Item} item
	 */
	_integrate(y, item) {
		super._integrate(y, item);
		/** @type {Map<string, any>} */ (this._prelimContent).forEach((value, key) => {
			this.set(key, value);
		});
		this._prelimContent = null;
	}

	/**
	 * @return {YMap<MapType>}
	 */
	_copy() {
		return new YMap();
	}

	/**
	 * Makes a copy of this data type that can be included somewhere else.
	 *
	 * Note that the content is only readable _after_ it has been included somewhere in the Ydoc.
	 *
	 * @return {YMap<MapType>}
	 */
	clone() {
		/**
		 * @type {YMap<MapType>}
		 */
		const map = new YMap();
		this.forEach((value, key) => {
			map.set(key, value instanceof AbstractType ? /** @type {typeof value} */ (value.clone()) : value);
		});
		return map;
	}

	/**
	 * Creates YMapEvent and calls observers.
	 *
	 * @param {Transaction} transaction
	 * @param {Set<null|string>} parentSubs Keys changed on this type. `null` if list was modified.
	 */
	_callObserver(transaction, parentSubs) {
		callTypeObservers(this, transaction, new YMapEvent(this, transaction, parentSubs));
	}

	/**
	 * Transforms this Shared Type to a JSON object.
	 *
	 * @return {Object<string,any>}
	 */
	toJSON() {
		this.doc ?? warnPrematureAccess();
		/**
		 * @type {Object<string,MapType>}
		 */
		const map = {};
		this._map.forEach((item, key) => {
			if (!item.deleted) {
				const v = item.content.getContent()[item.length - 1];
				map[key] = v instanceof AbstractType ? v.toJSON() : v;
			}
		});
		return map;
	}

	/**
	 * Returns the size of the YMap (count of key/value pairs)
	 *
	 * @return {number}
	 */
	get size() {
		return [...createMapIterator(this)].length;
	}

	/**
	 * Returns the keys for each element in the YMap Type.
	 *
	 * @return {IterableIterator<string>}
	 */
	keys() {
		return iteratorMap(createMapIterator(this), /** @param {any} v */ (v) => v[0]);
	}

	/**
	 * Returns the values for each element in the YMap Type.
	 *
	 * @return {IterableIterator<MapType>}
	 */
	values() {
		return iteratorMap(createMapIterator(this), /** @param {any} v */ (v) => v[1].content.getContent()[v[1].length - 1]);
	}

	/**
	 * Returns an Iterator of [key, value] pairs
	 *
	 * @return {IterableIterator<[string, MapType]>}
	 */
	entries() {
		return iteratorMap(createMapIterator(this), /** @param {any} v */ (v) => /** @type {any} */ ([v[0], v[1].content.getContent()[v[1].length - 1]]));
	}

	/**
	 * Executes a provided function on once on every key-value pair.
	 *
	 * @param {function(MapType,string,YMap<MapType>):void} f A function to execute on every element of this YArray.
	 */
	forEach(f) {
		this.doc ?? warnPrematureAccess();
		this._map.forEach((item, key) => {
			if (!item.deleted) {
				f(item.content.getContent()[item.length - 1], key, this);
			}
		});
	}

	/**
	 * Returns an Iterator of [key, value] pairs
	 *
	 * @return {IterableIterator<[string, MapType]>}
	 */
	[Symbol.iterator]() {
		return this.entries();
	}

	/**
	 * Remove a specified element from this YMap.
	 *
	 * @param {string} key The key of the element to remove.
	 */
	delete(key) {
		if (this.doc !== null) {
			transact(this.doc, (transaction) => {
				typeMapDelete(transaction, this, key);
			});
		} else {
			/** @type {Map<string, any>} */ (this._prelimContent).delete(key);
		}
	}

	/**
	 * Adds or updates an element with a specified key and value.
	 * @template {MapType} VAL
	 *
	 * @param {string} key The key of the element to add to this YMap
	 * @param {VAL} value The value of the element to add
	 * @return {VAL}
	 */
	set(key, value) {
		if (this.doc !== null) {
			transact(this.doc, (transaction) => {
				typeMapSet(transaction, this, key, /** @type {any} */ (value));
			});
		} else {
			/** @type {Map<string, any>} */ (this._prelimContent).set(key, value);
		}
		return value;
	}

	/**
	 * Returns a specified element from this YMap.
	 *
	 * @param {string} key
	 * @return {MapType|undefined}
	 */
	get(key) {
		return /** @type {any} */ (typeMapGet(this, key));
	}

	/**
	 * Returns a boolean indicating whether the specified key exists or not.
	 *
	 * @param {string} key The key to test.
	 * @return {boolean}
	 */
	has(key) {
		return typeMapHas(this, key);
	}

	/**
	 * Removes all elements from this YMap.
	 */
	clear() {
		if (this.doc !== null) {
			transact(this.doc, (transaction) => {
				this.forEach(function (_value, key, map) {
					typeMapDelete(transaction, map, key);
				});
			});
		} else {
			/** @type {Map<string, any>} */ (this._prelimContent).clear();
		}
	}

	/**
	 * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
	 */
	_write(encoder) {
		encoder.writeTypeRef(YMapRefID);
	}
}

/**
 * @param {UpdateDecoderV1 | UpdateDecoderV2} _decoder
 *
 * @private
 * @function
 */
const readYMap = (_decoder) => new YMap();

/**
 * @module YText
 */

/**
 * @param {any} a
 * @param {any} b
 * @return {boolean}
 */
const equalAttrs$1 = (a, b) => a === b || (typeof a === "object" && typeof b === "object" && a && b && equalFlat(a, b));

class ItemTextListPosition {
	/**
	 * @param {Item|null} left
	 * @param {Item|null} right
	 * @param {number} index
	 * @param {Map<string,any>} currentAttributes
	 */
	constructor(left, right, index, currentAttributes) {
		this.left = left;
		this.right = right;
		this.index = index;
		this.currentAttributes = currentAttributes;
	}

	/**
	 * Only call this if you know that this.right is defined
	 */
	forward() {
		if (this.right === null) {
			unexpectedCase();
		}
		switch (this.right.content.constructor) {
			case ContentFormat:
				if (!this.right.deleted) {
					updateCurrentAttributes(this.currentAttributes, /** @type {ContentFormat} */ (this.right.content));
				}
				break;
			default:
				if (!this.right.deleted) {
					this.index += this.right.length;
				}
				break;
		}
		this.left = this.right;
		this.right = this.right.right;
	}
}

/**
 * @param {Transaction} transaction
 * @param {ItemTextListPosition} pos
 * @param {number} count steps to move forward
 * @return {ItemTextListPosition}
 *
 * @private
 * @function
 */
const findNextPosition = (transaction, pos, count) => {
	while (pos.right !== null && count > 0) {
		switch (pos.right.content.constructor) {
			case ContentFormat:
				if (!pos.right.deleted) {
					updateCurrentAttributes(pos.currentAttributes, /** @type {ContentFormat} */ (pos.right.content));
				}
				break;
			default:
				if (!pos.right.deleted) {
					if (count < pos.right.length) {
						// split right
						getItemCleanStart(transaction, createID(pos.right.id.client, pos.right.id.clock + count));
					}
					pos.index += pos.right.length;
					count -= pos.right.length;
				}
				break;
		}
		pos.left = pos.right;
		pos.right = pos.right.right;
		// pos.forward() - we don't forward because that would halve the performance because we already do the checks above
	}
	return pos;
};

/**
 * @param {Transaction} transaction
 * @param {AbstractType<any>} parent
 * @param {number} index
 * @param {boolean} useSearchMarker
 * @return {ItemTextListPosition}
 *
 * @private
 * @function
 */
const findPosition = (transaction, parent, index, useSearchMarker) => {
	const currentAttributes = new Map();
	const marker = useSearchMarker ? findMarker(parent, index) : null;
	if (marker) {
		const pos = new ItemTextListPosition(marker.p.left, marker.p, marker.index, currentAttributes);
		return findNextPosition(transaction, pos, index - marker.index);
	} else {
		const pos = new ItemTextListPosition(null, parent._start, 0, currentAttributes);
		return findNextPosition(transaction, pos, index);
	}
};

/**
 * Negate applied formats
 *
 * @param {Transaction} transaction
 * @param {AbstractType<any>} parent
 * @param {ItemTextListPosition} currPos
 * @param {Map<string,any>} negatedAttributes
 *
 * @private
 * @function
 */
const insertNegatedAttributes = (transaction, parent, currPos, negatedAttributes) => {
	// check if we really need to remove attributes
	while (
		currPos.right !== null &&
		(currPos.right.deleted === true ||
			(currPos.right.content.constructor === ContentFormat &&
				equalAttrs$1(negatedAttributes.get(/** @type {ContentFormat} */ (currPos.right.content).key), /** @type {ContentFormat} */ (currPos.right.content).value)))
	) {
		if (!currPos.right.deleted) {
			negatedAttributes.delete(/** @type {ContentFormat} */ (currPos.right.content).key);
		}
		currPos.forward();
	}
	const doc = transaction.doc;
	const ownClientId = doc.clientID;
	negatedAttributes.forEach((val, key) => {
		const left = currPos.left;
		const right = currPos.right;
		const nextFormat = new Item$1(createID(ownClientId, getState(doc.store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, new ContentFormat(key, val));
		nextFormat.integrate(transaction, 0);
		currPos.right = nextFormat;
		currPos.forward();
	});
};

/**
 * @param {Map<string,any>} currentAttributes
 * @param {ContentFormat} format
 *
 * @private
 * @function
 */
const updateCurrentAttributes = (currentAttributes, format) => {
	const { key, value } = format;
	if (value === null) {
		currentAttributes.delete(key);
	} else {
		currentAttributes.set(key, value);
	}
};

/**
 * @param {ItemTextListPosition} currPos
 * @param {Object<string,any>} attributes
 *
 * @private
 * @function
 */
const minimizeAttributeChanges = (currPos, attributes) => {
	// go right while attributes[right.key] === right.value (or right is deleted)
	while (true) {
		if (currPos.right === null) {
			break;
		} else if (
			currPos.right.deleted ||
			(currPos.right.content.constructor === ContentFormat &&
				equalAttrs$1(attributes[/** @type {ContentFormat} */ (currPos.right.content).key] ?? null, /** @type {ContentFormat} */ (currPos.right.content).value))
		);
		else {
			break;
		}
		currPos.forward();
	}
};

/**
 * @param {Transaction} transaction
 * @param {AbstractType<any>} parent
 * @param {ItemTextListPosition} currPos
 * @param {Object<string,any>} attributes
 * @return {Map<string,any>}
 *
 * @private
 * @function
 **/
const insertAttributes = (transaction, parent, currPos, attributes) => {
	const doc = transaction.doc;
	const ownClientId = doc.clientID;
	const negatedAttributes = new Map();
	// insert format-start items
	for (const key in attributes) {
		const val = attributes[key];
		const currentVal = currPos.currentAttributes.get(key) ?? null;
		if (!equalAttrs$1(currentVal, val)) {
			// save negated attribute (set null if currentVal undefined)
			negatedAttributes.set(key, currentVal);
			const { left, right } = currPos;
			currPos.right = new Item$1(createID(ownClientId, getState(doc.store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, new ContentFormat(key, val));
			currPos.right.integrate(transaction, 0);
			currPos.forward();
		}
	}
	return negatedAttributes;
};

/**
 * @param {Transaction} transaction
 * @param {AbstractType<any>} parent
 * @param {ItemTextListPosition} currPos
 * @param {string|object|AbstractType<any>} text
 * @param {Object<string,any>} attributes
 *
 * @private
 * @function
 **/
const insertText = (transaction, parent, currPos, text, attributes) => {
	currPos.currentAttributes.forEach((_val, key) => {
		if (attributes[key] === undefined) {
			attributes[key] = null;
		}
	});
	const doc = transaction.doc;
	const ownClientId = doc.clientID;
	minimizeAttributeChanges(currPos, attributes);
	const negatedAttributes = insertAttributes(transaction, parent, currPos, attributes);
	// insert content
	const content =
		text.constructor === String ? new ContentString(/** @type {string} */ (text))
		: text instanceof AbstractType ? new ContentType(text)
		: new ContentEmbed(text);
	let { left, right, index } = currPos;
	if (parent._searchMarker) {
		updateMarkerChanges(parent._searchMarker, currPos.index, content.getLength());
	}
	right = new Item$1(createID(ownClientId, getState(doc.store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, content);
	right.integrate(transaction, 0);
	currPos.right = right;
	currPos.index = index;
	currPos.forward();
	insertNegatedAttributes(transaction, parent, currPos, negatedAttributes);
};

/**
 * @param {Transaction} transaction
 * @param {AbstractType<any>} parent
 * @param {ItemTextListPosition} currPos
 * @param {number} length
 * @param {Object<string,any>} attributes
 *
 * @private
 * @function
 */
const formatText = (transaction, parent, currPos, length, attributes) => {
	const doc = transaction.doc;
	const ownClientId = doc.clientID;
	minimizeAttributeChanges(currPos, attributes);
	const negatedAttributes = insertAttributes(transaction, parent, currPos, attributes);
	// iterate until first non-format or null is found
	// delete all formats with attributes[format.key] != null
	// also check the attributes after the first non-format as we do not want to insert redundant negated attributes there
	// eslint-disable-next-line no-labels
	iterationLoop: while (currPos.right !== null && (length > 0 || (negatedAttributes.size > 0 && (currPos.right.deleted || currPos.right.content.constructor === ContentFormat)))) {
		if (!currPos.right.deleted) {
			switch (currPos.right.content.constructor) {
				case ContentFormat: {
					const { key, value } = /** @type {ContentFormat} */ (currPos.right.content);
					const attr = attributes[key];
					if (attr !== undefined) {
						if (equalAttrs$1(attr, value)) {
							negatedAttributes.delete(key);
						} else {
							if (length === 0) {
								// no need to further extend negatedAttributes
								// eslint-disable-next-line no-labels
								break iterationLoop;
							}
							negatedAttributes.set(key, value);
						}
						currPos.right.delete(transaction);
					} else {
						currPos.currentAttributes.set(key, value);
					}
					break;
				}
				default:
					if (length < currPos.right.length) {
						getItemCleanStart(transaction, createID(currPos.right.id.client, currPos.right.id.clock + length));
					}
					length -= currPos.right.length;
					break;
			}
		}
		currPos.forward();
	}
	// Quill just assumes that the editor starts with a newline and that it always
	// ends with a newline. We only insert that newline when a new newline is
	// inserted - i.e when length is bigger than type.length
	if (length > 0) {
		let newlines = "";
		for (; length > 0; length--) {
			newlines += "\n";
		}
		currPos.right = new Item$1(
			createID(ownClientId, getState(doc.store, ownClientId)),
			currPos.left,
			currPos.left && currPos.left.lastId,
			currPos.right,
			currPos.right && currPos.right.id,
			parent,
			null,
			new ContentString(newlines),
		);
		currPos.right.integrate(transaction, 0);
		currPos.forward();
	}
	insertNegatedAttributes(transaction, parent, currPos, negatedAttributes);
};

/**
 * Call this function after string content has been deleted in order to
 * clean up formatting Items.
 *
 * @param {Transaction} transaction
 * @param {Item} start
 * @param {Item|null} curr exclusive end, automatically iterates to the next Content Item
 * @param {Map<string,any>} startAttributes
 * @param {Map<string,any>} currAttributes
 * @return {number} The amount of formatting Items deleted.
 *
 * @function
 */
const cleanupFormattingGap = (transaction, start, curr, startAttributes, currAttributes) => {
	/**
	 * @type {Item|null}
	 */
	let end = start;
	/**
	 * @type {Map<string,ContentFormat>}
	 */
	const endFormats = create$9();
	while (end && (!end.countable || end.deleted)) {
		if (!end.deleted && end.content.constructor === ContentFormat) {
			const cf = /** @type {ContentFormat} */ (end.content);
			endFormats.set(cf.key, cf);
		}
		end = end.right;
	}
	let cleanups = 0;
	let reachedCurr = false;
	while (start !== end) {
		if (curr === start) {
			reachedCurr = true;
		}
		if (!start.deleted) {
			const content = start.content;
			switch (content.constructor) {
				case ContentFormat: {
					const { key, value } = /** @type {ContentFormat} */ (content);
					const startAttrValue = startAttributes.get(key) ?? null;
					if (endFormats.get(key) !== content || startAttrValue === value) {
						// Either this format is overwritten or it is not necessary because the attribute already existed.
						start.delete(transaction);
						cleanups++;
						if (!reachedCurr && (currAttributes.get(key) ?? null) === value && startAttrValue !== value) {
							if (startAttrValue === null) {
								currAttributes.delete(key);
							} else {
								currAttributes.set(key, startAttrValue);
							}
						}
					}
					if (!reachedCurr && !start.deleted) {
						updateCurrentAttributes(currAttributes, /** @type {ContentFormat} */ (content));
					}
					break;
				}
			}
		}
		start = /** @type {Item} */ (start.right);
	}
	return cleanups;
};

/**
 * @param {Transaction} transaction
 * @param {Item | null} item
 */
const cleanupContextlessFormattingGap = (transaction, item) => {
	// iterate until item.right is null or content
	while (item && item.right && (item.right.deleted || !item.right.countable)) {
		item = item.right;
	}
	const attrs = new Set();
	// iterate back until a content item is found
	while (item && (item.deleted || !item.countable)) {
		if (!item.deleted && item.content.constructor === ContentFormat) {
			const key = /** @type {ContentFormat} */ (item.content).key;
			if (attrs.has(key)) {
				item.delete(transaction);
			} else {
				attrs.add(key);
			}
		}
		item = item.left;
	}
};

/**
 * This function is experimental and subject to change / be removed.
 *
 * Ideally, we don't need this function at all. Formatting attributes should be cleaned up
 * automatically after each change. This function iterates twice over the complete YText type
 * and removes unnecessary formatting attributes. This is also helpful for testing.
 *
 * This function won't be exported anymore as soon as there is confidence that the YText type works as intended.
 *
 * @param {YText} type
 * @return {number} How many formatting attributes have been cleaned up.
 */
const cleanupYTextFormatting = (type) => {
	let res = 0;
	transact(/** @type {Doc} */ (type.doc), (transaction) => {
		let start = /** @type {Item} */ (type._start);
		let end = type._start;
		let startAttributes = create$9();
		const currentAttributes = copy(startAttributes);
		while (end) {
			if (end.deleted === false) {
				switch (end.content.constructor) {
					case ContentFormat:
						updateCurrentAttributes(currentAttributes, /** @type {ContentFormat} */ (end.content));
						break;
					default:
						res += cleanupFormattingGap(transaction, start, end, startAttributes, currentAttributes);
						startAttributes = copy(currentAttributes);
						start = end;
						break;
				}
			}
			end = end.right;
		}
	});
	return res;
};

/**
 * This will be called by the transaction once the event handlers are called to potentially cleanup
 * formatting attributes.
 *
 * @param {Transaction} transaction
 */
const cleanupYTextAfterTransaction = (transaction) => {
	/**
	 * @type {Set<YText>}
	 */
	const needFullCleanup = new Set();
	// check if another formatting item was inserted
	const doc = transaction.doc;
	for (const [client, afterClock] of transaction.afterState.entries()) {
		const clock = transaction.beforeState.get(client) || 0;
		if (afterClock === clock) {
			continue;
		}
		iterateStructs(transaction, /** @type {Array<Item|GC>} */ (doc.store.clients.get(client)), clock, afterClock, (item) => {
			if (!item.deleted && /** @type {Item} */ (item).content.constructor === ContentFormat && item.constructor !== GC) {
				needFullCleanup.add(/** @type {any} */ (item).parent);
			}
		});
	}
	// cleanup in a new transaction
	transact(doc, (t) => {
		iterateDeletedStructs(transaction, transaction.deleteSet, (item) => {
			if (item instanceof GC || !(/** @type {YText} */ (item.parent)._hasFormatting) || needFullCleanup.has(/** @type {YText} */ (item.parent))) {
				return;
			}
			const parent = /** @type {YText} */ (item.parent);
			if (item.content.constructor === ContentFormat) {
				needFullCleanup.add(parent);
			} else {
				// If no formatting attribute was inserted or deleted, we can make due with contextless
				// formatting cleanups.
				// Contextless: it is not necessary to compute currentAttributes for the affected position.
				cleanupContextlessFormattingGap(t, item);
			}
		});
		// If a formatting item was inserted, we simply clean the whole type.
		// We need to compute currentAttributes for the current position anyway.
		for (const yText of needFullCleanup) {
			cleanupYTextFormatting(yText);
		}
	});
};

/**
 * @param {Transaction} transaction
 * @param {ItemTextListPosition} currPos
 * @param {number} length
 * @return {ItemTextListPosition}
 *
 * @private
 * @function
 */
const deleteText = (transaction, currPos, length) => {
	const startLength = length;
	const startAttrs = copy(currPos.currentAttributes);
	const start = currPos.right;
	while (length > 0 && currPos.right !== null) {
		if (currPos.right.deleted === false) {
			switch (currPos.right.content.constructor) {
				case ContentType:
				case ContentEmbed:
				case ContentString:
					if (length < currPos.right.length) {
						getItemCleanStart(transaction, createID(currPos.right.id.client, currPos.right.id.clock + length));
					}
					length -= currPos.right.length;
					currPos.right.delete(transaction);
					break;
			}
		}
		currPos.forward();
	}
	if (start) {
		cleanupFormattingGap(transaction, start, currPos.right, startAttrs, currPos.currentAttributes);
	}
	const parent = /** @type {AbstractType<any>} */ (/** @type {Item} */ (currPos.left || currPos.right).parent);
	if (parent._searchMarker) {
		updateMarkerChanges(parent._searchMarker, currPos.index, -startLength + length);
	}
	return currPos;
};

/**
 * The Quill Delta format represents changes on a text document with
 * formatting information. For more information visit {@link https://quilljs.com/docs/delta/|Quill Delta}
 *
 * @example
 *   {
 *     ops: [
 *       { insert: 'Gandalf', attributes: { bold: true } },
 *       { insert: ' the ' },
 *       { insert: 'Grey', attributes: { color: '#cccccc' } }
 *     ]
 *   }
 *
 */

/**
 * Attributes that can be assigned to a selection of text.
 *
 * @example
 *   {
 *     bold: true,
 *     font-size: '40px'
 *   }
 *
 * @typedef {Object} TextAttributes
 */

/**
 * @extends YEvent<YText>
 * Event that describes the changes on a YText type.
 */
class YTextEvent extends YEvent {
	/**
	 * @param {YText} ytext
	 * @param {Transaction} transaction
	 * @param {Set<any>} subs The keys that changed
	 */
	constructor(ytext, transaction, subs) {
		super(ytext, transaction);
		/**
		 * Whether the children changed.
		 * @type {Boolean}
		 * @private
		 */
		this.childListChanged = false;
		/**
		 * Set of all changed attributes.
		 * @type {Set<string>}
		 */
		this.keysChanged = new Set();
		subs.forEach((sub) => {
			if (sub === null) {
				this.childListChanged = true;
			} else {
				this.keysChanged.add(sub);
			}
		});
	}

	/**
	 * @type {{added:Set<Item>,deleted:Set<Item>,keys:Map<string,{action:'add'|'update'|'delete',oldValue:any}>,delta:Array<{insert?:Array<any>|string, delete?:number, retain?:number}>}}
	 */
	get changes() {
		if (this._changes === null) {
			/**
			 * @type {{added:Set<Item>,deleted:Set<Item>,keys:Map<string,{action:'add'|'update'|'delete',oldValue:any}>,delta:Array<{insert?:Array<any>|string|AbstractType<any>|object, delete?:number, retain?:number}>}}
			 */
			const changes = {
				keys: this.keys,
				delta: this.delta,
				added: new Set(),
				deleted: new Set(),
			};
			this._changes = changes;
		}
		return /** @type {any} */ (this._changes);
	}

	/**
	 * Compute the changes in the delta format.
	 * A {@link https://quilljs.com/docs/delta/|Quill Delta}) that represents the changes on the document.
	 *
	 * @type {Array<{insert?:string|object|AbstractType<any>, delete?:number, retain?:number, attributes?: Object<string,any>}>}
	 *
	 * @public
	 */
	get delta() {
		if (this._delta === null) {
			const y = /** @type {Doc} */ (this.target.doc);
			/**
			 * @type {Array<{insert?:string|object|AbstractType<any>, delete?:number, retain?:number, attributes?: Object<string,any>}>}
			 */
			const delta = [];
			transact(y, (transaction) => {
				const currentAttributes = new Map(); // saves all current attributes for insert
				const oldAttributes = new Map();
				let item = this.target._start;
				/**
				 * @type {string?}
				 */
				let action = null;
				/**
				 * @type {Object<string,any>}
				 */
				const attributes = {}; // counts added or removed new attributes for retain
				/**
				 * @type {string|object}
				 */
				let insert = "";
				let retain = 0;
				let deleteLen = 0;
				const addOp = () => {
					if (action !== null) {
						/**
						 * @type {any}
						 */
						let op = null;
						switch (action) {
							case "delete":
								if (deleteLen > 0) {
									op = { delete: deleteLen };
								}
								deleteLen = 0;
								break;
							case "insert":
								if (typeof insert === "object" || insert.length > 0) {
									op = { insert };
									if (currentAttributes.size > 0) {
										op.attributes = {};
										currentAttributes.forEach((value, key) => {
											if (value !== null) {
												op.attributes[key] = value;
											}
										});
									}
								}
								insert = "";
								break;
							case "retain":
								if (retain > 0) {
									op = { retain };
									if (!isEmpty(attributes)) {
										op.attributes = assign({}, attributes);
									}
								}
								retain = 0;
								break;
						}
						if (op) delta.push(op);
						action = null;
					}
				};
				while (item !== null) {
					switch (item.content.constructor) {
						case ContentType:
						case ContentEmbed:
							if (this.adds(item)) {
								if (!this.deletes(item)) {
									addOp();
									action = "insert";
									insert = item.content.getContent()[0];
									addOp();
								}
							} else if (this.deletes(item)) {
								if (action !== "delete") {
									addOp();
									action = "delete";
								}
								deleteLen += 1;
							} else if (!item.deleted) {
								if (action !== "retain") {
									addOp();
									action = "retain";
								}
								retain += 1;
							}
							break;
						case ContentString:
							if (this.adds(item)) {
								if (!this.deletes(item)) {
									if (action !== "insert") {
										addOp();
										action = "insert";
									}
									insert += /** @type {ContentString} */ (item.content).str;
								}
							} else if (this.deletes(item)) {
								if (action !== "delete") {
									addOp();
									action = "delete";
								}
								deleteLen += item.length;
							} else if (!item.deleted) {
								if (action !== "retain") {
									addOp();
									action = "retain";
								}
								retain += item.length;
							}
							break;
						case ContentFormat: {
							const { key, value } = /** @type {ContentFormat} */ (item.content);
							if (this.adds(item)) {
								if (!this.deletes(item)) {
									const curVal = currentAttributes.get(key) ?? null;
									if (!equalAttrs$1(curVal, value)) {
										if (action === "retain") {
											addOp();
										}
										if (equalAttrs$1(value, oldAttributes.get(key) ?? null)) {
											delete attributes[key];
										} else {
											attributes[key] = value;
										}
									} else if (value !== null) {
										item.delete(transaction);
									}
								}
							} else if (this.deletes(item)) {
								oldAttributes.set(key, value);
								const curVal = currentAttributes.get(key) ?? null;
								if (!equalAttrs$1(curVal, value)) {
									if (action === "retain") {
										addOp();
									}
									attributes[key] = curVal;
								}
							} else if (!item.deleted) {
								oldAttributes.set(key, value);
								const attr = attributes[key];
								if (attr !== undefined) {
									if (!equalAttrs$1(attr, value)) {
										if (action === "retain") {
											addOp();
										}
										if (value === null) {
											delete attributes[key];
										} else {
											attributes[key] = value;
										}
									} else if (attr !== null) {
										// this will be cleaned up automatically by the contextless cleanup function
										item.delete(transaction);
									}
								}
							}
							if (!item.deleted) {
								if (action === "insert") {
									addOp();
								}
								updateCurrentAttributes(currentAttributes, /** @type {ContentFormat} */ (item.content));
							}
							break;
						}
					}
					item = item.right;
				}
				addOp();
				while (delta.length > 0) {
					const lastOp = delta[delta.length - 1];
					if (lastOp.retain !== undefined && lastOp.attributes === undefined) {
						// retain delta's if they don't assign attributes
						delta.pop();
					} else {
						break;
					}
				}
			});
			this._delta = delta;
		}
		return /** @type {any} */ (this._delta);
	}
}

/**
 * Type that represents text with formatting information.
 *
 * This type replaces y-richtext as this implementation is able to handle
 * block formats (format information on a paragraph), embeds (complex elements
 * like pictures and videos), and text formats (**bold**, *italic*).
 *
 * @extends AbstractType<YTextEvent>
 */
class YText extends AbstractType {
	/**
	 * @param {String} [string] The initial value of the YText.
	 */
	constructor(string) {
		super();
		/**
		 * Array of pending operations on this type
		 * @type {Array<function():void>?}
		 */
		this._pending = string !== undefined ? [() => this.insert(0, string)] : [];
		/**
		 * @type {Array<ArraySearchMarker>|null}
		 */
		this._searchMarker = [];
		/**
		 * Whether this YText contains formatting attributes.
		 * This flag is updated when a formatting item is integrated (see ContentFormat.integrate)
		 */
		this._hasFormatting = false;
	}

	/**
	 * Number of characters of this text type.
	 *
	 * @type {number}
	 */
	get length() {
		this.doc ?? warnPrematureAccess();
		return this._length;
	}

	/**
	 * @param {Doc} y
	 * @param {Item} item
	 */
	_integrate(y, item) {
		super._integrate(y, item);
		try {
			/** @type {Array<function>} */ (this._pending).forEach((f) => f());
		} catch (e) {
			console.error(e);
		}
		this._pending = null;
	}

	_copy() {
		return new YText();
	}

	/**
	 * Makes a copy of this data type that can be included somewhere else.
	 *
	 * Note that the content is only readable _after_ it has been included somewhere in the Ydoc.
	 *
	 * @return {YText}
	 */
	clone() {
		const text = new YText();
		text.applyDelta(this.toDelta());
		return text;
	}

	/**
	 * Creates YTextEvent and calls observers.
	 *
	 * @param {Transaction} transaction
	 * @param {Set<null|string>} parentSubs Keys changed on this type. `null` if list was modified.
	 */
	_callObserver(transaction, parentSubs) {
		super._callObserver(transaction, parentSubs);
		const event = new YTextEvent(this, transaction, parentSubs);
		callTypeObservers(this, transaction, event);
		// If a remote change happened, we try to cleanup potential formatting duplicates.
		if (!transaction.local && this._hasFormatting) {
			transaction._needFormattingCleanup = true;
		}
	}

	/**
	 * Returns the unformatted string representation of this YText type.
	 *
	 * @public
	 */
	toString() {
		this.doc ?? warnPrematureAccess();
		let str = "";
		/**
		 * @type {Item|null}
		 */
		let n = this._start;
		while (n !== null) {
			if (!n.deleted && n.countable && n.content.constructor === ContentString) {
				str += /** @type {ContentString} */ (n.content).str;
			}
			n = n.right;
		}
		return str;
	}

	/**
	 * Returns the unformatted string representation of this YText type.
	 *
	 * @return {string}
	 * @public
	 */
	toJSON() {
		return this.toString();
	}

	/**
	 * Apply a {@link Delta} on this shared YText type.
	 *
	 * @param {Array<any>} delta The changes to apply on this element.
	 * @param {object}  opts
	 * @param {boolean} [opts.sanitize] Sanitize input delta. Removes ending newlines if set to true.
	 *
	 *
	 * @public
	 */
	applyDelta(delta, { sanitize = true } = {}) {
		if (this.doc !== null) {
			transact(this.doc, (transaction) => {
				const currPos = new ItemTextListPosition(null, this._start, 0, new Map());
				for (let i = 0; i < delta.length; i++) {
					const op = delta[i];
					if (op.insert !== undefined) {
						// Quill assumes that the content starts with an empty paragraph.
						// Yjs/Y.Text assumes that it starts empty. We always hide that
						// there is a newline at the end of the content.
						// If we omit this step, clients will see a different number of
						// paragraphs, but nothing bad will happen.
						const ins = !sanitize && typeof op.insert === "string" && i === delta.length - 1 && currPos.right === null && op.insert.slice(-1) === "\n" ? op.insert.slice(0, -1) : op.insert;
						if (typeof ins !== "string" || ins.length > 0) {
							insertText(transaction, this, currPos, ins, op.attributes || {});
						}
					} else if (op.retain !== undefined) {
						formatText(transaction, this, currPos, op.retain, op.attributes || {});
					} else if (op.delete !== undefined) {
						deleteText(transaction, currPos, op.delete);
					}
				}
			});
		} else {
			/** @type {Array<function>} */ (this._pending).push(() => this.applyDelta(delta));
		}
	}

	/**
	 * Returns the Delta representation of this YText type.
	 *
	 * @param {Snapshot} [snapshot]
	 * @param {Snapshot} [prevSnapshot]
	 * @param {function('removed' | 'added', ID):any} [computeYChange]
	 * @return {any} The Delta representation of this type.
	 *
	 * @public
	 */
	toDelta(snapshot, prevSnapshot, computeYChange) {
		this.doc ?? warnPrematureAccess();
		/**
		 * @type{Array<any>}
		 */
		const ops = [];
		const currentAttributes = new Map();
		const doc = /** @type {Doc} */ (this.doc);
		let str = "";
		let n = this._start;
		function packStr() {
			if (str.length > 0) {
				// pack str with attributes to ops
				/**
				 * @type {Object<string,any>}
				 */
				const attributes = {};
				let addAttributes = false;
				currentAttributes.forEach((value, key) => {
					addAttributes = true;
					attributes[key] = value;
				});
				/**
				 * @type {Object<string,any>}
				 */
				const op = { insert: str };
				if (addAttributes) {
					op.attributes = attributes;
				}
				ops.push(op);
				str = "";
			}
		}
		const computeDelta = () => {
			while (n !== null) {
				if (isVisible$1(n, snapshot) || (prevSnapshot !== undefined && isVisible$1(n, prevSnapshot))) {
					switch (n.content.constructor) {
						case ContentString: {
							const cur = currentAttributes.get("ychange");
							if (snapshot !== undefined && !isVisible$1(n, snapshot)) {
								if (cur === undefined || cur.user !== n.id.client || cur.type !== "removed") {
									packStr();
									currentAttributes.set("ychange", computeYChange ? computeYChange("removed", n.id) : { type: "removed" });
								}
							} else if (prevSnapshot !== undefined && !isVisible$1(n, prevSnapshot)) {
								if (cur === undefined || cur.user !== n.id.client || cur.type !== "added") {
									packStr();
									currentAttributes.set("ychange", computeYChange ? computeYChange("added", n.id) : { type: "added" });
								}
							} else if (cur !== undefined) {
								packStr();
								currentAttributes.delete("ychange");
							}
							str += /** @type {ContentString} */ (n.content).str;
							break;
						}
						case ContentType:
						case ContentEmbed: {
							packStr();
							/**
							 * @type {Object<string,any>}
							 */
							const op = {
								insert: n.content.getContent()[0],
							};
							if (currentAttributes.size > 0) {
								const attrs = /** @type {Object<string,any>} */ ({});
								op.attributes = attrs;
								currentAttributes.forEach((value, key) => {
									attrs[key] = value;
								});
							}
							ops.push(op);
							break;
						}
						case ContentFormat:
							if (isVisible$1(n, snapshot)) {
								packStr();
								updateCurrentAttributes(currentAttributes, /** @type {ContentFormat} */ (n.content));
							}
							break;
					}
				}
				n = n.right;
			}
			packStr();
		};
		if (snapshot || prevSnapshot) {
			// snapshots are merged again after the transaction, so we need to keep the
			// transaction alive until we are done
			transact(
				doc,
				(transaction) => {
					if (snapshot) {
						splitSnapshotAffectedStructs(transaction, snapshot);
					}
					if (prevSnapshot) {
						splitSnapshotAffectedStructs(transaction, prevSnapshot);
					}
					computeDelta();
				},
				"cleanup",
			);
		} else {
			computeDelta();
		}
		return ops;
	}

	/**
	 * Insert text at a given index.
	 *
	 * @param {number} index The index at which to start inserting.
	 * @param {String} text The text to insert at the specified position.
	 * @param {TextAttributes} [attributes] Optionally define some formatting
	 *                                    information to apply on the inserted
	 *                                    Text.
	 * @public
	 */
	insert(index, text, attributes) {
		if (text.length <= 0) {
			return;
		}
		const y = this.doc;
		if (y !== null) {
			transact(y, (transaction) => {
				const pos = findPosition(transaction, this, index, !attributes);
				if (!attributes) {
					attributes = {};
					// @ts-ignore
					pos.currentAttributes.forEach((v, k) => {
						attributes[k] = v;
					});
				}
				insertText(transaction, this, pos, text, attributes);
			});
		} else {
			/** @type {Array<function>} */ (this._pending).push(() => this.insert(index, text, attributes));
		}
	}

	/**
	 * Inserts an embed at a index.
	 *
	 * @param {number} index The index to insert the embed at.
	 * @param {Object | AbstractType<any>} embed The Object that represents the embed.
	 * @param {TextAttributes} [attributes] Attribute information to apply on the
	 *                                    embed
	 *
	 * @public
	 */
	insertEmbed(index, embed, attributes) {
		const y = this.doc;
		if (y !== null) {
			transact(y, (transaction) => {
				const pos = findPosition(transaction, this, index, !attributes);
				insertText(transaction, this, pos, embed, attributes || {});
			});
		} else {
			/** @type {Array<function>} */ (this._pending).push(() => this.insertEmbed(index, embed, attributes || {}));
		}
	}

	/**
	 * Deletes text starting from an index.
	 *
	 * @param {number} index Index at which to start deleting.
	 * @param {number} length The number of characters to remove. Defaults to 1.
	 *
	 * @public
	 */
	delete(index, length) {
		if (length === 0) {
			return;
		}
		const y = this.doc;
		if (y !== null) {
			transact(y, (transaction) => {
				deleteText(transaction, findPosition(transaction, this, index, true), length);
			});
		} else {
			/** @type {Array<function>} */ (this._pending).push(() => this.delete(index, length));
		}
	}

	/**
	 * Assigns properties to a range of text.
	 *
	 * @param {number} index The position where to start formatting.
	 * @param {number} length The amount of characters to assign properties to.
	 * @param {TextAttributes} attributes Attribute information to apply on the
	 *                                    text.
	 *
	 * @public
	 */
	format(index, length, attributes) {
		if (length === 0) {
			return;
		}
		const y = this.doc;
		if (y !== null) {
			transact(y, (transaction) => {
				const pos = findPosition(transaction, this, index, false);
				if (pos.right === null) {
					return;
				}
				formatText(transaction, this, pos, length, attributes);
			});
		} else {
			/** @type {Array<function>} */ (this._pending).push(() => this.format(index, length, attributes));
		}
	}

	/**
	 * Removes an attribute.
	 *
	 * @note Xml-Text nodes don't have attributes. You can use this feature to assign properties to complete text-blocks.
	 *
	 * @param {String} attributeName The attribute name that is to be removed.
	 *
	 * @public
	 */
	removeAttribute(attributeName) {
		if (this.doc !== null) {
			transact(this.doc, (transaction) => {
				typeMapDelete(transaction, this, attributeName);
			});
		} else {
			/** @type {Array<function>} */ (this._pending).push(() => this.removeAttribute(attributeName));
		}
	}

	/**
	 * Sets or updates an attribute.
	 *
	 * @note Xml-Text nodes don't have attributes. You can use this feature to assign properties to complete text-blocks.
	 *
	 * @param {String} attributeName The attribute name that is to be set.
	 * @param {any} attributeValue The attribute value that is to be set.
	 *
	 * @public
	 */
	setAttribute(attributeName, attributeValue) {
		if (this.doc !== null) {
			transact(this.doc, (transaction) => {
				typeMapSet(transaction, this, attributeName, attributeValue);
			});
		} else {
			/** @type {Array<function>} */ (this._pending).push(() => this.setAttribute(attributeName, attributeValue));
		}
	}

	/**
	 * Returns an attribute value that belongs to the attribute name.
	 *
	 * @note Xml-Text nodes don't have attributes. You can use this feature to assign properties to complete text-blocks.
	 *
	 * @param {String} attributeName The attribute name that identifies the
	 *                               queried value.
	 * @return {any} The queried attribute value.
	 *
	 * @public
	 */
	getAttribute(attributeName) {
		return /** @type {any} */ (typeMapGet(this, attributeName));
	}

	/**
	 * Returns all attribute name/value pairs in a JSON Object.
	 *
	 * @note Xml-Text nodes don't have attributes. You can use this feature to assign properties to complete text-blocks.
	 *
	 * @return {Object<string, any>} A JSON Object that describes the attributes.
	 *
	 * @public
	 */
	getAttributes() {
		return typeMapGetAll(this);
	}

	/**
	 * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
	 */
	_write(encoder) {
		encoder.writeTypeRef(YTextRefID);
	}
}

/**
 * @param {UpdateDecoderV1 | UpdateDecoderV2} _decoder
 * @return {YText}
 *
 * @private
 * @function
 */
const readYText = (_decoder) => new YText();

/**
 * @module YXml
 */

/**
 * Define the elements to which a set of CSS queries apply.
 * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors|CSS_Selectors}
 *
 * @example
 *   query = '.classSelector'
 *   query = 'nodeSelector'
 *   query = '#idSelector'
 *
 * @typedef {string} CSS_Selector
 */

/**
 * Dom filter function.
 *
 * @callback domFilter
 * @param {string} nodeName The nodeName of the element
 * @param {Map} attributes The map of attributes.
 * @return {boolean} Whether to include the Dom node in the YXmlElement.
 */

/**
 * Represents a subset of the nodes of a YXmlElement / YXmlFragment and a
 * position within them.
 *
 * Can be created with {@link YXmlFragment#createTreeWalker}
 *
 * @public
 * @implements {Iterable<YXmlElement|YXmlText|YXmlElement|YXmlHook>}
 */
class YXmlTreeWalker {
	/**
	 * @param {YXmlFragment | YXmlElement} root
	 * @param {function(AbstractType<any>):boolean} [f]
	 */
	constructor(root, f = () => true) {
		this._filter = f;
		this._root = root;
		/**
		 * @type {Item}
		 */
		this._currentNode = /** @type {Item} */ (root._start);
		this._firstCall = true;
		root.doc ?? warnPrematureAccess();
	}

	[Symbol.iterator]() {
		return this;
	}

	/**
	 * Get the next node.
	 *
	 * @return {IteratorResult<YXmlElement|YXmlText|YXmlHook>} The next node.
	 *
	 * @public
	 */
	next() {
		/**
		 * @type {Item|null}
		 */
		let n = this._currentNode;
		let type = n && n.content && /** @type {any} */ (n.content).type;
		if (n !== null && (!this._firstCall || n.deleted || !this._filter(type))) {
			// if first call, we check if we can use the first item
			do {
				type = /** @type {any} */ (n.content).type;
				if (!n.deleted && (type.constructor === YXmlElement || type.constructor === YXmlFragment) && type._start !== null) {
					// walk down in the tree
					n = type._start;
				} else {
					// walk right or up in the tree
					while (n !== null) {
						/**
						 * @type {Item | null}
						 */
						const nxt = n.next;
						if (nxt !== null) {
							n = nxt;
							break;
						} else if (n.parent === this._root) {
							n = null;
						} else {
							n = /** @type {AbstractType<any>} */ (n.parent)._item;
						}
					}
				}
			} while (n !== null && (n.deleted || !this._filter(/** @type {ContentType} */ (n.content).type)));
		}
		this._firstCall = false;
		if (n === null) {
			// @ts-ignore
			return { value: undefined, done: true };
		}
		this._currentNode = n;
		return { value: /** @type {any} */ (n.content).type, done: false };
	}
}

/**
 * Represents a list of {@link YXmlElement}.and {@link YXmlText} types.
 * A YxmlFragment is similar to a {@link YXmlElement}, but it does not have a
 * nodeName and it does not have attributes. Though it can be bound to a DOM
 * element - in this case the attributes and the nodeName are not shared.
 *
 * @public
 * @extends AbstractType<YXmlEvent>
 */
class YXmlFragment extends AbstractType {
	constructor() {
		super();
		/**
		 * @type {Array<any>|null}
		 */
		this._prelimContent = [];
	}

	/**
	 * @type {YXmlElement|YXmlText|null}
	 */
	get firstChild() {
		const first = this._first;
		return first ? first.content.getContent()[0] : null;
	}

	/**
	 * Integrate this type into the Yjs instance.
	 *
	 * * Save this struct in the os
	 * * This type is sent to other client
	 * * Observer functions are fired
	 *
	 * @param {Doc} y The Yjs instance
	 * @param {Item} item
	 */
	_integrate(y, item) {
		super._integrate(y, item);
		this.insert(0, /** @type {Array<any>} */ (this._prelimContent));
		this._prelimContent = null;
	}

	_copy() {
		return new YXmlFragment();
	}

	/**
	 * Makes a copy of this data type that can be included somewhere else.
	 *
	 * Note that the content is only readable _after_ it has been included somewhere in the Ydoc.
	 *
	 * @return {YXmlFragment}
	 */
	clone() {
		const el = new YXmlFragment();
		// @ts-ignore
		el.insert(
			0,
			this.toArray().map((item) => (item instanceof AbstractType ? item.clone() : item)),
		);
		return el;
	}

	get length() {
		this.doc ?? warnPrematureAccess();
		return this._prelimContent === null ? this._length : this._prelimContent.length;
	}

	/**
	 * Create a subtree of childNodes.
	 *
	 * @example
	 * const walker = elem.createTreeWalker(dom => dom.nodeName === 'div')
	 * for (let node in walker) {
	 *   // `node` is a div node
	 *   nop(node)
	 * }
	 *
	 * @param {function(AbstractType<any>):boolean} filter Function that is called on each child element and
	 *                          returns a Boolean indicating whether the child
	 *                          is to be included in the subtree.
	 * @return {YXmlTreeWalker} A subtree and a position within it.
	 *
	 * @public
	 */
	createTreeWalker(filter) {
		return new YXmlTreeWalker(this, filter);
	}

	/**
	 * Returns the first YXmlElement that matches the query.
	 * Similar to DOM's {@link querySelector}.
	 *
	 * Query support:
	 *   - tagname
	 * TODO:
	 *   - id
	 *   - attribute
	 *
	 * @param {CSS_Selector} query The query on the children.
	 * @return {YXmlElement|YXmlText|YXmlHook|null} The first element that matches the query or null.
	 *
	 * @public
	 */
	querySelector(query) {
		query = query.toUpperCase();
		// @ts-ignore
		const iterator = new YXmlTreeWalker(this, (element) => element.nodeName && element.nodeName.toUpperCase() === query);
		const next = iterator.next();
		if (next.done) {
			return null;
		} else {
			return next.value;
		}
	}

	/**
	 * Returns all YXmlElements that match the query.
	 * Similar to Dom's {@link querySelectorAll}.
	 *
	 * @todo Does not yet support all queries. Currently only query by tagName.
	 *
	 * @param {CSS_Selector} query The query on the children
	 * @return {Array<YXmlElement|YXmlText|YXmlHook|null>} The elements that match this query.
	 *
	 * @public
	 */
	querySelectorAll(query) {
		query = query.toUpperCase();
		// @ts-ignore
		return from(new YXmlTreeWalker(this, (element) => element.nodeName && element.nodeName.toUpperCase() === query));
	}

	/**
	 * Creates YXmlEvent and calls observers.
	 *
	 * @param {Transaction} transaction
	 * @param {Set<null|string>} parentSubs Keys changed on this type. `null` if list was modified.
	 */
	_callObserver(transaction, parentSubs) {
		callTypeObservers(this, transaction, new YXmlEvent(this, parentSubs, transaction));
	}

	/**
	 * Get the string representation of all the children of this YXmlFragment.
	 *
	 * @return {string} The string representation of all children.
	 */
	toString() {
		return typeListMap(this, (xml) => xml.toString()).join("");
	}

	/**
	 * @return {string}
	 */
	toJSON() {
		return this.toString();
	}

	/**
	 * Creates a Dom Element that mirrors this YXmlElement.
	 *
	 * @param {Document} [_document=document] The document object (you must define
	 *                                        this when calling this method in
	 *                                        nodejs)
	 * @param {Object<string, any>} [hooks={}] Optional property to customize how hooks
	 *                                             are presented in the DOM
	 * @param {any} [binding] You should not set this property. This is
	 *                               used if DomBinding wants to create a
	 *                               association to the created DOM type.
	 * @return {Node} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}
	 *
	 * @public
	 */
	toDOM(_document = document, hooks = {}, binding) {
		const fragment = _document.createDocumentFragment();
		if (binding !== undefined) {
			binding._createAssociation(fragment, this);
		}
		typeListForEach(this, (xmlType) => {
			fragment.insertBefore(xmlType.toDOM(_document, hooks, binding), null);
		});
		return fragment;
	}

	/**
	 * Inserts new content at an index.
	 *
	 * @example
	 *  // Insert character 'a' at position 0
	 *  xml.insert(0, [new Y.XmlText('text')])
	 *
	 * @param {number} index The index to insert content at
	 * @param {Array<YXmlElement|YXmlText>} content The array of content
	 */
	insert(index, content) {
		if (this.doc !== null) {
			transact(this.doc, (transaction) => {
				typeListInsertGenerics(transaction, this, index, content);
			});
		} else {
			// @ts-ignore _prelimContent is defined because this is not yet integrated
			this._prelimContent.splice(index, 0, ...content);
		}
	}

	/**
	 * Inserts new content at an index.
	 *
	 * @example
	 *  // Insert character 'a' at position 0
	 *  xml.insert(0, [new Y.XmlText('text')])
	 *
	 * @param {null|Item|YXmlElement|YXmlText} ref The index to insert content at
	 * @param {Array<YXmlElement|YXmlText>} content The array of content
	 */
	insertAfter(ref, content) {
		if (this.doc !== null) {
			transact(this.doc, (transaction) => {
				const refItem = ref && ref instanceof AbstractType ? ref._item : ref;
				typeListInsertGenericsAfter(transaction, this, refItem, content);
			});
		} else {
			const pc = /** @type {Array<any>} */ (this._prelimContent);
			const index = ref === null ? 0 : pc.findIndex((el) => el === ref) + 1;
			if (index === 0 && ref !== null) {
				throw create$7("Reference item not found");
			}
			pc.splice(index, 0, ...content);
		}
	}

	/**
	 * Deletes elements starting from an index.
	 *
	 * @param {number} index Index at which to start deleting elements
	 * @param {number} [length=1] The number of elements to remove. Defaults to 1.
	 */
	delete(index, length = 1) {
		if (this.doc !== null) {
			transact(this.doc, (transaction) => {
				typeListDelete(transaction, this, index, length);
			});
		} else {
			// @ts-ignore _prelimContent is defined because this is not yet integrated
			this._prelimContent.splice(index, length);
		}
	}

	/**
	 * Transforms this YArray to a JavaScript Array.
	 *
	 * @return {Array<YXmlElement|YXmlText|YXmlHook>}
	 */
	toArray() {
		return typeListToArray(this);
	}

	/**
	 * Appends content to this YArray.
	 *
	 * @param {Array<YXmlElement|YXmlText>} content Array of content to append.
	 */
	push(content) {
		this.insert(this.length, content);
	}

	/**
	 * Prepends content to this YArray.
	 *
	 * @param {Array<YXmlElement|YXmlText>} content Array of content to prepend.
	 */
	unshift(content) {
		this.insert(0, content);
	}

	/**
	 * Returns the i-th element from a YArray.
	 *
	 * @param {number} index The index of the element to return from the YArray
	 * @return {YXmlElement|YXmlText}
	 */
	get(index) {
		return typeListGet(this, index);
	}

	/**
	 * Returns a portion of this YXmlFragment into a JavaScript Array selected
	 * from start to end (end not included).
	 *
	 * @param {number} [start]
	 * @param {number} [end]
	 * @return {Array<YXmlElement|YXmlText>}
	 */
	slice(start = 0, end = this.length) {
		return typeListSlice(this, start, end);
	}

	/**
	 * Executes a provided function on once on every child element.
	 *
	 * @param {function(YXmlElement|YXmlText,number, typeof self):void} f A function to execute on every element of this YArray.
	 */
	forEach(f) {
		typeListForEach(this, f);
	}

	/**
	 * Transform the properties of this type to binary and write it to an
	 * BinaryEncoder.
	 *
	 * This is called when this Item is sent to a remote peer.
	 *
	 * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder The encoder to write data to.
	 */
	_write(encoder) {
		encoder.writeTypeRef(YXmlFragmentRefID);
	}
}

/**
 * @param {UpdateDecoderV1 | UpdateDecoderV2} _decoder
 * @return {YXmlFragment}
 *
 * @private
 * @function
 */
const readYXmlFragment = (_decoder) => new YXmlFragment();

/**
 * @typedef {Object|number|null|Array<any>|string|Uint8Array|AbstractType<any>} ValueTypes
 */

/**
 * An YXmlElement imitates the behavior of a
 * https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element
 *
 * * An YXmlElement has attributes (key value pairs)
 * * An YXmlElement has childElements that must inherit from YXmlElement
 *
 * @template {{ [key: string]: ValueTypes }} [KV={ [key: string]: string }]
 */
class YXmlElement extends YXmlFragment {
	constructor(nodeName = "UNDEFINED") {
		super();
		this.nodeName = nodeName;
		/**
		 * @type {Map<string, any>|null}
		 */
		this._prelimAttrs = new Map();
	}

	/**
	 * @type {YXmlElement|YXmlText|null}
	 */
	get nextSibling() {
		const n = this._item ? this._item.next : null;
		return n ? /** @type {YXmlElement|YXmlText} */ (/** @type {ContentType} */ (n.content).type) : null;
	}

	/**
	 * @type {YXmlElement|YXmlText|null}
	 */
	get prevSibling() {
		const n = this._item ? this._item.prev : null;
		return n ? /** @type {YXmlElement|YXmlText} */ (/** @type {ContentType} */ (n.content).type) : null;
	}

	/**
	 * Integrate this type into the Yjs instance.
	 *
	 * * Save this struct in the os
	 * * This type is sent to other client
	 * * Observer functions are fired
	 *
	 * @param {Doc} y The Yjs instance
	 * @param {Item} item
	 */
	_integrate(y, item) {
		super._integrate(y, item);
		/** @type {Map<string, any>} */ (this._prelimAttrs).forEach((value, key) => {
			this.setAttribute(key, value);
		});
		this._prelimAttrs = null;
	}

	/**
	 * Creates an Item with the same effect as this Item (without position effect)
	 *
	 * @return {YXmlElement}
	 */
	_copy() {
		return new YXmlElement(this.nodeName);
	}

	/**
	 * Makes a copy of this data type that can be included somewhere else.
	 *
	 * Note that the content is only readable _after_ it has been included somewhere in the Ydoc.
	 *
	 * @return {YXmlElement<KV>}
	 */
	clone() {
		/**
		 * @type {YXmlElement<KV>}
		 */
		const el = new YXmlElement(this.nodeName);
		const attrs = this.getAttributes();
		forEach(attrs, (value, key) => {
			el.setAttribute(key, /** @type {any} */ (value));
		});
		// @ts-ignore
		el.insert(
			0,
			this.toArray().map((v) => (v instanceof AbstractType ? v.clone() : v)),
		);
		return el;
	}

	/**
	 * Returns the XML serialization of this YXmlElement.
	 * The attributes are ordered by attribute-name, so you can easily use this
	 * method to compare YXmlElements
	 *
	 * @return {string} The string representation of this type.
	 *
	 * @public
	 */
	toString() {
		const attrs = this.getAttributes();
		const stringBuilder = [];
		const keys = [];
		for (const key in attrs) {
			keys.push(key);
		}
		keys.sort();
		const keysLen = keys.length;
		for (let i = 0; i < keysLen; i++) {
			const key = keys[i];
			stringBuilder.push(key + '="' + attrs[key] + '"');
		}
		const nodeName = this.nodeName.toLocaleLowerCase();
		const attrsString = stringBuilder.length > 0 ? " " + stringBuilder.join(" ") : "";
		return `<${nodeName}${attrsString}>${super.toString()}</${nodeName}>`;
	}

	/**
	 * Removes an attribute from this YXmlElement.
	 *
	 * @param {string} attributeName The attribute name that is to be removed.
	 *
	 * @public
	 */
	removeAttribute(attributeName) {
		if (this.doc !== null) {
			transact(this.doc, (transaction) => {
				typeMapDelete(transaction, this, attributeName);
			});
		} else {
			/** @type {Map<string,any>} */ (this._prelimAttrs).delete(attributeName);
		}
	}

	/**
	 * Sets or updates an attribute.
	 *
	 * @template {keyof KV & string} KEY
	 *
	 * @param {KEY} attributeName The attribute name that is to be set.
	 * @param {KV[KEY]} attributeValue The attribute value that is to be set.
	 *
	 * @public
	 */
	setAttribute(attributeName, attributeValue) {
		if (this.doc !== null) {
			transact(this.doc, (transaction) => {
				typeMapSet(transaction, this, attributeName, attributeValue);
			});
		} else {
			/** @type {Map<string, any>} */ (this._prelimAttrs).set(attributeName, attributeValue);
		}
	}

	/**
	 * Returns an attribute value that belongs to the attribute name.
	 *
	 * @template {keyof KV & string} KEY
	 *
	 * @param {KEY} attributeName The attribute name that identifies the
	 *                               queried value.
	 * @return {KV[KEY]|undefined} The queried attribute value.
	 *
	 * @public
	 */
	getAttribute(attributeName) {
		return /** @type {any} */ (typeMapGet(this, attributeName));
	}

	/**
	 * Returns whether an attribute exists
	 *
	 * @param {string} attributeName The attribute name to check for existence.
	 * @return {boolean} whether the attribute exists.
	 *
	 * @public
	 */
	hasAttribute(attributeName) {
		return /** @type {any} */ (typeMapHas(this, attributeName));
	}

	/**
	 * Returns all attribute name/value pairs in a JSON Object.
	 *
	 * @param {Snapshot} [snapshot]
	 * @return {{ [Key in Extract<keyof KV,string>]?: KV[Key]}} A JSON Object that describes the attributes.
	 *
	 * @public
	 */
	getAttributes(snapshot) {
		return /** @type {any} */ (snapshot ? typeMapGetAllSnapshot(this, snapshot) : typeMapGetAll(this));
	}

	/**
	 * Creates a Dom Element that mirrors this YXmlElement.
	 *
	 * @param {Document} [_document=document] The document object (you must define
	 *                                        this when calling this method in
	 *                                        nodejs)
	 * @param {Object<string, any>} [hooks={}] Optional property to customize how hooks
	 *                                             are presented in the DOM
	 * @param {any} [binding] You should not set this property. This is
	 *                               used if DomBinding wants to create a
	 *                               association to the created DOM type.
	 * @return {Node} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}
	 *
	 * @public
	 */
	toDOM(_document = document, hooks = {}, binding) {
		const dom = _document.createElement(this.nodeName);
		const attrs = this.getAttributes();
		for (const key in attrs) {
			const value = attrs[key];
			if (typeof value === "string") {
				dom.setAttribute(key, value);
			}
		}
		typeListForEach(this, (yxml) => {
			dom.appendChild(yxml.toDOM(_document, hooks, binding));
		});
		if (binding !== undefined) {
			binding._createAssociation(dom, this);
		}
		return dom;
	}

	/**
	 * Transform the properties of this type to binary and write it to an
	 * BinaryEncoder.
	 *
	 * This is called when this Item is sent to a remote peer.
	 *
	 * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder The encoder to write data to.
	 */
	_write(encoder) {
		encoder.writeTypeRef(YXmlElementRefID);
		encoder.writeKey(this.nodeName);
	}
}

/**
 * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
 * @return {YXmlElement}
 *
 * @function
 */
const readYXmlElement = (decoder) => new YXmlElement(decoder.readKey());

/**
 * @extends YEvent<YXmlElement|YXmlText|YXmlFragment>
 * An Event that describes changes on a YXml Element or Yxml Fragment
 */
class YXmlEvent extends YEvent {
	/**
	 * @param {YXmlElement|YXmlText|YXmlFragment} target The target on which the event is created.
	 * @param {Set<string|null>} subs The set of changed attributes. `null` is included if the
	 *                   child list changed.
	 * @param {Transaction} transaction The transaction instance with which the
	 *                                  change was created.
	 */
	constructor(target, subs, transaction) {
		super(target, transaction);
		/**
		 * Whether the children changed.
		 * @type {Boolean}
		 * @private
		 */
		this.childListChanged = false;
		/**
		 * Set of all changed attributes.
		 * @type {Set<string>}
		 */
		this.attributesChanged = new Set();
		subs.forEach((sub) => {
			if (sub === null) {
				this.childListChanged = true;
			} else {
				this.attributesChanged.add(sub);
			}
		});
	}
}

/**
 * You can manage binding to a custom type with YXmlHook.
 *
 * @extends {YMap<any>}
 */
class YXmlHook extends YMap {
	/**
	 * @param {string} hookName nodeName of the Dom Node.
	 */
	constructor(hookName) {
		super();
		/**
		 * @type {string}
		 */
		this.hookName = hookName;
	}

	/**
	 * Creates an Item with the same effect as this Item (without position effect)
	 */
	_copy() {
		return new YXmlHook(this.hookName);
	}

	/**
	 * Makes a copy of this data type that can be included somewhere else.
	 *
	 * Note that the content is only readable _after_ it has been included somewhere in the Ydoc.
	 *
	 * @return {YXmlHook}
	 */
	clone() {
		const el = new YXmlHook(this.hookName);
		this.forEach((value, key) => {
			el.set(key, value);
		});
		return el;
	}

	/**
	 * Creates a Dom Element that mirrors this YXmlElement.
	 *
	 * @param {Document} [_document=document] The document object (you must define
	 *                                        this when calling this method in
	 *                                        nodejs)
	 * @param {Object.<string, any>} [hooks] Optional property to customize how hooks
	 *                                             are presented in the DOM
	 * @param {any} [binding] You should not set this property. This is
	 *                               used if DomBinding wants to create a
	 *                               association to the created DOM type
	 * @return {Element} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}
	 *
	 * @public
	 */
	toDOM(_document = document, hooks = {}, binding) {
		const hook = hooks[this.hookName];
		let dom;
		if (hook !== undefined) {
			dom = hook.createDom(this);
		} else {
			dom = document.createElement(this.hookName);
		}
		dom.setAttribute("data-yjs-hook", this.hookName);
		if (binding !== undefined) {
			binding._createAssociation(dom, this);
		}
		return dom;
	}

	/**
	 * Transform the properties of this type to binary and write it to an
	 * BinaryEncoder.
	 *
	 * This is called when this Item is sent to a remote peer.
	 *
	 * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder The encoder to write data to.
	 */
	_write(encoder) {
		encoder.writeTypeRef(YXmlHookRefID);
		encoder.writeKey(this.hookName);
	}
}

/**
 * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
 * @return {YXmlHook}
 *
 * @private
 * @function
 */
const readYXmlHook = (decoder) => new YXmlHook(decoder.readKey());

/**
 * Represents text in a Dom Element. In the future this type will also handle
 * simple formatting information like bold and italic.
 */
class YXmlText extends YText {
	/**
	 * @type {YXmlElement|YXmlText|null}
	 */
	get nextSibling() {
		const n = this._item ? this._item.next : null;
		return n ? /** @type {YXmlElement|YXmlText} */ (/** @type {ContentType} */ (n.content).type) : null;
	}

	/**
	 * @type {YXmlElement|YXmlText|null}
	 */
	get prevSibling() {
		const n = this._item ? this._item.prev : null;
		return n ? /** @type {YXmlElement|YXmlText} */ (/** @type {ContentType} */ (n.content).type) : null;
	}

	_copy() {
		return new YXmlText();
	}

	/**
	 * Makes a copy of this data type that can be included somewhere else.
	 *
	 * Note that the content is only readable _after_ it has been included somewhere in the Ydoc.
	 *
	 * @return {YXmlText}
	 */
	clone() {
		const text = new YXmlText();
		text.applyDelta(this.toDelta());
		return text;
	}

	/**
	 * Creates a Dom Element that mirrors this YXmlText.
	 *
	 * @param {Document} [_document=document] The document object (you must define
	 *                                        this when calling this method in
	 *                                        nodejs)
	 * @param {Object<string, any>} [hooks] Optional property to customize how hooks
	 *                                             are presented in the DOM
	 * @param {any} [binding] You should not set this property. This is
	 *                               used if DomBinding wants to create a
	 *                               association to the created DOM type.
	 * @return {Text} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}
	 *
	 * @public
	 */
	toDOM(_document = document, hooks, binding) {
		const dom = _document.createTextNode(this.toString());
		if (binding !== undefined) {
			binding._createAssociation(dom, this);
		}
		return dom;
	}

	toString() {
		// @ts-ignore
		return this.toDelta()
			.map((delta) => {
				const nestedNodes = [];
				for (const nodeName in delta.attributes) {
					const attrs = [];
					for (const key in delta.attributes[nodeName]) {
						attrs.push({ key, value: delta.attributes[nodeName][key] });
					}
					// sort attributes to get a unique order
					attrs.sort((a, b) => (a.key < b.key ? -1 : 1));
					nestedNodes.push({ nodeName, attrs });
				}
				// sort node order to get a unique order
				nestedNodes.sort((a, b) => (a.nodeName < b.nodeName ? -1 : 1));
				// now convert to dom string
				let str = "";
				for (let i = 0; i < nestedNodes.length; i++) {
					const node = nestedNodes[i];
					str += `<${node.nodeName}`;
					for (let j = 0; j < node.attrs.length; j++) {
						const attr = node.attrs[j];
						str += ` ${attr.key}="${attr.value}"`;
					}
					str += ">";
				}
				str += delta.insert;
				for (let i = nestedNodes.length - 1; i >= 0; i--) {
					str += `</${nestedNodes[i].nodeName}>`;
				}
				return str;
			})
			.join("");
	}

	/**
	 * @return {string}
	 */
	toJSON() {
		return this.toString();
	}

	/**
	 * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
	 */
	_write(encoder) {
		encoder.writeTypeRef(YXmlTextRefID);
	}
}

/**
 * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
 * @return {YXmlText}
 *
 * @private
 * @function
 */
const readYXmlText = (decoder) => new YXmlText();

class AbstractStruct {
	/**
	 * @param {ID} id
	 * @param {number} length
	 */
	constructor(id, length) {
		this.id = id;
		this.length = length;
	}

	/**
	 * @type {boolean}
	 */
	get deleted() {
		throw methodUnimplemented();
	}

	/**
	 * Merge this struct with the item to the right.
	 * This method is already assuming that `this.id.clock + this.length === this.id.clock`.
	 * Also this method does *not* remove right from StructStore!
	 * @param {AbstractStruct} right
	 * @return {boolean} whether this merged with right
	 */
	mergeWith(right) {
		return false;
	}

	/**
	 * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder The encoder to write data to.
	 * @param {number} offset
	 * @param {number} encodingRef
	 */
	write(encoder, offset, encodingRef) {
		throw methodUnimplemented();
	}

	/**
	 * @param {Transaction} transaction
	 * @param {number} offset
	 */
	integrate(transaction, offset) {
		throw methodUnimplemented();
	}
}

const structGCRefNumber = 0;

/**
 * @private
 */
class GC extends AbstractStruct {
	get deleted() {
		return true;
	}

	delete() {}

	/**
	 * @param {GC} right
	 * @return {boolean}
	 */
	mergeWith(right) {
		if (this.constructor !== right.constructor) {
			return false;
		}
		this.length += right.length;
		return true;
	}

	/**
	 * @param {Transaction} transaction
	 * @param {number} offset
	 */
	integrate(transaction, offset) {
		if (offset > 0) {
			this.id.clock += offset;
			this.length -= offset;
		}
		addStruct(transaction.doc.store, this);
	}

	/**
	 * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
	 * @param {number} offset
	 */
	write(encoder, offset) {
		encoder.writeInfo(structGCRefNumber);
		encoder.writeLen(this.length - offset);
	}

	/**
	 * @param {Transaction} transaction
	 * @param {StructStore} store
	 * @return {null | number}
	 */
	getMissing(transaction, store) {
		return null;
	}
}

class ContentBinary {
	/**
	 * @param {Uint8Array} content
	 */
	constructor(content) {
		this.content = content;
	}

	/**
	 * @return {number}
	 */
	getLength() {
		return 1;
	}

	/**
	 * @return {Array<any>}
	 */
	getContent() {
		return [this.content];
	}

	/**
	 * @return {boolean}
	 */
	isCountable() {
		return true;
	}

	/**
	 * @return {ContentBinary}
	 */
	copy() {
		return new ContentBinary(this.content);
	}

	/**
	 * @param {number} offset
	 * @return {ContentBinary}
	 */
	splice(offset) {
		throw methodUnimplemented();
	}

	/**
	 * @param {ContentBinary} right
	 * @return {boolean}
	 */
	mergeWith(right) {
		return false;
	}

	/**
	 * @param {Transaction} transaction
	 * @param {Item} item
	 */
	integrate(transaction, item) {}
	/**
	 * @param {Transaction} transaction
	 */
	delete(transaction) {}
	/**
	 * @param {StructStore} store
	 */
	gc(store) {}
	/**
	 * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
	 * @param {number} offset
	 */
	write(encoder, offset) {
		encoder.writeBuf(this.content);
	}

	/**
	 * @return {number}
	 */
	getRef() {
		return 3;
	}
}

/**
 * @param {UpdateDecoderV1 | UpdateDecoderV2 } decoder
 * @return {ContentBinary}
 */
const readContentBinary = (decoder) => new ContentBinary(decoder.readBuf());

class ContentDeleted {
	/**
	 * @param {number} len
	 */
	constructor(len) {
		this.len = len;
	}

	/**
	 * @return {number}
	 */
	getLength() {
		return this.len;
	}

	/**
	 * @return {Array<any>}
	 */
	getContent() {
		return [];
	}

	/**
	 * @return {boolean}
	 */
	isCountable() {
		return false;
	}

	/**
	 * @return {ContentDeleted}
	 */
	copy() {
		return new ContentDeleted(this.len);
	}

	/**
	 * @param {number} offset
	 * @return {ContentDeleted}
	 */
	splice(offset) {
		const right = new ContentDeleted(this.len - offset);
		this.len = offset;
		return right;
	}

	/**
	 * @param {ContentDeleted} right
	 * @return {boolean}
	 */
	mergeWith(right) {
		this.len += right.len;
		return true;
	}

	/**
	 * @param {Transaction} transaction
	 * @param {Item} item
	 */
	integrate(transaction, item) {
		addToDeleteSet(transaction.deleteSet, item.id.client, item.id.clock, this.len);
		item.markDeleted();
	}

	/**
	 * @param {Transaction} transaction
	 */
	delete(transaction) {}
	/**
	 * @param {StructStore} store
	 */
	gc(store) {}
	/**
	 * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
	 * @param {number} offset
	 */
	write(encoder, offset) {
		encoder.writeLen(this.len - offset);
	}

	/**
	 * @return {number}
	 */
	getRef() {
		return 1;
	}
}

/**
 * @private
 *
 * @param {UpdateDecoderV1 | UpdateDecoderV2 } decoder
 * @return {ContentDeleted}
 */
const readContentDeleted = (decoder) => new ContentDeleted(decoder.readLen());

/**
 * @param {string} guid
 * @param {Object<string, any>} opts
 */
const createDocFromOpts = (guid, opts) => new Doc({ guid, ...opts, shouldLoad: opts.shouldLoad || opts.autoLoad || false });

/**
 * @private
 */
class ContentDoc {
	/**
	 * @param {Doc} doc
	 */
	constructor(doc) {
		if (doc._item) {
			console.error("This document was already integrated as a sub-document. You should create a second instance instead with the same guid.");
		}
		/**
		 * @type {Doc}
		 */
		this.doc = doc;
		/**
		 * @type {any}
		 */
		const opts = {};
		this.opts = opts;
		if (!doc.gc) {
			opts.gc = false;
		}
		if (doc.autoLoad) {
			opts.autoLoad = true;
		}
		if (doc.meta !== null) {
			opts.meta = doc.meta;
		}
	}

	/**
	 * @return {number}
	 */
	getLength() {
		return 1;
	}

	/**
	 * @return {Array<any>}
	 */
	getContent() {
		return [this.doc];
	}

	/**
	 * @return {boolean}
	 */
	isCountable() {
		return true;
	}

	/**
	 * @return {ContentDoc}
	 */
	copy() {
		return new ContentDoc(createDocFromOpts(this.doc.guid, this.opts));
	}

	/**
	 * @param {number} offset
	 * @return {ContentDoc}
	 */
	splice(offset) {
		throw methodUnimplemented();
	}

	/**
	 * @param {ContentDoc} right
	 * @return {boolean}
	 */
	mergeWith(right) {
		return false;
	}

	/**
	 * @param {Transaction} transaction
	 * @param {Item} item
	 */
	integrate(transaction, item) {
		// this needs to be reflected in doc.destroy as well
		this.doc._item = item;
		transaction.subdocsAdded.add(this.doc);
		if (this.doc.shouldLoad) {
			transaction.subdocsLoaded.add(this.doc);
		}
	}

	/**
	 * @param {Transaction} transaction
	 */
	delete(transaction) {
		if (transaction.subdocsAdded.has(this.doc)) {
			transaction.subdocsAdded.delete(this.doc);
		} else {
			transaction.subdocsRemoved.add(this.doc);
		}
	}

	/**
	 * @param {StructStore} store
	 */
	gc(store) {}

	/**
	 * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
	 * @param {number} offset
	 */
	write(encoder, offset) {
		encoder.writeString(this.doc.guid);
		encoder.writeAny(this.opts);
	}

	/**
	 * @return {number}
	 */
	getRef() {
		return 9;
	}
}

/**
 * @private
 *
 * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
 * @return {ContentDoc}
 */
const readContentDoc = (decoder) => new ContentDoc(createDocFromOpts(decoder.readString(), decoder.readAny()));

/**
 * @private
 */
class ContentEmbed {
	/**
	 * @param {Object} embed
	 */
	constructor(embed) {
		this.embed = embed;
	}

	/**
	 * @return {number}
	 */
	getLength() {
		return 1;
	}

	/**
	 * @return {Array<any>}
	 */
	getContent() {
		return [this.embed];
	}

	/**
	 * @return {boolean}
	 */
	isCountable() {
		return true;
	}

	/**
	 * @return {ContentEmbed}
	 */
	copy() {
		return new ContentEmbed(this.embed);
	}

	/**
	 * @param {number} offset
	 * @return {ContentEmbed}
	 */
	splice(offset) {
		throw methodUnimplemented();
	}

	/**
	 * @param {ContentEmbed} right
	 * @return {boolean}
	 */
	mergeWith(right) {
		return false;
	}

	/**
	 * @param {Transaction} transaction
	 * @param {Item} item
	 */
	integrate(transaction, item) {}
	/**
	 * @param {Transaction} transaction
	 */
	delete(transaction) {}
	/**
	 * @param {StructStore} store
	 */
	gc(store) {}
	/**
	 * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
	 * @param {number} offset
	 */
	write(encoder, offset) {
		encoder.writeJSON(this.embed);
	}

	/**
	 * @return {number}
	 */
	getRef() {
		return 5;
	}
}

/**
 * @private
 *
 * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
 * @return {ContentEmbed}
 */
const readContentEmbed = (decoder) => new ContentEmbed(decoder.readJSON());

/**
 * @private
 */
class ContentFormat {
	/**
	 * @param {string} key
	 * @param {Object} value
	 */
	constructor(key, value) {
		this.key = key;
		this.value = value;
	}

	/**
	 * @return {number}
	 */
	getLength() {
		return 1;
	}

	/**
	 * @return {Array<any>}
	 */
	getContent() {
		return [];
	}

	/**
	 * @return {boolean}
	 */
	isCountable() {
		return false;
	}

	/**
	 * @return {ContentFormat}
	 */
	copy() {
		return new ContentFormat(this.key, this.value);
	}

	/**
	 * @param {number} _offset
	 * @return {ContentFormat}
	 */
	splice(_offset) {
		throw methodUnimplemented();
	}

	/**
	 * @param {ContentFormat} _right
	 * @return {boolean}
	 */
	mergeWith(_right) {
		return false;
	}

	/**
	 * @param {Transaction} _transaction
	 * @param {Item} item
	 */
	integrate(_transaction, item) {
		// @todo searchmarker are currently unsupported for rich text documents
		const p = /** @type {YText} */ (item.parent);
		p._searchMarker = null;
		p._hasFormatting = true;
	}

	/**
	 * @param {Transaction} transaction
	 */
	delete(transaction) {}
	/**
	 * @param {StructStore} store
	 */
	gc(store) {}
	/**
	 * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
	 * @param {number} offset
	 */
	write(encoder, offset) {
		encoder.writeKey(this.key);
		encoder.writeJSON(this.value);
	}

	/**
	 * @return {number}
	 */
	getRef() {
		return 6;
	}
}

/**
 * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
 * @return {ContentFormat}
 */
const readContentFormat = (decoder) => new ContentFormat(decoder.readKey(), decoder.readJSON());

/**
 * @private
 */
class ContentJSON {
	/**
	 * @param {Array<any>} arr
	 */
	constructor(arr) {
		/**
		 * @type {Array<any>}
		 */
		this.arr = arr;
	}

	/**
	 * @return {number}
	 */
	getLength() {
		return this.arr.length;
	}

	/**
	 * @return {Array<any>}
	 */
	getContent() {
		return this.arr;
	}

	/**
	 * @return {boolean}
	 */
	isCountable() {
		return true;
	}

	/**
	 * @return {ContentJSON}
	 */
	copy() {
		return new ContentJSON(this.arr);
	}

	/**
	 * @param {number} offset
	 * @return {ContentJSON}
	 */
	splice(offset) {
		const right = new ContentJSON(this.arr.slice(offset));
		this.arr = this.arr.slice(0, offset);
		return right;
	}

	/**
	 * @param {ContentJSON} right
	 * @return {boolean}
	 */
	mergeWith(right) {
		this.arr = this.arr.concat(right.arr);
		return true;
	}

	/**
	 * @param {Transaction} transaction
	 * @param {Item} item
	 */
	integrate(transaction, item) {}
	/**
	 * @param {Transaction} transaction
	 */
	delete(transaction) {}
	/**
	 * @param {StructStore} store
	 */
	gc(store) {}
	/**
	 * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
	 * @param {number} offset
	 */
	write(encoder, offset) {
		const len = this.arr.length;
		encoder.writeLen(len - offset);
		for (let i = offset; i < len; i++) {
			const c = this.arr[i];
			encoder.writeString(c === undefined ? "undefined" : JSON.stringify(c));
		}
	}

	/**
	 * @return {number}
	 */
	getRef() {
		return 2;
	}
}

/**
 * @private
 *
 * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
 * @return {ContentJSON}
 */
const readContentJSON = (decoder) => {
	const len = decoder.readLen();
	const cs = [];
	for (let i = 0; i < len; i++) {
		const c = decoder.readString();
		if (c === "undefined") {
			cs.push(undefined);
		} else {
			cs.push(JSON.parse(c));
		}
	}
	return new ContentJSON(cs);
};

const isDevMode = getVariable("node_env") === "development";

class ContentAny {
	/**
	 * @param {Array<any>} arr
	 */
	constructor(arr) {
		/**
		 * @type {Array<any>}
		 */
		this.arr = arr;
		isDevMode && deepFreeze(arr);
	}

	/**
	 * @return {number}
	 */
	getLength() {
		return this.arr.length;
	}

	/**
	 * @return {Array<any>}
	 */
	getContent() {
		return this.arr;
	}

	/**
	 * @return {boolean}
	 */
	isCountable() {
		return true;
	}

	/**
	 * @return {ContentAny}
	 */
	copy() {
		return new ContentAny(this.arr);
	}

	/**
	 * @param {number} offset
	 * @return {ContentAny}
	 */
	splice(offset) {
		const right = new ContentAny(this.arr.slice(offset));
		this.arr = this.arr.slice(0, offset);
		return right;
	}

	/**
	 * @param {ContentAny} right
	 * @return {boolean}
	 */
	mergeWith(right) {
		this.arr = this.arr.concat(right.arr);
		return true;
	}

	/**
	 * @param {Transaction} transaction
	 * @param {Item} item
	 */
	integrate(transaction, item) {}
	/**
	 * @param {Transaction} transaction
	 */
	delete(transaction) {}
	/**
	 * @param {StructStore} store
	 */
	gc(store) {}
	/**
	 * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
	 * @param {number} offset
	 */
	write(encoder, offset) {
		const len = this.arr.length;
		encoder.writeLen(len - offset);
		for (let i = offset; i < len; i++) {
			const c = this.arr[i];
			encoder.writeAny(c);
		}
	}

	/**
	 * @return {number}
	 */
	getRef() {
		return 8;
	}
}

/**
 * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
 * @return {ContentAny}
 */
const readContentAny = (decoder) => {
	const len = decoder.readLen();
	const cs = [];
	for (let i = 0; i < len; i++) {
		cs.push(decoder.readAny());
	}
	return new ContentAny(cs);
};

/**
 * @private
 */
class ContentString {
	/**
	 * @param {string} str
	 */
	constructor(str) {
		/**
		 * @type {string}
		 */
		this.str = str;
	}

	/**
	 * @return {number}
	 */
	getLength() {
		return this.str.length;
	}

	/**
	 * @return {Array<any>}
	 */
	getContent() {
		return this.str.split("");
	}

	/**
	 * @return {boolean}
	 */
	isCountable() {
		return true;
	}

	/**
	 * @return {ContentString}
	 */
	copy() {
		return new ContentString(this.str);
	}

	/**
	 * @param {number} offset
	 * @return {ContentString}
	 */
	splice(offset) {
		const right = new ContentString(this.str.slice(offset));
		this.str = this.str.slice(0, offset);

		// Prevent encoding invalid documents because of splitting of surrogate pairs: https://github.com/yjs/yjs/issues/248
		const firstCharCode = this.str.charCodeAt(offset - 1);
		if (firstCharCode >= 0xd800 && firstCharCode <= 0xdbff) {
			// Last character of the left split is the start of a surrogate utf16/ucs2 pair.
			// We don't support splitting of surrogate pairs because this may lead to invalid documents.
			// Replace the invalid character with a unicode replacement character (� / U+FFFD)
			this.str = this.str.slice(0, offset - 1) + "�";
			// replace right as well
			right.str = "�" + right.str.slice(1);
		}
		return right;
	}

	/**
	 * @param {ContentString} right
	 * @return {boolean}
	 */
	mergeWith(right) {
		this.str += right.str;
		return true;
	}

	/**
	 * @param {Transaction} transaction
	 * @param {Item} item
	 */
	integrate(transaction, item) {}
	/**
	 * @param {Transaction} transaction
	 */
	delete(transaction) {}
	/**
	 * @param {StructStore} store
	 */
	gc(store) {}
	/**
	 * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
	 * @param {number} offset
	 */
	write(encoder, offset) {
		encoder.writeString(offset === 0 ? this.str : this.str.slice(offset));
	}

	/**
	 * @return {number}
	 */
	getRef() {
		return 4;
	}
}

/**
 * @private
 *
 * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
 * @return {ContentString}
 */
const readContentString = (decoder) => new ContentString(decoder.readString());

/**
 * @type {Array<function(UpdateDecoderV1 | UpdateDecoderV2):AbstractType<any>>}
 * @private
 */
const typeRefs = [readYArray, readYMap, readYText, readYXmlElement, readYXmlFragment, readYXmlHook, readYXmlText];

const YArrayRefID = 0;
const YMapRefID = 1;
const YTextRefID = 2;
const YXmlElementRefID = 3;
const YXmlFragmentRefID = 4;
const YXmlHookRefID = 5;
const YXmlTextRefID = 6;

/**
 * @private
 */
class ContentType {
	/**
	 * @param {AbstractType<any>} type
	 */
	constructor(type) {
		/**
		 * @type {AbstractType<any>}
		 */
		this.type = type;
	}

	/**
	 * @return {number}
	 */
	getLength() {
		return 1;
	}

	/**
	 * @return {Array<any>}
	 */
	getContent() {
		return [this.type];
	}

	/**
	 * @return {boolean}
	 */
	isCountable() {
		return true;
	}

	/**
	 * @return {ContentType}
	 */
	copy() {
		return new ContentType(this.type._copy());
	}

	/**
	 * @param {number} offset
	 * @return {ContentType}
	 */
	splice(offset) {
		throw methodUnimplemented();
	}

	/**
	 * @param {ContentType} right
	 * @return {boolean}
	 */
	mergeWith(right) {
		return false;
	}

	/**
	 * @param {Transaction} transaction
	 * @param {Item} item
	 */
	integrate(transaction, item) {
		this.type._integrate(transaction.doc, item);
	}

	/**
	 * @param {Transaction} transaction
	 */
	delete(transaction) {
		let item = this.type._start;
		while (item !== null) {
			if (!item.deleted) {
				item.delete(transaction);
			} else if (item.id.clock < (transaction.beforeState.get(item.id.client) || 0)) {
				// This will be gc'd later and we want to merge it if possible
				// We try to merge all deleted items after each transaction,
				// but we have no knowledge about that this needs to be merged
				// since it is not in transaction.ds. Hence we add it to transaction._mergeStructs
				transaction._mergeStructs.push(item);
			}
			item = item.right;
		}
		this.type._map.forEach((item) => {
			if (!item.deleted) {
				item.delete(transaction);
			} else if (item.id.clock < (transaction.beforeState.get(item.id.client) || 0)) {
				// same as above
				transaction._mergeStructs.push(item);
			}
		});
		transaction.changed.delete(this.type);
	}

	/**
	 * @param {StructStore} store
	 */
	gc(store) {
		let item = this.type._start;
		while (item !== null) {
			item.gc(store, true);
			item = item.right;
		}
		this.type._start = null;
		this.type._map.forEach(
			/** @param {Item | null} item */ (item) => {
				while (item !== null) {
					item.gc(store, true);
					item = item.left;
				}
			},
		);
		this.type._map = new Map();
	}

	/**
	 * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
	 * @param {number} offset
	 */
	write(encoder, offset) {
		this.type._write(encoder);
	}

	/**
	 * @return {number}
	 */
	getRef() {
		return 7;
	}
}

/**
 * @private
 *
 * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
 * @return {ContentType}
 */
const readContentType = (decoder) => new ContentType(typeRefs[decoder.readTypeRef()](decoder));

/**
 * @todo This should return several items
 *
 * @param {StructStore} store
 * @param {ID} id
 * @return {{item:Item, diff:number}}
 */
const followRedone = (store, id) => {
	/**
	 * @type {ID|null}
	 */
	let nextID = id;
	let diff = 0;
	let item;
	do {
		if (diff > 0) {
			nextID = createID(nextID.client, nextID.clock + diff);
		}
		item = getItem(store, nextID);
		diff = nextID.clock - item.id.clock;
		nextID = item.redone;
	} while (nextID !== null && item instanceof Item$1);
	return {
		item,
		diff,
	};
};

/**
 * Make sure that neither item nor any of its parents is ever deleted.
 *
 * This property does not persist when storing it into a database or when
 * sending it to other peers
 *
 * @param {Item|null} item
 * @param {boolean} keep
 */
const keepItem = (item, keep) => {
	while (item !== null && item.keep !== keep) {
		item.keep = keep;
		item = /** @type {AbstractType<any>} */ (item.parent)._item;
	}
};

/**
 * Split leftItem into two items
 * @param {Transaction} transaction
 * @param {Item} leftItem
 * @param {number} diff
 * @return {Item}
 *
 * @function
 * @private
 */
const splitItem = (transaction, leftItem, diff) => {
	// create rightItem
	const { client, clock } = leftItem.id;
	const rightItem = new Item$1(
		createID(client, clock + diff),
		leftItem,
		createID(client, clock + diff - 1),
		leftItem.right,
		leftItem.rightOrigin,
		leftItem.parent,
		leftItem.parentSub,
		leftItem.content.splice(diff),
	);
	if (leftItem.deleted) {
		rightItem.markDeleted();
	}
	if (leftItem.keep) {
		rightItem.keep = true;
	}
	if (leftItem.redone !== null) {
		rightItem.redone = createID(leftItem.redone.client, leftItem.redone.clock + diff);
	}
	// update left (do not set leftItem.rightOrigin as it will lead to problems when syncing)
	leftItem.right = rightItem;
	// update right
	if (rightItem.right !== null) {
		rightItem.right.left = rightItem;
	}
	// right is more specific.
	transaction._mergeStructs.push(rightItem);
	// update parent._map
	if (rightItem.parentSub !== null && rightItem.right === null) {
		/** @type {AbstractType<any>} */ (rightItem.parent)._map.set(rightItem.parentSub, rightItem);
	}
	leftItem.length = diff;
	return rightItem;
};

/**
 * @param {Array<StackItem>} stack
 * @param {ID} id
 */
const isDeletedByUndoStack = (stack, id) => some(stack, /** @param {StackItem} s */ (s) => isDeleted(s.deletions, id));

/**
 * Redoes the effect of this operation.
 *
 * @param {Transaction} transaction The Yjs instance.
 * @param {Item} item
 * @param {Set<Item>} redoitems
 * @param {DeleteSet} itemsToDelete
 * @param {boolean} ignoreRemoteMapChanges
 * @param {import('../utils/UndoManager.js').UndoManager} um
 *
 * @return {Item|null}
 *
 * @private
 */
const redoItem = (transaction, item, redoitems, itemsToDelete, ignoreRemoteMapChanges, um) => {
	const doc = transaction.doc;
	const store = doc.store;
	const ownClientID = doc.clientID;
	const redone = item.redone;
	if (redone !== null) {
		return getItemCleanStart(transaction, redone);
	}
	let parentItem = /** @type {AbstractType<any>} */ (item.parent)._item;
	/**
	 * @type {Item|null}
	 */
	let left = null;
	/**
	 * @type {Item|null}
	 */
	let right;
	// make sure that parent is redone
	if (parentItem !== null && parentItem.deleted === true) {
		// try to undo parent if it will be undone anyway
		if (parentItem.redone === null && (!redoitems.has(parentItem) || redoItem(transaction, parentItem, redoitems, itemsToDelete, ignoreRemoteMapChanges, um) === null)) {
			return null;
		}
		while (parentItem.redone !== null) {
			parentItem = getItemCleanStart(transaction, parentItem.redone);
		}
	}
	const parentType = parentItem === null ? /** @type {AbstractType<any>} */ (item.parent) : /** @type {ContentType} */ (parentItem.content).type;

	if (item.parentSub === null) {
		// Is an array item. Insert at the old position
		left = item.left;
		right = item;
		// find next cloned_redo items
		while (left !== null) {
			/**
			 * @type {Item|null}
			 */
			let leftTrace = left;
			// trace redone until parent matches
			while (leftTrace !== null && /** @type {AbstractType<any>} */ (leftTrace.parent)._item !== parentItem) {
				leftTrace = leftTrace.redone === null ? null : getItemCleanStart(transaction, leftTrace.redone);
			}
			if (leftTrace !== null && /** @type {AbstractType<any>} */ (leftTrace.parent)._item === parentItem) {
				left = leftTrace;
				break;
			}
			left = left.left;
		}
		while (right !== null) {
			/**
			 * @type {Item|null}
			 */
			let rightTrace = right;
			// trace redone until parent matches
			while (rightTrace !== null && /** @type {AbstractType<any>} */ (rightTrace.parent)._item !== parentItem) {
				rightTrace = rightTrace.redone === null ? null : getItemCleanStart(transaction, rightTrace.redone);
			}
			if (rightTrace !== null && /** @type {AbstractType<any>} */ (rightTrace.parent)._item === parentItem) {
				right = rightTrace;
				break;
			}
			right = right.right;
		}
	} else {
		right = null;
		if (item.right && !ignoreRemoteMapChanges) {
			left = item;
			// Iterate right while right is in itemsToDelete
			// If it is intended to delete right while item is redone, we can expect that item should replace right.
			while (
				left !== null &&
				left.right !== null &&
				(left.right.redone || isDeleted(itemsToDelete, left.right.id) || isDeletedByUndoStack(um.undoStack, left.right.id) || isDeletedByUndoStack(um.redoStack, left.right.id))
			) {
				left = left.right;
				// follow redone
				while (left.redone) left = getItemCleanStart(transaction, left.redone);
			}
			if (left && left.right !== null) {
				// It is not possible to redo this item because it conflicts with a
				// change from another client
				return null;
			}
		} else {
			left = parentType._map.get(item.parentSub) || null;
		}
		// drop cross-parent left so origin doesn't mislead the remote (#757)
		if (left !== null && /** @type {AbstractType<any>} */ (left.parent)._item !== parentItem) {
			left = parentType._map.get(item.parentSub) || null;
		}
	}
	const nextClock = getState(store, ownClientID);
	const nextId = createID(ownClientID, nextClock);
	const redoneItem = new Item$1(nextId, left, left && left.lastId, right, right && right.id, parentType, item.parentSub, item.content.copy());
	item.redone = nextId;
	keepItem(redoneItem, true);
	redoneItem.integrate(transaction, 0);
	return redoneItem;
};

/**
 * Abstract class that represents any content.
 */
let Item$1 = class Item extends AbstractStruct {
	/**
	 * @param {ID} id
	 * @param {Item | null} left
	 * @param {ID | null} origin
	 * @param {Item | null} right
	 * @param {ID | null} rightOrigin
	 * @param {AbstractType<any>|ID|null} parent Is a type if integrated, is null if it is possible to copy parent from left or right, is ID before integration to search for it.
	 * @param {string | null} parentSub
	 * @param {AbstractContent} content
	 */
	constructor(id, left, origin, right, rightOrigin, parent, parentSub, content) {
		super(id, content.getLength());
		/**
		 * The item that was originally to the left of this item.
		 * @type {ID | null}
		 */
		this.origin = origin;
		/**
		 * The item that is currently to the left of this item.
		 * @type {Item | null}
		 */
		this.left = left;
		/**
		 * The item that is currently to the right of this item.
		 * @type {Item | null}
		 */
		this.right = right;
		/**
		 * The item that was originally to the right of this item.
		 * @type {ID | null}
		 */
		this.rightOrigin = rightOrigin;
		/**
		 * @type {AbstractType<any>|ID|null}
		 */
		this.parent = parent;
		/**
		 * If the parent refers to this item with some kind of key (e.g. YMap, the
		 * key is specified here. The key is then used to refer to the list in which
		 * to insert this item. If `parentSub = null` type._start is the list in
		 * which to insert to. Otherwise it is `parent._map`.
		 * @type {String | null}
		 */
		this.parentSub = parentSub;
		/**
		 * If this type's effect is redone this type refers to the type that undid
		 * this operation.
		 * @type {ID | null}
		 */
		this.redone = null;
		/**
		 * @type {AbstractContent}
		 */
		this.content = content;
		/**
		 * bit1: keep
		 * bit2: countable
		 * bit3: deleted
		 * bit4: mark - mark node as fast-search-marker
		 * @type {number} byte
		 */
		this.info = this.content.isCountable() ? BIT2 : 0;
	}

	/**
	 * This is used to mark the item as an indexed fast-search marker
	 *
	 * @type {boolean}
	 */
	set marker(isMarked) {
		if ((this.info & BIT4) > 0 !== isMarked) {
			this.info ^= BIT4;
		}
	}

	get marker() {
		return (this.info & BIT4) > 0;
	}

	/**
	 * If true, do not garbage collect this Item.
	 */
	get keep() {
		return (this.info & BIT1) > 0;
	}

	set keep(doKeep) {
		if (this.keep !== doKeep) {
			this.info ^= BIT1;
		}
	}

	get countable() {
		return (this.info & BIT2) > 0;
	}

	/**
	 * Whether this item was deleted or not.
	 * @type {Boolean}
	 */
	get deleted() {
		return (this.info & BIT3) > 0;
	}

	set deleted(doDelete) {
		if (this.deleted !== doDelete) {
			this.info ^= BIT3;
		}
	}

	markDeleted() {
		this.info |= BIT3;
	}

	/**
	 * Return the creator clientID of the missing op or define missing items and return null.
	 *
	 * @param {Transaction} transaction
	 * @param {StructStore} store
	 * @return {null | number}
	 */
	getMissing(transaction, store) {
		if (this.origin && this.origin.client !== this.id.client && this.origin.clock >= getState(store, this.origin.client)) {
			return this.origin.client;
		}
		if (this.rightOrigin && this.rightOrigin.client !== this.id.client && this.rightOrigin.clock >= getState(store, this.rightOrigin.client)) {
			return this.rightOrigin.client;
		}
		if (this.parent && this.parent.constructor === ID && this.id.client !== this.parent.client && this.parent.clock >= getState(store, this.parent.client)) {
			return this.parent.client;
		}

		// We have all missing ids, now find the items

		if (this.origin) {
			this.left = getItemCleanEnd(transaction, store, this.origin);
			this.origin = this.left.lastId;
		}
		if (this.rightOrigin) {
			this.right = getItemCleanStart(transaction, this.rightOrigin);
			this.rightOrigin = this.right.id;
		}
		if ((this.left && this.left.constructor === GC) || (this.right && this.right.constructor === GC)) {
			this.parent = null;
		} else if (!this.parent) {
			// only set parent if this shouldn't be garbage collected
			if (this.left && this.left.constructor === Item) {
				this.parent = this.left.parent;
				this.parentSub = this.left.parentSub;
			} else if (this.right && this.right.constructor === Item) {
				this.parent = this.right.parent;
				this.parentSub = this.right.parentSub;
			}
		} else if (this.parent.constructor === ID) {
			const parentItem = getItem(store, this.parent);
			if (parentItem.constructor === GC) {
				this.parent = null;
			} else {
				this.parent = /** @type {ContentType} */ (parentItem.content).type;
			}
		}
		return null;
	}

	/**
	 * @param {Transaction} transaction
	 * @param {number} offset
	 */
	integrate(transaction, offset) {
		if (offset > 0) {
			this.id.clock += offset;
			this.left = getItemCleanEnd(transaction, transaction.doc.store, createID(this.id.client, this.id.clock - 1));
			this.origin = this.left.lastId;
			this.content = this.content.splice(offset);
			this.length -= offset;
		}

		if (this.parent) {
			if ((!this.left && (!this.right || this.right.left !== null)) || (this.left && this.left.right !== this.right)) {
				/**
				 * @type {Item|null}
				 */
				let left = this.left;

				/**
				 * @type {Item|null}
				 */
				let o;
				// set o to the first conflicting item
				if (left !== null) {
					o = left.right;
				} else if (this.parentSub !== null) {
					o = /** @type {AbstractType<any>} */ (this.parent)._map.get(this.parentSub) || null;
					while (o !== null && o.left !== null) {
						o = o.left;
					}
				} else {
					o = /** @type {AbstractType<any>} */ (this.parent)._start;
				}
				// TODO: use something like DeleteSet here (a tree implementation would be best)
				// @todo use global set definitions
				/**
				 * @type {Set<Item>}
				 */
				const conflictingItems = new Set();
				/**
				 * @type {Set<Item>}
				 */
				const itemsBeforeOrigin = new Set();
				// Let c in conflictingItems, b in itemsBeforeOrigin
				// ***{origin}bbbb{this}{c,b}{c,b}{o}***
				// Note that conflictingItems is a subset of itemsBeforeOrigin
				while (o !== null && o !== this.right) {
					itemsBeforeOrigin.add(o);
					conflictingItems.add(o);
					if (compareIDs(this.origin, o.origin)) {
						// case 1
						if (o.id.client < this.id.client) {
							left = o;
							conflictingItems.clear();
						} else if (compareIDs(this.rightOrigin, o.rightOrigin)) {
							// this and o are conflicting and point to the same integration points. The id decides which item comes first.
							// Since this is to the left of o, we can break here
							break;
						} // else, o might be integrated before an item that this conflicts with. If so, we will find it in the next iterations
					} else if (o.origin !== null && itemsBeforeOrigin.has(getItem(transaction.doc.store, o.origin))) {
						// use getItem instead of getItemCleanEnd because we don't want / need to split items.
						// case 2
						if (!conflictingItems.has(getItem(transaction.doc.store, o.origin))) {
							left = o;
							conflictingItems.clear();
						}
					} else {
						break;
					}
					o = o.right;
				}
				this.left = left;
			}
			// reconnect left/right + update parent map/start if necessary
			if (this.left !== null) {
				const right = this.left.right;
				this.right = right;
				this.left.right = this;
			} else {
				let r;
				if (this.parentSub !== null) {
					r = /** @type {AbstractType<any>} */ (this.parent)._map.get(this.parentSub) || null;
					while (r !== null && r.left !== null) {
						r = r.left;
					}
				} else {
					r = /** @type {AbstractType<any>} */ (this.parent)._start;
					/** @type {AbstractType<any>} */ (this.parent)._start = this;
				}
				this.right = r;
			}
			if (this.right !== null) {
				this.right.left = this;
			} else if (this.parentSub !== null) {
				// set as current parent value if right === null and this is parentSub
				/** @type {AbstractType<any>} */ (this.parent)._map.set(this.parentSub, this);
				if (this.left !== null) {
					// this is the current attribute value of parent. delete right
					this.left.delete(transaction);
				}
			}
			// adjust length of parent
			if (this.parentSub === null && this.countable && !this.deleted) {
				/** @type {AbstractType<any>} */ (this.parent)._length += this.length;
			}
			addStruct(transaction.doc.store, this);
			this.content.integrate(transaction, this);
			// add parent to transaction.changed
			addChangedTypeToTransaction(transaction, /** @type {AbstractType<any>} */ (this.parent), this.parentSub);
			if (/** @type {AbstractType<any>} */ (this.parent._item !== null && /** @type {AbstractType<any>} */ (this.parent)._item.deleted) || (this.parentSub !== null && this.right !== null)) {
				// delete if parent is deleted or if this is not the current attribute value of parent
				this.delete(transaction);
			}
		} else {
			// parent is not defined. Integrate GC struct instead
			new GC(this.id, this.length).integrate(transaction, 0);
		}
	}

	/**
	 * Returns the next non-deleted item
	 */
	get next() {
		let n = this.right;
		while (n !== null && n.deleted) {
			n = n.right;
		}
		return n;
	}

	/**
	 * Returns the previous non-deleted item
	 */
	get prev() {
		let n = this.left;
		while (n !== null && n.deleted) {
			n = n.left;
		}
		return n;
	}

	/**
	 * Computes the last content address of this Item.
	 */
	get lastId() {
		// allocating ids is pretty costly because of the amount of ids created, so we try to reuse whenever possible
		return this.length === 1 ? this.id : createID(this.id.client, this.id.clock + this.length - 1);
	}

	/**
	 * Try to merge two items
	 *
	 * @param {Item} right
	 * @return {boolean}
	 */
	mergeWith(right) {
		if (
			this.constructor === right.constructor &&
			compareIDs(right.origin, this.lastId) &&
			this.right === right &&
			compareIDs(this.rightOrigin, right.rightOrigin) &&
			this.id.client === right.id.client &&
			this.id.clock + this.length === right.id.clock &&
			this.deleted === right.deleted &&
			this.redone === null &&
			right.redone === null &&
			this.content.constructor === right.content.constructor &&
			this.content.mergeWith(right.content)
		) {
			const searchMarker = /** @type {AbstractType<any>} */ (this.parent)._searchMarker;
			if (searchMarker) {
				searchMarker.forEach((marker) => {
					if (marker.p === right) {
						// right is going to be "forgotten" so we need to update the marker
						marker.p = this;
						// adjust marker index
						if (!this.deleted && this.countable) {
							marker.index -= this.length;
						}
					}
				});
			}
			if (right.keep) {
				this.keep = true;
			}
			this.right = right.right;
			if (this.right !== null) {
				this.right.left = this;
			}
			this.length += right.length;
			return true;
		}
		return false;
	}

	/**
	 * Mark this Item as deleted.
	 *
	 * @param {Transaction} transaction
	 */
	delete(transaction) {
		if (!this.deleted) {
			const parent = /** @type {AbstractType<any>} */ (this.parent);
			// adjust the length of parent
			if (this.countable && this.parentSub === null) {
				parent._length -= this.length;
			}
			this.markDeleted();
			addToDeleteSet(transaction.deleteSet, this.id.client, this.id.clock, this.length);
			addChangedTypeToTransaction(transaction, parent, this.parentSub);
			this.content.delete(transaction);
		}
	}

	/**
	 * @param {StructStore} store
	 * @param {boolean} parentGCd
	 */
	gc(store, parentGCd) {
		if (!this.deleted) {
			throw unexpectedCase();
		}
		this.content.gc(store);
		if (parentGCd) {
			replaceStruct(store, this, new GC(this.id, this.length));
		} else {
			this.content = new ContentDeleted(this.length);
		}
	}

	/**
	 * Transform the properties of this type to binary and write it to an
	 * BinaryEncoder.
	 *
	 * This is called when this Item is sent to a remote peer.
	 *
	 * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder The encoder to write data to.
	 * @param {number} offset
	 */
	write(encoder, offset) {
		const origin = offset > 0 ? createID(this.id.client, this.id.clock + offset - 1) : this.origin;
		const rightOrigin = this.rightOrigin;
		const parentSub = this.parentSub;
		const info =
			(this.content.getRef() & BITS5) |
			(origin === null ? 0 : BIT8) | // origin is defined
			(rightOrigin === null ? 0 : BIT7) | // right origin is defined
			(parentSub === null ? 0 : BIT6); // parentSub is non-null
		encoder.writeInfo(info);
		if (origin !== null) {
			encoder.writeLeftID(origin);
		}
		if (rightOrigin !== null) {
			encoder.writeRightID(rightOrigin);
		}
		if (origin === null && rightOrigin === null) {
			const parent = /** @type {AbstractType<any>} */ (this.parent);
			if (parent._item !== undefined) {
				const parentItem = parent._item;
				if (parentItem === null) {
					// parent type on y._map
					// find the correct key
					const ykey = findRootTypeKey(parent);
					encoder.writeParentInfo(true); // write parentYKey
					encoder.writeString(ykey);
				} else {
					encoder.writeParentInfo(false); // write parent id
					encoder.writeLeftID(parentItem.id);
				}
			} else if (parent.constructor === String) {
				// this edge case was added by differential updates
				encoder.writeParentInfo(true); // write parentYKey
				encoder.writeString(parent);
			} else if (parent.constructor === ID) {
				encoder.writeParentInfo(false); // write parent id
				encoder.writeLeftID(parent);
			} else {
				unexpectedCase();
			}
			if (parentSub !== null) {
				encoder.writeString(parentSub);
			}
		}
		this.content.write(encoder, offset);
	}
};

/**
 * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
 * @param {number} info
 */
const readItemContent = (decoder, info) => contentRefs[info & BITS5](decoder);

/**
 * A lookup map for reading Item content.
 *
 * @type {Array<function(UpdateDecoderV1 | UpdateDecoderV2):AbstractContent>}
 */
const contentRefs = [
	() => {
		unexpectedCase();
	}, // GC is not ItemContent
	readContentDeleted, // 1
	readContentJSON, // 2
	readContentBinary, // 3
	readContentString, // 4
	readContentEmbed, // 5
	readContentFormat, // 6
	readContentType, // 7
	readContentAny, // 8
	readContentDoc, // 9
	() => {
		unexpectedCase();
	}, // 10 - Skip is not ItemContent
];

const structSkipRefNumber = 10;

/**
 * @private
 */
class Skip extends AbstractStruct {
	get deleted() {
		return true;
	}

	delete() {}

	/**
	 * @param {Skip} right
	 * @return {boolean}
	 */
	mergeWith(right) {
		if (this.constructor !== right.constructor) {
			return false;
		}
		this.length += right.length;
		return true;
	}

	/**
	 * @param {Transaction} transaction
	 * @param {number} offset
	 */
	integrate(transaction, offset) {
		// skip structs cannot be integrated
		unexpectedCase();
	}

	/**
	 * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
	 * @param {number} offset
	 */
	write(encoder, offset) {
		encoder.writeInfo(structSkipRefNumber);
		// write as VarUint because Skips can't make use of predictable length-encoding
		writeVarUint(encoder.restEncoder, this.length - offset);
	}

	/**
	 * @param {Transaction} transaction
	 * @param {StructStore} store
	 * @return {null | number}
	 */
	getMissing(transaction, store) {
		return null;
	}
}

/** eslint-env browser */

const glo = /** @type {any} */ (
	typeof globalThis !== "undefined" ? globalThis
	: typeof window !== "undefined" ? window
		// @ts-ignore
	: typeof global !== "undefined" ? global
	: {}
);

const importIdentifier = "__ $YJS$ __";

if (glo[importIdentifier] === true) {
	/**
	 * Dear reader of this message. Please take this seriously.
	 *
	 * If you see this message, make sure that you only import one version of Yjs. In many cases,
	 * your package manager installs two versions of Yjs that are used by different packages within your project.
	 * Another reason for this message is that some parts of your project use the commonjs version of Yjs
	 * and others use the EcmaScript version of Yjs.
	 *
	 * This often leads to issues that are hard to debug. We often need to perform constructor checks,
	 * e.g. `struct instanceof GC`. If you imported different versions of Yjs, it is impossible for us to
	 * do the constructor checks anymore - which might break the CRDT algorithm.
	 *
	 * https://github.com/yjs/yjs/issues/438
	 */
	console.error("Yjs was already imported. This breaks constructor checks and will lead to issues! - https://github.com/yjs/yjs/issues/438");
}
glo[importIdentifier] = true;

/**
 * Mutual exclude for JavaScript.
 *
 * @module mutex
 */

/**
 * @callback mutex
 * @param {function():void} cb Only executed when this mutex is not in the current stack
 * @param {function():void} [elseCb] Executed when this mutex is in the current stack
 */

/**
 * Creates a mutual exclude function with the following property:
 *
 * ```js
 * const mutex = createMutex()
 * mutex(() => {
 *   // This function is immediately executed
 *   mutex(() => {
 *     // This function is not executed, as the mutex is already active.
 *   })
 * })
 * ```
 *
 * @return {mutex} A mutual exclude function
 * @public
 */
const createMutex = () => {
	let token = true;
	return (f, g) => {
		if (token) {
			token = false;
			try {
				f();
			} finally {
				token = true;
			}
		} else if (g !== undefined) {
			g();
		}
	};
};

/**
 * Efficient diffs.
 *
 * @module diff
 */

/**
 * A SimpleDiff describes a change on a String.
 *
 * ```js
 * console.log(a) // the old value
 * console.log(b) // the updated value
 * // Apply changes of diff (pseudocode)
 * a.remove(diff.index, diff.remove) // Remove `diff.remove` characters
 * a.insert(diff.index, diff.insert) // Insert `diff.insert`
 * a === b // values match
 * ```
 *
 * @template {string|Array<any>} T
 * @typedef {Object} SimpleDiff
 * @property {Number} index The index where changes were applied
 * @property {Number} remove The number of characters to delete starting
 *                                  at `index`.
 * @property {T} insert The new text to insert at `index` after applying
 */

const highSurrogateRegex = /[\uD800-\uDBFF]/;
const lowSurrogateRegex = /[\uDC00-\uDFFF]/;

/**
 * Create a diff between two strings. This diff implementation is highly
 * efficient, but not very sophisticated.
 *
 * @function
 *
 * @param {string} a The old version of the string
 * @param {string} b The updated version of the string
 * @return {SimpleDiff<string>} The diff description.
 */
const simpleDiffString = (a, b) => {
	let left = 0; // number of same characters counting from left
	let right = 0; // number of same characters counting from right
	while (left < a.length && left < b.length && a[left] === b[left]) {
		left++;
	}
	// If the last same character is a high surrogate, we need to rollback to the previous character
	if (left > 0 && highSurrogateRegex.test(a[left - 1])) left--;
	while (right + left < a.length && right + left < b.length && a[a.length - right - 1] === b[b.length - right - 1]) {
		right++;
	}
	// If the last same character is a low surrogate, we need to rollback to the previous character
	if (right > 0 && lowSurrogateRegex.test(a[a.length - right])) right--;
	return {
		index: left,
		remove: a.length - left - right,
		insert: b.slice(left, b.length - right),
	};
};

/**
 * @todo Remove in favor of simpleDiffString
 * @deprecated
 */
const simpleDiff = simpleDiffString;

/**
 * The unique prosemirror plugin key for syncPlugin
 *
 * @public
 */
const ySyncPluginKey = new PluginKey("y-sync");

/**
 * The unique prosemirror plugin key for undoPlugin
 *
 * @public
 * @type {PluginKey<import('./undo-plugin').UndoPluginState>}
 */
const yUndoPluginKey = new PluginKey("y-undo");

/**
 * The unique prosemirror plugin key for cursorPlugin
 *
 * @public
 */
const yCursorPluginKey = new PluginKey("yjs-cursor");

/**
 * @module sha256
 * Spec: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf
 * Resources:
 * - https://web.archive.org/web/20150315061807/http://csrc.nist.gov/groups/STM/cavp/documents/shs/sha256-384-512.pdf
 */

/**
 * @param {number} w - a 32bit uint
 * @param {number} shift
 */
const rotr = (w, shift) => (w >>> shift) | (w << (32 - shift));

/**
 * Helper for SHA-224 & SHA-256. See 4.1.2.
 * @param {number} x
 */
const sum0to256 = (x) => rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22);

/**
 * Helper for SHA-224 & SHA-256. See 4.1.2.
 * @param {number} x
 */
const sum1to256 = (x) => rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25);

/**
 * Helper for SHA-224 & SHA-256. See 4.1.2.
 * @param {number} x
 */
const sigma0to256 = (x) => rotr(x, 7) ^ rotr(x, 18) ^ (x >>> 3);

/**
 * Helper for SHA-224 & SHA-256. See 4.1.2.
 * @param {number} x
 */
const sigma1to256 = (x) => rotr(x, 17) ^ rotr(x, 19) ^ (x >>> 10);

// @todo don't init these variables globally

/**
 * See 4.2.2: Constant for sha256 & sha224
 * These words represent the first thirty-two bits of the fractional parts of
 * the cube roots of the first sixty-four prime numbers. In hex, these constant words are (from left to
 * right)
 */
const K$1 = new Uint32Array([
	0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
	0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
	0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
	0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
]);

/**
 * See 5.3.3. Initial hash value.
 *
 * These words were obtained by taking the first thirty-two bits of the fractional parts of the
 * square roots of the first eight prime numbers.
 *
 * @todo shouldn't be a global variable
 */
const HINIT = new Uint32Array([0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19]);

// time to beat: (large value < 4.35s)

class Hasher {
	constructor() {
		const buf = new ArrayBuffer(64 + 64 * 4);
		// Init working variables using a single arraybuffer
		this._H = new Uint32Array(buf, 0, 8);
		this._H.set(HINIT);
		// "Message schedule" - a working variable
		this._W = new Uint32Array(buf, 64, 64);
	}

	_updateHash() {
		const H = this._H;
		const W = this._W;
		for (let t = 16; t < 64; t++) {
			W[t] = sigma1to256(W[t - 2]) + W[t - 7] + sigma0to256(W[t - 15]) + W[t - 16];
		}
		let a = H[0];
		let b = H[1];
		let c = H[2];
		let d = H[3];
		let e = H[4];
		let f = H[5];
		let g = H[6];
		let h = H[7];
		for (let tt = 0, T1, T2; tt < 64; tt++) {
			T1 = (h + sum1to256(e) + ((e & f) ^ (~e & g)) + K$1[tt] + W[tt]) >>> 0;
			T2 = (sum0to256(a) + ((a & b) ^ (a & c) ^ (b & c))) >>> 0;
			h = g;
			g = f;
			f = e;
			e = (d + T1) >>> 0;
			d = c;
			c = b;
			b = a;
			a = (T1 + T2) >>> 0;
		}
		H[0] += a;
		H[1] += b;
		H[2] += c;
		H[3] += d;
		H[4] += e;
		H[5] += f;
		H[6] += g;
		H[7] += h;
	}

	/**
	 * Returns a 32-byte hash.
	 *
	 * @param {Uint8Array} data
	 */
	digest(data) {
		let i = 0;
		for (; i + 56 <= data.length; ) {
			// write data in big endianess
			let j = 0;
			for (; j < 16 && i + 3 < data.length; j++) {
				this._W[j] = (data[i++] << 24) | (data[i++] << 16) | (data[i++] << 8) | data[i++];
			}
			if (i % 64 !== 0) {
				// there is still room to write partial content and the ending bit.
				this._W.fill(0, j, 16);
				while (i < data.length) {
					this._W[j] |= data[i] << ((3 - (i % 4)) * 8);
					i++;
				}
				this._W[j] |= BIT8 << ((3 - (i % 4)) * 8);
			}
			this._updateHash();
		}
		// same check as earlier - the ending bit has been written
		const isPaddedWith1 = i % 64 !== 0;
		this._W.fill(0, 0, 16);
		let j = 0;
		for (; i < data.length; j++) {
			for (let ci = 3; ci >= 0 && i < data.length; ci--) {
				this._W[j] |= data[i++] << (ci * 8);
			}
		}
		// Write padding of the message. See 5.1.2.
		if (!isPaddedWith1) {
			this._W[j - (i % 4 === 0 ? 0 : 1)] |= BIT8 << ((3 - (i % 4)) * 8);
		}
		// write length of message (size in bits) as 64 bit uint
		// @todo test that this works correctly
		this._W[14] = data.byteLength / BIT30; // same as data.byteLength >>> 30 - but works on floats
		this._W[15] = data.byteLength * 8;
		this._updateHash();
		// correct H endianness to use big endiannes and return a Uint8Array
		const dv = new Uint8Array(32);
		for (let i = 0; i < this._H.length; i++) {
			for (let ci = 0; ci < 4; ci++) {
				dv[i * 4 + ci] = this._H[i] >>> ((3 - ci) * 8);
			}
		}
		return dv;
	}
}

/**
 * Returns a 32-byte hash.
 *
 * @param {Uint8Array} data
 */
const digest = (data) => new Hasher().digest(data);

/**
 * Custom function to transform sha256 hash to N byte
 *
 * @param {Uint8Array} digest
 */
const _convolute = (digest) => {
	const N = 6;
	for (let i = N; i < digest.length; i++) {
		digest[i % N] = digest[i % N] ^ digest[i];
	}
	return digest.slice(0, N);
};

/**
 * @param {any} json
 */
const hashOfJSON = (json) => toBase64(_convolute(digest(encodeAny(json))));

/**
 * @module bindings/prosemirror
 */

/**
 * @param {Y.Item} item
 * @param {Y.Snapshot} [snapshot]
 */
const isVisible = (item, snapshot) =>
	snapshot === undefined ? !item.deleted : snapshot.sv.has(item.id.client) && /** @type {number} */ (snapshot.sv.get(item.id.client)) > item.id.clock && !isDeleted(snapshot.ds, item.id);

/**
 * Either a node if type is YXmlElement or an Array of text nodes if YXmlText
 * @typedef {Map<Y.AbstractType<any>, PModel.Node | Array<PModel.Node>>} ProsemirrorMapping
 */

/**
 * @typedef {Object} ColorDef
 * @property {string} ColorDef.light
 * @property {string} ColorDef.dark
 */

/**
 * @typedef {Object} YSyncOpts
 * @property {Array<ColorDef>} [YSyncOpts.colors]
 * @property {Map<string,ColorDef>} [YSyncOpts.colorMapping]
 * @property {Y.PermanentUserData|null} [YSyncOpts.permanentUserData]
 * @property {ProsemirrorMapping} [YSyncOpts.mapping]
 * @property {function} [YSyncOpts.onFirstRender] Fired when the content from Yjs is initially rendered to ProseMirror
 */

/**
 * @type {Array<ColorDef>}
 */
const defaultColors = [{ light: "#ecd44433", dark: "#ecd444" }];

/**
 * @param {Map<string,ColorDef>} colorMapping
 * @param {Array<ColorDef>} colors
 * @param {string} user
 * @return {ColorDef}
 */
const getUserColor = (colorMapping, colors, user) => {
	// @todo do not hit the same color twice if possible
	if (!colorMapping.has(user)) {
		if (colorMapping.size < colors.length) {
			const usedColors = create$8();
			colorMapping.forEach((color) => usedColors.add(color));
			colors = colors.filter((color) => !usedColors.has(color));
		}
		colorMapping.set(user, oneOf$1(colors));
	}
	return /** @type {ColorDef} */ (colorMapping.get(user));
};

/**
 * This plugin listens to changes in prosemirror view and keeps yXmlState and view in sync.
 *
 * This plugin also keeps references to the type and the shared document so other plugins can access it.
 * @param {Y.XmlFragment} yXmlFragment
 * @param {YSyncOpts} opts
 * @return {any} Returns a prosemirror plugin that binds to this type
 */
const ySyncPlugin = (yXmlFragment, { colors = defaultColors, colorMapping = new Map(), permanentUserData = null, onFirstRender = () => {}, mapping } = {}) => {
	let initialContentChanged = false;
	const binding = new ProsemirrorBinding(yXmlFragment, mapping);
	const plugin = new Plugin({
		props: {
			editable: (state) => {
				const syncState = ySyncPluginKey.getState(state);
				return syncState.snapshot == null && syncState.prevSnapshot == null;
			},
		},
		key: ySyncPluginKey,
		state: {
			/**
			 * @returns {any}
			 */
			init: (_initargs, _state) => {
				return {
					type: yXmlFragment,
					doc: yXmlFragment.doc,
					binding,
					snapshot: null,
					prevSnapshot: null,
					isChangeOrigin: false,
					isUndoRedoOperation: false,
					addToHistory: true,
					colors,
					colorMapping,
					permanentUserData,
				};
			},
			apply: (tr, pluginState) => {
				const change = tr.getMeta(ySyncPluginKey);
				if (change !== undefined) {
					pluginState = Object.assign({}, pluginState);
					for (const key in change) {
						pluginState[key] = change[key];
					}
				}
				pluginState.addToHistory = tr.getMeta("addToHistory") !== false;
				// always set isChangeOrigin. If undefined, this is not change origin.
				pluginState.isChangeOrigin = change !== undefined && !!change.isChangeOrigin;
				pluginState.isUndoRedoOperation = change !== undefined && !!change.isChangeOrigin && !!change.isUndoRedoOperation;
				if (binding.prosemirrorView !== null) {
					if (change !== undefined && (change.snapshot != null || change.prevSnapshot != null)) {
						// snapshot changed, rerender next
						timeout(0, () => {
							if (binding.prosemirrorView == null) {
								return;
							}
							if (change.restore == null) {
								binding._renderSnapshot(change.snapshot, change.prevSnapshot, pluginState);
							} else {
								binding._renderSnapshot(change.snapshot, change.snapshot, pluginState);
								// reset to current prosemirror state
								delete pluginState.restore;
								delete pluginState.snapshot;
								delete pluginState.prevSnapshot;
								binding.mux(() => {
									binding._prosemirrorChanged(binding.prosemirrorView.state.doc);
								});
							}
						});
					}
				}
				return pluginState;
			},
		},
		view: (view) => {
			binding.initView(view);
			if (mapping == null) {
				// force rerender to update the bindings mapping
				binding._forceRerender();
			}
			onFirstRender();
			return {
				update: () => {
					const pluginState = plugin.getState(view.state);
					if (pluginState.snapshot == null && pluginState.prevSnapshot == null) {
						if (
							// If the content doesn't change initially, we don't render anything to Yjs
							// If the content was cleared by a user action, we want to catch the change and
							// represent it in Yjs
							initialContentChanged ||
							view.state.doc.content.findDiffStart(view.state.doc.type.createAndFill().content) !== null
						) {
							initialContentChanged = true;
							if (pluginState.addToHistory === false && !pluginState.isChangeOrigin) {
								const yUndoPluginState = yUndoPluginKey.getState(view.state);
								/**
								 * @type {Y.UndoManager}
								 */
								const um = yUndoPluginState && yUndoPluginState.undoManager;
								if (um) {
									um.stopCapturing();
								}
							}
							binding.mux(() => {
								/** @type {Y.Doc} */ (pluginState.doc).transact((tr) => {
									tr.meta.set("addToHistory", pluginState.addToHistory);
									binding._prosemirrorChanged(view.state.doc);
								}, ySyncPluginKey);
							});
						}
					}
				},
				destroy: () => {
					binding.destroy();
				},
			};
		},
	});
	return plugin;
};

/**
 * @param {import('prosemirror-state').Transaction} tr
 * @param {ReturnType<typeof getRelativeSelection>} relSel
 * @param {ProsemirrorBinding} binding
 */
const restoreRelativeSelection = (tr, relSel, binding) => {
	if (relSel !== null && relSel.anchor !== null && relSel.head !== null) {
		if (relSel.type === "all") {
			tr.setSelection(new AllSelection(tr.doc));
		} else if (relSel.type === "node") {
			const anchor = relativePositionToAbsolutePosition(binding.doc, binding.type, relSel.anchor, binding.mapping);
			tr.setSelection(NodeSelection.create(tr.doc, anchor));
		} else {
			const anchor = relativePositionToAbsolutePosition(binding.doc, binding.type, relSel.anchor, binding.mapping);
			const head = relativePositionToAbsolutePosition(binding.doc, binding.type, relSel.head, binding.mapping);
			if (anchor !== null && head !== null) {
				const sel = TextSelection.between(tr.doc.resolve(anchor), tr.doc.resolve(head));
				tr.setSelection(sel);
			}
		}
	}
};

/**
 * @param {ProsemirrorBinding} pmbinding
 * @param {import('prosemirror-state').EditorState} state
 */
const getRelativeSelection = (pmbinding, state) => ({
	type: /** @type {any} */ (state.selection).jsonID,
	anchor: absolutePositionToRelativePosition(state.selection.anchor, pmbinding.type, pmbinding.mapping),
	head: absolutePositionToRelativePosition(state.selection.head, pmbinding.type, pmbinding.mapping),
});

/**
 * Binding for prosemirror.
 *
 * @protected
 */
class ProsemirrorBinding {
	/**
	 * @param {Y.XmlFragment} yXmlFragment The bind source
	 * @param {ProsemirrorMapping} mapping
	 */
	constructor(yXmlFragment, mapping = new Map()) {
		this.type = yXmlFragment;
		/**
		 * this will be set once the view is created
		 * @type {any}
		 */
		this.prosemirrorView = null;
		this.mux = createMutex();
		this.mapping = mapping;
		/**
		 * Is overlapping mark - i.e. mark does not exclude itself.
		 *
		 * @type {Map<import('prosemirror-model').MarkType, boolean>}
		 */
		this.isOMark = new Map();
		this._observeFunction = this._typeChanged.bind(this);
		/**
		 * @type {Y.Doc}
		 */
		// @ts-ignore
		this.doc = yXmlFragment.doc;
		/**
		 * current selection as relative positions in the Yjs model
		 */
		this.beforeTransactionSelection = null;
		this.beforeAllTransactions = () => {
			if (this.beforeTransactionSelection === null && this.prosemirrorView != null) {
				this.beforeTransactionSelection = getRelativeSelection(this, this.prosemirrorView.state);
			}
		};
		this.afterAllTransactions = () => {
			this.beforeTransactionSelection = null;
		};
		this._domSelectionInView = null;
	}

	/**
	 * Create a transaction for changing the prosemirror state.
	 *
	 * @returns
	 */
	get _tr() {
		return this.prosemirrorView.state.tr.setMeta("addToHistory", false);
	}

	_isLocalCursorInView() {
		if (!this.prosemirrorView.hasFocus()) return false;
		if (isBrowser && this._domSelectionInView === null) {
			// Calculate the domSelectionInView and clear by next tick after all events are finished
			timeout(0, () => {
				this._domSelectionInView = null;
			});
			this._domSelectionInView = this._isDomSelectionInView();
		}
		return this._domSelectionInView;
	}

	_isDomSelectionInView() {
		const selection = this.prosemirrorView._root.getSelection();

		if (selection == null || selection.anchorNode == null) return false;

		const range = this.prosemirrorView._root.createRange();
		range.setStart(selection.anchorNode, selection.anchorOffset);
		range.setEnd(selection.focusNode, selection.focusOffset);

		// This is a workaround for an edgecase where getBoundingClientRect will
		// return zero values if the selection is collapsed at the start of a newline
		// see reference here: https://stackoverflow.com/a/59780954
		const rects = range.getClientRects();
		if (rects.length === 0) {
			// probably buggy newline behavior, explicitly select the node contents
			if (range.startContainer && range.collapsed) {
				range.selectNodeContents(range.startContainer);
			}
		}

		const bounding = range.getBoundingClientRect();
		const documentElement = doc.documentElement;

		return (
			bounding.bottom >= 0 &&
			bounding.right >= 0 &&
			bounding.left <= (window.innerWidth || documentElement.clientWidth || 0) &&
			bounding.top <= (window.innerHeight || documentElement.clientHeight || 0)
		);
	}

	/**
	 * @param {Y.Snapshot} snapshot
	 * @param {Y.Snapshot} prevSnapshot
	 */
	renderSnapshot(snapshot, prevSnapshot) {
		if (!prevSnapshot) {
			prevSnapshot = createSnapshot(createDeleteSet(), new Map());
		}
		this.prosemirrorView.dispatch(this._tr.setMeta(ySyncPluginKey, { snapshot, prevSnapshot }));
	}

	unrenderSnapshot() {
		this.mapping.clear();
		this.mux(() => {
			const fragmentContent = this.type
				.toArray()
				.map((t) => createNodeFromYElement(/** @type {Y.XmlElement} */ (t), this.prosemirrorView.state.schema, this))
				.filter((n) => n !== null);
			// @ts-ignore
			const tr = this._tr.replace(0, this.prosemirrorView.state.doc.content.size, new Slice(Fragment.from(fragmentContent), 0, 0));
			tr.setMeta(ySyncPluginKey, { snapshot: null, prevSnapshot: null });
			this.prosemirrorView.dispatch(tr);
		});
	}

	_forceRerender() {
		this.mapping.clear();
		this.mux(() => {
			// If this is a forced rerender, this might neither happen as a pm change nor within a Yjs
			// transaction. Then the "before selection" doesn't exist. In this case, we need to create a
			// relative position before replacing content. Fixes #126
			const sel = this.beforeTransactionSelection !== null ? null : this.prosemirrorView.state.selection;
			const fragmentContent = this.type
				.toArray()
				.map((t) => createNodeFromYElement(/** @type {Y.XmlElement} */ (t), this.prosemirrorView.state.schema, this))
				.filter((n) => n !== null);
			// @ts-ignore
			const tr = this._tr.replace(0, this.prosemirrorView.state.doc.content.size, new Slice(Fragment.from(fragmentContent), 0, 0));
			if (sel) {
				/**
				 * If the Prosemirror document we just created from this.type is
				 * smaller than the previous document, the selection might be
				 * out of bound, which would make Prosemirror throw an error.
				 */
				const clampedAnchor = min$1(max$1(sel.anchor, 0), tr.doc.content.size);
				const clampedHead = min$1(max$1(sel.head, 0), tr.doc.content.size);

				tr.setSelection(TextSelection.create(tr.doc, clampedAnchor, clampedHead));
			}
			this.prosemirrorView.dispatch(tr.setMeta(ySyncPluginKey, { isChangeOrigin: true, binding: this }));
		});
	}

	/**
	 * @param {Y.Snapshot|Uint8Array} snapshot
	 * @param {Y.Snapshot|Uint8Array} prevSnapshot
	 * @param {Object} pluginState
	 */
	_renderSnapshot(snapshot$1, prevSnapshot, pluginState) {
		/**
		 * The document that contains the full history of this document.
		 * @type {Y.Doc}
		 */
		let historyDoc = this.doc;
		let historyType = this.type;
		if (!snapshot$1) {
			snapshot$1 = snapshot(this.doc);
		}
		if (snapshot$1 instanceof Uint8Array || prevSnapshot instanceof Uint8Array) {
			if (!(snapshot$1 instanceof Uint8Array) || !(prevSnapshot instanceof Uint8Array)) {
				// expected both snapshots to be v2 updates
				unexpectedCase();
			}
			historyDoc = new Doc({ gc: false });
			applyUpdateV2(historyDoc, prevSnapshot);
			prevSnapshot = snapshot(historyDoc);
			applyUpdateV2(historyDoc, snapshot$1);
			snapshot$1 = snapshot(historyDoc);
			if (historyType._item === null) {
				/**
				 * If is a root type, we need to find the root key in the initial document
				 * and use it to get the history type.
				 */
				const rootKey = Array.from(this.doc.share.keys()).find((key) => this.doc.share.get(key) === this.type);
				historyType = historyDoc.getXmlFragment(rootKey);
			} else {
				/**
				 * If it is a sub type, we use the item id to find the history type.
				 */
				const historyStructs = historyDoc.store.clients.get(historyType._item.id.client) ?? [];
				const itemIndex = findIndexSS(historyStructs, historyType._item.id.clock);
				const item = /** @type {Y.Item} */ (historyStructs[itemIndex]);
				const content = /** @type {Y.ContentType} */ (item.content);
				historyType = /** @type {Y.XmlFragment} */ (content.type);
			}
		}
		// clear mapping because we are going to rerender
		this.mapping.clear();
		this.mux(() => {
			historyDoc.transact((transaction) => {
				// before rendering, we are going to sanitize ops and split deleted ops
				// if they were deleted by seperate users.
				/**
				 * @type {Y.PermanentUserData}
				 */
				const pud = pluginState.permanentUserData;
				if (pud) {
					pud.dss.forEach((ds) => {
						iterateDeletedStructs(transaction, ds, (_item) => {});
					});
				}
				/**
				 * @param {'removed'|'added'} type
				 * @param {Y.ID} id
				 */
				const computeYChange = (type, id) => {
					const user = type === "added" ? pud.getUserByClientId(id.client) : pud.getUserByDeletedId(id);
					return {
						user,
						type,
						color: getUserColor(pluginState.colorMapping, pluginState.colors, user),
					};
				};
				// Create document fragment and render
				const fragmentContent = typeListToArraySnapshot(historyType, new Snapshot(prevSnapshot.ds, snapshot$1.sv))
					.map((t) => {
						if (!t._item.deleted || isVisible(t._item, snapshot$1) || isVisible(t._item, prevSnapshot)) {
							return createNodeFromYElement(t, this.prosemirrorView.state.schema, { mapping: new Map(), isOMark: new Map() }, snapshot$1, prevSnapshot, computeYChange);
						} else {
							// No need to render elements that are not visible by either snapshot.
							// If a client adds and deletes content in the same snapshot the element is not visible by either snapshot.
							return null;
						}
					})
					.filter((n) => n !== null);
				// @ts-ignore
				const tr = this._tr.replace(0, this.prosemirrorView.state.doc.content.size, new Slice(Fragment.from(fragmentContent), 0, 0));
				this.prosemirrorView.dispatch(tr.setMeta(ySyncPluginKey, { isChangeOrigin: true }));
			}, ySyncPluginKey);
		});
	}

	/**
	 * @param {Array<Y.YEvent<any>>} events
	 * @param {Y.Transaction} transaction
	 */
	_typeChanged(events, transaction) {
		if (this.prosemirrorView == null) return;
		const syncState = ySyncPluginKey.getState(this.prosemirrorView.state);
		if (events.length === 0 || syncState.snapshot != null || syncState.prevSnapshot != null) {
			// drop out if snapshot is active
			this.renderSnapshot(syncState.snapshot, syncState.prevSnapshot);
			return;
		}
		this.mux(() => {
			/**
			 * @param {any} _
			 * @param {Y.AbstractType<any>} type
			 */
			const delType = (_, type) => this.mapping.delete(type);
			iterateDeletedStructs(transaction, transaction.deleteSet, (struct) => {
				if (struct.constructor === Item$1) {
					const type = /** @type {Y.ContentType} */ (/** @type {Y.Item} */ (struct).content).type;
					type && this.mapping.delete(type);
				}
			});
			transaction.changed.forEach(delType);
			transaction.changedParentTypes.forEach(delType);
			const fragmentContent = this.type
				.toArray()
				.map((t) => createNodeIfNotExists(/** @type {Y.XmlElement | Y.XmlHook} */ (t), this.prosemirrorView.state.schema, this))
				.filter((n) => n !== null);
			// @ts-ignore
			let tr = this._tr.replace(0, this.prosemirrorView.state.doc.content.size, new Slice(Fragment.from(fragmentContent), 0, 0));
			restoreRelativeSelection(tr, this.beforeTransactionSelection, this);
			tr = tr.setMeta(ySyncPluginKey, { isChangeOrigin: true, isUndoRedoOperation: transaction.origin instanceof UndoManager });
			if (this.beforeTransactionSelection !== null && this._isLocalCursorInView()) {
				tr.scrollIntoView();
			}
			this.prosemirrorView.dispatch(tr);
		});
	}

	/**
	 * @param {import('prosemirror-model').Node} doc
	 */
	_prosemirrorChanged(doc) {
		this.doc.transact(() => {
			updateYFragment(this.doc, this.type, doc, this);
			this.beforeTransactionSelection = getRelativeSelection(this, this.prosemirrorView.state);
		}, ySyncPluginKey);
	}

	/**
	 * View is ready to listen to changes. Register observers.
	 * @param {any} prosemirrorView
	 */
	initView(prosemirrorView) {
		if (this.prosemirrorView != null) this.destroy();
		this.prosemirrorView = prosemirrorView;
		this.doc.on("beforeAllTransactions", this.beforeAllTransactions);
		this.doc.on("afterAllTransactions", this.afterAllTransactions);
		this.type.observeDeep(this._observeFunction);
	}

	destroy() {
		if (this.prosemirrorView == null) return;
		this.prosemirrorView = null;
		this.type.unobserveDeep(this._observeFunction);
		this.doc.off("beforeAllTransactions", this.beforeAllTransactions);
		this.doc.off("afterAllTransactions", this.afterAllTransactions);
	}
}

/**
 * @private
 * @param {Y.XmlElement | Y.XmlHook} el
 * @param {PModel.Schema} schema
 * @param {BindingMetadata} meta
 * @param {Y.Snapshot} [snapshot]
 * @param {Y.Snapshot} [prevSnapshot]
 * @param {function('removed' | 'added', Y.ID):any} [computeYChange]
 * @return {PModel.Node | null}
 */
const createNodeIfNotExists = (el, schema, meta, snapshot, prevSnapshot, computeYChange) => {
	const node = /** @type {PModel.Node} */ (meta.mapping.get(el));
	if (node === undefined) {
		if (el instanceof YXmlElement) {
			return createNodeFromYElement(el, schema, meta, snapshot, prevSnapshot, computeYChange);
		} else {
			throw methodUnimplemented(); // we are currently not handling hooks
		}
	}
	return node;
};

/**
 * @private
 * @param {Y.XmlElement} el
 * @param {any} schema
 * @param {BindingMetadata} meta
 * @param {Y.Snapshot} [snapshot]
 * @param {Y.Snapshot} [prevSnapshot]
 * @param {function('removed' | 'added', Y.ID):any} [computeYChange]
 * @return {PModel.Node | null} Returns node if node could be created. Otherwise it deletes the yjs type and returns null
 */
const createNodeFromYElement = (el, schema, meta, snapshot, prevSnapshot, computeYChange) => {
	const children = [];
	/**
	 * @param {Y.XmlElement | Y.XmlText} type
	 */
	const createChildren = (type) => {
		if (type instanceof YXmlElement) {
			const n = createNodeIfNotExists(type, schema, meta, snapshot, prevSnapshot, computeYChange);
			if (n !== null) {
				children.push(n);
			}
		} else {
			// If the next ytext exists and was created by us, move the content to the current ytext.
			// This is a fix for #160 -- duplication of characters when two Y.Text exist next to each
			// other.
			const nextytext = /** @type {Y.ContentType} */ (type._item.right?.content)?.type;
			if (nextytext instanceof YText && !nextytext._item.deleted && nextytext._item.id.client === nextytext.doc.clientID) {
				type.applyDelta([{ retain: type.length }, ...nextytext.toDelta()]);
				nextytext.doc.transact((tr) => {
					nextytext._item.delete(tr);
				});
			}
			// now create the prosemirror text nodes
			const ns = createTextNodesFromYText(type, schema, meta, snapshot, prevSnapshot, computeYChange);
			if (ns !== null) {
				ns.forEach((textchild) => {
					if (textchild !== null) {
						children.push(textchild);
					}
				});
			}
		}
	};
	if (snapshot === undefined || prevSnapshot === undefined) {
		el.toArray().forEach(createChildren);
	} else {
		typeListToArraySnapshot(el, new Snapshot(prevSnapshot.ds, snapshot.sv)).forEach(createChildren);
	}
	try {
		const attrs = el.getAttributes(snapshot);
		if (snapshot !== undefined) {
			if (!isVisible(/** @type {Y.Item} */ (el._item), snapshot)) {
				attrs.ychange = computeYChange ? computeYChange("removed", /** @type {Y.Item} */ (el._item).id) : { type: "removed" };
			} else if (!isVisible(/** @type {Y.Item} */ (el._item), prevSnapshot)) {
				attrs.ychange = computeYChange ? computeYChange("added", /** @type {Y.Item} */ (el._item).id) : { type: "added" };
			}
		}
		const node = schema.node(el.nodeName, attrs, children);
		meta.mapping.set(el, node);
		return node;
	} catch (e) {
		// an error occured while creating the node. This is probably a result of a concurrent action.
		/** @type {Y.Doc} */ (el.doc).transact((transaction) => {
			/** @type {Y.Item} */ (el._item).delete(transaction);
		}, ySyncPluginKey);
		meta.mapping.delete(el);
		return null;
	}
};

/**
 * @private
 * @param {Y.XmlText} text
 * @param {import('prosemirror-model').Schema} schema
 * @param {BindingMetadata} _meta
 * @param {Y.Snapshot} [snapshot]
 * @param {Y.Snapshot} [prevSnapshot]
 * @param {function('removed' | 'added', Y.ID):any} [computeYChange]
 * @return {Array<PModel.Node>|null}
 */
const createTextNodesFromYText = (text, schema, _meta, snapshot, prevSnapshot, computeYChange) => {
	const nodes = [];
	const deltas = text.toDelta(snapshot, prevSnapshot, computeYChange);
	try {
		for (let i = 0; i < deltas.length; i++) {
			const delta = deltas[i];
			nodes.push(schema.text(delta.insert, attributesToMarks(delta.attributes, schema)));
		}
	} catch (e) {
		// an error occured while creating the node. This is probably a result of a concurrent action.
		/** @type {Y.Doc} */ (text.doc).transact((transaction) => {
			/** @type {Y.Item} */ (text._item).delete(transaction);
		}, ySyncPluginKey);
		return null;
	}
	// @ts-ignore
	return nodes;
};

/**
 * @private
 * @param {Array<any>} nodes prosemirror node
 * @param {BindingMetadata} meta
 * @return {Y.XmlText}
 */
const createTypeFromTextNodes = (nodes, meta) => {
	const type = new YXmlText();
	const delta = nodes.map((node) => ({
		// @ts-ignore
		insert: node.text,
		attributes: marksToAttributes(node.marks, meta),
	}));
	type.applyDelta(delta);
	meta.mapping.set(type, nodes);
	return type;
};

/**
 * @private
 * @param {any} node prosemirror node
 * @param {BindingMetadata} meta
 * @return {Y.XmlElement}
 */
const createTypeFromElementNode = (node, meta) => {
	const type = new YXmlElement(node.type.name);
	for (const key in node.attrs) {
		const val = node.attrs[key];
		if (val !== null && key !== "ychange") {
			type.setAttribute(key, val);
		}
	}
	type.insert(
		0,
		normalizePNodeContent(node).map((n) => createTypeFromTextOrElementNode(n, meta)),
	);
	meta.mapping.set(type, node);
	return type;
};

/**
 * @private
 * @param {PModel.Node|Array<PModel.Node>} node prosemirror text node
 * @param {BindingMetadata} meta
 * @return {Y.XmlElement|Y.XmlText}
 */
const createTypeFromTextOrElementNode = (node, meta) => (node instanceof Array ? createTypeFromTextNodes(node, meta) : createTypeFromElementNode(node, meta));

/**
 * @param {any} val
 */
const isObject = (val) => typeof val === "object" && val !== null;

/**
 * @param {any} pattrs
 * @param {any} yattrs
 */
const equalAttrs = (pattrs, yattrs) => {
	const keys = Object.keys(pattrs).filter((key) => pattrs[key] !== null);
	let eq = keys.length === (yattrs == null ? 0 : Object.keys(yattrs).filter((key) => yattrs[key] !== null).length);
	for (let i = 0; i < keys.length && eq; i++) {
		const key = keys[i];
		const l = pattrs[key];
		const r = yattrs[key];
		eq = key === "ychange" || l === r || (isObject(l) && isObject(r) && equalAttrs(l, r));
	}
	return eq;
};

/**
 * @typedef {Array<Array<PModel.Node>|PModel.Node>} NormalizedPNodeContent
 */

/**
 * @param {any} pnode
 * @return {NormalizedPNodeContent}
 */
const normalizePNodeContent = (pnode) => {
	const c = pnode.content.content;
	const res = [];
	for (let i = 0; i < c.length; i++) {
		const n = c[i];
		if (n.isText) {
			const textNodes = [];
			for (let tnode = c[i]; i < c.length && tnode.isText; tnode = c[++i]) {
				textNodes.push(tnode);
			}
			i--;
			res.push(textNodes);
		} else {
			res.push(n);
		}
	}
	return res;
};

/**
 * @param {Y.XmlText} ytext
 * @param {Array<any>} ptexts
 */
const equalYTextPText = (ytext, ptexts) => {
	const delta = ytext.toDelta();
	return (
		delta.length === ptexts.length &&
		delta.every(
			/** @type {(d:any,i:number) => boolean} */ (d, i) =>
				d.insert === /** @type {any} */ (ptexts[i]).text &&
				keys$1(d.attributes || {}).length === ptexts[i].marks.length &&
				every(d.attributes, (attr, yattrname) => {
					const markname = yattr2markname(yattrname);
					const pmarks = ptexts[i].marks;
					return equalAttrs(attr, pmarks.find(/** @param {any} mark */ (mark) => mark.type.name === markname)?.attrs);
				}),
		)
	);
};

/**
 * @param {Y.XmlElement|Y.XmlText|Y.XmlHook} ytype
 * @param {any|Array<any>} pnode
 */
const equalYTypePNode = (ytype, pnode) => {
	if (ytype instanceof YXmlElement && !(pnode instanceof Array) && matchNodeName(ytype, pnode)) {
		const normalizedContent = normalizePNodeContent(pnode);
		return ytype._length === normalizedContent.length && equalAttrs(ytype.getAttributes(), pnode.attrs) && ytype.toArray().every((ychild, i) => equalYTypePNode(ychild, normalizedContent[i]));
	}
	return ytype instanceof YXmlText && pnode instanceof Array && equalYTextPText(ytype, pnode);
};

/**
 * @param {PModel.Node | Array<PModel.Node> | undefined} mapped
 * @param {PModel.Node | Array<PModel.Node>} pcontent
 */
const mappedIdentity = (mapped, pcontent) =>
	mapped === pcontent || (mapped instanceof Array && pcontent instanceof Array && mapped.length === pcontent.length && mapped.every((a, i) => pcontent[i] === a));

/**
 * @param {Y.XmlElement} ytype
 * @param {PModel.Node} pnode
 * @param {BindingMetadata} meta
 * @return {{ foundMappedChild: boolean, equalityFactor: number }}
 */
const computeChildEqualityFactor = (ytype, pnode, meta) => {
	const yChildren = ytype.toArray();
	const pChildren = normalizePNodeContent(pnode);
	const pChildCnt = pChildren.length;
	const yChildCnt = yChildren.length;
	const minCnt = min$1(yChildCnt, pChildCnt);
	let left = 0;
	let right = 0;
	let foundMappedChild = false;
	for (; left < minCnt; left++) {
		const leftY = yChildren[left];
		const leftP = pChildren[left];
		if (mappedIdentity(meta.mapping.get(leftY), leftP)) {
			foundMappedChild = true; // definite (good) match!
		} else if (!equalYTypePNode(leftY, leftP)) {
			break;
		}
	}
	for (; left + right < minCnt; right++) {
		const rightY = yChildren[yChildCnt - right - 1];
		const rightP = pChildren[pChildCnt - right - 1];
		if (mappedIdentity(meta.mapping.get(rightY), rightP)) {
			foundMappedChild = true;
		} else if (!equalYTypePNode(rightY, rightP)) {
			break;
		}
	}
	return {
		equalityFactor: left + right,
		foundMappedChild,
	};
};

/**
 * @param {Y.Text} ytext
 */
const ytextTrans = (ytext) => {
	let str = "";
	/**
	 * @type {Y.Item|null}
	 */
	let n = ytext._start;
	const nAttrs = {};
	while (n !== null) {
		if (!n.deleted) {
			if (n.countable && n.content instanceof ContentString) {
				str += n.content.str;
			} else if (n.content instanceof ContentFormat) {
				nAttrs[n.content.key] = null;
			}
		}
		n = n.right;
	}
	return {
		str,
		nAttrs,
	};
};

/**
 * @todo test this more
 *
 * @param {Y.Text} ytext
 * @param {Array<any>} ptexts
 * @param {BindingMetadata} meta
 */
const updateYText = (ytext, ptexts, meta) => {
	meta.mapping.set(ytext, ptexts);
	const { nAttrs, str } = ytextTrans(ytext);
	const content = ptexts.map((p) => ({
		insert: /** @type {any} */ (p).text,
		attributes: Object.assign({}, nAttrs, marksToAttributes(p.marks, meta)),
	}));
	const { insert, remove, index } = simpleDiff(str, content.map((c) => c.insert).join(""));
	ytext.delete(index, remove);
	ytext.insert(index, insert);
	ytext.applyDelta(content.map((c) => ({ retain: c.insert.length, attributes: c.attributes })));
};

const hashedMarkNameRegex = /(.*)(--[a-zA-Z0-9+/=]{8})$/;
/**
 * @param {string} attrName
 */
const yattr2markname = (attrName) => hashedMarkNameRegex.exec(attrName)?.[1] ?? attrName;

/**
 * @todo move this to markstoattributes
 *
 * @param {Object<string, any>} attrs
 * @param {import('prosemirror-model').Schema} schema
 */
const attributesToMarks = (attrs, schema) => {
	/**
	 * @type {Array<import('prosemirror-model').Mark>}
	 */
	const marks = [];
	for (const markName in attrs) {
		// remove hashes if necessary
		marks.push(schema.mark(yattr2markname(markName), attrs[markName]));
	}
	return marks;
};

/**
 * @param {Array<import('prosemirror-model').Mark>} marks
 * @param {BindingMetadata} meta
 */
const marksToAttributes = (marks, meta) => {
	const pattrs = {};
	marks.forEach((mark) => {
		if (mark.type.name !== "ychange") {
			const isOverlapping = setIfUndefined(meta.isOMark, mark.type, () => !mark.type.excludes(mark.type));
			pattrs[isOverlapping ? `${mark.type.name}--${hashOfJSON(mark.toJSON())}` : mark.type.name] = mark.attrs;
		}
	});
	return pattrs;
};

/**
 * Update a yDom node by syncing the current content of the prosemirror node.
 *
 * This is a y-prosemirror internal feature that you can use at your own risk.
 *
 * @private
 * @unstable
 *
 * @param {{transact: Function}} y
 * @param {Y.XmlFragment} yDomFragment
 * @param {any} pNode
 * @param {BindingMetadata} meta
 */
const updateYFragment = (y, yDomFragment, pNode, meta) => {
	if (yDomFragment instanceof YXmlElement && yDomFragment.nodeName !== pNode.type.name) {
		throw new Error("node name mismatch!");
	}
	meta.mapping.set(yDomFragment, pNode);
	// update attributes
	if (yDomFragment instanceof YXmlElement) {
		const yDomAttrs = yDomFragment.getAttributes();
		const pAttrs = pNode.attrs;
		for (const key in pAttrs) {
			if (pAttrs[key] !== null) {
				if (yDomAttrs[key] !== pAttrs[key] && key !== "ychange") {
					yDomFragment.setAttribute(key, pAttrs[key]);
				}
			} else {
				yDomFragment.removeAttribute(key);
			}
		}
		// remove all keys that are no longer in pAttrs
		for (const key in yDomAttrs) {
			if (pAttrs[key] === undefined) {
				yDomFragment.removeAttribute(key);
			}
		}
	}
	// update children
	const pChildren = normalizePNodeContent(pNode);
	const pChildCnt = pChildren.length;
	const yChildren = yDomFragment.toArray();
	const yChildCnt = yChildren.length;
	const minCnt = min$1(pChildCnt, yChildCnt);
	let left = 0;
	let right = 0;
	// find number of matching elements from left
	for (; left < minCnt; left++) {
		const leftY = yChildren[left];
		const leftP = pChildren[left];
		if (!mappedIdentity(meta.mapping.get(leftY), leftP)) {
			if (equalYTypePNode(leftY, leftP)) {
				// update mapping
				meta.mapping.set(leftY, leftP);
			} else {
				break;
			}
		}
	}
	// find number of matching elements from right
	for (; right + left < minCnt; right++) {
		const rightY = yChildren[yChildCnt - right - 1];
		const rightP = pChildren[pChildCnt - right - 1];
		if (!mappedIdentity(meta.mapping.get(rightY), rightP)) {
			if (equalYTypePNode(rightY, rightP)) {
				// update mapping
				meta.mapping.set(rightY, rightP);
			} else {
				break;
			}
		}
	}
	y.transact(() => {
		// try to compare and update
		while (yChildCnt - left - right > 0 && pChildCnt - left - right > 0) {
			const leftY = yChildren[left];
			const leftP = pChildren[left];
			const rightY = yChildren[yChildCnt - right - 1];
			const rightP = pChildren[pChildCnt - right - 1];
			if (leftY instanceof YXmlText && leftP instanceof Array) {
				if (!equalYTextPText(leftY, leftP)) {
					updateYText(leftY, leftP, meta);
				}
				left += 1;
			} else {
				let updateLeft = leftY instanceof YXmlElement && matchNodeName(leftY, leftP);
				let updateRight = rightY instanceof YXmlElement && matchNodeName(rightY, rightP);
				if (updateLeft && updateRight) {
					// decide which which element to update
					const equalityLeft = computeChildEqualityFactor(/** @type {Y.XmlElement} */ (leftY), /** @type {PModel.Node} */ (leftP), meta);
					const equalityRight = computeChildEqualityFactor(/** @type {Y.XmlElement} */ (rightY), /** @type {PModel.Node} */ (rightP), meta);
					if (equalityLeft.foundMappedChild && !equalityRight.foundMappedChild) {
						updateRight = false;
					} else if (!equalityLeft.foundMappedChild && equalityRight.foundMappedChild) {
						updateLeft = false;
					} else if (equalityLeft.equalityFactor < equalityRight.equalityFactor) {
						updateLeft = false;
					} else {
						updateRight = false;
					}
				}
				if (updateLeft) {
					updateYFragment(y, /** @type {Y.XmlFragment} */ (leftY), /** @type {PModel.Node} */ (leftP), meta);
					left += 1;
				} else if (updateRight) {
					updateYFragment(y, /** @type {Y.XmlFragment} */ (rightY), /** @type {PModel.Node} */ (rightP), meta);
					right += 1;
				} else {
					meta.mapping.delete(yDomFragment.get(left));
					yDomFragment.delete(left, 1);
					yDomFragment.insert(left, [createTypeFromTextOrElementNode(leftP, meta)]);
					left += 1;
				}
			}
		}
		const yDelLen = yChildCnt - left - right;
		if (yChildCnt === 1 && pChildCnt === 0 && yChildren[0] instanceof YXmlText) {
			meta.mapping.delete(yChildren[0]);
			// Edge case handling https://github.com/yjs/y-prosemirror/issues/108
			// Only delete the content of the Y.Text to retain remote changes on the same Y.Text object
			yChildren[0].delete(0, yChildren[0].length);
		} else if (yDelLen > 0) {
			yDomFragment.slice(left, left + yDelLen).forEach((type) => meta.mapping.delete(type));
			yDomFragment.delete(left, yDelLen);
		}
		if (left + right < pChildCnt) {
			const ins = [];
			for (let i = left; i < pChildCnt - right; i++) {
				ins.push(createTypeFromTextOrElementNode(pChildren[i], meta));
			}
			yDomFragment.insert(left, ins);
		}
	}, ySyncPluginKey);
};

/**
 * @function
 * @param {Y.XmlElement} yElement
 * @param {any} pNode Prosemirror Node
 */
const matchNodeName = (yElement, pNode) => !(pNode instanceof Array) && yElement.nodeName === pNode.type.name;

/**
 * Either a node if type is YXmlElement or an Array of text nodes if YXmlText
 * @typedef {Map<Y.AbstractType, Node | Array<Node>>} ProsemirrorMapping
 */

/**
 * Is null if no timeout is in progress.
 * Is defined if a timeout is in progress.
 * Maps from view
 * @type {Map<EditorView, Map<any, any>>|null}
 */
let viewsToUpdate = null;

const updateMetas = () => {
	const ups = /** @type {Map<EditorView, Map<any, any>>} */ (viewsToUpdate);
	viewsToUpdate = null;
	ups.forEach((metas, view) => {
		const tr = view.state.tr;
		const syncState = ySyncPluginKey.getState(view.state);
		if (syncState && syncState.binding && !syncState.binding.isDestroyed) {
			metas.forEach((val, key) => {
				tr.setMeta(key, val);
			});
			view.dispatch(tr);
		}
	});
};

const setMeta = (view, key, value) => {
	if (!viewsToUpdate) {
		viewsToUpdate = new Map();
		timeout(0, updateMetas);
	}
	setIfUndefined(viewsToUpdate, view, create$9).set(key, value);
};

/**
 * Transforms a Prosemirror based absolute position to a Yjs Cursor (relative position in the Yjs model).
 *
 * @param {number} pos
 * @param {Y.XmlFragment} type
 * @param {ProsemirrorMapping} mapping
 * @return {any} relative position
 */
const absolutePositionToRelativePosition = (pos, type, mapping) => {
	if (pos === 0) {
		// if the type is later populated, we want to retain the 0 position (hence assoc=-1)
		return createRelativePositionFromTypeIndex(type, 0, type.length === 0 ? -1 : 0);
	}
	/**
	 * @type {any}
	 */
	let n = type._first === null ? null : /** @type {Y.ContentType} */ (type._first.content).type;
	while (n !== null && type !== n) {
		if (n instanceof YXmlText) {
			if (n._length >= pos) {
				return createRelativePositionFromTypeIndex(n, pos, type.length === 0 ? -1 : 0);
			} else {
				pos -= n._length;
			}
			if (n._item !== null && n._item.next !== null) {
				n = /** @type {Y.ContentType} */ (n._item.next.content).type;
			} else {
				do {
					n = n._item === null ? null : n._item.parent;
					pos--;
				} while (n !== type && n !== null && n._item !== null && n._item.next === null);
				if (n !== null && n !== type) {
					// @ts-gnore we know that n.next !== null because of above loop conditition
					n = n._item === null ? null : /** @type {Y.ContentType} */ (/** @type Y.Item */ (n._item.next).content).type;
				}
			}
		} else {
			const pNodeSize = /** @type {any} */ (mapping.get(n) || { nodeSize: 0 }).nodeSize;
			if (n._first !== null && pos < pNodeSize) {
				n = /** @type {Y.ContentType} */ (n._first.content).type;
				pos--;
			} else {
				if (pos === 1 && n._length === 0 && pNodeSize > 1) {
					// edge case, should end in this paragraph
					return new RelativePosition(n._item === null ? null : n._item.id, n._item === null ? findRootTypeKey(n) : null, null);
				}
				pos -= pNodeSize;
				if (n._item !== null && n._item.next !== null) {
					n = /** @type {Y.ContentType} */ (n._item.next.content).type;
				} else {
					if (pos === 0) {
						// set to end of n.parent
						n = n._item === null ? n : n._item.parent;
						return new RelativePosition(n._item === null ? null : n._item.id, n._item === null ? findRootTypeKey(n) : null, null);
					}
					do {
						n = /** @type {Y.Item} */ (n._item).parent;
						pos--;
					} while (n !== type && /** @type {Y.Item} */ (n._item).next === null);
					// if n is null at this point, we have an unexpected case
					if (n !== type) {
						// We know that n._item.next is defined because of above loop condition
						n = /** @type {Y.ContentType} */ (/** @type {Y.Item} */ (/** @type {Y.Item} */ (n._item).next).content).type;
					}
				}
			}
		}
		if (n === null) {
			throw unexpectedCase();
		}
		if (pos === 0 && n.constructor !== YXmlText && n !== type) {
			// TODO: set to <= 0
			return createRelativePosition(n._item.parent, n._item);
		}
	}
	return createRelativePositionFromTypeIndex(type, type._length, type.length === 0 ? -1 : 0);
};

const createRelativePosition = (type, item) => {
	let typeid = null;
	let tname = null;
	if (type._item === null) {
		tname = findRootTypeKey(type);
	} else {
		typeid = createID(type._item.id.client, type._item.id.clock);
	}
	return new RelativePosition(typeid, tname, item.id);
};

/**
 * @param {Y.Doc} y
 * @param {Y.XmlFragment} documentType Top level type that is bound to pView
 * @param {any} relPos Encoded Yjs based relative position
 * @param {ProsemirrorMapping} mapping
 * @return {null|number}
 */
const relativePositionToAbsolutePosition = (y, documentType, relPos, mapping) => {
	const decodedPos = createAbsolutePositionFromRelativePosition(relPos, y);
	if (decodedPos === null || (decodedPos.type !== documentType && !isParentOf(documentType, decodedPos.type._item))) {
		return null;
	}
	let type = decodedPos.type;
	let pos = 0;
	if (type.constructor === YXmlText) {
		pos = decodedPos.index;
	} else if (type._item === null || !type._item.deleted) {
		let n = type._first;
		let i = 0;
		while (i < type._length && i < decodedPos.index && n !== null) {
			if (!n.deleted) {
				const t = /** @type {Y.ContentType} */ (n.content).type;
				i++;
				if (t instanceof YXmlText) {
					pos += t._length;
				} else {
					pos += /** @type {any} */ (mapping.get(t)).nodeSize;
				}
			}
			n = /** @type {Y.Item} */ (n.right);
		}
		pos += 1; // increase because we go out of n
	}
	while (type !== documentType && type._item !== null) {
		// @ts-ignore
		const parent = type._item.parent;
		// @ts-ignore
		if (parent._item === null || !parent._item.deleted) {
			pos += 1; // the start tag
			let n = /** @type {Y.AbstractType} */ (parent)._first;
			// now iterate until we found type
			while (n !== null) {
				const contentType = /** @type {Y.ContentType} */ (n.content).type;
				if (contentType === type) {
					break;
				}
				if (!n.deleted) {
					if (contentType instanceof YXmlText) {
						pos += contentType._length;
					} else {
						pos += /** @type {any} */ (mapping.get(contentType)).nodeSize;
					}
				}
				n = n.right;
			}
		}
		type = /** @type {Y.AbstractType} */ (parent);
	}
	return pos - 1; // we don't count the most outer tag, because it is a fragment
};

/**
 * Default awareness state filter
 *
 * @param {number} currentClientId current client id
 * @param {number} userClientId user client id
 * @param {any} _user user data
 * @return {boolean}
 */
const defaultAwarenessStateFilter = (currentClientId, userClientId, _user) => currentClientId !== userClientId;

/**
 * Default generator for a cursor element
 *
 * @param {any} user user data
 * @return {HTMLElement}
 */
const defaultCursorBuilder = (user) => {
	const cursor = document.createElement("span");
	cursor.classList.add("ProseMirror-yjs-cursor");
	cursor.setAttribute("style", `border-color: ${user.color}`);
	const userDiv = document.createElement("div");
	userDiv.setAttribute("style", `background-color: ${user.color}`);
	userDiv.insertBefore(document.createTextNode(user.name), null);
	const nonbreakingSpace1 = document.createTextNode("\u2060");
	const nonbreakingSpace2 = document.createTextNode("\u2060");
	cursor.insertBefore(nonbreakingSpace1, null);
	cursor.insertBefore(userDiv, null);
	cursor.insertBefore(nonbreakingSpace2, null);
	return cursor;
};

/**
 * Default generator for the selection attributes
 *
 * @param {any} user user data
 * @return {import('prosemirror-view').DecorationAttrs}
 */
const defaultSelectionBuilder = (user) => {
	return {
		style: `background-color: ${user.color}70`,
		class: "ProseMirror-yjs-selection",
	};
};

const rxValidColor = /^#[0-9a-fA-F]{6}$/;

/**
 * @param {any} state
 * @param {Awareness} awareness
 * @param {function(number, number, any):boolean} awarenessFilter
 * @param {(user: { name: string, color: string }, clientId: number) => Element} createCursor
 * @param {(user: { name: string, color: string }, clientId: number) => import('prosemirror-view').DecorationAttrs} createSelection
 * @return {any} DecorationSet
 */
const createDecorations = (state, awareness, awarenessFilter, createCursor, createSelection) => {
	const ystate = ySyncPluginKey.getState(state);
	const y = ystate.doc;
	const decorations = [];
	if (ystate.snapshot != null || ystate.prevSnapshot != null || ystate.binding.mapping.size === 0) {
		// do not render cursors while snapshot is active
		return DecorationSet.create(state.doc, []);
	}
	awareness.getStates().forEach((aw, clientId) => {
		if (!awarenessFilter(y.clientID, clientId, aw)) {
			return;
		}

		if (aw.cursor != null) {
			const user = aw.user || {};
			if (user.color == null) {
				user.color = "#ffa500";
			} else if (!rxValidColor.test(user.color)) {
				// We only support 6-digit RGB colors in y-prosemirror
				console.warn("A user uses an unsupported color format", user);
			}
			if (user.name == null) {
				user.name = `User: ${clientId}`;
			}
			let anchor = relativePositionToAbsolutePosition(y, ystate.type, createRelativePositionFromJSON(aw.cursor.anchor), ystate.binding.mapping);
			let head = relativePositionToAbsolutePosition(y, ystate.type, createRelativePositionFromJSON(aw.cursor.head), ystate.binding.mapping);
			if (anchor !== null && head !== null) {
				const maxsize = max$1(state.doc.content.size - 1, 0);
				anchor = min$1(anchor, maxsize);
				head = min$1(head, maxsize);
				decorations.push(
					Decoration.widget(head, () => createCursor(user, clientId), {
						key: clientId + "",
						side: 10,
					}),
				);
				const from = min$1(anchor, head);
				const to = max$1(anchor, head);
				decorations.push(
					Decoration.inline(from, to, createSelection(user, clientId), {
						inclusiveEnd: true,
						inclusiveStart: false,
					}),
				);
			}
		}
	});
	return DecorationSet.create(state.doc, decorations);
};

/**
 * A prosemirror plugin that listens to awareness information on Yjs.
 * This requires that a `prosemirrorPlugin` is also bound to the prosemirror.
 *
 * @public
 * @param {Awareness} awareness
 * @param {object} opts
 * @param {function(any, any, any):boolean} [opts.awarenessStateFilter]
 * @param {(user: any, clientId: number) => HTMLElement} [opts.cursorBuilder]
 * @param {(user: any, clientId: number) => import('prosemirror-view').DecorationAttrs} [opts.selectionBuilder]
 * @param {function(any):any} [opts.getSelection]
 * @param {string} [cursorStateField] By default all editor bindings use the awareness 'cursor' field to propagate cursor information.
 * @return {any}
 */
const yCursorPlugin = (
	awareness,
	{ awarenessStateFilter = defaultAwarenessStateFilter, cursorBuilder = defaultCursorBuilder, selectionBuilder = defaultSelectionBuilder, getSelection = (state) => state.selection } = {},
	cursorStateField = "cursor",
) =>
	new Plugin({
		key: yCursorPluginKey,
		state: {
			init(_, state) {
				return createDecorations(state, awareness, awarenessStateFilter, cursorBuilder, selectionBuilder);
			},
			apply(tr, prevState, _oldState, newState) {
				const ystate = ySyncPluginKey.getState(newState);
				const yCursorState = tr.getMeta(yCursorPluginKey);
				if ((ystate && ystate.isChangeOrigin) || (yCursorState && yCursorState.awarenessUpdated)) {
					return createDecorations(newState, awareness, awarenessStateFilter, cursorBuilder, selectionBuilder);
				}
				return prevState.map(tr.mapping, tr.doc);
			},
		},
		props: {
			decorations: (state) => {
				return yCursorPluginKey.getState(state);
			},
		},
		view: (view) => {
			const awarenessListener = () => {
				// @ts-ignore
				if (view.docView) {
					setMeta(view, yCursorPluginKey, { awarenessUpdated: true });
				}
			};
			const updateCursorInfo = () => {
				const ystate = ySyncPluginKey.getState(view.state);
				// @note We make implicit checks when checking for the cursor property
				const current = awareness.getLocalState() || {};
				if (view.hasFocus()) {
					const selection = getSelection(view.state);
					/**
					 * @type {Y.RelativePosition}
					 */
					const anchor = absolutePositionToRelativePosition(selection.anchor, ystate.type, ystate.binding.mapping);
					/**
					 * @type {Y.RelativePosition}
					 */
					const head = absolutePositionToRelativePosition(selection.head, ystate.type, ystate.binding.mapping);
					if (
						current.cursor == null ||
						!compareRelativePositions(createRelativePositionFromJSON(current.cursor.anchor), anchor) ||
						!compareRelativePositions(createRelativePositionFromJSON(current.cursor.head), head)
					) {
						awareness.setLocalStateField(cursorStateField, {
							anchor,
							head,
						});
					}
				} else if (
					current.cursor != null &&
					relativePositionToAbsolutePosition(ystate.doc, ystate.type, createRelativePositionFromJSON(current.cursor.anchor), ystate.binding.mapping) !== null
				) {
					// delete cursor information if current cursor information is owned by this editor binding
					awareness.setLocalStateField(cursorStateField, null);
				}
			};
			awareness.on("change", awarenessListener);
			view.dom.addEventListener("focusin", updateCursorInfo);
			view.dom.addEventListener("focusout", updateCursorInfo);
			return {
				update: updateCursorInfo,
				destroy: () => {
					view.dom.removeEventListener("focusin", updateCursorInfo);
					view.dom.removeEventListener("focusout", updateCursorInfo);
					awareness.off("change", awarenessListener);
					awareness.setLocalStateField(cursorStateField, null);
				},
			};
		},
	});

/**
 * @typedef {Object} UndoPluginState
 * @property {import('yjs').UndoManager} undoManager
 * @property {ReturnType<typeof getRelativeSelection> | null} prevSel
 * @property {boolean} hasUndoOps
 * @property {boolean} hasRedoOps
 */

/**
 * Undo the last user action
 *
 * @param {import('prosemirror-state').EditorState} state
 * @return {boolean} whether a change was undone
 */
const undo = (state) => yUndoPluginKey.getState(state)?.undoManager?.undo() != null;

/**
 * Redo the last user action
 *
 * @param {import('prosemirror-state').EditorState} state
 * @return {boolean} whether a change was undone
 */
const redo = (state) => yUndoPluginKey.getState(state)?.undoManager?.redo() != null;

/**
 * Undo the last user action if there are undo operations available
 * @type {import('prosemirror-state').Command}
 */
const undoCommand = (state, dispatch) => (dispatch == null ? yUndoPluginKey.getState(state)?.undoManager?.canUndo() : undo(state));

/**
 * Redo the last user action if there are redo operations available
 * @type {import('prosemirror-state').Command}
 */
const redoCommand = (state, dispatch) => (dispatch == null ? yUndoPluginKey.getState(state)?.undoManager?.canRedo() : redo(state));

const defaultProtectedNodes = new Set(["paragraph"]);

/**
 * @param {import('yjs').Item} item
 * @param {Set<string>} protectedNodes
 * @returns {boolean}
 */
const defaultDeleteFilter = (item, protectedNodes) =>
	!(item instanceof Item$1) ||
	!(item.content instanceof ContentType) ||
	!(item.content.type instanceof YText || (item.content.type instanceof YXmlElement && protectedNodes.has(item.content.type.nodeName))) ||
	item.content.type._length === 0;

/**
 * @param {object} [options]
 * @param {Set<string>} [options.protectedNodes]
 * @param {any[]} [options.trackedOrigins]
 * @param {import('yjs').UndoManager | null} [options.undoManager]
 */
const yUndoPlugin = ({ protectedNodes = defaultProtectedNodes, trackedOrigins = [], undoManager = null } = {}) =>
	new Plugin({
		key: yUndoPluginKey,
		state: {
			init: (initargs, state) => {
				// TODO: check if plugin order matches and fix
				const ystate = ySyncPluginKey.getState(state);
				const _undoManager =
					undoManager ||
					new UndoManager(ystate.type, {
						trackedOrigins: new Set([ySyncPluginKey].concat(trackedOrigins)),
						deleteFilter: (item) => defaultDeleteFilter(item, protectedNodes),
						captureTransaction: (tr) => tr.meta.get("addToHistory") !== false,
					});
				return {
					undoManager: _undoManager,
					prevSel: null,
					hasUndoOps: _undoManager.undoStack.length > 0,
					hasRedoOps: _undoManager.redoStack.length > 0,
				};
			},
			apply: (tr, val, oldState, state) => {
				const binding = ySyncPluginKey.getState(state).binding;
				const undoManager = val.undoManager;
				const hasUndoOps = undoManager.undoStack.length > 0;
				const hasRedoOps = undoManager.redoStack.length > 0;
				if (binding) {
					return {
						undoManager,
						prevSel: getRelativeSelection(binding, oldState),
						hasUndoOps,
						hasRedoOps,
					};
				} else {
					if (hasUndoOps !== val.hasUndoOps || hasRedoOps !== val.hasRedoOps) {
						return Object.assign({}, val, {
							hasUndoOps: undoManager.undoStack.length > 0,
							hasRedoOps: undoManager.redoStack.length > 0,
						});
					} else {
						// nothing changed
						return val;
					}
				}
			},
		},
		view: (view) => {
			const ystate = ySyncPluginKey.getState(view.state);
			const undoManager = yUndoPluginKey.getState(view.state).undoManager;
			undoManager.on("stack-item-added", ({ stackItem }) => {
				const binding = ystate.binding;
				if (binding) {
					stackItem.meta.set(binding, yUndoPluginKey.getState(view.state).prevSel);
				}
			});
			undoManager.on("stack-item-popped", ({ stackItem }) => {
				const binding = ystate.binding;
				if (binding) {
					binding.beforeTransactionSelection = stackItem.meta.get(binding) || binding.beforeTransactionSelection;
				}
			});
			return {
				destroy: () => {
					undoManager.destroy();
				},
			};
		},
	});

const __storeToDerived = /* @__PURE__ */ new WeakMap();
const __derivedToStore = /* @__PURE__ */ new WeakMap();
const __depsThatHaveWrittenThisTick = {
	current: [],
};
let __isFlushing = false;
const __pendingUpdates = /* @__PURE__ */ new Set();
const __initialBatchValues = /* @__PURE__ */ new Map();
function __flush_internals(relatedVals) {
	const sorted = Array.from(relatedVals).sort((a, b) => {
		if (a instanceof Derived && a.options.deps.includes(b)) return 1;
		if (b instanceof Derived && b.options.deps.includes(a)) return -1;
		return 0;
	});
	for (const derived of sorted) {
		if (__depsThatHaveWrittenThisTick.current.includes(derived)) {
			continue;
		}
		__depsThatHaveWrittenThisTick.current.push(derived);
		derived.recompute();
		const stores = __derivedToStore.get(derived);
		if (stores) {
			for (const store of stores) {
				const relatedLinkedDerivedVals = __storeToDerived.get(store);
				if (!relatedLinkedDerivedVals) continue;
				__flush_internals(relatedLinkedDerivedVals);
			}
		}
	}
}
function __notifyListeners(store) {
	const value = {
		prevVal: store.prevState,
		currentVal: store.state,
	};
	for (const listener of store.listeners) {
		listener(value);
	}
}
function __notifyDerivedListeners(derived) {
	const value = {
		prevVal: derived.prevState,
		currentVal: derived.state,
	};
	for (const listener of derived.listeners) {
		listener(value);
	}
}
function __flush(store) {
	__pendingUpdates.add(store);
	if (__isFlushing) return;
	try {
		__isFlushing = true;
		while (__pendingUpdates.size > 0) {
			const stores = Array.from(__pendingUpdates);
			__pendingUpdates.clear();
			for (const store2 of stores) {
				const prevState = __initialBatchValues.get(store2) ?? store2.prevState;
				store2.prevState = prevState;
				__notifyListeners(store2);
			}
			for (const store2 of stores) {
				const derivedVals = __storeToDerived.get(store2);
				if (!derivedVals) continue;
				__depsThatHaveWrittenThisTick.current.push(store2);
				__flush_internals(derivedVals);
			}
			for (const store2 of stores) {
				const derivedVals = __storeToDerived.get(store2);
				if (!derivedVals) continue;
				for (const derived of derivedVals) {
					__notifyDerivedListeners(derived);
				}
			}
		}
	} finally {
		__isFlushing = false;
		__depsThatHaveWrittenThisTick.current = [];
		__initialBatchValues.clear();
	}
}

function isUpdaterFunction(updater) {
	return typeof updater === "function";
}

class Store {
	constructor(initialState, options) {
		this.listeners = /* @__PURE__ */ new Set();
		this.subscribe = (listener) => {
			var _a, _b;
			this.listeners.add(listener);
			const unsub = (_b = (_a = this.options) == null ? void 0 : _a.onSubscribe) == null ? void 0 : _b.call(_a, listener, this);
			return () => {
				this.listeners.delete(listener);
				unsub == null ? void 0 : unsub();
			};
		};
		this.prevState = initialState;
		this.state = initialState;
		this.options = options;
	}
	setState(updater) {
		var _a, _b, _c;
		this.prevState = this.state;
		if ((_a = this.options) == null ? void 0 : _a.updateFn) {
			this.state = this.options.updateFn(this.prevState)(updater);
		} else {
			if (isUpdaterFunction(updater)) {
				this.state = updater(this.prevState);
			} else {
				this.state = updater;
			}
		}
		(_c = (_b = this.options) == null ? void 0 : _b.onUpdate) == null ? void 0 : _c.call(_b);
		__flush(this);
	}
}

class Derived {
	constructor(options) {
		this.listeners = /* @__PURE__ */ new Set();
		this._subscriptions = [];
		this.lastSeenDepValues = [];
		this.getDepVals = () => {
			const l = this.options.deps.length;
			const prevDepVals = new Array(l);
			const currDepVals = new Array(l);
			for (let i = 0; i < l; i++) {
				const dep = this.options.deps[i];
				prevDepVals[i] = dep.prevState;
				currDepVals[i] = dep.state;
			}
			this.lastSeenDepValues = currDepVals;
			return {
				prevDepVals,
				currDepVals,
				prevVal: this.prevState ?? void 0,
			};
		};
		this.recompute = () => {
			var _a, _b;
			this.prevState = this.state;
			const depVals = this.getDepVals();
			this.state = this.options.fn(depVals);
			(_b = (_a = this.options).onUpdate) == null ? void 0 : _b.call(_a);
		};
		this.checkIfRecalculationNeededDeeply = () => {
			for (const dep of this.options.deps) {
				if (dep instanceof Derived) {
					dep.checkIfRecalculationNeededDeeply();
				}
			}
			let shouldRecompute = false;
			const lastSeenDepValues = this.lastSeenDepValues;
			const { currDepVals } = this.getDepVals();
			for (let i = 0; i < currDepVals.length; i++) {
				if (currDepVals[i] !== lastSeenDepValues[i]) {
					shouldRecompute = true;
					break;
				}
			}
			if (shouldRecompute) {
				this.recompute();
			}
		};
		this.mount = () => {
			this.registerOnGraph();
			this.checkIfRecalculationNeededDeeply();
			return () => {
				this.unregisterFromGraph();
				for (const cleanup of this._subscriptions) {
					cleanup();
				}
			};
		};
		this.subscribe = (listener) => {
			var _a, _b;
			this.listeners.add(listener);
			const unsub = (_b = (_a = this.options).onSubscribe) == null ? void 0 : _b.call(_a, listener, this);
			return () => {
				this.listeners.delete(listener);
				unsub == null ? void 0 : unsub();
			};
		};
		this.options = options;
		this.state = options.fn({
			prevDepVals: void 0,
			prevVal: void 0,
			currDepVals: this.getDepVals().currDepVals,
		});
	}
	registerOnGraph(deps = this.options.deps) {
		for (const dep of deps) {
			if (dep instanceof Derived) {
				dep.registerOnGraph();
				this.registerOnGraph(dep.options.deps);
			} else if (dep instanceof Store) {
				let relatedLinkedDerivedVals = __storeToDerived.get(dep);
				if (!relatedLinkedDerivedVals) {
					relatedLinkedDerivedVals = /* @__PURE__ */ new Set();
					__storeToDerived.set(dep, relatedLinkedDerivedVals);
				}
				relatedLinkedDerivedVals.add(this);
				let relatedStores = __derivedToStore.get(this);
				if (!relatedStores) {
					relatedStores = /* @__PURE__ */ new Set();
					__derivedToStore.set(this, relatedStores);
				}
				relatedStores.add(dep);
			}
		}
	}
	unregisterFromGraph(deps = this.options.deps) {
		for (const dep of deps) {
			if (dep instanceof Derived) {
				this.unregisterFromGraph(dep.options.deps);
			} else if (dep instanceof Store) {
				const relatedLinkedDerivedVals = __storeToDerived.get(dep);
				if (relatedLinkedDerivedVals) {
					relatedLinkedDerivedVals.delete(this);
				}
				const relatedStores = __derivedToStore.get(this);
				if (relatedStores) {
					relatedStores.delete(dep);
				}
			}
		}
	}
}

const r$1 = Symbol("originalFactory");
function a$1(n) {
	if (typeof n == "object" && "key" in n)
		return function t() {
			return ((n[r$1] = t), n);
		};
	if (typeof n != "function") throw new Error("factory must be a function");
	return function t(e) {
		return (i) => {
			const o = n({ editor: i.editor, options: e });
			return ((o[r$1] = t), o);
		};
	};
}
function f$2(n, t) {
	return new Store(n, t);
}

//#region src/cache.ts
/**
 * Represents a cache of doc positions to the node and decorations at that position
 */
var DecorationCache = class DecorationCache {
	constructor(cache) {
		this.cache = new Map(cache);
	}
	/**
	 * Gets the cache entry at the given doc position, or null if it doesn't exist
	 * @param pos The doc position of the node you want the cache for
	 */
	get(pos) {
		return this.cache.get(pos);
	}
	/**
	 * Sets the cache entry at the given position with the give node/decoration
	 * values
	 * @param pos The doc position of the node to set the cache for
	 * @param node The node to place in cache
	 * @param decorations The decorations to place in cache
	 */
	set(pos, node, decorations) {
		if (pos < 0) return;
		this.cache.set(pos, [node, decorations]);
	}
	/**
	 * Removes the value at the oldPos (if it exists) and sets the new position to
	 * the given values
	 * @param oldPos The old node position to overwrite
	 * @param newPos The new node position to set the cache for
	 * @param node The new node to place in cache
	 * @param decorations The new decorations to place in cache
	 */
	replace(oldPos, newPos, node, decorations) {
		this.remove(oldPos);
		this.set(newPos, node, decorations);
	}
	/**
	 * Removes the cache entry at the given position
	 * @param pos The doc position to remove from cache
	 */
	remove(pos) {
		this.cache.delete(pos);
	}
	/**
	 * Invalidates the cache by removing all decoration entries on nodes that have
	 * changed, updating the positions of the nodes that haven't and removing all
	 * the entries that have been deleted; NOTE: this does not affect the current
	 * cache, but returns an entirely new one
	 * @param tr A transaction to map the current cache to
	 */
	invalidate(tr) {
		const returnCache = new DecorationCache(this.cache);
		const mapping = tr.mapping;
		this.cache.forEach(([node, decorations], pos) => {
			if (pos < 0) return;
			const result = mapping.mapResult(pos);
			const mappedNode = tr.doc.nodeAt(result.pos);
			if (result.deleted || !mappedNode?.eq(node)) returnCache.remove(pos);
			else if (pos !== result.pos) {
				const updatedDecorations = decorations
					.map((d) => {
						return d.map(mapping, 0, 0);
					})
					.filter((d) => d != null);
				returnCache.replace(pos, result.pos, mappedNode, updatedDecorations);
			}
		});
		return returnCache;
	}
};

//#endregion
//#region src/plugin.ts
/**
 * Creates a plugin that highlights the contents of all nodes (via Decorations)
 * with a type passed in blockTypes
 */
function createHighlightPlugin({ parser, nodeTypes = ["code_block", "codeBlock"], languageExtractor = (node) => node.attrs.language }) {
	const key = new PluginKey("prosemirror-highlight");
	return new Plugin({
		key,
		state: {
			init(_, instance) {
				const cache = new DecorationCache();
				const [decorations, promises] = calculateDecoration(instance.doc, parser, nodeTypes, languageExtractor, cache);
				return {
					cache,
					decorations,
					promises,
				};
			},
			apply: (tr, data) => {
				const cache = data.cache.invalidate(tr);
				const refresh = !!tr.getMeta("prosemirror-highlight-refresh");
				if (!tr.docChanged && !refresh)
					return {
						cache,
						decorations: data.decorations.map(tr.mapping, tr.doc),
						promises: data.promises,
					};
				const [decorations, promises] = calculateDecoration(tr.doc, parser, nodeTypes, languageExtractor, cache);
				return {
					cache,
					decorations,
					promises,
				};
			},
		},
		view: (view) => {
			const promises = /* @__PURE__ */ new Set();
			const refresh = () => {
				if (promises.size > 0) return;
				const tr = view.state.tr.setMeta("prosemirror-highlight-refresh", true);
				view.dispatch(tr);
			};
			const check = () => {
				const state = key.getState(view.state);
				for (const promise of state?.promises ?? []) {
					promises.add(promise);
					promise
						.then(() => {
							promises.delete(promise);
							refresh();
						})
						.catch(() => {
							promises.delete(promise);
						});
				}
			};
			check();
			return {
				update: () => {
					check();
				},
			};
		},
		props: {
			decorations(state) {
				return this.getState(state)?.decorations;
			},
		},
	});
}
function calculateDecoration(doc, parser, nodeTypes, languageExtractor, cache) {
	const result = [];
	const promises = [];
	doc.descendants((node, pos) => {
		if (!node.type.isTextblock) return true;
		if (nodeTypes.includes(node.type.name)) {
			const language = languageExtractor(node);
			const cached = cache.get(pos);
			if (cached) {
				const [_, decorations] = cached;
				result.push(...decorations);
			} else {
				const decorations = parser({
					content: node.textContent,
					language: language || void 0,
					pos,
					size: node.nodeSize,
				});
				if (decorations && Array.isArray(decorations)) {
					cache.set(pos, node, decorations);
					result.push(...decorations);
				} else if (decorations instanceof Promise) {
					cache.remove(pos);
					promises.push(decorations);
				}
			}
		}
		return false;
	});
	return [DecorationSet.create(doc, result), promises];
}

//#region src/shiki.ts
function createParser(highlighter, options) {
	return function parser({ content, language, pos, size }) {
		const decorations = [];
		const { tokens, fg, bg, rootStyle } = highlighter.codeToTokens(content, {
			lang: language,
			...{ theme: highlighter.getLoadedThemes()[0] },
		});
		const style = rootStyle || (fg && bg ? `--prosemirror-highlight:${fg};--prosemirror-highlight-bg:${bg}` : "");
		if (style) {
			const decoration = Decoration.node(pos, pos + size, { style });
			decorations.push(decoration);
		}
		let from = pos + 1;
		for (const line of tokens) {
			for (const token of line) {
				const to = from + token.content.length;
				const decoration = Decoration.inline(from, to, {
					style: stringifyTokenStyle(token.htmlStyle ?? `color: ${token.color}`),
					class: "shiki",
				});
				decorations.push(decoration);
				from = to;
			}
			from += 1;
		}
		return decorations;
	};
}
/**
 * Copied from https://github.com/shikijs/shiki/blob/f76a371dbc2752cba341023df00ebfe9b66cb3f6/packages/core/src/utils.ts#L213
 *
 * Copy instead of import it from `shiki` to avoid importing the `shiki` package in this file.
 */
function stringifyTokenStyle(token) {
	if (typeof token === "string") return token;
	return Object.entries(token)
		.map(([key, value]) => `${key}:${value}`)
		.join(";");
}

var Ve$1 = Object.defineProperty;
var We = (e, t, n) => (t in e ? Ve$1(e, t, { enumerable: true, configurable: true, writable: true, value: n }) : (e[t] = n));
var w = (e, t, n) => We(e, typeof t != "symbol" ? t + "" : t, n);
const bt = () => typeof navigator < "u" && (/Mac/.test(navigator.platform) || (/AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent)));
function L$1(e, t = "Ctrl") {
	return bt() ? e.replace("Mod", "⌘") : e.replace("Mod", t);
}
function D$2(...e) {
	return [
		// Converts to & from set to remove duplicates.
		...new Set(
			e
				.filter((t) => t)
				.join(" ")
				.split(" "),
		),
	].join(" ");
}
function Ct(e, t, n, o) {
	const r = document.createElement("div");
	((r.className = D$2("bn-block-content", n.class)), r.setAttribute("data-content-type", e));
	for (const [s, i] of Object.entries(n)) s !== "class" && r.setAttribute(s, i);
	const a = document.createElement(t);
	a.className = D$2("bn-inline-content", o.class);
	for (const [s, i] of Object.entries(o)) s !== "class" && a.setAttribute(s, i);
	return (
		r.appendChild(a),
		{
			dom: r,
			contentDOM: a,
		}
	);
}
const de$2 = (e, t) => {
	let n = bt$1(e, t.pmSchema);
	n.type.name === "blockContainer" && (n = n.firstChild);
	const o = t.pmSchema.nodes[n.type.name].spec.toDOM;
	if (o === void 0) throw new Error("This block has no default HTML serialization as its corresponding TipTap node doesn't implement `renderHTML`.");
	const r = o(n);
	if (typeof r != "object" || !("dom" in r))
		throw new Error("Cannot use this block's default HTML serialization as its corresponding TipTap node's `renderHTML` function does not return an object with the `dom` property.");
	return r;
};
function we$1(e, t = "<br>") {
	const n = e.querySelectorAll("p");
	if (n.length > 1) {
		const o = n[0];
		for (let r = 1; r < n.length; r++) {
			const a = n[r];
			((o.innerHTML += t + a.innerHTML), a.remove());
		}
	}
}
function F$1(e) {
	return "data-" + e.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
}
function fo$1(e) {
	const t = e.split("/");
	return (
			!t.length || // invalid?
				t[t.length - 1] === ""
		) ?
			e
		:	t[t.length - 1];
}
function mo$1(e) {
	var n;
	const t = ["mp4", "webm", "ogg", "mov", "mkv", "flv", "avi", "wmv", "m4v"];
	try {
		const r = ((n = new URL(e).pathname.split(".").pop()) == null ? void 0 : n.toLowerCase()) || "";
		return t.includes(r);
	} catch {
		return false;
	}
}
function kt(e) {
	const t = {};
	return (
		Object.entries(e).forEach(([n, o]) => {
			t[n] = {
				default: o.default,
				keepOnSplit: true,
				// Props are displayed in kebab-case as HTML attributes. If a prop's
				// value is the same as its default, we don't display an HTML
				// attribute for it.
				parseHTML: (r) => {
					const a = r.getAttribute(F$1(n));
					if (a === null) return null;
					if ((o.default === void 0 && o.type === "boolean") || (o.default !== void 0 && typeof o.default == "boolean"))
						return (
							a === "true" ? true
							: a === "false" ? false
							: null
						);
					if ((o.default === void 0 && o.type === "number") || (o.default !== void 0 && typeof o.default == "number")) {
						const s = parseFloat(a);
						return !Number.isNaN(s) && Number.isFinite(s) ? s : null;
					}
					return a;
				},
				renderHTML: (r) =>
					r[n] !== o.default ?
						{
							[F$1(n)]: r[n],
						}
					:	{},
			};
		}),
		t
	);
}
function yt(e, t, n, o) {
	const r = e();
	if (r === void 0) throw new Error("Cannot find node position");
	const s = n.state.doc.resolve(r).node().attrs.id;
	if (!s) throw new Error("Block doesn't have id");
	const i = t.getBlock(s);
	if (i.type !== o) throw new Error("Block type does not match");
	return i;
}
function z(e, t, n, o, r = false, a) {
	const s = document.createElement("div");
	if (a !== void 0) for (const [i, c] of Object.entries(a)) i !== "class" && s.setAttribute(i, c);
	((s.className = D$2("bn-block-content", (a == null ? void 0 : a.class) || "")), s.setAttribute("data-content-type", t));
	for (const [i, c] of Object.entries(n)) {
		const u = o[i].default;
		c !== u && s.setAttribute(F$1(i), c);
	}
	return (
		r && s.setAttribute("data-file-block", ""),
		s.appendChild(e.dom),
		e.contentDOM && (e.contentDOM.className = D$2("bn-inline-content", e.contentDOM.className)),
		{
			...e,
			dom: s,
		}
	);
}
function vt(e, t, n) {
	return {
		config: {
			type: e.type,
			content: e.content,
			propSchema: t,
		},
		implementation: {
			node: e.node,
			render: de$2,
			toExternalHTML: de$2,
		},
		extensions: n,
	};
}
function Et(e, t) {
	e.stopEvent = (n) => (
		n.type === "mousedown" &&
			setTimeout(() => {
				t.view.dom.blur();
			}, 10),
		true
	);
}
function St(e, t) {
	const n = [
		{
			tag: "[data-content-type=" + e.type + "]",
			contentElement: ".bn-inline-content",
		},
	];
	return (
		t.parse &&
			n.push({
				tag: "*",
				getAttrs(o) {
					var a;
					if (typeof o == "string") return false;
					const r = (a = t.parse) == null ? void 0 : a.call(t, o);
					return r === void 0 ? false : r;
				},
				// Because we do the parsing ourselves, we want to preserve whitespace for content we've parsed
				preserveWhitespace: true,
				getContent:
					e.content === "inline" || e.content === "none" ?
						(o, r) => {
							var a;
							if (t.parseContent) {
								const s = t.parseContent({
									el: o,
									schema: r,
								});
								if (s !== void 0) return s;
							}
							if (e.content === "inline") {
								const i = o.cloneNode(true);
								return (
									we$1(
										i,
										(a = t.meta) != null && a.code ?
											`
`
										:	"<br>",
									),
									DOMParser$1.fromSchema(r).parse(i, {
										topNode: r.nodes.paragraph.create(),
										preserveWhitespace: true,
									}).content
								);
							}
							return Fragment.empty;
						}
					:	void 0,
			}),
		n
	);
}
function ho$1(e, t, n, o) {
	var a, s, i, c;
	const r =
		t.node ||
		Node3.create({
			name: e.type,
			content:
				e.content === "inline" ? "inline*"
				: e.content === "none" ? ""
				: e.content,
			group: "blockContent",
			selectable: ((a = t.meta) == null ? void 0 : a.selectable) ?? true,
			isolating: ((s = t.meta) == null ? void 0 : s.isolating) ?? true,
			code: ((i = t.meta) == null ? void 0 : i.code) ?? false,
			defining: ((c = t.meta) == null ? void 0 : c.defining) ?? true,
			priority: o,
			addAttributes() {
				return kt(e.propSchema);
			},
			parseHTML() {
				return St(e, t);
			},
			renderHTML({ HTMLAttributes: l }) {
				var d;
				const u = document.createElement("div");
				return z(
					{
						dom: u,
						contentDOM: e.content === "inline" ? u : void 0,
					},
					e.type,
					{},
					e.propSchema,
					((d = t.meta) == null ? void 0 : d.fileBlockAccept) !== void 0,
					l,
				);
			},
			addNodeView() {
				return (l) => {
					var f, E;
					const u = this.options.editor,
						d = yt(l.getPos, u, this.editor, e.type),
						p = ((f = this.options.domAttributes) == null ? void 0 : f.blockContent) || {},
						h = t.render.call({ blockContentDOMAttributes: p, props: l, renderType: "nodeView" }, d, u);
					return (((E = t.meta) == null ? void 0 : E.selectable) === false && Et(h, this.editor), h);
				};
			},
		});
	if (r.name !== e.type) throw new Error("Node name does not match block type. This is a bug in BlockNote.");
	return {
		config: e,
		implementation: {
			...t,
			node: r,
			render(l, u) {
				var p;
				const d = ((p = r.options.domAttributes) == null ? void 0 : p.blockContent) || {};
				return t.render.call(
					{
						blockContentDOMAttributes: d,
						props: void 0,
						renderType: "dom",
					},
					l,
					u,
				);
			},
			// TODO: this should not have wrapInBlockStructure and generally be a lot simpler
			// post-processing in externalHTMLExporter should not be necessary
			toExternalHTML: (l, u, d) => {
				var h, f;
				const p = ((h = r.options.domAttributes) == null ? void 0 : h.blockContent) || {};
				return (
					((f = t.toExternalHTML) == null ? void 0 : f.call({ blockContentDOMAttributes: p }, l, u, d)) ??
					t.render.call({ blockContentDOMAttributes: p, renderType: "dom", props: void 0 }, l, u)
				);
			},
		},
		extensions: n,
	};
}
function v$1(e, t, n) {
	return (o = {}) => {
		const r = typeof e == "function" ? e(o) : e,
			a = typeof t == "function" ? t(o) : t,
			s =
				n ?
					typeof n == "function" ?
						n(o)
					:	n
				:	void 0;
		return {
			config: r,
			implementation: {
				...a,
				// TODO: this should not have wrapInBlockStructure and generally be a lot simpler
				// post-processing in externalHTMLExporter should not be necessary
				toExternalHTML(i, c, l) {
					var d, p;
					const u = (d = a.toExternalHTML) == null ? void 0 : d.call({ blockContentDOMAttributes: this.blockContentDOMAttributes }, i, c, l);
					if (u !== void 0) return z(u, i.type, i.props, r.propSchema, ((p = a.meta) == null ? void 0 : p.fileBlockAccept) !== void 0);
				},
				render(i, c) {
					var d;
					const l = a.render.call(
						{
							blockContentDOMAttributes: this.blockContentDOMAttributes,
							renderType: this.renderType,
							props: this.props,
						},
						i,
						c,
					);
					return z(l, i.type, i.props, r.propSchema, ((d = a.meta) == null ? void 0 : d.fileBlockAccept) !== void 0, this.blockContentDOMAttributes);
				},
			},
			extensions: s,
		};
	};
}
function Mt$1(e) {
	return Object.fromEntries(Object.entries(e).map(([t, n]) => [t, n.config]));
}
function wt(e) {
	return e === "boolean" ?
			{}
		:	{
				stringValue: {
					default: void 0,
					keepOnSplit: true,
					parseHTML: (t) => t.getAttribute("data-value"),
					renderHTML: (t) =>
						t.stringValue !== void 0 ?
							{
								"data-value": t.stringValue,
							}
						:	{},
				},
			};
}
function W$1(e, t, n, o) {
	return (e.dom.setAttribute("data-style-type", t), o === "string" && e.dom.setAttribute("data-value", n), e.contentDOM && e.contentDOM.setAttribute("data-editable", ""), e);
}
function Le$2(e, t) {
	return {
		config: e,
		implementation: t,
	};
}
function I$1(e, t) {
	return Le$2(
		{
			type: e.name,
			propSchema: t,
		},
		{
			mark: e,
			render(n, o) {
				const r = o.pmSchema.marks[e.name].spec.toDOM;
				if (r === void 0) throw new Error("This block has no default HTML serialization as its corresponding TipTap node doesn't implement `renderHTML`.");
				const a = o.pmSchema.mark(e.name, {
						stringValue: n,
					}),
					s = DOMSerializer.renderSpec(document, r(a, true));
				if (typeof s != "object" || !("dom" in s))
					throw new Error("Cannot use this block's default HTML serialization as its corresponding TipTap mark's `renderHTML` function does not return an object with the `dom` property.");
				return s;
			},
			toExternalHTML(n, o) {
				const r = o.pmSchema.marks[e.name].spec.toDOM;
				if (r === void 0) throw new Error("This block has no default HTML serialization as its corresponding TipTap node doesn't implement `renderHTML`.");
				const a = o.pmSchema.mark(e.name, {
						stringValue: n,
					}),
					s = DOMSerializer.renderSpec(document, r(a, true));
				if (typeof s != "object" || !("dom" in s))
					throw new Error("Cannot use this block's default HTML serialization as its corresponding TipTap mark's `renderHTML` function does not return an object with the `dom` property.");
				return s;
			},
		},
	);
}
function Lt$1(e) {
	return Object.fromEntries(Object.entries(e).map(([t, n]) => [t, n.config]));
}
function Tt$1(e, t) {
	const n = [
		{
			tag: `[data-style-type="${e.type}"]`,
			contentElement: (o) => {
				const r = o;
				return r.matches("[data-editable]") ? r : r.querySelector("[data-editable]") || r;
			},
		},
	];
	return (
		t &&
			n.push({
				tag: "*",
				// By default, styles can overlap each other, so the rules should not
				// completely consume the element they parse (which can have multiple
				// styles).
				consuming: false,
				getAttrs(o) {
					if (typeof o == "string") return false;
					const r = t == null ? void 0 : t(o);
					return r === void 0 ? false : { stringValue: r };
				},
			}),
		n
	);
}
function Te$2(e, t) {
	const n = Mark.create({
		name: e.type,
		addAttributes() {
			return wt(e.propSchema);
		},
		parseHTML() {
			return Tt$1(e, t.parse);
		},
		renderHTML({ mark: o }) {
			const r = (t.toExternalHTML || t.render)(o.attrs.stringValue);
			return W$1(r, e.type, o.attrs.stringValue, e.propSchema);
		},
		addMarkView() {
			return ({ mark: o }) => {
				const r = t.render(o.attrs.stringValue);
				return W$1(r, e.type, o.attrs.stringValue, e.propSchema);
			};
		},
	});
	return Le$2(e, {
		...t,
		mark: n,
		render: (o) => {
			const r = t.render(o);
			return W$1(r, e.type, o, e.propSchema);
		},
		toExternalHTML: (o) => {
			const r = (t.toExternalHTML || t.render)(o);
			return W$1(r, e.type, o, e.propSchema);
		},
	});
}
function Bt$1(e, t) {
	let n, o;
	if (
		(t.firstChild.descendants((r, a) =>
			n ? false
			: !At$1(r) || r.attrs.id !== e ? true
			: ((n = r), (o = a + 1), false),
		),
		!(n === void 0 || o === void 0))
	)
		return {
			node: n,
			posBeforeNode: o,
		};
}
function At$1(e) {
	return e.type.isInGroup("bnBlock");
}
const yo$1 =
	(e, t) =>
	({ tr: n, dispatch: o }) => (o && Q$1(n, e, t), true);
function Q$1(e, t, n, o, r) {
	const a = It$1(e.doc.resolve(t));
	let s = null;
	a.blockNoteType === "table" && (s = Pt$1(e));
	const i = ht(e);
	const c = i.nodes[a.blockNoteType],
		l = i.nodes[n.type || a.blockNoteType],
		u = l.isInGroup("bnBlock") ? l : i.nodes.blockContainer;
	if (a.isBlockContainer && l.isInGroup("blockContent")) {
		(ue$2(n, e, a), Nt$1(n, e, c, l, a));
	} else if (!a.isBlockContainer && l.isInGroup("bnBlock")) ue$2(n, e, a);
	else {
		const d = L$2(a.bnBlock.node, i);
		e.replaceWith(
			a.bnBlock.beforePos,
			a.bnBlock.afterPos,
			bt$1(
				{
					children: d.children,
					// if no children are passed in, use existing children
					...n,
				},
				i,
			),
		);
		return;
	}
	(e.setNodeMarkup(a.bnBlock.beforePos, u, {
		...a.bnBlock.node.attrs,
		...n.props,
	}),
		s && It(e, a, s));
}
function Nt$1(e, t, n, o, r, a, s) {
	const i = ht(t);
	let c = "keep";
	if (e.content)
		if (typeof e.content == "string") c = T$1([e.content], i, o.name);
		else if (Array.isArray(e.content)) c = T$1(e.content, i, o.name);
		else if (e.content.type === "tableContent") c = kt$1(e.content, i);
		else throw new O(e.content.type);
	else n.spec.content === "" || (o.spec.content !== n.spec.content && (c = []));
	if (c === "keep")
		t.setNodeMarkup(r.blockContent.beforePos, o, {
			...r.blockContent.node.attrs,
			...e.props,
		});
	else
		t.replaceWith(
			r.blockContent.beforePos,
			r.blockContent.afterPos,
			o.createChecked(
				{
					...r.blockContent.node.attrs,
					...e.props,
				},
				c,
			),
		);
}
function ue$2(e, t, n) {
	const o = ht(t);
	if (e.children !== void 0 && e.children.length > 0) {
		const r = e.children.map((a) => bt$1(a, o));
		if (n.childContainer) t.step(new ReplaceStep(n.childContainer.beforePos + 1, n.childContainer.afterPos - 1, new Slice(Fragment.from(r), 0, 0)));
		else {
			if (!n.isBlockContainer) throw new Error("impossible");
			t.insert(n.blockContent.afterPos, o.nodes.blockGroup.createChecked({}, r));
		}
	}
}
function vo$1(e, t, n, o, r) {
	const a = typeof t == "string" ? t : t.id,
		s = Bt$1(a, e.doc);
	if (!s) throw new Error(`Block with ID ${a} not found`);
	Q$1(e, s.posBeforeNode, n);
	const i = e.doc.resolve(s.posBeforeNode + 1).node(),
		c = ht(e);
	return L$2(i, c);
}
function Pt$1(e) {
	const t = "selection" in e ? e.selection : null;
	if (!(t instanceof TextSelection)) return null;
	const n = e.doc.resolve(t.head);
	let o = -1,
		r = -1;
	for (let x = n.depth; x >= 0; x--) {
		const S = n.node(x).type.name;
		if ((o < 0 && (S === "tableCell" || S === "tableHeader") && (o = x), S === "table")) {
			r = x;
			break;
		}
	}
	if (o < 0 || r < 0) return null;
	const a = n.before(o),
		s = n.before(r),
		i = e.doc.nodeAt(s);
	if (!i || i.type.name !== "table") return null;
	const c = TableMap.get(i),
		l = a - (s + 1),
		u = c.map.indexOf(l);
	if (u < 0) return null;
	const d = Math.floor(u / c.width),
		p = u % c.width,
		f = a + 1 + 1,
		E = Math.max(0, t.head - f);
	return { row: d, col: p, offset: E };
}
function It(e, t, n) {
	var x;
	if (t.blockNoteType !== "table") return false;
	let o = -1;
	if (t.isBlockContainer) o = e.mapping.map(t.blockContent.beforePos);
	else {
		const S = e.mapping.map(t.bnBlock.beforePos),
			N = S + (((x = e.doc.nodeAt(S)) == null ? void 0 : x.nodeSize) || 0);
		e.doc.nodesBetween(S, N, (g, M) => (g.type.name === "table" ? ((o = M), false) : true));
	}
	const r = o >= 0 ? e.doc.nodeAt(o) : null;
	if (!r || r.type.name !== "table") return false;
	const a = TableMap.get(r),
		s = Math.max(0, Math.min(n.row, a.height - 1)),
		i = Math.max(0, Math.min(n.col, a.width - 1)),
		c = s * a.width + i,
		l = a.map[c];
	if (l == null) return false;
	const d = o + 1 + l + 1,
		p = e.doc.nodeAt(d),
		h = d + 1,
		f = p ? p.content.size : 0,
		E = h + Math.max(0, Math.min(n.offset, f));
	return ("selection" in e && e.setSelection(TextSelection.create(e.doc, E)), true);
}
const T = {
		gray: {
			text: "#9b9a97",
			background: "#ebeced",
		},
		brown: {
			text: "#64473a",
			background: "#e9e5e3",
		},
		red: {
			text: "#e03e3e",
			background: "#fbe4e4",
		},
		orange: {
			text: "#d9730d",
			background: "#f6e9d9",
		},
		yellow: {
			text: "#dfab01",
			background: "#fbf3db",
		},
		green: {
			text: "#4d6461",
			background: "#ddedea",
		},
		blue: {
			text: "#0b6e99",
			background: "#ddebf1",
		},
		purple: {
			text: "#6940a5",
			background: "#eae4f2",
		},
		pink: {
			text: "#ad1a72",
			background: "#f4dfeb",
		},
	},
	m$1 = {
		backgroundColor: {
			default: "default",
		},
		textColor: {
			default: "default",
		},
		textAlignment: {
			default: "left",
			values: ["left", "center", "right", "justify"],
		},
	},
	C = (e) => {
		const t = {};
		return (
			e.hasAttribute("data-background-color") ? (t.backgroundColor = e.getAttribute("data-background-color")) : e.style.backgroundColor && (t.backgroundColor = e.style.backgroundColor),
			e.hasAttribute("data-text-color") ? (t.textColor = e.getAttribute("data-text-color")) : e.style.color && (t.textColor = e.style.color),
			(t.textAlignment = m$1.textAlignment.values.includes(e.style.textAlign) ? e.style.textAlign : void 0),
			t
		);
	},
	A$2 = (e, t) => {
		(e.backgroundColor && e.backgroundColor !== m$1.backgroundColor.default && (t.style.backgroundColor = e.backgroundColor in T ? T[e.backgroundColor].background : e.backgroundColor),
			e.textColor && e.textColor !== m$1.textColor.default && (t.style.color = e.textColor in T ? T[e.textColor].text : e.textColor),
			e.textAlignment && e.textAlignment !== m$1.textAlignment.default && (t.style.textAlign = e.textAlignment));
	},
	So$1 = (e = "backgroundColor") => ({
		default: m$1.backgroundColor.default,
		parseHTML: (t) =>
			t.hasAttribute("data-background-color") ? t.getAttribute("data-background-color")
			: t.style.backgroundColor ? t.style.backgroundColor
			: m$1.backgroundColor.default,
		renderHTML: (t) =>
			t[e] === m$1.backgroundColor.default ?
				{}
			:	{
					"data-background-color": t[e],
				},
	}),
	xo$1 = (e = "textColor") => ({
		default: m$1.textColor.default,
		parseHTML: (t) =>
			t.hasAttribute("data-text-color") ? t.getAttribute("data-text-color")
			: t.style.color ? t.style.color
			: m$1.textColor.default,
		renderHTML: (t) =>
			t[e] === m$1.textColor.default ?
				{}
			:	{
					"data-text-color": t[e],
				},
	}),
	$$2 = (e, t) => {
		const n = e.querySelector(t);
		if (!n) return;
		const o = e.querySelector("figcaption"),
			r = (o == null ? void 0 : o.textContent) ?? void 0;
		return { targetElement: n, caption: r };
	},
	H$1 = a$1(({ editor: e }) => {
		const t = f$2(void 0);
		function n() {
			t.setState(void 0);
		}
		return {
			key: "filePanel",
			store: t,
			mount({ signal: o }) {
				const r = e.onChange(
						n,
						// Don't trigger if the changes are caused by a remote user.
						false,
					),
					a = e.onSelectionChange(
						n,
						// Don't trigger if the changes are caused by a remote user.
						false,
					);
				o.addEventListener("abort", () => {
					(r(), a());
				});
			},
			closeMenu: n,
			showMenu(o) {
				t.setState(o);
			},
		};
	}),
	Ht$1 = (e, t, n) => {
		const o = document.createElement("div");
		o.className = "bn-add-file-button";
		const r = document.createElement("div");
		((r.className = "bn-add-file-button-icon"),
			n ?
				r.appendChild(n)
			:	(r.innerHTML =
					'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M3 8L9.00319 2H19.9978C20.5513 2 21 2.45531 21 2.9918V21.0082C21 21.556 20.5551 22 20.0066 22H3.9934C3.44476 22 3 21.5501 3 20.9932V8ZM10 4V9H5V20H19V4H10Z"></path></svg>'),
			o.appendChild(r));
		const a = document.createElement("p");
		((a.className = "bn-add-file-button-text"),
			(a.innerHTML = e.type in t.dictionary.file_blocks.add_button_text ? t.dictionary.file_blocks.add_button_text[e.type] : t.dictionary.file_blocks.add_button_text.file),
			o.appendChild(a));
		const s = (c) => {
				(c.preventDefault(), c.stopPropagation());
			},
			i = () => {
				var c;
				t.isEditable && ((c = t.getExtension(H$1)) == null || c.showMenu(e.id));
			};
		return (
			o.addEventListener("mousedown", s, true),
			o.addEventListener("click", i, true),
			{
				dom: o,
				destroy: () => {
					(o.removeEventListener("mousedown", s, true), o.removeEventListener("click", i, true));
				},
			}
		);
	},
	Dt$1 =
		'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M3 8L9.00319 2H19.9978C20.5513 2 21 2.45531 21 2.9918V21.0082C21 21.556 20.5551 22 20.0066 22H3.9934C3.44476 22 3 21.5501 3 20.9932V8ZM10 4V9H5V20H19V4H10Z"></path></svg>',
	Ot$1 = (e) => {
		const t = document.createElement("div");
		t.className = "bn-file-name-with-icon";
		const n = document.createElement("div");
		((n.className = "bn-file-icon"), (n.innerHTML = Dt$1), t.appendChild(n));
		const o = document.createElement("p");
		return (
			(o.className = "bn-file-name"),
			(o.textContent = e.props.name),
			t.appendChild(o),
			{
				dom: t,
			}
		);
	},
	J = (e, t, n, o) => {
		const r = document.createElement("div");
		if (((r.className = "bn-file-block-content-wrapper"), e.props.url === "")) {
			const s = Ht$1(e, t, o);
			r.appendChild(s.dom);
			const i = t.onUploadStart((c) => {
				if (c === e.id) {
					r.removeChild(s.dom);
					const l = document.createElement("div");
					((l.className = "bn-file-loading-preview"), (l.textContent = "Loading..."), r.appendChild(l));
				}
			});
			return {
				dom: r,
				destroy: () => {
					(i(), s.destroy());
				},
			};
		}
		const a = { dom: r };
		if (e.props.showPreview === false || !n) {
			const s = Ot$1(e);
			(r.appendChild(s.dom),
				(a.destroy = () => {
					var i;
					(i = s.destroy) == null || i.call(s);
				}));
		} else r.appendChild(n.dom);
		if (e.props.caption) {
			const s = document.createElement("p");
			((s.className = "bn-file-caption"), (s.textContent = e.props.caption), r.appendChild(s));
		}
		return a;
	},
	Y = (e, t) => {
		const n = document.createElement("figure"),
			o = document.createElement("figcaption");
		return ((o.textContent = t), n.appendChild(e), n.appendChild(o), { dom: n });
	},
	U = (e, t) => {
		const n = document.createElement("div"),
			o = document.createElement("p");
		return (
			(o.textContent = t),
			n.appendChild(e),
			n.appendChild(o),
			{
				dom: n,
			}
		);
	},
	pe$1 = (e) => ({ url: e.src || void 0 }),
	_t$1 =
		'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M2 16.0001H5.88889L11.1834 20.3319C11.2727 20.405 11.3846 20.4449 11.5 20.4449C11.7761 20.4449 12 20.2211 12 19.9449V4.05519C12 3.93977 11.9601 3.8279 11.887 3.73857C11.7121 3.52485 11.3971 3.49335 11.1834 3.66821L5.88889 8.00007H2C1.44772 8.00007 1 8.44778 1 9.00007V15.0001C1 15.5524 1.44772 16.0001 2 16.0001ZM23 12C23 15.292 21.5539 18.2463 19.2622 20.2622L17.8445 18.8444C19.7758 17.1937 21 14.7398 21 12C21 9.26016 19.7758 6.80629 17.8445 5.15557L19.2622 3.73779C21.5539 5.75368 23 8.70795 23 12ZM18 12C18 10.0883 17.106 8.38548 15.7133 7.28673L14.2842 8.71584C15.3213 9.43855 16 10.64 16 12C16 13.36 15.3213 14.5614 14.2842 15.2841L15.7133 16.7132C17.106 15.6145 18 13.9116 18 12Z"></path></svg>',
	Vt$1 = (e) => ({
		type: "audio",
		propSchema: {
			backgroundColor: m$1.backgroundColor,
			// File name.
			name: {
				default: "",
			},
			// File url.
			url: {
				default: "",
			},
			// File caption.
			caption: {
				default: "",
			},
			showPreview: {
				default: true,
			},
		},
		content: "none",
	}),
	Wt$1 =
		(e = {}) =>
		(t) => {
			if (t.tagName === "AUDIO") {
				if (t.closest("figure")) return;
				const { backgroundColor: n } = C(t);
				return {
					...pe$1(t),
					backgroundColor: n,
				};
			}
			if (t.tagName === "FIGURE") {
				const n = $$2(t, "audio");
				if (!n) return;
				const { targetElement: o, caption: r } = n,
					{ backgroundColor: a } = C(t);
				return {
					...pe$1(o),
					backgroundColor: a,
					caption: r,
				};
			}
		},
	Rt$1 =
		(e = {}) =>
		(t, n) => {
			const o = document.createElement("div");
			o.innerHTML = e.icon ?? _t$1;
			const r = document.createElement("audio");
			return (
				(r.className = "bn-audio"),
				n.resolveFileUrl ?
					n.resolveFileUrl(t.props.url).then((a) => {
						r.src = a;
					})
				:	(r.src = t.props.url),
				(r.controls = true),
				(r.contentEditable = "false"),
				(r.draggable = false),
				J(t, n, { dom: r }, o.firstElementChild)
			);
		},
	Ft$1 =
		(e = {}) =>
		(t, n) => {
			if (!t.props.url) {
				const r = document.createElement("p");
				return (
					(r.textContent = "Add audio"),
					{
						dom: r,
					}
				);
			}
			let o;
			return (
				t.props.showPreview ?
					((o = document.createElement("audio")), (o.src = t.props.url))
				:	((o = document.createElement("a")), (o.href = t.props.url), (o.textContent = t.props.name || t.props.url)),
				t.props.caption ?
					t.props.showPreview ?
						Y(o, t.props.caption)
					:	U(o, t.props.caption)
				:	{
						dom: o,
					}
			);
		},
	$t$1 = v$1(Vt$1, (e) => ({
		meta: {
			fileBlockAccept: ["audio/*"],
		},
		parse: Wt$1(e),
		render: Rt$1(e),
		toExternalHTML: Ft$1(e),
		runsBefore: ["file"],
	})),
	fe$1 = Symbol.for("blocknote.shikiParser"),
	j = Symbol.for("blocknote.shikiHighlighterPromise");
function Ut$1(e) {
	const t = globalThis;
	let n, o;
	return createHighlightPlugin({
		parser: (s) => {
			if (!e.createHighlighter) return [];
			if (!n)
				return (
					(t[j] = t[j] || e.createHighlighter()),
					t[j].then((c) => {
						n = c;
					})
				);
			const i = Be$1(e, s.language);
			return (
				!i || i === "text" || i === "none" || i === "plaintext" || i === "txt" ? []
				: n.getLoadedLanguages().includes(i) ? (o || ((o = t[fe$1] || createParser(n)), (t[fe$1] = o)), o(s))
				: n.loadLanguage(i)
			);
		},
		languageExtractor: (s) => s.attrs.language,
		nodeTypes: ["codeBlock"],
	});
}
const qt$1 = ({ defaultLanguage: e = "text" }) => ({
		type: "codeBlock",
		propSchema: {
			language: {
				default: e,
			},
		},
		content: "inline",
	}),
	jt$1 = v$1(
		qt$1,
		(e) => ({
			meta: {
				code: true,
				defining: true,
				isolating: false,
			},
			parse: (t) => {
				var r, a;
				if (t.tagName !== "PRE" || t.childElementCount !== 1 || ((r = t.firstElementChild) == null ? void 0 : r.tagName) !== "CODE") return;
				const n = t.firstElementChild;
				return { language: n.getAttribute("data-language") || ((a = n.className.split(" ").find((s) => s.includes("language-"))) == null ? void 0 : a.replace("language-", "")) };
			},
			parseContent: ({ el: t, schema: n }) => {
				const o = DOMParser$1.fromSchema(n),
					r = t.firstElementChild;
				return o.parse(r, {
					preserveWhitespace: "full",
					topNode: n.nodes.codeBlock.create(),
				}).content;
			},
			render(t, n) {
				const o = document.createDocumentFragment(),
					r = document.createElement("pre"),
					a = document.createElement("code");
				r.appendChild(a);
				let s;
				if (e.supportedLanguages) {
					const i = document.createElement("select");
					if (
						(Object.entries(e.supportedLanguages ?? {}).forEach(([l, { name: u }]) => {
							const d = document.createElement("option");
							((d.value = l), (d.text = u), i.appendChild(d));
						}),
						(i.value = t.props.language || e.defaultLanguage || "text"),
						n.isEditable)
					) {
						const l = (u) => {
							const d = u.target.value;
							n.updateBlock(t.id, { props: { language: d } });
						};
						(i.addEventListener("change", l), (s = () => i.removeEventListener("change", l)));
					} else i.disabled = true;
					const c = document.createElement("div");
					((c.contentEditable = "false"), c.appendChild(i), o.appendChild(c));
				}
				return (
					o.appendChild(r),
					{
						dom: o,
						contentDOM: a,
						destroy: () => {
							s == null || s();
						},
					}
				);
			},
			toExternalHTML(t) {
				const n = document.createElement("pre"),
					o = document.createElement("code");
				return (
					(o.className = `language-${t.props.language}`),
					(o.dataset.language = t.props.language),
					n.appendChild(o),
					{
						dom: n,
						contentDOM: o,
					}
				);
			},
		}),
		(e) => [
			a$1({
				key: "code-block-highlighter",
				prosemirrorPlugins: [Ut$1(e)],
			}),
			a$1({
				key: "code-block-keyboard-shortcuts",
				keyboardShortcuts: {
					"Delete": ({ editor: t }) =>
						t.transact((n) => {
							const { block: o } = t.getTextCursorPosition();
							if (o.type !== "codeBlock") return false;
							const { $from: r } = n.selection;
							return r.parent.textContent ? false : (t.removeBlocks([o]), true);
						}),
					"Tab": ({ editor: t }) =>
						e.indentLineWithTab === false ?
							false
						:	t.transact((n) => {
								const { block: o } = t.getTextCursorPosition();
								return o.type === "codeBlock" ? (n.insertText("  "), true) : false;
							}),
					"Enter": ({ editor: t }) =>
						t.transact((n) => {
							const { block: o, nextBlock: r } = t.getTextCursorPosition();
							if (o.type !== "codeBlock") return false;
							const { $from: a } = n.selection,
								s = a.parentOffset === a.parent.nodeSize - 2,
								i = a.parent.textContent.endsWith(`

`);
							if (s && i) {
								if ((n.delete(a.pos - 2, a.pos), r)) return (t.setTextCursorPosition(r, "start"), true);
								const [c] = t.insertBlocks([{ type: "paragraph" }], o, "after");
								return (t.setTextCursorPosition(c, "start"), true);
							}
							return (
								n.insertText(`
`),
								true
							);
						}),
					"Shift-Enter": ({ editor: t }) =>
						t.transact(() => {
							const { block: n } = t.getTextCursorPosition();
							if (n.type !== "codeBlock") return false;
							const [o] = t.insertBlocks(
								// insert a new paragraph
								[{ type: "paragraph" }],
								n,
								"after",
							);
							return (t.setTextCursorPosition(o, "start"), true);
						}),
				},
				inputRules: [
					{
						find: /^```(.*?)\s$/,
						replace: ({ match: t }) => {
							const n = t[1].trim();
							return {
								type: "codeBlock",
								props: {
									language: {
										language: Be$1(e, n) ?? n,
									}.language,
								},
								content: [],
							};
						},
					},
				],
			}),
		],
	);
function Be$1(e, t) {
	var n;
	return (n = Object.entries(e.supportedLanguages ?? {}).find(([o, { aliases: r }]) => (r == null ? void 0 : r.includes(t)) || o === t)) == null ? void 0 : n[0];
}
const Gt$1 = () => ({
		type: "divider",
		propSchema: {},
		content: "none",
	}),
	zt$1 = v$1(
		Gt$1,
		{
			meta: {
				isolating: false,
			},
			parse(e) {
				if (e.tagName === "HR") return {};
			},
			render() {
				return {
					dom: document.createElement("hr"),
				};
			},
		},
		[
			a$1({
				key: "divider-block-shortcuts",
				inputRules: [
					{
						find: new RegExp("^---$"),
						replace() {
							return { type: "divider", props: {}, content: [] };
						},
					},
				],
			}),
		],
	),
	me$1 = (e) => ({ url: e.src || void 0 }),
	Zt$2 = () => ({
		type: "file",
		propSchema: {
			backgroundColor: m$1.backgroundColor,
			// File name.
			name: {
				default: "",
			},
			// File url.
			url: {
				default: "",
			},
			// File caption.
			caption: {
				default: "",
			},
		},
		content: "none",
	}),
	Xt$1 = () => (e) => {
		if (e.tagName === "EMBED") {
			if (e.closest("figure")) return;
			const { backgroundColor: t } = C(e);
			return {
				...me$1(e),
				backgroundColor: t,
			};
		}
		if (e.tagName === "FIGURE") {
			const t = $$2(e, "embed");
			if (!t) return;
			const { targetElement: n, caption: o } = t,
				{ backgroundColor: r } = C(e);
			return {
				...me$1(n),
				backgroundColor: r,
				caption: o,
			};
		}
	},
	Kt$1 = v$1(Zt$2, {
		meta: {
			fileBlockAccept: ["*/*"],
		},
		parse: Xt$1(),
		render(e, t) {
			return J(e, t);
		},
		toExternalHTML(e) {
			if (!e.props.url) {
				const n = document.createElement("p");
				return (
					(n.textContent = "Add file"),
					{
						dom: n,
					}
				);
			}
			const t = document.createElement("a");
			return (
				(t.href = e.props.url),
				(t.textContent = e.props.name || e.props.url),
				e.props.caption ?
					U(t, e.props.caption)
				:	{
						dom: t,
					}
			);
		},
	});
function Ae$2(e, t, n) {
	var u;
	const o = DOMParser$1.fromSchema(t),
		r = e.querySelector(":scope > summary");
	let a;
	if (r) {
		const d = r.cloneNode(true);
		(we$1(d),
			(a = o.parse(d, {
				topNode: t.nodes.paragraph.create(),
				preserveWhitespace: true,
			}).content));
	} else a = Fragment.empty;
	const s = document.createElement("div");
	s.setAttribute("data-node-type", "blockGroup");
	let i = false;
	for (const d of Array.from(e.childNodes)) d.tagName !== "SUMMARY" && ((d.nodeType === 3 && !((u = d.textContent) != null && u.trim())) || ((i = true), s.appendChild(d.cloneNode(true))));
	const c = t.nodes[n].create({}, a);
	if (!i) return c.content;
	const l = o.parse(s, {
		topNode: t.nodes.blockGroup.create(),
	});
	return l.content.size > 0 ? c.content.addToEnd(l) : c.content;
}
const Qt$1 = {
		set: (e, t) => window.localStorage.setItem(`toggle-${e.id}`, t ? "true" : "false"),
		get: (e) => window.localStorage.getItem(`toggle-${e.id}`) === "true",
	},
	Ne$1 = (e, t, n, o = Qt$1) => {
		if ("isToggleable" in e.props && !e.props.isToggleable)
			return {
				dom: n,
			};
		const r = document.createElement("div"),
			a = document.createElement("div");
		a.className = "bn-toggle-wrapper";
		const s = document.createElement("button");
		((s.className = "bn-toggle-button"),
			(s.type = "button"),
			(s.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="CURRENTCOLOR"><path d="M320-200v-560l440 280-440 280Z"/></svg>')); // https://fonts.google.com/icons?selected=Material+Symbols+Rounded:chevron_right:FILL@0;wght@700;GRAD@0;opsz@24&icon.query=chevron&icon.style=Rounded&icon.size=24&icon.color=%23e8eaed
		const i = (f) => f.preventDefault();
		s.addEventListener("mousedown", i);
		const c = () => {
			var f;
			a.getAttribute("data-show-children") === "true" ?
				(a.setAttribute("data-show-children", "false"), o.set(t.getBlock(e), false), r.contains(l) && r.removeChild(l))
			:	(a.setAttribute("data-show-children", "true"),
				o.set(t.getBlock(e), true),
				t.isEditable && ((f = t.getBlock(e)) == null ? void 0 : f.children.length) === 0 && !r.contains(l) && r.appendChild(l));
		};
		(s.addEventListener("click", c), a.appendChild(s), a.appendChild(n));
		const l = document.createElement("button");
		((l.className = "bn-toggle-add-block-button"), (l.type = "button"), (l.textContent = t.dictionary.toggle_blocks.add_block_button));
		const u = (f) => f.preventDefault();
		l.addEventListener("mousedown", u);
		const d = () => {
			t.transact(() => {
				const f = t.updateBlock(e, {
					// Single empty block with default type.
					children: [{}],
				});
				(t.setTextCursorPosition(f.children[0].id, "end"), t.focus());
			});
		};
		(l.addEventListener("click", d), r.appendChild(a));
		let p = e.children.length;
		const h = t.onChange(() => {
			var E;
			const f = ((E = t.getBlock(e)) == null ? void 0 : E.children.length) ?? 0;
			(f > p ?
				(a.getAttribute("data-show-children") === "false" && (a.setAttribute("data-show-children", "true"), o.set(t.getBlock(e), true)), r.contains(l) && r.removeChild(l))
			:	f === 0 && f < p && (a.getAttribute("data-show-children") === "true" && (a.setAttribute("data-show-children", "false"), o.set(t.getBlock(e), false)), r.contains(l) && r.removeChild(l)),
				(p = f));
		});
		return (
			o.get(e) ? (a.setAttribute("data-show-children", "true"), t.isEditable && e.children.length === 0 && r.appendChild(l)) : a.setAttribute("data-show-children", "false"),
			{
				dom: r,
				// Prevents re-renders when the toggle button is clicked.
				ignoreMutation: (f) =>
					f instanceof MutationRecord && // We want to prevent re-renders when the view changes, so we ignore
					// all mutations where the `data-show-children` attribute is changed
					// or the "add block" button is added/removed.
					((f.type === "attributes" && f.target === a && f.attributeName === "data-show-children") || (f.type === "childList" && (f.addedNodes[0] === l || f.removedNodes[0] === l))),
				destroy: () => {
					(s.removeEventListener("mousedown", i), s.removeEventListener("click", c), l.removeEventListener("mousedown", u), l.removeEventListener("click", d), h == null || h());
				},
			}
		);
	},
	Pe$2 = [1, 2, 3, 4, 5, 6],
	Jt$1 =
		(e) =>
		({ editor: t }) => {
			const n = t.getTextCursorPosition();
			return t.schema.blockSchema[n.block.type].content !== "inline" ?
					false
				:	(t.updateBlock(n.block, {
						type: "heading",
						props: { level: e },
					}),
					true);
		},
	Yt$1 = ({ defaultLevel: e = 1, levels: t = Pe$2, allowToggleHeadings: n = true } = {}) => ({
		type: "heading",
		propSchema: {
			...m$1,
			level: { default: e, values: t },
			...(n ? { isToggleable: { default: false, optional: true } } : {}),
		},
		content: "inline",
	}),
	en$1 = v$1(
		Yt$1,
		({ allowToggleHeadings: e = true } = {}) => ({
			meta: {
				isolating: false,
			},
			parse(t) {
				if (e && t.tagName === "DETAILS") {
					const o = t.querySelector(":scope > summary");
					if (!o) return;
					const r = o.querySelector("h1, h2, h3, h4, h5, h6");
					return r ?
							{
								...C(r),
								level: parseInt(r.tagName[1]),
								isToggleable: true,
							}
						:	void 0;
				}
				let n;
				switch (t.tagName) {
					case "H1":
						n = 1;
						break;
					case "H2":
						n = 2;
						break;
					case "H3":
						n = 3;
						break;
					case "H4":
						n = 4;
						break;
					case "H5":
						n = 5;
						break;
					case "H6":
						n = 6;
						break;
					default:
						return;
				}
				return {
					...C(t),
					level: n,
				};
			},
			...(e ?
				{
					parseContent: ({ el: t, schema: n }) => {
						if (t.tagName === "DETAILS") return Ae$2(t, n, "heading");
					},
				}
			:	{}),
			runsBefore: ["toggleListItem"],
			render(t, n) {
				const o = document.createElement(`h${t.props.level}`);
				return e ?
						{ ...Ne$1(t, n, o), contentDOM: o }
					:	{
							dom: o,
							contentDOM: o,
						};
			},
			toExternalHTML(t) {
				const n = document.createElement(`h${t.props.level}`);
				if ((A$2(t.props, n), e && t.props.isToggleable)) {
					const o = document.createElement("details");
					o.setAttribute("open", "");
					const r = document.createElement("summary");
					return (
						r.appendChild(n),
						o.appendChild(r),
						{
							dom: o,
							contentDOM: n,
							childrenDOM: o,
						}
					);
				}
				return {
					dom: n,
					contentDOM: n,
				};
			},
		}),
		({ levels: e = Pe$2 } = {}) => [
			a$1({
				key: "heading-shortcuts",
				keyboardShortcuts: Object.fromEntries(e.map((t) => [`Mod-Alt-${t}`, Jt$1(t)]) ?? []),
				inputRules: e.map((t) => ({
					find: new RegExp(`^(#{${t}})\\s$`),
					replace({ match: n }) {
						return {
							type: "heading",
							props: {
								level: n[1].length,
							},
						};
					},
				})),
			}),
		],
	),
	Ie$2 = (e, t, n, o, r) => {
		const { dom: a, destroy: s } = J(e, t, n, r),
			i = a;
		((i.style.position = "relative"), e.props.url && e.props.showPreview && (e.props.previewWidth ? (i.style.width = `${e.props.previewWidth}px`) : (i.style.width = "fit-content")));
		const c = document.createElement("div");
		((c.className = "bn-resize-handle"), (c.style.left = "4px"));
		const l = document.createElement("div");
		((l.className = "bn-resize-handle"), (l.style.right = "4px"));
		const u = document.createElement("div");
		((u.style.position = "absolute"), (u.style.height = "100%"), (u.style.width = "100%"));
		let d,
			p = e.props.previewWidth;
		const h = (g) => {
				var te, ne;
				if (!d) {
					!t.isEditable && o.contains(c) && o.contains(l) && (o.removeChild(c), o.removeChild(l));
					return;
				}
				let M;
				const V = "touches" in g ? g.touches[0].clientX : g.clientX;
				(e.props.textAlignment === "center" ?
					d.handleUsed === "left" ?
						(M = d.initialWidth + (d.initialClientX - V) * 2)
					:	(M = d.initialWidth + (V - d.initialClientX) * 2)
				: d.handleUsed === "left" ? (M = d.initialWidth + d.initialClientX - V)
				: (M = d.initialWidth + V - d.initialClientX),
					(p = Math.min(Math.max(M, 64), ((ne = (te = t.domElement) == null ? void 0 : te.firstElementChild) == null ? void 0 : ne.clientWidth) || Number.MAX_VALUE)),
					(i.style.width = `${p}px`));
			},
			f = (g) => {
				((!g.target || !i.contains(g.target) || !t.isEditable) && o.contains(c) && o.contains(l) && (o.removeChild(c), o.removeChild(l)),
					d &&
						((d = void 0),
						i.contains(u) && i.removeChild(u),
						t.updateBlock(e, {
							props: {
								previewWidth: p,
							},
						})));
			},
			E = () => {
				t.isEditable && (o.appendChild(c), o.appendChild(l));
			},
			x = (g) => {
				g.relatedTarget === c || g.relatedTarget === l || d || (t.isEditable && o.contains(c) && o.contains(l) && (o.removeChild(c), o.removeChild(l)));
			},
			S = (g) => {
				(g.preventDefault(), i.contains(u) || i.appendChild(u));
				const M = "touches" in g ? g.touches[0].clientX : g.clientX;
				d = {
					handleUsed: "left",
					initialWidth: i.clientWidth,
					initialClientX: M,
				};
			},
			N = (g) => {
				(g.preventDefault(), i.contains(u) || i.appendChild(u));
				const M = "touches" in g ? g.touches[0].clientX : g.clientX;
				d = {
					handleUsed: "right",
					initialWidth: i.clientWidth,
					initialClientX: M,
				};
			};
		return (
			window.addEventListener("mousemove", h),
			window.addEventListener("touchmove", h),
			window.addEventListener("mouseup", f),
			window.addEventListener("touchend", f),
			i.addEventListener("mouseenter", E),
			i.addEventListener("mouseleave", x),
			c.addEventListener("mousedown", S),
			c.addEventListener("touchstart", S),
			l.addEventListener("mousedown", N),
			l.addEventListener("touchstart", N),
			{
				dom: i,
				destroy: () => {
					(s == null || s(),
						window.removeEventListener("mousemove", h),
						window.removeEventListener("touchmove", h),
						window.removeEventListener("mouseup", f),
						window.removeEventListener("touchend", f),
						i.removeEventListener("mouseenter", E),
						i.removeEventListener("mouseleave", x),
						c.removeEventListener("mousedown", S),
						c.removeEventListener("touchstart", S),
						l.removeEventListener("mousedown", N),
						l.removeEventListener("touchstart", N));
				},
			}
		);
	},
	he$2 = (e) => {
		const t = e.src || void 0,
			n = e.width || void 0,
			o = e.alt || void 0;
		return { url: t, previewWidth: n, name: o };
	},
	tn$1 =
		'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M5 11.1005L7 9.1005L12.5 14.6005L16 11.1005L19 14.1005V5H5V11.1005ZM4 3H20C20.5523 3 21 3.44772 21 4V20C21 20.5523 20.5523 21 20 21H4C3.44772 21 3 20.5523 3 20V4C3 3.44772 3.44772 3 4 3ZM15.5 10C14.6716 10 14 9.32843 14 8.5C14 7.67157 14.6716 7 15.5 7C16.3284 7 17 7.67157 17 8.5C17 9.32843 16.3284 10 15.5 10Z"></path></svg>',
	nn$1 = (e = {}) => ({
		type: "image",
		propSchema: {
			textAlignment: m$1.textAlignment,
			backgroundColor: m$1.backgroundColor,
			// File name.
			name: {
				default: "",
			},
			// File url.
			url: {
				default: "",
			},
			// File caption.
			caption: {
				default: "",
			},
			showPreview: {
				default: true,
			},
			// File preview width in px.
			previewWidth: {
				default: void 0,
				type: "number",
			},
		},
		content: "none",
	}),
	on$1 =
		(e = {}) =>
		(t) => {
			if (t.tagName === "IMG") {
				if (t.closest("figure")) return;
				const { backgroundColor: n } = C(t);
				return {
					...he$2(t),
					backgroundColor: n,
				};
			}
			if (t.tagName === "FIGURE") {
				const n = $$2(t, "img");
				if (!n) return;
				const { targetElement: o, caption: r } = n,
					{ backgroundColor: a } = C(t);
				return {
					...he$2(o),
					backgroundColor: a,
					caption: r,
				};
			}
		},
	rn$1 =
		(e = {}) =>
		(t, n) => {
			const o = document.createElement("div");
			o.innerHTML = e.icon ?? tn$1;
			const r = document.createElement("div");
			r.className = "bn-visual-media-wrapper";
			const a = document.createElement("img");
			return (
				(a.className = "bn-visual-media"),
				n.resolveFileUrl ?
					n.resolveFileUrl(t.props.url).then((s) => {
						a.src = s;
					})
				:	(a.src = t.props.url),
				(a.alt = t.props.name || t.props.caption || "BlockNote image"),
				(a.contentEditable = "false"),
				(a.draggable = false),
				r.appendChild(a),
				Ie$2(t, n, { dom: r }, r, o.firstElementChild)
			);
		},
	an$1 =
		(e = {}) =>
		(t, n) => {
			if (!t.props.url) {
				const r = document.createElement("p");
				return (
					(r.textContent = "Add image"),
					{
						dom: r,
					}
				);
			}
			let o;
			return (
				t.props.showPreview ?
					((o = document.createElement("img")),
					(o.src = t.props.url),
					(o.alt = t.props.name || t.props.caption || "BlockNote image"),
					t.props.previewWidth && (o.width = t.props.previewWidth))
				:	((o = document.createElement("a")), (o.href = t.props.url), (o.textContent = t.props.name || t.props.url)),
				t.props.caption ?
					t.props.showPreview ?
						Y(o, t.props.caption)
					:	U(o, t.props.caption)
				:	{
						dom: o,
					}
			);
		},
	sn$1 = v$1(nn$1, (e) => ({
		meta: {
			fileBlockAccept: ["image/*"],
		},
		parse: on$1(e),
		render: rn$1(e),
		toExternalHTML: an$1(e),
		runsBefore: ["file"],
	})),
	wo$1 =
		(e, t, n) =>
		({ state: o, dispatch: r }) =>
			r ? He$1(o.tr, e, t, n) : true,
	He$1 = (e, t, n, o) => {
		const r = Y$1(e.doc, t),
			a = Z(r);
		if (!a.isBlockContainer) return false;
		const s = ht(e),
			i = [
				{
					type: a.bnBlock.node.type,
					// always keep blockcontainer type
					attrs: o ? { ...a.bnBlock.node.attrs, id: void 0 } : {},
				},
				{
					type: n ? a.blockContent.node.type : s.nodes.paragraph,
					attrs: o ? { ...a.blockContent.node.attrs } : {},
				},
			];
		return (e.split(t, 2, i), true);
	},
	q$1 = (e, t) => {
		const { blockInfo: n, selectionEmpty: o } = e.transact((s) => ({
			blockInfo: Ot$2(s),
			selectionEmpty: s.selection.anchor === s.selection.head,
		}));
		if (!n.isBlockContainer) return false;
		const { bnBlock: r, blockContent: a } = n;
		return (
			a.node.type.name !== t || !o ? false
			: a.node.childCount === 0 ?
				(e.transact((s) => {
					Q$1(s, r.beforePos, {
						type: "paragraph",
						props: {},
					});
				}),
				true)
			: a.node.childCount > 0 ? e.transact((s) => (s.deleteSelection(), He$1(s, s.selection.from, true)))
			: false
		);
	};
function ee(e, t, n) {
	var d, p, h;
	const o = DOMParser$1.fromSchema(t),
		r = e,
		a = document.createElement("div");
	a.setAttribute("data-node-type", "blockGroup");
	for (const f of Array.from(r.childNodes)) a.appendChild(f.cloneNode(true));
	let s = o.parse(a, {
		topNode: t.nodes.blockGroup.create(),
	});
	((p = (d = s.firstChild) == null ? void 0 : d.firstChild) == null ? void 0 : p.type.name) === "checkListItem" && (s = s.copy(s.content.cut(s.firstChild.firstChild.nodeSize + 2)));
	const i = (h = s.firstChild) == null ? void 0 : h.firstChild;
	if (!(i != null && i.isTextblock)) return Fragment.from(s);
	const c = t.nodes[n].create({}, i.content),
		l = s.content.cut(
			// +2 for the `blockGroup` node's start and end markers
			i.nodeSize + 2,
		);
	if (l.size > 0) {
		const f = s.copy(l);
		return c.content.addToEnd(f);
	}
	return c.content;
}
const cn$1 = () => ({
		type: "bulletListItem",
		propSchema: {
			...m$1,
		},
		content: "inline",
	}),
	ln$1 = v$1(
		cn$1,
		{
			meta: {
				isolating: false,
			},
			parse(e) {
				var n;
				if (e.tagName !== "LI") return;
				const t = e.parentElement;
				if (t !== null && (t.tagName === "UL" || (t.tagName === "DIV" && ((n = t.parentElement) == null ? void 0 : n.tagName) === "UL"))) return C(e);
			},
			// As `li` elements can contain multiple paragraphs, we need to merge their contents
			// into a single one so that ProseMirror can parse everything correctly.
			parseContent: ({ el: e, schema: t }) => ee(e, t, "bulletListItem"),
			render() {
				const e = document.createElement("p");
				return {
					dom: e,
					contentDOM: e,
				};
			},
			toExternalHTML(e) {
				const t = document.createElement("li"),
					n = document.createElement("p");
				return (
					A$2(e.props, t),
					t.appendChild(n),
					{
						dom: t,
						contentDOM: n,
					}
				);
			},
		},
		[
			a$1({
				key: "bullet-list-item-shortcuts",
				keyboardShortcuts: {
					"Enter": ({ editor: e }) => q$1(e, "bulletListItem"),
					"Mod-Shift-8": ({ editor: e }) => {
						const t = e.getTextCursorPosition();
						return e.schema.blockSchema[t.block.type].content !== "inline" ?
								false
							:	(e.updateBlock(t.block, {
									type: "bulletListItem",
									props: {},
								}),
								true);
					},
				},
				inputRules: [
					{
						find: /^\s?[-+*]\s$/,
						replace({ editor: e }) {
							if (Tt$2(e.prosemirrorState).blockNoteType !== "heading")
								return {
									type: "bulletListItem",
									props: {},
								};
						},
					},
				],
			}),
		],
	),
	dn$1 = () => ({
		type: "checkListItem",
		propSchema: {
			...m$1,
			checked: { default: false, type: "boolean" },
		},
		content: "inline",
	}),
	un$1 = v$1(
		dn$1,
		{
			meta: {
				isolating: false,
			},
			parse(e) {
				var n;
				if (e.tagName === "input")
					return (
						e.closest("[data-content-type]") || e.closest("li") ? void 0
						: e.type === "checkbox" ? { checked: e.checked }
						: void 0
					);
				if (e.tagName !== "LI") return;
				const t = e.parentElement;
				if (t !== null && (t.tagName === "UL" || (t.tagName === "DIV" && ((n = t.parentElement) == null ? void 0 : n.tagName) === "UL"))) {
					const o = e.querySelector("input[type=checkbox]") || null;
					return o === null ? void 0 : { ...C(e), checked: o.checked };
				}
			},
			// As `li` elements can contain multiple paragraphs, we need to merge their contents
			// into a single one so that ProseMirror can parse everything correctly.
			parseContent: ({ el: e, schema: t }) => ee(e, t, "checkListItem"),
			render(e, t) {
				const n = document.createDocumentFragment(),
					o = document.createElement("input");
				((o.type = "checkbox"),
					(o.checked = e.props.checked),
					e.props.checked && o.setAttribute("checked", ""),
					(o.disabled = !t.isEditable),
					o.addEventListener("change", () => {
						t.isEditable && t.updateBlock(e, { props: { checked: !e.props.checked } });
					}));
				const r = document.createElement("p"),
					a = document.createElement("div");
				return (
					(a.contentEditable = "false"),
					a.appendChild(o),
					n.appendChild(a),
					n.appendChild(r),
					{
						dom: n,
						contentDOM: r,
					}
				);
			},
			toExternalHTML(e) {
				const t = document.createElement("li"),
					n = document.createElement("input");
				((n.type = "checkbox"), (n.checked = e.props.checked), e.props.checked && n.setAttribute("checked", ""));
				const o = document.createElement("p");
				return (
					A$2(e.props, t),
					t.appendChild(n),
					t.appendChild(o),
					{
						dom: t,
						contentDOM: o,
					}
				);
			},
			runsBefore: ["bulletListItem"],
		},
		[
			a$1({
				key: "check-list-item-shortcuts",
				keyboardShortcuts: {
					"Enter": ({ editor: e }) => q$1(e, "checkListItem"),
					"Mod-Shift-9": ({ editor: e }) => {
						const t = e.getTextCursorPosition();
						return e.schema.blockSchema[t.block.type].content !== "inline" ?
								false
							:	(e.updateBlock(t.block, {
									type: "checkListItem",
									props: {},
								}),
								true);
					},
				},
				inputRules: [
					{
						find: /^\s?\[\s*\]\s$/,
						replace() {
							return {
								type: "checkListItem",
								props: {
									checked: false,
								},
							};
						},
					},
					{
						find: /^\s?\[[Xx]\]\s$/,
						replace() {
							return {
								type: "checkListItem",
								props: {
									checked: true,
								},
							};
						},
					},
				],
			}),
		],
	);
function De$2(e, t, n, o) {
	let r = e.firstChild.attrs.start || 1,
		a = true;
	const s = !!e.firstChild.attrs.start,
		i = Z({
			posBeforeNode: t,
			node: e,
		});
	if (!i.isBlockContainer) throw new Error("impossible");
	const c = n.doc.resolve(i.bnBlock.beforePos).nodeBefore,
		l = c ? o.get(c) : void 0;
	return (
		l !== void 0 ?
			((r = l + 1), (a = false))
		:	c &&
			Z({
				posBeforeNode: i.bnBlock.beforePos - c.nodeSize,
				node: c,
			}).blockNoteType === "numberedListItem" &&
			((r = De$2(c, i.bnBlock.beforePos - c.nodeSize, n, o).index + 1), (a = false)),
		o.set(e, r),
		{ index: r, isFirst: a, hasStart: s }
	);
}
function ge(e, t) {
	const n = /* @__PURE__ */ new Map(),
		o = t.decorations.map(e.mapping, e.doc),
		r = [];
	e.doc.nodesBetween(0, e.doc.nodeSize - 2, (s, i) => {
		if (s.type.name === "blockContainer" && s.firstChild.type.name === "numberedListItem") {
			const { index: c, isFirst: l, hasStart: u } = De$2(s, i, e, n);
			if (o.find(i, i + s.nodeSize, (p) => p.index === c && p.isFirst === l && p.hasStart === u).length === 0) {
				const p = e.doc.nodeAt(i + 1);
				r.push(
					// move in by 1 to account for the block container
					Decoration.node(i + 1, i + 1 + p.nodeSize, {
						"data-index": c.toString(),
					}),
				);
			}
		}
	});
	const a = r.flatMap((s) => o.find(s.from, s.to));
	return {
		decorations: o.remove(a).add(e.doc, r),
	};
}
const pn$1 = () =>
		new Plugin({
			key: new PluginKey("numbered-list-indexing-decorations"),
			state: {
				init(e, t) {
					return ge(t.tr, {
						decorations: DecorationSet.empty,
					});
				},
				apply(e, t) {
					return !e.docChanged && !e.selectionSet && t.decorations ? t : ge(e, t);
				},
			},
			props: {
				decorations(e) {
					var t;
					return ((t = this.getState(e)) == null ? void 0 : t.decorations) ?? DecorationSet.empty;
				},
			},
		}),
	fn$1 = () => ({
		type: "numberedListItem",
		propSchema: {
			...m$1,
			start: { default: void 0, type: "number" },
		},
		content: "inline",
	}),
	mn$1 = v$1(
		fn$1,
		{
			meta: {
				isolating: false,
			},
			parse(e) {
				var n;
				if (e.tagName !== "LI") return;
				const t = e.parentElement;
				if (t !== null && (t.tagName === "OL" || (t.tagName === "DIV" && ((n = t.parentElement) == null ? void 0 : n.tagName) === "OL"))) {
					const o = parseInt(t.getAttribute("start") || "1"),
						r = C(e);
					return e.previousElementSibling || o === 1 ?
							r
						:	{
								...r,
								start: o,
							};
				}
			},
			// As `li` elements can contain multiple paragraphs, we need to merge their contents
			// into a single one so that ProseMirror can parse everything correctly.
			parseContent: ({ el: e, schema: t }) => ee(e, t, "numberedListItem"),
			render() {
				const e = document.createElement("p");
				return {
					dom: e,
					contentDOM: e,
				};
			},
			toExternalHTML(e) {
				const t = document.createElement("li"),
					n = document.createElement("p");
				return (
					A$2(e.props, t),
					t.appendChild(n),
					{
						dom: t,
						contentDOM: n,
					}
				);
			},
		},
		[
			a$1({
				key: "numbered-list-item-shortcuts",
				inputRules: [
					{
						find: /^\s?(\d+)\.\s$/,
						replace({ match: e, editor: t }) {
							if (Tt$2(t.prosemirrorState).blockNoteType === "heading") return;
							const o = parseInt(e[1]);
							return {
								type: "numberedListItem",
								props: {
									start: o !== 1 ? o : void 0,
								},
							};
						},
					},
				],
				keyboardShortcuts: {
					"Enter": ({ editor: e }) => q$1(e, "numberedListItem"),
					"Mod-Shift-7": ({ editor: e }) => {
						const t = e.getTextCursorPosition();
						return e.schema.blockSchema[t.block.type].content !== "inline" ?
								false
							:	(e.updateBlock(t.block, {
									type: "numberedListItem",
									props: {},
								}),
								true);
					},
				},
				prosemirrorPlugins: [pn$1()],
			}),
		],
	),
	hn$1 = () => ({
		type: "toggleListItem",
		propSchema: {
			...m$1,
		},
		content: "inline",
	}),
	gn$1 = v$1(
		hn$1,
		{
			meta: {
				isolating: false,
			},
			parse(e) {
				var t;
				if (e.tagName === "DETAILS") return C(e);
				if (e.tagName === "LI") {
					const n = e.parentElement;
					if (n && (n.tagName === "UL" || (n.tagName === "DIV" && ((t = n.parentElement) == null ? void 0 : t.tagName) === "UL")) && e.querySelector(":scope > details")) return C(e);
				}
			},
			parseContent: ({ el: e, schema: t }) => {
				const n = e.tagName === "DETAILS" ? e : e.querySelector(":scope > details");
				if (!n) throw new Error("No details found in toggleListItem parseContent");
				return Ae$2(n, t, "toggleListItem");
			},
			runsBefore: ["bulletListItem"],
			render(e, t) {
				const n = document.createElement("p");
				return { ...Ne$1(e, t, n), contentDOM: n };
			},
			toExternalHTML(e) {
				const t = document.createElement("li"),
					n = document.createElement("details");
				n.setAttribute("open", "");
				const o = document.createElement("summary"),
					r = document.createElement("p");
				return (
					o.appendChild(r),
					n.appendChild(o),
					A$2(e.props, t),
					t.appendChild(n),
					{
						dom: t,
						contentDOM: r,
						childrenDOM: n,
					}
				);
			},
		},
		[
			a$1({
				key: "toggle-list-item-shortcuts",
				keyboardShortcuts: {
					"Enter": ({ editor: e }) => q$1(e, "toggleListItem"),
					"Mod-Shift-6": ({ editor: e }) => {
						const t = e.getTextCursorPosition();
						return e.schema.blockSchema[t.block.type].content !== "inline" ?
								false
							:	(e.updateBlock(t.block, {
									type: "toggleListItem",
									props: {},
								}),
								true);
					},
				},
			}),
		],
	),
	bn$1 = () => ({
		type: "paragraph",
		propSchema: m$1,
		content: "inline",
	}),
	Cn$1 = v$1(
		bn$1,
		{
			meta: {
				isolating: false,
			},
			parse: (e) => {
				var t;
				if (e.tagName === "P" && (t = e.textContent) != null && t.trim()) return C(e);
			},
			render: () => {
				const e = document.createElement("p");
				return {
					dom: e,
					contentDOM: e,
				};
			},
			toExternalHTML: (e) => {
				const t = document.createElement("p");
				return (
					A$2(e.props, t),
					{
						dom: t,
						contentDOM: t,
					}
				);
			},
			runsBefore: ["default", "heading"],
		},
		[
			a$1({
				key: "paragraph-shortcuts",
				keyboardShortcuts: {
					"Mod-Alt-0": ({ editor: e }) => {
						const t = e.getTextCursorPosition();
						return e.schema.blockSchema[t.block.type].content !== "inline" ?
								false
							:	(e.updateBlock(t.block, {
									type: "paragraph",
									props: {},
								}),
								true);
					},
				},
			}),
		],
	),
	kn$1 = () => ({
		type: "quote",
		propSchema: {
			backgroundColor: m$1.backgroundColor,
			textColor: m$1.textColor,
		},
		content: "inline",
	}),
	yn$1 = v$1(
		kn$1,
		{
			meta: {
				isolating: false,
			},
			parse(e) {
				if (e.tagName === "BLOCKQUOTE") {
					const { backgroundColor: t, textColor: n } = C(e);
					return { backgroundColor: t, textColor: n };
				}
			},
			render() {
				const e = document.createElement("blockquote");
				return {
					dom: e,
					contentDOM: e,
				};
			},
			toExternalHTML(e) {
				const t = document.createElement("blockquote");
				return (
					A$2(e.props, t),
					{
						dom: t,
						contentDOM: t,
					}
				);
			},
		},
		[
			a$1({
				key: "quote-block-shortcuts",
				keyboardShortcuts: {
					"Mod-Alt-q": ({ editor: e }) => {
						const t = e.getTextCursorPosition();
						return e.schema.blockSchema[t.block.type].content !== "inline" ?
								false
							:	(e.updateBlock(t.block, {
									type: "quote",
									props: {},
								}),
								true);
					},
				},
				inputRules: [
					{
						find: new RegExp("^>\\s$"),
						replace() {
							return {
								type: "quote",
								props: {},
							};
						},
					},
				],
			}),
		],
	),
	vn$1 = 35,
	Oe$2 = 120,
	Lo$2 = 31,
	En$1 = Extension.create({
		name: "BlockNoteTableExtension",
		addProseMirrorPlugins: () => [
			columnResizing({
				cellMinWidth: vn$1,
				defaultCellMinWidth: Oe$2,
				// We set this to null as we implement our own node view in the table
				// block content. This node view is the same as what's used by default,
				// but is wrapped in a `blockContent` HTML element.
				View: null,
			}),
			tableEditing(),
		],
		addKeyboardShortcuts() {
			return {
				// Makes enter create a new line within the cell.
				"Enter": () =>
					this.editor.state.selection.empty && this.editor.state.selection.$head.parent.type.name === "tableParagraph" ?
						(this.editor.commands.insertContent({ type: "hardBreak" }), true)
					:	false,
				// Ensures that backspace won't delete the table if the text cursor is at
				// the start of a cell and the selection is empty.
				"Backspace": () => {
					const e = this.editor.state.selection,
						t = e.empty,
						n = e.$head.parentOffset === 0,
						o = e.$head.node().type.name === "tableParagraph";
					return t && n && o;
				},
				// Enables navigating cells using the tab key.
				"Tab": () => this.editor.commands.command(({ state: e, dispatch: t, view: n }) => goToNextCell(1)(e, t, n)),
				"Shift-Tab": () => this.editor.commands.command(({ state: e, dispatch: t, view: n }) => goToNextCell(-1)(e, t, n)),
			};
		},
		extendNodeSchema(e) {
			const t = {
				name: e.name,
				options: e.options,
				storage: e.storage,
			};
			return {
				tableRole: callOrReturn(getExtensionField(e, "tableRole", t)),
			};
		},
	}),
	Sn$1 = {
		textColor: m$1.textColor,
	},
	xn$1 = Node3.create({
		name: "tableHeader",
		addOptions() {
			return {
				HTMLAttributes: {},
			};
		},
		/**
		 * We allow table headers and cells to have multiple tableContent nodes because
		 * when merging cells, prosemirror-tables will concat the contents of the cells naively.
		 * This would cause that content to overflow into other cells when prosemirror tries to enforce the cell structure.
		 *
		 * So, we manually fix this up when reading back in the `nodeToBlock` and only ever place a single tableContent back into the cell.
		 */
		content: "tableContent+",
		addAttributes() {
			return {
				colspan: {
					default: 1,
				},
				rowspan: {
					default: 1,
				},
				colwidth: {
					default: null,
					parseHTML: (e) => {
						const t = e.getAttribute("colwidth");
						return t ? t.split(",").map((o) => parseInt(o, 10)) : null;
					},
				},
			};
		},
		tableRole: "header_cell",
		isolating: true,
		parseHTML() {
			return [
				{
					tag: "th",
					// As `th` elements can contain multiple paragraphs, we need to merge their contents
					// into a single one so that ProseMirror can parse everything correctly.
					getContent: (e, t) => _e$1(e, t),
				},
			];
		},
		renderHTML({ HTMLAttributes: e }) {
			return ["th", mergeAttributes(this.options.HTMLAttributes, e), 0];
		},
	}),
	Mn$1 = Node3.create({
		name: "tableCell",
		addOptions() {
			return {
				HTMLAttributes: {},
			};
		},
		content: "tableContent+",
		addAttributes() {
			return {
				colspan: {
					default: 1,
				},
				rowspan: {
					default: 1,
				},
				colwidth: {
					default: null,
					parseHTML: (e) => {
						const t = e.getAttribute("colwidth");
						return t ? t.split(",").map((o) => parseInt(o, 10)) : null;
					},
				},
			};
		},
		tableRole: "cell",
		isolating: true,
		parseHTML() {
			return [
				{
					tag: "td",
					// As `td` elements can contain multiple paragraphs, we need to merge their contents
					// into a single one so that ProseMirror can parse everything correctly.
					getContent: (e, t) => _e$1(e, t),
				},
			];
		},
		renderHTML({ HTMLAttributes: e }) {
			return ["td", mergeAttributes(this.options.HTMLAttributes, e), 0];
		},
	}),
	wn$1 = Node3.create({
		name: "table",
		content: "tableRow+",
		group: "blockContent",
		tableRole: "table",
		marks: "deletion insertion modification",
		isolating: true,
		parseHTML() {
			return [
				{
					tag: "table",
				},
			];
		},
		renderHTML({ node: e, HTMLAttributes: t }) {
			var r, a, s;
			const n = Ct(
					this.name,
					"table",
					{
						...(((r = this.options.domAttributes) == null ? void 0 : r.blockContent) || {}),
						...t,
					},
					((a = this.options.domAttributes) == null ? void 0 : a.inlineContent) || {},
				),
				o = document.createElement("colgroup");
			for (const i of e.children[0].children)
				if (i.attrs.colwidth)
					for (const l of i.attrs.colwidth) {
						const u = document.createElement("col");
						(l && (u.style = `width: ${l}px`), o.appendChild(u));
					}
				else o.appendChild(document.createElement("col"));
			return ((s = n.dom.firstChild) == null || s.appendChild(o), n);
		},
		// This node view is needed for the `columnResizing` plugin. By default, the
		// plugin adds its own node view, which overrides how the node is rendered vs
		// `renderHTML`. This means that the wrapping `blockContent` HTML element is
		// no longer rendered. The `columnResizing` plugin uses the `TableView` as its
		// default node view. `BlockNoteTableView` extends it by wrapping it in a
		// `blockContent` element, so the DOM structure is consistent with other block
		// types.
		addNodeView() {
			return ({ node: e, HTMLAttributes: t }) => {
				var o;
				class n extends TableView {
					constructor(a, s, i) {
						(super(a, s), (this.node = a), (this.cellMinWidth = s), (this.blockContentHTMLAttributes = i));
						const c = document.createElement("div");
						((c.className = D$2("bn-block-content", i.class)), c.setAttribute("data-content-type", "table"));
						for (const [p, h] of Object.entries(i)) p !== "class" && c.setAttribute(p, h);
						const l = this.dom,
							u = document.createElement("div");
						((u.className = "tableWrapper-inner"), u.appendChild(l.firstChild), l.appendChild(u), c.appendChild(l));
						const d = document.createElement("div");
						((d.className = "table-widgets-container"), (d.style.position = "relative"), l.appendChild(d), (this.dom = c));
					}
					ignoreMutation(a) {
						return !a.target.closest(".tableWrapper-inner") || super.ignoreMutation(a);
					}
				}
				return new n(e, Oe$2, {
					...(((o = this.options.domAttributes) == null ? void 0 : o.blockContent) || {}),
					...t,
				});
			};
		},
	}),
	Ln$1 = Node3.create({
		name: "tableParagraph",
		group: "tableContent",
		content: "inline*",
		parseHTML() {
			return [
				{
					tag: "p",
					getAttrs: (e) => {
						if (typeof e == "string" || !e.textContent || !e.closest("[data-content-type]")) return false;
						const t = e.parentElement;
						return (
							t === null ? false
							: t.tagName === "TD" || t.tagName === "TH" ? {}
							: false
						);
					},
					node: "tableParagraph",
				},
			];
		},
		renderHTML({ HTMLAttributes: e }) {
			return ["p", e, 0];
		},
	}),
	Tn$1 = Node3.create({
		name: "tableRow",
		addOptions() {
			return {
				HTMLAttributes: {},
			};
		},
		content: "(tableCell | tableHeader)+",
		tableRole: "row",
		marks: "deletion insertion modification",
		parseHTML() {
			return [{ tag: "tr" }];
		},
		renderHTML({ HTMLAttributes: e }) {
			return ["tr", mergeAttributes(this.options.HTMLAttributes, e), 0];
		},
	});
function _e$1(e, t) {
	const o = DOMParser$1.fromSchema(t).parse(e, {
			topNode: t.nodes.blockGroup.create(),
		}),
		r = [];
	return (
		o.content.descendants((a) => {
			if (a.isInline) return (r.push(a), false);
		}),
		Fragment.fromArray(r)
	);
}
const Bn$1 = () =>
		vt({ node: wn$1, type: "table", content: "table" }, Sn$1, [
			a$1({
				key: "table-extensions",
				tiptapExtensions: [En$1, Ln$1, xn$1, Mn$1, Tn$1],
			}),
			// Extension for keyboard shortcut which deletes the table if it's empty
			// and all cells are selected. Uses a separate extension as it needs
			// priority over keyboard handlers in the `TableExtension`'s
			// `tableEditing` plugin.
			a$1({
				key: "table-keyboard-delete",
				keyboardShortcuts: {
					Backspace: ({ editor: e }) => {
						if (!(e.prosemirrorState.selection instanceof CellSelection)) return false;
						const t = e.getTextCursorPosition().block,
							n = t.content;
						let o = 0;
						for (const a of n.rows)
							for (const s of a.cells) {
								if (("type" in s && s.content.length > 0) || (!("type" in s) && s.length > 0)) return false;
								o++;
							}
						let r = 0;
						return (
							e.prosemirrorState.selection.forEachCell(() => {
								r++;
							}),
							r < o ? false : (
								(e.transact(() => {
									((e.getPrevBlock(t) || e.getNextBlock(t)) && e.setTextCursorPosition(t), e.removeBlocks([t]));
								}),
								true)
							)
						);
					},
				},
			}),
		]),
	be = (e) => {
		const t = e.src || void 0,
			n = e.width || void 0;
		return { url: t, previewWidth: n };
	},
	An$1 =
		'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M2 3.9934C2 3.44476 2.45531 3 2.9918 3H21.0082C21.556 3 22 3.44495 22 3.9934V20.0066C22 20.5552 21.5447 21 21.0082 21H2.9918C2.44405 21 2 20.5551 2 20.0066V3.9934ZM8 5V19H16V5H8ZM4 5V7H6V5H4ZM18 5V7H20V5H18ZM4 9V11H6V9H4ZM18 9V11H20V9H18ZM4 13V15H6V13H4ZM18 13V15H20V13H18ZM4 17V19H6V17H4ZM18 17V19H20V17H18Z"></path></svg>',
	Nn$1 = (e) => ({
		type: "video",
		propSchema: {
			textAlignment: m$1.textAlignment,
			backgroundColor: m$1.backgroundColor,
			name: { default: "" },
			url: { default: "" },
			caption: { default: "" },
			showPreview: { default: true },
			previewWidth: { default: void 0, type: "number" },
		},
		content: "none",
	}),
	Pn$1 = (e) => (t) => {
		if (t.tagName === "VIDEO") {
			if (t.closest("figure")) return;
			const { backgroundColor: n } = C(t);
			return {
				...be(t),
				backgroundColor: n,
			};
		}
		if (t.tagName === "FIGURE") {
			const n = $$2(t, "video");
			if (!n) return;
			const { targetElement: o, caption: r } = n,
				{ backgroundColor: a } = C(t);
			return {
				...be(o),
				backgroundColor: a,
				caption: r,
			};
		}
	},
	In$1 = v$1(Nn$1, (e) => ({
		meta: {
			fileBlockAccept: ["video/*"],
		},
		parse: Pn$1(),
		render(t, n) {
			const o = document.createElement("div");
			o.innerHTML = e.icon ?? An$1;
			const r = document.createElement("div");
			r.className = "bn-visual-media-wrapper";
			const a = document.createElement("video");
			return (
				(a.className = "bn-visual-media"),
				n.resolveFileUrl ?
					n.resolveFileUrl(t.props.url).then((s) => {
						a.src = s;
					})
				:	(a.src = t.props.url),
				(a.controls = true),
				(a.contentEditable = "false"),
				(a.draggable = false),
				(a.width = t.props.previewWidth),
				r.appendChild(a),
				Ie$2(t, n, { dom: r }, r, o.firstElementChild)
			);
		},
		toExternalHTML(t) {
			if (!t.props.url) {
				const o = document.createElement("p");
				return (
					(o.textContent = "Add video"),
					{
						dom: o,
					}
				);
			}
			let n;
			return (
				t.props.showPreview ?
					((n = document.createElement("video")), (n.src = t.props.url), t.props.previewWidth && (n.width = t.props.previewWidth))
				:	((n = document.createElement("a")), (n.href = t.props.url), (n.textContent = t.props.name || t.props.url)),
				t.props.caption ?
					t.props.showPreview ?
						Y(n, t.props.caption)
					:	U(n, t.props.caption)
				:	{
						dom: n,
					}
			);
		},
		runsBefore: ["file"],
	}));
function b$2(e, t, n) {
	if (!(t in e.schema.blockSpecs)) return false;
	if (!n) return true;
	for (const [o, r] of Object.entries(n)) {
		if (!(o in e.schema.blockSpecs[t].config.propSchema)) return false;
		if (typeof r == "string") {
			if (
				(e.schema.blockSpecs[t].config.propSchema[o].default !== void 0 && typeof e.schema.blockSpecs[t].config.propSchema[o].default !== r) ||
				(e.schema.blockSpecs[t].config.propSchema[o].type !== void 0 && e.schema.blockSpecs[t].config.propSchema[o].type !== r)
			)
				return false;
		} else {
			if (
				e.schema.blockSpecs[t].config.propSchema[o].default !== r.default ||
				(e.schema.blockSpecs[t].config.propSchema[o].default === void 0 && r.default === void 0 && e.schema.blockSpecs[t].config.propSchema[o].type !== r.type) ||
				typeof e.schema.blockSpecs[t].config.propSchema[o].values != typeof r.values
			)
				return false;
			if (typeof e.schema.blockSpecs[t].config.propSchema[o].values == "object" && typeof r.values == "object") {
				for (const a of r.values) if (!e.schema.blockSpecs[t].config.propSchema[o].values.includes(a)) return false;
			}
		}
	}
	return true;
}
function To$1(e, t, n, o) {
	return b$2(t, n, o) && e.type === n;
}
function Bo$1(e) {
	return e instanceof CellSelection;
}
const R = /* @__PURE__ */ new Map();
function Hn$1(e) {
	if (R.has(e)) return R.get(e);
	const t = new Mapping();
	return (
		e._tiptapEditor.on("transaction", ({ transaction: n }) => {
			t.appendMapping(n.mapping);
		}),
		e._tiptapEditor.on("destroy", () => {
			R.delete(e);
		}),
		R.set(e, t),
		t
	);
}
function Dn$1(e, t, n = "left") {
	const o = ySyncPluginKey.getState(e.prosemirrorState);
	if (!o) {
		const a = Hn$1(e),
			s = a.maps.length;
		return () => a.slice(s).map(t, n === "left" ? -1 : 1);
	}
	const r = absolutePositionToRelativePosition(
		// Track the position after the position if we are on the right side
		t + (n === "right" ? 1 : -1),
		o.binding.type,
		o.binding.mapping,
	);
	return () => {
		const a = ySyncPluginKey.getState(e.prosemirrorState),
			s = relativePositionToAbsolutePosition(a.doc, a.binding.type, r, a.binding.mapping);
		if (s === null) throw new Error("Position not found, cannot track positions");
		return s + (n === "right" ? -1 : 1);
	};
}
const On$1 = findParentNode((e) => e.type.name === "blockContainer");
let _n$1 = class _n {
	constructor(t, n, o) {
		w(this, "state");
		w(this, "emitUpdate");
		w(this, "rootEl");
		w(this, "pluginState");
		w(this, "handleScroll", () => {
			var t2, n2;
			if ((t2 = this.state) != null && t2.show) {
				const o2 = (n2 = this.rootEl) == null ? void 0 : n2.querySelector(`[data-decoration-id="${this.pluginState.decorationId}"]`);
				if (!o2) return;
				((this.state.referencePos = o2.getBoundingClientRect().toJSON()), this.emitUpdate(this.pluginState.triggerCharacter));
			}
		});
		w(this, "closeMenu", () => {
			this.editor.transact((t2) => t2.setMeta(B$1, null));
		});
		w(this, "clearQuery", () => {
			this.pluginState !== void 0 &&
				this.editor._tiptapEditor
					.chain()
					.focus()
					.deleteRange({
						from: this.pluginState.queryStartPos() - (this.pluginState.deleteTriggerCharacter ? this.pluginState.triggerCharacter.length : 0),
						to: this.editor.transact((t2) => t2.selection.from),
					})
					.run();
		});
		var r;
		((this.editor = t),
			(this.pluginState = void 0),
			(this.emitUpdate = (a) => {
				var s;
				if (!this.state) throw new Error("Attempting to update uninitialized suggestions menu");
				n(a, {
					...this.state,
					ignoreQueryLength: (s = this.pluginState) == null ? void 0 : s.ignoreQueryLength,
				});
			}),
			(this.rootEl = o.root),
			(r = this.rootEl) == null || r.addEventListener("scroll", this.handleScroll, true));
	}
	update(t, n) {
		var l;
		const o = B$1.getState(n),
			r = B$1.getState(t.state),
			a = o === void 0 && r !== void 0,
			s = o !== void 0 && r === void 0;
		if (!a && !(o !== void 0 && r !== void 0) && !s) return;
		if (((this.pluginState = s ? o : r), s || !this.editor.isEditable)) {
			(this.state && (this.state.show = false), this.emitUpdate(this.pluginState.triggerCharacter));
			return;
		}
		const c = (l = this.rootEl) == null ? void 0 : l.querySelector(`[data-decoration-id="${this.pluginState.decorationId}"]`);
		this.editor.isEditable &&
			c &&
			((this.state = {
				show: true,
				referencePos: c.getBoundingClientRect().toJSON(),
				query: this.pluginState.query,
			}),
			this.emitUpdate(this.pluginState.triggerCharacter));
	}
	destroy() {
		var t;
		(t = this.rootEl) == null || t.removeEventListener("scroll", this.handleScroll, true);
	}
};
const B$1 = new PluginKey("SuggestionMenuPlugin"),
	Vn = a$1(({ editor: e }) => {
		const t = /* @__PURE__ */ new Map();
		let n;
		const o = f$2(void 0);
		return {
			key: "suggestionMenu",
			store: o,
			addSuggestionMenu: (r) => {
				t.set(r.triggerCharacter, r);
			},
			removeSuggestionMenu: (r) => {
				t.delete(r);
			},
			closeMenu: () => {
				n == null || n.closeMenu();
			},
			clearQuery: () => {
				n == null || n.clearQuery();
			},
			shown: () => {
				var r;
				return ((r = n == null ? void 0 : n.state) == null ? void 0 : r.show) || false;
			},
			openSuggestionMenu: (r, a) => {
				e.headless ||
					(e.focus(),
					e.transact((s) => {
						(a != null && a.deleteTriggerCharacter && s.insertText(r),
							s.scrollIntoView().setMeta(B$1, {
								triggerCharacter: r,
								deleteTriggerCharacter: (a == null ? void 0 : a.deleteTriggerCharacter) || false,
								ignoreQueryLength: (a == null ? void 0 : a.ignoreQueryLength) || false,
							}));
					}));
			},
			// TODO this whole plugin needs to be refactored (but I've done the minimal)
			prosemirrorPlugins: [
				new Plugin({
					key: B$1,
					view: (r) => (
						(n = new _n$1(
							e,
							(a, s) => {
								o.setState({ ...s, triggerCharacter: a });
							},
							r,
						)),
						n
					),
					state: {
						// Initialize the plugin's internal state.
						init() {},
						// Apply changes to the plugin state from an editor transaction.
						apply: (r, a, s, i) => {
							if (r.selection.$from.parent.type.spec.code) return a;
							const c = r.getMeta(B$1);
							if (typeof c == "object" && c !== null) {
								a && (n == null || n.closeMenu());
								const u = Dn$1(
									e,
									i.selection.from - // Need to account for the trigger char that was inserted, so we offset the position by the length of the trigger character.
										c.triggerCharacter.length,
								);
								return {
									triggerCharacter: c.triggerCharacter,
									deleteTriggerCharacter: c.deleteTriggerCharacter !== false,
									// When reading the queryStartPos, we offset the result by the length of the trigger character, to make it easy on the caller
									queryStartPos: () => u() + c.triggerCharacter.length,
									query: "",
									decorationId: `id_${Math.floor(Math.random() * 4294967295)}`,
									ignoreQueryLength: c == null ? void 0 : c.ignoreQueryLength,
								};
							}
							if (a === void 0) return a;
							if (
								// Highlighting text should hide the menu.
								i.selection.from !== i.selection.to || // Transactions with plugin metadata should hide the menu.
								c === null || // Certain mouse events should hide the menu.
								// TODO: Change to global mousedown listener.
								r.getMeta("focus") ||
								r.getMeta("blur") ||
								r.getMeta("pointer") || // Moving the caret before the character which triggered the menu should hide it.
								(a.triggerCharacter !== void 0 && i.selection.from < a.queryStartPos()) || // Moving the caret to a new block should hide the menu.
								!i.selection.$from.sameParent(i.doc.resolve(a.queryStartPos()))
							)
								return;
							const l = { ...a };
							return ((l.query = i.doc.textBetween(a.queryStartPos(), i.selection.from)), l);
						},
					},
					props: {
						handleTextInput(r, a, s, i) {
							if (a === s) {
								const c = r.state.doc;
								for (const [l, u] of t) {
									const d = l.length > 1 ? c.textBetween(a - l.length, a) + i : i;
									if (l === d) {
										if (u.shouldOpen && !u.shouldOpen(r.state.tr)) continue;
										return (
											r.dispatch(r.state.tr.insertText(i)),
											r.dispatch(
												r.state.tr
													.setMeta(B$1, {
														triggerCharacter: d,
													})
													.scrollIntoView(),
											),
											true
										);
									}
								}
							}
							return false;
						},
						// Setup decorator on the currently active suggestion.
						decorations(r) {
							const a = this.getState(r);
							if (a === void 0) return null;
							if (!a.deleteTriggerCharacter) {
								const s = On$1(r.selection);
								if (s)
									return DecorationSet.create(r.doc, [
										Decoration.node(s.pos, s.pos + s.node.nodeSize, {
											"nodeName": "span",
											"class": "bn-suggestion-decorator",
											"data-decoration-id": a.decorationId,
										}),
									]);
							}
							return DecorationSet.create(r.doc, [
								Decoration.inline(a.queryStartPos() - a.triggerCharacter.length, a.queryStartPos(), {
									"nodeName": "span",
									"class": "bn-suggestion-decorator",
									"data-decoration-id": a.decorationId,
								}),
							]);
						},
					},
				}),
			],
		};
	});
function Wn(e) {
	let t = e.getTextCursorPosition().block,
		n = e.schema.blockSchema[t.type].content;
	for (; n === "none"; ) {
		if (((t = e.getTextCursorPosition().nextBlock), t === void 0)) return;
		((n = e.schema.blockSchema[t.type].content), e.setTextCursorPosition(t, "end"));
	}
}
function k$3(e, t) {
	const n = e.getTextCursorPosition().block;
	if (n.content === void 0) throw new Error("Slash Menu open in a block that doesn't contain content.");
	let o;
	return (
		Array.isArray(n.content) && ((n.content.length === 1 && B$2(n.content[0]) && n.content[0].type === "text" && n.content[0].text === "/") || n.content.length === 0) ?
			((o = e.updateBlock(n, t)), e.setTextCursorPosition(o))
		:	((o = e.insertBlocks([t], n, "after")[0]), e.setTextCursorPosition(e.getTextCursorPosition().nextBlock)),
		Wn(e),
		o
	);
}
function Ao$2(e) {
	const t = [];
	return (
		b$2(e, "heading", { level: "number" }) &&
			(e.schema.blockSchema.heading.propSchema.level.values || [])
				.filter((n) => n <= 3)
				.forEach((n) => {
					t.push({
						onItemClick: () => {
							k$3(e, {
								type: "heading",
								props: { level: n },
							});
						},
						badge: L$1(`Mod-Alt-${n}`),
						key: n === 1 ? "heading" : `heading_${n}`,
						...e.dictionary.slash_menu[n === 1 ? "heading" : `heading_${n}`],
					});
				}),
		b$2(e, "quote") &&
			t.push({
				onItemClick: () => {
					k$3(e, {
						type: "quote",
					});
				},
				key: "quote",
				...e.dictionary.slash_menu.quote,
			}),
		b$2(e, "toggleListItem") &&
			t.push({
				onItemClick: () => {
					k$3(e, {
						type: "toggleListItem",
					});
				},
				badge: L$1("Mod-Shift-6"),
				key: "toggle_list",
				...e.dictionary.slash_menu.toggle_list,
			}),
		b$2(e, "numberedListItem") &&
			t.push({
				onItemClick: () => {
					k$3(e, {
						type: "numberedListItem",
					});
				},
				badge: L$1("Mod-Shift-7"),
				key: "numbered_list",
				...e.dictionary.slash_menu.numbered_list,
			}),
		b$2(e, "bulletListItem") &&
			t.push({
				onItemClick: () => {
					k$3(e, {
						type: "bulletListItem",
					});
				},
				badge: L$1("Mod-Shift-8"),
				key: "bullet_list",
				...e.dictionary.slash_menu.bullet_list,
			}),
		b$2(e, "checkListItem") &&
			t.push({
				onItemClick: () => {
					k$3(e, {
						type: "checkListItem",
					});
				},
				badge: L$1("Mod-Shift-9"),
				key: "check_list",
				...e.dictionary.slash_menu.check_list,
			}),
		b$2(e, "paragraph") &&
			t.push({
				onItemClick: () => {
					k$3(e, {
						type: "paragraph",
					});
				},
				badge: L$1("Mod-Alt-0"),
				key: "paragraph",
				...e.dictionary.slash_menu.paragraph,
			}),
		b$2(e, "codeBlock") &&
			t.push({
				onItemClick: () => {
					k$3(e, {
						type: "codeBlock",
					});
				},
				badge: L$1("Mod-Alt-c"),
				key: "code_block",
				...e.dictionary.slash_menu.code_block,
			}),
		b$2(e, "divider") &&
			t.push({
				onItemClick: () => {
					k$3(e, { type: "divider" });
				},
				key: "divider",
				...e.dictionary.slash_menu.divider,
			}),
		b$2(e, "table") &&
			t.push({
				onItemClick: () => {
					k$3(e, {
						type: "table",
						content: {
							type: "tableContent",
							rows: [
								{
									cells: ["", "", ""],
								},
								{
									cells: ["", "", ""],
								},
							],
						},
					});
				},
				badge: void 0,
				key: "table",
				...e.dictionary.slash_menu.table,
			}),
		b$2(e, "image", { url: "string" }) &&
			t.push({
				onItemClick: () => {
					var o;
					const n = k$3(e, {
						type: "image",
					});
					(o = e.getExtension(H$1)) == null || o.showMenu(n.id);
				},
				key: "image",
				...e.dictionary.slash_menu.image,
			}),
		b$2(e, "video", { url: "string" }) &&
			t.push({
				onItemClick: () => {
					var o;
					const n = k$3(e, {
						type: "video",
					});
					(o = e.getExtension(H$1)) == null || o.showMenu(n.id);
				},
				key: "video",
				...e.dictionary.slash_menu.video,
			}),
		b$2(e, "audio", { url: "string" }) &&
			t.push({
				onItemClick: () => {
					var o;
					const n = k$3(e, {
						type: "audio",
					});
					(o = e.getExtension(H$1)) == null || o.showMenu(n.id);
				},
				key: "audio",
				...e.dictionary.slash_menu.audio,
			}),
		b$2(e, "file", { url: "string" }) &&
			t.push({
				onItemClick: () => {
					var o;
					const n = k$3(e, {
						type: "file",
					});
					(o = e.getExtension(H$1)) == null || o.showMenu(n.id);
				},
				key: "file",
				...e.dictionary.slash_menu.file,
			}),
		b$2(e, "heading", {
			level: "number",
			isToggleable: "boolean",
		}) &&
			(e.schema.blockSchema.heading.propSchema.level.values || [])
				.filter((n) => n <= 3)
				.forEach((n) => {
					t.push({
						onItemClick: () => {
							k$3(e, {
								type: "heading",
								props: { level: n, isToggleable: true },
							});
						},
						key: n === 1 ? "toggle_heading" : `toggle_heading_${n}`,
						...e.dictionary.slash_menu[n === 1 ? "toggle_heading" : `toggle_heading_${n}`],
					});
				}),
		b$2(e, "heading", { level: "number" }) &&
			(e.schema.blockSchema.heading.propSchema.level.values || [])
				.filter((n) => n > 3)
				.forEach((n) => {
					t.push({
						onItemClick: () => {
							k$3(e, {
								type: "heading",
								props: { level: n },
							});
						},
						badge: L$1(`Mod-Alt-${n}`),
						key: `heading_${n}`,
						...e.dictionary.slash_menu[`heading_${n}`],
					});
				}),
		t.push({
			onItemClick: () => {
				var n;
				(n = e.getExtension(Vn)) == null ||
					n.openSuggestionMenu(":", {
						deleteTriggerCharacter: true,
						ignoreQueryLength: true,
					});
			},
			key: "emoji",
			...e.dictionary.slash_menu.emoji,
		}),
		t
	);
}
function No$2(e, t) {
	return e.filter(({ title: n, aliases: o }) => n.toLowerCase().includes(t.toLowerCase()) || (o && o.filter((r) => r.toLowerCase().includes(t.toLowerCase())).length !== 0));
}
const Po$2 = {
		audio: $t$1(),
		bulletListItem: ln$1(),
		checkListItem: un$1(),
		codeBlock: jt$1(),
		divider: zt$1(),
		file: Kt$1(),
		heading: en$1(),
		image: sn$1(),
		numberedListItem: mn$1(),
		paragraph: Cn$1(),
		quote: yn$1(),
		table: Bn$1(),
		toggleListItem: gn$1(),
		video: In$1(),
	},
	Rn = Te$2(
		{
			type: "textColor",
			propSchema: "string",
		},
		{
			render: () => {
				const e = document.createElement("span");
				return {
					dom: e,
					contentDOM: e,
				};
			},
			toExternalHTML: (e) => {
				const t = document.createElement("span");
				return (
					e !== m$1.textColor.default && (t.style.color = e in T ? T[e].text : e),
					{
						dom: t,
						contentDOM: t,
					}
				);
			},
			parse: (e) => {
				if (e.tagName === "SPAN" && e.style.color) return e.style.color;
			},
		},
	),
	Fn$1 = Te$2(
		{
			type: "backgroundColor",
			propSchema: "string",
		},
		{
			render: () => {
				const e = document.createElement("span");
				return {
					dom: e,
					contentDOM: e,
				};
			},
			toExternalHTML: (e) => {
				const t = document.createElement("span");
				return (
					e !== m$1.backgroundColor.default && (t.style.backgroundColor = e in T ? T[e].background : e),
					{
						dom: t,
						contentDOM: t,
					}
				);
			},
			parse: (e) => {
				if (e.tagName === "SPAN" && e.style.backgroundColor) return e.style.backgroundColor;
			},
		},
	),
	$n$1 = {
		bold: I$1(index_default$4, "boolean"),
		italic: I$1(index_default$3, "boolean"),
		underline: I$1(index_default$2, "boolean"),
		strike: I$1(index_default$1, "boolean"),
		code: I$1(index_default, "boolean"),
		textColor: Rn,
		backgroundColor: Fn$1,
	};
Lt$1($n$1);
const Un = {
		text: { config: "text", implementation: {} },
		link: { config: "link", implementation: {} },
	},
	Ho$2 = Mt$1(Un);

var fastDeepEqual;
var hasRequiredFastDeepEqual;

function requireFastDeepEqual() {
	if (hasRequiredFastDeepEqual) return fastDeepEqual;
	hasRequiredFastDeepEqual = 1;

	// do not edit .js files directly - edit src/index.jst

	fastDeepEqual = function equal(a, b) {
		if (a === b) return true;

		if (a && b && typeof a == "object" && typeof b == "object") {
			if (a.constructor !== b.constructor) return false;

			var length, i, keys;
			if (Array.isArray(a)) {
				length = a.length;
				if (length != b.length) return false;
				for (i = length; i-- !== 0; ) if (!equal(a[i], b[i])) return false;
				return true;
			}

			if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags;
			if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf();
			if (a.toString !== Object.prototype.toString) return a.toString() === b.toString();

			keys = Object.keys(a);
			length = keys.length;
			if (length !== Object.keys(b).length) return false;

			for (i = length; i-- !== 0; ) if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false;

			for (i = length; i-- !== 0; ) {
				var key = keys[i];

				if (!equal(a[key], b[key])) return false;
			}

			return true;
		}

		// true if both NaN, false otherwise
		return a !== a && b !== b;
	};
	return fastDeepEqual;
}

var fastDeepEqualExports = requireFastDeepEqual();
const He = /*@__PURE__*/ getDefaultExportFromCjs(fastDeepEqualExports);

function ok$1() {}

function unreachable() {}

/**
 * @import {Schema as SchemaType, Space} from 'property-information'
 */

/** @type {SchemaType} */
class Schema {
	/**
	 * @param {SchemaType['property']} property
	 *   Property.
	 * @param {SchemaType['normal']} normal
	 *   Normal.
	 * @param {Space | undefined} [space]
	 *   Space.
	 * @returns
	 *   Schema.
	 */
	constructor(property, normal, space) {
		this.normal = normal;
		this.property = property;

		if (space) {
			this.space = space;
		}
	}
}

Schema.prototype.normal = {};
Schema.prototype.property = {};
Schema.prototype.space = undefined;

/**
 * @import {Info, Space} from 'property-information'
 */

/**
 * @param {ReadonlyArray<Schema>} definitions
 *   Definitions.
 * @param {Space | undefined} [space]
 *   Space.
 * @returns {Schema}
 *   Schema.
 */
function merge(definitions, space) {
	/** @type {Record<string, Info>} */
	const property = {};
	/** @type {Record<string, string>} */
	const normal = {};

	for (const definition of definitions) {
		Object.assign(property, definition.property);
		Object.assign(normal, definition.normal);
	}

	return new Schema(property, normal, space);
}

/**
 * Get the cleaned case insensitive form of an attribute or property.
 *
 * @param {string} value
 *   An attribute-like or property-like name.
 * @returns {string}
 *   Value that can be used to look up the properly cased property on a
 *   `Schema`.
 */
function normalize$1(value) {
	return value.toLowerCase();
}

/**
 * @import {Info as InfoType} from 'property-information'
 */

/** @type {InfoType} */
class Info {
	/**
	 * @param {string} property
	 *   Property.
	 * @param {string} attribute
	 *   Attribute.
	 * @returns
	 *   Info.
	 */
	constructor(property, attribute) {
		this.attribute = attribute;
		this.property = property;
	}
}

Info.prototype.attribute = "";
Info.prototype.booleanish = false;
Info.prototype.boolean = false;
Info.prototype.commaOrSpaceSeparated = false;
Info.prototype.commaSeparated = false;
Info.prototype.defined = false;
Info.prototype.mustUseProperty = false;
Info.prototype.number = false;
Info.prototype.overloadedBoolean = false;
Info.prototype.property = "";
Info.prototype.spaceSeparated = false;
Info.prototype.space = undefined;

let powers = 0;

const boolean = increment();
const booleanish = increment();
const overloadedBoolean = increment();
const number = increment();
const spaceSeparated = increment();
const commaSeparated = increment();
const commaOrSpaceSeparated = increment();

function increment() {
	return 2 ** ++powers;
}

const types = /*#__PURE__*/ Object.freeze(
	/*#__PURE__*/ Object.defineProperty(
		{
			__proto__: null,
			boolean,
			booleanish,
			commaOrSpaceSeparated,
			commaSeparated,
			number,
			overloadedBoolean,
			spaceSeparated,
		},
		Symbol.toStringTag,
		{ value: "Module" },
	),
);

/**
 * @import {Space} from 'property-information'
 */

const checks = /** @type {ReadonlyArray<keyof typeof types>} */ (Object.keys(types));

class DefinedInfo extends Info {
	/**
	 * @constructor
	 * @param {string} property
	 *   Property.
	 * @param {string} attribute
	 *   Attribute.
	 * @param {number | null | undefined} [mask]
	 *   Mask.
	 * @param {Space | undefined} [space]
	 *   Space.
	 * @returns
	 *   Info.
	 */
	constructor(property, attribute, mask, space) {
		let index = -1;

		super(property, attribute);

		mark(this, "space", space);

		if (typeof mask === "number") {
			while (++index < checks.length) {
				const check = checks[index];
				mark(this, checks[index], (mask & types[check]) === types[check]);
			}
		}
	}
}

DefinedInfo.prototype.defined = true;

/**
 * @template {keyof DefinedInfo} Key
 *   Key type.
 * @param {DefinedInfo} values
 *   Info.
 * @param {Key} key
 *   Key.
 * @param {DefinedInfo[Key]} value
 *   Value.
 * @returns {undefined}
 *   Nothing.
 */
function mark(values, key, value) {
	if (value) {
		values[key] = value;
	}
}

/**
 * @import {Info, Space} from 'property-information'
 */

/**
 * @param {Definition} definition
 *   Definition.
 * @returns {Schema}
 *   Schema.
 */
function create$3(definition) {
	/** @type {Record<string, Info>} */
	const properties = {};
	/** @type {Record<string, string>} */
	const normals = {};

	for (const [property, value] of Object.entries(definition.properties)) {
		const info = new DefinedInfo(property, definition.transform(definition.attributes || {}, property), value, definition.space);

		if (definition.mustUseProperty && definition.mustUseProperty.includes(property)) {
			info.mustUseProperty = true;
		}

		properties[property] = info;

		normals[normalize$1(property)] = property;
		normals[normalize$1(info.attribute)] = property;
	}

	return new Schema(properties, normals, definition.space);
}

const aria = create$3({
	properties: {
		ariaActiveDescendant: null,
		ariaAtomic: booleanish,
		ariaAutoComplete: null,
		ariaBusy: booleanish,
		ariaChecked: booleanish,
		ariaColCount: number,
		ariaColIndex: number,
		ariaColSpan: number,
		ariaControls: spaceSeparated,
		ariaCurrent: null,
		ariaDescribedBy: spaceSeparated,
		ariaDetails: null,
		ariaDisabled: booleanish,
		ariaDropEffect: spaceSeparated,
		ariaErrorMessage: null,
		ariaExpanded: booleanish,
		ariaFlowTo: spaceSeparated,
		ariaGrabbed: booleanish,
		ariaHasPopup: null,
		ariaHidden: booleanish,
		ariaInvalid: null,
		ariaKeyShortcuts: null,
		ariaLabel: null,
		ariaLabelledBy: spaceSeparated,
		ariaLevel: number,
		ariaLive: null,
		ariaModal: booleanish,
		ariaMultiLine: booleanish,
		ariaMultiSelectable: booleanish,
		ariaOrientation: null,
		ariaOwns: spaceSeparated,
		ariaPlaceholder: null,
		ariaPosInSet: number,
		ariaPressed: booleanish,
		ariaReadOnly: booleanish,
		ariaRelevant: null,
		ariaRequired: booleanish,
		ariaRoleDescription: spaceSeparated,
		ariaRowCount: number,
		ariaRowIndex: number,
		ariaRowSpan: number,
		ariaSelected: booleanish,
		ariaSetSize: number,
		ariaSort: null,
		ariaValueMax: number,
		ariaValueMin: number,
		ariaValueNow: number,
		ariaValueText: null,
		role: null,
	},
	transform(_, property) {
		return property === "role" ? property : "aria-" + property.slice(4).toLowerCase();
	},
});

/**
 * @param {Record<string, string>} attributes
 *   Attributes.
 * @param {string} attribute
 *   Attribute.
 * @returns {string}
 *   Transformed attribute.
 */
function caseSensitiveTransform(attributes, attribute) {
	return attribute in attributes ? attributes[attribute] : attribute;
}

/**
 * @param {Record<string, string>} attributes
 *   Attributes.
 * @param {string} property
 *   Property.
 * @returns {string}
 *   Transformed property.
 */
function caseInsensitiveTransform(attributes, property) {
	return caseSensitiveTransform(attributes, property.toLowerCase());
}

const html$5 = create$3({
	attributes: {
		acceptcharset: "accept-charset",
		classname: "class",
		htmlfor: "for",
		httpequiv: "http-equiv",
	},
	mustUseProperty: ["checked", "multiple", "muted", "selected"],
	properties: {
		// Standard Properties.
		abbr: null,
		accept: commaSeparated,
		acceptCharset: spaceSeparated,
		accessKey: spaceSeparated,
		action: null,
		allow: null,
		allowFullScreen: boolean,
		allowPaymentRequest: boolean,
		allowUserMedia: boolean,
		alpha: boolean,
		alt: null,
		as: null,
		async: boolean,
		autoCapitalize: null,
		autoComplete: spaceSeparated,
		autoFocus: boolean,
		autoPlay: boolean,
		blocking: spaceSeparated,
		capture: null,
		charSet: null,
		checked: boolean,
		cite: null,
		className: spaceSeparated,
		closedBy: null,
		colorSpace: null,
		cols: number,
		colSpan: number,
		command: null,
		commandFor: null,
		content: null,
		contentEditable: booleanish,
		controls: boolean,
		controlsList: spaceSeparated,
		coords: number | commaSeparated,
		crossOrigin: null,
		data: null,
		dateTime: null,
		decoding: null,
		default: boolean,
		defer: boolean,
		dir: null,
		dirName: null,
		disabled: boolean,
		download: overloadedBoolean,
		draggable: booleanish,
		encType: null,
		enterKeyHint: null,
		fetchPriority: null,
		form: null,
		formAction: null,
		formEncType: null,
		formMethod: null,
		formNoValidate: boolean,
		formTarget: null,
		headers: spaceSeparated,
		height: number,
		hidden: overloadedBoolean,
		high: number,
		href: null,
		hrefLang: null,
		htmlFor: spaceSeparated,
		httpEquiv: spaceSeparated,
		id: null,
		imageSizes: null,
		imageSrcSet: null,
		inert: boolean,
		inputMode: null,
		integrity: null,
		is: null,
		isMap: boolean,
		itemId: null,
		itemProp: spaceSeparated,
		itemRef: spaceSeparated,
		itemScope: boolean,
		itemType: spaceSeparated,
		kind: null,
		label: null,
		lang: null,
		language: null,
		list: null,
		loading: null,
		loop: boolean,
		low: number,
		manifest: null,
		max: null,
		maxLength: number,
		media: null,
		method: null,
		min: null,
		minLength: number,
		multiple: boolean,
		muted: boolean,
		name: null,
		nonce: null,
		noModule: boolean,
		noValidate: boolean,
		onAbort: null,
		onAfterPrint: null,
		onAuxClick: null,
		onBeforeMatch: null,
		onBeforePrint: null,
		onBeforeToggle: null,
		onBeforeUnload: null,
		onBlur: null,
		onCancel: null,
		onCanPlay: null,
		onCanPlayThrough: null,
		onChange: null,
		onClick: null,
		onClose: null,
		onContextLost: null,
		onContextMenu: null,
		onContextRestored: null,
		onCopy: null,
		onCueChange: null,
		onCut: null,
		onDblClick: null,
		onDrag: null,
		onDragEnd: null,
		onDragEnter: null,
		onDragExit: null,
		onDragLeave: null,
		onDragOver: null,
		onDragStart: null,
		onDrop: null,
		onDurationChange: null,
		onEmptied: null,
		onEnded: null,
		onError: null,
		onFocus: null,
		onFormData: null,
		onHashChange: null,
		onInput: null,
		onInvalid: null,
		onKeyDown: null,
		onKeyPress: null,
		onKeyUp: null,
		onLanguageChange: null,
		onLoad: null,
		onLoadedData: null,
		onLoadedMetadata: null,
		onLoadEnd: null,
		onLoadStart: null,
		onMessage: null,
		onMessageError: null,
		onMouseDown: null,
		onMouseEnter: null,
		onMouseLeave: null,
		onMouseMove: null,
		onMouseOut: null,
		onMouseOver: null,
		onMouseUp: null,
		onOffline: null,
		onOnline: null,
		onPageHide: null,
		onPageShow: null,
		onPaste: null,
		onPause: null,
		onPlay: null,
		onPlaying: null,
		onPopState: null,
		onProgress: null,
		onRateChange: null,
		onRejectionHandled: null,
		onReset: null,
		onResize: null,
		onScroll: null,
		onScrollEnd: null,
		onSecurityPolicyViolation: null,
		onSeeked: null,
		onSeeking: null,
		onSelect: null,
		onSlotChange: null,
		onStalled: null,
		onStorage: null,
		onSubmit: null,
		onSuspend: null,
		onTimeUpdate: null,
		onToggle: null,
		onUnhandledRejection: null,
		onUnload: null,
		onVolumeChange: null,
		onWaiting: null,
		onWheel: null,
		open: boolean,
		optimum: number,
		pattern: null,
		ping: spaceSeparated,
		placeholder: null,
		playsInline: boolean,
		popover: null,
		popoverTarget: null,
		popoverTargetAction: null,
		poster: null,
		preload: null,
		readOnly: boolean,
		referrerPolicy: null,
		rel: spaceSeparated,
		required: boolean,
		reversed: boolean,
		rows: number,
		rowSpan: number,
		sandbox: spaceSeparated,
		scope: null,
		scoped: boolean,
		seamless: boolean,
		selected: boolean,
		shadowRootClonable: boolean,
		shadowRootCustomElementRegistry: boolean,
		shadowRootDelegatesFocus: boolean,
		shadowRootMode: null,
		shadowRootSerializable: boolean,
		shape: null,
		size: number,
		sizes: null,
		slot: null,
		span: number,
		spellCheck: booleanish,
		src: null,
		srcDoc: null,
		srcLang: null,
		srcSet: null,
		start: number,
		step: null,
		style: null,
		tabIndex: number,
		target: null,
		title: null,
		translate: null,
		type: null,
		typeMustMatch: boolean,
		useMap: null,
		value: booleanish,
		width: number,
		wrap: null,
		writingSuggestions: null,

		// Legacy.
		// See: https://html.spec.whatwg.org/#other-elements,-attributes-and-apis
		align: null, // Several. Use CSS `text-align` instead,
		aLink: null, // `<body>`. Use CSS `a:active {color}` instead
		archive: spaceSeparated, // `<object>`. List of URIs to archives
		axis: null, // `<td>` and `<th>`. Use `scope` on `<th>`
		background: null, // `<body>`. Use CSS `background-image` instead
		bgColor: null, // `<body>` and table elements. Use CSS `background-color` instead
		border: number, // `<table>`. Use CSS `border-width` instead,
		borderColor: null, // `<table>`. Use CSS `border-color` instead,
		bottomMargin: number, // `<body>`
		cellPadding: null, // `<table>`
		cellSpacing: null, // `<table>`
		char: null, // Several table elements. When `align=char`, sets the character to align on
		charOff: null, // Several table elements. When `char`, offsets the alignment
		classId: null, // `<object>`
		clear: null, // `<br>`. Use CSS `clear` instead
		code: null, // `<object>`
		codeBase: null, // `<object>`
		codeType: null, // `<object>`
		color: null, // `<font>` and `<hr>`. Use CSS instead
		compact: boolean, // Lists. Use CSS to reduce space between items instead
		declare: boolean, // `<object>`
		event: null, // `<script>`
		face: null, // `<font>`. Use CSS instead
		frame: null, // `<table>`
		frameBorder: null, // `<iframe>`. Use CSS `border` instead
		hSpace: number, // `<img>` and `<object>`
		leftMargin: number, // `<body>`
		link: null, // `<body>`. Use CSS `a:link {color: *}` instead
		longDesc: null, // `<frame>`, `<iframe>`, and `<img>`. Use an `<a>`
		lowSrc: null, // `<img>`. Use a `<picture>`
		marginHeight: number, // `<body>`
		marginWidth: number, // `<body>`
		noResize: boolean, // `<frame>`
		noHref: boolean, // `<area>`. Use no href instead of an explicit `nohref`
		noShade: boolean, // `<hr>`. Use background-color and height instead of borders
		noWrap: boolean, // `<td>` and `<th>`
		object: null, // `<applet>`
		profile: null, // `<head>`
		prompt: null, // `<isindex>`
		rev: null, // `<link>`
		rightMargin: number, // `<body>`
		rules: null, // `<table>`
		scheme: null, // `<meta>`
		scrolling: booleanish, // `<frame>`. Use overflow in the child context
		standby: null, // `<object>`
		summary: null, // `<table>`
		text: null, // `<body>`. Use CSS `color` instead
		topMargin: number, // `<body>`
		valueType: null, // `<param>`
		version: null, // `<html>`. Use a doctype.
		vAlign: null, // Several. Use CSS `vertical-align` instead
		vLink: null, // `<body>`. Use CSS `a:visited {color}` instead
		vSpace: number, // `<img>` and `<object>`

		// Non-standard Properties.
		allowTransparency: null,
		autoCorrect: null,
		autoSave: null,
		credentialless: boolean,
		disablePictureInPicture: boolean,
		disableRemotePlayback: boolean,
		exportParts: commaSeparated,
		part: spaceSeparated,
		prefix: null,
		property: null,
		results: number,
		security: null,
		unselectable: null,
	},
	space: "html",
	transform: caseInsensitiveTransform,
});

const svg$1 = create$3({
	attributes: {
		accentHeight: "accent-height",
		alignmentBaseline: "alignment-baseline",
		arabicForm: "arabic-form",
		baselineShift: "baseline-shift",
		capHeight: "cap-height",
		className: "class",
		clipPath: "clip-path",
		clipRule: "clip-rule",
		colorInterpolation: "color-interpolation",
		colorInterpolationFilters: "color-interpolation-filters",
		colorProfile: "color-profile",
		colorRendering: "color-rendering",
		crossOrigin: "crossorigin",
		dataType: "datatype",
		dominantBaseline: "dominant-baseline",
		enableBackground: "enable-background",
		fillOpacity: "fill-opacity",
		fillRule: "fill-rule",
		floodColor: "flood-color",
		floodOpacity: "flood-opacity",
		fontFamily: "font-family",
		fontSize: "font-size",
		fontSizeAdjust: "font-size-adjust",
		fontStretch: "font-stretch",
		fontStyle: "font-style",
		fontVariant: "font-variant",
		fontWeight: "font-weight",
		glyphName: "glyph-name",
		glyphOrientationHorizontal: "glyph-orientation-horizontal",
		glyphOrientationVertical: "glyph-orientation-vertical",
		hrefLang: "hreflang",
		horizAdvX: "horiz-adv-x",
		horizOriginX: "horiz-origin-x",
		horizOriginY: "horiz-origin-y",
		imageRendering: "image-rendering",
		letterSpacing: "letter-spacing",
		lightingColor: "lighting-color",
		markerEnd: "marker-end",
		markerMid: "marker-mid",
		markerStart: "marker-start",
		maskType: "mask-type",
		navDown: "nav-down",
		navDownLeft: "nav-down-left",
		navDownRight: "nav-down-right",
		navLeft: "nav-left",
		navNext: "nav-next",
		navPrev: "nav-prev",
		navRight: "nav-right",
		navUp: "nav-up",
		navUpLeft: "nav-up-left",
		navUpRight: "nav-up-right",
		onAbort: "onabort",
		onActivate: "onactivate",
		onAfterPrint: "onafterprint",
		onBeforePrint: "onbeforeprint",
		onBegin: "onbegin",
		onCancel: "oncancel",
		onCanPlay: "oncanplay",
		onCanPlayThrough: "oncanplaythrough",
		onChange: "onchange",
		onClick: "onclick",
		onClose: "onclose",
		onCopy: "oncopy",
		onCueChange: "oncuechange",
		onCut: "oncut",
		onDblClick: "ondblclick",
		onDrag: "ondrag",
		onDragEnd: "ondragend",
		onDragEnter: "ondragenter",
		onDragExit: "ondragexit",
		onDragLeave: "ondragleave",
		onDragOver: "ondragover",
		onDragStart: "ondragstart",
		onDrop: "ondrop",
		onDurationChange: "ondurationchange",
		onEmptied: "onemptied",
		onEnd: "onend",
		onEnded: "onended",
		onError: "onerror",
		onFocus: "onfocus",
		onFocusIn: "onfocusin",
		onFocusOut: "onfocusout",
		onHashChange: "onhashchange",
		onInput: "oninput",
		onInvalid: "oninvalid",
		onKeyDown: "onkeydown",
		onKeyPress: "onkeypress",
		onKeyUp: "onkeyup",
		onLoad: "onload",
		onLoadedData: "onloadeddata",
		onLoadedMetadata: "onloadedmetadata",
		onLoadStart: "onloadstart",
		onMessage: "onmessage",
		onMouseDown: "onmousedown",
		onMouseEnter: "onmouseenter",
		onMouseLeave: "onmouseleave",
		onMouseMove: "onmousemove",
		onMouseOut: "onmouseout",
		onMouseOver: "onmouseover",
		onMouseUp: "onmouseup",
		onMouseWheel: "onmousewheel",
		onOffline: "onoffline",
		onOnline: "ononline",
		onPageHide: "onpagehide",
		onPageShow: "onpageshow",
		onPaste: "onpaste",
		onPause: "onpause",
		onPlay: "onplay",
		onPlaying: "onplaying",
		onPopState: "onpopstate",
		onProgress: "onprogress",
		onRateChange: "onratechange",
		onRepeat: "onrepeat",
		onReset: "onreset",
		onResize: "onresize",
		onScroll: "onscroll",
		onSeeked: "onseeked",
		onSeeking: "onseeking",
		onSelect: "onselect",
		onShow: "onshow",
		onStalled: "onstalled",
		onStorage: "onstorage",
		onSubmit: "onsubmit",
		onSuspend: "onsuspend",
		onTimeUpdate: "ontimeupdate",
		onToggle: "ontoggle",
		onUnload: "onunload",
		onVolumeChange: "onvolumechange",
		onWaiting: "onwaiting",
		onZoom: "onzoom",
		overlinePosition: "overline-position",
		overlineThickness: "overline-thickness",
		paintOrder: "paint-order",
		panose1: "panose-1",
		pointerEvents: "pointer-events",
		referrerPolicy: "referrerpolicy",
		renderingIntent: "rendering-intent",
		shapeRendering: "shape-rendering",
		stopColor: "stop-color",
		stopOpacity: "stop-opacity",
		strikethroughPosition: "strikethrough-position",
		strikethroughThickness: "strikethrough-thickness",
		strokeDashArray: "stroke-dasharray",
		strokeDashOffset: "stroke-dashoffset",
		strokeLineCap: "stroke-linecap",
		strokeLineJoin: "stroke-linejoin",
		strokeMiterLimit: "stroke-miterlimit",
		strokeOpacity: "stroke-opacity",
		strokeWidth: "stroke-width",
		tabIndex: "tabindex",
		textAnchor: "text-anchor",
		textDecoration: "text-decoration",
		textRendering: "text-rendering",
		transformOrigin: "transform-origin",
		typeOf: "typeof",
		underlinePosition: "underline-position",
		underlineThickness: "underline-thickness",
		unicodeBidi: "unicode-bidi",
		unicodeRange: "unicode-range",
		unitsPerEm: "units-per-em",
		vAlphabetic: "v-alphabetic",
		vHanging: "v-hanging",
		vIdeographic: "v-ideographic",
		vMathematical: "v-mathematical",
		vectorEffect: "vector-effect",
		vertAdvY: "vert-adv-y",
		vertOriginX: "vert-origin-x",
		vertOriginY: "vert-origin-y",
		wordSpacing: "word-spacing",
		writingMode: "writing-mode",
		xHeight: "x-height",
		// These were camelcased in Tiny. Now lowercased in SVG 2
		playbackOrder: "playbackorder",
		timelineBegin: "timelinebegin",
	},
	properties: {
		about: commaOrSpaceSeparated,
		accentHeight: number,
		accumulate: null,
		additive: null,
		alignmentBaseline: null,
		alphabetic: number,
		amplitude: number,
		arabicForm: null,
		ascent: number,
		attributeName: null,
		attributeType: null,
		azimuth: number,
		bandwidth: null,
		baselineShift: null,
		baseFrequency: null,
		baseProfile: null,
		bbox: null,
		begin: null,
		bias: number,
		by: null,
		calcMode: null,
		capHeight: number,
		className: spaceSeparated,
		clip: null,
		clipPath: null,
		clipPathUnits: null,
		clipRule: null,
		color: null,
		colorInterpolation: null,
		colorInterpolationFilters: null,
		colorProfile: null,
		colorRendering: null,
		content: null,
		contentScriptType: null,
		contentStyleType: null,
		crossOrigin: null,
		cursor: null,
		cx: null,
		cy: null,
		d: null,
		dataType: null,
		defaultAction: null,
		descent: number,
		diffuseConstant: number,
		direction: null,
		display: null,
		dur: null,
		divisor: number,
		dominantBaseline: null,
		download: boolean,
		dx: null,
		dy: null,
		edgeMode: null,
		editable: null,
		elevation: number,
		enableBackground: null,
		end: null,
		event: null,
		exponent: number,
		externalResourcesRequired: null,
		fill: null,
		fillOpacity: number,
		fillRule: null,
		filter: null,
		filterRes: null,
		filterUnits: null,
		floodColor: null,
		floodOpacity: null,
		focusable: null,
		focusHighlight: null,
		fontFamily: null,
		fontSize: null,
		fontSizeAdjust: null,
		fontStretch: null,
		fontStyle: null,
		fontVariant: null,
		fontWeight: null,
		format: null,
		fr: null,
		from: null,
		fx: null,
		fy: null,
		g1: commaSeparated,
		g2: commaSeparated,
		glyphName: commaSeparated,
		glyphOrientationHorizontal: null,
		glyphOrientationVertical: null,
		glyphRef: null,
		gradientTransform: null,
		gradientUnits: null,
		handler: null,
		hanging: number,
		hatchContentUnits: null,
		hatchUnits: null,
		height: null,
		href: null,
		hrefLang: null,
		horizAdvX: number,
		horizOriginX: number,
		horizOriginY: number,
		id: null,
		ideographic: number,
		imageRendering: null,
		initialVisibility: null,
		in: null,
		in2: null,
		intercept: number,
		k: number,
		k1: number,
		k2: number,
		k3: number,
		k4: number,
		kernelMatrix: commaOrSpaceSeparated,
		kernelUnitLength: null,
		keyPoints: null, // SEMI_COLON_SEPARATED
		keySplines: null, // SEMI_COLON_SEPARATED
		keyTimes: null, // SEMI_COLON_SEPARATED
		kerning: null,
		lang: null,
		lengthAdjust: null,
		letterSpacing: null,
		lightingColor: null,
		limitingConeAngle: number,
		local: null,
		markerEnd: null,
		markerMid: null,
		markerStart: null,
		markerHeight: null,
		markerUnits: null,
		markerWidth: null,
		mask: null,
		maskContentUnits: null,
		maskType: null,
		maskUnits: null,
		mathematical: null,
		max: null,
		media: null,
		mediaCharacterEncoding: null,
		mediaContentEncodings: null,
		mediaSize: number,
		mediaTime: null,
		method: null,
		min: null,
		mode: null,
		name: null,
		navDown: null,
		navDownLeft: null,
		navDownRight: null,
		navLeft: null,
		navNext: null,
		navPrev: null,
		navRight: null,
		navUp: null,
		navUpLeft: null,
		navUpRight: null,
		numOctaves: null,
		observer: null,
		offset: null,
		onAbort: null,
		onActivate: null,
		onAfterPrint: null,
		onBeforePrint: null,
		onBegin: null,
		onCancel: null,
		onCanPlay: null,
		onCanPlayThrough: null,
		onChange: null,
		onClick: null,
		onClose: null,
		onCopy: null,
		onCueChange: null,
		onCut: null,
		onDblClick: null,
		onDrag: null,
		onDragEnd: null,
		onDragEnter: null,
		onDragExit: null,
		onDragLeave: null,
		onDragOver: null,
		onDragStart: null,
		onDrop: null,
		onDurationChange: null,
		onEmptied: null,
		onEnd: null,
		onEnded: null,
		onError: null,
		onFocus: null,
		onFocusIn: null,
		onFocusOut: null,
		onHashChange: null,
		onInput: null,
		onInvalid: null,
		onKeyDown: null,
		onKeyPress: null,
		onKeyUp: null,
		onLoad: null,
		onLoadedData: null,
		onLoadedMetadata: null,
		onLoadStart: null,
		onMessage: null,
		onMouseDown: null,
		onMouseEnter: null,
		onMouseLeave: null,
		onMouseMove: null,
		onMouseOut: null,
		onMouseOver: null,
		onMouseUp: null,
		onMouseWheel: null,
		onOffline: null,
		onOnline: null,
		onPageHide: null,
		onPageShow: null,
		onPaste: null,
		onPause: null,
		onPlay: null,
		onPlaying: null,
		onPopState: null,
		onProgress: null,
		onRateChange: null,
		onRepeat: null,
		onReset: null,
		onResize: null,
		onScroll: null,
		onSeeked: null,
		onSeeking: null,
		onSelect: null,
		onShow: null,
		onStalled: null,
		onStorage: null,
		onSubmit: null,
		onSuspend: null,
		onTimeUpdate: null,
		onToggle: null,
		onUnload: null,
		onVolumeChange: null,
		onWaiting: null,
		onZoom: null,
		opacity: null,
		operator: null,
		order: null,
		orient: null,
		orientation: null,
		origin: null,
		overflow: null,
		overlay: null,
		overlinePosition: number,
		overlineThickness: number,
		paintOrder: null,
		panose1: null,
		path: null,
		pathLength: number,
		patternContentUnits: null,
		patternTransform: null,
		patternUnits: null,
		phase: null,
		ping: spaceSeparated,
		pitch: null,
		playbackOrder: null,
		pointerEvents: null,
		points: null,
		pointsAtX: number,
		pointsAtY: number,
		pointsAtZ: number,
		preserveAlpha: null,
		preserveAspectRatio: null,
		primitiveUnits: null,
		propagate: null,
		property: commaOrSpaceSeparated,
		r: null,
		radius: null,
		referrerPolicy: null,
		refX: null,
		refY: null,
		rel: commaOrSpaceSeparated,
		rev: commaOrSpaceSeparated,
		renderingIntent: null,
		repeatCount: null,
		repeatDur: null,
		requiredExtensions: commaOrSpaceSeparated,
		requiredFeatures: commaOrSpaceSeparated,
		requiredFonts: commaOrSpaceSeparated,
		requiredFormats: commaOrSpaceSeparated,
		resource: null,
		restart: null,
		result: null,
		rotate: null,
		rx: null,
		ry: null,
		scale: null,
		seed: null,
		shapeRendering: null,
		side: null,
		slope: null,
		snapshotTime: null,
		specularConstant: number,
		specularExponent: number,
		spreadMethod: null,
		spacing: null,
		startOffset: null,
		stdDeviation: null,
		stemh: null,
		stemv: null,
		stitchTiles: null,
		stopColor: null,
		stopOpacity: null,
		strikethroughPosition: number,
		strikethroughThickness: number,
		string: null,
		stroke: null,
		strokeDashArray: commaOrSpaceSeparated,
		strokeDashOffset: null,
		strokeLineCap: null,
		strokeLineJoin: null,
		strokeMiterLimit: number,
		strokeOpacity: number,
		strokeWidth: null,
		style: null,
		surfaceScale: number,
		syncBehavior: null,
		syncBehaviorDefault: null,
		syncMaster: null,
		syncTolerance: null,
		syncToleranceDefault: null,
		systemLanguage: commaOrSpaceSeparated,
		tabIndex: number,
		tableValues: null,
		target: null,
		targetX: number,
		targetY: number,
		textAnchor: null,
		textDecoration: null,
		textRendering: null,
		textLength: null,
		timelineBegin: null,
		title: null,
		transformBehavior: null,
		type: null,
		typeOf: commaOrSpaceSeparated,
		to: null,
		transform: null,
		transformOrigin: null,
		u1: null,
		u2: null,
		underlinePosition: number,
		underlineThickness: number,
		unicode: null,
		unicodeBidi: null,
		unicodeRange: null,
		unitsPerEm: number,
		values: null,
		vAlphabetic: number,
		vMathematical: number,
		vectorEffect: null,
		vHanging: number,
		vIdeographic: number,
		version: null,
		vertAdvY: number,
		vertOriginX: number,
		vertOriginY: number,
		viewBox: null,
		viewTarget: null,
		visibility: null,
		width: null,
		widths: null,
		wordSpacing: null,
		writingMode: null,
		x: null,
		x1: null,
		x2: null,
		xChannelSelector: null,
		xHeight: number,
		y: null,
		y1: null,
		y2: null,
		yChannelSelector: null,
		z: null,
		zoomAndPan: null,
	},
	space: "svg",
	transform: caseSensitiveTransform,
});

const xlink = create$3({
	properties: {
		xLinkActuate: null,
		xLinkArcRole: null,
		xLinkHref: null,
		xLinkRole: null,
		xLinkShow: null,
		xLinkTitle: null,
		xLinkType: null,
	},
	space: "xlink",
	transform(_, property) {
		return "xlink:" + property.slice(5).toLowerCase();
	},
});

const xmlns = create$3({
	attributes: { xmlnsxlink: "xmlns:xlink" },
	properties: { xmlnsXLink: null, xmlns: null },
	space: "xmlns",
	transform: caseInsensitiveTransform,
});

const xml = create$3({
	properties: { xmlBase: null, xmlLang: null, xmlSpace: null },
	space: "xml",
	transform(_, property) {
		return "xml:" + property.slice(3).toLowerCase();
	},
});

/**
 * @import {Schema} from 'property-information'
 */

const cap = /[A-Z]/g;
const dash = /-[a-z]/g;
const valid = /^data[-\w.:]+$/i;

/**
 * Look up info on a property.
 *
 * In most cases the given `schema` contains info on the property.
 * All standard,
 * most legacy,
 * and some non-standard properties are supported.
 * For these cases,
 * the returned `Info` has hints about the value of the property.
 *
 * `name` can also be a valid data attribute or property,
 * in which case an `Info` object with the correctly cased `attribute` and
 * `property` is returned.
 *
 * `name` can be an unknown attribute,
 * in which case an `Info` object with `attribute` and `property` set to the
 * given name is returned.
 * It is not recommended to provide unsupported legacy or recently specced
 * properties.
 *
 *
 * @param {Schema} schema
 *   Schema;
 *   either the `html` or `svg` export.
 * @param {string} value
 *   An attribute-like or property-like name;
 *   it will be passed through `normalize` to hopefully find the correct info.
 * @returns {Info}
 *   Info.
 */
function find(schema, value) {
	const normal = normalize$1(value);
	let property = value;
	let Type = Info;

	if (normal in schema.normal) {
		return schema.property[schema.normal[normal]];
	}

	if (normal.length > 4 && normal.slice(0, 4) === "data" && valid.test(value)) {
		// Attribute or property.
		if (value.charAt(4) === "-") {
			// Turn it into a property.
			const rest = value.slice(5).replace(dash, camelcase$1);
			property = "data" + rest.charAt(0).toUpperCase() + rest.slice(1);
		} else {
			// Turn it into an attribute.
			const rest = value.slice(4);

			if (!dash.test(rest)) {
				let dashes = rest.replace(cap, kebab);

				if (dashes.charAt(0) !== "-") {
					dashes = "-" + dashes;
				}

				value = "data" + dashes;
			}
		}

		Type = DefinedInfo;
	}

	return new Type(property, value);
}

/**
 * @param {string} $0
 *   Value.
 * @returns {string}
 *   Kebab.
 */
function kebab($0) {
	return "-" + $0.toLowerCase();
}

/**
 * @param {string} $0
 *   Value.
 * @returns {string}
 *   Camel.
 */
function camelcase$1($0) {
	return $0.charAt(1).toUpperCase();
}

// Note: types exposed from `index.d.ts`.

const html$4 = merge([aria, html$5, xlink, xmlns, xml], "html");

const svg = merge([aria, svg$1, xlink, xmlns, xml], "svg");

/**
 * @typedef Options
 *   Configuration for `stringify`.
 * @property {boolean} [padLeft=true]
 *   Whether to pad a space before a token.
 * @property {boolean} [padRight=false]
 *   Whether to pad a space after a token.
 */

/**
 * @typedef {Options} StringifyOptions
 *   Please use `StringifyOptions` instead.
 */

/**
 * Parse comma-separated tokens to an array.
 *
 * @param {string} value
 *   Comma-separated tokens.
 * @returns {Array<string>}
 *   List of tokens.
 */
function parse$4(value) {
	/** @type {Array<string>} */
	const tokens = [];
	const input = String(value || "");
	let index = input.indexOf(",");
	let start = 0;
	/** @type {boolean} */
	let end = false;

	while (!end) {
		if (index === -1) {
			index = input.length;
			end = true;
		}

		const token = input.slice(start, index).trim();

		if (token || !end) {
			tokens.push(token);
		}

		start = index + 1;
		index = input.indexOf(",", start);
	}

	return tokens;
}

/**
 * Serialize an array of strings or numbers to comma-separated tokens.
 *
 * @param {Array<string|number>} values
 *   List of tokens.
 * @param {Options} [options]
 *   Configuration for `stringify` (optional).
 * @returns {string}
 *   Comma-separated tokens.
 */
function stringify$1(values, options) {
	const settings = options || {};

	// Ensure the last empty entry is seen.
	const input = values[values.length - 1] === "" ? [...values, ""] : values;

	return input.join((settings.padRight ? " " : "") + "," + (settings.padLeft === false ? "" : " ")).trim();
}

/**
 * @typedef {import('hast').Element} Element
 * @typedef {import('hast').Properties} Properties
 */

/**
 * @template {string} SimpleSelector
 *   Selector type.
 * @template {string} DefaultTagName
 *   Default tag name.
 * @typedef {(
 *   SimpleSelector extends ''
 *     ? DefaultTagName
 *     : SimpleSelector extends `${infer TagName}.${infer Rest}`
 *     ? ExtractTagName<TagName, DefaultTagName>
 *     : SimpleSelector extends `${infer TagName}#${infer Rest}`
 *     ? ExtractTagName<TagName, DefaultTagName>
 *     : SimpleSelector extends string
 *     ? SimpleSelector
 *     : DefaultTagName
 * )} ExtractTagName
 *   Extract tag name from a simple selector.
 */

const search$1 = /[#.]/g;

/**
 * Create a hast element from a simple CSS selector.
 *
 * @template {string} Selector
 *   Type of selector.
 * @template {string} [DefaultTagName='div']
 *   Type of default tag name (default: `'div'`).
 * @param {Selector | null | undefined} [selector]
 *   Simple CSS selector (optional).
 *
 *   Can contain a tag name (`foo`), classes (`.bar`), and an ID (`#baz`).
 *   Multiple classes are allowed.
 *   Uses the last ID if multiple IDs are found.
 * @param {DefaultTagName | null | undefined} [defaultTagName='div']
 *   Tag name to use if `selector` does not specify one (default: `'div'`).
 * @returns {Element & {tagName: ExtractTagName<Selector, DefaultTagName>}}
 *   Built element.
 */
function parseSelector(selector, defaultTagName) {
	const value = selector || "";
	/** @type {Properties} */
	const props = {};
	let start = 0;
	/** @type {string | undefined} */
	let previous;
	/** @type {string | undefined} */
	let tagName;

	while (start < value.length) {
		search$1.lastIndex = start;
		const match = search$1.exec(value);
		const subvalue = value.slice(start, match ? match.index : value.length);

		if (subvalue) {
			if (!previous) {
				tagName = subvalue;
			} else if (previous === "#") {
				props.id = subvalue;
			} else if (Array.isArray(props.className)) {
				props.className.push(subvalue);
			} else {
				props.className = [subvalue];
			}

			start += subvalue.length;
		}

		if (match) {
			previous = match[0];
			start++;
		}
	}

	return {
		type: "element",
		// @ts-expect-error: tag name is parsed.
		tagName: tagName || defaultTagName || "div",
		properties: props,
		children: [],
	};
}

/**
 * Parse space-separated tokens to an array of strings.
 *
 * @param {string} value
 *   Space-separated tokens.
 * @returns {Array<string>}
 *   List of tokens.
 */
function parse$3(value) {
	const input = String(value || "").trim();
	return input ? input.split(/[ \t\n\r\f]+/g) : [];
}

/**
 * Serialize an array of strings as space separated-tokens.
 *
 * @param {Array<string|number>} values
 *   List of tokens.
 * @returns {string}
 *   Space-separated tokens.
 */
function stringify(values) {
	return values.join(" ").trim();
}

/**
 * @import {Element, Nodes, RootContent, Root} from 'hast'
 * @import {Info, Schema} from 'property-information'
 */

/**
 * @param {Schema} schema
 *   Schema to use.
 * @param {string} defaultTagName
 *   Default tag name.
 * @param {ReadonlyArray<string> | undefined} [caseSensitive]
 *   Case-sensitive tag names (default: `undefined`).
 * @returns
 *   `h`.
 */
function createH(schema, defaultTagName, caseSensitive) {
	const adjust = caseSensitive ? createAdjustMap(caseSensitive) : undefined;

	/**
	 * Hyperscript compatible DSL for creating virtual hast trees.
	 *
	 * @overload
	 * @param {null | undefined} [selector]
	 * @param {...Child} children
	 * @returns {Root}
	 *
	 * @overload
	 * @param {string} selector
	 * @param {Properties} properties
	 * @param {...Child} children
	 * @returns {Element}
	 *
	 * @overload
	 * @param {string} selector
	 * @param {...Child} children
	 * @returns {Element}
	 *
	 * @param {string | null | undefined} [selector]
	 *   Selector.
	 * @param {Child | Properties | null | undefined} [properties]
	 *   Properties (or first child) (default: `undefined`).
	 * @param {...Child} children
	 *   Children.
	 * @returns {Result}
	 *   Result.
	 */
	function h(selector, properties, ...children) {
		/** @type {Result} */
		let node;

		if (selector === null || selector === undefined) {
			node = { type: "root", children: [] };
			// Properties are not supported for roots.
			const child = /** @type {Child} */ (properties);
			children.unshift(child);
		} else {
			node = parseSelector(selector, defaultTagName);
			// Normalize the name.
			const lower = node.tagName.toLowerCase();
			const adjusted = adjust ? adjust.get(lower) : undefined;
			node.tagName = adjusted || lower;

			// Handle properties.
			if (isChild(properties)) {
				children.unshift(properties);
			} else {
				for (const [key, value] of Object.entries(properties)) {
					addProperty(schema, node.properties, key, value);
				}
			}
		}

		// Handle children.
		for (const child of children) {
			addChild(node.children, child);
		}

		if (node.type === "element" && node.tagName === "template") {
			node.content = { type: "root", children: node.children };
			node.children = [];
		}

		return node;
	}

	return h;
}

/**
 * Check if something is properties or a child.
 *
 * @param {Child | Properties} value
 *   Value to check.
 * @returns {value is Child}
 *   Whether `value` is definitely a child.
 */
function isChild(value) {
	// Never properties if not an object.
	if (value === null || typeof value !== "object" || Array.isArray(value)) {
		return true;
	}

	// Never node without `type`; that’s the main discriminator.
	if (typeof value.type !== "string") return false;

	// Slower check: never property value if object or array with
	// non-number/strings.
	const record = /** @type {Record<string, unknown>} */ (value);
	const keys = Object.keys(value);

	for (const key of keys) {
		const value = record[key];

		if (value && typeof value === "object") {
			if (!Array.isArray(value)) return true;

			const list = /** @type {ReadonlyArray<unknown>} */ (value);

			for (const item of list) {
				if (typeof item !== "number" && typeof item !== "string") {
					return true;
				}
			}
		}
	}

	// Also see empty `children` as a node.
	if ("children" in value && Array.isArray(value.children)) {
		return true;
	}

	// Default to properties, someone can always pass an empty object,
	// put `data: {}` in a node,
	// or wrap it in an array.
	return false;
}

/**
 * @param {Schema} schema
 *   Schema.
 * @param {Properties} properties
 *   Properties object.
 * @param {string} key
 *   Property name.
 * @param {PropertyValue | Style} value
 *   Property value.
 * @returns {undefined}
 *   Nothing.
 */
function addProperty(schema, properties, key, value) {
	const info = find(schema, key);
	/** @type {PropertyValue} */
	let result;

	// Ignore nullish and NaN values.
	if (value === null || value === undefined) return;

	if (typeof value === "number") {
		// Ignore NaN.
		if (Number.isNaN(value)) return;

		result = value;
	}
	// Booleans.
	else if (typeof value === "boolean") {
		result = value;
	}
	// Handle list values.
	else if (typeof value === "string") {
		if (info.spaceSeparated) {
			result = parse$3(value);
		} else if (info.commaSeparated) {
			result = parse$4(value);
		} else if (info.commaOrSpaceSeparated) {
			result = parse$3(parse$4(value).join(" "));
		} else {
			result = parsePrimitive(info, info.property, value);
		}
	} else if (Array.isArray(value)) {
		result = [...value];
	} else {
		result = info.property === "style" ? style(value) : String(value);
	}

	if (Array.isArray(result)) {
		/** @type {Array<number | string>} */
		const finalResult = [];

		for (const item of result) {
			// Assume no booleans in array.
			finalResult.push(/** @type {number | string} */ (parsePrimitive(info, info.property, item)));
		}

		result = finalResult;
	}

	// Class names (which can be added both on the `selector` and here).
	if (info.property === "className" && Array.isArray(properties.className)) {
		// Assume no booleans in `className`.
		result = properties.className.concat(/** @type {Array<number | string> | number | string} */ (result));
	}

	properties[info.property] = result;
}

/**
 * @param {Array<RootContent>} nodes
 *   Children.
 * @param {Child} value
 *   Child.
 * @returns {undefined}
 *   Nothing.
 */
function addChild(nodes, value) {
	if (value === null || value === undefined);
	else if (typeof value === "number" || typeof value === "string") {
		nodes.push({ type: "text", value: String(value) });
	} else if (Array.isArray(value)) {
		for (const child of value) {
			addChild(nodes, child);
		}
	} else if (typeof value === "object" && "type" in value) {
		if (value.type === "root") {
			addChild(nodes, value.children);
		} else {
			nodes.push(value);
		}
	} else {
		throw new Error("Expected node, nodes, or string, got `" + value + "`");
	}
}

/**
 * Parse a single primitives.
 *
 * @param {Info} info
 *   Property information.
 * @param {string} name
 *   Property name.
 * @param {PrimitiveValue} value
 *   Property value.
 * @returns {PrimitiveValue}
 *   Property value.
 */
function parsePrimitive(info, name, value) {
	if (typeof value === "string") {
		if (info.number && value && !Number.isNaN(Number(value))) {
			return Number(value);
		}

		if ((info.boolean || info.overloadedBoolean) && (value === "" || normalize$1(value) === normalize$1(name))) {
			return true;
		}
	}

	return value;
}

/**
 * Serialize a `style` object as a string.
 *
 * @param {Style} styles
 *   Style object.
 * @returns {string}
 *   CSS string.
 */
function style(styles) {
	/** @type {Array<string>} */
	const result = [];

	for (const [key, value] of Object.entries(styles)) {
		result.push([key, value].join(": "));
	}

	return result.join("; ");
}

/**
 * Create a map to adjust casing.
 *
 * @param {ReadonlyArray<string>} values
 *   List of properly cased keys.
 * @returns {Map<string, string>}
 *   Map of lowercase keys to uppercase keys.
 */
function createAdjustMap(values) {
	/** @type {Map<string, string>} */
	const result = new Map();

	for (const value of values) {
		result.set(value.toLowerCase(), value);
	}

	return result;
}

/**
 * List of case-sensitive SVG tag names.
 *
 * @type {ReadonlyArray<string>}
 */
const svgCaseSensitiveTagNames = [
	"altGlyph",
	"altGlyphDef",
	"altGlyphItem",
	"animateColor",
	"animateMotion",
	"animateTransform",
	"clipPath",
	"feBlend",
	"feColorMatrix",
	"feComponentTransfer",
	"feComposite",
	"feConvolveMatrix",
	"feDiffuseLighting",
	"feDisplacementMap",
	"feDistantLight",
	"feDropShadow",
	"feFlood",
	"feFuncA",
	"feFuncB",
	"feFuncG",
	"feFuncR",
	"feGaussianBlur",
	"feImage",
	"feMerge",
	"feMergeNode",
	"feMorphology",
	"feOffset",
	"fePointLight",
	"feSpecularLighting",
	"feSpotLight",
	"feTile",
	"feTurbulence",
	"foreignObject",
	"glyphRef",
	"linearGradient",
	"radialGradient",
	"solidColor",
	"textArea",
	"textPath",
];

// Register the JSX namespace on `h`.
/**
 * @typedef {import('./jsx-classic.js').Element} h.JSX.Element
 * @typedef {import('./jsx-classic.js').ElementChildrenAttribute} h.JSX.ElementChildrenAttribute
 * @typedef {import('./jsx-classic.js').IntrinsicAttributes} h.JSX.IntrinsicAttributes
 * @typedef {import('./jsx-classic.js').IntrinsicElements} h.JSX.IntrinsicElements
 */

// Note: this explicit type is needed, otherwise TS creates broken types.
/** @type {ReturnType<createH>} */
const h$2 = createH(html$4, "div");

// Note: this explicit type is needed, otherwise TS creates broken types.
/** @type {ReturnType<createH>} */
const s$1 = createH(svg, "g", svgCaseSensitiveTagNames);

/**
 * @import {VFile, Value} from 'vfile'
 * @import {Location} from 'vfile-location'
 */

/**
 * Create an index of the given document to translate between line/column and
 * offset based positional info.
 *
 * Also implemented in Rust in [`wooorm/markdown-rs`][markdown-rs].
 *
 * [markdown-rs]: https://github.com/wooorm/markdown-rs/blob/main/src/util/location.rs
 *
 * @param {VFile | Value} file
 *   File to index.
 * @returns {Location}
 *   Accessors for index.
 */
function location$1(file) {
	const value = String(file);
	/**
	 * List, where each index is a line number (0-based), and each value is the
	 * byte index *after* where the line ends.
	 *
	 * @type {Array<number>}
	 */
	const indices = [];

	return { toOffset, toPoint };

	/** @type {Location['toPoint']} */
	function toPoint(offset) {
		if (typeof offset === "number" && offset > -1 && offset <= value.length) {
			let index = 0;

			while (true) {
				let end = indices[index];

				if (end === undefined) {
					const eol = next(value, indices[index - 1]);
					end = eol === -1 ? value.length + 1 : eol + 1;
					indices[index] = end;
				}

				if (end > offset) {
					return {
						line: index + 1,
						column: offset - (index > 0 ? indices[index - 1] : 0) + 1,
						offset,
					};
				}

				index++;
			}
		}
	}

	/** @type {Location['toOffset']} */
	function toOffset(point) {
		if (point && typeof point.line === "number" && typeof point.column === "number" && !Number.isNaN(point.line) && !Number.isNaN(point.column)) {
			while (indices.length < point.line) {
				const from = indices[indices.length - 1];
				const eol = next(value, from);
				const end = eol === -1 ? value.length + 1 : eol + 1;
				if (from === end) break;
				indices.push(end);
			}

			const offset = (point.line > 1 ? indices[point.line - 2] : 0) + point.column - 1;
			// The given `column` could not exist on this line.
			if (offset < indices[point.line - 1]) return offset;
		}
	}
}

/**
 * @param {string} value
 * @param {number} from
 */
function next(value, from) {
	const cr = value.indexOf("\r", from);
	const lf = value.indexOf("\n", from);
	if (lf === -1) return cr;
	if (cr === -1 || cr + 1 === lf) return lf;
	return cr < lf ? cr : lf;
}

/**
 * Map of web namespaces.
 *
 * @type {Record<string, string>}
 */
const webNamespaces = {
	html: "http://www.w3.org/1999/xhtml",
	svg: "http://www.w3.org/2000/svg",
};

/**
 * @import {ElementData, Element, Nodes, RootContent, Root} from 'hast'
 * @import {DefaultTreeAdapterMap, Token} from 'parse5'
 * @import {Schema} from 'property-information'
 * @import {Point, Position} from 'unist'
 * @import {VFile} from 'vfile'
 * @import {Options} from 'hast-util-from-parse5'
 */

const own$9 = {}.hasOwnProperty;
/** @type {unknown} */
// type-coverage:ignore-next-line
const proto = Object.prototype;

/**
 * Transform a `parse5` AST to hast.
 *
 * @param {DefaultTreeAdapterMap['node']} tree
 *   `parse5` tree to transform.
 * @param {Options | null | undefined} [options]
 *   Configuration (optional).
 * @returns {Nodes}
 *   hast tree.
 */
function fromParse5(tree, options) {
	const settings = options || {};

	return one$4(
		{
			file: settings.file || undefined,
			location: false,
			schema: settings.space === "svg" ? svg : html$4,
			verbose: settings.verbose || false,
		},
		tree,
	);
}

/**
 * Transform a node.
 *
 * @param {State} state
 *   Info passed around about the current state.
 * @param {DefaultTreeAdapterMap['node']} node
 *   p5 node.
 * @returns {Nodes}
 *   hast node.
 */
function one$4(state, node) {
	/** @type {Nodes} */
	let result;

	switch (node.nodeName) {
		case "#comment": {
			const reference = /** @type {DefaultTreeAdapterMap['commentNode']} */ (node);
			result = { type: "comment", value: reference.data };
			patch$2(state, reference, result);
			return result;
		}

		case "#document":
		case "#document-fragment": {
			const reference = /** @type {DefaultTreeAdapterMap['document'] | DefaultTreeAdapterMap['documentFragment']} */ (node);
			const quirksMode = "mode" in reference ? reference.mode === "quirks" || reference.mode === "limited-quirks" : false;

			result = {
				type: "root",
				children: all$6(state, node.childNodes),
				data: { quirksMode },
			};

			if (state.file && state.location) {
				const document = String(state.file);
				const loc = location$1(document);
				const start = loc.toPoint(0);
				const end = loc.toPoint(document.length);
				result.position = { start, end };
			}

			return result;
		}

		case "#documentType": {
			const reference = /** @type {DefaultTreeAdapterMap['documentType']} */ (node);
			result = { type: "doctype" };
			patch$2(state, reference, result);
			return result;
		}

		case "#text": {
			const reference = /** @type {DefaultTreeAdapterMap['textNode']} */ (node);
			result = { type: "text", value: reference.value };
			patch$2(state, reference, result);
			return result;
		}

		// Element.
		default: {
			const reference = /** @type {DefaultTreeAdapterMap['element']} */ (node);
			result = element$4(state, reference);
			return result;
		}
	}
}

/**
 * Transform children.
 *
 * @param {State} state
 *   Info passed around about the current state.
 * @param {Array<DefaultTreeAdapterMap['node']>} nodes
 *   Nodes.
 * @returns {Array<RootContent>}
 *   hast nodes.
 */
function all$6(state, nodes) {
	let index = -1;
	/** @type {Array<RootContent>} */
	const results = [];

	while (++index < nodes.length) {
		// Assume no roots in `nodes`.
		const result = /** @type {RootContent} */ (one$4(state, nodes[index]));
		results.push(result);
	}

	return results;
}

/**
 * Transform an element.
 *
 * @param {State} state
 *   Info passed around about the current state.
 * @param {DefaultTreeAdapterMap['element']} node
 *   `parse5` node to transform.
 * @returns {Element}
 *   hast node.
 */
function element$4(state, node) {
	const schema = state.schema;

	state.schema = node.namespaceURI === webNamespaces.svg ? svg : html$4;

	// Props.
	let index = -1;
	/** @type {Record<string, string>} */
	const properties = {};

	while (++index < node.attrs.length) {
		const attribute = node.attrs[index];
		const name = (attribute.prefix ? attribute.prefix + ":" : "") + attribute.name;
		if (!own$9.call(proto, name)) {
			properties[name] = attribute.value;
		}
	}

	// Build.
	const x = state.schema.space === "svg" ? s$1 : h$2;
	const result = x(node.tagName, properties, all$6(state, node.childNodes));
	patch$2(state, node, result);

	// Switch content.
	if (result.tagName === "template") {
		const reference = /** @type {DefaultTreeAdapterMap['template']} */ (node);
		const pos = reference.sourceCodeLocation;
		const startTag = pos && pos.startTag && position$2(pos.startTag);
		const endTag = pos && pos.endTag && position$2(pos.endTag);

		// Root in, root out.
		const content = /** @type {Root} */ (one$4(state, reference.content));

		if (startTag && endTag && state.file) {
			content.position = { start: startTag.end, end: endTag.start };
		}

		result.content = content;
	}

	state.schema = schema;

	return result;
}

/**
 * Patch positional info from `from` onto `to`.
 *
 * @param {State} state
 *   Info passed around about the current state.
 * @param {DefaultTreeAdapterMap['node']} from
 *   p5 node.
 * @param {Nodes} to
 *   hast node.
 * @returns {undefined}
 *   Nothing.
 */
function patch$2(state, from, to) {
	if ("sourceCodeLocation" in from && from.sourceCodeLocation && state.file) {
		const position = createLocation(state, to, from.sourceCodeLocation);

		if (position) {
			state.location = true;
			to.position = position;
		}
	}
}

/**
 * Create clean positional information.
 *
 * @param {State} state
 *   Info passed around about the current state.
 * @param {Nodes} node
 *   hast node.
 * @param {Token.ElementLocation} location
 *   p5 location info.
 * @returns {Position | undefined}
 *   Position, or nothing.
 */
function createLocation(state, node, location) {
	const result = position$2(location);

	if (node.type === "element") {
		const tail = node.children[node.children.length - 1];

		// Bug for unclosed with children.
		// See: <https://github.com/inikulin/parse5/issues/109>.
		if (result && !location.endTag && tail && tail.position && tail.position.end) {
			result.end = Object.assign({}, tail.position.end);
		}

		if (state.verbose) {
			/** @type {Record<string, Position | undefined>} */
			const properties = {};
			/** @type {string} */
			let key;

			if (location.attrs) {
				for (key in location.attrs) {
					if (own$9.call(location.attrs, key)) {
						properties[find(state.schema, key).property] = position$2(location.attrs[key]);
					}
				}
			}

			ok$1(location.startTag);
			const opening = position$2(location.startTag);
			const closing = location.endTag ? position$2(location.endTag) : undefined;
			/** @type {ElementData['position']} */
			const data = { opening };
			if (closing) data.closing = closing;
			data.properties = properties;

			node.data = { position: data };
		}
	}

	return result;
}

/**
 * Turn a p5 location into a position.
 *
 * @param {Token.Location} loc
 *   Location.
 * @returns {Position | undefined}
 *   Position or nothing.
 */
function position$2(loc) {
	const start = point$3({
		line: loc.startLine,
		column: loc.startCol,
		offset: loc.startOffset,
	});
	const end = point$3({
		line: loc.endLine,
		column: loc.endCol,
		offset: loc.endOffset,
	});

	// @ts-expect-error: we do use `undefined` for points if one or the other
	// exists.
	return start || end ? { start, end } : undefined;
}

/**
 * Filter out invalid points.
 *
 * @param {Point} point
 *   Point with potentially `undefined` values.
 * @returns {Point | undefined}
 *   Point or nothing.
 */
function point$3(point) {
	return point.line && point.column ? point : undefined;
}

const UNDEFINED_CODE_POINTS = new Set([
	65534, 65535, 131070, 131071, 196606, 196607, 262142, 262143, 327678, 327679, 393214, 393215, 458750, 458751, 524286, 524287, 589822, 589823, 655358, 655359, 720894, 720895, 786430, 786431,
	851966, 851967, 917502, 917503, 983038, 983039, 1048574, 1048575, 1114110, 1114111,
]);
const REPLACEMENT_CHARACTER = "\uFFFD";
var CODE_POINTS;
(function (CODE_POINTS) {
	CODE_POINTS[(CODE_POINTS["EOF"] = -1)] = "EOF";
	CODE_POINTS[(CODE_POINTS["NULL"] = 0)] = "NULL";
	CODE_POINTS[(CODE_POINTS["TABULATION"] = 9)] = "TABULATION";
	CODE_POINTS[(CODE_POINTS["CARRIAGE_RETURN"] = 13)] = "CARRIAGE_RETURN";
	CODE_POINTS[(CODE_POINTS["LINE_FEED"] = 10)] = "LINE_FEED";
	CODE_POINTS[(CODE_POINTS["FORM_FEED"] = 12)] = "FORM_FEED";
	CODE_POINTS[(CODE_POINTS["SPACE"] = 32)] = "SPACE";
	CODE_POINTS[(CODE_POINTS["EXCLAMATION_MARK"] = 33)] = "EXCLAMATION_MARK";
	CODE_POINTS[(CODE_POINTS["QUOTATION_MARK"] = 34)] = "QUOTATION_MARK";
	CODE_POINTS[(CODE_POINTS["AMPERSAND"] = 38)] = "AMPERSAND";
	CODE_POINTS[(CODE_POINTS["APOSTROPHE"] = 39)] = "APOSTROPHE";
	CODE_POINTS[(CODE_POINTS["HYPHEN_MINUS"] = 45)] = "HYPHEN_MINUS";
	CODE_POINTS[(CODE_POINTS["SOLIDUS"] = 47)] = "SOLIDUS";
	CODE_POINTS[(CODE_POINTS["DIGIT_0"] = 48)] = "DIGIT_0";
	CODE_POINTS[(CODE_POINTS["DIGIT_9"] = 57)] = "DIGIT_9";
	CODE_POINTS[(CODE_POINTS["SEMICOLON"] = 59)] = "SEMICOLON";
	CODE_POINTS[(CODE_POINTS["LESS_THAN_SIGN"] = 60)] = "LESS_THAN_SIGN";
	CODE_POINTS[(CODE_POINTS["EQUALS_SIGN"] = 61)] = "EQUALS_SIGN";
	CODE_POINTS[(CODE_POINTS["GREATER_THAN_SIGN"] = 62)] = "GREATER_THAN_SIGN";
	CODE_POINTS[(CODE_POINTS["QUESTION_MARK"] = 63)] = "QUESTION_MARK";
	CODE_POINTS[(CODE_POINTS["LATIN_CAPITAL_A"] = 65)] = "LATIN_CAPITAL_A";
	CODE_POINTS[(CODE_POINTS["LATIN_CAPITAL_Z"] = 90)] = "LATIN_CAPITAL_Z";
	CODE_POINTS[(CODE_POINTS["RIGHT_SQUARE_BRACKET"] = 93)] = "RIGHT_SQUARE_BRACKET";
	CODE_POINTS[(CODE_POINTS["GRAVE_ACCENT"] = 96)] = "GRAVE_ACCENT";
	CODE_POINTS[(CODE_POINTS["LATIN_SMALL_A"] = 97)] = "LATIN_SMALL_A";
	CODE_POINTS[(CODE_POINTS["LATIN_SMALL_Z"] = 122)] = "LATIN_SMALL_Z";
})(CODE_POINTS || (CODE_POINTS = {}));
const SEQUENCES = {
	DASH_DASH: "--",
	CDATA_START: "[CDATA[",
	DOCTYPE: "doctype",
	SCRIPT: "script",
	PUBLIC: "public",
	SYSTEM: "system",
};
//Surrogates
function isSurrogate(cp) {
	return cp >= 55296 && cp <= 57343;
}
function isSurrogatePair(cp) {
	return cp >= 56320 && cp <= 57343;
}
function getSurrogatePairCodePoint(cp1, cp2) {
	return (cp1 - 55296) * 1024 + 9216 + cp2;
}
//NOTE: excluding NULL and ASCII whitespace
function isControlCodePoint(cp) {
	return (cp !== 0x20 && cp !== 0x0a && cp !== 0x0d && cp !== 0x09 && cp !== 0x0c && cp >= 0x01 && cp <= 0x1f) || (cp >= 0x7f && cp <= 0x9f);
}
function isUndefinedCodePoint(cp) {
	return (cp >= 64976 && cp <= 65007) || UNDEFINED_CODE_POINTS.has(cp);
}

var ERR;
(function (ERR) {
	ERR["controlCharacterInInputStream"] = "control-character-in-input-stream";
	ERR["noncharacterInInputStream"] = "noncharacter-in-input-stream";
	ERR["surrogateInInputStream"] = "surrogate-in-input-stream";
	ERR["nonVoidHtmlElementStartTagWithTrailingSolidus"] = "non-void-html-element-start-tag-with-trailing-solidus";
	ERR["endTagWithAttributes"] = "end-tag-with-attributes";
	ERR["endTagWithTrailingSolidus"] = "end-tag-with-trailing-solidus";
	ERR["unexpectedSolidusInTag"] = "unexpected-solidus-in-tag";
	ERR["unexpectedNullCharacter"] = "unexpected-null-character";
	ERR["unexpectedQuestionMarkInsteadOfTagName"] = "unexpected-question-mark-instead-of-tag-name";
	ERR["invalidFirstCharacterOfTagName"] = "invalid-first-character-of-tag-name";
	ERR["unexpectedEqualsSignBeforeAttributeName"] = "unexpected-equals-sign-before-attribute-name";
	ERR["missingEndTagName"] = "missing-end-tag-name";
	ERR["unexpectedCharacterInAttributeName"] = "unexpected-character-in-attribute-name";
	ERR["unknownNamedCharacterReference"] = "unknown-named-character-reference";
	ERR["missingSemicolonAfterCharacterReference"] = "missing-semicolon-after-character-reference";
	ERR["unexpectedCharacterAfterDoctypeSystemIdentifier"] = "unexpected-character-after-doctype-system-identifier";
	ERR["unexpectedCharacterInUnquotedAttributeValue"] = "unexpected-character-in-unquoted-attribute-value";
	ERR["eofBeforeTagName"] = "eof-before-tag-name";
	ERR["eofInTag"] = "eof-in-tag";
	ERR["missingAttributeValue"] = "missing-attribute-value";
	ERR["missingWhitespaceBetweenAttributes"] = "missing-whitespace-between-attributes";
	ERR["missingWhitespaceAfterDoctypePublicKeyword"] = "missing-whitespace-after-doctype-public-keyword";
	ERR["missingWhitespaceBetweenDoctypePublicAndSystemIdentifiers"] = "missing-whitespace-between-doctype-public-and-system-identifiers";
	ERR["missingWhitespaceAfterDoctypeSystemKeyword"] = "missing-whitespace-after-doctype-system-keyword";
	ERR["missingQuoteBeforeDoctypePublicIdentifier"] = "missing-quote-before-doctype-public-identifier";
	ERR["missingQuoteBeforeDoctypeSystemIdentifier"] = "missing-quote-before-doctype-system-identifier";
	ERR["missingDoctypePublicIdentifier"] = "missing-doctype-public-identifier";
	ERR["missingDoctypeSystemIdentifier"] = "missing-doctype-system-identifier";
	ERR["abruptDoctypePublicIdentifier"] = "abrupt-doctype-public-identifier";
	ERR["abruptDoctypeSystemIdentifier"] = "abrupt-doctype-system-identifier";
	ERR["cdataInHtmlContent"] = "cdata-in-html-content";
	ERR["incorrectlyOpenedComment"] = "incorrectly-opened-comment";
	ERR["eofInScriptHtmlCommentLikeText"] = "eof-in-script-html-comment-like-text";
	ERR["eofInDoctype"] = "eof-in-doctype";
	ERR["nestedComment"] = "nested-comment";
	ERR["abruptClosingOfEmptyComment"] = "abrupt-closing-of-empty-comment";
	ERR["eofInComment"] = "eof-in-comment";
	ERR["incorrectlyClosedComment"] = "incorrectly-closed-comment";
	ERR["eofInCdata"] = "eof-in-cdata";
	ERR["absenceOfDigitsInNumericCharacterReference"] = "absence-of-digits-in-numeric-character-reference";
	ERR["nullCharacterReference"] = "null-character-reference";
	ERR["surrogateCharacterReference"] = "surrogate-character-reference";
	ERR["characterReferenceOutsideUnicodeRange"] = "character-reference-outside-unicode-range";
	ERR["controlCharacterReference"] = "control-character-reference";
	ERR["noncharacterCharacterReference"] = "noncharacter-character-reference";
	ERR["missingWhitespaceBeforeDoctypeName"] = "missing-whitespace-before-doctype-name";
	ERR["missingDoctypeName"] = "missing-doctype-name";
	ERR["invalidCharacterSequenceAfterDoctypeName"] = "invalid-character-sequence-after-doctype-name";
	ERR["duplicateAttribute"] = "duplicate-attribute";
	ERR["nonConformingDoctype"] = "non-conforming-doctype";
	ERR["missingDoctype"] = "missing-doctype";
	ERR["misplacedDoctype"] = "misplaced-doctype";
	ERR["endTagWithoutMatchingOpenElement"] = "end-tag-without-matching-open-element";
	ERR["closingOfElementWithOpenChildElements"] = "closing-of-element-with-open-child-elements";
	ERR["disallowedContentInNoscriptInHead"] = "disallowed-content-in-noscript-in-head";
	ERR["openElementsLeftAfterEof"] = "open-elements-left-after-eof";
	ERR["abandonedHeadElementChild"] = "abandoned-head-element-child";
	ERR["misplacedStartTagForHeadElement"] = "misplaced-start-tag-for-head-element";
	ERR["nestedNoscriptInHead"] = "nested-noscript-in-head";
	ERR["eofInElementThatCanContainOnlyText"] = "eof-in-element-that-can-contain-only-text";
})(ERR || (ERR = {}));

//Const
const DEFAULT_BUFFER_WATERLINE = 1 << 16;
//Preprocessor
//NOTE: HTML input preprocessing
//(see: http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#preprocessing-the-input-stream)
class Preprocessor {
	constructor(handler) {
		this.handler = handler;
		this.html = "";
		this.pos = -1;
		// NOTE: Initial `lastGapPos` is -2, to ensure `col` on initialisation is 0
		this.lastGapPos = -2;
		this.gapStack = [];
		this.skipNextNewLine = false;
		this.lastChunkWritten = false;
		this.endOfChunkHit = false;
		this.bufferWaterline = DEFAULT_BUFFER_WATERLINE;
		this.isEol = false;
		this.lineStartPos = 0;
		this.droppedBufferSize = 0;
		this.line = 1;
		//NOTE: avoid reporting errors twice on advance/retreat
		this.lastErrOffset = -1;
	}
	/** The column on the current line. If we just saw a gap (eg. a surrogate pair), return the index before. */
	get col() {
		return this.pos - this.lineStartPos + Number(this.lastGapPos !== this.pos);
	}
	get offset() {
		return this.droppedBufferSize + this.pos;
	}
	getError(code, cpOffset) {
		const { line, col, offset } = this;
		const startCol = col + cpOffset;
		const startOffset = offset + cpOffset;
		return {
			code,
			startLine: line,
			endLine: line,
			startCol,
			endCol: startCol,
			startOffset,
			endOffset: startOffset,
		};
	}
	_err(code) {
		if (this.handler.onParseError && this.lastErrOffset !== this.offset) {
			this.lastErrOffset = this.offset;
			this.handler.onParseError(this.getError(code, 0));
		}
	}
	_addGap() {
		this.gapStack.push(this.lastGapPos);
		this.lastGapPos = this.pos;
	}
	_processSurrogate(cp) {
		//NOTE: try to peek a surrogate pair
		if (this.pos !== this.html.length - 1) {
			const nextCp = this.html.charCodeAt(this.pos + 1);
			if (isSurrogatePair(nextCp)) {
				//NOTE: we have a surrogate pair. Peek pair character and recalculate code point.
				this.pos++;
				//NOTE: add a gap that should be avoided during retreat
				this._addGap();
				return getSurrogatePairCodePoint(cp, nextCp);
			}
		}
		//NOTE: we are at the end of a chunk, therefore we can't infer the surrogate pair yet.
		else if (!this.lastChunkWritten) {
			this.endOfChunkHit = true;
			return CODE_POINTS.EOF;
		}
		//NOTE: isolated surrogate
		this._err(ERR.surrogateInInputStream);
		return cp;
	}
	willDropParsedChunk() {
		return this.pos > this.bufferWaterline;
	}
	dropParsedChunk() {
		if (this.willDropParsedChunk()) {
			this.html = this.html.substring(this.pos);
			this.lineStartPos -= this.pos;
			this.droppedBufferSize += this.pos;
			this.pos = 0;
			this.lastGapPos = -2;
			this.gapStack.length = 0;
		}
	}
	write(chunk, isLastChunk) {
		if (this.html.length > 0) {
			this.html += chunk;
		} else {
			this.html = chunk;
		}
		this.endOfChunkHit = false;
		this.lastChunkWritten = isLastChunk;
	}
	insertHtmlAtCurrentPos(chunk) {
		this.html = this.html.substring(0, this.pos + 1) + chunk + this.html.substring(this.pos + 1);
		this.endOfChunkHit = false;
	}
	startsWith(pattern, caseSensitive) {
		// Check if our buffer has enough characters
		if (this.pos + pattern.length > this.html.length) {
			this.endOfChunkHit = !this.lastChunkWritten;
			return false;
		}
		if (caseSensitive) {
			return this.html.startsWith(pattern, this.pos);
		}
		for (let i = 0; i < pattern.length; i++) {
			const cp = this.html.charCodeAt(this.pos + i) | 0x20;
			if (cp !== pattern.charCodeAt(i)) {
				return false;
			}
		}
		return true;
	}
	peek(offset) {
		const pos = this.pos + offset;
		if (pos >= this.html.length) {
			this.endOfChunkHit = !this.lastChunkWritten;
			return CODE_POINTS.EOF;
		}
		const code = this.html.charCodeAt(pos);
		return code === CODE_POINTS.CARRIAGE_RETURN ? CODE_POINTS.LINE_FEED : code;
	}
	advance() {
		this.pos++;
		//NOTE: LF should be in the last column of the line
		if (this.isEol) {
			this.isEol = false;
			this.line++;
			this.lineStartPos = this.pos;
		}
		if (this.pos >= this.html.length) {
			this.endOfChunkHit = !this.lastChunkWritten;
			return CODE_POINTS.EOF;
		}
		let cp = this.html.charCodeAt(this.pos);
		//NOTE: all U+000D CARRIAGE RETURN (CR) characters must be converted to U+000A LINE FEED (LF) characters
		if (cp === CODE_POINTS.CARRIAGE_RETURN) {
			this.isEol = true;
			this.skipNextNewLine = true;
			return CODE_POINTS.LINE_FEED;
		}
		//NOTE: any U+000A LINE FEED (LF) characters that immediately follow a U+000D CARRIAGE RETURN (CR) character
		//must be ignored.
		if (cp === CODE_POINTS.LINE_FEED) {
			this.isEol = true;
			if (this.skipNextNewLine) {
				// `line` will be bumped again in the recursive call.
				this.line--;
				this.skipNextNewLine = false;
				this._addGap();
				return this.advance();
			}
		}
		this.skipNextNewLine = false;
		if (isSurrogate(cp)) {
			cp = this._processSurrogate(cp);
		}
		//OPTIMIZATION: first check if code point is in the common allowed
		//range (ASCII alphanumeric, whitespaces, big chunk of BMP)
		//before going into detailed performance cost validation.
		const isCommonValidRange = this.handler.onParseError === null || (cp > 0x1f && cp < 0x7f) || cp === CODE_POINTS.LINE_FEED || cp === CODE_POINTS.CARRIAGE_RETURN || (cp > 0x9f && cp < 64976);
		if (!isCommonValidRange) {
			this._checkForProblematicCharacters(cp);
		}
		return cp;
	}
	_checkForProblematicCharacters(cp) {
		if (isControlCodePoint(cp)) {
			this._err(ERR.controlCharacterInInputStream);
		} else if (isUndefinedCodePoint(cp)) {
			this._err(ERR.noncharacterInInputStream);
		}
	}
	retreat(count) {
		this.pos -= count;
		while (this.pos < this.lastGapPos) {
			this.lastGapPos = this.gapStack.pop();
			this.pos--;
		}
		this.isEol = false;
	}
}

var TokenType;
(function (TokenType) {
	TokenType[(TokenType["CHARACTER"] = 0)] = "CHARACTER";
	TokenType[(TokenType["NULL_CHARACTER"] = 1)] = "NULL_CHARACTER";
	TokenType[(TokenType["WHITESPACE_CHARACTER"] = 2)] = "WHITESPACE_CHARACTER";
	TokenType[(TokenType["START_TAG"] = 3)] = "START_TAG";
	TokenType[(TokenType["END_TAG"] = 4)] = "END_TAG";
	TokenType[(TokenType["COMMENT"] = 5)] = "COMMENT";
	TokenType[(TokenType["DOCTYPE"] = 6)] = "DOCTYPE";
	TokenType[(TokenType["EOF"] = 7)] = "EOF";
	TokenType[(TokenType["HIBERNATION"] = 8)] = "HIBERNATION";
})(TokenType || (TokenType = {}));
function getTokenAttr(token, attrName) {
	for (let i = token.attrs.length - 1; i >= 0; i--) {
		if (token.attrs[i].name === attrName) {
			return token.attrs[i].value;
		}
	}
	return null;
}

// Generated using scripts/write-decode-map.ts
const htmlDecodeTree = /* #__PURE__ */ new Uint16Array(
	// prettier-ignore
	/* #__PURE__ */ "\u1d41<\xd5\u0131\u028a\u049d\u057b\u05d0\u0675\u06de\u07a2\u07d6\u080f\u0a4a\u0a91\u0da1\u0e6d\u0f09\u0f26\u10ca\u1228\u12e1\u1415\u149d\u14c3\u14df\u1525\0\0\0\0\0\0\u156b\u16cd\u198d\u1c12\u1ddd\u1f7e\u2060\u21b0\u228d\u23c0\u23fb\u2442\u2824\u2912\u2d08\u2e48\u2fce\u3016\u32ba\u3639\u37ac\u38fe\u3a28\u3a71\u3ae0\u3b2e\u0800EMabcfglmnoprstu\\bfms\x7f\x84\x8b\x90\x95\x98\xa6\xb3\xb9\xc8\xcflig\u803b\xc6\u40c6P\u803b&\u4026cute\u803b\xc1\u40c1reve;\u4102\u0100iyx}rc\u803b\xc2\u40c2;\u4410r;\uc000\ud835\udd04rave\u803b\xc0\u40c0pha;\u4391acr;\u4100d;\u6a53\u0100gp\x9d\xa1on;\u4104f;\uc000\ud835\udd38plyFunction;\u6061ing\u803b\xc5\u40c5\u0100cs\xbe\xc3r;\uc000\ud835\udc9cign;\u6254ilde\u803b\xc3\u40c3ml\u803b\xc4\u40c4\u0400aceforsu\xe5\xfb\xfe\u0117\u011c\u0122\u0127\u012a\u0100cr\xea\xf2kslash;\u6216\u0176\xf6\xf8;\u6ae7ed;\u6306y;\u4411\u0180crt\u0105\u010b\u0114ause;\u6235noullis;\u612ca;\u4392r;\uc000\ud835\udd05pf;\uc000\ud835\udd39eve;\u42d8c\xf2\u0113mpeq;\u624e\u0700HOacdefhilorsu\u014d\u0151\u0156\u0180\u019e\u01a2\u01b5\u01b7\u01ba\u01dc\u0215\u0273\u0278\u027ecy;\u4427PY\u803b\xa9\u40a9\u0180cpy\u015d\u0162\u017aute;\u4106\u0100;i\u0167\u0168\u62d2talDifferentialD;\u6145leys;\u612d\u0200aeio\u0189\u018e\u0194\u0198ron;\u410cdil\u803b\xc7\u40c7rc;\u4108nint;\u6230ot;\u410a\u0100dn\u01a7\u01adilla;\u40b8terDot;\u40b7\xf2\u017fi;\u43a7rcle\u0200DMPT\u01c7\u01cb\u01d1\u01d6ot;\u6299inus;\u6296lus;\u6295imes;\u6297o\u0100cs\u01e2\u01f8kwiseContourIntegral;\u6232eCurly\u0100DQ\u0203\u020foubleQuote;\u601duote;\u6019\u0200lnpu\u021e\u0228\u0247\u0255on\u0100;e\u0225\u0226\u6237;\u6a74\u0180git\u022f\u0236\u023aruent;\u6261nt;\u622fourIntegral;\u622e\u0100fr\u024c\u024e;\u6102oduct;\u6210nterClockwiseContourIntegral;\u6233oss;\u6a2fcr;\uc000\ud835\udc9ep\u0100;C\u0284\u0285\u62d3ap;\u624d\u0580DJSZacefios\u02a0\u02ac\u02b0\u02b4\u02b8\u02cb\u02d7\u02e1\u02e6\u0333\u048d\u0100;o\u0179\u02a5trahd;\u6911cy;\u4402cy;\u4405cy;\u440f\u0180grs\u02bf\u02c4\u02c7ger;\u6021r;\u61a1hv;\u6ae4\u0100ay\u02d0\u02d5ron;\u410e;\u4414l\u0100;t\u02dd\u02de\u6207a;\u4394r;\uc000\ud835\udd07\u0100af\u02eb\u0327\u0100cm\u02f0\u0322ritical\u0200ADGT\u0300\u0306\u0316\u031ccute;\u40b4o\u0174\u030b\u030d;\u42d9bleAcute;\u42ddrave;\u4060ilde;\u42dcond;\u62c4ferentialD;\u6146\u0470\u033d\0\0\0\u0342\u0354\0\u0405f;\uc000\ud835\udd3b\u0180;DE\u0348\u0349\u034d\u40a8ot;\u60dcqual;\u6250ble\u0300CDLRUV\u0363\u0372\u0382\u03cf\u03e2\u03f8ontourIntegra\xec\u0239o\u0274\u0379\0\0\u037b\xbb\u0349nArrow;\u61d3\u0100eo\u0387\u03a4ft\u0180ART\u0390\u0396\u03a1rrow;\u61d0ightArrow;\u61d4e\xe5\u02cang\u0100LR\u03ab\u03c4eft\u0100AR\u03b3\u03b9rrow;\u67f8ightArrow;\u67faightArrow;\u67f9ight\u0100AT\u03d8\u03derrow;\u61d2ee;\u62a8p\u0241\u03e9\0\0\u03efrrow;\u61d1ownArrow;\u61d5erticalBar;\u6225n\u0300ABLRTa\u0412\u042a\u0430\u045e\u047f\u037crrow\u0180;BU\u041d\u041e\u0422\u6193ar;\u6913pArrow;\u61f5reve;\u4311eft\u02d2\u043a\0\u0446\0\u0450ightVector;\u6950eeVector;\u695eector\u0100;B\u0459\u045a\u61bdar;\u6956ight\u01d4\u0467\0\u0471eeVector;\u695fector\u0100;B\u047a\u047b\u61c1ar;\u6957ee\u0100;A\u0486\u0487\u62a4rrow;\u61a7\u0100ct\u0492\u0497r;\uc000\ud835\udc9frok;\u4110\u0800NTacdfglmopqstux\u04bd\u04c0\u04c4\u04cb\u04de\u04e2\u04e7\u04ee\u04f5\u0521\u052f\u0536\u0552\u055d\u0560\u0565G;\u414aH\u803b\xd0\u40d0cute\u803b\xc9\u40c9\u0180aiy\u04d2\u04d7\u04dcron;\u411arc\u803b\xca\u40ca;\u442dot;\u4116r;\uc000\ud835\udd08rave\u803b\xc8\u40c8ement;\u6208\u0100ap\u04fa\u04fecr;\u4112ty\u0253\u0506\0\0\u0512mallSquare;\u65fberySmallSquare;\u65ab\u0100gp\u0526\u052aon;\u4118f;\uc000\ud835\udd3csilon;\u4395u\u0100ai\u053c\u0549l\u0100;T\u0542\u0543\u6a75ilde;\u6242librium;\u61cc\u0100ci\u0557\u055ar;\u6130m;\u6a73a;\u4397ml\u803b\xcb\u40cb\u0100ip\u056a\u056fsts;\u6203onentialE;\u6147\u0280cfios\u0585\u0588\u058d\u05b2\u05ccy;\u4424r;\uc000\ud835\udd09lled\u0253\u0597\0\0\u05a3mallSquare;\u65fcerySmallSquare;\u65aa\u0370\u05ba\0\u05bf\0\0\u05c4f;\uc000\ud835\udd3dAll;\u6200riertrf;\u6131c\xf2\u05cb\u0600JTabcdfgorst\u05e8\u05ec\u05ef\u05fa\u0600\u0612\u0616\u061b\u061d\u0623\u066c\u0672cy;\u4403\u803b>\u403emma\u0100;d\u05f7\u05f8\u4393;\u43dcreve;\u411e\u0180eiy\u0607\u060c\u0610dil;\u4122rc;\u411c;\u4413ot;\u4120r;\uc000\ud835\udd0a;\u62d9pf;\uc000\ud835\udd3eeater\u0300EFGLST\u0635\u0644\u064e\u0656\u065b\u0666qual\u0100;L\u063e\u063f\u6265ess;\u62dbullEqual;\u6267reater;\u6aa2ess;\u6277lantEqual;\u6a7eilde;\u6273cr;\uc000\ud835\udca2;\u626b\u0400Aacfiosu\u0685\u068b\u0696\u069b\u069e\u06aa\u06be\u06caRDcy;\u442a\u0100ct\u0690\u0694ek;\u42c7;\u405eirc;\u4124r;\u610clbertSpace;\u610b\u01f0\u06af\0\u06b2f;\u610dizontalLine;\u6500\u0100ct\u06c3\u06c5\xf2\u06a9rok;\u4126mp\u0144\u06d0\u06d8ownHum\xf0\u012fqual;\u624f\u0700EJOacdfgmnostu\u06fa\u06fe\u0703\u0707\u070e\u071a\u071e\u0721\u0728\u0744\u0778\u078b\u078f\u0795cy;\u4415lig;\u4132cy;\u4401cute\u803b\xcd\u40cd\u0100iy\u0713\u0718rc\u803b\xce\u40ce;\u4418ot;\u4130r;\u6111rave\u803b\xcc\u40cc\u0180;ap\u0720\u072f\u073f\u0100cg\u0734\u0737r;\u412ainaryI;\u6148lie\xf3\u03dd\u01f4\u0749\0\u0762\u0100;e\u074d\u074e\u622c\u0100gr\u0753\u0758ral;\u622bsection;\u62c2isible\u0100CT\u076c\u0772omma;\u6063imes;\u6062\u0180gpt\u077f\u0783\u0788on;\u412ef;\uc000\ud835\udd40a;\u4399cr;\u6110ilde;\u4128\u01eb\u079a\0\u079ecy;\u4406l\u803b\xcf\u40cf\u0280cfosu\u07ac\u07b7\u07bc\u07c2\u07d0\u0100iy\u07b1\u07b5rc;\u4134;\u4419r;\uc000\ud835\udd0dpf;\uc000\ud835\udd41\u01e3\u07c7\0\u07ccr;\uc000\ud835\udca5rcy;\u4408kcy;\u4404\u0380HJacfos\u07e4\u07e8\u07ec\u07f1\u07fd\u0802\u0808cy;\u4425cy;\u440cppa;\u439a\u0100ey\u07f6\u07fbdil;\u4136;\u441ar;\uc000\ud835\udd0epf;\uc000\ud835\udd42cr;\uc000\ud835\udca6\u0580JTaceflmost\u0825\u0829\u082c\u0850\u0863\u09b3\u09b8\u09c7\u09cd\u0a37\u0a47cy;\u4409\u803b<\u403c\u0280cmnpr\u0837\u083c\u0841\u0844\u084dute;\u4139bda;\u439bg;\u67ealacetrf;\u6112r;\u619e\u0180aey\u0857\u085c\u0861ron;\u413ddil;\u413b;\u441b\u0100fs\u0868\u0970t\u0500ACDFRTUVar\u087e\u08a9\u08b1\u08e0\u08e6\u08fc\u092f\u095b\u0390\u096a\u0100nr\u0883\u088fgleBracket;\u67e8row\u0180;BR\u0899\u089a\u089e\u6190ar;\u61e4ightArrow;\u61c6eiling;\u6308o\u01f5\u08b7\0\u08c3bleBracket;\u67e6n\u01d4\u08c8\0\u08d2eeVector;\u6961ector\u0100;B\u08db\u08dc\u61c3ar;\u6959loor;\u630aight\u0100AV\u08ef\u08f5rrow;\u6194ector;\u694e\u0100er\u0901\u0917e\u0180;AV\u0909\u090a\u0910\u62a3rrow;\u61a4ector;\u695aiangle\u0180;BE\u0924\u0925\u0929\u62b2ar;\u69cfqual;\u62b4p\u0180DTV\u0937\u0942\u094cownVector;\u6951eeVector;\u6960ector\u0100;B\u0956\u0957\u61bfar;\u6958ector\u0100;B\u0965\u0966\u61bcar;\u6952ight\xe1\u039cs\u0300EFGLST\u097e\u098b\u0995\u099d\u09a2\u09adqualGreater;\u62daullEqual;\u6266reater;\u6276ess;\u6aa1lantEqual;\u6a7dilde;\u6272r;\uc000\ud835\udd0f\u0100;e\u09bd\u09be\u62d8ftarrow;\u61daidot;\u413f\u0180npw\u09d4\u0a16\u0a1bg\u0200LRlr\u09de\u09f7\u0a02\u0a10eft\u0100AR\u09e6\u09ecrrow;\u67f5ightArrow;\u67f7ightArrow;\u67f6eft\u0100ar\u03b3\u0a0aight\xe1\u03bfight\xe1\u03caf;\uc000\ud835\udd43er\u0100LR\u0a22\u0a2ceftArrow;\u6199ightArrow;\u6198\u0180cht\u0a3e\u0a40\u0a42\xf2\u084c;\u61b0rok;\u4141;\u626a\u0400acefiosu\u0a5a\u0a5d\u0a60\u0a77\u0a7c\u0a85\u0a8b\u0a8ep;\u6905y;\u441c\u0100dl\u0a65\u0a6fiumSpace;\u605flintrf;\u6133r;\uc000\ud835\udd10nusPlus;\u6213pf;\uc000\ud835\udd44c\xf2\u0a76;\u439c\u0480Jacefostu\u0aa3\u0aa7\u0aad\u0ac0\u0b14\u0b19\u0d91\u0d97\u0d9ecy;\u440acute;\u4143\u0180aey\u0ab4\u0ab9\u0aberon;\u4147dil;\u4145;\u441d\u0180gsw\u0ac7\u0af0\u0b0eative\u0180MTV\u0ad3\u0adf\u0ae8ediumSpace;\u600bhi\u0100cn\u0ae6\u0ad8\xeb\u0ad9eryThi\xee\u0ad9ted\u0100GL\u0af8\u0b06reaterGreate\xf2\u0673essLes\xf3\u0a48Line;\u400ar;\uc000\ud835\udd11\u0200Bnpt\u0b22\u0b28\u0b37\u0b3areak;\u6060BreakingSpace;\u40a0f;\u6115\u0680;CDEGHLNPRSTV\u0b55\u0b56\u0b6a\u0b7c\u0ba1\u0beb\u0c04\u0c5e\u0c84\u0ca6\u0cd8\u0d61\u0d85\u6aec\u0100ou\u0b5b\u0b64ngruent;\u6262pCap;\u626doubleVerticalBar;\u6226\u0180lqx\u0b83\u0b8a\u0b9bement;\u6209ual\u0100;T\u0b92\u0b93\u6260ilde;\uc000\u2242\u0338ists;\u6204reater\u0380;EFGLST\u0bb6\u0bb7\u0bbd\u0bc9\u0bd3\u0bd8\u0be5\u626fqual;\u6271ullEqual;\uc000\u2267\u0338reater;\uc000\u226b\u0338ess;\u6279lantEqual;\uc000\u2a7e\u0338ilde;\u6275ump\u0144\u0bf2\u0bfdownHump;\uc000\u224e\u0338qual;\uc000\u224f\u0338e\u0100fs\u0c0a\u0c27tTriangle\u0180;BE\u0c1a\u0c1b\u0c21\u62eaar;\uc000\u29cf\u0338qual;\u62ecs\u0300;EGLST\u0c35\u0c36\u0c3c\u0c44\u0c4b\u0c58\u626equal;\u6270reater;\u6278ess;\uc000\u226a\u0338lantEqual;\uc000\u2a7d\u0338ilde;\u6274ested\u0100GL\u0c68\u0c79reaterGreater;\uc000\u2aa2\u0338essLess;\uc000\u2aa1\u0338recedes\u0180;ES\u0c92\u0c93\u0c9b\u6280qual;\uc000\u2aaf\u0338lantEqual;\u62e0\u0100ei\u0cab\u0cb9verseElement;\u620cghtTriangle\u0180;BE\u0ccb\u0ccc\u0cd2\u62ebar;\uc000\u29d0\u0338qual;\u62ed\u0100qu\u0cdd\u0d0cuareSu\u0100bp\u0ce8\u0cf9set\u0100;E\u0cf0\u0cf3\uc000\u228f\u0338qual;\u62e2erset\u0100;E\u0d03\u0d06\uc000\u2290\u0338qual;\u62e3\u0180bcp\u0d13\u0d24\u0d4eset\u0100;E\u0d1b\u0d1e\uc000\u2282\u20d2qual;\u6288ceeds\u0200;EST\u0d32\u0d33\u0d3b\u0d46\u6281qual;\uc000\u2ab0\u0338lantEqual;\u62e1ilde;\uc000\u227f\u0338erset\u0100;E\u0d58\u0d5b\uc000\u2283\u20d2qual;\u6289ilde\u0200;EFT\u0d6e\u0d6f\u0d75\u0d7f\u6241qual;\u6244ullEqual;\u6247ilde;\u6249erticalBar;\u6224cr;\uc000\ud835\udca9ilde\u803b\xd1\u40d1;\u439d\u0700Eacdfgmoprstuv\u0dbd\u0dc2\u0dc9\u0dd5\u0ddb\u0de0\u0de7\u0dfc\u0e02\u0e20\u0e22\u0e32\u0e3f\u0e44lig;\u4152cute\u803b\xd3\u40d3\u0100iy\u0dce\u0dd3rc\u803b\xd4\u40d4;\u441eblac;\u4150r;\uc000\ud835\udd12rave\u803b\xd2\u40d2\u0180aei\u0dee\u0df2\u0df6cr;\u414cga;\u43a9cron;\u439fpf;\uc000\ud835\udd46enCurly\u0100DQ\u0e0e\u0e1aoubleQuote;\u601cuote;\u6018;\u6a54\u0100cl\u0e27\u0e2cr;\uc000\ud835\udcaaash\u803b\xd8\u40d8i\u016c\u0e37\u0e3cde\u803b\xd5\u40d5es;\u6a37ml\u803b\xd6\u40d6er\u0100BP\u0e4b\u0e60\u0100ar\u0e50\u0e53r;\u603eac\u0100ek\u0e5a\u0e5c;\u63deet;\u63b4arenthesis;\u63dc\u0480acfhilors\u0e7f\u0e87\u0e8a\u0e8f\u0e92\u0e94\u0e9d\u0eb0\u0efcrtialD;\u6202y;\u441fr;\uc000\ud835\udd13i;\u43a6;\u43a0usMinus;\u40b1\u0100ip\u0ea2\u0eadncareplan\xe5\u069df;\u6119\u0200;eio\u0eb9\u0eba\u0ee0\u0ee4\u6abbcedes\u0200;EST\u0ec8\u0ec9\u0ecf\u0eda\u627aqual;\u6aaflantEqual;\u627cilde;\u627eme;\u6033\u0100dp\u0ee9\u0eeeuct;\u620fortion\u0100;a\u0225\u0ef9l;\u621d\u0100ci\u0f01\u0f06r;\uc000\ud835\udcab;\u43a8\u0200Ufos\u0f11\u0f16\u0f1b\u0f1fOT\u803b\"\u4022r;\uc000\ud835\udd14pf;\u611acr;\uc000\ud835\udcac\u0600BEacefhiorsu\u0f3e\u0f43\u0f47\u0f60\u0f73\u0fa7\u0faa\u0fad\u1096\u10a9\u10b4\u10bearr;\u6910G\u803b\xae\u40ae\u0180cnr\u0f4e\u0f53\u0f56ute;\u4154g;\u67ebr\u0100;t\u0f5c\u0f5d\u61a0l;\u6916\u0180aey\u0f67\u0f6c\u0f71ron;\u4158dil;\u4156;\u4420\u0100;v\u0f78\u0f79\u611cerse\u0100EU\u0f82\u0f99\u0100lq\u0f87\u0f8eement;\u620builibrium;\u61cbpEquilibrium;\u696fr\xbb\u0f79o;\u43a1ght\u0400ACDFTUVa\u0fc1\u0feb\u0ff3\u1022\u1028\u105b\u1087\u03d8\u0100nr\u0fc6\u0fd2gleBracket;\u67e9row\u0180;BL\u0fdc\u0fdd\u0fe1\u6192ar;\u61e5eftArrow;\u61c4eiling;\u6309o\u01f5\u0ff9\0\u1005bleBracket;\u67e7n\u01d4\u100a\0\u1014eeVector;\u695dector\u0100;B\u101d\u101e\u61c2ar;\u6955loor;\u630b\u0100er\u102d\u1043e\u0180;AV\u1035\u1036\u103c\u62a2rrow;\u61a6ector;\u695biangle\u0180;BE\u1050\u1051\u1055\u62b3ar;\u69d0qual;\u62b5p\u0180DTV\u1063\u106e\u1078ownVector;\u694feeVector;\u695cector\u0100;B\u1082\u1083\u61bear;\u6954ector\u0100;B\u1091\u1092\u61c0ar;\u6953\u0100pu\u109b\u109ef;\u611dndImplies;\u6970ightarrow;\u61db\u0100ch\u10b9\u10bcr;\u611b;\u61b1leDelayed;\u69f4\u0680HOacfhimoqstu\u10e4\u10f1\u10f7\u10fd\u1119\u111e\u1151\u1156\u1161\u1167\u11b5\u11bb\u11bf\u0100Cc\u10e9\u10eeHcy;\u4429y;\u4428FTcy;\u442ccute;\u415a\u0280;aeiy\u1108\u1109\u110e\u1113\u1117\u6abcron;\u4160dil;\u415erc;\u415c;\u4421r;\uc000\ud835\udd16ort\u0200DLRU\u112a\u1134\u113e\u1149ownArrow\xbb\u041eeftArrow\xbb\u089aightArrow\xbb\u0fddpArrow;\u6191gma;\u43a3allCircle;\u6218pf;\uc000\ud835\udd4a\u0272\u116d\0\0\u1170t;\u621aare\u0200;ISU\u117b\u117c\u1189\u11af\u65a1ntersection;\u6293u\u0100bp\u118f\u119eset\u0100;E\u1197\u1198\u628fqual;\u6291erset\u0100;E\u11a8\u11a9\u6290qual;\u6292nion;\u6294cr;\uc000\ud835\udcaear;\u62c6\u0200bcmp\u11c8\u11db\u1209\u120b\u0100;s\u11cd\u11ce\u62d0et\u0100;E\u11cd\u11d5qual;\u6286\u0100ch\u11e0\u1205eeds\u0200;EST\u11ed\u11ee\u11f4\u11ff\u627bqual;\u6ab0lantEqual;\u627dilde;\u627fTh\xe1\u0f8c;\u6211\u0180;es\u1212\u1213\u1223\u62d1rset\u0100;E\u121c\u121d\u6283qual;\u6287et\xbb\u1213\u0580HRSacfhiors\u123e\u1244\u1249\u1255\u125e\u1271\u1276\u129f\u12c2\u12c8\u12d1ORN\u803b\xde\u40deADE;\u6122\u0100Hc\u124e\u1252cy;\u440by;\u4426\u0100bu\u125a\u125c;\u4009;\u43a4\u0180aey\u1265\u126a\u126fron;\u4164dil;\u4162;\u4422r;\uc000\ud835\udd17\u0100ei\u127b\u1289\u01f2\u1280\0\u1287efore;\u6234a;\u4398\u0100cn\u128e\u1298kSpace;\uc000\u205f\u200aSpace;\u6009lde\u0200;EFT\u12ab\u12ac\u12b2\u12bc\u623cqual;\u6243ullEqual;\u6245ilde;\u6248pf;\uc000\ud835\udd4bipleDot;\u60db\u0100ct\u12d6\u12dbr;\uc000\ud835\udcafrok;\u4166\u0ae1\u12f7\u130e\u131a\u1326\0\u132c\u1331\0\0\0\0\0\u1338\u133d\u1377\u1385\0\u13ff\u1404\u140a\u1410\u0100cr\u12fb\u1301ute\u803b\xda\u40dar\u0100;o\u1307\u1308\u619fcir;\u6949r\u01e3\u1313\0\u1316y;\u440eve;\u416c\u0100iy\u131e\u1323rc\u803b\xdb\u40db;\u4423blac;\u4170r;\uc000\ud835\udd18rave\u803b\xd9\u40d9acr;\u416a\u0100di\u1341\u1369er\u0100BP\u1348\u135d\u0100ar\u134d\u1350r;\u405fac\u0100ek\u1357\u1359;\u63dfet;\u63b5arenthesis;\u63ddon\u0100;P\u1370\u1371\u62c3lus;\u628e\u0100gp\u137b\u137fon;\u4172f;\uc000\ud835\udd4c\u0400ADETadps\u1395\u13ae\u13b8\u13c4\u03e8\u13d2\u13d7\u13f3rrow\u0180;BD\u1150\u13a0\u13a4ar;\u6912ownArrow;\u61c5ownArrow;\u6195quilibrium;\u696eee\u0100;A\u13cb\u13cc\u62a5rrow;\u61a5own\xe1\u03f3er\u0100LR\u13de\u13e8eftArrow;\u6196ightArrow;\u6197i\u0100;l\u13f9\u13fa\u43d2on;\u43a5ing;\u416ecr;\uc000\ud835\udcb0ilde;\u4168ml\u803b\xdc\u40dc\u0480Dbcdefosv\u1427\u142c\u1430\u1433\u143e\u1485\u148a\u1490\u1496ash;\u62abar;\u6aeby;\u4412ash\u0100;l\u143b\u143c\u62a9;\u6ae6\u0100er\u1443\u1445;\u62c1\u0180bty\u144c\u1450\u147aar;\u6016\u0100;i\u144f\u1455cal\u0200BLST\u1461\u1465\u146a\u1474ar;\u6223ine;\u407ceparator;\u6758ilde;\u6240ThinSpace;\u600ar;\uc000\ud835\udd19pf;\uc000\ud835\udd4dcr;\uc000\ud835\udcb1dash;\u62aa\u0280cefos\u14a7\u14ac\u14b1\u14b6\u14bcirc;\u4174dge;\u62c0r;\uc000\ud835\udd1apf;\uc000\ud835\udd4ecr;\uc000\ud835\udcb2\u0200fios\u14cb\u14d0\u14d2\u14d8r;\uc000\ud835\udd1b;\u439epf;\uc000\ud835\udd4fcr;\uc000\ud835\udcb3\u0480AIUacfosu\u14f1\u14f5\u14f9\u14fd\u1504\u150f\u1514\u151a\u1520cy;\u442fcy;\u4407cy;\u442ecute\u803b\xdd\u40dd\u0100iy\u1509\u150drc;\u4176;\u442br;\uc000\ud835\udd1cpf;\uc000\ud835\udd50cr;\uc000\ud835\udcb4ml;\u4178\u0400Hacdefos\u1535\u1539\u153f\u154b\u154f\u155d\u1560\u1564cy;\u4416cute;\u4179\u0100ay\u1544\u1549ron;\u417d;\u4417ot;\u417b\u01f2\u1554\0\u155boWidt\xe8\u0ad9a;\u4396r;\u6128pf;\u6124cr;\uc000\ud835\udcb5\u0be1\u1583\u158a\u1590\0\u15b0\u15b6\u15bf\0\0\0\0\u15c6\u15db\u15eb\u165f\u166d\0\u1695\u169b\u16b2\u16b9\0\u16becute\u803b\xe1\u40e1reve;\u4103\u0300;Ediuy\u159c\u159d\u15a1\u15a3\u15a8\u15ad\u623e;\uc000\u223e\u0333;\u623frc\u803b\xe2\u40e2te\u80bb\xb4\u0306;\u4430lig\u803b\xe6\u40e6\u0100;r\xb2\u15ba;\uc000\ud835\udd1erave\u803b\xe0\u40e0\u0100ep\u15ca\u15d6\u0100fp\u15cf\u15d4sym;\u6135\xe8\u15d3ha;\u43b1\u0100ap\u15dfc\u0100cl\u15e4\u15e7r;\u4101g;\u6a3f\u0264\u15f0\0\0\u160a\u0280;adsv\u15fa\u15fb\u15ff\u1601\u1607\u6227nd;\u6a55;\u6a5clope;\u6a58;\u6a5a\u0380;elmrsz\u1618\u1619\u161b\u161e\u163f\u164f\u1659\u6220;\u69a4e\xbb\u1619sd\u0100;a\u1625\u1626\u6221\u0461\u1630\u1632\u1634\u1636\u1638\u163a\u163c\u163e;\u69a8;\u69a9;\u69aa;\u69ab;\u69ac;\u69ad;\u69ae;\u69aft\u0100;v\u1645\u1646\u621fb\u0100;d\u164c\u164d\u62be;\u699d\u0100pt\u1654\u1657h;\u6222\xbb\xb9arr;\u637c\u0100gp\u1663\u1667on;\u4105f;\uc000\ud835\udd52\u0380;Eaeiop\u12c1\u167b\u167d\u1682\u1684\u1687\u168a;\u6a70cir;\u6a6f;\u624ad;\u624bs;\u4027rox\u0100;e\u12c1\u1692\xf1\u1683ing\u803b\xe5\u40e5\u0180cty\u16a1\u16a6\u16a8r;\uc000\ud835\udcb6;\u402amp\u0100;e\u12c1\u16af\xf1\u0288ilde\u803b\xe3\u40e3ml\u803b\xe4\u40e4\u0100ci\u16c2\u16c8onin\xf4\u0272nt;\u6a11\u0800Nabcdefiklnoprsu\u16ed\u16f1\u1730\u173c\u1743\u1748\u1778\u177d\u17e0\u17e6\u1839\u1850\u170d\u193d\u1948\u1970ot;\u6aed\u0100cr\u16f6\u171ek\u0200ceps\u1700\u1705\u170d\u1713ong;\u624cpsilon;\u43f6rime;\u6035im\u0100;e\u171a\u171b\u623dq;\u62cd\u0176\u1722\u1726ee;\u62bded\u0100;g\u172c\u172d\u6305e\xbb\u172drk\u0100;t\u135c\u1737brk;\u63b6\u0100oy\u1701\u1741;\u4431quo;\u601e\u0280cmprt\u1753\u175b\u1761\u1764\u1768aus\u0100;e\u010a\u0109ptyv;\u69b0s\xe9\u170cno\xf5\u0113\u0180ahw\u176f\u1771\u1773;\u43b2;\u6136een;\u626cr;\uc000\ud835\udd1fg\u0380costuvw\u178d\u179d\u17b3\u17c1\u17d5\u17db\u17de\u0180aiu\u1794\u1796\u179a\xf0\u0760rc;\u65efp\xbb\u1371\u0180dpt\u17a4\u17a8\u17adot;\u6a00lus;\u6a01imes;\u6a02\u0271\u17b9\0\0\u17becup;\u6a06ar;\u6605riangle\u0100du\u17cd\u17d2own;\u65bdp;\u65b3plus;\u6a04e\xe5\u1444\xe5\u14adarow;\u690d\u0180ako\u17ed\u1826\u1835\u0100cn\u17f2\u1823k\u0180lst\u17fa\u05ab\u1802ozenge;\u69ebriangle\u0200;dlr\u1812\u1813\u1818\u181d\u65b4own;\u65beeft;\u65c2ight;\u65b8k;\u6423\u01b1\u182b\0\u1833\u01b2\u182f\0\u1831;\u6592;\u65914;\u6593ck;\u6588\u0100eo\u183e\u184d\u0100;q\u1843\u1846\uc000=\u20e5uiv;\uc000\u2261\u20e5t;\u6310\u0200ptwx\u1859\u185e\u1867\u186cf;\uc000\ud835\udd53\u0100;t\u13cb\u1863om\xbb\u13cctie;\u62c8\u0600DHUVbdhmptuv\u1885\u1896\u18aa\u18bb\u18d7\u18db\u18ec\u18ff\u1905\u190a\u1910\u1921\u0200LRlr\u188e\u1890\u1892\u1894;\u6557;\u6554;\u6556;\u6553\u0280;DUdu\u18a1\u18a2\u18a4\u18a6\u18a8\u6550;\u6566;\u6569;\u6564;\u6567\u0200LRlr\u18b3\u18b5\u18b7\u18b9;\u655d;\u655a;\u655c;\u6559\u0380;HLRhlr\u18ca\u18cb\u18cd\u18cf\u18d1\u18d3\u18d5\u6551;\u656c;\u6563;\u6560;\u656b;\u6562;\u655fox;\u69c9\u0200LRlr\u18e4\u18e6\u18e8\u18ea;\u6555;\u6552;\u6510;\u650c\u0280;DUdu\u06bd\u18f7\u18f9\u18fb\u18fd;\u6565;\u6568;\u652c;\u6534inus;\u629flus;\u629eimes;\u62a0\u0200LRlr\u1919\u191b\u191d\u191f;\u655b;\u6558;\u6518;\u6514\u0380;HLRhlr\u1930\u1931\u1933\u1935\u1937\u1939\u193b\u6502;\u656a;\u6561;\u655e;\u653c;\u6524;\u651c\u0100ev\u0123\u1942bar\u803b\xa6\u40a6\u0200ceio\u1951\u1956\u195a\u1960r;\uc000\ud835\udcb7mi;\u604fm\u0100;e\u171a\u171cl\u0180;bh\u1968\u1969\u196b\u405c;\u69c5sub;\u67c8\u016c\u1974\u197el\u0100;e\u1979\u197a\u6022t\xbb\u197ap\u0180;Ee\u012f\u1985\u1987;\u6aae\u0100;q\u06dc\u06db\u0ce1\u19a7\0\u19e8\u1a11\u1a15\u1a32\0\u1a37\u1a50\0\0\u1ab4\0\0\u1ac1\0\0\u1b21\u1b2e\u1b4d\u1b52\0\u1bfd\0\u1c0c\u0180cpr\u19ad\u19b2\u19ddute;\u4107\u0300;abcds\u19bf\u19c0\u19c4\u19ca\u19d5\u19d9\u6229nd;\u6a44rcup;\u6a49\u0100au\u19cf\u19d2p;\u6a4bp;\u6a47ot;\u6a40;\uc000\u2229\ufe00\u0100eo\u19e2\u19e5t;\u6041\xee\u0693\u0200aeiu\u19f0\u19fb\u1a01\u1a05\u01f0\u19f5\0\u19f8s;\u6a4don;\u410ddil\u803b\xe7\u40e7rc;\u4109ps\u0100;s\u1a0c\u1a0d\u6a4cm;\u6a50ot;\u410b\u0180dmn\u1a1b\u1a20\u1a26il\u80bb\xb8\u01adptyv;\u69b2t\u8100\xa2;e\u1a2d\u1a2e\u40a2r\xe4\u01b2r;\uc000\ud835\udd20\u0180cei\u1a3d\u1a40\u1a4dy;\u4447ck\u0100;m\u1a47\u1a48\u6713ark\xbb\u1a48;\u43c7r\u0380;Ecefms\u1a5f\u1a60\u1a62\u1a6b\u1aa4\u1aaa\u1aae\u65cb;\u69c3\u0180;el\u1a69\u1a6a\u1a6d\u42c6q;\u6257e\u0261\u1a74\0\0\u1a88rrow\u0100lr\u1a7c\u1a81eft;\u61baight;\u61bb\u0280RSacd\u1a92\u1a94\u1a96\u1a9a\u1a9f\xbb\u0f47;\u64c8st;\u629birc;\u629aash;\u629dnint;\u6a10id;\u6aefcir;\u69c2ubs\u0100;u\u1abb\u1abc\u6663it\xbb\u1abc\u02ec\u1ac7\u1ad4\u1afa\0\u1b0aon\u0100;e\u1acd\u1ace\u403a\u0100;q\xc7\xc6\u026d\u1ad9\0\0\u1ae2a\u0100;t\u1ade\u1adf\u402c;\u4040\u0180;fl\u1ae8\u1ae9\u1aeb\u6201\xee\u1160e\u0100mx\u1af1\u1af6ent\xbb\u1ae9e\xf3\u024d\u01e7\u1afe\0\u1b07\u0100;d\u12bb\u1b02ot;\u6a6dn\xf4\u0246\u0180fry\u1b10\u1b14\u1b17;\uc000\ud835\udd54o\xe4\u0254\u8100\xa9;s\u0155\u1b1dr;\u6117\u0100ao\u1b25\u1b29rr;\u61b5ss;\u6717\u0100cu\u1b32\u1b37r;\uc000\ud835\udcb8\u0100bp\u1b3c\u1b44\u0100;e\u1b41\u1b42\u6acf;\u6ad1\u0100;e\u1b49\u1b4a\u6ad0;\u6ad2dot;\u62ef\u0380delprvw\u1b60\u1b6c\u1b77\u1b82\u1bac\u1bd4\u1bf9arr\u0100lr\u1b68\u1b6a;\u6938;\u6935\u0270\u1b72\0\0\u1b75r;\u62dec;\u62dfarr\u0100;p\u1b7f\u1b80\u61b6;\u693d\u0300;bcdos\u1b8f\u1b90\u1b96\u1ba1\u1ba5\u1ba8\u622arcap;\u6a48\u0100au\u1b9b\u1b9ep;\u6a46p;\u6a4aot;\u628dr;\u6a45;\uc000\u222a\ufe00\u0200alrv\u1bb5\u1bbf\u1bde\u1be3rr\u0100;m\u1bbc\u1bbd\u61b7;\u693cy\u0180evw\u1bc7\u1bd4\u1bd8q\u0270\u1bce\0\0\u1bd2re\xe3\u1b73u\xe3\u1b75ee;\u62ceedge;\u62cfen\u803b\xa4\u40a4earrow\u0100lr\u1bee\u1bf3eft\xbb\u1b80ight\xbb\u1bbde\xe4\u1bdd\u0100ci\u1c01\u1c07onin\xf4\u01f7nt;\u6231lcty;\u632d\u0980AHabcdefhijlorstuwz\u1c38\u1c3b\u1c3f\u1c5d\u1c69\u1c75\u1c8a\u1c9e\u1cac\u1cb7\u1cfb\u1cff\u1d0d\u1d7b\u1d91\u1dab\u1dbb\u1dc6\u1dcdr\xf2\u0381ar;\u6965\u0200glrs\u1c48\u1c4d\u1c52\u1c54ger;\u6020eth;\u6138\xf2\u1133h\u0100;v\u1c5a\u1c5b\u6010\xbb\u090a\u016b\u1c61\u1c67arow;\u690fa\xe3\u0315\u0100ay\u1c6e\u1c73ron;\u410f;\u4434\u0180;ao\u0332\u1c7c\u1c84\u0100gr\u02bf\u1c81r;\u61catseq;\u6a77\u0180glm\u1c91\u1c94\u1c98\u803b\xb0\u40b0ta;\u43b4ptyv;\u69b1\u0100ir\u1ca3\u1ca8sht;\u697f;\uc000\ud835\udd21ar\u0100lr\u1cb3\u1cb5\xbb\u08dc\xbb\u101e\u0280aegsv\u1cc2\u0378\u1cd6\u1cdc\u1ce0m\u0180;os\u0326\u1cca\u1cd4nd\u0100;s\u0326\u1cd1uit;\u6666amma;\u43ddin;\u62f2\u0180;io\u1ce7\u1ce8\u1cf8\u40f7de\u8100\xf7;o\u1ce7\u1cf0ntimes;\u62c7n\xf8\u1cf7cy;\u4452c\u026f\u1d06\0\0\u1d0arn;\u631eop;\u630d\u0280lptuw\u1d18\u1d1d\u1d22\u1d49\u1d55lar;\u4024f;\uc000\ud835\udd55\u0280;emps\u030b\u1d2d\u1d37\u1d3d\u1d42q\u0100;d\u0352\u1d33ot;\u6251inus;\u6238lus;\u6214quare;\u62a1blebarwedg\xe5\xfan\u0180adh\u112e\u1d5d\u1d67ownarrow\xf3\u1c83arpoon\u0100lr\u1d72\u1d76ef\xf4\u1cb4igh\xf4\u1cb6\u0162\u1d7f\u1d85karo\xf7\u0f42\u026f\u1d8a\0\0\u1d8ern;\u631fop;\u630c\u0180cot\u1d98\u1da3\u1da6\u0100ry\u1d9d\u1da1;\uc000\ud835\udcb9;\u4455l;\u69f6rok;\u4111\u0100dr\u1db0\u1db4ot;\u62f1i\u0100;f\u1dba\u1816\u65bf\u0100ah\u1dc0\u1dc3r\xf2\u0429a\xf2\u0fa6angle;\u69a6\u0100ci\u1dd2\u1dd5y;\u445fgrarr;\u67ff\u0900Dacdefglmnopqrstux\u1e01\u1e09\u1e19\u1e38\u0578\u1e3c\u1e49\u1e61\u1e7e\u1ea5\u1eaf\u1ebd\u1ee1\u1f2a\u1f37\u1f44\u1f4e\u1f5a\u0100Do\u1e06\u1d34o\xf4\u1c89\u0100cs\u1e0e\u1e14ute\u803b\xe9\u40e9ter;\u6a6e\u0200aioy\u1e22\u1e27\u1e31\u1e36ron;\u411br\u0100;c\u1e2d\u1e2e\u6256\u803b\xea\u40ealon;\u6255;\u444dot;\u4117\u0100Dr\u1e41\u1e45ot;\u6252;\uc000\ud835\udd22\u0180;rs\u1e50\u1e51\u1e57\u6a9aave\u803b\xe8\u40e8\u0100;d\u1e5c\u1e5d\u6a96ot;\u6a98\u0200;ils\u1e6a\u1e6b\u1e72\u1e74\u6a99nters;\u63e7;\u6113\u0100;d\u1e79\u1e7a\u6a95ot;\u6a97\u0180aps\u1e85\u1e89\u1e97cr;\u4113ty\u0180;sv\u1e92\u1e93\u1e95\u6205et\xbb\u1e93p\u01001;\u1e9d\u1ea4\u0133\u1ea1\u1ea3;\u6004;\u6005\u6003\u0100gs\u1eaa\u1eac;\u414bp;\u6002\u0100gp\u1eb4\u1eb8on;\u4119f;\uc000\ud835\udd56\u0180als\u1ec4\u1ece\u1ed2r\u0100;s\u1eca\u1ecb\u62d5l;\u69e3us;\u6a71i\u0180;lv\u1eda\u1edb\u1edf\u43b5on\xbb\u1edb;\u43f5\u0200csuv\u1eea\u1ef3\u1f0b\u1f23\u0100io\u1eef\u1e31rc\xbb\u1e2e\u0269\u1ef9\0\0\u1efb\xed\u0548ant\u0100gl\u1f02\u1f06tr\xbb\u1e5dess\xbb\u1e7a\u0180aei\u1f12\u1f16\u1f1als;\u403dst;\u625fv\u0100;D\u0235\u1f20D;\u6a78parsl;\u69e5\u0100Da\u1f2f\u1f33ot;\u6253rr;\u6971\u0180cdi\u1f3e\u1f41\u1ef8r;\u612fo\xf4\u0352\u0100ah\u1f49\u1f4b;\u43b7\u803b\xf0\u40f0\u0100mr\u1f53\u1f57l\u803b\xeb\u40ebo;\u60ac\u0180cip\u1f61\u1f64\u1f67l;\u4021s\xf4\u056e\u0100eo\u1f6c\u1f74ctatio\xee\u0559nential\xe5\u0579\u09e1\u1f92\0\u1f9e\0\u1fa1\u1fa7\0\0\u1fc6\u1fcc\0\u1fd3\0\u1fe6\u1fea\u2000\0\u2008\u205allingdotse\xf1\u1e44y;\u4444male;\u6640\u0180ilr\u1fad\u1fb3\u1fc1lig;\u8000\ufb03\u0269\u1fb9\0\0\u1fbdg;\u8000\ufb00ig;\u8000\ufb04;\uc000\ud835\udd23lig;\u8000\ufb01lig;\uc000fj\u0180alt\u1fd9\u1fdc\u1fe1t;\u666dig;\u8000\ufb02ns;\u65b1of;\u4192\u01f0\u1fee\0\u1ff3f;\uc000\ud835\udd57\u0100ak\u05bf\u1ff7\u0100;v\u1ffc\u1ffd\u62d4;\u6ad9artint;\u6a0d\u0100ao\u200c\u2055\u0100cs\u2011\u2052\u03b1\u201a\u2030\u2038\u2045\u2048\0\u2050\u03b2\u2022\u2025\u2027\u202a\u202c\0\u202e\u803b\xbd\u40bd;\u6153\u803b\xbc\u40bc;\u6155;\u6159;\u615b\u01b3\u2034\0\u2036;\u6154;\u6156\u02b4\u203e\u2041\0\0\u2043\u803b\xbe\u40be;\u6157;\u615c5;\u6158\u01b6\u204c\0\u204e;\u615a;\u615d8;\u615el;\u6044wn;\u6322cr;\uc000\ud835\udcbb\u0880Eabcdefgijlnorstv\u2082\u2089\u209f\u20a5\u20b0\u20b4\u20f0\u20f5\u20fa\u20ff\u2103\u2112\u2138\u0317\u213e\u2152\u219e\u0100;l\u064d\u2087;\u6a8c\u0180cmp\u2090\u2095\u209dute;\u41f5ma\u0100;d\u209c\u1cda\u43b3;\u6a86reve;\u411f\u0100iy\u20aa\u20aerc;\u411d;\u4433ot;\u4121\u0200;lqs\u063e\u0642\u20bd\u20c9\u0180;qs\u063e\u064c\u20c4lan\xf4\u0665\u0200;cdl\u0665\u20d2\u20d5\u20e5c;\u6aa9ot\u0100;o\u20dc\u20dd\u6a80\u0100;l\u20e2\u20e3\u6a82;\u6a84\u0100;e\u20ea\u20ed\uc000\u22db\ufe00s;\u6a94r;\uc000\ud835\udd24\u0100;g\u0673\u061bmel;\u6137cy;\u4453\u0200;Eaj\u065a\u210c\u210e\u2110;\u6a92;\u6aa5;\u6aa4\u0200Eaes\u211b\u211d\u2129\u2134;\u6269p\u0100;p\u2123\u2124\u6a8arox\xbb\u2124\u0100;q\u212e\u212f\u6a88\u0100;q\u212e\u211bim;\u62e7pf;\uc000\ud835\udd58\u0100ci\u2143\u2146r;\u610am\u0180;el\u066b\u214e\u2150;\u6a8e;\u6a90\u8300>;cdlqr\u05ee\u2160\u216a\u216e\u2173\u2179\u0100ci\u2165\u2167;\u6aa7r;\u6a7aot;\u62d7Par;\u6995uest;\u6a7c\u0280adels\u2184\u216a\u2190\u0656\u219b\u01f0\u2189\0\u218epro\xf8\u209er;\u6978q\u0100lq\u063f\u2196les\xf3\u2088i\xed\u066b\u0100en\u21a3\u21adrtneqq;\uc000\u2269\ufe00\xc5\u21aa\u0500Aabcefkosy\u21c4\u21c7\u21f1\u21f5\u21fa\u2218\u221d\u222f\u2268\u227dr\xf2\u03a0\u0200ilmr\u21d0\u21d4\u21d7\u21dbrs\xf0\u1484f\xbb\u2024il\xf4\u06a9\u0100dr\u21e0\u21e4cy;\u444a\u0180;cw\u08f4\u21eb\u21efir;\u6948;\u61adar;\u610firc;\u4125\u0180alr\u2201\u220e\u2213rts\u0100;u\u2209\u220a\u6665it\xbb\u220alip;\u6026con;\u62b9r;\uc000\ud835\udd25s\u0100ew\u2223\u2229arow;\u6925arow;\u6926\u0280amopr\u223a\u223e\u2243\u225e\u2263rr;\u61fftht;\u623bk\u0100lr\u2249\u2253eftarrow;\u61a9ightarrow;\u61aaf;\uc000\ud835\udd59bar;\u6015\u0180clt\u226f\u2274\u2278r;\uc000\ud835\udcbdas\xe8\u21f4rok;\u4127\u0100bp\u2282\u2287ull;\u6043hen\xbb\u1c5b\u0ae1\u22a3\0\u22aa\0\u22b8\u22c5\u22ce\0\u22d5\u22f3\0\0\u22f8\u2322\u2367\u2362\u237f\0\u2386\u23aa\u23b4cute\u803b\xed\u40ed\u0180;iy\u0771\u22b0\u22b5rc\u803b\xee\u40ee;\u4438\u0100cx\u22bc\u22bfy;\u4435cl\u803b\xa1\u40a1\u0100fr\u039f\u22c9;\uc000\ud835\udd26rave\u803b\xec\u40ec\u0200;ino\u073e\u22dd\u22e9\u22ee\u0100in\u22e2\u22e6nt;\u6a0ct;\u622dfin;\u69dcta;\u6129lig;\u4133\u0180aop\u22fe\u231a\u231d\u0180cgt\u2305\u2308\u2317r;\u412b\u0180elp\u071f\u230f\u2313in\xe5\u078ear\xf4\u0720h;\u4131f;\u62b7ed;\u41b5\u0280;cfot\u04f4\u232c\u2331\u233d\u2341are;\u6105in\u0100;t\u2338\u2339\u621eie;\u69dddo\xf4\u2319\u0280;celp\u0757\u234c\u2350\u235b\u2361al;\u62ba\u0100gr\u2355\u2359er\xf3\u1563\xe3\u234darhk;\u6a17rod;\u6a3c\u0200cgpt\u236f\u2372\u2376\u237by;\u4451on;\u412ff;\uc000\ud835\udd5aa;\u43b9uest\u803b\xbf\u40bf\u0100ci\u238a\u238fr;\uc000\ud835\udcben\u0280;Edsv\u04f4\u239b\u239d\u23a1\u04f3;\u62f9ot;\u62f5\u0100;v\u23a6\u23a7\u62f4;\u62f3\u0100;i\u0777\u23aelde;\u4129\u01eb\u23b8\0\u23bccy;\u4456l\u803b\xef\u40ef\u0300cfmosu\u23cc\u23d7\u23dc\u23e1\u23e7\u23f5\u0100iy\u23d1\u23d5rc;\u4135;\u4439r;\uc000\ud835\udd27ath;\u4237pf;\uc000\ud835\udd5b\u01e3\u23ec\0\u23f1r;\uc000\ud835\udcbfrcy;\u4458kcy;\u4454\u0400acfghjos\u240b\u2416\u2422\u2427\u242d\u2431\u2435\u243bppa\u0100;v\u2413\u2414\u43ba;\u43f0\u0100ey\u241b\u2420dil;\u4137;\u443ar;\uc000\ud835\udd28reen;\u4138cy;\u4445cy;\u445cpf;\uc000\ud835\udd5ccr;\uc000\ud835\udcc0\u0b80ABEHabcdefghjlmnoprstuv\u2470\u2481\u2486\u248d\u2491\u250e\u253d\u255a\u2580\u264e\u265e\u2665\u2679\u267d\u269a\u26b2\u26d8\u275d\u2768\u278b\u27c0\u2801\u2812\u0180art\u2477\u247a\u247cr\xf2\u09c6\xf2\u0395ail;\u691barr;\u690e\u0100;g\u0994\u248b;\u6a8bar;\u6962\u0963\u24a5\0\u24aa\0\u24b1\0\0\0\0\0\u24b5\u24ba\0\u24c6\u24c8\u24cd\0\u24f9ute;\u413amptyv;\u69b4ra\xee\u084cbda;\u43bbg\u0180;dl\u088e\u24c1\u24c3;\u6991\xe5\u088e;\u6a85uo\u803b\xab\u40abr\u0400;bfhlpst\u0899\u24de\u24e6\u24e9\u24eb\u24ee\u24f1\u24f5\u0100;f\u089d\u24e3s;\u691fs;\u691d\xeb\u2252p;\u61abl;\u6939im;\u6973l;\u61a2\u0180;ae\u24ff\u2500\u2504\u6aabil;\u6919\u0100;s\u2509\u250a\u6aad;\uc000\u2aad\ufe00\u0180abr\u2515\u2519\u251drr;\u690crk;\u6772\u0100ak\u2522\u252cc\u0100ek\u2528\u252a;\u407b;\u405b\u0100es\u2531\u2533;\u698bl\u0100du\u2539\u253b;\u698f;\u698d\u0200aeuy\u2546\u254b\u2556\u2558ron;\u413e\u0100di\u2550\u2554il;\u413c\xec\u08b0\xe2\u2529;\u443b\u0200cqrs\u2563\u2566\u256d\u257da;\u6936uo\u0100;r\u0e19\u1746\u0100du\u2572\u2577har;\u6967shar;\u694bh;\u61b2\u0280;fgqs\u258b\u258c\u0989\u25f3\u25ff\u6264t\u0280ahlrt\u2598\u25a4\u25b7\u25c2\u25e8rrow\u0100;t\u0899\u25a1a\xe9\u24f6arpoon\u0100du\u25af\u25b4own\xbb\u045ap\xbb\u0966eftarrows;\u61c7ight\u0180ahs\u25cd\u25d6\u25derrow\u0100;s\u08f4\u08a7arpoon\xf3\u0f98quigarro\xf7\u21f0hreetimes;\u62cb\u0180;qs\u258b\u0993\u25falan\xf4\u09ac\u0280;cdgs\u09ac\u260a\u260d\u261d\u2628c;\u6aa8ot\u0100;o\u2614\u2615\u6a7f\u0100;r\u261a\u261b\u6a81;\u6a83\u0100;e\u2622\u2625\uc000\u22da\ufe00s;\u6a93\u0280adegs\u2633\u2639\u263d\u2649\u264bppro\xf8\u24c6ot;\u62d6q\u0100gq\u2643\u2645\xf4\u0989gt\xf2\u248c\xf4\u099bi\xed\u09b2\u0180ilr\u2655\u08e1\u265asht;\u697c;\uc000\ud835\udd29\u0100;E\u099c\u2663;\u6a91\u0161\u2669\u2676r\u0100du\u25b2\u266e\u0100;l\u0965\u2673;\u696alk;\u6584cy;\u4459\u0280;acht\u0a48\u2688\u268b\u2691\u2696r\xf2\u25c1orne\xf2\u1d08ard;\u696bri;\u65fa\u0100io\u269f\u26a4dot;\u4140ust\u0100;a\u26ac\u26ad\u63b0che\xbb\u26ad\u0200Eaes\u26bb\u26bd\u26c9\u26d4;\u6268p\u0100;p\u26c3\u26c4\u6a89rox\xbb\u26c4\u0100;q\u26ce\u26cf\u6a87\u0100;q\u26ce\u26bbim;\u62e6\u0400abnoptwz\u26e9\u26f4\u26f7\u271a\u272f\u2741\u2747\u2750\u0100nr\u26ee\u26f1g;\u67ecr;\u61fdr\xeb\u08c1g\u0180lmr\u26ff\u270d\u2714eft\u0100ar\u09e6\u2707ight\xe1\u09f2apsto;\u67fcight\xe1\u09fdparrow\u0100lr\u2725\u2729ef\xf4\u24edight;\u61ac\u0180afl\u2736\u2739\u273dr;\u6985;\uc000\ud835\udd5dus;\u6a2dimes;\u6a34\u0161\u274b\u274fst;\u6217\xe1\u134e\u0180;ef\u2757\u2758\u1800\u65cange\xbb\u2758ar\u0100;l\u2764\u2765\u4028t;\u6993\u0280achmt\u2773\u2776\u277c\u2785\u2787r\xf2\u08a8orne\xf2\u1d8car\u0100;d\u0f98\u2783;\u696d;\u600eri;\u62bf\u0300achiqt\u2798\u279d\u0a40\u27a2\u27ae\u27bbquo;\u6039r;\uc000\ud835\udcc1m\u0180;eg\u09b2\u27aa\u27ac;\u6a8d;\u6a8f\u0100bu\u252a\u27b3o\u0100;r\u0e1f\u27b9;\u601arok;\u4142\u8400<;cdhilqr\u082b\u27d2\u2639\u27dc\u27e0\u27e5\u27ea\u27f0\u0100ci\u27d7\u27d9;\u6aa6r;\u6a79re\xe5\u25f2mes;\u62c9arr;\u6976uest;\u6a7b\u0100Pi\u27f5\u27f9ar;\u6996\u0180;ef\u2800\u092d\u181b\u65c3r\u0100du\u2807\u280dshar;\u694ahar;\u6966\u0100en\u2817\u2821rtneqq;\uc000\u2268\ufe00\xc5\u281e\u0700Dacdefhilnopsu\u2840\u2845\u2882\u288e\u2893\u28a0\u28a5\u28a8\u28da\u28e2\u28e4\u0a83\u28f3\u2902Dot;\u623a\u0200clpr\u284e\u2852\u2863\u287dr\u803b\xaf\u40af\u0100et\u2857\u2859;\u6642\u0100;e\u285e\u285f\u6720se\xbb\u285f\u0100;s\u103b\u2868to\u0200;dlu\u103b\u2873\u2877\u287bow\xee\u048cef\xf4\u090f\xf0\u13d1ker;\u65ae\u0100oy\u2887\u288cmma;\u6a29;\u443cash;\u6014asuredangle\xbb\u1626r;\uc000\ud835\udd2ao;\u6127\u0180cdn\u28af\u28b4\u28c9ro\u803b\xb5\u40b5\u0200;acd\u1464\u28bd\u28c0\u28c4s\xf4\u16a7ir;\u6af0ot\u80bb\xb7\u01b5us\u0180;bd\u28d2\u1903\u28d3\u6212\u0100;u\u1d3c\u28d8;\u6a2a\u0163\u28de\u28e1p;\u6adb\xf2\u2212\xf0\u0a81\u0100dp\u28e9\u28eeels;\u62a7f;\uc000\ud835\udd5e\u0100ct\u28f8\u28fdr;\uc000\ud835\udcc2pos\xbb\u159d\u0180;lm\u2909\u290a\u290d\u43bctimap;\u62b8\u0c00GLRVabcdefghijlmoprstuvw\u2942\u2953\u297e\u2989\u2998\u29da\u29e9\u2a15\u2a1a\u2a58\u2a5d\u2a83\u2a95\u2aa4\u2aa8\u2b04\u2b07\u2b44\u2b7f\u2bae\u2c34\u2c67\u2c7c\u2ce9\u0100gt\u2947\u294b;\uc000\u22d9\u0338\u0100;v\u2950\u0bcf\uc000\u226b\u20d2\u0180elt\u295a\u2972\u2976ft\u0100ar\u2961\u2967rrow;\u61cdightarrow;\u61ce;\uc000\u22d8\u0338\u0100;v\u297b\u0c47\uc000\u226a\u20d2ightarrow;\u61cf\u0100Dd\u298e\u2993ash;\u62afash;\u62ae\u0280bcnpt\u29a3\u29a7\u29ac\u29b1\u29ccla\xbb\u02deute;\u4144g;\uc000\u2220\u20d2\u0280;Eiop\u0d84\u29bc\u29c0\u29c5\u29c8;\uc000\u2a70\u0338d;\uc000\u224b\u0338s;\u4149ro\xf8\u0d84ur\u0100;a\u29d3\u29d4\u666el\u0100;s\u29d3\u0b38\u01f3\u29df\0\u29e3p\u80bb\xa0\u0b37mp\u0100;e\u0bf9\u0c00\u0280aeouy\u29f4\u29fe\u2a03\u2a10\u2a13\u01f0\u29f9\0\u29fb;\u6a43on;\u4148dil;\u4146ng\u0100;d\u0d7e\u2a0aot;\uc000\u2a6d\u0338p;\u6a42;\u443dash;\u6013\u0380;Aadqsx\u0b92\u2a29\u2a2d\u2a3b\u2a41\u2a45\u2a50rr;\u61d7r\u0100hr\u2a33\u2a36k;\u6924\u0100;o\u13f2\u13f0ot;\uc000\u2250\u0338ui\xf6\u0b63\u0100ei\u2a4a\u2a4ear;\u6928\xed\u0b98ist\u0100;s\u0ba0\u0b9fr;\uc000\ud835\udd2b\u0200Eest\u0bc5\u2a66\u2a79\u2a7c\u0180;qs\u0bbc\u2a6d\u0be1\u0180;qs\u0bbc\u0bc5\u2a74lan\xf4\u0be2i\xed\u0bea\u0100;r\u0bb6\u2a81\xbb\u0bb7\u0180Aap\u2a8a\u2a8d\u2a91r\xf2\u2971rr;\u61aear;\u6af2\u0180;sv\u0f8d\u2a9c\u0f8c\u0100;d\u2aa1\u2aa2\u62fc;\u62facy;\u445a\u0380AEadest\u2ab7\u2aba\u2abe\u2ac2\u2ac5\u2af6\u2af9r\xf2\u2966;\uc000\u2266\u0338rr;\u619ar;\u6025\u0200;fqs\u0c3b\u2ace\u2ae3\u2aeft\u0100ar\u2ad4\u2ad9rro\xf7\u2ac1ightarro\xf7\u2a90\u0180;qs\u0c3b\u2aba\u2aealan\xf4\u0c55\u0100;s\u0c55\u2af4\xbb\u0c36i\xed\u0c5d\u0100;r\u0c35\u2afei\u0100;e\u0c1a\u0c25i\xe4\u0d90\u0100pt\u2b0c\u2b11f;\uc000\ud835\udd5f\u8180\xac;in\u2b19\u2b1a\u2b36\u40acn\u0200;Edv\u0b89\u2b24\u2b28\u2b2e;\uc000\u22f9\u0338ot;\uc000\u22f5\u0338\u01e1\u0b89\u2b33\u2b35;\u62f7;\u62f6i\u0100;v\u0cb8\u2b3c\u01e1\u0cb8\u2b41\u2b43;\u62fe;\u62fd\u0180aor\u2b4b\u2b63\u2b69r\u0200;ast\u0b7b\u2b55\u2b5a\u2b5flle\xec\u0b7bl;\uc000\u2afd\u20e5;\uc000\u2202\u0338lint;\u6a14\u0180;ce\u0c92\u2b70\u2b73u\xe5\u0ca5\u0100;c\u0c98\u2b78\u0100;e\u0c92\u2b7d\xf1\u0c98\u0200Aait\u2b88\u2b8b\u2b9d\u2ba7r\xf2\u2988rr\u0180;cw\u2b94\u2b95\u2b99\u619b;\uc000\u2933\u0338;\uc000\u219d\u0338ghtarrow\xbb\u2b95ri\u0100;e\u0ccb\u0cd6\u0380chimpqu\u2bbd\u2bcd\u2bd9\u2b04\u0b78\u2be4\u2bef\u0200;cer\u0d32\u2bc6\u0d37\u2bc9u\xe5\u0d45;\uc000\ud835\udcc3ort\u026d\u2b05\0\0\u2bd6ar\xe1\u2b56m\u0100;e\u0d6e\u2bdf\u0100;q\u0d74\u0d73su\u0100bp\u2beb\u2bed\xe5\u0cf8\xe5\u0d0b\u0180bcp\u2bf6\u2c11\u2c19\u0200;Ees\u2bff\u2c00\u0d22\u2c04\u6284;\uc000\u2ac5\u0338et\u0100;e\u0d1b\u2c0bq\u0100;q\u0d23\u2c00c\u0100;e\u0d32\u2c17\xf1\u0d38\u0200;Ees\u2c22\u2c23\u0d5f\u2c27\u6285;\uc000\u2ac6\u0338et\u0100;e\u0d58\u2c2eq\u0100;q\u0d60\u2c23\u0200gilr\u2c3d\u2c3f\u2c45\u2c47\xec\u0bd7lde\u803b\xf1\u40f1\xe7\u0c43iangle\u0100lr\u2c52\u2c5ceft\u0100;e\u0c1a\u2c5a\xf1\u0c26ight\u0100;e\u0ccb\u2c65\xf1\u0cd7\u0100;m\u2c6c\u2c6d\u43bd\u0180;es\u2c74\u2c75\u2c79\u4023ro;\u6116p;\u6007\u0480DHadgilrs\u2c8f\u2c94\u2c99\u2c9e\u2ca3\u2cb0\u2cb6\u2cd3\u2ce3ash;\u62adarr;\u6904p;\uc000\u224d\u20d2ash;\u62ac\u0100et\u2ca8\u2cac;\uc000\u2265\u20d2;\uc000>\u20d2nfin;\u69de\u0180Aet\u2cbd\u2cc1\u2cc5rr;\u6902;\uc000\u2264\u20d2\u0100;r\u2cca\u2ccd\uc000<\u20d2ie;\uc000\u22b4\u20d2\u0100At\u2cd8\u2cdcrr;\u6903rie;\uc000\u22b5\u20d2im;\uc000\u223c\u20d2\u0180Aan\u2cf0\u2cf4\u2d02rr;\u61d6r\u0100hr\u2cfa\u2cfdk;\u6923\u0100;o\u13e7\u13e5ear;\u6927\u1253\u1a95\0\0\0\0\0\0\0\0\0\0\0\0\0\u2d2d\0\u2d38\u2d48\u2d60\u2d65\u2d72\u2d84\u1b07\0\0\u2d8d\u2dab\0\u2dc8\u2dce\0\u2ddc\u2e19\u2e2b\u2e3e\u2e43\u0100cs\u2d31\u1a97ute\u803b\xf3\u40f3\u0100iy\u2d3c\u2d45r\u0100;c\u1a9e\u2d42\u803b\xf4\u40f4;\u443e\u0280abios\u1aa0\u2d52\u2d57\u01c8\u2d5alac;\u4151v;\u6a38old;\u69bclig;\u4153\u0100cr\u2d69\u2d6dir;\u69bf;\uc000\ud835\udd2c\u036f\u2d79\0\0\u2d7c\0\u2d82n;\u42dbave\u803b\xf2\u40f2;\u69c1\u0100bm\u2d88\u0df4ar;\u69b5\u0200acit\u2d95\u2d98\u2da5\u2da8r\xf2\u1a80\u0100ir\u2d9d\u2da0r;\u69beoss;\u69bbn\xe5\u0e52;\u69c0\u0180aei\u2db1\u2db5\u2db9cr;\u414dga;\u43c9\u0180cdn\u2dc0\u2dc5\u01cdron;\u43bf;\u69b6pf;\uc000\ud835\udd60\u0180ael\u2dd4\u2dd7\u01d2r;\u69b7rp;\u69b9\u0380;adiosv\u2dea\u2deb\u2dee\u2e08\u2e0d\u2e10\u2e16\u6228r\xf2\u1a86\u0200;efm\u2df7\u2df8\u2e02\u2e05\u6a5dr\u0100;o\u2dfe\u2dff\u6134f\xbb\u2dff\u803b\xaa\u40aa\u803b\xba\u40bagof;\u62b6r;\u6a56lope;\u6a57;\u6a5b\u0180clo\u2e1f\u2e21\u2e27\xf2\u2e01ash\u803b\xf8\u40f8l;\u6298i\u016c\u2e2f\u2e34de\u803b\xf5\u40f5es\u0100;a\u01db\u2e3as;\u6a36ml\u803b\xf6\u40f6bar;\u633d\u0ae1\u2e5e\0\u2e7d\0\u2e80\u2e9d\0\u2ea2\u2eb9\0\0\u2ecb\u0e9c\0\u2f13\0\0\u2f2b\u2fbc\0\u2fc8r\u0200;ast\u0403\u2e67\u2e72\u0e85\u8100\xb6;l\u2e6d\u2e6e\u40b6le\xec\u0403\u0269\u2e78\0\0\u2e7bm;\u6af3;\u6afdy;\u443fr\u0280cimpt\u2e8b\u2e8f\u2e93\u1865\u2e97nt;\u4025od;\u402eil;\u6030enk;\u6031r;\uc000\ud835\udd2d\u0180imo\u2ea8\u2eb0\u2eb4\u0100;v\u2ead\u2eae\u43c6;\u43d5ma\xf4\u0a76ne;\u660e\u0180;tv\u2ebf\u2ec0\u2ec8\u43c0chfork\xbb\u1ffd;\u43d6\u0100au\u2ecf\u2edfn\u0100ck\u2ed5\u2eddk\u0100;h\u21f4\u2edb;\u610e\xf6\u21f4s\u0480;abcdemst\u2ef3\u2ef4\u1908\u2ef9\u2efd\u2f04\u2f06\u2f0a\u2f0e\u402bcir;\u6a23ir;\u6a22\u0100ou\u1d40\u2f02;\u6a25;\u6a72n\u80bb\xb1\u0e9dim;\u6a26wo;\u6a27\u0180ipu\u2f19\u2f20\u2f25ntint;\u6a15f;\uc000\ud835\udd61nd\u803b\xa3\u40a3\u0500;Eaceinosu\u0ec8\u2f3f\u2f41\u2f44\u2f47\u2f81\u2f89\u2f92\u2f7e\u2fb6;\u6ab3p;\u6ab7u\xe5\u0ed9\u0100;c\u0ece\u2f4c\u0300;acens\u0ec8\u2f59\u2f5f\u2f66\u2f68\u2f7eppro\xf8\u2f43urlye\xf1\u0ed9\xf1\u0ece\u0180aes\u2f6f\u2f76\u2f7approx;\u6ab9qq;\u6ab5im;\u62e8i\xed\u0edfme\u0100;s\u2f88\u0eae\u6032\u0180Eas\u2f78\u2f90\u2f7a\xf0\u2f75\u0180dfp\u0eec\u2f99\u2faf\u0180als\u2fa0\u2fa5\u2faalar;\u632eine;\u6312urf;\u6313\u0100;t\u0efb\u2fb4\xef\u0efbrel;\u62b0\u0100ci\u2fc0\u2fc5r;\uc000\ud835\udcc5;\u43c8ncsp;\u6008\u0300fiopsu\u2fda\u22e2\u2fdf\u2fe5\u2feb\u2ff1r;\uc000\ud835\udd2epf;\uc000\ud835\udd62rime;\u6057cr;\uc000\ud835\udcc6\u0180aeo\u2ff8\u3009\u3013t\u0100ei\u2ffe\u3005rnion\xf3\u06b0nt;\u6a16st\u0100;e\u3010\u3011\u403f\xf1\u1f19\xf4\u0f14\u0a80ABHabcdefhilmnoprstux\u3040\u3051\u3055\u3059\u30e0\u310e\u312b\u3147\u3162\u3172\u318e\u3206\u3215\u3224\u3229\u3258\u326e\u3272\u3290\u32b0\u32b7\u0180art\u3047\u304a\u304cr\xf2\u10b3\xf2\u03ddail;\u691car\xf2\u1c65ar;\u6964\u0380cdenqrt\u3068\u3075\u3078\u307f\u308f\u3094\u30cc\u0100eu\u306d\u3071;\uc000\u223d\u0331te;\u4155i\xe3\u116emptyv;\u69b3g\u0200;del\u0fd1\u3089\u308b\u308d;\u6992;\u69a5\xe5\u0fd1uo\u803b\xbb\u40bbr\u0580;abcfhlpstw\u0fdc\u30ac\u30af\u30b7\u30b9\u30bc\u30be\u30c0\u30c3\u30c7\u30cap;\u6975\u0100;f\u0fe0\u30b4s;\u6920;\u6933s;\u691e\xeb\u225d\xf0\u272el;\u6945im;\u6974l;\u61a3;\u619d\u0100ai\u30d1\u30d5il;\u691ao\u0100;n\u30db\u30dc\u6236al\xf3\u0f1e\u0180abr\u30e7\u30ea\u30eer\xf2\u17e5rk;\u6773\u0100ak\u30f3\u30fdc\u0100ek\u30f9\u30fb;\u407d;\u405d\u0100es\u3102\u3104;\u698cl\u0100du\u310a\u310c;\u698e;\u6990\u0200aeuy\u3117\u311c\u3127\u3129ron;\u4159\u0100di\u3121\u3125il;\u4157\xec\u0ff2\xe2\u30fa;\u4440\u0200clqs\u3134\u3137\u313d\u3144a;\u6937dhar;\u6969uo\u0100;r\u020e\u020dh;\u61b3\u0180acg\u314e\u315f\u0f44l\u0200;ips\u0f78\u3158\u315b\u109cn\xe5\u10bbar\xf4\u0fa9t;\u65ad\u0180ilr\u3169\u1023\u316esht;\u697d;\uc000\ud835\udd2f\u0100ao\u3177\u3186r\u0100du\u317d\u317f\xbb\u047b\u0100;l\u1091\u3184;\u696c\u0100;v\u318b\u318c\u43c1;\u43f1\u0180gns\u3195\u31f9\u31fcht\u0300ahlrst\u31a4\u31b0\u31c2\u31d8\u31e4\u31eerrow\u0100;t\u0fdc\u31ada\xe9\u30c8arpoon\u0100du\u31bb\u31bfow\xee\u317ep\xbb\u1092eft\u0100ah\u31ca\u31d0rrow\xf3\u0feaarpoon\xf3\u0551ightarrows;\u61c9quigarro\xf7\u30cbhreetimes;\u62ccg;\u42daingdotse\xf1\u1f32\u0180ahm\u320d\u3210\u3213r\xf2\u0feaa\xf2\u0551;\u600foust\u0100;a\u321e\u321f\u63b1che\xbb\u321fmid;\u6aee\u0200abpt\u3232\u323d\u3240\u3252\u0100nr\u3237\u323ag;\u67edr;\u61fer\xeb\u1003\u0180afl\u3247\u324a\u324er;\u6986;\uc000\ud835\udd63us;\u6a2eimes;\u6a35\u0100ap\u325d\u3267r\u0100;g\u3263\u3264\u4029t;\u6994olint;\u6a12ar\xf2\u31e3\u0200achq\u327b\u3280\u10bc\u3285quo;\u603ar;\uc000\ud835\udcc7\u0100bu\u30fb\u328ao\u0100;r\u0214\u0213\u0180hir\u3297\u329b\u32a0re\xe5\u31f8mes;\u62cai\u0200;efl\u32aa\u1059\u1821\u32ab\u65b9tri;\u69celuhar;\u6968;\u611e\u0d61\u32d5\u32db\u32df\u332c\u3338\u3371\0\u337a\u33a4\0\0\u33ec\u33f0\0\u3428\u3448\u345a\u34ad\u34b1\u34ca\u34f1\0\u3616\0\0\u3633cute;\u415bqu\xef\u27ba\u0500;Eaceinpsy\u11ed\u32f3\u32f5\u32ff\u3302\u330b\u330f\u331f\u3326\u3329;\u6ab4\u01f0\u32fa\0\u32fc;\u6ab8on;\u4161u\xe5\u11fe\u0100;d\u11f3\u3307il;\u415frc;\u415d\u0180Eas\u3316\u3318\u331b;\u6ab6p;\u6abaim;\u62e9olint;\u6a13i\xed\u1204;\u4441ot\u0180;be\u3334\u1d47\u3335\u62c5;\u6a66\u0380Aacmstx\u3346\u334a\u3357\u335b\u335e\u3363\u336drr;\u61d8r\u0100hr\u3350\u3352\xeb\u2228\u0100;o\u0a36\u0a34t\u803b\xa7\u40a7i;\u403bwar;\u6929m\u0100in\u3369\xf0nu\xf3\xf1t;\u6736r\u0100;o\u3376\u2055\uc000\ud835\udd30\u0200acoy\u3382\u3386\u3391\u33a0rp;\u666f\u0100hy\u338b\u338fcy;\u4449;\u4448rt\u026d\u3399\0\0\u339ci\xe4\u1464ara\xec\u2e6f\u803b\xad\u40ad\u0100gm\u33a8\u33b4ma\u0180;fv\u33b1\u33b2\u33b2\u43c3;\u43c2\u0400;deglnpr\u12ab\u33c5\u33c9\u33ce\u33d6\u33de\u33e1\u33e6ot;\u6a6a\u0100;q\u12b1\u12b0\u0100;E\u33d3\u33d4\u6a9e;\u6aa0\u0100;E\u33db\u33dc\u6a9d;\u6a9fe;\u6246lus;\u6a24arr;\u6972ar\xf2\u113d\u0200aeit\u33f8\u3408\u340f\u3417\u0100ls\u33fd\u3404lsetm\xe9\u336ahp;\u6a33parsl;\u69e4\u0100dl\u1463\u3414e;\u6323\u0100;e\u341c\u341d\u6aaa\u0100;s\u3422\u3423\u6aac;\uc000\u2aac\ufe00\u0180flp\u342e\u3433\u3442tcy;\u444c\u0100;b\u3438\u3439\u402f\u0100;a\u343e\u343f\u69c4r;\u633ff;\uc000\ud835\udd64a\u0100dr\u344d\u0402es\u0100;u\u3454\u3455\u6660it\xbb\u3455\u0180csu\u3460\u3479\u349f\u0100au\u3465\u346fp\u0100;s\u1188\u346b;\uc000\u2293\ufe00p\u0100;s\u11b4\u3475;\uc000\u2294\ufe00u\u0100bp\u347f\u348f\u0180;es\u1197\u119c\u3486et\u0100;e\u1197\u348d\xf1\u119d\u0180;es\u11a8\u11ad\u3496et\u0100;e\u11a8\u349d\xf1\u11ae\u0180;af\u117b\u34a6\u05b0r\u0165\u34ab\u05b1\xbb\u117car\xf2\u1148\u0200cemt\u34b9\u34be\u34c2\u34c5r;\uc000\ud835\udcc8tm\xee\xf1i\xec\u3415ar\xe6\u11be\u0100ar\u34ce\u34d5r\u0100;f\u34d4\u17bf\u6606\u0100an\u34da\u34edight\u0100ep\u34e3\u34eapsilo\xee\u1ee0h\xe9\u2eafs\xbb\u2852\u0280bcmnp\u34fb\u355e\u1209\u358b\u358e\u0480;Edemnprs\u350e\u350f\u3511\u3515\u351e\u3523\u352c\u3531\u3536\u6282;\u6ac5ot;\u6abd\u0100;d\u11da\u351aot;\u6ac3ult;\u6ac1\u0100Ee\u3528\u352a;\u6acb;\u628alus;\u6abfarr;\u6979\u0180eiu\u353d\u3552\u3555t\u0180;en\u350e\u3545\u354bq\u0100;q\u11da\u350feq\u0100;q\u352b\u3528m;\u6ac7\u0100bp\u355a\u355c;\u6ad5;\u6ad3c\u0300;acens\u11ed\u356c\u3572\u3579\u357b\u3326ppro\xf8\u32faurlye\xf1\u11fe\xf1\u11f3\u0180aes\u3582\u3588\u331bppro\xf8\u331aq\xf1\u3317g;\u666a\u0680123;Edehlmnps\u35a9\u35ac\u35af\u121c\u35b2\u35b4\u35c0\u35c9\u35d5\u35da\u35df\u35e8\u35ed\u803b\xb9\u40b9\u803b\xb2\u40b2\u803b\xb3\u40b3;\u6ac6\u0100os\u35b9\u35bct;\u6abeub;\u6ad8\u0100;d\u1222\u35c5ot;\u6ac4s\u0100ou\u35cf\u35d2l;\u67c9b;\u6ad7arr;\u697bult;\u6ac2\u0100Ee\u35e4\u35e6;\u6acc;\u628blus;\u6ac0\u0180eiu\u35f4\u3609\u360ct\u0180;en\u121c\u35fc\u3602q\u0100;q\u1222\u35b2eq\u0100;q\u35e7\u35e4m;\u6ac8\u0100bp\u3611\u3613;\u6ad4;\u6ad6\u0180Aan\u361c\u3620\u362drr;\u61d9r\u0100hr\u3626\u3628\xeb\u222e\u0100;o\u0a2b\u0a29war;\u692alig\u803b\xdf\u40df\u0be1\u3651\u365d\u3660\u12ce\u3673\u3679\0\u367e\u36c2\0\0\0\0\0\u36db\u3703\0\u3709\u376c\0\0\0\u3787\u0272\u3656\0\0\u365bget;\u6316;\u43c4r\xeb\u0e5f\u0180aey\u3666\u366b\u3670ron;\u4165dil;\u4163;\u4442lrec;\u6315r;\uc000\ud835\udd31\u0200eiko\u3686\u369d\u36b5\u36bc\u01f2\u368b\0\u3691e\u01004f\u1284\u1281a\u0180;sv\u3698\u3699\u369b\u43b8ym;\u43d1\u0100cn\u36a2\u36b2k\u0100as\u36a8\u36aeppro\xf8\u12c1im\xbb\u12acs\xf0\u129e\u0100as\u36ba\u36ae\xf0\u12c1rn\u803b\xfe\u40fe\u01ec\u031f\u36c6\u22e7es\u8180\xd7;bd\u36cf\u36d0\u36d8\u40d7\u0100;a\u190f\u36d5r;\u6a31;\u6a30\u0180eps\u36e1\u36e3\u3700\xe1\u2a4d\u0200;bcf\u0486\u36ec\u36f0\u36f4ot;\u6336ir;\u6af1\u0100;o\u36f9\u36fc\uc000\ud835\udd65rk;\u6ada\xe1\u3362rime;\u6034\u0180aip\u370f\u3712\u3764d\xe5\u1248\u0380adempst\u3721\u374d\u3740\u3751\u3757\u375c\u375fngle\u0280;dlqr\u3730\u3731\u3736\u3740\u3742\u65b5own\xbb\u1dbbeft\u0100;e\u2800\u373e\xf1\u092e;\u625cight\u0100;e\u32aa\u374b\xf1\u105aot;\u65ecinus;\u6a3alus;\u6a39b;\u69cdime;\u6a3bezium;\u63e2\u0180cht\u3772\u377d\u3781\u0100ry\u3777\u377b;\uc000\ud835\udcc9;\u4446cy;\u445brok;\u4167\u0100io\u378b\u378ex\xf4\u1777head\u0100lr\u3797\u37a0eftarro\xf7\u084fightarrow\xbb\u0f5d\u0900AHabcdfghlmoprstuw\u37d0\u37d3\u37d7\u37e4\u37f0\u37fc\u380e\u381c\u3823\u3834\u3851\u385d\u386b\u38a9\u38cc\u38d2\u38ea\u38f6r\xf2\u03edar;\u6963\u0100cr\u37dc\u37e2ute\u803b\xfa\u40fa\xf2\u1150r\u01e3\u37ea\0\u37edy;\u445eve;\u416d\u0100iy\u37f5\u37farc\u803b\xfb\u40fb;\u4443\u0180abh\u3803\u3806\u380br\xf2\u13adlac;\u4171a\xf2\u13c3\u0100ir\u3813\u3818sht;\u697e;\uc000\ud835\udd32rave\u803b\xf9\u40f9\u0161\u3827\u3831r\u0100lr\u382c\u382e\xbb\u0957\xbb\u1083lk;\u6580\u0100ct\u3839\u384d\u026f\u383f\0\0\u384arn\u0100;e\u3845\u3846\u631cr\xbb\u3846op;\u630fri;\u65f8\u0100al\u3856\u385acr;\u416b\u80bb\xa8\u0349\u0100gp\u3862\u3866on;\u4173f;\uc000\ud835\udd66\u0300adhlsu\u114b\u3878\u387d\u1372\u3891\u38a0own\xe1\u13b3arpoon\u0100lr\u3888\u388cef\xf4\u382digh\xf4\u382fi\u0180;hl\u3899\u389a\u389c\u43c5\xbb\u13faon\xbb\u389aparrows;\u61c8\u0180cit\u38b0\u38c4\u38c8\u026f\u38b6\0\0\u38c1rn\u0100;e\u38bc\u38bd\u631dr\xbb\u38bdop;\u630eng;\u416fri;\u65f9cr;\uc000\ud835\udcca\u0180dir\u38d9\u38dd\u38e2ot;\u62f0lde;\u4169i\u0100;f\u3730\u38e8\xbb\u1813\u0100am\u38ef\u38f2r\xf2\u38a8l\u803b\xfc\u40fcangle;\u69a7\u0780ABDacdeflnoprsz\u391c\u391f\u3929\u392d\u39b5\u39b8\u39bd\u39df\u39e4\u39e8\u39f3\u39f9\u39fd\u3a01\u3a20r\xf2\u03f7ar\u0100;v\u3926\u3927\u6ae8;\u6ae9as\xe8\u03e1\u0100nr\u3932\u3937grt;\u699c\u0380eknprst\u34e3\u3946\u394b\u3952\u395d\u3964\u3996app\xe1\u2415othin\xe7\u1e96\u0180hir\u34eb\u2ec8\u3959op\xf4\u2fb5\u0100;h\u13b7\u3962\xef\u318d\u0100iu\u3969\u396dgm\xe1\u33b3\u0100bp\u3972\u3984setneq\u0100;q\u397d\u3980\uc000\u228a\ufe00;\uc000\u2acb\ufe00setneq\u0100;q\u398f\u3992\uc000\u228b\ufe00;\uc000\u2acc\ufe00\u0100hr\u399b\u399fet\xe1\u369ciangle\u0100lr\u39aa\u39afeft\xbb\u0925ight\xbb\u1051y;\u4432ash\xbb\u1036\u0180elr\u39c4\u39d2\u39d7\u0180;be\u2dea\u39cb\u39cfar;\u62bbq;\u625alip;\u62ee\u0100bt\u39dc\u1468a\xf2\u1469r;\uc000\ud835\udd33tr\xe9\u39aesu\u0100bp\u39ef\u39f1\xbb\u0d1c\xbb\u0d59pf;\uc000\ud835\udd67ro\xf0\u0efbtr\xe9\u39b4\u0100cu\u3a06\u3a0br;\uc000\ud835\udccb\u0100bp\u3a10\u3a18n\u0100Ee\u3980\u3a16\xbb\u397en\u0100Ee\u3992\u3a1e\xbb\u3990igzag;\u699a\u0380cefoprs\u3a36\u3a3b\u3a56\u3a5b\u3a54\u3a61\u3a6airc;\u4175\u0100di\u3a40\u3a51\u0100bg\u3a45\u3a49ar;\u6a5fe\u0100;q\u15fa\u3a4f;\u6259erp;\u6118r;\uc000\ud835\udd34pf;\uc000\ud835\udd68\u0100;e\u1479\u3a66at\xe8\u1479cr;\uc000\ud835\udccc\u0ae3\u178e\u3a87\0\u3a8b\0\u3a90\u3a9b\0\0\u3a9d\u3aa8\u3aab\u3aaf\0\0\u3ac3\u3ace\0\u3ad8\u17dc\u17dftr\xe9\u17d1r;\uc000\ud835\udd35\u0100Aa\u3a94\u3a97r\xf2\u03c3r\xf2\u09f6;\u43be\u0100Aa\u3aa1\u3aa4r\xf2\u03b8r\xf2\u09eba\xf0\u2713is;\u62fb\u0180dpt\u17a4\u3ab5\u3abe\u0100fl\u3aba\u17a9;\uc000\ud835\udd69im\xe5\u17b2\u0100Aa\u3ac7\u3acar\xf2\u03cer\xf2\u0a01\u0100cq\u3ad2\u17b8r;\uc000\ud835\udccd\u0100pt\u17d6\u3adcr\xe9\u17d4\u0400acefiosu\u3af0\u3afd\u3b08\u3b0c\u3b11\u3b15\u3b1b\u3b21c\u0100uy\u3af6\u3afbte\u803b\xfd\u40fd;\u444f\u0100iy\u3b02\u3b06rc;\u4177;\u444bn\u803b\xa5\u40a5r;\uc000\ud835\udd36cy;\u4457pf;\uc000\ud835\udd6acr;\uc000\ud835\udcce\u0100cm\u3b26\u3b29y;\u444el\u803b\xff\u40ff\u0500acdefhiosw\u3b42\u3b48\u3b54\u3b58\u3b64\u3b69\u3b6d\u3b74\u3b7a\u3b80cute;\u417a\u0100ay\u3b4d\u3b52ron;\u417e;\u4437ot;\u417c\u0100et\u3b5d\u3b61tr\xe6\u155fa;\u43b6r;\uc000\ud835\udd37cy;\u4436grarr;\u61ddpf;\uc000\ud835\udd6bcr;\uc000\ud835\udccf\u0100jn\u3b85\u3b87;\u600dj;\u600c"
    .split("")
    .map((c) => c.charCodeAt(0)),
);

// Adapted from https://github.com/mathiasbynens/he/blob/36afe179392226cf1b6ccdb16ebbb7a5a844d93a/src/he.js#L106-L134
const decodeMap = new Map([
	[0, 65533],
	// C1 Unicode control character reference replacements
	[128, 8364],
	[130, 8218],
	[131, 402],
	[132, 8222],
	[133, 8230],
	[134, 8224],
	[135, 8225],
	[136, 710],
	[137, 8240],
	[138, 352],
	[139, 8249],
	[140, 338],
	[142, 381],
	[145, 8216],
	[146, 8217],
	[147, 8220],
	[148, 8221],
	[149, 8226],
	[150, 8211],
	[151, 8212],
	[152, 732],
	[153, 8482],
	[154, 353],
	[155, 8250],
	[156, 339],
	[158, 382],
	[159, 376],
]);
/**
 * Replace the given code point with a replacement character if it is a
 * surrogate or is outside the valid range. Otherwise return the code
 * point unchanged.
 */
function replaceCodePoint(codePoint) {
	var _a;
	if ((codePoint >= 55296 && codePoint <= 57343) || codePoint > 1114111) {
		return 65533;
	}
	return (_a = decodeMap.get(codePoint)) !== null && _a !== void 0 ? _a : codePoint;
}

var CharCodes;
(function (CharCodes) {
	CharCodes[(CharCodes["NUM"] = 35)] = "NUM";
	CharCodes[(CharCodes["SEMI"] = 59)] = "SEMI";
	CharCodes[(CharCodes["EQUALS"] = 61)] = "EQUALS";
	CharCodes[(CharCodes["ZERO"] = 48)] = "ZERO";
	CharCodes[(CharCodes["NINE"] = 57)] = "NINE";
	CharCodes[(CharCodes["LOWER_A"] = 97)] = "LOWER_A";
	CharCodes[(CharCodes["LOWER_F"] = 102)] = "LOWER_F";
	CharCodes[(CharCodes["LOWER_X"] = 120)] = "LOWER_X";
	CharCodes[(CharCodes["LOWER_Z"] = 122)] = "LOWER_Z";
	CharCodes[(CharCodes["UPPER_A"] = 65)] = "UPPER_A";
	CharCodes[(CharCodes["UPPER_F"] = 70)] = "UPPER_F";
	CharCodes[(CharCodes["UPPER_Z"] = 90)] = "UPPER_Z";
})(CharCodes || (CharCodes = {}));
/** Bit that needs to be set to convert an upper case ASCII character to lower case */
const TO_LOWER_BIT = 32;
var BinTrieFlags;
(function (BinTrieFlags) {
	BinTrieFlags[(BinTrieFlags["VALUE_LENGTH"] = 49152)] = "VALUE_LENGTH";
	BinTrieFlags[(BinTrieFlags["BRANCH_LENGTH"] = 16256)] = "BRANCH_LENGTH";
	BinTrieFlags[(BinTrieFlags["JUMP_TABLE"] = 127)] = "JUMP_TABLE";
})(BinTrieFlags || (BinTrieFlags = {}));
function isNumber$1(code) {
	return code >= CharCodes.ZERO && code <= CharCodes.NINE;
}
function isHexadecimalCharacter(code) {
	return (code >= CharCodes.UPPER_A && code <= CharCodes.UPPER_F) || (code >= CharCodes.LOWER_A && code <= CharCodes.LOWER_F);
}
function isAsciiAlphaNumeric$1(code) {
	return (code >= CharCodes.UPPER_A && code <= CharCodes.UPPER_Z) || (code >= CharCodes.LOWER_A && code <= CharCodes.LOWER_Z) || isNumber$1(code);
}
/**
 * Checks if the given character is a valid end character for an entity in an attribute.
 *
 * Attribute values that aren't terminated properly aren't parsed, and shouldn't lead to a parser error.
 * See the example in https://html.spec.whatwg.org/multipage/parsing.html#named-character-reference-state
 */
function isEntityInAttributeInvalidEnd(code) {
	return code === CharCodes.EQUALS || isAsciiAlphaNumeric$1(code);
}
var EntityDecoderState;
(function (EntityDecoderState) {
	EntityDecoderState[(EntityDecoderState["EntityStart"] = 0)] = "EntityStart";
	EntityDecoderState[(EntityDecoderState["NumericStart"] = 1)] = "NumericStart";
	EntityDecoderState[(EntityDecoderState["NumericDecimal"] = 2)] = "NumericDecimal";
	EntityDecoderState[(EntityDecoderState["NumericHex"] = 3)] = "NumericHex";
	EntityDecoderState[(EntityDecoderState["NamedEntity"] = 4)] = "NamedEntity";
})(EntityDecoderState || (EntityDecoderState = {}));
var DecodingMode;
(function (DecodingMode) {
	/** Entities in text nodes that can end with any character. */
	DecodingMode[(DecodingMode["Legacy"] = 0)] = "Legacy";
	/** Only allow entities terminated with a semicolon. */
	DecodingMode[(DecodingMode["Strict"] = 1)] = "Strict";
	/** Entities in attributes have limitations on ending characters. */
	DecodingMode[(DecodingMode["Attribute"] = 2)] = "Attribute";
})(DecodingMode || (DecodingMode = {}));
/**
 * Token decoder with support of writing partial entities.
 */
class EntityDecoder {
	constructor(
		/** The tree used to decode entities. */
		decodeTree,
		/**
		 * The function that is called when a codepoint is decoded.
		 *
		 * For multi-byte named entities, this will be called multiple times,
		 * with the second codepoint, and the same `consumed` value.
		 *
		 * @param codepoint The decoded codepoint.
		 * @param consumed The number of bytes consumed by the decoder.
		 */
		emitCodePoint,
		/** An object that is used to produce errors. */
		errors,
	) {
		this.decodeTree = decodeTree;
		this.emitCodePoint = emitCodePoint;
		this.errors = errors;
		/** The current state of the decoder. */
		this.state = EntityDecoderState.EntityStart;
		/** Characters that were consumed while parsing an entity. */
		this.consumed = 1;
		/**
		 * The result of the entity.
		 *
		 * Either the result index of a numeric entity, or the codepoint of a
		 * numeric entity.
		 */
		this.result = 0;
		/** The current index in the decode tree. */
		this.treeIndex = 0;
		/** The number of characters that were consumed in excess. */
		this.excess = 1;
		/** The mode in which the decoder is operating. */
		this.decodeMode = DecodingMode.Strict;
	}
	/** Resets the instance to make it reusable. */
	startEntity(decodeMode) {
		this.decodeMode = decodeMode;
		this.state = EntityDecoderState.EntityStart;
		this.result = 0;
		this.treeIndex = 0;
		this.excess = 1;
		this.consumed = 1;
	}
	/**
	 * Write an entity to the decoder. This can be called multiple times with partial entities.
	 * If the entity is incomplete, the decoder will return -1.
	 *
	 * Mirrors the implementation of `getDecoder`, but with the ability to stop decoding if the
	 * entity is incomplete, and resume when the next string is written.
	 *
	 * @param input The string containing the entity (or a continuation of the entity).
	 * @param offset The offset at which the entity begins. Should be 0 if this is not the first call.
	 * @returns The number of characters that were consumed, or -1 if the entity is incomplete.
	 */
	write(input, offset) {
		switch (this.state) {
			case EntityDecoderState.EntityStart: {
				if (input.charCodeAt(offset) === CharCodes.NUM) {
					this.state = EntityDecoderState.NumericStart;
					this.consumed += 1;
					return this.stateNumericStart(input, offset + 1);
				}
				this.state = EntityDecoderState.NamedEntity;
				return this.stateNamedEntity(input, offset);
			}
			case EntityDecoderState.NumericStart: {
				return this.stateNumericStart(input, offset);
			}
			case EntityDecoderState.NumericDecimal: {
				return this.stateNumericDecimal(input, offset);
			}
			case EntityDecoderState.NumericHex: {
				return this.stateNumericHex(input, offset);
			}
			case EntityDecoderState.NamedEntity: {
				return this.stateNamedEntity(input, offset);
			}
		}
	}
	/**
	 * Switches between the numeric decimal and hexadecimal states.
	 *
	 * Equivalent to the `Numeric character reference state` in the HTML spec.
	 *
	 * @param input The string containing the entity (or a continuation of the entity).
	 * @param offset The current offset.
	 * @returns The number of characters that were consumed, or -1 if the entity is incomplete.
	 */
	stateNumericStart(input, offset) {
		if (offset >= input.length) {
			return -1;
		}
		if ((input.charCodeAt(offset) | TO_LOWER_BIT) === CharCodes.LOWER_X) {
			this.state = EntityDecoderState.NumericHex;
			this.consumed += 1;
			return this.stateNumericHex(input, offset + 1);
		}
		this.state = EntityDecoderState.NumericDecimal;
		return this.stateNumericDecimal(input, offset);
	}
	addToNumericResult(input, start, end, base) {
		if (start !== end) {
			const digitCount = end - start;
			this.result = this.result * Math.pow(base, digitCount) + Number.parseInt(input.substr(start, digitCount), base);
			this.consumed += digitCount;
		}
	}
	/**
	 * Parses a hexadecimal numeric entity.
	 *
	 * Equivalent to the `Hexademical character reference state` in the HTML spec.
	 *
	 * @param input The string containing the entity (or a continuation of the entity).
	 * @param offset The current offset.
	 * @returns The number of characters that were consumed, or -1 if the entity is incomplete.
	 */
	stateNumericHex(input, offset) {
		const startIndex = offset;
		while (offset < input.length) {
			const char = input.charCodeAt(offset);
			if (isNumber$1(char) || isHexadecimalCharacter(char)) {
				offset += 1;
			} else {
				this.addToNumericResult(input, startIndex, offset, 16);
				return this.emitNumericEntity(char, 3);
			}
		}
		this.addToNumericResult(input, startIndex, offset, 16);
		return -1;
	}
	/**
	 * Parses a decimal numeric entity.
	 *
	 * Equivalent to the `Decimal character reference state` in the HTML spec.
	 *
	 * @param input The string containing the entity (or a continuation of the entity).
	 * @param offset The current offset.
	 * @returns The number of characters that were consumed, or -1 if the entity is incomplete.
	 */
	stateNumericDecimal(input, offset) {
		const startIndex = offset;
		while (offset < input.length) {
			const char = input.charCodeAt(offset);
			if (isNumber$1(char)) {
				offset += 1;
			} else {
				this.addToNumericResult(input, startIndex, offset, 10);
				return this.emitNumericEntity(char, 2);
			}
		}
		this.addToNumericResult(input, startIndex, offset, 10);
		return -1;
	}
	/**
	 * Validate and emit a numeric entity.
	 *
	 * Implements the logic from the `Hexademical character reference start
	 * state` and `Numeric character reference end state` in the HTML spec.
	 *
	 * @param lastCp The last code point of the entity. Used to see if the
	 *               entity was terminated with a semicolon.
	 * @param expectedLength The minimum number of characters that should be
	 *                       consumed. Used to validate that at least one digit
	 *                       was consumed.
	 * @returns The number of characters that were consumed.
	 */
	emitNumericEntity(lastCp, expectedLength) {
		var _a;
		// Ensure we consumed at least one digit.
		if (this.consumed <= expectedLength) {
			(_a = this.errors) === null || _a === void 0 ? void 0 : _a.absenceOfDigitsInNumericCharacterReference(this.consumed);
			return 0;
		}
		// Figure out if this is a legit end of the entity
		if (lastCp === CharCodes.SEMI) {
			this.consumed += 1;
		} else if (this.decodeMode === DecodingMode.Strict) {
			return 0;
		}
		this.emitCodePoint(replaceCodePoint(this.result), this.consumed);
		if (this.errors) {
			if (lastCp !== CharCodes.SEMI) {
				this.errors.missingSemicolonAfterCharacterReference();
			}
			this.errors.validateNumericCharacterReference(this.result);
		}
		return this.consumed;
	}
	/**
	 * Parses a named entity.
	 *
	 * Equivalent to the `Named character reference state` in the HTML spec.
	 *
	 * @param input The string containing the entity (or a continuation of the entity).
	 * @param offset The current offset.
	 * @returns The number of characters that were consumed, or -1 if the entity is incomplete.
	 */
	stateNamedEntity(input, offset) {
		const { decodeTree } = this;
		let current = decodeTree[this.treeIndex];
		// The mask is the number of bytes of the value, including the current byte.
		let valueLength = (current & BinTrieFlags.VALUE_LENGTH) >> 14;
		for (; offset < input.length; offset++, this.excess++) {
			const char = input.charCodeAt(offset);
			this.treeIndex = determineBranch(decodeTree, current, this.treeIndex + Math.max(1, valueLength), char);
			if (this.treeIndex < 0) {
				return (
						this.result === 0 ||
							// If we are parsing an attribute
							(this.decodeMode === DecodingMode.Attribute &&
								// We shouldn't have consumed any characters after the entity,
								(valueLength === 0 ||
									// And there should be no invalid characters.
									isEntityInAttributeInvalidEnd(char)))
					) ?
						0
					:	this.emitNotTerminatedNamedEntity();
			}
			current = decodeTree[this.treeIndex];
			valueLength = (current & BinTrieFlags.VALUE_LENGTH) >> 14;
			// If the branch is a value, store it and continue
			if (valueLength !== 0) {
				// If the entity is terminated by a semicolon, we are done.
				if (char === CharCodes.SEMI) {
					return this.emitNamedEntityData(this.treeIndex, valueLength, this.consumed + this.excess);
				}
				// If we encounter a non-terminated (legacy) entity while parsing strictly, then ignore it.
				if (this.decodeMode !== DecodingMode.Strict) {
					this.result = this.treeIndex;
					this.consumed += this.excess;
					this.excess = 0;
				}
			}
		}
		return -1;
	}
	/**
	 * Emit a named entity that was not terminated with a semicolon.
	 *
	 * @returns The number of characters consumed.
	 */
	emitNotTerminatedNamedEntity() {
		var _a;
		const { result, decodeTree } = this;
		const valueLength = (decodeTree[result] & BinTrieFlags.VALUE_LENGTH) >> 14;
		this.emitNamedEntityData(result, valueLength, this.consumed);
		(_a = this.errors) === null || _a === void 0 ? void 0 : _a.missingSemicolonAfterCharacterReference();
		return this.consumed;
	}
	/**
	 * Emit a named entity.
	 *
	 * @param result The index of the entity in the decode tree.
	 * @param valueLength The number of bytes in the entity.
	 * @param consumed The number of characters consumed.
	 *
	 * @returns The number of characters consumed.
	 */
	emitNamedEntityData(result, valueLength, consumed) {
		const { decodeTree } = this;
		this.emitCodePoint(valueLength === 1 ? decodeTree[result] & ~BinTrieFlags.VALUE_LENGTH : decodeTree[result + 1], consumed);
		if (valueLength === 3) {
			// For multi-byte values, we need to emit the second byte.
			this.emitCodePoint(decodeTree[result + 2], consumed);
		}
		return consumed;
	}
	/**
	 * Signal to the parser that the end of the input was reached.
	 *
	 * Remaining data will be emitted and relevant errors will be produced.
	 *
	 * @returns The number of characters consumed.
	 */
	end() {
		var _a;
		switch (this.state) {
			case EntityDecoderState.NamedEntity: {
				// Emit a named entity if we have one.
				return this.result !== 0 && (this.decodeMode !== DecodingMode.Attribute || this.result === this.treeIndex) ? this.emitNotTerminatedNamedEntity() : 0;
			}
			// Otherwise, emit a numeric entity if we have one.
			case EntityDecoderState.NumericDecimal: {
				return this.emitNumericEntity(0, 2);
			}
			case EntityDecoderState.NumericHex: {
				return this.emitNumericEntity(0, 3);
			}
			case EntityDecoderState.NumericStart: {
				(_a = this.errors) === null || _a === void 0 ? void 0 : _a.absenceOfDigitsInNumericCharacterReference(this.consumed);
				return 0;
			}
			case EntityDecoderState.EntityStart: {
				// Return 0 if we have no entity.
				return 0;
			}
		}
	}
}
/**
 * Determines the branch of the current node that is taken given the current
 * character. This function is used to traverse the trie.
 *
 * @param decodeTree The trie.
 * @param current The current node.
 * @param nodeIdx The index right after the current node and its value.
 * @param char The current character.
 * @returns The index of the next node, or -1 if no branch is taken.
 */
function determineBranch(decodeTree, current, nodeIndex, char) {
	const branchCount = (current & BinTrieFlags.BRANCH_LENGTH) >> 7;
	const jumpOffset = current & BinTrieFlags.JUMP_TABLE;
	// Case 1: Single branch encoded in jump offset
	if (branchCount === 0) {
		return jumpOffset !== 0 && char === jumpOffset ? nodeIndex : -1;
	}
	// Case 2: Multiple branches encoded in jump table
	if (jumpOffset) {
		const value = char - jumpOffset;
		return value < 0 || value >= branchCount ? -1 : decodeTree[nodeIndex + value] - 1;
	}
	// Case 3: Multiple branches encoded in dictionary
	// Binary search for the character.
	let lo = nodeIndex;
	let hi = lo + branchCount - 1;
	while (lo <= hi) {
		const mid = (lo + hi) >>> 1;
		const midValue = decodeTree[mid];
		if (midValue < char) {
			lo = mid + 1;
		} else if (midValue > char) {
			hi = mid - 1;
		} else {
			return decodeTree[mid + branchCount];
		}
	}
	return -1;
}

/** All valid namespaces in HTML. */
var NS;
(function (NS) {
	NS["HTML"] = "http://www.w3.org/1999/xhtml";
	NS["MATHML"] = "http://www.w3.org/1998/Math/MathML";
	NS["SVG"] = "http://www.w3.org/2000/svg";
	NS["XLINK"] = "http://www.w3.org/1999/xlink";
	NS["XML"] = "http://www.w3.org/XML/1998/namespace";
	NS["XMLNS"] = "http://www.w3.org/2000/xmlns/";
})(NS || (NS = {}));
var ATTRS;
(function (ATTRS) {
	ATTRS["TYPE"] = "type";
	ATTRS["ACTION"] = "action";
	ATTRS["ENCODING"] = "encoding";
	ATTRS["PROMPT"] = "prompt";
	ATTRS["NAME"] = "name";
	ATTRS["COLOR"] = "color";
	ATTRS["FACE"] = "face";
	ATTRS["SIZE"] = "size";
})(ATTRS || (ATTRS = {}));
/**
 * The mode of the document.
 *
 * @see {@link https://dom.spec.whatwg.org/#concept-document-limited-quirks}
 */
var DOCUMENT_MODE;
(function (DOCUMENT_MODE) {
	DOCUMENT_MODE["NO_QUIRKS"] = "no-quirks";
	DOCUMENT_MODE["QUIRKS"] = "quirks";
	DOCUMENT_MODE["LIMITED_QUIRKS"] = "limited-quirks";
})(DOCUMENT_MODE || (DOCUMENT_MODE = {}));
var TAG_NAMES;
(function (TAG_NAMES) {
	TAG_NAMES["A"] = "a";
	TAG_NAMES["ADDRESS"] = "address";
	TAG_NAMES["ANNOTATION_XML"] = "annotation-xml";
	TAG_NAMES["APPLET"] = "applet";
	TAG_NAMES["AREA"] = "area";
	TAG_NAMES["ARTICLE"] = "article";
	TAG_NAMES["ASIDE"] = "aside";
	TAG_NAMES["B"] = "b";
	TAG_NAMES["BASE"] = "base";
	TAG_NAMES["BASEFONT"] = "basefont";
	TAG_NAMES["BGSOUND"] = "bgsound";
	TAG_NAMES["BIG"] = "big";
	TAG_NAMES["BLOCKQUOTE"] = "blockquote";
	TAG_NAMES["BODY"] = "body";
	TAG_NAMES["BR"] = "br";
	TAG_NAMES["BUTTON"] = "button";
	TAG_NAMES["CAPTION"] = "caption";
	TAG_NAMES["CENTER"] = "center";
	TAG_NAMES["CODE"] = "code";
	TAG_NAMES["COL"] = "col";
	TAG_NAMES["COLGROUP"] = "colgroup";
	TAG_NAMES["DD"] = "dd";
	TAG_NAMES["DESC"] = "desc";
	TAG_NAMES["DETAILS"] = "details";
	TAG_NAMES["DIALOG"] = "dialog";
	TAG_NAMES["DIR"] = "dir";
	TAG_NAMES["DIV"] = "div";
	TAG_NAMES["DL"] = "dl";
	TAG_NAMES["DT"] = "dt";
	TAG_NAMES["EM"] = "em";
	TAG_NAMES["EMBED"] = "embed";
	TAG_NAMES["FIELDSET"] = "fieldset";
	TAG_NAMES["FIGCAPTION"] = "figcaption";
	TAG_NAMES["FIGURE"] = "figure";
	TAG_NAMES["FONT"] = "font";
	TAG_NAMES["FOOTER"] = "footer";
	TAG_NAMES["FOREIGN_OBJECT"] = "foreignObject";
	TAG_NAMES["FORM"] = "form";
	TAG_NAMES["FRAME"] = "frame";
	TAG_NAMES["FRAMESET"] = "frameset";
	TAG_NAMES["H1"] = "h1";
	TAG_NAMES["H2"] = "h2";
	TAG_NAMES["H3"] = "h3";
	TAG_NAMES["H4"] = "h4";
	TAG_NAMES["H5"] = "h5";
	TAG_NAMES["H6"] = "h6";
	TAG_NAMES["HEAD"] = "head";
	TAG_NAMES["HEADER"] = "header";
	TAG_NAMES["HGROUP"] = "hgroup";
	TAG_NAMES["HR"] = "hr";
	TAG_NAMES["HTML"] = "html";
	TAG_NAMES["I"] = "i";
	TAG_NAMES["IMG"] = "img";
	TAG_NAMES["IMAGE"] = "image";
	TAG_NAMES["INPUT"] = "input";
	TAG_NAMES["IFRAME"] = "iframe";
	TAG_NAMES["KEYGEN"] = "keygen";
	TAG_NAMES["LABEL"] = "label";
	TAG_NAMES["LI"] = "li";
	TAG_NAMES["LINK"] = "link";
	TAG_NAMES["LISTING"] = "listing";
	TAG_NAMES["MAIN"] = "main";
	TAG_NAMES["MALIGNMARK"] = "malignmark";
	TAG_NAMES["MARQUEE"] = "marquee";
	TAG_NAMES["MATH"] = "math";
	TAG_NAMES["MENU"] = "menu";
	TAG_NAMES["META"] = "meta";
	TAG_NAMES["MGLYPH"] = "mglyph";
	TAG_NAMES["MI"] = "mi";
	TAG_NAMES["MO"] = "mo";
	TAG_NAMES["MN"] = "mn";
	TAG_NAMES["MS"] = "ms";
	TAG_NAMES["MTEXT"] = "mtext";
	TAG_NAMES["NAV"] = "nav";
	TAG_NAMES["NOBR"] = "nobr";
	TAG_NAMES["NOFRAMES"] = "noframes";
	TAG_NAMES["NOEMBED"] = "noembed";
	TAG_NAMES["NOSCRIPT"] = "noscript";
	TAG_NAMES["OBJECT"] = "object";
	TAG_NAMES["OL"] = "ol";
	TAG_NAMES["OPTGROUP"] = "optgroup";
	TAG_NAMES["OPTION"] = "option";
	TAG_NAMES["P"] = "p";
	TAG_NAMES["PARAM"] = "param";
	TAG_NAMES["PLAINTEXT"] = "plaintext";
	TAG_NAMES["PRE"] = "pre";
	TAG_NAMES["RB"] = "rb";
	TAG_NAMES["RP"] = "rp";
	TAG_NAMES["RT"] = "rt";
	TAG_NAMES["RTC"] = "rtc";
	TAG_NAMES["RUBY"] = "ruby";
	TAG_NAMES["S"] = "s";
	TAG_NAMES["SCRIPT"] = "script";
	TAG_NAMES["SEARCH"] = "search";
	TAG_NAMES["SECTION"] = "section";
	TAG_NAMES["SELECT"] = "select";
	TAG_NAMES["SOURCE"] = "source";
	TAG_NAMES["SMALL"] = "small";
	TAG_NAMES["SPAN"] = "span";
	TAG_NAMES["STRIKE"] = "strike";
	TAG_NAMES["STRONG"] = "strong";
	TAG_NAMES["STYLE"] = "style";
	TAG_NAMES["SUB"] = "sub";
	TAG_NAMES["SUMMARY"] = "summary";
	TAG_NAMES["SUP"] = "sup";
	TAG_NAMES["TABLE"] = "table";
	TAG_NAMES["TBODY"] = "tbody";
	TAG_NAMES["TEMPLATE"] = "template";
	TAG_NAMES["TEXTAREA"] = "textarea";
	TAG_NAMES["TFOOT"] = "tfoot";
	TAG_NAMES["TD"] = "td";
	TAG_NAMES["TH"] = "th";
	TAG_NAMES["THEAD"] = "thead";
	TAG_NAMES["TITLE"] = "title";
	TAG_NAMES["TR"] = "tr";
	TAG_NAMES["TRACK"] = "track";
	TAG_NAMES["TT"] = "tt";
	TAG_NAMES["U"] = "u";
	TAG_NAMES["UL"] = "ul";
	TAG_NAMES["SVG"] = "svg";
	TAG_NAMES["VAR"] = "var";
	TAG_NAMES["WBR"] = "wbr";
	TAG_NAMES["XMP"] = "xmp";
})(TAG_NAMES || (TAG_NAMES = {}));
/**
 * Tag IDs are numeric IDs for known tag names.
 *
 * We use tag IDs to improve the performance of tag name comparisons.
 */
var TAG_ID;
(function (TAG_ID) {
	TAG_ID[(TAG_ID["UNKNOWN"] = 0)] = "UNKNOWN";
	TAG_ID[(TAG_ID["A"] = 1)] = "A";
	TAG_ID[(TAG_ID["ADDRESS"] = 2)] = "ADDRESS";
	TAG_ID[(TAG_ID["ANNOTATION_XML"] = 3)] = "ANNOTATION_XML";
	TAG_ID[(TAG_ID["APPLET"] = 4)] = "APPLET";
	TAG_ID[(TAG_ID["AREA"] = 5)] = "AREA";
	TAG_ID[(TAG_ID["ARTICLE"] = 6)] = "ARTICLE";
	TAG_ID[(TAG_ID["ASIDE"] = 7)] = "ASIDE";
	TAG_ID[(TAG_ID["B"] = 8)] = "B";
	TAG_ID[(TAG_ID["BASE"] = 9)] = "BASE";
	TAG_ID[(TAG_ID["BASEFONT"] = 10)] = "BASEFONT";
	TAG_ID[(TAG_ID["BGSOUND"] = 11)] = "BGSOUND";
	TAG_ID[(TAG_ID["BIG"] = 12)] = "BIG";
	TAG_ID[(TAG_ID["BLOCKQUOTE"] = 13)] = "BLOCKQUOTE";
	TAG_ID[(TAG_ID["BODY"] = 14)] = "BODY";
	TAG_ID[(TAG_ID["BR"] = 15)] = "BR";
	TAG_ID[(TAG_ID["BUTTON"] = 16)] = "BUTTON";
	TAG_ID[(TAG_ID["CAPTION"] = 17)] = "CAPTION";
	TAG_ID[(TAG_ID["CENTER"] = 18)] = "CENTER";
	TAG_ID[(TAG_ID["CODE"] = 19)] = "CODE";
	TAG_ID[(TAG_ID["COL"] = 20)] = "COL";
	TAG_ID[(TAG_ID["COLGROUP"] = 21)] = "COLGROUP";
	TAG_ID[(TAG_ID["DD"] = 22)] = "DD";
	TAG_ID[(TAG_ID["DESC"] = 23)] = "DESC";
	TAG_ID[(TAG_ID["DETAILS"] = 24)] = "DETAILS";
	TAG_ID[(TAG_ID["DIALOG"] = 25)] = "DIALOG";
	TAG_ID[(TAG_ID["DIR"] = 26)] = "DIR";
	TAG_ID[(TAG_ID["DIV"] = 27)] = "DIV";
	TAG_ID[(TAG_ID["DL"] = 28)] = "DL";
	TAG_ID[(TAG_ID["DT"] = 29)] = "DT";
	TAG_ID[(TAG_ID["EM"] = 30)] = "EM";
	TAG_ID[(TAG_ID["EMBED"] = 31)] = "EMBED";
	TAG_ID[(TAG_ID["FIELDSET"] = 32)] = "FIELDSET";
	TAG_ID[(TAG_ID["FIGCAPTION"] = 33)] = "FIGCAPTION";
	TAG_ID[(TAG_ID["FIGURE"] = 34)] = "FIGURE";
	TAG_ID[(TAG_ID["FONT"] = 35)] = "FONT";
	TAG_ID[(TAG_ID["FOOTER"] = 36)] = "FOOTER";
	TAG_ID[(TAG_ID["FOREIGN_OBJECT"] = 37)] = "FOREIGN_OBJECT";
	TAG_ID[(TAG_ID["FORM"] = 38)] = "FORM";
	TAG_ID[(TAG_ID["FRAME"] = 39)] = "FRAME";
	TAG_ID[(TAG_ID["FRAMESET"] = 40)] = "FRAMESET";
	TAG_ID[(TAG_ID["H1"] = 41)] = "H1";
	TAG_ID[(TAG_ID["H2"] = 42)] = "H2";
	TAG_ID[(TAG_ID["H3"] = 43)] = "H3";
	TAG_ID[(TAG_ID["H4"] = 44)] = "H4";
	TAG_ID[(TAG_ID["H5"] = 45)] = "H5";
	TAG_ID[(TAG_ID["H6"] = 46)] = "H6";
	TAG_ID[(TAG_ID["HEAD"] = 47)] = "HEAD";
	TAG_ID[(TAG_ID["HEADER"] = 48)] = "HEADER";
	TAG_ID[(TAG_ID["HGROUP"] = 49)] = "HGROUP";
	TAG_ID[(TAG_ID["HR"] = 50)] = "HR";
	TAG_ID[(TAG_ID["HTML"] = 51)] = "HTML";
	TAG_ID[(TAG_ID["I"] = 52)] = "I";
	TAG_ID[(TAG_ID["IMG"] = 53)] = "IMG";
	TAG_ID[(TAG_ID["IMAGE"] = 54)] = "IMAGE";
	TAG_ID[(TAG_ID["INPUT"] = 55)] = "INPUT";
	TAG_ID[(TAG_ID["IFRAME"] = 56)] = "IFRAME";
	TAG_ID[(TAG_ID["KEYGEN"] = 57)] = "KEYGEN";
	TAG_ID[(TAG_ID["LABEL"] = 58)] = "LABEL";
	TAG_ID[(TAG_ID["LI"] = 59)] = "LI";
	TAG_ID[(TAG_ID["LINK"] = 60)] = "LINK";
	TAG_ID[(TAG_ID["LISTING"] = 61)] = "LISTING";
	TAG_ID[(TAG_ID["MAIN"] = 62)] = "MAIN";
	TAG_ID[(TAG_ID["MALIGNMARK"] = 63)] = "MALIGNMARK";
	TAG_ID[(TAG_ID["MARQUEE"] = 64)] = "MARQUEE";
	TAG_ID[(TAG_ID["MATH"] = 65)] = "MATH";
	TAG_ID[(TAG_ID["MENU"] = 66)] = "MENU";
	TAG_ID[(TAG_ID["META"] = 67)] = "META";
	TAG_ID[(TAG_ID["MGLYPH"] = 68)] = "MGLYPH";
	TAG_ID[(TAG_ID["MI"] = 69)] = "MI";
	TAG_ID[(TAG_ID["MO"] = 70)] = "MO";
	TAG_ID[(TAG_ID["MN"] = 71)] = "MN";
	TAG_ID[(TAG_ID["MS"] = 72)] = "MS";
	TAG_ID[(TAG_ID["MTEXT"] = 73)] = "MTEXT";
	TAG_ID[(TAG_ID["NAV"] = 74)] = "NAV";
	TAG_ID[(TAG_ID["NOBR"] = 75)] = "NOBR";
	TAG_ID[(TAG_ID["NOFRAMES"] = 76)] = "NOFRAMES";
	TAG_ID[(TAG_ID["NOEMBED"] = 77)] = "NOEMBED";
	TAG_ID[(TAG_ID["NOSCRIPT"] = 78)] = "NOSCRIPT";
	TAG_ID[(TAG_ID["OBJECT"] = 79)] = "OBJECT";
	TAG_ID[(TAG_ID["OL"] = 80)] = "OL";
	TAG_ID[(TAG_ID["OPTGROUP"] = 81)] = "OPTGROUP";
	TAG_ID[(TAG_ID["OPTION"] = 82)] = "OPTION";
	TAG_ID[(TAG_ID["P"] = 83)] = "P";
	TAG_ID[(TAG_ID["PARAM"] = 84)] = "PARAM";
	TAG_ID[(TAG_ID["PLAINTEXT"] = 85)] = "PLAINTEXT";
	TAG_ID[(TAG_ID["PRE"] = 86)] = "PRE";
	TAG_ID[(TAG_ID["RB"] = 87)] = "RB";
	TAG_ID[(TAG_ID["RP"] = 88)] = "RP";
	TAG_ID[(TAG_ID["RT"] = 89)] = "RT";
	TAG_ID[(TAG_ID["RTC"] = 90)] = "RTC";
	TAG_ID[(TAG_ID["RUBY"] = 91)] = "RUBY";
	TAG_ID[(TAG_ID["S"] = 92)] = "S";
	TAG_ID[(TAG_ID["SCRIPT"] = 93)] = "SCRIPT";
	TAG_ID[(TAG_ID["SEARCH"] = 94)] = "SEARCH";
	TAG_ID[(TAG_ID["SECTION"] = 95)] = "SECTION";
	TAG_ID[(TAG_ID["SELECT"] = 96)] = "SELECT";
	TAG_ID[(TAG_ID["SOURCE"] = 97)] = "SOURCE";
	TAG_ID[(TAG_ID["SMALL"] = 98)] = "SMALL";
	TAG_ID[(TAG_ID["SPAN"] = 99)] = "SPAN";
	TAG_ID[(TAG_ID["STRIKE"] = 100)] = "STRIKE";
	TAG_ID[(TAG_ID["STRONG"] = 101)] = "STRONG";
	TAG_ID[(TAG_ID["STYLE"] = 102)] = "STYLE";
	TAG_ID[(TAG_ID["SUB"] = 103)] = "SUB";
	TAG_ID[(TAG_ID["SUMMARY"] = 104)] = "SUMMARY";
	TAG_ID[(TAG_ID["SUP"] = 105)] = "SUP";
	TAG_ID[(TAG_ID["TABLE"] = 106)] = "TABLE";
	TAG_ID[(TAG_ID["TBODY"] = 107)] = "TBODY";
	TAG_ID[(TAG_ID["TEMPLATE"] = 108)] = "TEMPLATE";
	TAG_ID[(TAG_ID["TEXTAREA"] = 109)] = "TEXTAREA";
	TAG_ID[(TAG_ID["TFOOT"] = 110)] = "TFOOT";
	TAG_ID[(TAG_ID["TD"] = 111)] = "TD";
	TAG_ID[(TAG_ID["TH"] = 112)] = "TH";
	TAG_ID[(TAG_ID["THEAD"] = 113)] = "THEAD";
	TAG_ID[(TAG_ID["TITLE"] = 114)] = "TITLE";
	TAG_ID[(TAG_ID["TR"] = 115)] = "TR";
	TAG_ID[(TAG_ID["TRACK"] = 116)] = "TRACK";
	TAG_ID[(TAG_ID["TT"] = 117)] = "TT";
	TAG_ID[(TAG_ID["U"] = 118)] = "U";
	TAG_ID[(TAG_ID["UL"] = 119)] = "UL";
	TAG_ID[(TAG_ID["SVG"] = 120)] = "SVG";
	TAG_ID[(TAG_ID["VAR"] = 121)] = "VAR";
	TAG_ID[(TAG_ID["WBR"] = 122)] = "WBR";
	TAG_ID[(TAG_ID["XMP"] = 123)] = "XMP";
})(TAG_ID || (TAG_ID = {}));
const TAG_NAME_TO_ID = new Map([
	[TAG_NAMES.A, TAG_ID.A],
	[TAG_NAMES.ADDRESS, TAG_ID.ADDRESS],
	[TAG_NAMES.ANNOTATION_XML, TAG_ID.ANNOTATION_XML],
	[TAG_NAMES.APPLET, TAG_ID.APPLET],
	[TAG_NAMES.AREA, TAG_ID.AREA],
	[TAG_NAMES.ARTICLE, TAG_ID.ARTICLE],
	[TAG_NAMES.ASIDE, TAG_ID.ASIDE],
	[TAG_NAMES.B, TAG_ID.B],
	[TAG_NAMES.BASE, TAG_ID.BASE],
	[TAG_NAMES.BASEFONT, TAG_ID.BASEFONT],
	[TAG_NAMES.BGSOUND, TAG_ID.BGSOUND],
	[TAG_NAMES.BIG, TAG_ID.BIG],
	[TAG_NAMES.BLOCKQUOTE, TAG_ID.BLOCKQUOTE],
	[TAG_NAMES.BODY, TAG_ID.BODY],
	[TAG_NAMES.BR, TAG_ID.BR],
	[TAG_NAMES.BUTTON, TAG_ID.BUTTON],
	[TAG_NAMES.CAPTION, TAG_ID.CAPTION],
	[TAG_NAMES.CENTER, TAG_ID.CENTER],
	[TAG_NAMES.CODE, TAG_ID.CODE],
	[TAG_NAMES.COL, TAG_ID.COL],
	[TAG_NAMES.COLGROUP, TAG_ID.COLGROUP],
	[TAG_NAMES.DD, TAG_ID.DD],
	[TAG_NAMES.DESC, TAG_ID.DESC],
	[TAG_NAMES.DETAILS, TAG_ID.DETAILS],
	[TAG_NAMES.DIALOG, TAG_ID.DIALOG],
	[TAG_NAMES.DIR, TAG_ID.DIR],
	[TAG_NAMES.DIV, TAG_ID.DIV],
	[TAG_NAMES.DL, TAG_ID.DL],
	[TAG_NAMES.DT, TAG_ID.DT],
	[TAG_NAMES.EM, TAG_ID.EM],
	[TAG_NAMES.EMBED, TAG_ID.EMBED],
	[TAG_NAMES.FIELDSET, TAG_ID.FIELDSET],
	[TAG_NAMES.FIGCAPTION, TAG_ID.FIGCAPTION],
	[TAG_NAMES.FIGURE, TAG_ID.FIGURE],
	[TAG_NAMES.FONT, TAG_ID.FONT],
	[TAG_NAMES.FOOTER, TAG_ID.FOOTER],
	[TAG_NAMES.FOREIGN_OBJECT, TAG_ID.FOREIGN_OBJECT],
	[TAG_NAMES.FORM, TAG_ID.FORM],
	[TAG_NAMES.FRAME, TAG_ID.FRAME],
	[TAG_NAMES.FRAMESET, TAG_ID.FRAMESET],
	[TAG_NAMES.H1, TAG_ID.H1],
	[TAG_NAMES.H2, TAG_ID.H2],
	[TAG_NAMES.H3, TAG_ID.H3],
	[TAG_NAMES.H4, TAG_ID.H4],
	[TAG_NAMES.H5, TAG_ID.H5],
	[TAG_NAMES.H6, TAG_ID.H6],
	[TAG_NAMES.HEAD, TAG_ID.HEAD],
	[TAG_NAMES.HEADER, TAG_ID.HEADER],
	[TAG_NAMES.HGROUP, TAG_ID.HGROUP],
	[TAG_NAMES.HR, TAG_ID.HR],
	[TAG_NAMES.HTML, TAG_ID.HTML],
	[TAG_NAMES.I, TAG_ID.I],
	[TAG_NAMES.IMG, TAG_ID.IMG],
	[TAG_NAMES.IMAGE, TAG_ID.IMAGE],
	[TAG_NAMES.INPUT, TAG_ID.INPUT],
	[TAG_NAMES.IFRAME, TAG_ID.IFRAME],
	[TAG_NAMES.KEYGEN, TAG_ID.KEYGEN],
	[TAG_NAMES.LABEL, TAG_ID.LABEL],
	[TAG_NAMES.LI, TAG_ID.LI],
	[TAG_NAMES.LINK, TAG_ID.LINK],
	[TAG_NAMES.LISTING, TAG_ID.LISTING],
	[TAG_NAMES.MAIN, TAG_ID.MAIN],
	[TAG_NAMES.MALIGNMARK, TAG_ID.MALIGNMARK],
	[TAG_NAMES.MARQUEE, TAG_ID.MARQUEE],
	[TAG_NAMES.MATH, TAG_ID.MATH],
	[TAG_NAMES.MENU, TAG_ID.MENU],
	[TAG_NAMES.META, TAG_ID.META],
	[TAG_NAMES.MGLYPH, TAG_ID.MGLYPH],
	[TAG_NAMES.MI, TAG_ID.MI],
	[TAG_NAMES.MO, TAG_ID.MO],
	[TAG_NAMES.MN, TAG_ID.MN],
	[TAG_NAMES.MS, TAG_ID.MS],
	[TAG_NAMES.MTEXT, TAG_ID.MTEXT],
	[TAG_NAMES.NAV, TAG_ID.NAV],
	[TAG_NAMES.NOBR, TAG_ID.NOBR],
	[TAG_NAMES.NOFRAMES, TAG_ID.NOFRAMES],
	[TAG_NAMES.NOEMBED, TAG_ID.NOEMBED],
	[TAG_NAMES.NOSCRIPT, TAG_ID.NOSCRIPT],
	[TAG_NAMES.OBJECT, TAG_ID.OBJECT],
	[TAG_NAMES.OL, TAG_ID.OL],
	[TAG_NAMES.OPTGROUP, TAG_ID.OPTGROUP],
	[TAG_NAMES.OPTION, TAG_ID.OPTION],
	[TAG_NAMES.P, TAG_ID.P],
	[TAG_NAMES.PARAM, TAG_ID.PARAM],
	[TAG_NAMES.PLAINTEXT, TAG_ID.PLAINTEXT],
	[TAG_NAMES.PRE, TAG_ID.PRE],
	[TAG_NAMES.RB, TAG_ID.RB],
	[TAG_NAMES.RP, TAG_ID.RP],
	[TAG_NAMES.RT, TAG_ID.RT],
	[TAG_NAMES.RTC, TAG_ID.RTC],
	[TAG_NAMES.RUBY, TAG_ID.RUBY],
	[TAG_NAMES.S, TAG_ID.S],
	[TAG_NAMES.SCRIPT, TAG_ID.SCRIPT],
	[TAG_NAMES.SEARCH, TAG_ID.SEARCH],
	[TAG_NAMES.SECTION, TAG_ID.SECTION],
	[TAG_NAMES.SELECT, TAG_ID.SELECT],
	[TAG_NAMES.SOURCE, TAG_ID.SOURCE],
	[TAG_NAMES.SMALL, TAG_ID.SMALL],
	[TAG_NAMES.SPAN, TAG_ID.SPAN],
	[TAG_NAMES.STRIKE, TAG_ID.STRIKE],
	[TAG_NAMES.STRONG, TAG_ID.STRONG],
	[TAG_NAMES.STYLE, TAG_ID.STYLE],
	[TAG_NAMES.SUB, TAG_ID.SUB],
	[TAG_NAMES.SUMMARY, TAG_ID.SUMMARY],
	[TAG_NAMES.SUP, TAG_ID.SUP],
	[TAG_NAMES.TABLE, TAG_ID.TABLE],
	[TAG_NAMES.TBODY, TAG_ID.TBODY],
	[TAG_NAMES.TEMPLATE, TAG_ID.TEMPLATE],
	[TAG_NAMES.TEXTAREA, TAG_ID.TEXTAREA],
	[TAG_NAMES.TFOOT, TAG_ID.TFOOT],
	[TAG_NAMES.TD, TAG_ID.TD],
	[TAG_NAMES.TH, TAG_ID.TH],
	[TAG_NAMES.THEAD, TAG_ID.THEAD],
	[TAG_NAMES.TITLE, TAG_ID.TITLE],
	[TAG_NAMES.TR, TAG_ID.TR],
	[TAG_NAMES.TRACK, TAG_ID.TRACK],
	[TAG_NAMES.TT, TAG_ID.TT],
	[TAG_NAMES.U, TAG_ID.U],
	[TAG_NAMES.UL, TAG_ID.UL],
	[TAG_NAMES.SVG, TAG_ID.SVG],
	[TAG_NAMES.VAR, TAG_ID.VAR],
	[TAG_NAMES.WBR, TAG_ID.WBR],
	[TAG_NAMES.XMP, TAG_ID.XMP],
]);
function getTagID(tagName) {
	var _a;
	return (_a = TAG_NAME_TO_ID.get(tagName)) !== null && _a !== void 0 ? _a : TAG_ID.UNKNOWN;
}
const $$1 = TAG_ID;
const SPECIAL_ELEMENTS = {
	[NS.HTML]: new Set([
		$$1.ADDRESS,
		$$1.APPLET,
		$$1.AREA,
		$$1.ARTICLE,
		$$1.ASIDE,
		$$1.BASE,
		$$1.BASEFONT,
		$$1.BGSOUND,
		$$1.BLOCKQUOTE,
		$$1.BODY,
		$$1.BR,
		$$1.BUTTON,
		$$1.CAPTION,
		$$1.CENTER,
		$$1.COL,
		$$1.COLGROUP,
		$$1.DD,
		$$1.DETAILS,
		$$1.DIR,
		$$1.DIV,
		$$1.DL,
		$$1.DT,
		$$1.EMBED,
		$$1.FIELDSET,
		$$1.FIGCAPTION,
		$$1.FIGURE,
		$$1.FOOTER,
		$$1.FORM,
		$$1.FRAME,
		$$1.FRAMESET,
		$$1.H1,
		$$1.H2,
		$$1.H3,
		$$1.H4,
		$$1.H5,
		$$1.H6,
		$$1.HEAD,
		$$1.HEADER,
		$$1.HGROUP,
		$$1.HR,
		$$1.HTML,
		$$1.IFRAME,
		$$1.IMG,
		$$1.INPUT,
		$$1.LI,
		$$1.LINK,
		$$1.LISTING,
		$$1.MAIN,
		$$1.MARQUEE,
		$$1.MENU,
		$$1.META,
		$$1.NAV,
		$$1.NOEMBED,
		$$1.NOFRAMES,
		$$1.NOSCRIPT,
		$$1.OBJECT,
		$$1.OL,
		$$1.P,
		$$1.PARAM,
		$$1.PLAINTEXT,
		$$1.PRE,
		$$1.SCRIPT,
		$$1.SECTION,
		$$1.SELECT,
		$$1.SOURCE,
		$$1.STYLE,
		$$1.SUMMARY,
		$$1.TABLE,
		$$1.TBODY,
		$$1.TD,
		$$1.TEMPLATE,
		$$1.TEXTAREA,
		$$1.TFOOT,
		$$1.TH,
		$$1.THEAD,
		$$1.TITLE,
		$$1.TR,
		$$1.TRACK,
		$$1.UL,
		$$1.WBR,
		$$1.XMP,
	]),
	[NS.MATHML]: new Set([$$1.MI, $$1.MO, $$1.MN, $$1.MS, $$1.MTEXT, $$1.ANNOTATION_XML]),
	[NS.SVG]: new Set([$$1.TITLE, $$1.FOREIGN_OBJECT, $$1.DESC]),
	[NS.XLINK]: new Set(),
	[NS.XML]: new Set(),
	[NS.XMLNS]: new Set(),
};
const NUMBERED_HEADERS = new Set([$$1.H1, $$1.H2, $$1.H3, $$1.H4, $$1.H5, $$1.H6]);
new Set([TAG_NAMES.STYLE, TAG_NAMES.SCRIPT, TAG_NAMES.XMP, TAG_NAMES.IFRAME, TAG_NAMES.NOEMBED, TAG_NAMES.NOFRAMES, TAG_NAMES.PLAINTEXT]);

//States
var State;
(function (State) {
	State[(State["DATA"] = 0)] = "DATA";
	State[(State["RCDATA"] = 1)] = "RCDATA";
	State[(State["RAWTEXT"] = 2)] = "RAWTEXT";
	State[(State["SCRIPT_DATA"] = 3)] = "SCRIPT_DATA";
	State[(State["PLAINTEXT"] = 4)] = "PLAINTEXT";
	State[(State["TAG_OPEN"] = 5)] = "TAG_OPEN";
	State[(State["END_TAG_OPEN"] = 6)] = "END_TAG_OPEN";
	State[(State["TAG_NAME"] = 7)] = "TAG_NAME";
	State[(State["RCDATA_LESS_THAN_SIGN"] = 8)] = "RCDATA_LESS_THAN_SIGN";
	State[(State["RCDATA_END_TAG_OPEN"] = 9)] = "RCDATA_END_TAG_OPEN";
	State[(State["RCDATA_END_TAG_NAME"] = 10)] = "RCDATA_END_TAG_NAME";
	State[(State["RAWTEXT_LESS_THAN_SIGN"] = 11)] = "RAWTEXT_LESS_THAN_SIGN";
	State[(State["RAWTEXT_END_TAG_OPEN"] = 12)] = "RAWTEXT_END_TAG_OPEN";
	State[(State["RAWTEXT_END_TAG_NAME"] = 13)] = "RAWTEXT_END_TAG_NAME";
	State[(State["SCRIPT_DATA_LESS_THAN_SIGN"] = 14)] = "SCRIPT_DATA_LESS_THAN_SIGN";
	State[(State["SCRIPT_DATA_END_TAG_OPEN"] = 15)] = "SCRIPT_DATA_END_TAG_OPEN";
	State[(State["SCRIPT_DATA_END_TAG_NAME"] = 16)] = "SCRIPT_DATA_END_TAG_NAME";
	State[(State["SCRIPT_DATA_ESCAPE_START"] = 17)] = "SCRIPT_DATA_ESCAPE_START";
	State[(State["SCRIPT_DATA_ESCAPE_START_DASH"] = 18)] = "SCRIPT_DATA_ESCAPE_START_DASH";
	State[(State["SCRIPT_DATA_ESCAPED"] = 19)] = "SCRIPT_DATA_ESCAPED";
	State[(State["SCRIPT_DATA_ESCAPED_DASH"] = 20)] = "SCRIPT_DATA_ESCAPED_DASH";
	State[(State["SCRIPT_DATA_ESCAPED_DASH_DASH"] = 21)] = "SCRIPT_DATA_ESCAPED_DASH_DASH";
	State[(State["SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN"] = 22)] = "SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN";
	State[(State["SCRIPT_DATA_ESCAPED_END_TAG_OPEN"] = 23)] = "SCRIPT_DATA_ESCAPED_END_TAG_OPEN";
	State[(State["SCRIPT_DATA_ESCAPED_END_TAG_NAME"] = 24)] = "SCRIPT_DATA_ESCAPED_END_TAG_NAME";
	State[(State["SCRIPT_DATA_DOUBLE_ESCAPE_START"] = 25)] = "SCRIPT_DATA_DOUBLE_ESCAPE_START";
	State[(State["SCRIPT_DATA_DOUBLE_ESCAPED"] = 26)] = "SCRIPT_DATA_DOUBLE_ESCAPED";
	State[(State["SCRIPT_DATA_DOUBLE_ESCAPED_DASH"] = 27)] = "SCRIPT_DATA_DOUBLE_ESCAPED_DASH";
	State[(State["SCRIPT_DATA_DOUBLE_ESCAPED_DASH_DASH"] = 28)] = "SCRIPT_DATA_DOUBLE_ESCAPED_DASH_DASH";
	State[(State["SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN"] = 29)] = "SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN";
	State[(State["SCRIPT_DATA_DOUBLE_ESCAPE_END"] = 30)] = "SCRIPT_DATA_DOUBLE_ESCAPE_END";
	State[(State["BEFORE_ATTRIBUTE_NAME"] = 31)] = "BEFORE_ATTRIBUTE_NAME";
	State[(State["ATTRIBUTE_NAME"] = 32)] = "ATTRIBUTE_NAME";
	State[(State["AFTER_ATTRIBUTE_NAME"] = 33)] = "AFTER_ATTRIBUTE_NAME";
	State[(State["BEFORE_ATTRIBUTE_VALUE"] = 34)] = "BEFORE_ATTRIBUTE_VALUE";
	State[(State["ATTRIBUTE_VALUE_DOUBLE_QUOTED"] = 35)] = "ATTRIBUTE_VALUE_DOUBLE_QUOTED";
	State[(State["ATTRIBUTE_VALUE_SINGLE_QUOTED"] = 36)] = "ATTRIBUTE_VALUE_SINGLE_QUOTED";
	State[(State["ATTRIBUTE_VALUE_UNQUOTED"] = 37)] = "ATTRIBUTE_VALUE_UNQUOTED";
	State[(State["AFTER_ATTRIBUTE_VALUE_QUOTED"] = 38)] = "AFTER_ATTRIBUTE_VALUE_QUOTED";
	State[(State["SELF_CLOSING_START_TAG"] = 39)] = "SELF_CLOSING_START_TAG";
	State[(State["BOGUS_COMMENT"] = 40)] = "BOGUS_COMMENT";
	State[(State["MARKUP_DECLARATION_OPEN"] = 41)] = "MARKUP_DECLARATION_OPEN";
	State[(State["COMMENT_START"] = 42)] = "COMMENT_START";
	State[(State["COMMENT_START_DASH"] = 43)] = "COMMENT_START_DASH";
	State[(State["COMMENT"] = 44)] = "COMMENT";
	State[(State["COMMENT_LESS_THAN_SIGN"] = 45)] = "COMMENT_LESS_THAN_SIGN";
	State[(State["COMMENT_LESS_THAN_SIGN_BANG"] = 46)] = "COMMENT_LESS_THAN_SIGN_BANG";
	State[(State["COMMENT_LESS_THAN_SIGN_BANG_DASH"] = 47)] = "COMMENT_LESS_THAN_SIGN_BANG_DASH";
	State[(State["COMMENT_LESS_THAN_SIGN_BANG_DASH_DASH"] = 48)] = "COMMENT_LESS_THAN_SIGN_BANG_DASH_DASH";
	State[(State["COMMENT_END_DASH"] = 49)] = "COMMENT_END_DASH";
	State[(State["COMMENT_END"] = 50)] = "COMMENT_END";
	State[(State["COMMENT_END_BANG"] = 51)] = "COMMENT_END_BANG";
	State[(State["DOCTYPE"] = 52)] = "DOCTYPE";
	State[(State["BEFORE_DOCTYPE_NAME"] = 53)] = "BEFORE_DOCTYPE_NAME";
	State[(State["DOCTYPE_NAME"] = 54)] = "DOCTYPE_NAME";
	State[(State["AFTER_DOCTYPE_NAME"] = 55)] = "AFTER_DOCTYPE_NAME";
	State[(State["AFTER_DOCTYPE_PUBLIC_KEYWORD"] = 56)] = "AFTER_DOCTYPE_PUBLIC_KEYWORD";
	State[(State["BEFORE_DOCTYPE_PUBLIC_IDENTIFIER"] = 57)] = "BEFORE_DOCTYPE_PUBLIC_IDENTIFIER";
	State[(State["DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED"] = 58)] = "DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED";
	State[(State["DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED"] = 59)] = "DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED";
	State[(State["AFTER_DOCTYPE_PUBLIC_IDENTIFIER"] = 60)] = "AFTER_DOCTYPE_PUBLIC_IDENTIFIER";
	State[(State["BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS"] = 61)] = "BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS";
	State[(State["AFTER_DOCTYPE_SYSTEM_KEYWORD"] = 62)] = "AFTER_DOCTYPE_SYSTEM_KEYWORD";
	State[(State["BEFORE_DOCTYPE_SYSTEM_IDENTIFIER"] = 63)] = "BEFORE_DOCTYPE_SYSTEM_IDENTIFIER";
	State[(State["DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED"] = 64)] = "DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED";
	State[(State["DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED"] = 65)] = "DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED";
	State[(State["AFTER_DOCTYPE_SYSTEM_IDENTIFIER"] = 66)] = "AFTER_DOCTYPE_SYSTEM_IDENTIFIER";
	State[(State["BOGUS_DOCTYPE"] = 67)] = "BOGUS_DOCTYPE";
	State[(State["CDATA_SECTION"] = 68)] = "CDATA_SECTION";
	State[(State["CDATA_SECTION_BRACKET"] = 69)] = "CDATA_SECTION_BRACKET";
	State[(State["CDATA_SECTION_END"] = 70)] = "CDATA_SECTION_END";
	State[(State["CHARACTER_REFERENCE"] = 71)] = "CHARACTER_REFERENCE";
	State[(State["AMBIGUOUS_AMPERSAND"] = 72)] = "AMBIGUOUS_AMPERSAND";
})(State || (State = {}));
//Tokenizer initial states for different modes
const TokenizerMode = {
	DATA: State.DATA,
	RCDATA: State.RCDATA,
	RAWTEXT: State.RAWTEXT,
	SCRIPT_DATA: State.SCRIPT_DATA,
	PLAINTEXT: State.PLAINTEXT,
	CDATA_SECTION: State.CDATA_SECTION,
};
//Utils
//OPTIMIZATION: these utility functions should not be moved out of this module. V8 Crankshaft will not inline
//this functions if they will be situated in another module due to context switch.
//Always perform inlining check before modifying this functions ('node --trace-inlining').
function isAsciiDigit(cp) {
	return cp >= CODE_POINTS.DIGIT_0 && cp <= CODE_POINTS.DIGIT_9;
}
function isAsciiUpper(cp) {
	return cp >= CODE_POINTS.LATIN_CAPITAL_A && cp <= CODE_POINTS.LATIN_CAPITAL_Z;
}
function isAsciiLower(cp) {
	return cp >= CODE_POINTS.LATIN_SMALL_A && cp <= CODE_POINTS.LATIN_SMALL_Z;
}
function isAsciiLetter(cp) {
	return isAsciiLower(cp) || isAsciiUpper(cp);
}
function isAsciiAlphaNumeric(cp) {
	return isAsciiLetter(cp) || isAsciiDigit(cp);
}
function toAsciiLower(cp) {
	return cp + 32;
}
function isWhitespace(cp) {
	return cp === CODE_POINTS.SPACE || cp === CODE_POINTS.LINE_FEED || cp === CODE_POINTS.TABULATION || cp === CODE_POINTS.FORM_FEED;
}
function isScriptDataDoubleEscapeSequenceEnd(cp) {
	return isWhitespace(cp) || cp === CODE_POINTS.SOLIDUS || cp === CODE_POINTS.GREATER_THAN_SIGN;
}
function getErrorForNumericCharacterReference(code) {
	if (code === CODE_POINTS.NULL) {
		return ERR.nullCharacterReference;
	} else if (code > 1114111) {
		return ERR.characterReferenceOutsideUnicodeRange;
	} else if (isSurrogate(code)) {
		return ERR.surrogateCharacterReference;
	} else if (isUndefinedCodePoint(code)) {
		return ERR.noncharacterCharacterReference;
	} else if (isControlCodePoint(code) || code === CODE_POINTS.CARRIAGE_RETURN) {
		return ERR.controlCharacterReference;
	}
	return null;
}
//Tokenizer
class Tokenizer {
	constructor(options, handler) {
		this.options = options;
		this.handler = handler;
		this.paused = false;
		/** Ensures that the parsing loop isn't run multiple times at once. */
		this.inLoop = false;
		/**
		 * Indicates that the current adjusted node exists, is not an element in the HTML namespace,
		 * and that it is not an integration point for either MathML or HTML.
		 *
		 * @see {@link https://html.spec.whatwg.org/multipage/parsing.html#tree-construction}
		 */
		this.inForeignNode = false;
		this.lastStartTagName = "";
		this.active = false;
		this.state = State.DATA;
		this.returnState = State.DATA;
		this.entityStartPos = 0;
		this.consumedAfterSnapshot = -1;
		this.currentCharacterToken = null;
		this.currentToken = null;
		this.currentAttr = { name: "", value: "" };
		this.preprocessor = new Preprocessor(handler);
		this.currentLocation = this.getCurrentLocation(-1);
		this.entityDecoder = new EntityDecoder(
			htmlDecodeTree,
			(cp, consumed) => {
				// Note: Set `pos` _before_ flushing, as flushing might drop
				// the current chunk and invalidate `entityStartPos`.
				this.preprocessor.pos = this.entityStartPos + consumed - 1;
				this._flushCodePointConsumedAsCharacterReference(cp);
			},
			handler.onParseError ?
				{
					missingSemicolonAfterCharacterReference: () => {
						this._err(ERR.missingSemicolonAfterCharacterReference, 1);
					},
					absenceOfDigitsInNumericCharacterReference: (consumed) => {
						this._err(ERR.absenceOfDigitsInNumericCharacterReference, this.entityStartPos - this.preprocessor.pos + consumed);
					},
					validateNumericCharacterReference: (code) => {
						const error = getErrorForNumericCharacterReference(code);
						if (error) this._err(error, 1);
					},
				}
			:	undefined,
		);
	}
	//Errors
	_err(code, cpOffset = 0) {
		var _a, _b;
		(_b = (_a = this.handler).onParseError) === null || _b === void 0 ? void 0 : _b.call(_a, this.preprocessor.getError(code, cpOffset));
	}
	// NOTE: `offset` may never run across line boundaries.
	getCurrentLocation(offset) {
		if (!this.options.sourceCodeLocationInfo) {
			return null;
		}
		return {
			startLine: this.preprocessor.line,
			startCol: this.preprocessor.col - offset,
			startOffset: this.preprocessor.offset - offset,
			endLine: -1,
			endCol: -1,
			endOffset: -1,
		};
	}
	_runParsingLoop() {
		if (this.inLoop) return;
		this.inLoop = true;
		while (this.active && !this.paused) {
			this.consumedAfterSnapshot = 0;
			const cp = this._consume();
			if (!this._ensureHibernation()) {
				this._callState(cp);
			}
		}
		this.inLoop = false;
	}
	//API
	pause() {
		this.paused = true;
	}
	resume(writeCallback) {
		if (!this.paused) {
			throw new Error("Parser was already resumed");
		}
		this.paused = false;
		// Necessary for synchronous resume.
		if (this.inLoop) return;
		this._runParsingLoop();
		if (!this.paused) {
			writeCallback === null || writeCallback === void 0 ? void 0 : writeCallback();
		}
	}
	write(chunk, isLastChunk, writeCallback) {
		this.active = true;
		this.preprocessor.write(chunk, isLastChunk);
		this._runParsingLoop();
		if (!this.paused) {
			writeCallback === null || writeCallback === void 0 ? void 0 : writeCallback();
		}
	}
	insertHtmlAtCurrentPos(chunk) {
		this.active = true;
		this.preprocessor.insertHtmlAtCurrentPos(chunk);
		this._runParsingLoop();
	}
	//Hibernation
	_ensureHibernation() {
		if (this.preprocessor.endOfChunkHit) {
			this.preprocessor.retreat(this.consumedAfterSnapshot);
			this.consumedAfterSnapshot = 0;
			this.active = false;
			return true;
		}
		return false;
	}
	//Consumption
	_consume() {
		this.consumedAfterSnapshot++;
		return this.preprocessor.advance();
	}
	_advanceBy(count) {
		this.consumedAfterSnapshot += count;
		for (let i = 0; i < count; i++) {
			this.preprocessor.advance();
		}
	}
	_consumeSequenceIfMatch(pattern, caseSensitive) {
		if (this.preprocessor.startsWith(pattern, caseSensitive)) {
			// We will already have consumed one character before calling this method.
			this._advanceBy(pattern.length - 1);
			return true;
		}
		return false;
	}
	//Token creation
	_createStartTagToken() {
		this.currentToken = {
			type: TokenType.START_TAG,
			tagName: "",
			tagID: TAG_ID.UNKNOWN,
			selfClosing: false,
			ackSelfClosing: false,
			attrs: [],
			location: this.getCurrentLocation(1),
		};
	}
	_createEndTagToken() {
		this.currentToken = {
			type: TokenType.END_TAG,
			tagName: "",
			tagID: TAG_ID.UNKNOWN,
			selfClosing: false,
			ackSelfClosing: false,
			attrs: [],
			location: this.getCurrentLocation(2),
		};
	}
	_createCommentToken(offset) {
		this.currentToken = {
			type: TokenType.COMMENT,
			data: "",
			location: this.getCurrentLocation(offset),
		};
	}
	_createDoctypeToken(initialName) {
		this.currentToken = {
			type: TokenType.DOCTYPE,
			name: initialName,
			forceQuirks: false,
			publicId: null,
			systemId: null,
			location: this.currentLocation,
		};
	}
	_createCharacterToken(type, chars) {
		this.currentCharacterToken = {
			type,
			chars,
			location: this.currentLocation,
		};
	}
	//Tag attributes
	_createAttr(attrNameFirstCh) {
		this.currentAttr = {
			name: attrNameFirstCh,
			value: "",
		};
		this.currentLocation = this.getCurrentLocation(0);
	}
	_leaveAttrName() {
		var _a;
		var _b;
		const token = this.currentToken;
		if (getTokenAttr(token, this.currentAttr.name) === null) {
			token.attrs.push(this.currentAttr);
			if (token.location && this.currentLocation) {
				const attrLocations = (_a = (_b = token.location).attrs) !== null && _a !== void 0 ? _a : (_b.attrs = Object.create(null));
				attrLocations[this.currentAttr.name] = this.currentLocation;
				// Set end location
				this._leaveAttrValue();
			}
		} else {
			this._err(ERR.duplicateAttribute);
		}
	}
	_leaveAttrValue() {
		if (this.currentLocation) {
			this.currentLocation.endLine = this.preprocessor.line;
			this.currentLocation.endCol = this.preprocessor.col;
			this.currentLocation.endOffset = this.preprocessor.offset;
		}
	}
	//Token emission
	prepareToken(ct) {
		this._emitCurrentCharacterToken(ct.location);
		this.currentToken = null;
		if (ct.location) {
			ct.location.endLine = this.preprocessor.line;
			ct.location.endCol = this.preprocessor.col + 1;
			ct.location.endOffset = this.preprocessor.offset + 1;
		}
		this.currentLocation = this.getCurrentLocation(-1);
	}
	emitCurrentTagToken() {
		const ct = this.currentToken;
		this.prepareToken(ct);
		ct.tagID = getTagID(ct.tagName);
		if (ct.type === TokenType.START_TAG) {
			this.lastStartTagName = ct.tagName;
			this.handler.onStartTag(ct);
		} else {
			if (ct.attrs.length > 0) {
				this._err(ERR.endTagWithAttributes);
			}
			if (ct.selfClosing) {
				this._err(ERR.endTagWithTrailingSolidus);
			}
			this.handler.onEndTag(ct);
		}
		this.preprocessor.dropParsedChunk();
	}
	emitCurrentComment(ct) {
		this.prepareToken(ct);
		this.handler.onComment(ct);
		this.preprocessor.dropParsedChunk();
	}
	emitCurrentDoctype(ct) {
		this.prepareToken(ct);
		this.handler.onDoctype(ct);
		this.preprocessor.dropParsedChunk();
	}
	_emitCurrentCharacterToken(nextLocation) {
		if (this.currentCharacterToken) {
			//NOTE: if we have a pending character token, make it's end location equal to the
			//current token's start location.
			if (nextLocation && this.currentCharacterToken.location) {
				this.currentCharacterToken.location.endLine = nextLocation.startLine;
				this.currentCharacterToken.location.endCol = nextLocation.startCol;
				this.currentCharacterToken.location.endOffset = nextLocation.startOffset;
			}
			switch (this.currentCharacterToken.type) {
				case TokenType.CHARACTER: {
					this.handler.onCharacter(this.currentCharacterToken);
					break;
				}
				case TokenType.NULL_CHARACTER: {
					this.handler.onNullCharacter(this.currentCharacterToken);
					break;
				}
				case TokenType.WHITESPACE_CHARACTER: {
					this.handler.onWhitespaceCharacter(this.currentCharacterToken);
					break;
				}
			}
			this.currentCharacterToken = null;
		}
	}
	_emitEOFToken() {
		const location = this.getCurrentLocation(0);
		if (location) {
			location.endLine = location.startLine;
			location.endCol = location.startCol;
			location.endOffset = location.startOffset;
		}
		this._emitCurrentCharacterToken(location);
		this.handler.onEof({ type: TokenType.EOF, location });
		this.active = false;
	}
	//Characters emission
	//OPTIMIZATION: The specification uses only one type of character token (one token per character).
	//This causes a huge memory overhead and a lot of unnecessary parser loops. parse5 uses 3 groups of characters.
	//If we have a sequence of characters that belong to the same group, the parser can process it
	//as a single solid character token.
	//So, there are 3 types of character tokens in parse5:
	//1)TokenType.NULL_CHARACTER - \u0000-character sequences (e.g. '\u0000\u0000\u0000')
	//2)TokenType.WHITESPACE_CHARACTER - any whitespace/new-line character sequences (e.g. '\n  \r\t   \f')
	//3)TokenType.CHARACTER - any character sequence which don't belong to groups 1 and 2 (e.g. 'abcdef1234@@#$%^')
	_appendCharToCurrentCharacterToken(type, ch) {
		if (this.currentCharacterToken) {
			if (this.currentCharacterToken.type === type) {
				this.currentCharacterToken.chars += ch;
				return;
			} else {
				this.currentLocation = this.getCurrentLocation(0);
				this._emitCurrentCharacterToken(this.currentLocation);
				this.preprocessor.dropParsedChunk();
			}
		}
		this._createCharacterToken(type, ch);
	}
	_emitCodePoint(cp) {
		const type =
			isWhitespace(cp) ? TokenType.WHITESPACE_CHARACTER
			: cp === CODE_POINTS.NULL ? TokenType.NULL_CHARACTER
			: TokenType.CHARACTER;
		this._appendCharToCurrentCharacterToken(type, String.fromCodePoint(cp));
	}
	//NOTE: used when we emit characters explicitly.
	//This is always for non-whitespace and non-null characters, which allows us to avoid additional checks.
	_emitChars(ch) {
		this._appendCharToCurrentCharacterToken(TokenType.CHARACTER, ch);
	}
	// Character reference helpers
	_startCharacterReference() {
		this.returnState = this.state;
		this.state = State.CHARACTER_REFERENCE;
		this.entityStartPos = this.preprocessor.pos;
		this.entityDecoder.startEntity(this._isCharacterReferenceInAttribute() ? DecodingMode.Attribute : DecodingMode.Legacy);
	}
	_isCharacterReferenceInAttribute() {
		return this.returnState === State.ATTRIBUTE_VALUE_DOUBLE_QUOTED || this.returnState === State.ATTRIBUTE_VALUE_SINGLE_QUOTED || this.returnState === State.ATTRIBUTE_VALUE_UNQUOTED;
	}
	_flushCodePointConsumedAsCharacterReference(cp) {
		if (this._isCharacterReferenceInAttribute()) {
			this.currentAttr.value += String.fromCodePoint(cp);
		} else {
			this._emitCodePoint(cp);
		}
	}
	// Calling states this way turns out to be much faster than any other approach.
	_callState(cp) {
		switch (this.state) {
			case State.DATA: {
				this._stateData(cp);
				break;
			}
			case State.RCDATA: {
				this._stateRcdata(cp);
				break;
			}
			case State.RAWTEXT: {
				this._stateRawtext(cp);
				break;
			}
			case State.SCRIPT_DATA: {
				this._stateScriptData(cp);
				break;
			}
			case State.PLAINTEXT: {
				this._statePlaintext(cp);
				break;
			}
			case State.TAG_OPEN: {
				this._stateTagOpen(cp);
				break;
			}
			case State.END_TAG_OPEN: {
				this._stateEndTagOpen(cp);
				break;
			}
			case State.TAG_NAME: {
				this._stateTagName(cp);
				break;
			}
			case State.RCDATA_LESS_THAN_SIGN: {
				this._stateRcdataLessThanSign(cp);
				break;
			}
			case State.RCDATA_END_TAG_OPEN: {
				this._stateRcdataEndTagOpen(cp);
				break;
			}
			case State.RCDATA_END_TAG_NAME: {
				this._stateRcdataEndTagName(cp);
				break;
			}
			case State.RAWTEXT_LESS_THAN_SIGN: {
				this._stateRawtextLessThanSign(cp);
				break;
			}
			case State.RAWTEXT_END_TAG_OPEN: {
				this._stateRawtextEndTagOpen(cp);
				break;
			}
			case State.RAWTEXT_END_TAG_NAME: {
				this._stateRawtextEndTagName(cp);
				break;
			}
			case State.SCRIPT_DATA_LESS_THAN_SIGN: {
				this._stateScriptDataLessThanSign(cp);
				break;
			}
			case State.SCRIPT_DATA_END_TAG_OPEN: {
				this._stateScriptDataEndTagOpen(cp);
				break;
			}
			case State.SCRIPT_DATA_END_TAG_NAME: {
				this._stateScriptDataEndTagName(cp);
				break;
			}
			case State.SCRIPT_DATA_ESCAPE_START: {
				this._stateScriptDataEscapeStart(cp);
				break;
			}
			case State.SCRIPT_DATA_ESCAPE_START_DASH: {
				this._stateScriptDataEscapeStartDash(cp);
				break;
			}
			case State.SCRIPT_DATA_ESCAPED: {
				this._stateScriptDataEscaped(cp);
				break;
			}
			case State.SCRIPT_DATA_ESCAPED_DASH: {
				this._stateScriptDataEscapedDash(cp);
				break;
			}
			case State.SCRIPT_DATA_ESCAPED_DASH_DASH: {
				this._stateScriptDataEscapedDashDash(cp);
				break;
			}
			case State.SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN: {
				this._stateScriptDataEscapedLessThanSign(cp);
				break;
			}
			case State.SCRIPT_DATA_ESCAPED_END_TAG_OPEN: {
				this._stateScriptDataEscapedEndTagOpen(cp);
				break;
			}
			case State.SCRIPT_DATA_ESCAPED_END_TAG_NAME: {
				this._stateScriptDataEscapedEndTagName(cp);
				break;
			}
			case State.SCRIPT_DATA_DOUBLE_ESCAPE_START: {
				this._stateScriptDataDoubleEscapeStart(cp);
				break;
			}
			case State.SCRIPT_DATA_DOUBLE_ESCAPED: {
				this._stateScriptDataDoubleEscaped(cp);
				break;
			}
			case State.SCRIPT_DATA_DOUBLE_ESCAPED_DASH: {
				this._stateScriptDataDoubleEscapedDash(cp);
				break;
			}
			case State.SCRIPT_DATA_DOUBLE_ESCAPED_DASH_DASH: {
				this._stateScriptDataDoubleEscapedDashDash(cp);
				break;
			}
			case State.SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN: {
				this._stateScriptDataDoubleEscapedLessThanSign(cp);
				break;
			}
			case State.SCRIPT_DATA_DOUBLE_ESCAPE_END: {
				this._stateScriptDataDoubleEscapeEnd(cp);
				break;
			}
			case State.BEFORE_ATTRIBUTE_NAME: {
				this._stateBeforeAttributeName(cp);
				break;
			}
			case State.ATTRIBUTE_NAME: {
				this._stateAttributeName(cp);
				break;
			}
			case State.AFTER_ATTRIBUTE_NAME: {
				this._stateAfterAttributeName(cp);
				break;
			}
			case State.BEFORE_ATTRIBUTE_VALUE: {
				this._stateBeforeAttributeValue(cp);
				break;
			}
			case State.ATTRIBUTE_VALUE_DOUBLE_QUOTED: {
				this._stateAttributeValueDoubleQuoted(cp);
				break;
			}
			case State.ATTRIBUTE_VALUE_SINGLE_QUOTED: {
				this._stateAttributeValueSingleQuoted(cp);
				break;
			}
			case State.ATTRIBUTE_VALUE_UNQUOTED: {
				this._stateAttributeValueUnquoted(cp);
				break;
			}
			case State.AFTER_ATTRIBUTE_VALUE_QUOTED: {
				this._stateAfterAttributeValueQuoted(cp);
				break;
			}
			case State.SELF_CLOSING_START_TAG: {
				this._stateSelfClosingStartTag(cp);
				break;
			}
			case State.BOGUS_COMMENT: {
				this._stateBogusComment(cp);
				break;
			}
			case State.MARKUP_DECLARATION_OPEN: {
				this._stateMarkupDeclarationOpen(cp);
				break;
			}
			case State.COMMENT_START: {
				this._stateCommentStart(cp);
				break;
			}
			case State.COMMENT_START_DASH: {
				this._stateCommentStartDash(cp);
				break;
			}
			case State.COMMENT: {
				this._stateComment(cp);
				break;
			}
			case State.COMMENT_LESS_THAN_SIGN: {
				this._stateCommentLessThanSign(cp);
				break;
			}
			case State.COMMENT_LESS_THAN_SIGN_BANG: {
				this._stateCommentLessThanSignBang(cp);
				break;
			}
			case State.COMMENT_LESS_THAN_SIGN_BANG_DASH: {
				this._stateCommentLessThanSignBangDash(cp);
				break;
			}
			case State.COMMENT_LESS_THAN_SIGN_BANG_DASH_DASH: {
				this._stateCommentLessThanSignBangDashDash(cp);
				break;
			}
			case State.COMMENT_END_DASH: {
				this._stateCommentEndDash(cp);
				break;
			}
			case State.COMMENT_END: {
				this._stateCommentEnd(cp);
				break;
			}
			case State.COMMENT_END_BANG: {
				this._stateCommentEndBang(cp);
				break;
			}
			case State.DOCTYPE: {
				this._stateDoctype(cp);
				break;
			}
			case State.BEFORE_DOCTYPE_NAME: {
				this._stateBeforeDoctypeName(cp);
				break;
			}
			case State.DOCTYPE_NAME: {
				this._stateDoctypeName(cp);
				break;
			}
			case State.AFTER_DOCTYPE_NAME: {
				this._stateAfterDoctypeName(cp);
				break;
			}
			case State.AFTER_DOCTYPE_PUBLIC_KEYWORD: {
				this._stateAfterDoctypePublicKeyword(cp);
				break;
			}
			case State.BEFORE_DOCTYPE_PUBLIC_IDENTIFIER: {
				this._stateBeforeDoctypePublicIdentifier(cp);
				break;
			}
			case State.DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED: {
				this._stateDoctypePublicIdentifierDoubleQuoted(cp);
				break;
			}
			case State.DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED: {
				this._stateDoctypePublicIdentifierSingleQuoted(cp);
				break;
			}
			case State.AFTER_DOCTYPE_PUBLIC_IDENTIFIER: {
				this._stateAfterDoctypePublicIdentifier(cp);
				break;
			}
			case State.BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS: {
				this._stateBetweenDoctypePublicAndSystemIdentifiers(cp);
				break;
			}
			case State.AFTER_DOCTYPE_SYSTEM_KEYWORD: {
				this._stateAfterDoctypeSystemKeyword(cp);
				break;
			}
			case State.BEFORE_DOCTYPE_SYSTEM_IDENTIFIER: {
				this._stateBeforeDoctypeSystemIdentifier(cp);
				break;
			}
			case State.DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED: {
				this._stateDoctypeSystemIdentifierDoubleQuoted(cp);
				break;
			}
			case State.DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED: {
				this._stateDoctypeSystemIdentifierSingleQuoted(cp);
				break;
			}
			case State.AFTER_DOCTYPE_SYSTEM_IDENTIFIER: {
				this._stateAfterDoctypeSystemIdentifier(cp);
				break;
			}
			case State.BOGUS_DOCTYPE: {
				this._stateBogusDoctype(cp);
				break;
			}
			case State.CDATA_SECTION: {
				this._stateCdataSection(cp);
				break;
			}
			case State.CDATA_SECTION_BRACKET: {
				this._stateCdataSectionBracket(cp);
				break;
			}
			case State.CDATA_SECTION_END: {
				this._stateCdataSectionEnd(cp);
				break;
			}
			case State.CHARACTER_REFERENCE: {
				this._stateCharacterReference();
				break;
			}
			case State.AMBIGUOUS_AMPERSAND: {
				this._stateAmbiguousAmpersand(cp);
				break;
			}
			default: {
				throw new Error("Unknown state");
			}
		}
	}
	// State machine
	// Data state
	//------------------------------------------------------------------
	_stateData(cp) {
		switch (cp) {
			case CODE_POINTS.LESS_THAN_SIGN: {
				this.state = State.TAG_OPEN;
				break;
			}
			case CODE_POINTS.AMPERSAND: {
				this._startCharacterReference();
				break;
			}
			case CODE_POINTS.NULL: {
				this._err(ERR.unexpectedNullCharacter);
				this._emitCodePoint(cp);
				break;
			}
			case CODE_POINTS.EOF: {
				this._emitEOFToken();
				break;
			}
			default: {
				this._emitCodePoint(cp);
			}
		}
	}
	//  RCDATA state
	//------------------------------------------------------------------
	_stateRcdata(cp) {
		switch (cp) {
			case CODE_POINTS.AMPERSAND: {
				this._startCharacterReference();
				break;
			}
			case CODE_POINTS.LESS_THAN_SIGN: {
				this.state = State.RCDATA_LESS_THAN_SIGN;
				break;
			}
			case CODE_POINTS.NULL: {
				this._err(ERR.unexpectedNullCharacter);
				this._emitChars(REPLACEMENT_CHARACTER);
				break;
			}
			case CODE_POINTS.EOF: {
				this._emitEOFToken();
				break;
			}
			default: {
				this._emitCodePoint(cp);
			}
		}
	}
	// RAWTEXT state
	//------------------------------------------------------------------
	_stateRawtext(cp) {
		switch (cp) {
			case CODE_POINTS.LESS_THAN_SIGN: {
				this.state = State.RAWTEXT_LESS_THAN_SIGN;
				break;
			}
			case CODE_POINTS.NULL: {
				this._err(ERR.unexpectedNullCharacter);
				this._emitChars(REPLACEMENT_CHARACTER);
				break;
			}
			case CODE_POINTS.EOF: {
				this._emitEOFToken();
				break;
			}
			default: {
				this._emitCodePoint(cp);
			}
		}
	}
	// Script data state
	//------------------------------------------------------------------
	_stateScriptData(cp) {
		switch (cp) {
			case CODE_POINTS.LESS_THAN_SIGN: {
				this.state = State.SCRIPT_DATA_LESS_THAN_SIGN;
				break;
			}
			case CODE_POINTS.NULL: {
				this._err(ERR.unexpectedNullCharacter);
				this._emitChars(REPLACEMENT_CHARACTER);
				break;
			}
			case CODE_POINTS.EOF: {
				this._emitEOFToken();
				break;
			}
			default: {
				this._emitCodePoint(cp);
			}
		}
	}
	// PLAINTEXT state
	//------------------------------------------------------------------
	_statePlaintext(cp) {
		switch (cp) {
			case CODE_POINTS.NULL: {
				this._err(ERR.unexpectedNullCharacter);
				this._emitChars(REPLACEMENT_CHARACTER);
				break;
			}
			case CODE_POINTS.EOF: {
				this._emitEOFToken();
				break;
			}
			default: {
				this._emitCodePoint(cp);
			}
		}
	}
	// Tag open state
	//------------------------------------------------------------------
	_stateTagOpen(cp) {
		if (isAsciiLetter(cp)) {
			this._createStartTagToken();
			this.state = State.TAG_NAME;
			this._stateTagName(cp);
		} else
			switch (cp) {
				case CODE_POINTS.EXCLAMATION_MARK: {
					this.state = State.MARKUP_DECLARATION_OPEN;
					break;
				}
				case CODE_POINTS.SOLIDUS: {
					this.state = State.END_TAG_OPEN;
					break;
				}
				case CODE_POINTS.QUESTION_MARK: {
					this._err(ERR.unexpectedQuestionMarkInsteadOfTagName);
					this._createCommentToken(1);
					this.state = State.BOGUS_COMMENT;
					this._stateBogusComment(cp);
					break;
				}
				case CODE_POINTS.EOF: {
					this._err(ERR.eofBeforeTagName);
					this._emitChars("<");
					this._emitEOFToken();
					break;
				}
				default: {
					this._err(ERR.invalidFirstCharacterOfTagName);
					this._emitChars("<");
					this.state = State.DATA;
					this._stateData(cp);
				}
			}
	}
	// End tag open state
	//------------------------------------------------------------------
	_stateEndTagOpen(cp) {
		if (isAsciiLetter(cp)) {
			this._createEndTagToken();
			this.state = State.TAG_NAME;
			this._stateTagName(cp);
		} else
			switch (cp) {
				case CODE_POINTS.GREATER_THAN_SIGN: {
					this._err(ERR.missingEndTagName);
					this.state = State.DATA;
					break;
				}
				case CODE_POINTS.EOF: {
					this._err(ERR.eofBeforeTagName);
					this._emitChars("</");
					this._emitEOFToken();
					break;
				}
				default: {
					this._err(ERR.invalidFirstCharacterOfTagName);
					this._createCommentToken(2);
					this.state = State.BOGUS_COMMENT;
					this._stateBogusComment(cp);
				}
			}
	}
	// Tag name state
	//------------------------------------------------------------------
	_stateTagName(cp) {
		const token = this.currentToken;
		switch (cp) {
			case CODE_POINTS.SPACE:
			case CODE_POINTS.LINE_FEED:
			case CODE_POINTS.TABULATION:
			case CODE_POINTS.FORM_FEED: {
				this.state = State.BEFORE_ATTRIBUTE_NAME;
				break;
			}
			case CODE_POINTS.SOLIDUS: {
				this.state = State.SELF_CLOSING_START_TAG;
				break;
			}
			case CODE_POINTS.GREATER_THAN_SIGN: {
				this.state = State.DATA;
				this.emitCurrentTagToken();
				break;
			}
			case CODE_POINTS.NULL: {
				this._err(ERR.unexpectedNullCharacter);
				token.tagName += REPLACEMENT_CHARACTER;
				break;
			}
			case CODE_POINTS.EOF: {
				this._err(ERR.eofInTag);
				this._emitEOFToken();
				break;
			}
			default: {
				token.tagName += String.fromCodePoint(isAsciiUpper(cp) ? toAsciiLower(cp) : cp);
			}
		}
	}
	// RCDATA less-than sign state
	//------------------------------------------------------------------
	_stateRcdataLessThanSign(cp) {
		if (cp === CODE_POINTS.SOLIDUS) {
			this.state = State.RCDATA_END_TAG_OPEN;
		} else {
			this._emitChars("<");
			this.state = State.RCDATA;
			this._stateRcdata(cp);
		}
	}
	// RCDATA end tag open state
	//------------------------------------------------------------------
	_stateRcdataEndTagOpen(cp) {
		if (isAsciiLetter(cp)) {
			this.state = State.RCDATA_END_TAG_NAME;
			this._stateRcdataEndTagName(cp);
		} else {
			this._emitChars("</");
			this.state = State.RCDATA;
			this._stateRcdata(cp);
		}
	}
	handleSpecialEndTag(_cp) {
		if (!this.preprocessor.startsWith(this.lastStartTagName, false)) {
			return !this._ensureHibernation();
		}
		this._createEndTagToken();
		const token = this.currentToken;
		token.tagName = this.lastStartTagName;
		const cp = this.preprocessor.peek(this.lastStartTagName.length);
		switch (cp) {
			case CODE_POINTS.SPACE:
			case CODE_POINTS.LINE_FEED:
			case CODE_POINTS.TABULATION:
			case CODE_POINTS.FORM_FEED: {
				this._advanceBy(this.lastStartTagName.length);
				this.state = State.BEFORE_ATTRIBUTE_NAME;
				return false;
			}
			case CODE_POINTS.SOLIDUS: {
				this._advanceBy(this.lastStartTagName.length);
				this.state = State.SELF_CLOSING_START_TAG;
				return false;
			}
			case CODE_POINTS.GREATER_THAN_SIGN: {
				this._advanceBy(this.lastStartTagName.length);
				this.emitCurrentTagToken();
				this.state = State.DATA;
				return false;
			}
			default: {
				return !this._ensureHibernation();
			}
		}
	}
	// RCDATA end tag name state
	//------------------------------------------------------------------
	_stateRcdataEndTagName(cp) {
		if (this.handleSpecialEndTag(cp)) {
			this._emitChars("</");
			this.state = State.RCDATA;
			this._stateRcdata(cp);
		}
	}
	// RAWTEXT less-than sign state
	//------------------------------------------------------------------
	_stateRawtextLessThanSign(cp) {
		if (cp === CODE_POINTS.SOLIDUS) {
			this.state = State.RAWTEXT_END_TAG_OPEN;
		} else {
			this._emitChars("<");
			this.state = State.RAWTEXT;
			this._stateRawtext(cp);
		}
	}
	// RAWTEXT end tag open state
	//------------------------------------------------------------------
	_stateRawtextEndTagOpen(cp) {
		if (isAsciiLetter(cp)) {
			this.state = State.RAWTEXT_END_TAG_NAME;
			this._stateRawtextEndTagName(cp);
		} else {
			this._emitChars("</");
			this.state = State.RAWTEXT;
			this._stateRawtext(cp);
		}
	}
	// RAWTEXT end tag name state
	//------------------------------------------------------------------
	_stateRawtextEndTagName(cp) {
		if (this.handleSpecialEndTag(cp)) {
			this._emitChars("</");
			this.state = State.RAWTEXT;
			this._stateRawtext(cp);
		}
	}
	// Script data less-than sign state
	//------------------------------------------------------------------
	_stateScriptDataLessThanSign(cp) {
		switch (cp) {
			case CODE_POINTS.SOLIDUS: {
				this.state = State.SCRIPT_DATA_END_TAG_OPEN;
				break;
			}
			case CODE_POINTS.EXCLAMATION_MARK: {
				this.state = State.SCRIPT_DATA_ESCAPE_START;
				this._emitChars("<!");
				break;
			}
			default: {
				this._emitChars("<");
				this.state = State.SCRIPT_DATA;
				this._stateScriptData(cp);
			}
		}
	}
	// Script data end tag open state
	//------------------------------------------------------------------
	_stateScriptDataEndTagOpen(cp) {
		if (isAsciiLetter(cp)) {
			this.state = State.SCRIPT_DATA_END_TAG_NAME;
			this._stateScriptDataEndTagName(cp);
		} else {
			this._emitChars("</");
			this.state = State.SCRIPT_DATA;
			this._stateScriptData(cp);
		}
	}
	// Script data end tag name state
	//------------------------------------------------------------------
	_stateScriptDataEndTagName(cp) {
		if (this.handleSpecialEndTag(cp)) {
			this._emitChars("</");
			this.state = State.SCRIPT_DATA;
			this._stateScriptData(cp);
		}
	}
	// Script data escape start state
	//------------------------------------------------------------------
	_stateScriptDataEscapeStart(cp) {
		if (cp === CODE_POINTS.HYPHEN_MINUS) {
			this.state = State.SCRIPT_DATA_ESCAPE_START_DASH;
			this._emitChars("-");
		} else {
			this.state = State.SCRIPT_DATA;
			this._stateScriptData(cp);
		}
	}
	// Script data escape start dash state
	//------------------------------------------------------------------
	_stateScriptDataEscapeStartDash(cp) {
		if (cp === CODE_POINTS.HYPHEN_MINUS) {
			this.state = State.SCRIPT_DATA_ESCAPED_DASH_DASH;
			this._emitChars("-");
		} else {
			this.state = State.SCRIPT_DATA;
			this._stateScriptData(cp);
		}
	}
	// Script data escaped state
	//------------------------------------------------------------------
	_stateScriptDataEscaped(cp) {
		switch (cp) {
			case CODE_POINTS.HYPHEN_MINUS: {
				this.state = State.SCRIPT_DATA_ESCAPED_DASH;
				this._emitChars("-");
				break;
			}
			case CODE_POINTS.LESS_THAN_SIGN: {
				this.state = State.SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN;
				break;
			}
			case CODE_POINTS.NULL: {
				this._err(ERR.unexpectedNullCharacter);
				this._emitChars(REPLACEMENT_CHARACTER);
				break;
			}
			case CODE_POINTS.EOF: {
				this._err(ERR.eofInScriptHtmlCommentLikeText);
				this._emitEOFToken();
				break;
			}
			default: {
				this._emitCodePoint(cp);
			}
		}
	}
	// Script data escaped dash state
	//------------------------------------------------------------------
	_stateScriptDataEscapedDash(cp) {
		switch (cp) {
			case CODE_POINTS.HYPHEN_MINUS: {
				this.state = State.SCRIPT_DATA_ESCAPED_DASH_DASH;
				this._emitChars("-");
				break;
			}
			case CODE_POINTS.LESS_THAN_SIGN: {
				this.state = State.SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN;
				break;
			}
			case CODE_POINTS.NULL: {
				this._err(ERR.unexpectedNullCharacter);
				this.state = State.SCRIPT_DATA_ESCAPED;
				this._emitChars(REPLACEMENT_CHARACTER);
				break;
			}
			case CODE_POINTS.EOF: {
				this._err(ERR.eofInScriptHtmlCommentLikeText);
				this._emitEOFToken();
				break;
			}
			default: {
				this.state = State.SCRIPT_DATA_ESCAPED;
				this._emitCodePoint(cp);
			}
		}
	}
	// Script data escaped dash dash state
	//------------------------------------------------------------------
	_stateScriptDataEscapedDashDash(cp) {
		switch (cp) {
			case CODE_POINTS.HYPHEN_MINUS: {
				this._emitChars("-");
				break;
			}
			case CODE_POINTS.LESS_THAN_SIGN: {
				this.state = State.SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN;
				break;
			}
			case CODE_POINTS.GREATER_THAN_SIGN: {
				this.state = State.SCRIPT_DATA;
				this._emitChars(">");
				break;
			}
			case CODE_POINTS.NULL: {
				this._err(ERR.unexpectedNullCharacter);
				this.state = State.SCRIPT_DATA_ESCAPED;
				this._emitChars(REPLACEMENT_CHARACTER);
				break;
			}
			case CODE_POINTS.EOF: {
				this._err(ERR.eofInScriptHtmlCommentLikeText);
				this._emitEOFToken();
				break;
			}
			default: {
				this.state = State.SCRIPT_DATA_ESCAPED;
				this._emitCodePoint(cp);
			}
		}
	}
	// Script data escaped less-than sign state
	//------------------------------------------------------------------
	_stateScriptDataEscapedLessThanSign(cp) {
		if (cp === CODE_POINTS.SOLIDUS) {
			this.state = State.SCRIPT_DATA_ESCAPED_END_TAG_OPEN;
		} else if (isAsciiLetter(cp)) {
			this._emitChars("<");
			this.state = State.SCRIPT_DATA_DOUBLE_ESCAPE_START;
			this._stateScriptDataDoubleEscapeStart(cp);
		} else {
			this._emitChars("<");
			this.state = State.SCRIPT_DATA_ESCAPED;
			this._stateScriptDataEscaped(cp);
		}
	}
	// Script data escaped end tag open state
	//------------------------------------------------------------------
	_stateScriptDataEscapedEndTagOpen(cp) {
		if (isAsciiLetter(cp)) {
			this.state = State.SCRIPT_DATA_ESCAPED_END_TAG_NAME;
			this._stateScriptDataEscapedEndTagName(cp);
		} else {
			this._emitChars("</");
			this.state = State.SCRIPT_DATA_ESCAPED;
			this._stateScriptDataEscaped(cp);
		}
	}
	// Script data escaped end tag name state
	//------------------------------------------------------------------
	_stateScriptDataEscapedEndTagName(cp) {
		if (this.handleSpecialEndTag(cp)) {
			this._emitChars("</");
			this.state = State.SCRIPT_DATA_ESCAPED;
			this._stateScriptDataEscaped(cp);
		}
	}
	// Script data double escape start state
	//------------------------------------------------------------------
	_stateScriptDataDoubleEscapeStart(cp) {
		if (this.preprocessor.startsWith(SEQUENCES.SCRIPT, false) && isScriptDataDoubleEscapeSequenceEnd(this.preprocessor.peek(SEQUENCES.SCRIPT.length))) {
			this._emitCodePoint(cp);
			for (let i = 0; i < SEQUENCES.SCRIPT.length; i++) {
				this._emitCodePoint(this._consume());
			}
			this.state = State.SCRIPT_DATA_DOUBLE_ESCAPED;
		} else if (!this._ensureHibernation()) {
			this.state = State.SCRIPT_DATA_ESCAPED;
			this._stateScriptDataEscaped(cp);
		}
	}
	// Script data double escaped state
	//------------------------------------------------------------------
	_stateScriptDataDoubleEscaped(cp) {
		switch (cp) {
			case CODE_POINTS.HYPHEN_MINUS: {
				this.state = State.SCRIPT_DATA_DOUBLE_ESCAPED_DASH;
				this._emitChars("-");
				break;
			}
			case CODE_POINTS.LESS_THAN_SIGN: {
				this.state = State.SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN;
				this._emitChars("<");
				break;
			}
			case CODE_POINTS.NULL: {
				this._err(ERR.unexpectedNullCharacter);
				this._emitChars(REPLACEMENT_CHARACTER);
				break;
			}
			case CODE_POINTS.EOF: {
				this._err(ERR.eofInScriptHtmlCommentLikeText);
				this._emitEOFToken();
				break;
			}
			default: {
				this._emitCodePoint(cp);
			}
		}
	}
	// Script data double escaped dash state
	//------------------------------------------------------------------
	_stateScriptDataDoubleEscapedDash(cp) {
		switch (cp) {
			case CODE_POINTS.HYPHEN_MINUS: {
				this.state = State.SCRIPT_DATA_DOUBLE_ESCAPED_DASH_DASH;
				this._emitChars("-");
				break;
			}
			case CODE_POINTS.LESS_THAN_SIGN: {
				this.state = State.SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN;
				this._emitChars("<");
				break;
			}
			case CODE_POINTS.NULL: {
				this._err(ERR.unexpectedNullCharacter);
				this.state = State.SCRIPT_DATA_DOUBLE_ESCAPED;
				this._emitChars(REPLACEMENT_CHARACTER);
				break;
			}
			case CODE_POINTS.EOF: {
				this._err(ERR.eofInScriptHtmlCommentLikeText);
				this._emitEOFToken();
				break;
			}
			default: {
				this.state = State.SCRIPT_DATA_DOUBLE_ESCAPED;
				this._emitCodePoint(cp);
			}
		}
	}
	// Script data double escaped dash dash state
	//------------------------------------------------------------------
	_stateScriptDataDoubleEscapedDashDash(cp) {
		switch (cp) {
			case CODE_POINTS.HYPHEN_MINUS: {
				this._emitChars("-");
				break;
			}
			case CODE_POINTS.LESS_THAN_SIGN: {
				this.state = State.SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN;
				this._emitChars("<");
				break;
			}
			case CODE_POINTS.GREATER_THAN_SIGN: {
				this.state = State.SCRIPT_DATA;
				this._emitChars(">");
				break;
			}
			case CODE_POINTS.NULL: {
				this._err(ERR.unexpectedNullCharacter);
				this.state = State.SCRIPT_DATA_DOUBLE_ESCAPED;
				this._emitChars(REPLACEMENT_CHARACTER);
				break;
			}
			case CODE_POINTS.EOF: {
				this._err(ERR.eofInScriptHtmlCommentLikeText);
				this._emitEOFToken();
				break;
			}
			default: {
				this.state = State.SCRIPT_DATA_DOUBLE_ESCAPED;
				this._emitCodePoint(cp);
			}
		}
	}
	// Script data double escaped less-than sign state
	//------------------------------------------------------------------
	_stateScriptDataDoubleEscapedLessThanSign(cp) {
		if (cp === CODE_POINTS.SOLIDUS) {
			this.state = State.SCRIPT_DATA_DOUBLE_ESCAPE_END;
			this._emitChars("/");
		} else {
			this.state = State.SCRIPT_DATA_DOUBLE_ESCAPED;
			this._stateScriptDataDoubleEscaped(cp);
		}
	}
	// Script data double escape end state
	//------------------------------------------------------------------
	_stateScriptDataDoubleEscapeEnd(cp) {
		if (this.preprocessor.startsWith(SEQUENCES.SCRIPT, false) && isScriptDataDoubleEscapeSequenceEnd(this.preprocessor.peek(SEQUENCES.SCRIPT.length))) {
			this._emitCodePoint(cp);
			for (let i = 0; i < SEQUENCES.SCRIPT.length; i++) {
				this._emitCodePoint(this._consume());
			}
			this.state = State.SCRIPT_DATA_ESCAPED;
		} else if (!this._ensureHibernation()) {
			this.state = State.SCRIPT_DATA_DOUBLE_ESCAPED;
			this._stateScriptDataDoubleEscaped(cp);
		}
	}
	// Before attribute name state
	//------------------------------------------------------------------
	_stateBeforeAttributeName(cp) {
		switch (cp) {
			case CODE_POINTS.SPACE:
			case CODE_POINTS.LINE_FEED:
			case CODE_POINTS.TABULATION:
			case CODE_POINTS.FORM_FEED: {
				// Ignore whitespace
				break;
			}
			case CODE_POINTS.SOLIDUS:
			case CODE_POINTS.GREATER_THAN_SIGN:
			case CODE_POINTS.EOF: {
				this.state = State.AFTER_ATTRIBUTE_NAME;
				this._stateAfterAttributeName(cp);
				break;
			}
			case CODE_POINTS.EQUALS_SIGN: {
				this._err(ERR.unexpectedEqualsSignBeforeAttributeName);
				this._createAttr("=");
				this.state = State.ATTRIBUTE_NAME;
				break;
			}
			default: {
				this._createAttr("");
				this.state = State.ATTRIBUTE_NAME;
				this._stateAttributeName(cp);
			}
		}
	}
	// Attribute name state
	//------------------------------------------------------------------
	_stateAttributeName(cp) {
		switch (cp) {
			case CODE_POINTS.SPACE:
			case CODE_POINTS.LINE_FEED:
			case CODE_POINTS.TABULATION:
			case CODE_POINTS.FORM_FEED:
			case CODE_POINTS.SOLIDUS:
			case CODE_POINTS.GREATER_THAN_SIGN:
			case CODE_POINTS.EOF: {
				this._leaveAttrName();
				this.state = State.AFTER_ATTRIBUTE_NAME;
				this._stateAfterAttributeName(cp);
				break;
			}
			case CODE_POINTS.EQUALS_SIGN: {
				this._leaveAttrName();
				this.state = State.BEFORE_ATTRIBUTE_VALUE;
				break;
			}
			case CODE_POINTS.QUOTATION_MARK:
			case CODE_POINTS.APOSTROPHE:
			case CODE_POINTS.LESS_THAN_SIGN: {
				this._err(ERR.unexpectedCharacterInAttributeName);
				this.currentAttr.name += String.fromCodePoint(cp);
				break;
			}
			case CODE_POINTS.NULL: {
				this._err(ERR.unexpectedNullCharacter);
				this.currentAttr.name += REPLACEMENT_CHARACTER;
				break;
			}
			default: {
				this.currentAttr.name += String.fromCodePoint(isAsciiUpper(cp) ? toAsciiLower(cp) : cp);
			}
		}
	}
	// After attribute name state
	//------------------------------------------------------------------
	_stateAfterAttributeName(cp) {
		switch (cp) {
			case CODE_POINTS.SPACE:
			case CODE_POINTS.LINE_FEED:
			case CODE_POINTS.TABULATION:
			case CODE_POINTS.FORM_FEED: {
				// Ignore whitespace
				break;
			}
			case CODE_POINTS.SOLIDUS: {
				this.state = State.SELF_CLOSING_START_TAG;
				break;
			}
			case CODE_POINTS.EQUALS_SIGN: {
				this.state = State.BEFORE_ATTRIBUTE_VALUE;
				break;
			}
			case CODE_POINTS.GREATER_THAN_SIGN: {
				this.state = State.DATA;
				this.emitCurrentTagToken();
				break;
			}
			case CODE_POINTS.EOF: {
				this._err(ERR.eofInTag);
				this._emitEOFToken();
				break;
			}
			default: {
				this._createAttr("");
				this.state = State.ATTRIBUTE_NAME;
				this._stateAttributeName(cp);
			}
		}
	}
	// Before attribute value state
	//------------------------------------------------------------------
	_stateBeforeAttributeValue(cp) {
		switch (cp) {
			case CODE_POINTS.SPACE:
			case CODE_POINTS.LINE_FEED:
			case CODE_POINTS.TABULATION:
			case CODE_POINTS.FORM_FEED: {
				// Ignore whitespace
				break;
			}
			case CODE_POINTS.QUOTATION_MARK: {
				this.state = State.ATTRIBUTE_VALUE_DOUBLE_QUOTED;
				break;
			}
			case CODE_POINTS.APOSTROPHE: {
				this.state = State.ATTRIBUTE_VALUE_SINGLE_QUOTED;
				break;
			}
			case CODE_POINTS.GREATER_THAN_SIGN: {
				this._err(ERR.missingAttributeValue);
				this.state = State.DATA;
				this.emitCurrentTagToken();
				break;
			}
			default: {
				this.state = State.ATTRIBUTE_VALUE_UNQUOTED;
				this._stateAttributeValueUnquoted(cp);
			}
		}
	}
	// Attribute value (double-quoted) state
	//------------------------------------------------------------------
	_stateAttributeValueDoubleQuoted(cp) {
		switch (cp) {
			case CODE_POINTS.QUOTATION_MARK: {
				this.state = State.AFTER_ATTRIBUTE_VALUE_QUOTED;
				break;
			}
			case CODE_POINTS.AMPERSAND: {
				this._startCharacterReference();
				break;
			}
			case CODE_POINTS.NULL: {
				this._err(ERR.unexpectedNullCharacter);
				this.currentAttr.value += REPLACEMENT_CHARACTER;
				break;
			}
			case CODE_POINTS.EOF: {
				this._err(ERR.eofInTag);
				this._emitEOFToken();
				break;
			}
			default: {
				this.currentAttr.value += String.fromCodePoint(cp);
			}
		}
	}
	// Attribute value (single-quoted) state
	//------------------------------------------------------------------
	_stateAttributeValueSingleQuoted(cp) {
		switch (cp) {
			case CODE_POINTS.APOSTROPHE: {
				this.state = State.AFTER_ATTRIBUTE_VALUE_QUOTED;
				break;
			}
			case CODE_POINTS.AMPERSAND: {
				this._startCharacterReference();
				break;
			}
			case CODE_POINTS.NULL: {
				this._err(ERR.unexpectedNullCharacter);
				this.currentAttr.value += REPLACEMENT_CHARACTER;
				break;
			}
			case CODE_POINTS.EOF: {
				this._err(ERR.eofInTag);
				this._emitEOFToken();
				break;
			}
			default: {
				this.currentAttr.value += String.fromCodePoint(cp);
			}
		}
	}
	// Attribute value (unquoted) state
	//------------------------------------------------------------------
	_stateAttributeValueUnquoted(cp) {
		switch (cp) {
			case CODE_POINTS.SPACE:
			case CODE_POINTS.LINE_FEED:
			case CODE_POINTS.TABULATION:
			case CODE_POINTS.FORM_FEED: {
				this._leaveAttrValue();
				this.state = State.BEFORE_ATTRIBUTE_NAME;
				break;
			}
			case CODE_POINTS.AMPERSAND: {
				this._startCharacterReference();
				break;
			}
			case CODE_POINTS.GREATER_THAN_SIGN: {
				this._leaveAttrValue();
				this.state = State.DATA;
				this.emitCurrentTagToken();
				break;
			}
			case CODE_POINTS.NULL: {
				this._err(ERR.unexpectedNullCharacter);
				this.currentAttr.value += REPLACEMENT_CHARACTER;
				break;
			}
			case CODE_POINTS.QUOTATION_MARK:
			case CODE_POINTS.APOSTROPHE:
			case CODE_POINTS.LESS_THAN_SIGN:
			case CODE_POINTS.EQUALS_SIGN:
			case CODE_POINTS.GRAVE_ACCENT: {
				this._err(ERR.unexpectedCharacterInUnquotedAttributeValue);
				this.currentAttr.value += String.fromCodePoint(cp);
				break;
			}
			case CODE_POINTS.EOF: {
				this._err(ERR.eofInTag);
				this._emitEOFToken();
				break;
			}
			default: {
				this.currentAttr.value += String.fromCodePoint(cp);
			}
		}
	}
	// After attribute value (quoted) state
	//------------------------------------------------------------------
	_stateAfterAttributeValueQuoted(cp) {
		switch (cp) {
			case CODE_POINTS.SPACE:
			case CODE_POINTS.LINE_FEED:
			case CODE_POINTS.TABULATION:
			case CODE_POINTS.FORM_FEED: {
				this._leaveAttrValue();
				this.state = State.BEFORE_ATTRIBUTE_NAME;
				break;
			}
			case CODE_POINTS.SOLIDUS: {
				this._leaveAttrValue();
				this.state = State.SELF_CLOSING_START_TAG;
				break;
			}
			case CODE_POINTS.GREATER_THAN_SIGN: {
				this._leaveAttrValue();
				this.state = State.DATA;
				this.emitCurrentTagToken();
				break;
			}
			case CODE_POINTS.EOF: {
				this._err(ERR.eofInTag);
				this._emitEOFToken();
				break;
			}
			default: {
				this._err(ERR.missingWhitespaceBetweenAttributes);
				this.state = State.BEFORE_ATTRIBUTE_NAME;
				this._stateBeforeAttributeName(cp);
			}
		}
	}
	// Self-closing start tag state
	//------------------------------------------------------------------
	_stateSelfClosingStartTag(cp) {
		switch (cp) {
			case CODE_POINTS.GREATER_THAN_SIGN: {
				const token = this.currentToken;
				token.selfClosing = true;
				this.state = State.DATA;
				this.emitCurrentTagToken();
				break;
			}
			case CODE_POINTS.EOF: {
				this._err(ERR.eofInTag);
				this._emitEOFToken();
				break;
			}
			default: {
				this._err(ERR.unexpectedSolidusInTag);
				this.state = State.BEFORE_ATTRIBUTE_NAME;
				this._stateBeforeAttributeName(cp);
			}
		}
	}
	// Bogus comment state
	//------------------------------------------------------------------
	_stateBogusComment(cp) {
		const token = this.currentToken;
		switch (cp) {
			case CODE_POINTS.GREATER_THAN_SIGN: {
				this.state = State.DATA;
				this.emitCurrentComment(token);
				break;
			}
			case CODE_POINTS.EOF: {
				this.emitCurrentComment(token);
				this._emitEOFToken();
				break;
			}
			case CODE_POINTS.NULL: {
				this._err(ERR.unexpectedNullCharacter);
				token.data += REPLACEMENT_CHARACTER;
				break;
			}
			default: {
				token.data += String.fromCodePoint(cp);
			}
		}
	}
	// Markup declaration open state
	//------------------------------------------------------------------
	_stateMarkupDeclarationOpen(cp) {
		if (this._consumeSequenceIfMatch(SEQUENCES.DASH_DASH, true)) {
			this._createCommentToken(SEQUENCES.DASH_DASH.length + 1);
			this.state = State.COMMENT_START;
		} else if (this._consumeSequenceIfMatch(SEQUENCES.DOCTYPE, false)) {
			// NOTE: Doctypes tokens are created without fixed offsets. We keep track of the moment a doctype *might* start here.
			this.currentLocation = this.getCurrentLocation(SEQUENCES.DOCTYPE.length + 1);
			this.state = State.DOCTYPE;
		} else if (this._consumeSequenceIfMatch(SEQUENCES.CDATA_START, true)) {
			if (this.inForeignNode) {
				this.state = State.CDATA_SECTION;
			} else {
				this._err(ERR.cdataInHtmlContent);
				this._createCommentToken(SEQUENCES.CDATA_START.length + 1);
				this.currentToken.data = "[CDATA[";
				this.state = State.BOGUS_COMMENT;
			}
		}
		//NOTE: Sequence lookups can be abrupted by hibernation. In that case, lookup
		//results are no longer valid and we will need to start over.
		else if (!this._ensureHibernation()) {
			this._err(ERR.incorrectlyOpenedComment);
			this._createCommentToken(2);
			this.state = State.BOGUS_COMMENT;
			this._stateBogusComment(cp);
		}
	}
	// Comment start state
	//------------------------------------------------------------------
	_stateCommentStart(cp) {
		switch (cp) {
			case CODE_POINTS.HYPHEN_MINUS: {
				this.state = State.COMMENT_START_DASH;
				break;
			}
			case CODE_POINTS.GREATER_THAN_SIGN: {
				this._err(ERR.abruptClosingOfEmptyComment);
				this.state = State.DATA;
				const token = this.currentToken;
				this.emitCurrentComment(token);
				break;
			}
			default: {
				this.state = State.COMMENT;
				this._stateComment(cp);
			}
		}
	}
	// Comment start dash state
	//------------------------------------------------------------------
	_stateCommentStartDash(cp) {
		const token = this.currentToken;
		switch (cp) {
			case CODE_POINTS.HYPHEN_MINUS: {
				this.state = State.COMMENT_END;
				break;
			}
			case CODE_POINTS.GREATER_THAN_SIGN: {
				this._err(ERR.abruptClosingOfEmptyComment);
				this.state = State.DATA;
				this.emitCurrentComment(token);
				break;
			}
			case CODE_POINTS.EOF: {
				this._err(ERR.eofInComment);
				this.emitCurrentComment(token);
				this._emitEOFToken();
				break;
			}
			default: {
				token.data += "-";
				this.state = State.COMMENT;
				this._stateComment(cp);
			}
		}
	}
	// Comment state
	//------------------------------------------------------------------
	_stateComment(cp) {
		const token = this.currentToken;
		switch (cp) {
			case CODE_POINTS.HYPHEN_MINUS: {
				this.state = State.COMMENT_END_DASH;
				break;
			}
			case CODE_POINTS.LESS_THAN_SIGN: {
				token.data += "<";
				this.state = State.COMMENT_LESS_THAN_SIGN;
				break;
			}
			case CODE_POINTS.NULL: {
				this._err(ERR.unexpectedNullCharacter);
				token.data += REPLACEMENT_CHARACTER;
				break;
			}
			case CODE_POINTS.EOF: {
				this._err(ERR.eofInComment);
				this.emitCurrentComment(token);
				this._emitEOFToken();
				break;
			}
			default: {
				token.data += String.fromCodePoint(cp);
			}
		}
	}
	// Comment less-than sign state
	//------------------------------------------------------------------
	_stateCommentLessThanSign(cp) {
		const token = this.currentToken;
		switch (cp) {
			case CODE_POINTS.EXCLAMATION_MARK: {
				token.data += "!";
				this.state = State.COMMENT_LESS_THAN_SIGN_BANG;
				break;
			}
			case CODE_POINTS.LESS_THAN_SIGN: {
				token.data += "<";
				break;
			}
			default: {
				this.state = State.COMMENT;
				this._stateComment(cp);
			}
		}
	}
	// Comment less-than sign bang state
	//------------------------------------------------------------------
	_stateCommentLessThanSignBang(cp) {
		if (cp === CODE_POINTS.HYPHEN_MINUS) {
			this.state = State.COMMENT_LESS_THAN_SIGN_BANG_DASH;
		} else {
			this.state = State.COMMENT;
			this._stateComment(cp);
		}
	}
	// Comment less-than sign bang dash state
	//------------------------------------------------------------------
	_stateCommentLessThanSignBangDash(cp) {
		if (cp === CODE_POINTS.HYPHEN_MINUS) {
			this.state = State.COMMENT_LESS_THAN_SIGN_BANG_DASH_DASH;
		} else {
			this.state = State.COMMENT_END_DASH;
			this._stateCommentEndDash(cp);
		}
	}
	// Comment less-than sign bang dash dash state
	//------------------------------------------------------------------
	_stateCommentLessThanSignBangDashDash(cp) {
		if (cp !== CODE_POINTS.GREATER_THAN_SIGN && cp !== CODE_POINTS.EOF) {
			this._err(ERR.nestedComment);
		}
		this.state = State.COMMENT_END;
		this._stateCommentEnd(cp);
	}
	// Comment end dash state
	//------------------------------------------------------------------
	_stateCommentEndDash(cp) {
		const token = this.currentToken;
		switch (cp) {
			case CODE_POINTS.HYPHEN_MINUS: {
				this.state = State.COMMENT_END;
				break;
			}
			case CODE_POINTS.EOF: {
				this._err(ERR.eofInComment);
				this.emitCurrentComment(token);
				this._emitEOFToken();
				break;
			}
			default: {
				token.data += "-";
				this.state = State.COMMENT;
				this._stateComment(cp);
			}
		}
	}
	// Comment end state
	//------------------------------------------------------------------
	_stateCommentEnd(cp) {
		const token = this.currentToken;
		switch (cp) {
			case CODE_POINTS.GREATER_THAN_SIGN: {
				this.state = State.DATA;
				this.emitCurrentComment(token);
				break;
			}
			case CODE_POINTS.EXCLAMATION_MARK: {
				this.state = State.COMMENT_END_BANG;
				break;
			}
			case CODE_POINTS.HYPHEN_MINUS: {
				token.data += "-";
				break;
			}
			case CODE_POINTS.EOF: {
				this._err(ERR.eofInComment);
				this.emitCurrentComment(token);
				this._emitEOFToken();
				break;
			}
			default: {
				token.data += "--";
				this.state = State.COMMENT;
				this._stateComment(cp);
			}
		}
	}
	// Comment end bang state
	//------------------------------------------------------------------
	_stateCommentEndBang(cp) {
		const token = this.currentToken;
		switch (cp) {
			case CODE_POINTS.HYPHEN_MINUS: {
				token.data += "--!";
				this.state = State.COMMENT_END_DASH;
				break;
			}
			case CODE_POINTS.GREATER_THAN_SIGN: {
				this._err(ERR.incorrectlyClosedComment);
				this.state = State.DATA;
				this.emitCurrentComment(token);
				break;
			}
			case CODE_POINTS.EOF: {
				this._err(ERR.eofInComment);
				this.emitCurrentComment(token);
				this._emitEOFToken();
				break;
			}
			default: {
				token.data += "--!";
				this.state = State.COMMENT;
				this._stateComment(cp);
			}
		}
	}
	// DOCTYPE state
	//------------------------------------------------------------------
	_stateDoctype(cp) {
		switch (cp) {
			case CODE_POINTS.SPACE:
			case CODE_POINTS.LINE_FEED:
			case CODE_POINTS.TABULATION:
			case CODE_POINTS.FORM_FEED: {
				this.state = State.BEFORE_DOCTYPE_NAME;
				break;
			}
			case CODE_POINTS.GREATER_THAN_SIGN: {
				this.state = State.BEFORE_DOCTYPE_NAME;
				this._stateBeforeDoctypeName(cp);
				break;
			}
			case CODE_POINTS.EOF: {
				this._err(ERR.eofInDoctype);
				this._createDoctypeToken(null);
				const token = this.currentToken;
				token.forceQuirks = true;
				this.emitCurrentDoctype(token);
				this._emitEOFToken();
				break;
			}
			default: {
				this._err(ERR.missingWhitespaceBeforeDoctypeName);
				this.state = State.BEFORE_DOCTYPE_NAME;
				this._stateBeforeDoctypeName(cp);
			}
		}
	}
	// Before DOCTYPE name state
	//------------------------------------------------------------------
	_stateBeforeDoctypeName(cp) {
		if (isAsciiUpper(cp)) {
			this._createDoctypeToken(String.fromCharCode(toAsciiLower(cp)));
			this.state = State.DOCTYPE_NAME;
		} else
			switch (cp) {
				case CODE_POINTS.SPACE:
				case CODE_POINTS.LINE_FEED:
				case CODE_POINTS.TABULATION:
				case CODE_POINTS.FORM_FEED: {
					// Ignore whitespace
					break;
				}
				case CODE_POINTS.NULL: {
					this._err(ERR.unexpectedNullCharacter);
					this._createDoctypeToken(REPLACEMENT_CHARACTER);
					this.state = State.DOCTYPE_NAME;
					break;
				}
				case CODE_POINTS.GREATER_THAN_SIGN: {
					this._err(ERR.missingDoctypeName);
					this._createDoctypeToken(null);
					const token = this.currentToken;
					token.forceQuirks = true;
					this.emitCurrentDoctype(token);
					this.state = State.DATA;
					break;
				}
				case CODE_POINTS.EOF: {
					this._err(ERR.eofInDoctype);
					this._createDoctypeToken(null);
					const token = this.currentToken;
					token.forceQuirks = true;
					this.emitCurrentDoctype(token);
					this._emitEOFToken();
					break;
				}
				default: {
					this._createDoctypeToken(String.fromCodePoint(cp));
					this.state = State.DOCTYPE_NAME;
				}
			}
	}
	// DOCTYPE name state
	//------------------------------------------------------------------
	_stateDoctypeName(cp) {
		const token = this.currentToken;
		switch (cp) {
			case CODE_POINTS.SPACE:
			case CODE_POINTS.LINE_FEED:
			case CODE_POINTS.TABULATION:
			case CODE_POINTS.FORM_FEED: {
				this.state = State.AFTER_DOCTYPE_NAME;
				break;
			}
			case CODE_POINTS.GREATER_THAN_SIGN: {
				this.state = State.DATA;
				this.emitCurrentDoctype(token);
				break;
			}
			case CODE_POINTS.NULL: {
				this._err(ERR.unexpectedNullCharacter);
				token.name += REPLACEMENT_CHARACTER;
				break;
			}
			case CODE_POINTS.EOF: {
				this._err(ERR.eofInDoctype);
				token.forceQuirks = true;
				this.emitCurrentDoctype(token);
				this._emitEOFToken();
				break;
			}
			default: {
				token.name += String.fromCodePoint(isAsciiUpper(cp) ? toAsciiLower(cp) : cp);
			}
		}
	}
	// After DOCTYPE name state
	//------------------------------------------------------------------
	_stateAfterDoctypeName(cp) {
		const token = this.currentToken;
		switch (cp) {
			case CODE_POINTS.SPACE:
			case CODE_POINTS.LINE_FEED:
			case CODE_POINTS.TABULATION:
			case CODE_POINTS.FORM_FEED: {
				// Ignore whitespace
				break;
			}
			case CODE_POINTS.GREATER_THAN_SIGN: {
				this.state = State.DATA;
				this.emitCurrentDoctype(token);
				break;
			}
			case CODE_POINTS.EOF: {
				this._err(ERR.eofInDoctype);
				token.forceQuirks = true;
				this.emitCurrentDoctype(token);
				this._emitEOFToken();
				break;
			}
			default: {
				if (this._consumeSequenceIfMatch(SEQUENCES.PUBLIC, false)) {
					this.state = State.AFTER_DOCTYPE_PUBLIC_KEYWORD;
				} else if (this._consumeSequenceIfMatch(SEQUENCES.SYSTEM, false)) {
					this.state = State.AFTER_DOCTYPE_SYSTEM_KEYWORD;
				}
				//NOTE: sequence lookup can be abrupted by hibernation. In that case lookup
				//results are no longer valid and we will need to start over.
				else if (!this._ensureHibernation()) {
					this._err(ERR.invalidCharacterSequenceAfterDoctypeName);
					token.forceQuirks = true;
					this.state = State.BOGUS_DOCTYPE;
					this._stateBogusDoctype(cp);
				}
			}
		}
	}
	// After DOCTYPE public keyword state
	//------------------------------------------------------------------
	_stateAfterDoctypePublicKeyword(cp) {
		const token = this.currentToken;
		switch (cp) {
			case CODE_POINTS.SPACE:
			case CODE_POINTS.LINE_FEED:
			case CODE_POINTS.TABULATION:
			case CODE_POINTS.FORM_FEED: {
				this.state = State.BEFORE_DOCTYPE_PUBLIC_IDENTIFIER;
				break;
			}
			case CODE_POINTS.QUOTATION_MARK: {
				this._err(ERR.missingWhitespaceAfterDoctypePublicKeyword);
				token.publicId = "";
				this.state = State.DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED;
				break;
			}
			case CODE_POINTS.APOSTROPHE: {
				this._err(ERR.missingWhitespaceAfterDoctypePublicKeyword);
				token.publicId = "";
				this.state = State.DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED;
				break;
			}
			case CODE_POINTS.GREATER_THAN_SIGN: {
				this._err(ERR.missingDoctypePublicIdentifier);
				token.forceQuirks = true;
				this.state = State.DATA;
				this.emitCurrentDoctype(token);
				break;
			}
			case CODE_POINTS.EOF: {
				this._err(ERR.eofInDoctype);
				token.forceQuirks = true;
				this.emitCurrentDoctype(token);
				this._emitEOFToken();
				break;
			}
			default: {
				this._err(ERR.missingQuoteBeforeDoctypePublicIdentifier);
				token.forceQuirks = true;
				this.state = State.BOGUS_DOCTYPE;
				this._stateBogusDoctype(cp);
			}
		}
	}
	// Before DOCTYPE public identifier state
	//------------------------------------------------------------------
	_stateBeforeDoctypePublicIdentifier(cp) {
		const token = this.currentToken;
		switch (cp) {
			case CODE_POINTS.SPACE:
			case CODE_POINTS.LINE_FEED:
			case CODE_POINTS.TABULATION:
			case CODE_POINTS.FORM_FEED: {
				// Ignore whitespace
				break;
			}
			case CODE_POINTS.QUOTATION_MARK: {
				token.publicId = "";
				this.state = State.DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED;
				break;
			}
			case CODE_POINTS.APOSTROPHE: {
				token.publicId = "";
				this.state = State.DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED;
				break;
			}
			case CODE_POINTS.GREATER_THAN_SIGN: {
				this._err(ERR.missingDoctypePublicIdentifier);
				token.forceQuirks = true;
				this.state = State.DATA;
				this.emitCurrentDoctype(token);
				break;
			}
			case CODE_POINTS.EOF: {
				this._err(ERR.eofInDoctype);
				token.forceQuirks = true;
				this.emitCurrentDoctype(token);
				this._emitEOFToken();
				break;
			}
			default: {
				this._err(ERR.missingQuoteBeforeDoctypePublicIdentifier);
				token.forceQuirks = true;
				this.state = State.BOGUS_DOCTYPE;
				this._stateBogusDoctype(cp);
			}
		}
	}
	// DOCTYPE public identifier (double-quoted) state
	//------------------------------------------------------------------
	_stateDoctypePublicIdentifierDoubleQuoted(cp) {
		const token = this.currentToken;
		switch (cp) {
			case CODE_POINTS.QUOTATION_MARK: {
				this.state = State.AFTER_DOCTYPE_PUBLIC_IDENTIFIER;
				break;
			}
			case CODE_POINTS.NULL: {
				this._err(ERR.unexpectedNullCharacter);
				token.publicId += REPLACEMENT_CHARACTER;
				break;
			}
			case CODE_POINTS.GREATER_THAN_SIGN: {
				this._err(ERR.abruptDoctypePublicIdentifier);
				token.forceQuirks = true;
				this.emitCurrentDoctype(token);
				this.state = State.DATA;
				break;
			}
			case CODE_POINTS.EOF: {
				this._err(ERR.eofInDoctype);
				token.forceQuirks = true;
				this.emitCurrentDoctype(token);
				this._emitEOFToken();
				break;
			}
			default: {
				token.publicId += String.fromCodePoint(cp);
			}
		}
	}
	// DOCTYPE public identifier (single-quoted) state
	//------------------------------------------------------------------
	_stateDoctypePublicIdentifierSingleQuoted(cp) {
		const token = this.currentToken;
		switch (cp) {
			case CODE_POINTS.APOSTROPHE: {
				this.state = State.AFTER_DOCTYPE_PUBLIC_IDENTIFIER;
				break;
			}
			case CODE_POINTS.NULL: {
				this._err(ERR.unexpectedNullCharacter);
				token.publicId += REPLACEMENT_CHARACTER;
				break;
			}
			case CODE_POINTS.GREATER_THAN_SIGN: {
				this._err(ERR.abruptDoctypePublicIdentifier);
				token.forceQuirks = true;
				this.emitCurrentDoctype(token);
				this.state = State.DATA;
				break;
			}
			case CODE_POINTS.EOF: {
				this._err(ERR.eofInDoctype);
				token.forceQuirks = true;
				this.emitCurrentDoctype(token);
				this._emitEOFToken();
				break;
			}
			default: {
				token.publicId += String.fromCodePoint(cp);
			}
		}
	}
	// After DOCTYPE public identifier state
	//------------------------------------------------------------------
	_stateAfterDoctypePublicIdentifier(cp) {
		const token = this.currentToken;
		switch (cp) {
			case CODE_POINTS.SPACE:
			case CODE_POINTS.LINE_FEED:
			case CODE_POINTS.TABULATION:
			case CODE_POINTS.FORM_FEED: {
				this.state = State.BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS;
				break;
			}
			case CODE_POINTS.GREATER_THAN_SIGN: {
				this.state = State.DATA;
				this.emitCurrentDoctype(token);
				break;
			}
			case CODE_POINTS.QUOTATION_MARK: {
				this._err(ERR.missingWhitespaceBetweenDoctypePublicAndSystemIdentifiers);
				token.systemId = "";
				this.state = State.DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED;
				break;
			}
			case CODE_POINTS.APOSTROPHE: {
				this._err(ERR.missingWhitespaceBetweenDoctypePublicAndSystemIdentifiers);
				token.systemId = "";
				this.state = State.DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED;
				break;
			}
			case CODE_POINTS.EOF: {
				this._err(ERR.eofInDoctype);
				token.forceQuirks = true;
				this.emitCurrentDoctype(token);
				this._emitEOFToken();
				break;
			}
			default: {
				this._err(ERR.missingQuoteBeforeDoctypeSystemIdentifier);
				token.forceQuirks = true;
				this.state = State.BOGUS_DOCTYPE;
				this._stateBogusDoctype(cp);
			}
		}
	}
	// Between DOCTYPE public and system identifiers state
	//------------------------------------------------------------------
	_stateBetweenDoctypePublicAndSystemIdentifiers(cp) {
		const token = this.currentToken;
		switch (cp) {
			case CODE_POINTS.SPACE:
			case CODE_POINTS.LINE_FEED:
			case CODE_POINTS.TABULATION:
			case CODE_POINTS.FORM_FEED: {
				// Ignore whitespace
				break;
			}
			case CODE_POINTS.GREATER_THAN_SIGN: {
				this.emitCurrentDoctype(token);
				this.state = State.DATA;
				break;
			}
			case CODE_POINTS.QUOTATION_MARK: {
				token.systemId = "";
				this.state = State.DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED;
				break;
			}
			case CODE_POINTS.APOSTROPHE: {
				token.systemId = "";
				this.state = State.DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED;
				break;
			}
			case CODE_POINTS.EOF: {
				this._err(ERR.eofInDoctype);
				token.forceQuirks = true;
				this.emitCurrentDoctype(token);
				this._emitEOFToken();
				break;
			}
			default: {
				this._err(ERR.missingQuoteBeforeDoctypeSystemIdentifier);
				token.forceQuirks = true;
				this.state = State.BOGUS_DOCTYPE;
				this._stateBogusDoctype(cp);
			}
		}
	}
	// After DOCTYPE system keyword state
	//------------------------------------------------------------------
	_stateAfterDoctypeSystemKeyword(cp) {
		const token = this.currentToken;
		switch (cp) {
			case CODE_POINTS.SPACE:
			case CODE_POINTS.LINE_FEED:
			case CODE_POINTS.TABULATION:
			case CODE_POINTS.FORM_FEED: {
				this.state = State.BEFORE_DOCTYPE_SYSTEM_IDENTIFIER;
				break;
			}
			case CODE_POINTS.QUOTATION_MARK: {
				this._err(ERR.missingWhitespaceAfterDoctypeSystemKeyword);
				token.systemId = "";
				this.state = State.DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED;
				break;
			}
			case CODE_POINTS.APOSTROPHE: {
				this._err(ERR.missingWhitespaceAfterDoctypeSystemKeyword);
				token.systemId = "";
				this.state = State.DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED;
				break;
			}
			case CODE_POINTS.GREATER_THAN_SIGN: {
				this._err(ERR.missingDoctypeSystemIdentifier);
				token.forceQuirks = true;
				this.state = State.DATA;
				this.emitCurrentDoctype(token);
				break;
			}
			case CODE_POINTS.EOF: {
				this._err(ERR.eofInDoctype);
				token.forceQuirks = true;
				this.emitCurrentDoctype(token);
				this._emitEOFToken();
				break;
			}
			default: {
				this._err(ERR.missingQuoteBeforeDoctypeSystemIdentifier);
				token.forceQuirks = true;
				this.state = State.BOGUS_DOCTYPE;
				this._stateBogusDoctype(cp);
			}
		}
	}
	// Before DOCTYPE system identifier state
	//------------------------------------------------------------------
	_stateBeforeDoctypeSystemIdentifier(cp) {
		const token = this.currentToken;
		switch (cp) {
			case CODE_POINTS.SPACE:
			case CODE_POINTS.LINE_FEED:
			case CODE_POINTS.TABULATION:
			case CODE_POINTS.FORM_FEED: {
				// Ignore whitespace
				break;
			}
			case CODE_POINTS.QUOTATION_MARK: {
				token.systemId = "";
				this.state = State.DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED;
				break;
			}
			case CODE_POINTS.APOSTROPHE: {
				token.systemId = "";
				this.state = State.DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED;
				break;
			}
			case CODE_POINTS.GREATER_THAN_SIGN: {
				this._err(ERR.missingDoctypeSystemIdentifier);
				token.forceQuirks = true;
				this.state = State.DATA;
				this.emitCurrentDoctype(token);
				break;
			}
			case CODE_POINTS.EOF: {
				this._err(ERR.eofInDoctype);
				token.forceQuirks = true;
				this.emitCurrentDoctype(token);
				this._emitEOFToken();
				break;
			}
			default: {
				this._err(ERR.missingQuoteBeforeDoctypeSystemIdentifier);
				token.forceQuirks = true;
				this.state = State.BOGUS_DOCTYPE;
				this._stateBogusDoctype(cp);
			}
		}
	}
	// DOCTYPE system identifier (double-quoted) state
	//------------------------------------------------------------------
	_stateDoctypeSystemIdentifierDoubleQuoted(cp) {
		const token = this.currentToken;
		switch (cp) {
			case CODE_POINTS.QUOTATION_MARK: {
				this.state = State.AFTER_DOCTYPE_SYSTEM_IDENTIFIER;
				break;
			}
			case CODE_POINTS.NULL: {
				this._err(ERR.unexpectedNullCharacter);
				token.systemId += REPLACEMENT_CHARACTER;
				break;
			}
			case CODE_POINTS.GREATER_THAN_SIGN: {
				this._err(ERR.abruptDoctypeSystemIdentifier);
				token.forceQuirks = true;
				this.emitCurrentDoctype(token);
				this.state = State.DATA;
				break;
			}
			case CODE_POINTS.EOF: {
				this._err(ERR.eofInDoctype);
				token.forceQuirks = true;
				this.emitCurrentDoctype(token);
				this._emitEOFToken();
				break;
			}
			default: {
				token.systemId += String.fromCodePoint(cp);
			}
		}
	}
	// DOCTYPE system identifier (single-quoted) state
	//------------------------------------------------------------------
	_stateDoctypeSystemIdentifierSingleQuoted(cp) {
		const token = this.currentToken;
		switch (cp) {
			case CODE_POINTS.APOSTROPHE: {
				this.state = State.AFTER_DOCTYPE_SYSTEM_IDENTIFIER;
				break;
			}
			case CODE_POINTS.NULL: {
				this._err(ERR.unexpectedNullCharacter);
				token.systemId += REPLACEMENT_CHARACTER;
				break;
			}
			case CODE_POINTS.GREATER_THAN_SIGN: {
				this._err(ERR.abruptDoctypeSystemIdentifier);
				token.forceQuirks = true;
				this.emitCurrentDoctype(token);
				this.state = State.DATA;
				break;
			}
			case CODE_POINTS.EOF: {
				this._err(ERR.eofInDoctype);
				token.forceQuirks = true;
				this.emitCurrentDoctype(token);
				this._emitEOFToken();
				break;
			}
			default: {
				token.systemId += String.fromCodePoint(cp);
			}
		}
	}
	// After DOCTYPE system identifier state
	//------------------------------------------------------------------
	_stateAfterDoctypeSystemIdentifier(cp) {
		const token = this.currentToken;
		switch (cp) {
			case CODE_POINTS.SPACE:
			case CODE_POINTS.LINE_FEED:
			case CODE_POINTS.TABULATION:
			case CODE_POINTS.FORM_FEED: {
				// Ignore whitespace
				break;
			}
			case CODE_POINTS.GREATER_THAN_SIGN: {
				this.emitCurrentDoctype(token);
				this.state = State.DATA;
				break;
			}
			case CODE_POINTS.EOF: {
				this._err(ERR.eofInDoctype);
				token.forceQuirks = true;
				this.emitCurrentDoctype(token);
				this._emitEOFToken();
				break;
			}
			default: {
				this._err(ERR.unexpectedCharacterAfterDoctypeSystemIdentifier);
				this.state = State.BOGUS_DOCTYPE;
				this._stateBogusDoctype(cp);
			}
		}
	}
	// Bogus DOCTYPE state
	//------------------------------------------------------------------
	_stateBogusDoctype(cp) {
		const token = this.currentToken;
		switch (cp) {
			case CODE_POINTS.GREATER_THAN_SIGN: {
				this.emitCurrentDoctype(token);
				this.state = State.DATA;
				break;
			}
			case CODE_POINTS.NULL: {
				this._err(ERR.unexpectedNullCharacter);
				break;
			}
			case CODE_POINTS.EOF: {
				this.emitCurrentDoctype(token);
				this._emitEOFToken();
				break;
			}
			// Do nothing
		}
	}
	// CDATA section state
	//------------------------------------------------------------------
	_stateCdataSection(cp) {
		switch (cp) {
			case CODE_POINTS.RIGHT_SQUARE_BRACKET: {
				this.state = State.CDATA_SECTION_BRACKET;
				break;
			}
			case CODE_POINTS.EOF: {
				this._err(ERR.eofInCdata);
				this._emitEOFToken();
				break;
			}
			default: {
				this._emitCodePoint(cp);
			}
		}
	}
	// CDATA section bracket state
	//------------------------------------------------------------------
	_stateCdataSectionBracket(cp) {
		if (cp === CODE_POINTS.RIGHT_SQUARE_BRACKET) {
			this.state = State.CDATA_SECTION_END;
		} else {
			this._emitChars("]");
			this.state = State.CDATA_SECTION;
			this._stateCdataSection(cp);
		}
	}
	// CDATA section end state
	//------------------------------------------------------------------
	_stateCdataSectionEnd(cp) {
		switch (cp) {
			case CODE_POINTS.GREATER_THAN_SIGN: {
				this.state = State.DATA;
				break;
			}
			case CODE_POINTS.RIGHT_SQUARE_BRACKET: {
				this._emitChars("]");
				break;
			}
			default: {
				this._emitChars("]]");
				this.state = State.CDATA_SECTION;
				this._stateCdataSection(cp);
			}
		}
	}
	// Character reference state
	//------------------------------------------------------------------
	_stateCharacterReference() {
		let length = this.entityDecoder.write(this.preprocessor.html, this.preprocessor.pos);
		if (length < 0) {
			if (this.preprocessor.lastChunkWritten) {
				length = this.entityDecoder.end();
			} else {
				// Wait for the rest of the entity.
				this.active = false;
				// Mark the entire buffer as read.
				this.preprocessor.pos = this.preprocessor.html.length - 1;
				this.consumedAfterSnapshot = 0;
				this.preprocessor.endOfChunkHit = true;
				return;
			}
		}
		if (length === 0) {
			// This was not a valid entity. Go back to the beginning, and
			// figure out what to do.
			this.preprocessor.pos = this.entityStartPos;
			this._flushCodePointConsumedAsCharacterReference(CODE_POINTS.AMPERSAND);
			this.state = !this._isCharacterReferenceInAttribute() && isAsciiAlphaNumeric(this.preprocessor.peek(1)) ? State.AMBIGUOUS_AMPERSAND : this.returnState;
		} else {
			// We successfully parsed an entity. Switch to the return state.
			this.state = this.returnState;
		}
	}
	// Ambiguos ampersand state
	//------------------------------------------------------------------
	_stateAmbiguousAmpersand(cp) {
		if (isAsciiAlphaNumeric(cp)) {
			this._flushCodePointConsumedAsCharacterReference(cp);
		} else {
			if (cp === CODE_POINTS.SEMICOLON) {
				this._err(ERR.unknownNamedCharacterReference);
			}
			this.state = this.returnState;
			this._callState(cp);
		}
	}
}

//Element utils
const IMPLICIT_END_TAG_REQUIRED = new Set([TAG_ID.DD, TAG_ID.DT, TAG_ID.LI, TAG_ID.OPTGROUP, TAG_ID.OPTION, TAG_ID.P, TAG_ID.RB, TAG_ID.RP, TAG_ID.RT, TAG_ID.RTC]);
const IMPLICIT_END_TAG_REQUIRED_THOROUGHLY = new Set([...IMPLICIT_END_TAG_REQUIRED, TAG_ID.CAPTION, TAG_ID.COLGROUP, TAG_ID.TBODY, TAG_ID.TD, TAG_ID.TFOOT, TAG_ID.TH, TAG_ID.THEAD, TAG_ID.TR]);
const SCOPING_ELEMENTS_HTML = new Set([TAG_ID.APPLET, TAG_ID.CAPTION, TAG_ID.HTML, TAG_ID.MARQUEE, TAG_ID.OBJECT, TAG_ID.TABLE, TAG_ID.TD, TAG_ID.TEMPLATE, TAG_ID.TH]);
const SCOPING_ELEMENTS_HTML_LIST = new Set([...SCOPING_ELEMENTS_HTML, TAG_ID.OL, TAG_ID.UL]);
const SCOPING_ELEMENTS_HTML_BUTTON = new Set([...SCOPING_ELEMENTS_HTML, TAG_ID.BUTTON]);
const SCOPING_ELEMENTS_MATHML = new Set([TAG_ID.ANNOTATION_XML, TAG_ID.MI, TAG_ID.MN, TAG_ID.MO, TAG_ID.MS, TAG_ID.MTEXT]);
const SCOPING_ELEMENTS_SVG = new Set([TAG_ID.DESC, TAG_ID.FOREIGN_OBJECT, TAG_ID.TITLE]);
const TABLE_ROW_CONTEXT = new Set([TAG_ID.TR, TAG_ID.TEMPLATE, TAG_ID.HTML]);
const TABLE_BODY_CONTEXT = new Set([TAG_ID.TBODY, TAG_ID.TFOOT, TAG_ID.THEAD, TAG_ID.TEMPLATE, TAG_ID.HTML]);
const TABLE_CONTEXT = new Set([TAG_ID.TABLE, TAG_ID.TEMPLATE, TAG_ID.HTML]);
const TABLE_CELLS = new Set([TAG_ID.TD, TAG_ID.TH]);
//Stack of open elements
class OpenElementStack {
	get currentTmplContentOrNode() {
		return this._isInTemplate() ? this.treeAdapter.getTemplateContent(this.current) : this.current;
	}
	constructor(document, treeAdapter, handler) {
		this.treeAdapter = treeAdapter;
		this.handler = handler;
		this.items = [];
		this.tagIDs = [];
		this.stackTop = -1;
		this.tmplCount = 0;
		this.currentTagId = TAG_ID.UNKNOWN;
		this.current = document;
	}
	//Index of element
	_indexOf(element) {
		return this.items.lastIndexOf(element, this.stackTop);
	}
	//Update current element
	_isInTemplate() {
		return this.currentTagId === TAG_ID.TEMPLATE && this.treeAdapter.getNamespaceURI(this.current) === NS.HTML;
	}
	_updateCurrentElement() {
		this.current = this.items[this.stackTop];
		this.currentTagId = this.tagIDs[this.stackTop];
	}
	//Mutations
	push(element, tagID) {
		this.stackTop++;
		this.items[this.stackTop] = element;
		this.current = element;
		this.tagIDs[this.stackTop] = tagID;
		this.currentTagId = tagID;
		if (this._isInTemplate()) {
			this.tmplCount++;
		}
		this.handler.onItemPush(element, tagID, true);
	}
	pop() {
		const popped = this.current;
		if (this.tmplCount > 0 && this._isInTemplate()) {
			this.tmplCount--;
		}
		this.stackTop--;
		this._updateCurrentElement();
		this.handler.onItemPop(popped, true);
	}
	replace(oldElement, newElement) {
		const idx = this._indexOf(oldElement);
		this.items[idx] = newElement;
		if (idx === this.stackTop) {
			this.current = newElement;
		}
	}
	insertAfter(referenceElement, newElement, newElementID) {
		const insertionIdx = this._indexOf(referenceElement) + 1;
		this.items.splice(insertionIdx, 0, newElement);
		this.tagIDs.splice(insertionIdx, 0, newElementID);
		this.stackTop++;
		if (insertionIdx === this.stackTop) {
			this._updateCurrentElement();
		}
		if (this.current && this.currentTagId !== undefined) {
			this.handler.onItemPush(this.current, this.currentTagId, insertionIdx === this.stackTop);
		}
	}
	popUntilTagNamePopped(tagName) {
		let targetIdx = this.stackTop + 1;
		do {
			targetIdx = this.tagIDs.lastIndexOf(tagName, targetIdx - 1);
		} while (targetIdx > 0 && this.treeAdapter.getNamespaceURI(this.items[targetIdx]) !== NS.HTML);
		this.shortenToLength(Math.max(targetIdx, 0));
	}
	shortenToLength(idx) {
		while (this.stackTop >= idx) {
			const popped = this.current;
			if (this.tmplCount > 0 && this._isInTemplate()) {
				this.tmplCount -= 1;
			}
			this.stackTop--;
			this._updateCurrentElement();
			this.handler.onItemPop(popped, this.stackTop < idx);
		}
	}
	popUntilElementPopped(element) {
		const idx = this._indexOf(element);
		this.shortenToLength(Math.max(idx, 0));
	}
	popUntilPopped(tagNames, targetNS) {
		const idx = this._indexOfTagNames(tagNames, targetNS);
		this.shortenToLength(Math.max(idx, 0));
	}
	popUntilNumberedHeaderPopped() {
		this.popUntilPopped(NUMBERED_HEADERS, NS.HTML);
	}
	popUntilTableCellPopped() {
		this.popUntilPopped(TABLE_CELLS, NS.HTML);
	}
	popAllUpToHtmlElement() {
		//NOTE: here we assume that the root <html> element is always first in the open element stack, so
		//we perform this fast stack clean up.
		this.tmplCount = 0;
		this.shortenToLength(1);
	}
	_indexOfTagNames(tagNames, namespace) {
		for (let i = this.stackTop; i >= 0; i--) {
			if (tagNames.has(this.tagIDs[i]) && this.treeAdapter.getNamespaceURI(this.items[i]) === namespace) {
				return i;
			}
		}
		return -1;
	}
	clearBackTo(tagNames, targetNS) {
		const idx = this._indexOfTagNames(tagNames, targetNS);
		this.shortenToLength(idx + 1);
	}
	clearBackToTableContext() {
		this.clearBackTo(TABLE_CONTEXT, NS.HTML);
	}
	clearBackToTableBodyContext() {
		this.clearBackTo(TABLE_BODY_CONTEXT, NS.HTML);
	}
	clearBackToTableRowContext() {
		this.clearBackTo(TABLE_ROW_CONTEXT, NS.HTML);
	}
	remove(element) {
		const idx = this._indexOf(element);
		if (idx >= 0) {
			if (idx === this.stackTop) {
				this.pop();
			} else {
				this.items.splice(idx, 1);
				this.tagIDs.splice(idx, 1);
				this.stackTop--;
				this._updateCurrentElement();
				this.handler.onItemPop(element, false);
			}
		}
	}
	//Search
	tryPeekProperlyNestedBodyElement() {
		//Properly nested <body> element (should be second element in stack).
		return this.stackTop >= 1 && this.tagIDs[1] === TAG_ID.BODY ? this.items[1] : null;
	}
	contains(element) {
		return this._indexOf(element) > -1;
	}
	getCommonAncestor(element) {
		const elementIdx = this._indexOf(element) - 1;
		return elementIdx >= 0 ? this.items[elementIdx] : null;
	}
	isRootHtmlElementCurrent() {
		return this.stackTop === 0 && this.tagIDs[0] === TAG_ID.HTML;
	}
	//Element in scope
	hasInDynamicScope(tagName, htmlScope) {
		for (let i = this.stackTop; i >= 0; i--) {
			const tn = this.tagIDs[i];
			switch (this.treeAdapter.getNamespaceURI(this.items[i])) {
				case NS.HTML: {
					if (tn === tagName) return true;
					if (htmlScope.has(tn)) return false;
					break;
				}
				case NS.SVG: {
					if (SCOPING_ELEMENTS_SVG.has(tn)) return false;
					break;
				}
				case NS.MATHML: {
					if (SCOPING_ELEMENTS_MATHML.has(tn)) return false;
					break;
				}
			}
		}
		return true;
	}
	hasInScope(tagName) {
		return this.hasInDynamicScope(tagName, SCOPING_ELEMENTS_HTML);
	}
	hasInListItemScope(tagName) {
		return this.hasInDynamicScope(tagName, SCOPING_ELEMENTS_HTML_LIST);
	}
	hasInButtonScope(tagName) {
		return this.hasInDynamicScope(tagName, SCOPING_ELEMENTS_HTML_BUTTON);
	}
	hasNumberedHeaderInScope() {
		for (let i = this.stackTop; i >= 0; i--) {
			const tn = this.tagIDs[i];
			switch (this.treeAdapter.getNamespaceURI(this.items[i])) {
				case NS.HTML: {
					if (NUMBERED_HEADERS.has(tn)) return true;
					if (SCOPING_ELEMENTS_HTML.has(tn)) return false;
					break;
				}
				case NS.SVG: {
					if (SCOPING_ELEMENTS_SVG.has(tn)) return false;
					break;
				}
				case NS.MATHML: {
					if (SCOPING_ELEMENTS_MATHML.has(tn)) return false;
					break;
				}
			}
		}
		return true;
	}
	hasInTableScope(tagName) {
		for (let i = this.stackTop; i >= 0; i--) {
			if (this.treeAdapter.getNamespaceURI(this.items[i]) !== NS.HTML) {
				continue;
			}
			switch (this.tagIDs[i]) {
				case tagName: {
					return true;
				}
				case TAG_ID.TABLE:
				case TAG_ID.HTML: {
					return false;
				}
			}
		}
		return true;
	}
	hasTableBodyContextInTableScope() {
		for (let i = this.stackTop; i >= 0; i--) {
			if (this.treeAdapter.getNamespaceURI(this.items[i]) !== NS.HTML) {
				continue;
			}
			switch (this.tagIDs[i]) {
				case TAG_ID.TBODY:
				case TAG_ID.THEAD:
				case TAG_ID.TFOOT: {
					return true;
				}
				case TAG_ID.TABLE:
				case TAG_ID.HTML: {
					return false;
				}
			}
		}
		return true;
	}
	hasInSelectScope(tagName) {
		for (let i = this.stackTop; i >= 0; i--) {
			if (this.treeAdapter.getNamespaceURI(this.items[i]) !== NS.HTML) {
				continue;
			}
			switch (this.tagIDs[i]) {
				case tagName: {
					return true;
				}
				case TAG_ID.OPTION:
				case TAG_ID.OPTGROUP: {
					break;
				}
				default: {
					return false;
				}
			}
		}
		return true;
	}
	//Implied end tags
	generateImpliedEndTags() {
		while (this.currentTagId !== undefined && IMPLICIT_END_TAG_REQUIRED.has(this.currentTagId)) {
			this.pop();
		}
	}
	generateImpliedEndTagsThoroughly() {
		while (this.currentTagId !== undefined && IMPLICIT_END_TAG_REQUIRED_THOROUGHLY.has(this.currentTagId)) {
			this.pop();
		}
	}
	generateImpliedEndTagsWithExclusion(exclusionId) {
		while (this.currentTagId !== undefined && this.currentTagId !== exclusionId && IMPLICIT_END_TAG_REQUIRED_THOROUGHLY.has(this.currentTagId)) {
			this.pop();
		}
	}
}

//Const
const NOAH_ARK_CAPACITY = 3;
var EntryType;
(function (EntryType) {
	EntryType[(EntryType["Marker"] = 0)] = "Marker";
	EntryType[(EntryType["Element"] = 1)] = "Element";
})(EntryType || (EntryType = {}));
const MARKER = { type: EntryType.Marker };
//List of formatting elements
class FormattingElementList {
	constructor(treeAdapter) {
		this.treeAdapter = treeAdapter;
		this.entries = [];
		this.bookmark = null;
	}
	//Noah Ark's condition
	//OPTIMIZATION: at first we try to find possible candidates for exclusion using
	//lightweight heuristics without thorough attributes check.
	_getNoahArkConditionCandidates(newElement, neAttrs) {
		const candidates = [];
		const neAttrsLength = neAttrs.length;
		const neTagName = this.treeAdapter.getTagName(newElement);
		const neNamespaceURI = this.treeAdapter.getNamespaceURI(newElement);
		for (let i = 0; i < this.entries.length; i++) {
			const entry = this.entries[i];
			if (entry.type === EntryType.Marker) {
				break;
			}
			const { element } = entry;
			if (this.treeAdapter.getTagName(element) === neTagName && this.treeAdapter.getNamespaceURI(element) === neNamespaceURI) {
				const elementAttrs = this.treeAdapter.getAttrList(element);
				if (elementAttrs.length === neAttrsLength) {
					candidates.push({ idx: i, attrs: elementAttrs });
				}
			}
		}
		return candidates;
	}
	_ensureNoahArkCondition(newElement) {
		if (this.entries.length < NOAH_ARK_CAPACITY) return;
		const neAttrs = this.treeAdapter.getAttrList(newElement);
		const candidates = this._getNoahArkConditionCandidates(newElement, neAttrs);
		if (candidates.length < NOAH_ARK_CAPACITY) return;
		//NOTE: build attrs map for the new element, so we can perform fast lookups
		const neAttrsMap = new Map(neAttrs.map((neAttr) => [neAttr.name, neAttr.value]));
		let validCandidates = 0;
		//NOTE: remove bottommost candidates, until Noah's Ark condition will not be met
		for (let i = 0; i < candidates.length; i++) {
			const candidate = candidates[i];
			// We know that `candidate.attrs.length === neAttrs.length`
			if (candidate.attrs.every((cAttr) => neAttrsMap.get(cAttr.name) === cAttr.value)) {
				validCandidates += 1;
				if (validCandidates >= NOAH_ARK_CAPACITY) {
					this.entries.splice(candidate.idx, 1);
				}
			}
		}
	}
	//Mutations
	insertMarker() {
		this.entries.unshift(MARKER);
	}
	pushElement(element, token) {
		this._ensureNoahArkCondition(element);
		this.entries.unshift({
			type: EntryType.Element,
			element,
			token,
		});
	}
	insertElementAfterBookmark(element, token) {
		const bookmarkIdx = this.entries.indexOf(this.bookmark);
		this.entries.splice(bookmarkIdx, 0, {
			type: EntryType.Element,
			element,
			token,
		});
	}
	removeEntry(entry) {
		const entryIndex = this.entries.indexOf(entry);
		if (entryIndex !== -1) {
			this.entries.splice(entryIndex, 1);
		}
	}
	/**
	 * Clears the list of formatting elements up to the last marker.
	 *
	 * @see https://html.spec.whatwg.org/multipage/parsing.html#clear-the-list-of-active-formatting-elements-up-to-the-last-marker
	 */
	clearToLastMarker() {
		const markerIdx = this.entries.indexOf(MARKER);
		if (markerIdx === -1) {
			this.entries.length = 0;
		} else {
			this.entries.splice(0, markerIdx + 1);
		}
	}
	//Search
	getElementEntryInScopeWithTagName(tagName) {
		const entry = this.entries.find((entry) => entry.type === EntryType.Marker || this.treeAdapter.getTagName(entry.element) === tagName);
		return entry && entry.type === EntryType.Element ? entry : null;
	}
	getElementEntry(element) {
		return this.entries.find((entry) => entry.type === EntryType.Element && entry.element === element);
	}
}

const defaultTreeAdapter = {
	//Node construction
	createDocument() {
		return {
			nodeName: "#document",
			mode: DOCUMENT_MODE.NO_QUIRKS,
			childNodes: [],
		};
	},
	createDocumentFragment() {
		return {
			nodeName: "#document-fragment",
			childNodes: [],
		};
	},
	createElement(tagName, namespaceURI, attrs) {
		return {
			nodeName: tagName,
			tagName,
			attrs,
			namespaceURI,
			childNodes: [],
			parentNode: null,
		};
	},
	createCommentNode(data) {
		return {
			nodeName: "#comment",
			data,
			parentNode: null,
		};
	},
	createTextNode(value) {
		return {
			nodeName: "#text",
			value,
			parentNode: null,
		};
	},
	//Tree mutation
	appendChild(parentNode, newNode) {
		parentNode.childNodes.push(newNode);
		newNode.parentNode = parentNode;
	},
	insertBefore(parentNode, newNode, referenceNode) {
		const insertionIdx = parentNode.childNodes.indexOf(referenceNode);
		parentNode.childNodes.splice(insertionIdx, 0, newNode);
		newNode.parentNode = parentNode;
	},
	setTemplateContent(templateElement, contentElement) {
		templateElement.content = contentElement;
	},
	getTemplateContent(templateElement) {
		return templateElement.content;
	},
	setDocumentType(document, name, publicId, systemId) {
		const doctypeNode = document.childNodes.find((node) => node.nodeName === "#documentType");
		if (doctypeNode) {
			doctypeNode.name = name;
			doctypeNode.publicId = publicId;
			doctypeNode.systemId = systemId;
		} else {
			const node = {
				nodeName: "#documentType",
				name,
				publicId,
				systemId,
				parentNode: null,
			};
			defaultTreeAdapter.appendChild(document, node);
		}
	},
	setDocumentMode(document, mode) {
		document.mode = mode;
	},
	getDocumentMode(document) {
		return document.mode;
	},
	detachNode(node) {
		if (node.parentNode) {
			const idx = node.parentNode.childNodes.indexOf(node);
			node.parentNode.childNodes.splice(idx, 1);
			node.parentNode = null;
		}
	},
	insertText(parentNode, text) {
		if (parentNode.childNodes.length > 0) {
			const prevNode = parentNode.childNodes[parentNode.childNodes.length - 1];
			if (defaultTreeAdapter.isTextNode(prevNode)) {
				prevNode.value += text;
				return;
			}
		}
		defaultTreeAdapter.appendChild(parentNode, defaultTreeAdapter.createTextNode(text));
	},
	insertTextBefore(parentNode, text, referenceNode) {
		const prevNode = parentNode.childNodes[parentNode.childNodes.indexOf(referenceNode) - 1];
		if (prevNode && defaultTreeAdapter.isTextNode(prevNode)) {
			prevNode.value += text;
		} else {
			defaultTreeAdapter.insertBefore(parentNode, defaultTreeAdapter.createTextNode(text), referenceNode);
		}
	},
	adoptAttributes(recipient, attrs) {
		const recipientAttrsMap = new Set(recipient.attrs.map((attr) => attr.name));
		for (let j = 0; j < attrs.length; j++) {
			if (!recipientAttrsMap.has(attrs[j].name)) {
				recipient.attrs.push(attrs[j]);
			}
		}
	},
	//Tree traversing
	getFirstChild(node) {
		return node.childNodes[0];
	},
	getChildNodes(node) {
		return node.childNodes;
	},
	getParentNode(node) {
		return node.parentNode;
	},
	getAttrList(element) {
		return element.attrs;
	},
	//Node data
	getTagName(element) {
		return element.tagName;
	},
	getNamespaceURI(element) {
		return element.namespaceURI;
	},
	getTextNodeContent(textNode) {
		return textNode.value;
	},
	getCommentNodeContent(commentNode) {
		return commentNode.data;
	},
	getDocumentTypeNodeName(doctypeNode) {
		return doctypeNode.name;
	},
	getDocumentTypeNodePublicId(doctypeNode) {
		return doctypeNode.publicId;
	},
	getDocumentTypeNodeSystemId(doctypeNode) {
		return doctypeNode.systemId;
	},
	//Node types
	isTextNode(node) {
		return node.nodeName === "#text";
	},
	isCommentNode(node) {
		return node.nodeName === "#comment";
	},
	isDocumentTypeNode(node) {
		return node.nodeName === "#documentType";
	},
	isElementNode(node) {
		return Object.prototype.hasOwnProperty.call(node, "tagName");
	},
	// Source code location
	setNodeSourceCodeLocation(node, location) {
		node.sourceCodeLocation = location;
	},
	getNodeSourceCodeLocation(node) {
		return node.sourceCodeLocation;
	},
	updateNodeSourceCodeLocation(node, endLocation) {
		node.sourceCodeLocation = { ...node.sourceCodeLocation, ...endLocation };
	},
};

//Const
const VALID_DOCTYPE_NAME = "html";
const VALID_SYSTEM_ID = "about:legacy-compat";
const QUIRKS_MODE_SYSTEM_ID = "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd";
const QUIRKS_MODE_PUBLIC_ID_PREFIXES = [
	"+//silmaril//dtd html pro v0r11 19970101//",
	"-//as//dtd html 3.0 aswedit + extensions//",
	"-//advasoft ltd//dtd html 3.0 aswedit + extensions//",
	"-//ietf//dtd html 2.0 level 1//",
	"-//ietf//dtd html 2.0 level 2//",
	"-//ietf//dtd html 2.0 strict level 1//",
	"-//ietf//dtd html 2.0 strict level 2//",
	"-//ietf//dtd html 2.0 strict//",
	"-//ietf//dtd html 2.0//",
	"-//ietf//dtd html 2.1e//",
	"-//ietf//dtd html 3.0//",
	"-//ietf//dtd html 3.2 final//",
	"-//ietf//dtd html 3.2//",
	"-//ietf//dtd html 3//",
	"-//ietf//dtd html level 0//",
	"-//ietf//dtd html level 1//",
	"-//ietf//dtd html level 2//",
	"-//ietf//dtd html level 3//",
	"-//ietf//dtd html strict level 0//",
	"-//ietf//dtd html strict level 1//",
	"-//ietf//dtd html strict level 2//",
	"-//ietf//dtd html strict level 3//",
	"-//ietf//dtd html strict//",
	"-//ietf//dtd html//",
	"-//metrius//dtd metrius presentational//",
	"-//microsoft//dtd internet explorer 2.0 html strict//",
	"-//microsoft//dtd internet explorer 2.0 html//",
	"-//microsoft//dtd internet explorer 2.0 tables//",
	"-//microsoft//dtd internet explorer 3.0 html strict//",
	"-//microsoft//dtd internet explorer 3.0 html//",
	"-//microsoft//dtd internet explorer 3.0 tables//",
	"-//netscape comm. corp.//dtd html//",
	"-//netscape comm. corp.//dtd strict html//",
	"-//o'reilly and associates//dtd html 2.0//",
	"-//o'reilly and associates//dtd html extended 1.0//",
	"-//o'reilly and associates//dtd html extended relaxed 1.0//",
	"-//sq//dtd html 2.0 hotmetal + extensions//",
	"-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//",
	"-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//",
	"-//spyglass//dtd html 2.0 extended//",
	"-//sun microsystems corp.//dtd hotjava html//",
	"-//sun microsystems corp.//dtd hotjava strict html//",
	"-//w3c//dtd html 3 1995-03-24//",
	"-//w3c//dtd html 3.2 draft//",
	"-//w3c//dtd html 3.2 final//",
	"-//w3c//dtd html 3.2//",
	"-//w3c//dtd html 3.2s draft//",
	"-//w3c//dtd html 4.0 frameset//",
	"-//w3c//dtd html 4.0 transitional//",
	"-//w3c//dtd html experimental 19960712//",
	"-//w3c//dtd html experimental 970421//",
	"-//w3c//dtd w3 html//",
	"-//w3o//dtd w3 html 3.0//",
	"-//webtechs//dtd mozilla html 2.0//",
	"-//webtechs//dtd mozilla html//",
];
const QUIRKS_MODE_NO_SYSTEM_ID_PUBLIC_ID_PREFIXES = [...QUIRKS_MODE_PUBLIC_ID_PREFIXES, "-//w3c//dtd html 4.01 frameset//", "-//w3c//dtd html 4.01 transitional//"];
const QUIRKS_MODE_PUBLIC_IDS = new Set(["-//w3o//dtd w3 html strict 3.0//en//", "-/w3c/dtd html 4.0 transitional/en", "html"]);
const LIMITED_QUIRKS_PUBLIC_ID_PREFIXES = ["-//w3c//dtd xhtml 1.0 frameset//", "-//w3c//dtd xhtml 1.0 transitional//"];
const LIMITED_QUIRKS_WITH_SYSTEM_ID_PUBLIC_ID_PREFIXES = [...LIMITED_QUIRKS_PUBLIC_ID_PREFIXES, "-//w3c//dtd html 4.01 frameset//", "-//w3c//dtd html 4.01 transitional//"];
//Utils
function hasPrefix(publicId, prefixes) {
	return prefixes.some((prefix) => publicId.startsWith(prefix));
}
//API
function isConforming(token) {
	return token.name === VALID_DOCTYPE_NAME && token.publicId === null && (token.systemId === null || token.systemId === VALID_SYSTEM_ID);
}
function getDocumentMode(token) {
	if (token.name !== VALID_DOCTYPE_NAME) {
		return DOCUMENT_MODE.QUIRKS;
	}
	const { systemId } = token;
	if (systemId && systemId.toLowerCase() === QUIRKS_MODE_SYSTEM_ID) {
		return DOCUMENT_MODE.QUIRKS;
	}
	let { publicId } = token;
	if (publicId !== null) {
		publicId = publicId.toLowerCase();
		if (QUIRKS_MODE_PUBLIC_IDS.has(publicId)) {
			return DOCUMENT_MODE.QUIRKS;
		}
		let prefixes = systemId === null ? QUIRKS_MODE_NO_SYSTEM_ID_PUBLIC_ID_PREFIXES : QUIRKS_MODE_PUBLIC_ID_PREFIXES;
		if (hasPrefix(publicId, prefixes)) {
			return DOCUMENT_MODE.QUIRKS;
		}
		prefixes = systemId === null ? LIMITED_QUIRKS_PUBLIC_ID_PREFIXES : LIMITED_QUIRKS_WITH_SYSTEM_ID_PUBLIC_ID_PREFIXES;
		if (hasPrefix(publicId, prefixes)) {
			return DOCUMENT_MODE.LIMITED_QUIRKS;
		}
	}
	return DOCUMENT_MODE.NO_QUIRKS;
}

//MIME types
const MIME_TYPES = {
	TEXT_HTML: "text/html",
	APPLICATION_XML: "application/xhtml+xml",
};
//Attributes
const DEFINITION_URL_ATTR = "definitionurl";
const ADJUSTED_DEFINITION_URL_ATTR = "definitionURL";
const SVG_ATTRS_ADJUSTMENT_MAP = new Map(
	[
		"attributeName",
		"attributeType",
		"baseFrequency",
		"baseProfile",
		"calcMode",
		"clipPathUnits",
		"diffuseConstant",
		"edgeMode",
		"filterUnits",
		"glyphRef",
		"gradientTransform",
		"gradientUnits",
		"kernelMatrix",
		"kernelUnitLength",
		"keyPoints",
		"keySplines",
		"keyTimes",
		"lengthAdjust",
		"limitingConeAngle",
		"markerHeight",
		"markerUnits",
		"markerWidth",
		"maskContentUnits",
		"maskUnits",
		"numOctaves",
		"pathLength",
		"patternContentUnits",
		"patternTransform",
		"patternUnits",
		"pointsAtX",
		"pointsAtY",
		"pointsAtZ",
		"preserveAlpha",
		"preserveAspectRatio",
		"primitiveUnits",
		"refX",
		"refY",
		"repeatCount",
		"repeatDur",
		"requiredExtensions",
		"requiredFeatures",
		"specularConstant",
		"specularExponent",
		"spreadMethod",
		"startOffset",
		"stdDeviation",
		"stitchTiles",
		"surfaceScale",
		"systemLanguage",
		"tableValues",
		"targetX",
		"targetY",
		"textLength",
		"viewBox",
		"viewTarget",
		"xChannelSelector",
		"yChannelSelector",
		"zoomAndPan",
	].map((attr) => [attr.toLowerCase(), attr]),
);
const XML_ATTRS_ADJUSTMENT_MAP = new Map([
	["xlink:actuate", { prefix: "xlink", name: "actuate", namespace: NS.XLINK }],
	["xlink:arcrole", { prefix: "xlink", name: "arcrole", namespace: NS.XLINK }],
	["xlink:href", { prefix: "xlink", name: "href", namespace: NS.XLINK }],
	["xlink:role", { prefix: "xlink", name: "role", namespace: NS.XLINK }],
	["xlink:show", { prefix: "xlink", name: "show", namespace: NS.XLINK }],
	["xlink:title", { prefix: "xlink", name: "title", namespace: NS.XLINK }],
	["xlink:type", { prefix: "xlink", name: "type", namespace: NS.XLINK }],
	["xml:lang", { prefix: "xml", name: "lang", namespace: NS.XML }],
	["xml:space", { prefix: "xml", name: "space", namespace: NS.XML }],
	["xmlns", { prefix: "", name: "xmlns", namespace: NS.XMLNS }],
	["xmlns:xlink", { prefix: "xmlns", name: "xlink", namespace: NS.XMLNS }],
]);
//SVG tag names adjustment map
const SVG_TAG_NAMES_ADJUSTMENT_MAP = new Map(
	[
		"altGlyph",
		"altGlyphDef",
		"altGlyphItem",
		"animateColor",
		"animateMotion",
		"animateTransform",
		"clipPath",
		"feBlend",
		"feColorMatrix",
		"feComponentTransfer",
		"feComposite",
		"feConvolveMatrix",
		"feDiffuseLighting",
		"feDisplacementMap",
		"feDistantLight",
		"feFlood",
		"feFuncA",
		"feFuncB",
		"feFuncG",
		"feFuncR",
		"feGaussianBlur",
		"feImage",
		"feMerge",
		"feMergeNode",
		"feMorphology",
		"feOffset",
		"fePointLight",
		"feSpecularLighting",
		"feSpotLight",
		"feTile",
		"feTurbulence",
		"foreignObject",
		"glyphRef",
		"linearGradient",
		"radialGradient",
		"textPath",
	].map((tn) => [tn.toLowerCase(), tn]),
);
//Tags that causes exit from foreign content
const EXITS_FOREIGN_CONTENT = new Set([
	TAG_ID.B,
	TAG_ID.BIG,
	TAG_ID.BLOCKQUOTE,
	TAG_ID.BODY,
	TAG_ID.BR,
	TAG_ID.CENTER,
	TAG_ID.CODE,
	TAG_ID.DD,
	TAG_ID.DIV,
	TAG_ID.DL,
	TAG_ID.DT,
	TAG_ID.EM,
	TAG_ID.EMBED,
	TAG_ID.H1,
	TAG_ID.H2,
	TAG_ID.H3,
	TAG_ID.H4,
	TAG_ID.H5,
	TAG_ID.H6,
	TAG_ID.HEAD,
	TAG_ID.HR,
	TAG_ID.I,
	TAG_ID.IMG,
	TAG_ID.LI,
	TAG_ID.LISTING,
	TAG_ID.MENU,
	TAG_ID.META,
	TAG_ID.NOBR,
	TAG_ID.OL,
	TAG_ID.P,
	TAG_ID.PRE,
	TAG_ID.RUBY,
	TAG_ID.S,
	TAG_ID.SMALL,
	TAG_ID.SPAN,
	TAG_ID.STRONG,
	TAG_ID.STRIKE,
	TAG_ID.SUB,
	TAG_ID.SUP,
	TAG_ID.TABLE,
	TAG_ID.TT,
	TAG_ID.U,
	TAG_ID.UL,
	TAG_ID.VAR,
]);
//Check exit from foreign content
function causesExit(startTagToken) {
	const tn = startTagToken.tagID;
	const isFontWithAttrs = tn === TAG_ID.FONT && startTagToken.attrs.some(({ name }) => name === ATTRS.COLOR || name === ATTRS.SIZE || name === ATTRS.FACE);
	return isFontWithAttrs || EXITS_FOREIGN_CONTENT.has(tn);
}
//Token adjustments
function adjustTokenMathMLAttrs(token) {
	for (let i = 0; i < token.attrs.length; i++) {
		if (token.attrs[i].name === DEFINITION_URL_ATTR) {
			token.attrs[i].name = ADJUSTED_DEFINITION_URL_ATTR;
			break;
		}
	}
}
function adjustTokenSVGAttrs(token) {
	for (let i = 0; i < token.attrs.length; i++) {
		const adjustedAttrName = SVG_ATTRS_ADJUSTMENT_MAP.get(token.attrs[i].name);
		if (adjustedAttrName != null) {
			token.attrs[i].name = adjustedAttrName;
		}
	}
}
function adjustTokenXMLAttrs(token) {
	for (let i = 0; i < token.attrs.length; i++) {
		const adjustedAttrEntry = XML_ATTRS_ADJUSTMENT_MAP.get(token.attrs[i].name);
		if (adjustedAttrEntry) {
			token.attrs[i].prefix = adjustedAttrEntry.prefix;
			token.attrs[i].name = adjustedAttrEntry.name;
			token.attrs[i].namespace = adjustedAttrEntry.namespace;
		}
	}
}
function adjustTokenSVGTagName(token) {
	const adjustedTagName = SVG_TAG_NAMES_ADJUSTMENT_MAP.get(token.tagName);
	if (adjustedTagName != null) {
		token.tagName = adjustedTagName;
		token.tagID = getTagID(token.tagName);
	}
}
//Integration points
function isMathMLTextIntegrationPoint(tn, ns) {
	return ns === NS.MATHML && (tn === TAG_ID.MI || tn === TAG_ID.MO || tn === TAG_ID.MN || tn === TAG_ID.MS || tn === TAG_ID.MTEXT);
}
function isHtmlIntegrationPoint(tn, ns, attrs) {
	if (ns === NS.MATHML && tn === TAG_ID.ANNOTATION_XML) {
		for (let i = 0; i < attrs.length; i++) {
			if (attrs[i].name === ATTRS.ENCODING) {
				const value = attrs[i].value.toLowerCase();
				return value === MIME_TYPES.TEXT_HTML || value === MIME_TYPES.APPLICATION_XML;
			}
		}
	}
	return ns === NS.SVG && (tn === TAG_ID.FOREIGN_OBJECT || tn === TAG_ID.DESC || tn === TAG_ID.TITLE);
}
function isIntegrationPoint(tn, ns, attrs, foreignNS) {
	return ((!foreignNS || foreignNS === NS.HTML) && isHtmlIntegrationPoint(tn, ns, attrs)) || ((!foreignNS || foreignNS === NS.MATHML) && isMathMLTextIntegrationPoint(tn, ns));
}

//Misc constants
const HIDDEN_INPUT_TYPE = "hidden";
//Adoption agency loops iteration count
const AA_OUTER_LOOP_ITER = 8;
const AA_INNER_LOOP_ITER = 3;
//Insertion modes
var InsertionMode;
(function (InsertionMode) {
	InsertionMode[(InsertionMode["INITIAL"] = 0)] = "INITIAL";
	InsertionMode[(InsertionMode["BEFORE_HTML"] = 1)] = "BEFORE_HTML";
	InsertionMode[(InsertionMode["BEFORE_HEAD"] = 2)] = "BEFORE_HEAD";
	InsertionMode[(InsertionMode["IN_HEAD"] = 3)] = "IN_HEAD";
	InsertionMode[(InsertionMode["IN_HEAD_NO_SCRIPT"] = 4)] = "IN_HEAD_NO_SCRIPT";
	InsertionMode[(InsertionMode["AFTER_HEAD"] = 5)] = "AFTER_HEAD";
	InsertionMode[(InsertionMode["IN_BODY"] = 6)] = "IN_BODY";
	InsertionMode[(InsertionMode["TEXT"] = 7)] = "TEXT";
	InsertionMode[(InsertionMode["IN_TABLE"] = 8)] = "IN_TABLE";
	InsertionMode[(InsertionMode["IN_TABLE_TEXT"] = 9)] = "IN_TABLE_TEXT";
	InsertionMode[(InsertionMode["IN_CAPTION"] = 10)] = "IN_CAPTION";
	InsertionMode[(InsertionMode["IN_COLUMN_GROUP"] = 11)] = "IN_COLUMN_GROUP";
	InsertionMode[(InsertionMode["IN_TABLE_BODY"] = 12)] = "IN_TABLE_BODY";
	InsertionMode[(InsertionMode["IN_ROW"] = 13)] = "IN_ROW";
	InsertionMode[(InsertionMode["IN_CELL"] = 14)] = "IN_CELL";
	InsertionMode[(InsertionMode["IN_SELECT"] = 15)] = "IN_SELECT";
	InsertionMode[(InsertionMode["IN_SELECT_IN_TABLE"] = 16)] = "IN_SELECT_IN_TABLE";
	InsertionMode[(InsertionMode["IN_TEMPLATE"] = 17)] = "IN_TEMPLATE";
	InsertionMode[(InsertionMode["AFTER_BODY"] = 18)] = "AFTER_BODY";
	InsertionMode[(InsertionMode["IN_FRAMESET"] = 19)] = "IN_FRAMESET";
	InsertionMode[(InsertionMode["AFTER_FRAMESET"] = 20)] = "AFTER_FRAMESET";
	InsertionMode[(InsertionMode["AFTER_AFTER_BODY"] = 21)] = "AFTER_AFTER_BODY";
	InsertionMode[(InsertionMode["AFTER_AFTER_FRAMESET"] = 22)] = "AFTER_AFTER_FRAMESET";
})(InsertionMode || (InsertionMode = {}));
const BASE_LOC = {
	startLine: -1,
	startCol: -1,
	startOffset: -1,
	endLine: -1,
	endCol: -1,
	endOffset: -1,
};
const TABLE_STRUCTURE_TAGS = new Set([TAG_ID.TABLE, TAG_ID.TBODY, TAG_ID.TFOOT, TAG_ID.THEAD, TAG_ID.TR]);
const defaultParserOptions = {
	scriptingEnabled: true,
	sourceCodeLocationInfo: false,
	treeAdapter: defaultTreeAdapter,
	onParseError: null,
};
//Parser
class Parser {
	constructor(
		options,
		document,
		/** @internal */
		fragmentContext = null,
		/** @internal */
		scriptHandler = null,
	) {
		this.fragmentContext = fragmentContext;
		this.scriptHandler = scriptHandler;
		this.currentToken = null;
		this.stopped = false;
		/** @internal */
		this.insertionMode = InsertionMode.INITIAL;
		/** @internal */
		this.originalInsertionMode = InsertionMode.INITIAL;
		/** @internal */
		this.headElement = null;
		/** @internal */
		this.formElement = null;
		/** Indicates that the current node is not an element in the HTML namespace */
		this.currentNotInHTML = false;
		/**
		 * The template insertion mode stack is maintained from the left.
		 * Ie. the topmost element will always have index 0.
		 *
		 * @internal
		 */
		this.tmplInsertionModeStack = [];
		/** @internal */
		this.pendingCharacterTokens = [];
		/** @internal */
		this.hasNonWhitespacePendingCharacterToken = false;
		/** @internal */
		this.framesetOk = true;
		/** @internal */
		this.skipNextNewLine = false;
		/** @internal */
		this.fosterParentingEnabled = false;
		this.options = {
			...defaultParserOptions,
			...options,
		};
		this.treeAdapter = this.options.treeAdapter;
		this.onParseError = this.options.onParseError;
		// Always enable location info if we report parse errors.
		if (this.onParseError) {
			this.options.sourceCodeLocationInfo = true;
		}
		this.document = document !== null && document !== void 0 ? document : this.treeAdapter.createDocument();
		this.tokenizer = new Tokenizer(this.options, this);
		this.activeFormattingElements = new FormattingElementList(this.treeAdapter);
		this.fragmentContextID = fragmentContext ? getTagID(this.treeAdapter.getTagName(fragmentContext)) : TAG_ID.UNKNOWN;
		this._setContextModes(fragmentContext !== null && fragmentContext !== void 0 ? fragmentContext : this.document, this.fragmentContextID);
		this.openElements = new OpenElementStack(this.document, this.treeAdapter, this);
	}
	// API
	static parse(html, options) {
		const parser = new this(options);
		parser.tokenizer.write(html, true);
		return parser.document;
	}
	static getFragmentParser(fragmentContext, options) {
		const opts = {
			...defaultParserOptions,
			...options,
		};
		//NOTE: use a <template> element as the fragment context if no context element was provided,
		//so we will parse in a "forgiving" manner
		fragmentContext !== null && fragmentContext !== void 0 ? fragmentContext : (fragmentContext = opts.treeAdapter.createElement(TAG_NAMES.TEMPLATE, NS.HTML, []));
		//NOTE: create a fake element which will be used as the `document` for fragment parsing.
		//This is important for jsdom, where a new `document` cannot be created. This led to
		//fragment parsing messing with the main `document`.
		const documentMock = opts.treeAdapter.createElement("documentmock", NS.HTML, []);
		const parser = new this(opts, documentMock, fragmentContext);
		if (parser.fragmentContextID === TAG_ID.TEMPLATE) {
			parser.tmplInsertionModeStack.unshift(InsertionMode.IN_TEMPLATE);
		}
		parser._initTokenizerForFragmentParsing();
		parser._insertFakeRootElement();
		parser._resetInsertionMode();
		parser._findFormInFragmentContext();
		return parser;
	}
	getFragment() {
		const rootElement = this.treeAdapter.getFirstChild(this.document);
		const fragment = this.treeAdapter.createDocumentFragment();
		this._adoptNodes(rootElement, fragment);
		return fragment;
	}
	//Errors
	/** @internal */
	_err(token, code, beforeToken) {
		var _a;
		if (!this.onParseError) return;
		const loc = (_a = token.location) !== null && _a !== void 0 ? _a : BASE_LOC;
		const err = {
			code,
			startLine: loc.startLine,
			startCol: loc.startCol,
			startOffset: loc.startOffset,
			endLine: beforeToken ? loc.startLine : loc.endLine,
			endCol: beforeToken ? loc.startCol : loc.endCol,
			endOffset: beforeToken ? loc.startOffset : loc.endOffset,
		};
		this.onParseError(err);
	}
	//Stack events
	/** @internal */
	onItemPush(node, tid, isTop) {
		var _a, _b;
		(_b = (_a = this.treeAdapter).onItemPush) === null || _b === void 0 ? void 0 : _b.call(_a, node);
		if (isTop && this.openElements.stackTop > 0) this._setContextModes(node, tid);
	}
	/** @internal */
	onItemPop(node, isTop) {
		var _a, _b;
		if (this.options.sourceCodeLocationInfo) {
			this._setEndLocation(node, this.currentToken);
		}
		(_b = (_a = this.treeAdapter).onItemPop) === null || _b === void 0 ? void 0 : _b.call(_a, node, this.openElements.current);
		if (isTop) {
			let current;
			let currentTagId;
			if (this.openElements.stackTop === 0 && this.fragmentContext) {
				current = this.fragmentContext;
				currentTagId = this.fragmentContextID;
			} else {
				({ current, currentTagId } = this.openElements);
			}
			this._setContextModes(current, currentTagId);
		}
	}
	_setContextModes(current, tid) {
		const isHTML = current === this.document || (current && this.treeAdapter.getNamespaceURI(current) === NS.HTML);
		this.currentNotInHTML = !isHTML;
		this.tokenizer.inForeignNode = !isHTML && current !== undefined && tid !== undefined && !this._isIntegrationPoint(tid, current);
	}
	/** @protected */
	_switchToTextParsing(currentToken, nextTokenizerState) {
		this._insertElement(currentToken, NS.HTML);
		this.tokenizer.state = nextTokenizerState;
		this.originalInsertionMode = this.insertionMode;
		this.insertionMode = InsertionMode.TEXT;
	}
	switchToPlaintextParsing() {
		this.insertionMode = InsertionMode.TEXT;
		this.originalInsertionMode = InsertionMode.IN_BODY;
		this.tokenizer.state = TokenizerMode.PLAINTEXT;
	}
	//Fragment parsing
	/** @protected */
	_getAdjustedCurrentElement() {
		return this.openElements.stackTop === 0 && this.fragmentContext ? this.fragmentContext : this.openElements.current;
	}
	/** @protected */
	_findFormInFragmentContext() {
		let node = this.fragmentContext;
		while (node) {
			if (this.treeAdapter.getTagName(node) === TAG_NAMES.FORM) {
				this.formElement = node;
				break;
			}
			node = this.treeAdapter.getParentNode(node);
		}
	}
	_initTokenizerForFragmentParsing() {
		if (!this.fragmentContext || this.treeAdapter.getNamespaceURI(this.fragmentContext) !== NS.HTML) {
			return;
		}
		switch (this.fragmentContextID) {
			case TAG_ID.TITLE:
			case TAG_ID.TEXTAREA: {
				this.tokenizer.state = TokenizerMode.RCDATA;
				break;
			}
			case TAG_ID.STYLE:
			case TAG_ID.XMP:
			case TAG_ID.IFRAME:
			case TAG_ID.NOEMBED:
			case TAG_ID.NOFRAMES:
			case TAG_ID.NOSCRIPT: {
				this.tokenizer.state = TokenizerMode.RAWTEXT;
				break;
			}
			case TAG_ID.SCRIPT: {
				this.tokenizer.state = TokenizerMode.SCRIPT_DATA;
				break;
			}
			case TAG_ID.PLAINTEXT: {
				this.tokenizer.state = TokenizerMode.PLAINTEXT;
				break;
			}
			// Do nothing
		}
	}
	//Tree mutation
	/** @protected */
	_setDocumentType(token) {
		const name = token.name || "";
		const publicId = token.publicId || "";
		const systemId = token.systemId || "";
		this.treeAdapter.setDocumentType(this.document, name, publicId, systemId);
		if (token.location) {
			const documentChildren = this.treeAdapter.getChildNodes(this.document);
			const docTypeNode = documentChildren.find((node) => this.treeAdapter.isDocumentTypeNode(node));
			if (docTypeNode) {
				this.treeAdapter.setNodeSourceCodeLocation(docTypeNode, token.location);
			}
		}
	}
	/** @protected */
	_attachElementToTree(element, location) {
		if (this.options.sourceCodeLocationInfo) {
			const loc = location && {
				...location,
				startTag: location,
			};
			this.treeAdapter.setNodeSourceCodeLocation(element, loc);
		}
		if (this._shouldFosterParentOnInsertion()) {
			this._fosterParentElement(element);
		} else {
			const parent = this.openElements.currentTmplContentOrNode;
			this.treeAdapter.appendChild(parent !== null && parent !== void 0 ? parent : this.document, element);
		}
	}
	/**
	 * For self-closing tags. Add an element to the tree, but skip adding it
	 * to the stack.
	 */
	/** @protected */
	_appendElement(token, namespaceURI) {
		const element = this.treeAdapter.createElement(token.tagName, namespaceURI, token.attrs);
		this._attachElementToTree(element, token.location);
	}
	/** @protected */
	_insertElement(token, namespaceURI) {
		const element = this.treeAdapter.createElement(token.tagName, namespaceURI, token.attrs);
		this._attachElementToTree(element, token.location);
		this.openElements.push(element, token.tagID);
	}
	/** @protected */
	_insertFakeElement(tagName, tagID) {
		const element = this.treeAdapter.createElement(tagName, NS.HTML, []);
		this._attachElementToTree(element, null);
		this.openElements.push(element, tagID);
	}
	/** @protected */
	_insertTemplate(token) {
		const tmpl = this.treeAdapter.createElement(token.tagName, NS.HTML, token.attrs);
		const content = this.treeAdapter.createDocumentFragment();
		this.treeAdapter.setTemplateContent(tmpl, content);
		this._attachElementToTree(tmpl, token.location);
		this.openElements.push(tmpl, token.tagID);
		if (this.options.sourceCodeLocationInfo) this.treeAdapter.setNodeSourceCodeLocation(content, null);
	}
	/** @protected */
	_insertFakeRootElement() {
		const element = this.treeAdapter.createElement(TAG_NAMES.HTML, NS.HTML, []);
		if (this.options.sourceCodeLocationInfo) this.treeAdapter.setNodeSourceCodeLocation(element, null);
		this.treeAdapter.appendChild(this.openElements.current, element);
		this.openElements.push(element, TAG_ID.HTML);
	}
	/** @protected */
	_appendCommentNode(token, parent) {
		const commentNode = this.treeAdapter.createCommentNode(token.data);
		this.treeAdapter.appendChild(parent, commentNode);
		if (this.options.sourceCodeLocationInfo) {
			this.treeAdapter.setNodeSourceCodeLocation(commentNode, token.location);
		}
	}
	/** @protected */
	_insertCharacters(token) {
		let parent;
		let beforeElement;
		if (this._shouldFosterParentOnInsertion()) {
			({ parent, beforeElement } = this._findFosterParentingLocation());
			if (beforeElement) {
				this.treeAdapter.insertTextBefore(parent, token.chars, beforeElement);
			} else {
				this.treeAdapter.insertText(parent, token.chars);
			}
		} else {
			parent = this.openElements.currentTmplContentOrNode;
			this.treeAdapter.insertText(parent, token.chars);
		}
		if (!token.location) return;
		const siblings = this.treeAdapter.getChildNodes(parent);
		const textNodeIdx = beforeElement ? siblings.lastIndexOf(beforeElement) : siblings.length;
		const textNode = siblings[textNodeIdx - 1];
		//NOTE: if we have a location assigned by another token, then just update the end position
		const tnLoc = this.treeAdapter.getNodeSourceCodeLocation(textNode);
		if (tnLoc) {
			const { endLine, endCol, endOffset } = token.location;
			this.treeAdapter.updateNodeSourceCodeLocation(textNode, { endLine, endCol, endOffset });
		} else if (this.options.sourceCodeLocationInfo) {
			this.treeAdapter.setNodeSourceCodeLocation(textNode, token.location);
		}
	}
	/** @protected */
	_adoptNodes(donor, recipient) {
		for (let child = this.treeAdapter.getFirstChild(donor); child; child = this.treeAdapter.getFirstChild(donor)) {
			this.treeAdapter.detachNode(child);
			this.treeAdapter.appendChild(recipient, child);
		}
	}
	/** @protected */
	_setEndLocation(element, closingToken) {
		if (this.treeAdapter.getNodeSourceCodeLocation(element) && closingToken.location) {
			const ctLoc = closingToken.location;
			const tn = this.treeAdapter.getTagName(element);
			const endLoc =
				// NOTE: For cases like <p> <p> </p> - First 'p' closes without a closing
				// tag and for cases like <td> <p> </td> - 'p' closes without a closing tag.
				closingToken.type === TokenType.END_TAG && tn === closingToken.tagName ?
					{
						endTag: { ...ctLoc },
						endLine: ctLoc.endLine,
						endCol: ctLoc.endCol,
						endOffset: ctLoc.endOffset,
					}
				:	{
						endLine: ctLoc.startLine,
						endCol: ctLoc.startCol,
						endOffset: ctLoc.startOffset,
					};
			this.treeAdapter.updateNodeSourceCodeLocation(element, endLoc);
		}
	}
	//Token processing
	shouldProcessStartTagTokenInForeignContent(token) {
		// Check that neither current === document, or ns === NS.HTML
		if (!this.currentNotInHTML) return false;
		let current;
		let currentTagId;
		if (this.openElements.stackTop === 0 && this.fragmentContext) {
			current = this.fragmentContext;
			currentTagId = this.fragmentContextID;
		} else {
			({ current, currentTagId } = this.openElements);
		}
		if (token.tagID === TAG_ID.SVG && this.treeAdapter.getTagName(current) === TAG_NAMES.ANNOTATION_XML && this.treeAdapter.getNamespaceURI(current) === NS.MATHML) {
			return false;
		}
		return (
			// Check that `current` is not an integration point for HTML or MathML elements.
			this.tokenizer.inForeignNode ||
			// If it _is_ an integration point, then we might have to check that it is not an HTML
			// integration point.
			((token.tagID === TAG_ID.MGLYPH || token.tagID === TAG_ID.MALIGNMARK) && currentTagId !== undefined && !this._isIntegrationPoint(currentTagId, current, NS.HTML))
		);
	}
	/** @protected */
	_processToken(token) {
		switch (token.type) {
			case TokenType.CHARACTER: {
				this.onCharacter(token);
				break;
			}
			case TokenType.NULL_CHARACTER: {
				this.onNullCharacter(token);
				break;
			}
			case TokenType.COMMENT: {
				this.onComment(token);
				break;
			}
			case TokenType.DOCTYPE: {
				this.onDoctype(token);
				break;
			}
			case TokenType.START_TAG: {
				this._processStartTag(token);
				break;
			}
			case TokenType.END_TAG: {
				this.onEndTag(token);
				break;
			}
			case TokenType.EOF: {
				this.onEof(token);
				break;
			}
			case TokenType.WHITESPACE_CHARACTER: {
				this.onWhitespaceCharacter(token);
				break;
			}
		}
	}
	//Integration points
	/** @protected */
	_isIntegrationPoint(tid, element, foreignNS) {
		const ns = this.treeAdapter.getNamespaceURI(element);
		const attrs = this.treeAdapter.getAttrList(element);
		return isIntegrationPoint(tid, ns, attrs, foreignNS);
	}
	//Active formatting elements reconstruction
	/** @protected */
	_reconstructActiveFormattingElements() {
		const listLength = this.activeFormattingElements.entries.length;
		if (listLength) {
			const endIndex = this.activeFormattingElements.entries.findIndex((entry) => entry.type === EntryType.Marker || this.openElements.contains(entry.element));
			const unopenIdx = endIndex === -1 ? listLength - 1 : endIndex - 1;
			for (let i = unopenIdx; i >= 0; i--) {
				const entry = this.activeFormattingElements.entries[i];
				this._insertElement(entry.token, this.treeAdapter.getNamespaceURI(entry.element));
				entry.element = this.openElements.current;
			}
		}
	}
	//Close elements
	/** @protected */
	_closeTableCell() {
		this.openElements.generateImpliedEndTags();
		this.openElements.popUntilTableCellPopped();
		this.activeFormattingElements.clearToLastMarker();
		this.insertionMode = InsertionMode.IN_ROW;
	}
	/** @protected */
	_closePElement() {
		this.openElements.generateImpliedEndTagsWithExclusion(TAG_ID.P);
		this.openElements.popUntilTagNamePopped(TAG_ID.P);
	}
	//Insertion modes
	/** @protected */
	_resetInsertionMode() {
		for (let i = this.openElements.stackTop; i >= 0; i--) {
			//Insertion mode reset map
			switch (i === 0 && this.fragmentContext ? this.fragmentContextID : this.openElements.tagIDs[i]) {
				case TAG_ID.TR: {
					this.insertionMode = InsertionMode.IN_ROW;
					return;
				}
				case TAG_ID.TBODY:
				case TAG_ID.THEAD:
				case TAG_ID.TFOOT: {
					this.insertionMode = InsertionMode.IN_TABLE_BODY;
					return;
				}
				case TAG_ID.CAPTION: {
					this.insertionMode = InsertionMode.IN_CAPTION;
					return;
				}
				case TAG_ID.COLGROUP: {
					this.insertionMode = InsertionMode.IN_COLUMN_GROUP;
					return;
				}
				case TAG_ID.TABLE: {
					this.insertionMode = InsertionMode.IN_TABLE;
					return;
				}
				case TAG_ID.BODY: {
					this.insertionMode = InsertionMode.IN_BODY;
					return;
				}
				case TAG_ID.FRAMESET: {
					this.insertionMode = InsertionMode.IN_FRAMESET;
					return;
				}
				case TAG_ID.SELECT: {
					this._resetInsertionModeForSelect(i);
					return;
				}
				case TAG_ID.TEMPLATE: {
					this.insertionMode = this.tmplInsertionModeStack[0];
					return;
				}
				case TAG_ID.HTML: {
					this.insertionMode = this.headElement ? InsertionMode.AFTER_HEAD : InsertionMode.BEFORE_HEAD;
					return;
				}
				case TAG_ID.TD:
				case TAG_ID.TH: {
					if (i > 0) {
						this.insertionMode = InsertionMode.IN_CELL;
						return;
					}
					break;
				}
				case TAG_ID.HEAD: {
					if (i > 0) {
						this.insertionMode = InsertionMode.IN_HEAD;
						return;
					}
					break;
				}
			}
		}
		this.insertionMode = InsertionMode.IN_BODY;
	}
	/** @protected */
	_resetInsertionModeForSelect(selectIdx) {
		if (selectIdx > 0) {
			for (let i = selectIdx - 1; i > 0; i--) {
				const tn = this.openElements.tagIDs[i];
				if (tn === TAG_ID.TEMPLATE) {
					break;
				} else if (tn === TAG_ID.TABLE) {
					this.insertionMode = InsertionMode.IN_SELECT_IN_TABLE;
					return;
				}
			}
		}
		this.insertionMode = InsertionMode.IN_SELECT;
	}
	//Foster parenting
	/** @protected */
	_isElementCausesFosterParenting(tn) {
		return TABLE_STRUCTURE_TAGS.has(tn);
	}
	/** @protected */
	_shouldFosterParentOnInsertion() {
		return this.fosterParentingEnabled && this.openElements.currentTagId !== undefined && this._isElementCausesFosterParenting(this.openElements.currentTagId);
	}
	/** @protected */
	_findFosterParentingLocation() {
		for (let i = this.openElements.stackTop; i >= 0; i--) {
			const openElement = this.openElements.items[i];
			switch (this.openElements.tagIDs[i]) {
				case TAG_ID.TEMPLATE: {
					if (this.treeAdapter.getNamespaceURI(openElement) === NS.HTML) {
						return { parent: this.treeAdapter.getTemplateContent(openElement), beforeElement: null };
					}
					break;
				}
				case TAG_ID.TABLE: {
					const parent = this.treeAdapter.getParentNode(openElement);
					if (parent) {
						return { parent, beforeElement: openElement };
					}
					return { parent: this.openElements.items[i - 1], beforeElement: null };
				}
				// Do nothing
			}
		}
		return { parent: this.openElements.items[0], beforeElement: null };
	}
	/** @protected */
	_fosterParentElement(element) {
		const location = this._findFosterParentingLocation();
		if (location.beforeElement) {
			this.treeAdapter.insertBefore(location.parent, element, location.beforeElement);
		} else {
			this.treeAdapter.appendChild(location.parent, element);
		}
	}
	//Special elements
	/** @protected */
	_isSpecialElement(element, id) {
		const ns = this.treeAdapter.getNamespaceURI(element);
		return SPECIAL_ELEMENTS[ns].has(id);
	}
	/** @internal */
	onCharacter(token) {
		this.skipNextNewLine = false;
		if (this.tokenizer.inForeignNode) {
			characterInForeignContent(this, token);
			return;
		}
		switch (this.insertionMode) {
			case InsertionMode.INITIAL: {
				tokenInInitialMode(this, token);
				break;
			}
			case InsertionMode.BEFORE_HTML: {
				tokenBeforeHtml(this, token);
				break;
			}
			case InsertionMode.BEFORE_HEAD: {
				tokenBeforeHead(this, token);
				break;
			}
			case InsertionMode.IN_HEAD: {
				tokenInHead(this, token);
				break;
			}
			case InsertionMode.IN_HEAD_NO_SCRIPT: {
				tokenInHeadNoScript(this, token);
				break;
			}
			case InsertionMode.AFTER_HEAD: {
				tokenAfterHead(this, token);
				break;
			}
			case InsertionMode.IN_BODY:
			case InsertionMode.IN_CAPTION:
			case InsertionMode.IN_CELL:
			case InsertionMode.IN_TEMPLATE: {
				characterInBody(this, token);
				break;
			}
			case InsertionMode.TEXT:
			case InsertionMode.IN_SELECT:
			case InsertionMode.IN_SELECT_IN_TABLE: {
				this._insertCharacters(token);
				break;
			}
			case InsertionMode.IN_TABLE:
			case InsertionMode.IN_TABLE_BODY:
			case InsertionMode.IN_ROW: {
				characterInTable(this, token);
				break;
			}
			case InsertionMode.IN_TABLE_TEXT: {
				characterInTableText(this, token);
				break;
			}
			case InsertionMode.IN_COLUMN_GROUP: {
				tokenInColumnGroup(this, token);
				break;
			}
			case InsertionMode.AFTER_BODY: {
				tokenAfterBody(this, token);
				break;
			}
			case InsertionMode.AFTER_AFTER_BODY: {
				tokenAfterAfterBody(this, token);
				break;
			}
			// Do nothing
		}
	}
	/** @internal */
	onNullCharacter(token) {
		this.skipNextNewLine = false;
		if (this.tokenizer.inForeignNode) {
			nullCharacterInForeignContent(this, token);
			return;
		}
		switch (this.insertionMode) {
			case InsertionMode.INITIAL: {
				tokenInInitialMode(this, token);
				break;
			}
			case InsertionMode.BEFORE_HTML: {
				tokenBeforeHtml(this, token);
				break;
			}
			case InsertionMode.BEFORE_HEAD: {
				tokenBeforeHead(this, token);
				break;
			}
			case InsertionMode.IN_HEAD: {
				tokenInHead(this, token);
				break;
			}
			case InsertionMode.IN_HEAD_NO_SCRIPT: {
				tokenInHeadNoScript(this, token);
				break;
			}
			case InsertionMode.AFTER_HEAD: {
				tokenAfterHead(this, token);
				break;
			}
			case InsertionMode.TEXT: {
				this._insertCharacters(token);
				break;
			}
			case InsertionMode.IN_TABLE:
			case InsertionMode.IN_TABLE_BODY:
			case InsertionMode.IN_ROW: {
				characterInTable(this, token);
				break;
			}
			case InsertionMode.IN_COLUMN_GROUP: {
				tokenInColumnGroup(this, token);
				break;
			}
			case InsertionMode.AFTER_BODY: {
				tokenAfterBody(this, token);
				break;
			}
			case InsertionMode.AFTER_AFTER_BODY: {
				tokenAfterAfterBody(this, token);
				break;
			}
			// Do nothing
		}
	}
	/** @internal */
	onComment(token) {
		this.skipNextNewLine = false;
		if (this.currentNotInHTML) {
			appendComment(this, token);
			return;
		}
		switch (this.insertionMode) {
			case InsertionMode.INITIAL:
			case InsertionMode.BEFORE_HTML:
			case InsertionMode.BEFORE_HEAD:
			case InsertionMode.IN_HEAD:
			case InsertionMode.IN_HEAD_NO_SCRIPT:
			case InsertionMode.AFTER_HEAD:
			case InsertionMode.IN_BODY:
			case InsertionMode.IN_TABLE:
			case InsertionMode.IN_CAPTION:
			case InsertionMode.IN_COLUMN_GROUP:
			case InsertionMode.IN_TABLE_BODY:
			case InsertionMode.IN_ROW:
			case InsertionMode.IN_CELL:
			case InsertionMode.IN_SELECT:
			case InsertionMode.IN_SELECT_IN_TABLE:
			case InsertionMode.IN_TEMPLATE:
			case InsertionMode.IN_FRAMESET:
			case InsertionMode.AFTER_FRAMESET: {
				appendComment(this, token);
				break;
			}
			case InsertionMode.IN_TABLE_TEXT: {
				tokenInTableText(this, token);
				break;
			}
			case InsertionMode.AFTER_BODY: {
				appendCommentToRootHtmlElement(this, token);
				break;
			}
			case InsertionMode.AFTER_AFTER_BODY:
			case InsertionMode.AFTER_AFTER_FRAMESET: {
				appendCommentToDocument(this, token);
				break;
			}
			// Do nothing
		}
	}
	/** @internal */
	onDoctype(token) {
		this.skipNextNewLine = false;
		switch (this.insertionMode) {
			case InsertionMode.INITIAL: {
				doctypeInInitialMode(this, token);
				break;
			}
			case InsertionMode.BEFORE_HEAD:
			case InsertionMode.IN_HEAD:
			case InsertionMode.IN_HEAD_NO_SCRIPT:
			case InsertionMode.AFTER_HEAD: {
				this._err(token, ERR.misplacedDoctype);
				break;
			}
			case InsertionMode.IN_TABLE_TEXT: {
				tokenInTableText(this, token);
				break;
			}
			// Do nothing
		}
	}
	/** @internal */
	onStartTag(token) {
		this.skipNextNewLine = false;
		this.currentToken = token;
		this._processStartTag(token);
		if (token.selfClosing && !token.ackSelfClosing) {
			this._err(token, ERR.nonVoidHtmlElementStartTagWithTrailingSolidus);
		}
	}
	/**
	 * Processes a given start tag.
	 *
	 * `onStartTag` checks if a self-closing tag was recognized. When a token
	 * is moved inbetween multiple insertion modes, this check for self-closing
	 * could lead to false positives. To avoid this, `_processStartTag` is used
	 * for nested calls.
	 *
	 * @param token The token to process.
	 * @protected
	 */
	_processStartTag(token) {
		if (this.shouldProcessStartTagTokenInForeignContent(token)) {
			startTagInForeignContent(this, token);
		} else {
			this._startTagOutsideForeignContent(token);
		}
	}
	/** @protected */
	_startTagOutsideForeignContent(token) {
		switch (this.insertionMode) {
			case InsertionMode.INITIAL: {
				tokenInInitialMode(this, token);
				break;
			}
			case InsertionMode.BEFORE_HTML: {
				startTagBeforeHtml(this, token);
				break;
			}
			case InsertionMode.BEFORE_HEAD: {
				startTagBeforeHead(this, token);
				break;
			}
			case InsertionMode.IN_HEAD: {
				startTagInHead(this, token);
				break;
			}
			case InsertionMode.IN_HEAD_NO_SCRIPT: {
				startTagInHeadNoScript(this, token);
				break;
			}
			case InsertionMode.AFTER_HEAD: {
				startTagAfterHead(this, token);
				break;
			}
			case InsertionMode.IN_BODY: {
				startTagInBody(this, token);
				break;
			}
			case InsertionMode.IN_TABLE: {
				startTagInTable(this, token);
				break;
			}
			case InsertionMode.IN_TABLE_TEXT: {
				tokenInTableText(this, token);
				break;
			}
			case InsertionMode.IN_CAPTION: {
				startTagInCaption(this, token);
				break;
			}
			case InsertionMode.IN_COLUMN_GROUP: {
				startTagInColumnGroup(this, token);
				break;
			}
			case InsertionMode.IN_TABLE_BODY: {
				startTagInTableBody(this, token);
				break;
			}
			case InsertionMode.IN_ROW: {
				startTagInRow(this, token);
				break;
			}
			case InsertionMode.IN_CELL: {
				startTagInCell(this, token);
				break;
			}
			case InsertionMode.IN_SELECT: {
				startTagInSelect(this, token);
				break;
			}
			case InsertionMode.IN_SELECT_IN_TABLE: {
				startTagInSelectInTable(this, token);
				break;
			}
			case InsertionMode.IN_TEMPLATE: {
				startTagInTemplate(this, token);
				break;
			}
			case InsertionMode.AFTER_BODY: {
				startTagAfterBody(this, token);
				break;
			}
			case InsertionMode.IN_FRAMESET: {
				startTagInFrameset(this, token);
				break;
			}
			case InsertionMode.AFTER_FRAMESET: {
				startTagAfterFrameset(this, token);
				break;
			}
			case InsertionMode.AFTER_AFTER_BODY: {
				startTagAfterAfterBody(this, token);
				break;
			}
			case InsertionMode.AFTER_AFTER_FRAMESET: {
				startTagAfterAfterFrameset(this, token);
				break;
			}
			// Do nothing
		}
	}
	/** @internal */
	onEndTag(token) {
		this.skipNextNewLine = false;
		this.currentToken = token;
		if (this.currentNotInHTML) {
			endTagInForeignContent(this, token);
		} else {
			this._endTagOutsideForeignContent(token);
		}
	}
	/** @protected */
	_endTagOutsideForeignContent(token) {
		switch (this.insertionMode) {
			case InsertionMode.INITIAL: {
				tokenInInitialMode(this, token);
				break;
			}
			case InsertionMode.BEFORE_HTML: {
				endTagBeforeHtml(this, token);
				break;
			}
			case InsertionMode.BEFORE_HEAD: {
				endTagBeforeHead(this, token);
				break;
			}
			case InsertionMode.IN_HEAD: {
				endTagInHead(this, token);
				break;
			}
			case InsertionMode.IN_HEAD_NO_SCRIPT: {
				endTagInHeadNoScript(this, token);
				break;
			}
			case InsertionMode.AFTER_HEAD: {
				endTagAfterHead(this, token);
				break;
			}
			case InsertionMode.IN_BODY: {
				endTagInBody(this, token);
				break;
			}
			case InsertionMode.TEXT: {
				endTagInText(this, token);
				break;
			}
			case InsertionMode.IN_TABLE: {
				endTagInTable(this, token);
				break;
			}
			case InsertionMode.IN_TABLE_TEXT: {
				tokenInTableText(this, token);
				break;
			}
			case InsertionMode.IN_CAPTION: {
				endTagInCaption(this, token);
				break;
			}
			case InsertionMode.IN_COLUMN_GROUP: {
				endTagInColumnGroup(this, token);
				break;
			}
			case InsertionMode.IN_TABLE_BODY: {
				endTagInTableBody(this, token);
				break;
			}
			case InsertionMode.IN_ROW: {
				endTagInRow(this, token);
				break;
			}
			case InsertionMode.IN_CELL: {
				endTagInCell(this, token);
				break;
			}
			case InsertionMode.IN_SELECT: {
				endTagInSelect(this, token);
				break;
			}
			case InsertionMode.IN_SELECT_IN_TABLE: {
				endTagInSelectInTable(this, token);
				break;
			}
			case InsertionMode.IN_TEMPLATE: {
				endTagInTemplate(this, token);
				break;
			}
			case InsertionMode.AFTER_BODY: {
				endTagAfterBody(this, token);
				break;
			}
			case InsertionMode.IN_FRAMESET: {
				endTagInFrameset(this, token);
				break;
			}
			case InsertionMode.AFTER_FRAMESET: {
				endTagAfterFrameset(this, token);
				break;
			}
			case InsertionMode.AFTER_AFTER_BODY: {
				tokenAfterAfterBody(this, token);
				break;
			}
			// Do nothing
		}
	}
	/** @internal */
	onEof(token) {
		switch (this.insertionMode) {
			case InsertionMode.INITIAL: {
				tokenInInitialMode(this, token);
				break;
			}
			case InsertionMode.BEFORE_HTML: {
				tokenBeforeHtml(this, token);
				break;
			}
			case InsertionMode.BEFORE_HEAD: {
				tokenBeforeHead(this, token);
				break;
			}
			case InsertionMode.IN_HEAD: {
				tokenInHead(this, token);
				break;
			}
			case InsertionMode.IN_HEAD_NO_SCRIPT: {
				tokenInHeadNoScript(this, token);
				break;
			}
			case InsertionMode.AFTER_HEAD: {
				tokenAfterHead(this, token);
				break;
			}
			case InsertionMode.IN_BODY:
			case InsertionMode.IN_TABLE:
			case InsertionMode.IN_CAPTION:
			case InsertionMode.IN_COLUMN_GROUP:
			case InsertionMode.IN_TABLE_BODY:
			case InsertionMode.IN_ROW:
			case InsertionMode.IN_CELL:
			case InsertionMode.IN_SELECT:
			case InsertionMode.IN_SELECT_IN_TABLE: {
				eofInBody(this, token);
				break;
			}
			case InsertionMode.TEXT: {
				eofInText(this, token);
				break;
			}
			case InsertionMode.IN_TABLE_TEXT: {
				tokenInTableText(this, token);
				break;
			}
			case InsertionMode.IN_TEMPLATE: {
				eofInTemplate(this, token);
				break;
			}
			case InsertionMode.AFTER_BODY:
			case InsertionMode.IN_FRAMESET:
			case InsertionMode.AFTER_FRAMESET:
			case InsertionMode.AFTER_AFTER_BODY:
			case InsertionMode.AFTER_AFTER_FRAMESET: {
				stopParsing(this, token);
				break;
			}
			// Do nothing
		}
	}
	/** @internal */
	onWhitespaceCharacter(token) {
		if (this.skipNextNewLine) {
			this.skipNextNewLine = false;
			if (token.chars.charCodeAt(0) === CODE_POINTS.LINE_FEED) {
				if (token.chars.length === 1) {
					return;
				}
				token.chars = token.chars.substr(1);
			}
		}
		if (this.tokenizer.inForeignNode) {
			this._insertCharacters(token);
			return;
		}
		switch (this.insertionMode) {
			case InsertionMode.IN_HEAD:
			case InsertionMode.IN_HEAD_NO_SCRIPT:
			case InsertionMode.AFTER_HEAD:
			case InsertionMode.TEXT:
			case InsertionMode.IN_COLUMN_GROUP:
			case InsertionMode.IN_SELECT:
			case InsertionMode.IN_SELECT_IN_TABLE:
			case InsertionMode.IN_FRAMESET:
			case InsertionMode.AFTER_FRAMESET: {
				this._insertCharacters(token);
				break;
			}
			case InsertionMode.IN_BODY:
			case InsertionMode.IN_CAPTION:
			case InsertionMode.IN_CELL:
			case InsertionMode.IN_TEMPLATE:
			case InsertionMode.AFTER_BODY:
			case InsertionMode.AFTER_AFTER_BODY:
			case InsertionMode.AFTER_AFTER_FRAMESET: {
				whitespaceCharacterInBody(this, token);
				break;
			}
			case InsertionMode.IN_TABLE:
			case InsertionMode.IN_TABLE_BODY:
			case InsertionMode.IN_ROW: {
				characterInTable(this, token);
				break;
			}
			case InsertionMode.IN_TABLE_TEXT: {
				whitespaceCharacterInTableText(this, token);
				break;
			}
			// Do nothing
		}
	}
}
//Adoption agency algorithm
//(see: http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#adoptionAgency)
//------------------------------------------------------------------
//Steps 5-8 of the algorithm
function aaObtainFormattingElementEntry(p, token) {
	let formattingElementEntry = p.activeFormattingElements.getElementEntryInScopeWithTagName(token.tagName);
	if (formattingElementEntry) {
		if (!p.openElements.contains(formattingElementEntry.element)) {
			p.activeFormattingElements.removeEntry(formattingElementEntry);
			formattingElementEntry = null;
		} else if (!p.openElements.hasInScope(token.tagID)) {
			formattingElementEntry = null;
		}
	} else {
		genericEndTagInBody(p, token);
	}
	return formattingElementEntry;
}
//Steps 9 and 10 of the algorithm
function aaObtainFurthestBlock(p, formattingElementEntry) {
	let furthestBlock = null;
	let idx = p.openElements.stackTop;
	for (; idx >= 0; idx--) {
		const element = p.openElements.items[idx];
		if (element === formattingElementEntry.element) {
			break;
		}
		if (p._isSpecialElement(element, p.openElements.tagIDs[idx])) {
			furthestBlock = element;
		}
	}
	if (!furthestBlock) {
		p.openElements.shortenToLength(Math.max(idx, 0));
		p.activeFormattingElements.removeEntry(formattingElementEntry);
	}
	return furthestBlock;
}
//Step 13 of the algorithm
function aaInnerLoop(p, furthestBlock, formattingElement) {
	let lastElement = furthestBlock;
	let nextElement = p.openElements.getCommonAncestor(furthestBlock);
	for (let i = 0, element = nextElement; element !== formattingElement; i++, element = nextElement) {
		//NOTE: store the next element for the next loop iteration (it may be deleted from the stack by step 9.5)
		nextElement = p.openElements.getCommonAncestor(element);
		const elementEntry = p.activeFormattingElements.getElementEntry(element);
		const counterOverflow = elementEntry && i >= AA_INNER_LOOP_ITER;
		const shouldRemoveFromOpenElements = !elementEntry || counterOverflow;
		if (shouldRemoveFromOpenElements) {
			if (counterOverflow) {
				p.activeFormattingElements.removeEntry(elementEntry);
			}
			p.openElements.remove(element);
		} else {
			element = aaRecreateElementFromEntry(p, elementEntry);
			if (lastElement === furthestBlock) {
				p.activeFormattingElements.bookmark = elementEntry;
			}
			p.treeAdapter.detachNode(lastElement);
			p.treeAdapter.appendChild(element, lastElement);
			lastElement = element;
		}
	}
	return lastElement;
}
//Step 13.7 of the algorithm
function aaRecreateElementFromEntry(p, elementEntry) {
	const ns = p.treeAdapter.getNamespaceURI(elementEntry.element);
	const newElement = p.treeAdapter.createElement(elementEntry.token.tagName, ns, elementEntry.token.attrs);
	p.openElements.replace(elementEntry.element, newElement);
	elementEntry.element = newElement;
	return newElement;
}
//Step 14 of the algorithm
function aaInsertLastNodeInCommonAncestor(p, commonAncestor, lastElement) {
	const tn = p.treeAdapter.getTagName(commonAncestor);
	const tid = getTagID(tn);
	if (p._isElementCausesFosterParenting(tid)) {
		p._fosterParentElement(lastElement);
	} else {
		const ns = p.treeAdapter.getNamespaceURI(commonAncestor);
		if (tid === TAG_ID.TEMPLATE && ns === NS.HTML) {
			commonAncestor = p.treeAdapter.getTemplateContent(commonAncestor);
		}
		p.treeAdapter.appendChild(commonAncestor, lastElement);
	}
}
//Steps 15-19 of the algorithm
function aaReplaceFormattingElement(p, furthestBlock, formattingElementEntry) {
	const ns = p.treeAdapter.getNamespaceURI(formattingElementEntry.element);
	const { token } = formattingElementEntry;
	const newElement = p.treeAdapter.createElement(token.tagName, ns, token.attrs);
	p._adoptNodes(furthestBlock, newElement);
	p.treeAdapter.appendChild(furthestBlock, newElement);
	p.activeFormattingElements.insertElementAfterBookmark(newElement, token);
	p.activeFormattingElements.removeEntry(formattingElementEntry);
	p.openElements.remove(formattingElementEntry.element);
	p.openElements.insertAfter(furthestBlock, newElement, token.tagID);
}
//Algorithm entry point
function callAdoptionAgency(p, token) {
	for (let i = 0; i < AA_OUTER_LOOP_ITER; i++) {
		const formattingElementEntry = aaObtainFormattingElementEntry(p, token);
		if (!formattingElementEntry) {
			break;
		}
		const furthestBlock = aaObtainFurthestBlock(p, formattingElementEntry);
		if (!furthestBlock) {
			break;
		}
		p.activeFormattingElements.bookmark = formattingElementEntry;
		const lastElement = aaInnerLoop(p, furthestBlock, formattingElementEntry.element);
		const commonAncestor = p.openElements.getCommonAncestor(formattingElementEntry.element);
		p.treeAdapter.detachNode(lastElement);
		if (commonAncestor) aaInsertLastNodeInCommonAncestor(p, commonAncestor, lastElement);
		aaReplaceFormattingElement(p, furthestBlock, formattingElementEntry);
	}
}
//Generic token handlers
//------------------------------------------------------------------
function appendComment(p, token) {
	p._appendCommentNode(token, p.openElements.currentTmplContentOrNode);
}
function appendCommentToRootHtmlElement(p, token) {
	p._appendCommentNode(token, p.openElements.items[0]);
}
function appendCommentToDocument(p, token) {
	p._appendCommentNode(token, p.document);
}
function stopParsing(p, token) {
	p.stopped = true;
	// NOTE: Set end locations for elements that remain on the open element stack.
	if (token.location) {
		// NOTE: If we are not in a fragment, `html` and `body` will stay on the stack.
		// This is a problem, as we might overwrite their end position here.
		const target = p.fragmentContext ? 0 : 2;
		for (let i = p.openElements.stackTop; i >= target; i--) {
			p._setEndLocation(p.openElements.items[i], token);
		}
		// Handle `html` and `body`
		if (!p.fragmentContext && p.openElements.stackTop >= 0) {
			const htmlElement = p.openElements.items[0];
			const htmlLocation = p.treeAdapter.getNodeSourceCodeLocation(htmlElement);
			if (htmlLocation && !htmlLocation.endTag) {
				p._setEndLocation(htmlElement, token);
				if (p.openElements.stackTop >= 1) {
					const bodyElement = p.openElements.items[1];
					const bodyLocation = p.treeAdapter.getNodeSourceCodeLocation(bodyElement);
					if (bodyLocation && !bodyLocation.endTag) {
						p._setEndLocation(bodyElement, token);
					}
				}
			}
		}
	}
}
// The "initial" insertion mode
//------------------------------------------------------------------
function doctypeInInitialMode(p, token) {
	p._setDocumentType(token);
	const mode = token.forceQuirks ? DOCUMENT_MODE.QUIRKS : getDocumentMode(token);
	if (!isConforming(token)) {
		p._err(token, ERR.nonConformingDoctype);
	}
	p.treeAdapter.setDocumentMode(p.document, mode);
	p.insertionMode = InsertionMode.BEFORE_HTML;
}
function tokenInInitialMode(p, token) {
	p._err(token, ERR.missingDoctype, true);
	p.treeAdapter.setDocumentMode(p.document, DOCUMENT_MODE.QUIRKS);
	p.insertionMode = InsertionMode.BEFORE_HTML;
	p._processToken(token);
}
// The "before html" insertion mode
//------------------------------------------------------------------
function startTagBeforeHtml(p, token) {
	if (token.tagID === TAG_ID.HTML) {
		p._insertElement(token, NS.HTML);
		p.insertionMode = InsertionMode.BEFORE_HEAD;
	} else {
		tokenBeforeHtml(p, token);
	}
}
function endTagBeforeHtml(p, token) {
	const tn = token.tagID;
	if (tn === TAG_ID.HTML || tn === TAG_ID.HEAD || tn === TAG_ID.BODY || tn === TAG_ID.BR) {
		tokenBeforeHtml(p, token);
	}
}
function tokenBeforeHtml(p, token) {
	p._insertFakeRootElement();
	p.insertionMode = InsertionMode.BEFORE_HEAD;
	p._processToken(token);
}
// The "before head" insertion mode
//------------------------------------------------------------------
function startTagBeforeHead(p, token) {
	switch (token.tagID) {
		case TAG_ID.HTML: {
			startTagInBody(p, token);
			break;
		}
		case TAG_ID.HEAD: {
			p._insertElement(token, NS.HTML);
			p.headElement = p.openElements.current;
			p.insertionMode = InsertionMode.IN_HEAD;
			break;
		}
		default: {
			tokenBeforeHead(p, token);
		}
	}
}
function endTagBeforeHead(p, token) {
	const tn = token.tagID;
	if (tn === TAG_ID.HEAD || tn === TAG_ID.BODY || tn === TAG_ID.HTML || tn === TAG_ID.BR) {
		tokenBeforeHead(p, token);
	} else {
		p._err(token, ERR.endTagWithoutMatchingOpenElement);
	}
}
function tokenBeforeHead(p, token) {
	p._insertFakeElement(TAG_NAMES.HEAD, TAG_ID.HEAD);
	p.headElement = p.openElements.current;
	p.insertionMode = InsertionMode.IN_HEAD;
	p._processToken(token);
}
// The "in head" insertion mode
//------------------------------------------------------------------
function startTagInHead(p, token) {
	switch (token.tagID) {
		case TAG_ID.HTML: {
			startTagInBody(p, token);
			break;
		}
		case TAG_ID.BASE:
		case TAG_ID.BASEFONT:
		case TAG_ID.BGSOUND:
		case TAG_ID.LINK:
		case TAG_ID.META: {
			p._appendElement(token, NS.HTML);
			token.ackSelfClosing = true;
			break;
		}
		case TAG_ID.TITLE: {
			p._switchToTextParsing(token, TokenizerMode.RCDATA);
			break;
		}
		case TAG_ID.NOSCRIPT: {
			if (p.options.scriptingEnabled) {
				p._switchToTextParsing(token, TokenizerMode.RAWTEXT);
			} else {
				p._insertElement(token, NS.HTML);
				p.insertionMode = InsertionMode.IN_HEAD_NO_SCRIPT;
			}
			break;
		}
		case TAG_ID.NOFRAMES:
		case TAG_ID.STYLE: {
			p._switchToTextParsing(token, TokenizerMode.RAWTEXT);
			break;
		}
		case TAG_ID.SCRIPT: {
			p._switchToTextParsing(token, TokenizerMode.SCRIPT_DATA);
			break;
		}
		case TAG_ID.TEMPLATE: {
			p._insertTemplate(token);
			p.activeFormattingElements.insertMarker();
			p.framesetOk = false;
			p.insertionMode = InsertionMode.IN_TEMPLATE;
			p.tmplInsertionModeStack.unshift(InsertionMode.IN_TEMPLATE);
			break;
		}
		case TAG_ID.HEAD: {
			p._err(token, ERR.misplacedStartTagForHeadElement);
			break;
		}
		default: {
			tokenInHead(p, token);
		}
	}
}
function endTagInHead(p, token) {
	switch (token.tagID) {
		case TAG_ID.HEAD: {
			p.openElements.pop();
			p.insertionMode = InsertionMode.AFTER_HEAD;
			break;
		}
		case TAG_ID.BODY:
		case TAG_ID.BR:
		case TAG_ID.HTML: {
			tokenInHead(p, token);
			break;
		}
		case TAG_ID.TEMPLATE: {
			templateEndTagInHead(p, token);
			break;
		}
		default: {
			p._err(token, ERR.endTagWithoutMatchingOpenElement);
		}
	}
}
function templateEndTagInHead(p, token) {
	if (p.openElements.tmplCount > 0) {
		p.openElements.generateImpliedEndTagsThoroughly();
		if (p.openElements.currentTagId !== TAG_ID.TEMPLATE) {
			p._err(token, ERR.closingOfElementWithOpenChildElements);
		}
		p.openElements.popUntilTagNamePopped(TAG_ID.TEMPLATE);
		p.activeFormattingElements.clearToLastMarker();
		p.tmplInsertionModeStack.shift();
		p._resetInsertionMode();
	} else {
		p._err(token, ERR.endTagWithoutMatchingOpenElement);
	}
}
function tokenInHead(p, token) {
	p.openElements.pop();
	p.insertionMode = InsertionMode.AFTER_HEAD;
	p._processToken(token);
}
// The "in head no script" insertion mode
//------------------------------------------------------------------
function startTagInHeadNoScript(p, token) {
	switch (token.tagID) {
		case TAG_ID.HTML: {
			startTagInBody(p, token);
			break;
		}
		case TAG_ID.BASEFONT:
		case TAG_ID.BGSOUND:
		case TAG_ID.HEAD:
		case TAG_ID.LINK:
		case TAG_ID.META:
		case TAG_ID.NOFRAMES:
		case TAG_ID.STYLE: {
			startTagInHead(p, token);
			break;
		}
		case TAG_ID.NOSCRIPT: {
			p._err(token, ERR.nestedNoscriptInHead);
			break;
		}
		default: {
			tokenInHeadNoScript(p, token);
		}
	}
}
function endTagInHeadNoScript(p, token) {
	switch (token.tagID) {
		case TAG_ID.NOSCRIPT: {
			p.openElements.pop();
			p.insertionMode = InsertionMode.IN_HEAD;
			break;
		}
		case TAG_ID.BR: {
			tokenInHeadNoScript(p, token);
			break;
		}
		default: {
			p._err(token, ERR.endTagWithoutMatchingOpenElement);
		}
	}
}
function tokenInHeadNoScript(p, token) {
	const errCode = token.type === TokenType.EOF ? ERR.openElementsLeftAfterEof : ERR.disallowedContentInNoscriptInHead;
	p._err(token, errCode);
	p.openElements.pop();
	p.insertionMode = InsertionMode.IN_HEAD;
	p._processToken(token);
}
// The "after head" insertion mode
//------------------------------------------------------------------
function startTagAfterHead(p, token) {
	switch (token.tagID) {
		case TAG_ID.HTML: {
			startTagInBody(p, token);
			break;
		}
		case TAG_ID.BODY: {
			p._insertElement(token, NS.HTML);
			p.framesetOk = false;
			p.insertionMode = InsertionMode.IN_BODY;
			break;
		}
		case TAG_ID.FRAMESET: {
			p._insertElement(token, NS.HTML);
			p.insertionMode = InsertionMode.IN_FRAMESET;
			break;
		}
		case TAG_ID.BASE:
		case TAG_ID.BASEFONT:
		case TAG_ID.BGSOUND:
		case TAG_ID.LINK:
		case TAG_ID.META:
		case TAG_ID.NOFRAMES:
		case TAG_ID.SCRIPT:
		case TAG_ID.STYLE:
		case TAG_ID.TEMPLATE:
		case TAG_ID.TITLE: {
			p._err(token, ERR.abandonedHeadElementChild);
			p.openElements.push(p.headElement, TAG_ID.HEAD);
			startTagInHead(p, token);
			p.openElements.remove(p.headElement);
			break;
		}
		case TAG_ID.HEAD: {
			p._err(token, ERR.misplacedStartTagForHeadElement);
			break;
		}
		default: {
			tokenAfterHead(p, token);
		}
	}
}
function endTagAfterHead(p, token) {
	switch (token.tagID) {
		case TAG_ID.BODY:
		case TAG_ID.HTML:
		case TAG_ID.BR: {
			tokenAfterHead(p, token);
			break;
		}
		case TAG_ID.TEMPLATE: {
			templateEndTagInHead(p, token);
			break;
		}
		default: {
			p._err(token, ERR.endTagWithoutMatchingOpenElement);
		}
	}
}
function tokenAfterHead(p, token) {
	p._insertFakeElement(TAG_NAMES.BODY, TAG_ID.BODY);
	p.insertionMode = InsertionMode.IN_BODY;
	modeInBody(p, token);
}
// The "in body" insertion mode
//------------------------------------------------------------------
function modeInBody(p, token) {
	switch (token.type) {
		case TokenType.CHARACTER: {
			characterInBody(p, token);
			break;
		}
		case TokenType.WHITESPACE_CHARACTER: {
			whitespaceCharacterInBody(p, token);
			break;
		}
		case TokenType.COMMENT: {
			appendComment(p, token);
			break;
		}
		case TokenType.START_TAG: {
			startTagInBody(p, token);
			break;
		}
		case TokenType.END_TAG: {
			endTagInBody(p, token);
			break;
		}
		case TokenType.EOF: {
			eofInBody(p, token);
			break;
		}
		// Do nothing
	}
}
function whitespaceCharacterInBody(p, token) {
	p._reconstructActiveFormattingElements();
	p._insertCharacters(token);
}
function characterInBody(p, token) {
	p._reconstructActiveFormattingElements();
	p._insertCharacters(token);
	p.framesetOk = false;
}
function htmlStartTagInBody(p, token) {
	if (p.openElements.tmplCount === 0) {
		p.treeAdapter.adoptAttributes(p.openElements.items[0], token.attrs);
	}
}
function bodyStartTagInBody(p, token) {
	const bodyElement = p.openElements.tryPeekProperlyNestedBodyElement();
	if (bodyElement && p.openElements.tmplCount === 0) {
		p.framesetOk = false;
		p.treeAdapter.adoptAttributes(bodyElement, token.attrs);
	}
}
function framesetStartTagInBody(p, token) {
	const bodyElement = p.openElements.tryPeekProperlyNestedBodyElement();
	if (p.framesetOk && bodyElement) {
		p.treeAdapter.detachNode(bodyElement);
		p.openElements.popAllUpToHtmlElement();
		p._insertElement(token, NS.HTML);
		p.insertionMode = InsertionMode.IN_FRAMESET;
	}
}
function addressStartTagInBody(p, token) {
	if (p.openElements.hasInButtonScope(TAG_ID.P)) {
		p._closePElement();
	}
	p._insertElement(token, NS.HTML);
}
function numberedHeaderStartTagInBody(p, token) {
	if (p.openElements.hasInButtonScope(TAG_ID.P)) {
		p._closePElement();
	}
	if (p.openElements.currentTagId !== undefined && NUMBERED_HEADERS.has(p.openElements.currentTagId)) {
		p.openElements.pop();
	}
	p._insertElement(token, NS.HTML);
}
function preStartTagInBody(p, token) {
	if (p.openElements.hasInButtonScope(TAG_ID.P)) {
		p._closePElement();
	}
	p._insertElement(token, NS.HTML);
	//NOTE: If the next token is a U+000A LINE FEED (LF) character token, then ignore that token and move
	//on to the next one. (Newlines at the start of pre blocks are ignored as an authoring convenience.)
	p.skipNextNewLine = true;
	p.framesetOk = false;
}
function formStartTagInBody(p, token) {
	const inTemplate = p.openElements.tmplCount > 0;
	if (!p.formElement || inTemplate) {
		if (p.openElements.hasInButtonScope(TAG_ID.P)) {
			p._closePElement();
		}
		p._insertElement(token, NS.HTML);
		if (!inTemplate) {
			p.formElement = p.openElements.current;
		}
	}
}
function listItemStartTagInBody(p, token) {
	p.framesetOk = false;
	const tn = token.tagID;
	for (let i = p.openElements.stackTop; i >= 0; i--) {
		const elementId = p.openElements.tagIDs[i];
		if ((tn === TAG_ID.LI && elementId === TAG_ID.LI) || ((tn === TAG_ID.DD || tn === TAG_ID.DT) && (elementId === TAG_ID.DD || elementId === TAG_ID.DT))) {
			p.openElements.generateImpliedEndTagsWithExclusion(elementId);
			p.openElements.popUntilTagNamePopped(elementId);
			break;
		}
		if (elementId !== TAG_ID.ADDRESS && elementId !== TAG_ID.DIV && elementId !== TAG_ID.P && p._isSpecialElement(p.openElements.items[i], elementId)) {
			break;
		}
	}
	if (p.openElements.hasInButtonScope(TAG_ID.P)) {
		p._closePElement();
	}
	p._insertElement(token, NS.HTML);
}
function plaintextStartTagInBody(p, token) {
	if (p.openElements.hasInButtonScope(TAG_ID.P)) {
		p._closePElement();
	}
	p._insertElement(token, NS.HTML);
	p.tokenizer.state = TokenizerMode.PLAINTEXT;
}
function buttonStartTagInBody(p, token) {
	if (p.openElements.hasInScope(TAG_ID.BUTTON)) {
		p.openElements.generateImpliedEndTags();
		p.openElements.popUntilTagNamePopped(TAG_ID.BUTTON);
	}
	p._reconstructActiveFormattingElements();
	p._insertElement(token, NS.HTML);
	p.framesetOk = false;
}
function aStartTagInBody(p, token) {
	const activeElementEntry = p.activeFormattingElements.getElementEntryInScopeWithTagName(TAG_NAMES.A);
	if (activeElementEntry) {
		callAdoptionAgency(p, token);
		p.openElements.remove(activeElementEntry.element);
		p.activeFormattingElements.removeEntry(activeElementEntry);
	}
	p._reconstructActiveFormattingElements();
	p._insertElement(token, NS.HTML);
	p.activeFormattingElements.pushElement(p.openElements.current, token);
}
function bStartTagInBody(p, token) {
	p._reconstructActiveFormattingElements();
	p._insertElement(token, NS.HTML);
	p.activeFormattingElements.pushElement(p.openElements.current, token);
}
function nobrStartTagInBody(p, token) {
	p._reconstructActiveFormattingElements();
	if (p.openElements.hasInScope(TAG_ID.NOBR)) {
		callAdoptionAgency(p, token);
		p._reconstructActiveFormattingElements();
	}
	p._insertElement(token, NS.HTML);
	p.activeFormattingElements.pushElement(p.openElements.current, token);
}
function appletStartTagInBody(p, token) {
	p._reconstructActiveFormattingElements();
	p._insertElement(token, NS.HTML);
	p.activeFormattingElements.insertMarker();
	p.framesetOk = false;
}
function tableStartTagInBody(p, token) {
	if (p.treeAdapter.getDocumentMode(p.document) !== DOCUMENT_MODE.QUIRKS && p.openElements.hasInButtonScope(TAG_ID.P)) {
		p._closePElement();
	}
	p._insertElement(token, NS.HTML);
	p.framesetOk = false;
	p.insertionMode = InsertionMode.IN_TABLE;
}
function areaStartTagInBody(p, token) {
	p._reconstructActiveFormattingElements();
	p._appendElement(token, NS.HTML);
	p.framesetOk = false;
	token.ackSelfClosing = true;
}
function isHiddenInput(token) {
	const inputType = getTokenAttr(token, ATTRS.TYPE);
	return inputType != null && inputType.toLowerCase() === HIDDEN_INPUT_TYPE;
}
function inputStartTagInBody(p, token) {
	p._reconstructActiveFormattingElements();
	p._appendElement(token, NS.HTML);
	if (!isHiddenInput(token)) {
		p.framesetOk = false;
	}
	token.ackSelfClosing = true;
}
function paramStartTagInBody(p, token) {
	p._appendElement(token, NS.HTML);
	token.ackSelfClosing = true;
}
function hrStartTagInBody(p, token) {
	if (p.openElements.hasInButtonScope(TAG_ID.P)) {
		p._closePElement();
	}
	p._appendElement(token, NS.HTML);
	p.framesetOk = false;
	token.ackSelfClosing = true;
}
function imageStartTagInBody(p, token) {
	token.tagName = TAG_NAMES.IMG;
	token.tagID = TAG_ID.IMG;
	areaStartTagInBody(p, token);
}
function textareaStartTagInBody(p, token) {
	p._insertElement(token, NS.HTML);
	//NOTE: If the next token is a U+000A LINE FEED (LF) character token, then ignore that token and move
	//on to the next one. (Newlines at the start of textarea elements are ignored as an authoring convenience.)
	p.skipNextNewLine = true;
	p.tokenizer.state = TokenizerMode.RCDATA;
	p.originalInsertionMode = p.insertionMode;
	p.framesetOk = false;
	p.insertionMode = InsertionMode.TEXT;
}
function xmpStartTagInBody(p, token) {
	if (p.openElements.hasInButtonScope(TAG_ID.P)) {
		p._closePElement();
	}
	p._reconstructActiveFormattingElements();
	p.framesetOk = false;
	p._switchToTextParsing(token, TokenizerMode.RAWTEXT);
}
function iframeStartTagInBody(p, token) {
	p.framesetOk = false;
	p._switchToTextParsing(token, TokenizerMode.RAWTEXT);
}
//NOTE: here we assume that we always act as a user agent with enabled plugins/frames, so we parse
//<noembed>/<noframes> as rawtext.
function rawTextStartTagInBody(p, token) {
	p._switchToTextParsing(token, TokenizerMode.RAWTEXT);
}
function selectStartTagInBody(p, token) {
	p._reconstructActiveFormattingElements();
	p._insertElement(token, NS.HTML);
	p.framesetOk = false;
	p.insertionMode =
		(
			p.insertionMode === InsertionMode.IN_TABLE ||
			p.insertionMode === InsertionMode.IN_CAPTION ||
			p.insertionMode === InsertionMode.IN_TABLE_BODY ||
			p.insertionMode === InsertionMode.IN_ROW ||
			p.insertionMode === InsertionMode.IN_CELL
		) ?
			InsertionMode.IN_SELECT_IN_TABLE
		:	InsertionMode.IN_SELECT;
}
function optgroupStartTagInBody(p, token) {
	if (p.openElements.currentTagId === TAG_ID.OPTION) {
		p.openElements.pop();
	}
	p._reconstructActiveFormattingElements();
	p._insertElement(token, NS.HTML);
}
function rbStartTagInBody(p, token) {
	if (p.openElements.hasInScope(TAG_ID.RUBY)) {
		p.openElements.generateImpliedEndTags();
	}
	p._insertElement(token, NS.HTML);
}
function rtStartTagInBody(p, token) {
	if (p.openElements.hasInScope(TAG_ID.RUBY)) {
		p.openElements.generateImpliedEndTagsWithExclusion(TAG_ID.RTC);
	}
	p._insertElement(token, NS.HTML);
}
function mathStartTagInBody(p, token) {
	p._reconstructActiveFormattingElements();
	adjustTokenMathMLAttrs(token);
	adjustTokenXMLAttrs(token);
	if (token.selfClosing) {
		p._appendElement(token, NS.MATHML);
	} else {
		p._insertElement(token, NS.MATHML);
	}
	token.ackSelfClosing = true;
}
function svgStartTagInBody(p, token) {
	p._reconstructActiveFormattingElements();
	adjustTokenSVGAttrs(token);
	adjustTokenXMLAttrs(token);
	if (token.selfClosing) {
		p._appendElement(token, NS.SVG);
	} else {
		p._insertElement(token, NS.SVG);
	}
	token.ackSelfClosing = true;
}
function genericStartTagInBody(p, token) {
	p._reconstructActiveFormattingElements();
	p._insertElement(token, NS.HTML);
}
function startTagInBody(p, token) {
	switch (token.tagID) {
		case TAG_ID.I:
		case TAG_ID.S:
		case TAG_ID.B:
		case TAG_ID.U:
		case TAG_ID.EM:
		case TAG_ID.TT:
		case TAG_ID.BIG:
		case TAG_ID.CODE:
		case TAG_ID.FONT:
		case TAG_ID.SMALL:
		case TAG_ID.STRIKE:
		case TAG_ID.STRONG: {
			bStartTagInBody(p, token);
			break;
		}
		case TAG_ID.A: {
			aStartTagInBody(p, token);
			break;
		}
		case TAG_ID.H1:
		case TAG_ID.H2:
		case TAG_ID.H3:
		case TAG_ID.H4:
		case TAG_ID.H5:
		case TAG_ID.H6: {
			numberedHeaderStartTagInBody(p, token);
			break;
		}
		case TAG_ID.P:
		case TAG_ID.DL:
		case TAG_ID.OL:
		case TAG_ID.UL:
		case TAG_ID.DIV:
		case TAG_ID.DIR:
		case TAG_ID.NAV:
		case TAG_ID.MAIN:
		case TAG_ID.MENU:
		case TAG_ID.ASIDE:
		case TAG_ID.CENTER:
		case TAG_ID.FIGURE:
		case TAG_ID.FOOTER:
		case TAG_ID.HEADER:
		case TAG_ID.HGROUP:
		case TAG_ID.DIALOG:
		case TAG_ID.DETAILS:
		case TAG_ID.ADDRESS:
		case TAG_ID.ARTICLE:
		case TAG_ID.SEARCH:
		case TAG_ID.SECTION:
		case TAG_ID.SUMMARY:
		case TAG_ID.FIELDSET:
		case TAG_ID.BLOCKQUOTE:
		case TAG_ID.FIGCAPTION: {
			addressStartTagInBody(p, token);
			break;
		}
		case TAG_ID.LI:
		case TAG_ID.DD:
		case TAG_ID.DT: {
			listItemStartTagInBody(p, token);
			break;
		}
		case TAG_ID.BR:
		case TAG_ID.IMG:
		case TAG_ID.WBR:
		case TAG_ID.AREA:
		case TAG_ID.EMBED:
		case TAG_ID.KEYGEN: {
			areaStartTagInBody(p, token);
			break;
		}
		case TAG_ID.HR: {
			hrStartTagInBody(p, token);
			break;
		}
		case TAG_ID.RB:
		case TAG_ID.RTC: {
			rbStartTagInBody(p, token);
			break;
		}
		case TAG_ID.RT:
		case TAG_ID.RP: {
			rtStartTagInBody(p, token);
			break;
		}
		case TAG_ID.PRE:
		case TAG_ID.LISTING: {
			preStartTagInBody(p, token);
			break;
		}
		case TAG_ID.XMP: {
			xmpStartTagInBody(p, token);
			break;
		}
		case TAG_ID.SVG: {
			svgStartTagInBody(p, token);
			break;
		}
		case TAG_ID.HTML: {
			htmlStartTagInBody(p, token);
			break;
		}
		case TAG_ID.BASE:
		case TAG_ID.LINK:
		case TAG_ID.META:
		case TAG_ID.STYLE:
		case TAG_ID.TITLE:
		case TAG_ID.SCRIPT:
		case TAG_ID.BGSOUND:
		case TAG_ID.BASEFONT:
		case TAG_ID.TEMPLATE: {
			startTagInHead(p, token);
			break;
		}
		case TAG_ID.BODY: {
			bodyStartTagInBody(p, token);
			break;
		}
		case TAG_ID.FORM: {
			formStartTagInBody(p, token);
			break;
		}
		case TAG_ID.NOBR: {
			nobrStartTagInBody(p, token);
			break;
		}
		case TAG_ID.MATH: {
			mathStartTagInBody(p, token);
			break;
		}
		case TAG_ID.TABLE: {
			tableStartTagInBody(p, token);
			break;
		}
		case TAG_ID.INPUT: {
			inputStartTagInBody(p, token);
			break;
		}
		case TAG_ID.PARAM:
		case TAG_ID.TRACK:
		case TAG_ID.SOURCE: {
			paramStartTagInBody(p, token);
			break;
		}
		case TAG_ID.IMAGE: {
			imageStartTagInBody(p, token);
			break;
		}
		case TAG_ID.BUTTON: {
			buttonStartTagInBody(p, token);
			break;
		}
		case TAG_ID.APPLET:
		case TAG_ID.OBJECT:
		case TAG_ID.MARQUEE: {
			appletStartTagInBody(p, token);
			break;
		}
		case TAG_ID.IFRAME: {
			iframeStartTagInBody(p, token);
			break;
		}
		case TAG_ID.SELECT: {
			selectStartTagInBody(p, token);
			break;
		}
		case TAG_ID.OPTION:
		case TAG_ID.OPTGROUP: {
			optgroupStartTagInBody(p, token);
			break;
		}
		case TAG_ID.NOEMBED:
		case TAG_ID.NOFRAMES: {
			rawTextStartTagInBody(p, token);
			break;
		}
		case TAG_ID.FRAMESET: {
			framesetStartTagInBody(p, token);
			break;
		}
		case TAG_ID.TEXTAREA: {
			textareaStartTagInBody(p, token);
			break;
		}
		case TAG_ID.NOSCRIPT: {
			if (p.options.scriptingEnabled) {
				rawTextStartTagInBody(p, token);
			} else {
				genericStartTagInBody(p, token);
			}
			break;
		}
		case TAG_ID.PLAINTEXT: {
			plaintextStartTagInBody(p, token);
			break;
		}
		case TAG_ID.COL:
		case TAG_ID.TH:
		case TAG_ID.TD:
		case TAG_ID.TR:
		case TAG_ID.HEAD:
		case TAG_ID.FRAME:
		case TAG_ID.TBODY:
		case TAG_ID.TFOOT:
		case TAG_ID.THEAD:
		case TAG_ID.CAPTION:
		case TAG_ID.COLGROUP: {
			// Ignore token
			break;
		}
		default: {
			genericStartTagInBody(p, token);
		}
	}
}
function bodyEndTagInBody(p, token) {
	if (p.openElements.hasInScope(TAG_ID.BODY)) {
		p.insertionMode = InsertionMode.AFTER_BODY;
		//NOTE: <body> is never popped from the stack, so we need to updated
		//the end location explicitly.
		if (p.options.sourceCodeLocationInfo) {
			const bodyElement = p.openElements.tryPeekProperlyNestedBodyElement();
			if (bodyElement) {
				p._setEndLocation(bodyElement, token);
			}
		}
	}
}
function htmlEndTagInBody(p, token) {
	if (p.openElements.hasInScope(TAG_ID.BODY)) {
		p.insertionMode = InsertionMode.AFTER_BODY;
		endTagAfterBody(p, token);
	}
}
function addressEndTagInBody(p, token) {
	const tn = token.tagID;
	if (p.openElements.hasInScope(tn)) {
		p.openElements.generateImpliedEndTags();
		p.openElements.popUntilTagNamePopped(tn);
	}
}
function formEndTagInBody(p) {
	const inTemplate = p.openElements.tmplCount > 0;
	const { formElement } = p;
	if (!inTemplate) {
		p.formElement = null;
	}
	if ((formElement || inTemplate) && p.openElements.hasInScope(TAG_ID.FORM)) {
		p.openElements.generateImpliedEndTags();
		if (inTemplate) {
			p.openElements.popUntilTagNamePopped(TAG_ID.FORM);
		} else if (formElement) {
			p.openElements.remove(formElement);
		}
	}
}
function pEndTagInBody(p) {
	if (!p.openElements.hasInButtonScope(TAG_ID.P)) {
		p._insertFakeElement(TAG_NAMES.P, TAG_ID.P);
	}
	p._closePElement();
}
function liEndTagInBody(p) {
	if (p.openElements.hasInListItemScope(TAG_ID.LI)) {
		p.openElements.generateImpliedEndTagsWithExclusion(TAG_ID.LI);
		p.openElements.popUntilTagNamePopped(TAG_ID.LI);
	}
}
function ddEndTagInBody(p, token) {
	const tn = token.tagID;
	if (p.openElements.hasInScope(tn)) {
		p.openElements.generateImpliedEndTagsWithExclusion(tn);
		p.openElements.popUntilTagNamePopped(tn);
	}
}
function numberedHeaderEndTagInBody(p) {
	if (p.openElements.hasNumberedHeaderInScope()) {
		p.openElements.generateImpliedEndTags();
		p.openElements.popUntilNumberedHeaderPopped();
	}
}
function appletEndTagInBody(p, token) {
	const tn = token.tagID;
	if (p.openElements.hasInScope(tn)) {
		p.openElements.generateImpliedEndTags();
		p.openElements.popUntilTagNamePopped(tn);
		p.activeFormattingElements.clearToLastMarker();
	}
}
function brEndTagInBody(p) {
	p._reconstructActiveFormattingElements();
	p._insertFakeElement(TAG_NAMES.BR, TAG_ID.BR);
	p.openElements.pop();
	p.framesetOk = false;
}
function genericEndTagInBody(p, token) {
	const tn = token.tagName;
	const tid = token.tagID;
	for (let i = p.openElements.stackTop; i > 0; i--) {
		const element = p.openElements.items[i];
		const elementId = p.openElements.tagIDs[i];
		// Compare the tag name here, as the tag might not be a known tag with an ID.
		if (tid === elementId && (tid !== TAG_ID.UNKNOWN || p.treeAdapter.getTagName(element) === tn)) {
			p.openElements.generateImpliedEndTagsWithExclusion(tid);
			if (p.openElements.stackTop >= i) p.openElements.shortenToLength(i);
			break;
		}
		if (p._isSpecialElement(element, elementId)) {
			break;
		}
	}
}
function endTagInBody(p, token) {
	switch (token.tagID) {
		case TAG_ID.A:
		case TAG_ID.B:
		case TAG_ID.I:
		case TAG_ID.S:
		case TAG_ID.U:
		case TAG_ID.EM:
		case TAG_ID.TT:
		case TAG_ID.BIG:
		case TAG_ID.CODE:
		case TAG_ID.FONT:
		case TAG_ID.NOBR:
		case TAG_ID.SMALL:
		case TAG_ID.STRIKE:
		case TAG_ID.STRONG: {
			callAdoptionAgency(p, token);
			break;
		}
		case TAG_ID.P: {
			pEndTagInBody(p);
			break;
		}
		case TAG_ID.DL:
		case TAG_ID.UL:
		case TAG_ID.OL:
		case TAG_ID.DIR:
		case TAG_ID.DIV:
		case TAG_ID.NAV:
		case TAG_ID.PRE:
		case TAG_ID.MAIN:
		case TAG_ID.MENU:
		case TAG_ID.ASIDE:
		case TAG_ID.BUTTON:
		case TAG_ID.CENTER:
		case TAG_ID.FIGURE:
		case TAG_ID.FOOTER:
		case TAG_ID.HEADER:
		case TAG_ID.HGROUP:
		case TAG_ID.DIALOG:
		case TAG_ID.ADDRESS:
		case TAG_ID.ARTICLE:
		case TAG_ID.DETAILS:
		case TAG_ID.SEARCH:
		case TAG_ID.SECTION:
		case TAG_ID.SUMMARY:
		case TAG_ID.LISTING:
		case TAG_ID.FIELDSET:
		case TAG_ID.BLOCKQUOTE:
		case TAG_ID.FIGCAPTION: {
			addressEndTagInBody(p, token);
			break;
		}
		case TAG_ID.LI: {
			liEndTagInBody(p);
			break;
		}
		case TAG_ID.DD:
		case TAG_ID.DT: {
			ddEndTagInBody(p, token);
			break;
		}
		case TAG_ID.H1:
		case TAG_ID.H2:
		case TAG_ID.H3:
		case TAG_ID.H4:
		case TAG_ID.H5:
		case TAG_ID.H6: {
			numberedHeaderEndTagInBody(p);
			break;
		}
		case TAG_ID.BR: {
			brEndTagInBody(p);
			break;
		}
		case TAG_ID.BODY: {
			bodyEndTagInBody(p, token);
			break;
		}
		case TAG_ID.HTML: {
			htmlEndTagInBody(p, token);
			break;
		}
		case TAG_ID.FORM: {
			formEndTagInBody(p);
			break;
		}
		case TAG_ID.APPLET:
		case TAG_ID.OBJECT:
		case TAG_ID.MARQUEE: {
			appletEndTagInBody(p, token);
			break;
		}
		case TAG_ID.TEMPLATE: {
			templateEndTagInHead(p, token);
			break;
		}
		default: {
			genericEndTagInBody(p, token);
		}
	}
}
function eofInBody(p, token) {
	if (p.tmplInsertionModeStack.length > 0) {
		eofInTemplate(p, token);
	} else {
		stopParsing(p, token);
	}
}
// The "text" insertion mode
//------------------------------------------------------------------
function endTagInText(p, token) {
	var _a;
	if (token.tagID === TAG_ID.SCRIPT) {
		(_a = p.scriptHandler) === null || _a === void 0 ? void 0 : _a.call(p, p.openElements.current);
	}
	p.openElements.pop();
	p.insertionMode = p.originalInsertionMode;
}
function eofInText(p, token) {
	p._err(token, ERR.eofInElementThatCanContainOnlyText);
	p.openElements.pop();
	p.insertionMode = p.originalInsertionMode;
	p.onEof(token);
}
// The "in table" insertion mode
//------------------------------------------------------------------
function characterInTable(p, token) {
	if (p.openElements.currentTagId !== undefined && TABLE_STRUCTURE_TAGS.has(p.openElements.currentTagId)) {
		p.pendingCharacterTokens.length = 0;
		p.hasNonWhitespacePendingCharacterToken = false;
		p.originalInsertionMode = p.insertionMode;
		p.insertionMode = InsertionMode.IN_TABLE_TEXT;
		switch (token.type) {
			case TokenType.CHARACTER: {
				characterInTableText(p, token);
				break;
			}
			case TokenType.WHITESPACE_CHARACTER: {
				whitespaceCharacterInTableText(p, token);
				break;
			}
			// Ignore null
		}
	} else {
		tokenInTable(p, token);
	}
}
function captionStartTagInTable(p, token) {
	p.openElements.clearBackToTableContext();
	p.activeFormattingElements.insertMarker();
	p._insertElement(token, NS.HTML);
	p.insertionMode = InsertionMode.IN_CAPTION;
}
function colgroupStartTagInTable(p, token) {
	p.openElements.clearBackToTableContext();
	p._insertElement(token, NS.HTML);
	p.insertionMode = InsertionMode.IN_COLUMN_GROUP;
}
function colStartTagInTable(p, token) {
	p.openElements.clearBackToTableContext();
	p._insertFakeElement(TAG_NAMES.COLGROUP, TAG_ID.COLGROUP);
	p.insertionMode = InsertionMode.IN_COLUMN_GROUP;
	startTagInColumnGroup(p, token);
}
function tbodyStartTagInTable(p, token) {
	p.openElements.clearBackToTableContext();
	p._insertElement(token, NS.HTML);
	p.insertionMode = InsertionMode.IN_TABLE_BODY;
}
function tdStartTagInTable(p, token) {
	p.openElements.clearBackToTableContext();
	p._insertFakeElement(TAG_NAMES.TBODY, TAG_ID.TBODY);
	p.insertionMode = InsertionMode.IN_TABLE_BODY;
	startTagInTableBody(p, token);
}
function tableStartTagInTable(p, token) {
	if (p.openElements.hasInTableScope(TAG_ID.TABLE)) {
		p.openElements.popUntilTagNamePopped(TAG_ID.TABLE);
		p._resetInsertionMode();
		p._processStartTag(token);
	}
}
function inputStartTagInTable(p, token) {
	if (isHiddenInput(token)) {
		p._appendElement(token, NS.HTML);
	} else {
		tokenInTable(p, token);
	}
	token.ackSelfClosing = true;
}
function formStartTagInTable(p, token) {
	if (!p.formElement && p.openElements.tmplCount === 0) {
		p._insertElement(token, NS.HTML);
		p.formElement = p.openElements.current;
		p.openElements.pop();
	}
}
function startTagInTable(p, token) {
	switch (token.tagID) {
		case TAG_ID.TD:
		case TAG_ID.TH:
		case TAG_ID.TR: {
			tdStartTagInTable(p, token);
			break;
		}
		case TAG_ID.STYLE:
		case TAG_ID.SCRIPT:
		case TAG_ID.TEMPLATE: {
			startTagInHead(p, token);
			break;
		}
		case TAG_ID.COL: {
			colStartTagInTable(p, token);
			break;
		}
		case TAG_ID.FORM: {
			formStartTagInTable(p, token);
			break;
		}
		case TAG_ID.TABLE: {
			tableStartTagInTable(p, token);
			break;
		}
		case TAG_ID.TBODY:
		case TAG_ID.TFOOT:
		case TAG_ID.THEAD: {
			tbodyStartTagInTable(p, token);
			break;
		}
		case TAG_ID.INPUT: {
			inputStartTagInTable(p, token);
			break;
		}
		case TAG_ID.CAPTION: {
			captionStartTagInTable(p, token);
			break;
		}
		case TAG_ID.COLGROUP: {
			colgroupStartTagInTable(p, token);
			break;
		}
		default: {
			tokenInTable(p, token);
		}
	}
}
function endTagInTable(p, token) {
	switch (token.tagID) {
		case TAG_ID.TABLE: {
			if (p.openElements.hasInTableScope(TAG_ID.TABLE)) {
				p.openElements.popUntilTagNamePopped(TAG_ID.TABLE);
				p._resetInsertionMode();
			}
			break;
		}
		case TAG_ID.TEMPLATE: {
			templateEndTagInHead(p, token);
			break;
		}
		case TAG_ID.BODY:
		case TAG_ID.CAPTION:
		case TAG_ID.COL:
		case TAG_ID.COLGROUP:
		case TAG_ID.HTML:
		case TAG_ID.TBODY:
		case TAG_ID.TD:
		case TAG_ID.TFOOT:
		case TAG_ID.TH:
		case TAG_ID.THEAD:
		case TAG_ID.TR: {
			// Ignore token
			break;
		}
		default: {
			tokenInTable(p, token);
		}
	}
}
function tokenInTable(p, token) {
	const savedFosterParentingState = p.fosterParentingEnabled;
	p.fosterParentingEnabled = true;
	// Process token in `In Body` mode
	modeInBody(p, token);
	p.fosterParentingEnabled = savedFosterParentingState;
}
// The "in table text" insertion mode
//------------------------------------------------------------------
function whitespaceCharacterInTableText(p, token) {
	p.pendingCharacterTokens.push(token);
}
function characterInTableText(p, token) {
	p.pendingCharacterTokens.push(token);
	p.hasNonWhitespacePendingCharacterToken = true;
}
function tokenInTableText(p, token) {
	let i = 0;
	if (p.hasNonWhitespacePendingCharacterToken) {
		for (; i < p.pendingCharacterTokens.length; i++) {
			tokenInTable(p, p.pendingCharacterTokens[i]);
		}
	} else {
		for (; i < p.pendingCharacterTokens.length; i++) {
			p._insertCharacters(p.pendingCharacterTokens[i]);
		}
	}
	p.insertionMode = p.originalInsertionMode;
	p._processToken(token);
}
// The "in caption" insertion mode
//------------------------------------------------------------------
const TABLE_VOID_ELEMENTS = new Set([TAG_ID.CAPTION, TAG_ID.COL, TAG_ID.COLGROUP, TAG_ID.TBODY, TAG_ID.TD, TAG_ID.TFOOT, TAG_ID.TH, TAG_ID.THEAD, TAG_ID.TR]);
function startTagInCaption(p, token) {
	const tn = token.tagID;
	if (TABLE_VOID_ELEMENTS.has(tn)) {
		if (p.openElements.hasInTableScope(TAG_ID.CAPTION)) {
			p.openElements.generateImpliedEndTags();
			p.openElements.popUntilTagNamePopped(TAG_ID.CAPTION);
			p.activeFormattingElements.clearToLastMarker();
			p.insertionMode = InsertionMode.IN_TABLE;
			startTagInTable(p, token);
		}
	} else {
		startTagInBody(p, token);
	}
}
function endTagInCaption(p, token) {
	const tn = token.tagID;
	switch (tn) {
		case TAG_ID.CAPTION:
		case TAG_ID.TABLE: {
			if (p.openElements.hasInTableScope(TAG_ID.CAPTION)) {
				p.openElements.generateImpliedEndTags();
				p.openElements.popUntilTagNamePopped(TAG_ID.CAPTION);
				p.activeFormattingElements.clearToLastMarker();
				p.insertionMode = InsertionMode.IN_TABLE;
				if (tn === TAG_ID.TABLE) {
					endTagInTable(p, token);
				}
			}
			break;
		}
		case TAG_ID.BODY:
		case TAG_ID.COL:
		case TAG_ID.COLGROUP:
		case TAG_ID.HTML:
		case TAG_ID.TBODY:
		case TAG_ID.TD:
		case TAG_ID.TFOOT:
		case TAG_ID.TH:
		case TAG_ID.THEAD:
		case TAG_ID.TR: {
			// Ignore token
			break;
		}
		default: {
			endTagInBody(p, token);
		}
	}
}
// The "in column group" insertion mode
//------------------------------------------------------------------
function startTagInColumnGroup(p, token) {
	switch (token.tagID) {
		case TAG_ID.HTML: {
			startTagInBody(p, token);
			break;
		}
		case TAG_ID.COL: {
			p._appendElement(token, NS.HTML);
			token.ackSelfClosing = true;
			break;
		}
		case TAG_ID.TEMPLATE: {
			startTagInHead(p, token);
			break;
		}
		default: {
			tokenInColumnGroup(p, token);
		}
	}
}
function endTagInColumnGroup(p, token) {
	switch (token.tagID) {
		case TAG_ID.COLGROUP: {
			if (p.openElements.currentTagId === TAG_ID.COLGROUP) {
				p.openElements.pop();
				p.insertionMode = InsertionMode.IN_TABLE;
			}
			break;
		}
		case TAG_ID.TEMPLATE: {
			templateEndTagInHead(p, token);
			break;
		}
		case TAG_ID.COL: {
			// Ignore token
			break;
		}
		default: {
			tokenInColumnGroup(p, token);
		}
	}
}
function tokenInColumnGroup(p, token) {
	if (p.openElements.currentTagId === TAG_ID.COLGROUP) {
		p.openElements.pop();
		p.insertionMode = InsertionMode.IN_TABLE;
		p._processToken(token);
	}
}
// The "in table body" insertion mode
//------------------------------------------------------------------
function startTagInTableBody(p, token) {
	switch (token.tagID) {
		case TAG_ID.TR: {
			p.openElements.clearBackToTableBodyContext();
			p._insertElement(token, NS.HTML);
			p.insertionMode = InsertionMode.IN_ROW;
			break;
		}
		case TAG_ID.TH:
		case TAG_ID.TD: {
			p.openElements.clearBackToTableBodyContext();
			p._insertFakeElement(TAG_NAMES.TR, TAG_ID.TR);
			p.insertionMode = InsertionMode.IN_ROW;
			startTagInRow(p, token);
			break;
		}
		case TAG_ID.CAPTION:
		case TAG_ID.COL:
		case TAG_ID.COLGROUP:
		case TAG_ID.TBODY:
		case TAG_ID.TFOOT:
		case TAG_ID.THEAD: {
			if (p.openElements.hasTableBodyContextInTableScope()) {
				p.openElements.clearBackToTableBodyContext();
				p.openElements.pop();
				p.insertionMode = InsertionMode.IN_TABLE;
				startTagInTable(p, token);
			}
			break;
		}
		default: {
			startTagInTable(p, token);
		}
	}
}
function endTagInTableBody(p, token) {
	const tn = token.tagID;
	switch (token.tagID) {
		case TAG_ID.TBODY:
		case TAG_ID.TFOOT:
		case TAG_ID.THEAD: {
			if (p.openElements.hasInTableScope(tn)) {
				p.openElements.clearBackToTableBodyContext();
				p.openElements.pop();
				p.insertionMode = InsertionMode.IN_TABLE;
			}
			break;
		}
		case TAG_ID.TABLE: {
			if (p.openElements.hasTableBodyContextInTableScope()) {
				p.openElements.clearBackToTableBodyContext();
				p.openElements.pop();
				p.insertionMode = InsertionMode.IN_TABLE;
				endTagInTable(p, token);
			}
			break;
		}
		case TAG_ID.BODY:
		case TAG_ID.CAPTION:
		case TAG_ID.COL:
		case TAG_ID.COLGROUP:
		case TAG_ID.HTML:
		case TAG_ID.TD:
		case TAG_ID.TH:
		case TAG_ID.TR: {
			// Ignore token
			break;
		}
		default: {
			endTagInTable(p, token);
		}
	}
}
// The "in row" insertion mode
//------------------------------------------------------------------
function startTagInRow(p, token) {
	switch (token.tagID) {
		case TAG_ID.TH:
		case TAG_ID.TD: {
			p.openElements.clearBackToTableRowContext();
			p._insertElement(token, NS.HTML);
			p.insertionMode = InsertionMode.IN_CELL;
			p.activeFormattingElements.insertMarker();
			break;
		}
		case TAG_ID.CAPTION:
		case TAG_ID.COL:
		case TAG_ID.COLGROUP:
		case TAG_ID.TBODY:
		case TAG_ID.TFOOT:
		case TAG_ID.THEAD:
		case TAG_ID.TR: {
			if (p.openElements.hasInTableScope(TAG_ID.TR)) {
				p.openElements.clearBackToTableRowContext();
				p.openElements.pop();
				p.insertionMode = InsertionMode.IN_TABLE_BODY;
				startTagInTableBody(p, token);
			}
			break;
		}
		default: {
			startTagInTable(p, token);
		}
	}
}
function endTagInRow(p, token) {
	switch (token.tagID) {
		case TAG_ID.TR: {
			if (p.openElements.hasInTableScope(TAG_ID.TR)) {
				p.openElements.clearBackToTableRowContext();
				p.openElements.pop();
				p.insertionMode = InsertionMode.IN_TABLE_BODY;
			}
			break;
		}
		case TAG_ID.TABLE: {
			if (p.openElements.hasInTableScope(TAG_ID.TR)) {
				p.openElements.clearBackToTableRowContext();
				p.openElements.pop();
				p.insertionMode = InsertionMode.IN_TABLE_BODY;
				endTagInTableBody(p, token);
			}
			break;
		}
		case TAG_ID.TBODY:
		case TAG_ID.TFOOT:
		case TAG_ID.THEAD: {
			if (p.openElements.hasInTableScope(token.tagID) || p.openElements.hasInTableScope(TAG_ID.TR)) {
				p.openElements.clearBackToTableRowContext();
				p.openElements.pop();
				p.insertionMode = InsertionMode.IN_TABLE_BODY;
				endTagInTableBody(p, token);
			}
			break;
		}
		case TAG_ID.BODY:
		case TAG_ID.CAPTION:
		case TAG_ID.COL:
		case TAG_ID.COLGROUP:
		case TAG_ID.HTML:
		case TAG_ID.TD:
		case TAG_ID.TH: {
			// Ignore end tag
			break;
		}
		default: {
			endTagInTable(p, token);
		}
	}
}
// The "in cell" insertion mode
//------------------------------------------------------------------
function startTagInCell(p, token) {
	const tn = token.tagID;
	if (TABLE_VOID_ELEMENTS.has(tn)) {
		if (p.openElements.hasInTableScope(TAG_ID.TD) || p.openElements.hasInTableScope(TAG_ID.TH)) {
			p._closeTableCell();
			startTagInRow(p, token);
		}
	} else {
		startTagInBody(p, token);
	}
}
function endTagInCell(p, token) {
	const tn = token.tagID;
	switch (tn) {
		case TAG_ID.TD:
		case TAG_ID.TH: {
			if (p.openElements.hasInTableScope(tn)) {
				p.openElements.generateImpliedEndTags();
				p.openElements.popUntilTagNamePopped(tn);
				p.activeFormattingElements.clearToLastMarker();
				p.insertionMode = InsertionMode.IN_ROW;
			}
			break;
		}
		case TAG_ID.TABLE:
		case TAG_ID.TBODY:
		case TAG_ID.TFOOT:
		case TAG_ID.THEAD:
		case TAG_ID.TR: {
			if (p.openElements.hasInTableScope(tn)) {
				p._closeTableCell();
				endTagInRow(p, token);
			}
			break;
		}
		case TAG_ID.BODY:
		case TAG_ID.CAPTION:
		case TAG_ID.COL:
		case TAG_ID.COLGROUP:
		case TAG_ID.HTML: {
			// Ignore token
			break;
		}
		default: {
			endTagInBody(p, token);
		}
	}
}
// The "in select" insertion mode
//------------------------------------------------------------------
function startTagInSelect(p, token) {
	switch (token.tagID) {
		case TAG_ID.HTML: {
			startTagInBody(p, token);
			break;
		}
		case TAG_ID.OPTION: {
			if (p.openElements.currentTagId === TAG_ID.OPTION) {
				p.openElements.pop();
			}
			p._insertElement(token, NS.HTML);
			break;
		}
		case TAG_ID.OPTGROUP: {
			if (p.openElements.currentTagId === TAG_ID.OPTION) {
				p.openElements.pop();
			}
			if (p.openElements.currentTagId === TAG_ID.OPTGROUP) {
				p.openElements.pop();
			}
			p._insertElement(token, NS.HTML);
			break;
		}
		case TAG_ID.HR: {
			if (p.openElements.currentTagId === TAG_ID.OPTION) {
				p.openElements.pop();
			}
			if (p.openElements.currentTagId === TAG_ID.OPTGROUP) {
				p.openElements.pop();
			}
			p._appendElement(token, NS.HTML);
			token.ackSelfClosing = true;
			break;
		}
		case TAG_ID.INPUT:
		case TAG_ID.KEYGEN:
		case TAG_ID.TEXTAREA:
		case TAG_ID.SELECT: {
			if (p.openElements.hasInSelectScope(TAG_ID.SELECT)) {
				p.openElements.popUntilTagNamePopped(TAG_ID.SELECT);
				p._resetInsertionMode();
				if (token.tagID !== TAG_ID.SELECT) {
					p._processStartTag(token);
				}
			}
			break;
		}
		case TAG_ID.SCRIPT:
		case TAG_ID.TEMPLATE: {
			startTagInHead(p, token);
			break;
		}
		// Do nothing
	}
}
function endTagInSelect(p, token) {
	switch (token.tagID) {
		case TAG_ID.OPTGROUP: {
			if (p.openElements.stackTop > 0 && p.openElements.currentTagId === TAG_ID.OPTION && p.openElements.tagIDs[p.openElements.stackTop - 1] === TAG_ID.OPTGROUP) {
				p.openElements.pop();
			}
			if (p.openElements.currentTagId === TAG_ID.OPTGROUP) {
				p.openElements.pop();
			}
			break;
		}
		case TAG_ID.OPTION: {
			if (p.openElements.currentTagId === TAG_ID.OPTION) {
				p.openElements.pop();
			}
			break;
		}
		case TAG_ID.SELECT: {
			if (p.openElements.hasInSelectScope(TAG_ID.SELECT)) {
				p.openElements.popUntilTagNamePopped(TAG_ID.SELECT);
				p._resetInsertionMode();
			}
			break;
		}
		case TAG_ID.TEMPLATE: {
			templateEndTagInHead(p, token);
			break;
		}
		// Do nothing
	}
}
// The "in select in table" insertion mode
//------------------------------------------------------------------
function startTagInSelectInTable(p, token) {
	const tn = token.tagID;
	if (tn === TAG_ID.CAPTION || tn === TAG_ID.TABLE || tn === TAG_ID.TBODY || tn === TAG_ID.TFOOT || tn === TAG_ID.THEAD || tn === TAG_ID.TR || tn === TAG_ID.TD || tn === TAG_ID.TH) {
		p.openElements.popUntilTagNamePopped(TAG_ID.SELECT);
		p._resetInsertionMode();
		p._processStartTag(token);
	} else {
		startTagInSelect(p, token);
	}
}
function endTagInSelectInTable(p, token) {
	const tn = token.tagID;
	if (tn === TAG_ID.CAPTION || tn === TAG_ID.TABLE || tn === TAG_ID.TBODY || tn === TAG_ID.TFOOT || tn === TAG_ID.THEAD || tn === TAG_ID.TR || tn === TAG_ID.TD || tn === TAG_ID.TH) {
		if (p.openElements.hasInTableScope(tn)) {
			p.openElements.popUntilTagNamePopped(TAG_ID.SELECT);
			p._resetInsertionMode();
			p.onEndTag(token);
		}
	} else {
		endTagInSelect(p, token);
	}
}
// The "in template" insertion mode
//------------------------------------------------------------------
function startTagInTemplate(p, token) {
	switch (token.tagID) {
		// First, handle tags that can start without a mode change
		case TAG_ID.BASE:
		case TAG_ID.BASEFONT:
		case TAG_ID.BGSOUND:
		case TAG_ID.LINK:
		case TAG_ID.META:
		case TAG_ID.NOFRAMES:
		case TAG_ID.SCRIPT:
		case TAG_ID.STYLE:
		case TAG_ID.TEMPLATE:
		case TAG_ID.TITLE: {
			startTagInHead(p, token);
			break;
		}
		// Re-process the token in the appropriate mode
		case TAG_ID.CAPTION:
		case TAG_ID.COLGROUP:
		case TAG_ID.TBODY:
		case TAG_ID.TFOOT:
		case TAG_ID.THEAD: {
			p.tmplInsertionModeStack[0] = InsertionMode.IN_TABLE;
			p.insertionMode = InsertionMode.IN_TABLE;
			startTagInTable(p, token);
			break;
		}
		case TAG_ID.COL: {
			p.tmplInsertionModeStack[0] = InsertionMode.IN_COLUMN_GROUP;
			p.insertionMode = InsertionMode.IN_COLUMN_GROUP;
			startTagInColumnGroup(p, token);
			break;
		}
		case TAG_ID.TR: {
			p.tmplInsertionModeStack[0] = InsertionMode.IN_TABLE_BODY;
			p.insertionMode = InsertionMode.IN_TABLE_BODY;
			startTagInTableBody(p, token);
			break;
		}
		case TAG_ID.TD:
		case TAG_ID.TH: {
			p.tmplInsertionModeStack[0] = InsertionMode.IN_ROW;
			p.insertionMode = InsertionMode.IN_ROW;
			startTagInRow(p, token);
			break;
		}
		default: {
			p.tmplInsertionModeStack[0] = InsertionMode.IN_BODY;
			p.insertionMode = InsertionMode.IN_BODY;
			startTagInBody(p, token);
		}
	}
}
function endTagInTemplate(p, token) {
	if (token.tagID === TAG_ID.TEMPLATE) {
		templateEndTagInHead(p, token);
	}
}
function eofInTemplate(p, token) {
	if (p.openElements.tmplCount > 0) {
		p.openElements.popUntilTagNamePopped(TAG_ID.TEMPLATE);
		p.activeFormattingElements.clearToLastMarker();
		p.tmplInsertionModeStack.shift();
		p._resetInsertionMode();
		p.onEof(token);
	} else {
		stopParsing(p, token);
	}
}
// The "after body" insertion mode
//------------------------------------------------------------------
function startTagAfterBody(p, token) {
	if (token.tagID === TAG_ID.HTML) {
		startTagInBody(p, token);
	} else {
		tokenAfterBody(p, token);
	}
}
function endTagAfterBody(p, token) {
	var _a;
	if (token.tagID === TAG_ID.HTML) {
		if (!p.fragmentContext) {
			p.insertionMode = InsertionMode.AFTER_AFTER_BODY;
		}
		//NOTE: <html> is never popped from the stack, so we need to updated
		//the end location explicitly.
		if (p.options.sourceCodeLocationInfo && p.openElements.tagIDs[0] === TAG_ID.HTML) {
			p._setEndLocation(p.openElements.items[0], token);
			// Update the body element, if it doesn't have an end tag
			const bodyElement = p.openElements.items[1];
			if (bodyElement && !((_a = p.treeAdapter.getNodeSourceCodeLocation(bodyElement)) === null || _a === void 0 ? void 0 : _a.endTag)) {
				p._setEndLocation(bodyElement, token);
			}
		}
	} else {
		tokenAfterBody(p, token);
	}
}
function tokenAfterBody(p, token) {
	p.insertionMode = InsertionMode.IN_BODY;
	modeInBody(p, token);
}
// The "in frameset" insertion mode
//------------------------------------------------------------------
function startTagInFrameset(p, token) {
	switch (token.tagID) {
		case TAG_ID.HTML: {
			startTagInBody(p, token);
			break;
		}
		case TAG_ID.FRAMESET: {
			p._insertElement(token, NS.HTML);
			break;
		}
		case TAG_ID.FRAME: {
			p._appendElement(token, NS.HTML);
			token.ackSelfClosing = true;
			break;
		}
		case TAG_ID.NOFRAMES: {
			startTagInHead(p, token);
			break;
		}
		// Do nothing
	}
}
function endTagInFrameset(p, token) {
	if (token.tagID === TAG_ID.FRAMESET && !p.openElements.isRootHtmlElementCurrent()) {
		p.openElements.pop();
		if (!p.fragmentContext && p.openElements.currentTagId !== TAG_ID.FRAMESET) {
			p.insertionMode = InsertionMode.AFTER_FRAMESET;
		}
	}
}
// The "after frameset" insertion mode
//------------------------------------------------------------------
function startTagAfterFrameset(p, token) {
	switch (token.tagID) {
		case TAG_ID.HTML: {
			startTagInBody(p, token);
			break;
		}
		case TAG_ID.NOFRAMES: {
			startTagInHead(p, token);
			break;
		}
		// Do nothing
	}
}
function endTagAfterFrameset(p, token) {
	if (token.tagID === TAG_ID.HTML) {
		p.insertionMode = InsertionMode.AFTER_AFTER_FRAMESET;
	}
}
// The "after after body" insertion mode
//------------------------------------------------------------------
function startTagAfterAfterBody(p, token) {
	if (token.tagID === TAG_ID.HTML) {
		startTagInBody(p, token);
	} else {
		tokenAfterAfterBody(p, token);
	}
}
function tokenAfterAfterBody(p, token) {
	p.insertionMode = InsertionMode.IN_BODY;
	modeInBody(p, token);
}
// The "after after frameset" insertion mode
//------------------------------------------------------------------
function startTagAfterAfterFrameset(p, token) {
	switch (token.tagID) {
		case TAG_ID.HTML: {
			startTagInBody(p, token);
			break;
		}
		case TAG_ID.NOFRAMES: {
			startTagInHead(p, token);
			break;
		}
		// Do nothing
	}
}
// The rules for parsing tokens in foreign content
//------------------------------------------------------------------
function nullCharacterInForeignContent(p, token) {
	token.chars = REPLACEMENT_CHARACTER;
	p._insertCharacters(token);
}
function characterInForeignContent(p, token) {
	p._insertCharacters(token);
	p.framesetOk = false;
}
function popUntilHtmlOrIntegrationPoint(p) {
	while (
		p.treeAdapter.getNamespaceURI(p.openElements.current) !== NS.HTML &&
		p.openElements.currentTagId !== undefined &&
		!p._isIntegrationPoint(p.openElements.currentTagId, p.openElements.current)
	) {
		p.openElements.pop();
	}
}
function startTagInForeignContent(p, token) {
	if (causesExit(token)) {
		popUntilHtmlOrIntegrationPoint(p);
		p._startTagOutsideForeignContent(token);
	} else {
		const current = p._getAdjustedCurrentElement();
		const currentNs = p.treeAdapter.getNamespaceURI(current);
		if (currentNs === NS.MATHML) {
			adjustTokenMathMLAttrs(token);
		} else if (currentNs === NS.SVG) {
			adjustTokenSVGTagName(token);
			adjustTokenSVGAttrs(token);
		}
		adjustTokenXMLAttrs(token);
		if (token.selfClosing) {
			p._appendElement(token, currentNs);
		} else {
			p._insertElement(token, currentNs);
		}
		token.ackSelfClosing = true;
	}
}
function endTagInForeignContent(p, token) {
	if (token.tagID === TAG_ID.P || token.tagID === TAG_ID.BR) {
		popUntilHtmlOrIntegrationPoint(p);
		p._endTagOutsideForeignContent(token);
		return;
	}
	for (let i = p.openElements.stackTop; i > 0; i--) {
		const element = p.openElements.items[i];
		if (p.treeAdapter.getNamespaceURI(element) === NS.HTML) {
			p._endTagOutsideForeignContent(token);
			break;
		}
		const tagName = p.treeAdapter.getTagName(element);
		if (tagName.toLowerCase() === token.tagName) {
			//NOTE: update the token tag name for `_setEndLocation`.
			token.tagName = tagName;
			p.openElements.shortenToLength(i);
			break;
		}
	}
}

// Sets
new Set([
	TAG_NAMES.AREA,
	TAG_NAMES.BASE,
	TAG_NAMES.BASEFONT,
	TAG_NAMES.BGSOUND,
	TAG_NAMES.BR,
	TAG_NAMES.COL,
	TAG_NAMES.EMBED,
	TAG_NAMES.FRAME,
	TAG_NAMES.HR,
	TAG_NAMES.IMG,
	TAG_NAMES.INPUT,
	TAG_NAMES.KEYGEN,
	TAG_NAMES.LINK,
	TAG_NAMES.META,
	TAG_NAMES.PARAM,
	TAG_NAMES.SOURCE,
	TAG_NAMES.TRACK,
	TAG_NAMES.WBR,
]);

// Shorthands
/**
 * Parses an HTML string.
 *
 * @param html Input HTML string.
 * @param options Parsing options.
 * @returns Document
 *
 * @example
 *
 * ```js
 * const parse5 = require('parse5');
 *
 * const document = parse5.parse('<!DOCTYPE html><html><head></head><body>Hi there!</body></html>');
 *
 * console.log(document.childNodes[1].tagName); //> 'html'
 *```
 */
function parse$2(html, options) {
	return Parser.parse(html, options);
}
function parseFragment(fragmentContext, html, options) {
	if (typeof fragmentContext === "string") {
		options = html;
		html = fragmentContext;
		fragmentContext = null;
	}
	const parser = Parser.getFragmentParser(fragmentContext, options);
	parser.tokenizer.write(html, true);
	return parser.getFragment();
}

/**
 * @typedef {import('unist').Node} Node
 * @typedef {import('unist').Point} Point
 * @typedef {import('unist').Position} Position
 */

/**
 * @typedef NodeLike
 * @property {string} type
 * @property {PositionLike | null | undefined} [position]
 *
 * @typedef PointLike
 * @property {number | null | undefined} [line]
 * @property {number | null | undefined} [column]
 * @property {number | null | undefined} [offset]
 *
 * @typedef PositionLike
 * @property {PointLike | null | undefined} [start]
 * @property {PointLike | null | undefined} [end]
 */

/**
 * Serialize the positional info of a point, position (start and end points),
 * or node.
 *
 * @param {Node | NodeLike | Point | PointLike | Position | PositionLike | null | undefined} [value]
 *   Node, position, or point.
 * @returns {string}
 *   Pretty printed positional info of a node (`string`).
 *
 *   In the format of a range `ls:cs-le:ce` (when given `node` or `position`)
 *   or a point `l:c` (when given `point`), where `l` stands for line, `c` for
 *   column, `s` for `start`, and `e` for end.
 *   An empty string (`''`) is returned if the given value is neither `node`,
 *   `position`, nor `point`.
 */
function stringifyPosition(value) {
	// Nothing.
	if (!value || typeof value !== "object") {
		return "";
	}

	// Node.
	if ("position" in value || "type" in value) {
		return position$1(value.position);
	}

	// Position.
	if ("start" in value || "end" in value) {
		return position$1(value);
	}

	// Point.
	if ("line" in value || "column" in value) {
		return point$2(value);
	}

	// ?
	return "";
}

/**
 * @param {Point | PointLike | null | undefined} point
 * @returns {string}
 */
function point$2(point) {
	return index$1(point && point.line) + ":" + index$1(point && point.column);
}

/**
 * @param {Position | PositionLike | null | undefined} pos
 * @returns {string}
 */
function position$1(pos) {
	return point$2(pos && pos.start) + "-" + point$2(pos && pos.end);
}

/**
 * @param {number | null | undefined} value
 * @returns {number}
 */
function index$1(value) {
	return value && typeof value === "number" ? value : 1;
}

/**
 * @import {Node, Point, Position} from 'unist'
 */

/**
 * Message.
 */
class VFileMessage extends Error {
	/**
	 * Create a message for `reason`.
	 *
	 * > 🪦 **Note**: also has obsolete signatures.
	 *
	 * @overload
	 * @param {string} reason
	 * @param {Options | null | undefined} [options]
	 * @returns
	 *
	 * @overload
	 * @param {string} reason
	 * @param {Node | NodeLike | null | undefined} parent
	 * @param {string | null | undefined} [origin]
	 * @returns
	 *
	 * @overload
	 * @param {string} reason
	 * @param {Point | Position | null | undefined} place
	 * @param {string | null | undefined} [origin]
	 * @returns
	 *
	 * @overload
	 * @param {string} reason
	 * @param {string | null | undefined} [origin]
	 * @returns
	 *
	 * @overload
	 * @param {Error | VFileMessage} cause
	 * @param {Node | NodeLike | null | undefined} parent
	 * @param {string | null | undefined} [origin]
	 * @returns
	 *
	 * @overload
	 * @param {Error | VFileMessage} cause
	 * @param {Point | Position | null | undefined} place
	 * @param {string | null | undefined} [origin]
	 * @returns
	 *
	 * @overload
	 * @param {Error | VFileMessage} cause
	 * @param {string | null | undefined} [origin]
	 * @returns
	 *
	 * @param {Error | VFileMessage | string} causeOrReason
	 *   Reason for message, should use markdown.
	 * @param {Node | NodeLike | Options | Point | Position | string | null | undefined} [optionsOrParentOrPlace]
	 *   Configuration (optional).
	 * @param {string | null | undefined} [origin]
	 *   Place in code where the message originates (example:
	 *   `'my-package:my-rule'` or `'my-rule'`).
	 * @returns
	 *   Instance of `VFileMessage`.
	 */
	// eslint-disable-next-line complexity
	constructor(causeOrReason, optionsOrParentOrPlace, origin) {
		super();

		if (typeof optionsOrParentOrPlace === "string") {
			origin = optionsOrParentOrPlace;
			optionsOrParentOrPlace = undefined;
		}

		/** @type {string} */
		let reason = "";
		/** @type {Options} */
		let options = {};
		let legacyCause = false;

		if (optionsOrParentOrPlace) {
			// Point.
			if ("line" in optionsOrParentOrPlace && "column" in optionsOrParentOrPlace) {
				options = { place: optionsOrParentOrPlace };
			}
			// Position.
			else if ("start" in optionsOrParentOrPlace && "end" in optionsOrParentOrPlace) {
				options = { place: optionsOrParentOrPlace };
			}
			// Node.
			else if ("type" in optionsOrParentOrPlace) {
				options = {
					ancestors: [optionsOrParentOrPlace],
					place: optionsOrParentOrPlace.position,
				};
			}
			// Options.
			else {
				options = { ...optionsOrParentOrPlace };
			}
		}

		if (typeof causeOrReason === "string") {
			reason = causeOrReason;
		}
		// Error.
		else if (!options.cause && causeOrReason) {
			legacyCause = true;
			reason = causeOrReason.message;
			options.cause = causeOrReason;
		}

		if (!options.ruleId && !options.source && typeof origin === "string") {
			const index = origin.indexOf(":");

			if (index === -1) {
				options.ruleId = origin;
			} else {
				options.source = origin.slice(0, index);
				options.ruleId = origin.slice(index + 1);
			}
		}

		if (!options.place && options.ancestors && options.ancestors) {
			const parent = options.ancestors[options.ancestors.length - 1];

			if (parent) {
				options.place = parent.position;
			}
		}

		const start = options.place && "start" in options.place ? options.place.start : options.place;

		/**
		 * Stack of ancestor nodes surrounding the message.
		 *
		 * @type {Array<Node> | undefined}
		 */
		this.ancestors = options.ancestors || undefined;

		/**
		 * Original error cause of the message.
		 *
		 * @type {Error | undefined}
		 */
		this.cause = options.cause || undefined;

		/**
		 * Starting column of message.
		 *
		 * @type {number | undefined}
		 */
		this.column = start ? start.column : undefined;

		/**
		 * State of problem.
		 *
		 * * `true` — error, file not usable
		 * * `false` — warning, change may be needed
		 * * `undefined` — change likely not needed
		 *
		 * @type {boolean | null | undefined}
		 */
		this.fatal = undefined;

		/**
		 * Path of a file (used throughout the `VFile` ecosystem).
		 *
		 * @type {string | undefined}
		 */
		this.file = "";

		// Field from `Error`.
		/**
		 * Reason for message.
		 *
		 * @type {string}
		 */
		this.message = reason;

		/**
		 * Starting line of error.
		 *
		 * @type {number | undefined}
		 */
		this.line = start ? start.line : undefined;

		// Field from `Error`.
		/**
		 * Serialized positional info of message.
		 *
		 * On normal errors, this would be something like `ParseError`, buit in
		 * `VFile` messages we use this space to show where an error happened.
		 */
		this.name = stringifyPosition(options.place) || "1:1";

		/**
		 * Place of message.
		 *
		 * @type {Point | Position | undefined}
		 */
		this.place = options.place || undefined;

		/**
		 * Reason for message, should use markdown.
		 *
		 * @type {string}
		 */
		this.reason = this.message;

		/**
		 * Category of message (example: `'my-rule'`).
		 *
		 * @type {string | undefined}
		 */
		this.ruleId = options.ruleId || undefined;

		/**
		 * Namespace of message (example: `'my-package'`).
		 *
		 * @type {string | undefined}
		 */
		this.source = options.source || undefined;

		// Field from `Error`.
		/**
		 * Stack of message.
		 *
		 * This is used by normal errors to show where something happened in
		 * programming code, irrelevant for `VFile` messages,
		 *
		 * @type {string}
		 */
		this.stack = legacyCause && options.cause && typeof options.cause.stack === "string" ? options.cause.stack : "";

		// The following fields are “well known”.
		// Not standard.
		// Feel free to add other non-standard fields to your messages.

		/**
		 * Specify the source value that’s being reported, which is deemed
		 * incorrect.
		 *
		 * @type {string | undefined}
		 */
		this.actual = undefined;

		/**
		 * Suggest acceptable values that can be used instead of `actual`.
		 *
		 * @type {Array<string> | undefined}
		 */
		this.expected = undefined;

		/**
		 * Long form description of the message (you should use markdown).
		 *
		 * @type {string | undefined}
		 */
		this.note = undefined;

		/**
		 * Link to docs for the message.
		 *
		 * > 👉 **Note**: this must be an absolute URL that can be passed as `x`
		 * > to `new URL(x)`.
		 *
		 * @type {string | undefined}
		 */
		this.url = undefined;
	}
}

VFileMessage.prototype.file = "";
VFileMessage.prototype.name = "";
VFileMessage.prototype.reason = "";
VFileMessage.prototype.message = "";
VFileMessage.prototype.stack = "";
VFileMessage.prototype.column = undefined;
VFileMessage.prototype.line = undefined;
VFileMessage.prototype.ancestors = undefined;
VFileMessage.prototype.cause = undefined;
VFileMessage.prototype.fatal = undefined;
VFileMessage.prototype.place = undefined;
VFileMessage.prototype.ruleId = undefined;
VFileMessage.prototype.source = undefined;

// A derivative work based on:
// <https://github.com/browserify/path-browserify>.
// Which is licensed:
//
// MIT License
//
// Copyright (c) 2013 James Halliday
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// A derivative work based on:
//
// Parts of that are extracted from Node’s internal `path` module:
// <https://github.com/nodejs/node/blob/master/lib/path.js>.
// Which is licensed:
//
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

const minpath = { basename, dirname, extname, join: join$1, sep: "/" };

/* eslint-disable max-depth, complexity */

/**
 * Get the basename from a path.
 *
 * @param {string} path
 *   File path.
 * @param {string | null | undefined} [extname]
 *   Extension to strip.
 * @returns {string}
 *   Stem or basename.
 */
function basename(path, extname) {
	if (extname !== undefined && typeof extname !== "string") {
		throw new TypeError('"ext" argument must be a string');
	}

	assertPath$1(path);
	let start = 0;
	let end = -1;
	let index = path.length;
	/** @type {boolean | undefined} */
	let seenNonSlash;

	if (extname === undefined || extname.length === 0 || extname.length > path.length) {
		while (index--) {
			if (path.codePointAt(index) === 47 /* `/` */) {
				// If we reached a path separator that was not part of a set of path
				// separators at the end of the string, stop now.
				if (seenNonSlash) {
					start = index + 1;
					break;
				}
			} else if (end < 0) {
				// We saw the first non-path separator, mark this as the end of our
				// path component.
				seenNonSlash = true;
				end = index + 1;
			}
		}

		return end < 0 ? "" : path.slice(start, end);
	}

	if (extname === path) {
		return "";
	}

	let firstNonSlashEnd = -1;
	let extnameIndex = extname.length - 1;

	while (index--) {
		if (path.codePointAt(index) === 47 /* `/` */) {
			// If we reached a path separator that was not part of a set of path
			// separators at the end of the string, stop now.
			if (seenNonSlash) {
				start = index + 1;
				break;
			}
		} else {
			if (firstNonSlashEnd < 0) {
				// We saw the first non-path separator, remember this index in case
				// we need it if the extension ends up not matching.
				seenNonSlash = true;
				firstNonSlashEnd = index + 1;
			}

			if (extnameIndex > -1) {
				// Try to match the explicit extension.
				if (path.codePointAt(index) === extname.codePointAt(extnameIndex--)) {
					if (extnameIndex < 0) {
						// We matched the extension, so mark this as the end of our path
						// component
						end = index;
					}
				} else {
					// Extension does not match, so our result is the entire path
					// component
					extnameIndex = -1;
					end = firstNonSlashEnd;
				}
			}
		}
	}

	if (start === end) {
		end = firstNonSlashEnd;
	} else if (end < 0) {
		end = path.length;
	}

	return path.slice(start, end);
}

/**
 * Get the dirname from a path.
 *
 * @param {string} path
 *   File path.
 * @returns {string}
 *   File path.
 */
function dirname(path) {
	assertPath$1(path);

	if (path.length === 0) {
		return ".";
	}

	let end = -1;
	let index = path.length;
	/** @type {boolean | undefined} */
	let unmatchedSlash;

	// Prefix `--` is important to not run on `0`.
	while (--index) {
		if (path.codePointAt(index) === 47 /* `/` */) {
			if (unmatchedSlash) {
				end = index;
				break;
			}
		} else if (!unmatchedSlash) {
			// We saw the first non-path separator
			unmatchedSlash = true;
		}
	}

	return (
		end < 0 ?
			path.codePointAt(0) === 47 /* `/` */ ?
				"/"
			:	"."
		: end === 1 && path.codePointAt(0) === 47 /* `/` */ ? "//"
		: path.slice(0, end)
	);
}

/**
 * Get an extname from a path.
 *
 * @param {string} path
 *   File path.
 * @returns {string}
 *   Extname.
 */
function extname(path) {
	assertPath$1(path);

	let index = path.length;

	let end = -1;
	let startPart = 0;
	let startDot = -1;
	// Track the state of characters (if any) we see before our first dot and
	// after any path separator we find.
	let preDotState = 0;
	/** @type {boolean | undefined} */
	let unmatchedSlash;

	while (index--) {
		const code = path.codePointAt(index);

		if (code === 47 /* `/` */) {
			// If we reached a path separator that was not part of a set of path
			// separators at the end of the string, stop now.
			if (unmatchedSlash) {
				startPart = index + 1;
				break;
			}

			continue;
		}

		if (end < 0) {
			// We saw the first non-path separator, mark this as the end of our
			// extension.
			unmatchedSlash = true;
			end = index + 1;
		}

		if (code === 46 /* `.` */) {
			// If this is our first dot, mark it as the start of our extension.
			if (startDot < 0) {
				startDot = index;
			} else if (preDotState !== 1) {
				preDotState = 1;
			}
		} else if (startDot > -1) {
			// We saw a non-dot and non-path separator before our dot, so we should
			// have a good chance at having a non-empty extension.
			preDotState = -1;
		}
	}

	if (
		startDot < 0 ||
		end < 0 ||
		// We saw a non-dot character immediately before the dot.
		preDotState === 0 ||
		// The (right-most) trimmed path component is exactly `..`.
		(preDotState === 1 && startDot === end - 1 && startDot === startPart + 1)
	) {
		return "";
	}

	return path.slice(startDot, end);
}

/**
 * Join segments from a path.
 *
 * @param {Array<string>} segments
 *   Path segments.
 * @returns {string}
 *   File path.
 */
function join$1(...segments) {
	let index = -1;
	/** @type {string | undefined} */
	let joined;

	while (++index < segments.length) {
		assertPath$1(segments[index]);

		if (segments[index]) {
			joined = joined === undefined ? segments[index] : joined + "/" + segments[index];
		}
	}

	return joined === undefined ? "." : normalize(joined);
}

/**
 * Normalize a basic file path.
 *
 * @param {string} path
 *   File path.
 * @returns {string}
 *   File path.
 */
// Note: `normalize` is not exposed as `path.normalize`, so some code is
// manually removed from it.
function normalize(path) {
	assertPath$1(path);

	const absolute = path.codePointAt(0) === 47; /* `/` */

	// Normalize the path according to POSIX rules.
	let value = normalizeString(path, !absolute);

	if (value.length === 0 && !absolute) {
		value = ".";
	}

	if (value.length > 0 && path.codePointAt(path.length - 1) === 47 /* / */) {
		value += "/";
	}

	return absolute ? "/" + value : value;
}

/**
 * Resolve `.` and `..` elements in a path with directory names.
 *
 * @param {string} path
 *   File path.
 * @param {boolean} allowAboveRoot
 *   Whether `..` can move above root.
 * @returns {string}
 *   File path.
 */
function normalizeString(path, allowAboveRoot) {
	let result = "";
	let lastSegmentLength = 0;
	let lastSlash = -1;
	let dots = 0;
	let index = -1;
	/** @type {number | undefined} */
	let code;
	/** @type {number} */
	let lastSlashIndex;

	while (++index <= path.length) {
		if (index < path.length) {
			code = path.codePointAt(index);
		} else if (code === 47 /* `/` */) {
			break;
		} else {
			code = 47; /* `/` */
		}

		if (code === 47 /* `/` */) {
			if (lastSlash === index - 1 || dots === 1);
			else if (lastSlash !== index - 1 && dots === 2) {
				if (result.length < 2 || lastSegmentLength !== 2 || result.codePointAt(result.length - 1) !== 46 /* `.` */ || result.codePointAt(result.length - 2) !== 46 /* `.` */) {
					if (result.length > 2) {
						lastSlashIndex = result.lastIndexOf("/");

						if (lastSlashIndex !== result.length - 1) {
							if (lastSlashIndex < 0) {
								result = "";
								lastSegmentLength = 0;
							} else {
								result = result.slice(0, lastSlashIndex);
								lastSegmentLength = result.length - 1 - result.lastIndexOf("/");
							}

							lastSlash = index;
							dots = 0;
							continue;
						}
					} else if (result.length > 0) {
						result = "";
						lastSegmentLength = 0;
						lastSlash = index;
						dots = 0;
						continue;
					}
				}

				if (allowAboveRoot) {
					result = result.length > 0 ? result + "/.." : "..";
					lastSegmentLength = 2;
				}
			} else {
				if (result.length > 0) {
					result += "/" + path.slice(lastSlash + 1, index);
				} else {
					result = path.slice(lastSlash + 1, index);
				}

				lastSegmentLength = index - lastSlash - 1;
			}

			lastSlash = index;
			dots = 0;
		} else if (code === 46 /* `.` */ && dots > -1) {
			dots++;
		} else {
			dots = -1;
		}
	}

	return result;
}

/**
 * Make sure `path` is a string.
 *
 * @param {string} path
 *   File path.
 * @returns {asserts path is string}
 *   Nothing.
 */
function assertPath$1(path) {
	if (typeof path !== "string") {
		throw new TypeError("Path must be a string. Received " + JSON.stringify(path));
	}
}

/* eslint-enable max-depth, complexity */

// Somewhat based on:
// <https://github.com/defunctzombie/node-process/blob/master/browser.js>.
// But I don’t think one tiny line of code can be copyrighted. 😅
const minproc = { cwd };

function cwd() {
	return "/";
}

/**
 * Checks if a value has the shape of a WHATWG URL object.
 *
 * Using a symbol or instanceof would not be able to recognize URL objects
 * coming from other implementations (e.g. in Electron), so instead we are
 * checking some well known properties for a lack of a better test.
 *
 * We use `href` and `protocol` as they are the only properties that are
 * easy to retrieve and calculate due to the lazy nature of the getters.
 *
 * We check for auth attribute to distinguish legacy url instance with
 * WHATWG URL instance.
 *
 * @param {unknown} fileUrlOrPath
 *   File path or URL.
 * @returns {fileUrlOrPath is URL}
 *   Whether it’s a URL.
 */
// From: <https://github.com/nodejs/node/blob/6a3403c/lib/internal/url.js#L720>
function isUrl(fileUrlOrPath) {
	return Boolean(
		fileUrlOrPath !== null &&
		typeof fileUrlOrPath === "object" &&
		"href" in fileUrlOrPath &&
		fileUrlOrPath.href &&
		"protocol" in fileUrlOrPath &&
		fileUrlOrPath.protocol &&
		// @ts-expect-error: indexing is fine.
		fileUrlOrPath.auth === undefined,
	);
}

// See: <https://github.com/nodejs/node/blob/6a3403c/lib/internal/url.js>

/**
 * @param {URL | string} path
 *   File URL.
 * @returns {string}
 *   File URL.
 */
function urlToPath(path) {
	if (typeof path === "string") {
		path = new URL(path);
	} else if (!isUrl(path)) {
		/** @type {NodeJS.ErrnoException} */
		const error = new TypeError('The "path" argument must be of type string or an instance of URL. Received `' + path + "`");
		error.code = "ERR_INVALID_ARG_TYPE";
		throw error;
	}

	if (path.protocol !== "file:") {
		/** @type {NodeJS.ErrnoException} */
		const error = new TypeError("The URL must be of scheme file");
		error.code = "ERR_INVALID_URL_SCHEME";
		throw error;
	}

	return getPathFromURLPosix(path);
}

/**
 * Get a path from a POSIX URL.
 *
 * @param {URL} url
 *   URL.
 * @returns {string}
 *   File path.
 */
function getPathFromURLPosix(url) {
	if (url.hostname !== "") {
		/** @type {NodeJS.ErrnoException} */
		const error = new TypeError('File URL host must be "localhost" or empty on darwin');
		error.code = "ERR_INVALID_FILE_URL_HOST";
		throw error;
	}

	const pathname = url.pathname;
	let index = -1;

	while (++index < pathname.length) {
		if (pathname.codePointAt(index) === 37 /* `%` */ && pathname.codePointAt(index + 1) === 50 /* `2` */) {
			const third = pathname.codePointAt(index + 2);
			if (third === 70 /* `F` */ || third === 102 /* `f` */) {
				/** @type {NodeJS.ErrnoException} */
				const error = new TypeError("File URL path must not include encoded / characters");
				error.code = "ERR_INVALID_FILE_URL_PATH";
				throw error;
			}
		}
	}

	return decodeURIComponent(pathname);
}

/**
 * @import {Node, Point, Position} from 'unist'
 * @import {Options as MessageOptions} from 'vfile-message'
 * @import {Compatible, Data, Map, Options, Value} from 'vfile'
 */

/**
 * Order of setting (least specific to most), we need this because otherwise
 * `{stem: 'a', path: '~/b.js'}` would throw, as a path is needed before a
 * stem can be set.
 */
const order = /** @type {const} */ (["history", "path", "basename", "stem", "extname", "dirname"]);

class VFile {
	/**
	 * Create a new virtual file.
	 *
	 * `options` is treated as:
	 *
	 * *   `string` or `Uint8Array` — `{value: options}`
	 * *   `URL` — `{path: options}`
	 * *   `VFile` — shallow copies its data over to the new file
	 * *   `object` — all fields are shallow copied over to the new file
	 *
	 * Path related fields are set in the following order (least specific to
	 * most specific): `history`, `path`, `basename`, `stem`, `extname`,
	 * `dirname`.
	 *
	 * You cannot set `dirname` or `extname` without setting either `history`,
	 * `path`, `basename`, or `stem` too.
	 *
	 * @param {Compatible | null | undefined} [value]
	 *   File value.
	 * @returns
	 *   New instance.
	 */
	constructor(value) {
		/** @type {Options | VFile} */
		let options;

		if (!value) {
			options = {};
		} else if (isUrl(value)) {
			options = { path: value };
		} else if (typeof value === "string" || isUint8Array$1(value)) {
			options = { value };
		} else {
			options = value;
		}

		/* eslint-disable no-unused-expressions */

		/**
		 * Base of `path` (default: `process.cwd()` or `'/'` in browsers).
		 *
		 * @type {string}
		 */
		// Prevent calling `cwd` (which could be expensive) if it’s not needed;
		// the empty string will be overridden in the next block.
		this.cwd = "cwd" in options ? "" : minproc.cwd();

		/**
		 * Place to store custom info (default: `{}`).
		 *
		 * It’s OK to store custom data directly on the file but moving it to
		 * `data` is recommended.
		 *
		 * @type {Data}
		 */
		this.data = {};

		/**
		 * List of file paths the file moved between.
		 *
		 * The first is the original path and the last is the current path.
		 *
		 * @type {Array<string>}
		 */
		this.history = [];

		/**
		 * List of messages associated with the file.
		 *
		 * @type {Array<VFileMessage>}
		 */
		this.messages = [];

		/**
		 * Raw value.
		 *
		 * @type {Value}
		 */
		this.value;

		// The below are non-standard, they are “well-known”.
		// As in, used in several tools.
		/**
		 * Source map.
		 *
		 * This type is equivalent to the `RawSourceMap` type from the `source-map`
		 * module.
		 *
		 * @type {Map | null | undefined}
		 */
		this.map;

		/**
		 * Custom, non-string, compiled, representation.
		 *
		 * This is used by unified to store non-string results.
		 * One example is when turning markdown into React nodes.
		 *
		 * @type {unknown}
		 */
		this.result;

		/**
		 * Whether a file was saved to disk.
		 *
		 * This is used by vfile reporters.
		 *
		 * @type {boolean}
		 */
		this.stored;
		/* eslint-enable no-unused-expressions */

		// Set path related properties in the correct order.
		let index = -1;

		while (++index < order.length) {
			const field = order[index];

			// Note: we specifically use `in` instead of `hasOwnProperty` to accept
			// `vfile`s too.
			if (field in options && options[field] !== undefined && options[field] !== null) {
				// @ts-expect-error: TS doesn’t understand basic reality.
				this[field] = field === "history" ? [...options[field]] : options[field];
			}
		}

		/** @type {string} */
		let field;

		// Set non-path related properties.
		for (field in options) {
			// @ts-expect-error: fine to set other things.
			if (!order.includes(field)) {
				// @ts-expect-error: fine to set other things.
				this[field] = options[field];
			}
		}
	}

	/**
	 * Get the basename (including extname) (example: `'index.min.js'`).
	 *
	 * @returns {string | undefined}
	 *   Basename.
	 */
	get basename() {
		return typeof this.path === "string" ? minpath.basename(this.path) : undefined;
	}

	/**
	 * Set basename (including extname) (`'index.min.js'`).
	 *
	 * Cannot contain path separators (`'/'` on unix, macOS, and browsers, `'\'`
	 * on windows).
	 * Cannot be nullified (use `file.path = file.dirname` instead).
	 *
	 * @param {string} basename
	 *   Basename.
	 * @returns {undefined}
	 *   Nothing.
	 */
	set basename(basename) {
		assertNonEmpty(basename, "basename");
		assertPart(basename, "basename");
		this.path = minpath.join(this.dirname || "", basename);
	}

	/**
	 * Get the parent path (example: `'~'`).
	 *
	 * @returns {string | undefined}
	 *   Dirname.
	 */
	get dirname() {
		return typeof this.path === "string" ? minpath.dirname(this.path) : undefined;
	}

	/**
	 * Set the parent path (example: `'~'`).
	 *
	 * Cannot be set if there’s no `path` yet.
	 *
	 * @param {string | undefined} dirname
	 *   Dirname.
	 * @returns {undefined}
	 *   Nothing.
	 */
	set dirname(dirname) {
		assertPath(this.basename, "dirname");
		this.path = minpath.join(dirname || "", this.basename);
	}

	/**
	 * Get the extname (including dot) (example: `'.js'`).
	 *
	 * @returns {string | undefined}
	 *   Extname.
	 */
	get extname() {
		return typeof this.path === "string" ? minpath.extname(this.path) : undefined;
	}

	/**
	 * Set the extname (including dot) (example: `'.js'`).
	 *
	 * Cannot contain path separators (`'/'` on unix, macOS, and browsers, `'\'`
	 * on windows).
	 * Cannot be set if there’s no `path` yet.
	 *
	 * @param {string | undefined} extname
	 *   Extname.
	 * @returns {undefined}
	 *   Nothing.
	 */
	set extname(extname) {
		assertPart(extname, "extname");
		assertPath(this.dirname, "extname");

		if (extname) {
			if (extname.codePointAt(0) !== 46 /* `.` */) {
				throw new Error("`extname` must start with `.`");
			}

			if (extname.includes(".", 1)) {
				throw new Error("`extname` cannot contain multiple dots");
			}
		}

		this.path = minpath.join(this.dirname, this.stem + (extname || ""));
	}

	/**
	 * Get the full path (example: `'~/index.min.js'`).
	 *
	 * @returns {string}
	 *   Path.
	 */
	get path() {
		return this.history[this.history.length - 1];
	}

	/**
	 * Set the full path (example: `'~/index.min.js'`).
	 *
	 * Cannot be nullified.
	 * You can set a file URL (a `URL` object with a `file:` protocol) which will
	 * be turned into a path with `url.fileURLToPath`.
	 *
	 * @param {URL | string} path
	 *   Path.
	 * @returns {undefined}
	 *   Nothing.
	 */
	set path(path) {
		if (isUrl(path)) {
			path = urlToPath(path);
		}

		assertNonEmpty(path, "path");

		if (this.path !== path) {
			this.history.push(path);
		}
	}

	/**
	 * Get the stem (basename w/o extname) (example: `'index.min'`).
	 *
	 * @returns {string | undefined}
	 *   Stem.
	 */
	get stem() {
		return typeof this.path === "string" ? minpath.basename(this.path, this.extname) : undefined;
	}

	/**
	 * Set the stem (basename w/o extname) (example: `'index.min'`).
	 *
	 * Cannot contain path separators (`'/'` on unix, macOS, and browsers, `'\'`
	 * on windows).
	 * Cannot be nullified (use `file.path = file.dirname` instead).
	 *
	 * @param {string} stem
	 *   Stem.
	 * @returns {undefined}
	 *   Nothing.
	 */
	set stem(stem) {
		assertNonEmpty(stem, "stem");
		assertPart(stem, "stem");
		this.path = minpath.join(this.dirname || "", stem + (this.extname || ""));
	}

	// Normal prototypal methods.
	/**
	 * Create a fatal message for `reason` associated with the file.
	 *
	 * The `fatal` field of the message is set to `true` (error; file not usable)
	 * and the `file` field is set to the current file path.
	 * The message is added to the `messages` field on `file`.
	 *
	 * > 🪦 **Note**: also has obsolete signatures.
	 *
	 * @overload
	 * @param {string} reason
	 * @param {MessageOptions | null | undefined} [options]
	 * @returns {never}
	 *
	 * @overload
	 * @param {string} reason
	 * @param {Node | NodeLike | null | undefined} parent
	 * @param {string | null | undefined} [origin]
	 * @returns {never}
	 *
	 * @overload
	 * @param {string} reason
	 * @param {Point | Position | null | undefined} place
	 * @param {string | null | undefined} [origin]
	 * @returns {never}
	 *
	 * @overload
	 * @param {string} reason
	 * @param {string | null | undefined} [origin]
	 * @returns {never}
	 *
	 * @overload
	 * @param {Error | VFileMessage} cause
	 * @param {Node | NodeLike | null | undefined} parent
	 * @param {string | null | undefined} [origin]
	 * @returns {never}
	 *
	 * @overload
	 * @param {Error | VFileMessage} cause
	 * @param {Point | Position | null | undefined} place
	 * @param {string | null | undefined} [origin]
	 * @returns {never}
	 *
	 * @overload
	 * @param {Error | VFileMessage} cause
	 * @param {string | null | undefined} [origin]
	 * @returns {never}
	 *
	 * @param {Error | VFileMessage | string} causeOrReason
	 *   Reason for message, should use markdown.
	 * @param {Node | NodeLike | MessageOptions | Point | Position | string | null | undefined} [optionsOrParentOrPlace]
	 *   Configuration (optional).
	 * @param {string | null | undefined} [origin]
	 *   Place in code where the message originates (example:
	 *   `'my-package:my-rule'` or `'my-rule'`).
	 * @returns {never}
	 *   Never.
	 * @throws {VFileMessage}
	 *   Message.
	 */
	fail(causeOrReason, optionsOrParentOrPlace, origin) {
		// @ts-expect-error: the overloads are fine.
		const message = this.message(causeOrReason, optionsOrParentOrPlace, origin);

		message.fatal = true;

		throw message;
	}

	/**
	 * Create an info message for `reason` associated with the file.
	 *
	 * The `fatal` field of the message is set to `undefined` (info; change
	 * likely not needed) and the `file` field is set to the current file path.
	 * The message is added to the `messages` field on `file`.
	 *
	 * > 🪦 **Note**: also has obsolete signatures.
	 *
	 * @overload
	 * @param {string} reason
	 * @param {MessageOptions | null | undefined} [options]
	 * @returns {VFileMessage}
	 *
	 * @overload
	 * @param {string} reason
	 * @param {Node | NodeLike | null | undefined} parent
	 * @param {string | null | undefined} [origin]
	 * @returns {VFileMessage}
	 *
	 * @overload
	 * @param {string} reason
	 * @param {Point | Position | null | undefined} place
	 * @param {string | null | undefined} [origin]
	 * @returns {VFileMessage}
	 *
	 * @overload
	 * @param {string} reason
	 * @param {string | null | undefined} [origin]
	 * @returns {VFileMessage}
	 *
	 * @overload
	 * @param {Error | VFileMessage} cause
	 * @param {Node | NodeLike | null | undefined} parent
	 * @param {string | null | undefined} [origin]
	 * @returns {VFileMessage}
	 *
	 * @overload
	 * @param {Error | VFileMessage} cause
	 * @param {Point | Position | null | undefined} place
	 * @param {string | null | undefined} [origin]
	 * @returns {VFileMessage}
	 *
	 * @overload
	 * @param {Error | VFileMessage} cause
	 * @param {string | null | undefined} [origin]
	 * @returns {VFileMessage}
	 *
	 * @param {Error | VFileMessage | string} causeOrReason
	 *   Reason for message, should use markdown.
	 * @param {Node | NodeLike | MessageOptions | Point | Position | string | null | undefined} [optionsOrParentOrPlace]
	 *   Configuration (optional).
	 * @param {string | null | undefined} [origin]
	 *   Place in code where the message originates (example:
	 *   `'my-package:my-rule'` or `'my-rule'`).
	 * @returns {VFileMessage}
	 *   Message.
	 */
	info(causeOrReason, optionsOrParentOrPlace, origin) {
		// @ts-expect-error: the overloads are fine.
		const message = this.message(causeOrReason, optionsOrParentOrPlace, origin);

		message.fatal = undefined;

		return message;
	}

	/**
	 * Create a message for `reason` associated with the file.
	 *
	 * The `fatal` field of the message is set to `false` (warning; change may be
	 * needed) and the `file` field is set to the current file path.
	 * The message is added to the `messages` field on `file`.
	 *
	 * > 🪦 **Note**: also has obsolete signatures.
	 *
	 * @overload
	 * @param {string} reason
	 * @param {MessageOptions | null | undefined} [options]
	 * @returns {VFileMessage}
	 *
	 * @overload
	 * @param {string} reason
	 * @param {Node | NodeLike | null | undefined} parent
	 * @param {string | null | undefined} [origin]
	 * @returns {VFileMessage}
	 *
	 * @overload
	 * @param {string} reason
	 * @param {Point | Position | null | undefined} place
	 * @param {string | null | undefined} [origin]
	 * @returns {VFileMessage}
	 *
	 * @overload
	 * @param {string} reason
	 * @param {string | null | undefined} [origin]
	 * @returns {VFileMessage}
	 *
	 * @overload
	 * @param {Error | VFileMessage} cause
	 * @param {Node | NodeLike | null | undefined} parent
	 * @param {string | null | undefined} [origin]
	 * @returns {VFileMessage}
	 *
	 * @overload
	 * @param {Error | VFileMessage} cause
	 * @param {Point | Position | null | undefined} place
	 * @param {string | null | undefined} [origin]
	 * @returns {VFileMessage}
	 *
	 * @overload
	 * @param {Error | VFileMessage} cause
	 * @param {string | null | undefined} [origin]
	 * @returns {VFileMessage}
	 *
	 * @param {Error | VFileMessage | string} causeOrReason
	 *   Reason for message, should use markdown.
	 * @param {Node | NodeLike | MessageOptions | Point | Position | string | null | undefined} [optionsOrParentOrPlace]
	 *   Configuration (optional).
	 * @param {string | null | undefined} [origin]
	 *   Place in code where the message originates (example:
	 *   `'my-package:my-rule'` or `'my-rule'`).
	 * @returns {VFileMessage}
	 *   Message.
	 */
	message(causeOrReason, optionsOrParentOrPlace, origin) {
		const message = new VFileMessage(
			// @ts-expect-error: the overloads are fine.
			causeOrReason,
			optionsOrParentOrPlace,
			origin,
		);

		if (this.path) {
			message.name = this.path + ":" + message.name;
			message.file = this.path;
		}

		message.fatal = false;

		this.messages.push(message);

		return message;
	}

	/**
	 * Serialize the file.
	 *
	 * > **Note**: which encodings are supported depends on the engine.
	 * > For info on Node.js, see:
	 * > <https://nodejs.org/api/util.html#whatwg-supported-encodings>.
	 *
	 * @param {string | null | undefined} [encoding='utf8']
	 *   Character encoding to understand `value` as when it’s a `Uint8Array`
	 *   (default: `'utf-8'`).
	 * @returns {string}
	 *   Serialized file.
	 */
	toString(encoding) {
		if (this.value === undefined) {
			return "";
		}

		if (typeof this.value === "string") {
			return this.value;
		}

		const decoder = new TextDecoder(encoding || undefined);
		return decoder.decode(this.value);
	}
}

/**
 * Assert that `part` is not a path (as in, does not contain `path.sep`).
 *
 * @param {string | null | undefined} part
 *   File path part.
 * @param {string} name
 *   Part name.
 * @returns {undefined}
 *   Nothing.
 */
function assertPart(part, name) {
	if (part && part.includes(minpath.sep)) {
		throw new Error("`" + name + "` cannot be a path: did not expect `" + minpath.sep + "`");
	}
}

/**
 * Assert that `part` is not empty.
 *
 * @param {string | undefined} part
 *   Thing.
 * @param {string} name
 *   Part name.
 * @returns {asserts part is string}
 *   Nothing.
 */
function assertNonEmpty(part, name) {
	if (!part) {
		throw new Error("`" + name + "` cannot be empty");
	}
}

/**
 * Assert `path` exists.
 *
 * @param {string | undefined} path
 *   Path.
 * @param {string} name
 *   Dependency name.
 * @returns {asserts path is string}
 *   Nothing.
 */
function assertPath(path, name) {
	if (!path) {
		throw new Error("Setting `" + name + "` requires `path` to be set too");
	}
}

/**
 * Assert `value` is an `Uint8Array`.
 *
 * @param {unknown} value
 *   thing.
 * @returns {value is Uint8Array}
 *   Whether `value` is an `Uint8Array`.
 */
function isUint8Array$1(value) {
	return Boolean(value && typeof value === "object" && "byteLength" in value && "byteOffset" in value);
}

/**
 * @typedef ErrorInfo
 *   Info on a `parse5` error.
 * @property {string} reason
 *   Reason of error.
 * @property {string} description
 *   More info on error.
 * @property {false} [url]
 *   Turn off if this is not documented in the html5 spec (optional).
 */

const errors = {
	/** @type {ErrorInfo} */
	abandonedHeadElementChild: {
		reason: "Unexpected metadata element after head",
		description: "Unexpected element after head. Expected the element before `</head>`",
		url: false,
	},
	/** @type {ErrorInfo} */
	abruptClosingOfEmptyComment: {
		reason: "Unexpected abruptly closed empty comment",
		description: "Unexpected `>` or `->`. Expected `-->` to close comments",
	},
	/** @type {ErrorInfo} */
	abruptDoctypePublicIdentifier: {
		reason: "Unexpected abruptly closed public identifier",
		description: "Unexpected `>`. Expected a closing `\"` or `'` after the public identifier",
	},
	/** @type {ErrorInfo} */
	abruptDoctypeSystemIdentifier: {
		reason: "Unexpected abruptly closed system identifier",
		description: "Unexpected `>`. Expected a closing `\"` or `'` after the identifier identifier",
	},
	/** @type {ErrorInfo} */
	absenceOfDigitsInNumericCharacterReference: {
		reason: "Unexpected non-digit at start of numeric character reference",
		description: "Unexpected `%c`. Expected `[0-9]` for decimal references or `[0-9a-fA-F]` for hexadecimal references",
	},
	/** @type {ErrorInfo} */
	cdataInHtmlContent: {
		reason: "Unexpected CDATA section in HTML",
		description: "Unexpected `<![CDATA[` in HTML. Remove it, use a comment, or encode special characters instead",
	},
	/** @type {ErrorInfo} */
	characterReferenceOutsideUnicodeRange: {
		reason: "Unexpected too big numeric character reference",
		description: "Unexpectedly high character reference. Expected character references to be at most hexadecimal 10ffff (or decimal 1114111)",
	},
	/** @type {ErrorInfo} */
	closingOfElementWithOpenChildElements: {
		reason: "Unexpected closing tag with open child elements",
		description: "Unexpectedly closing tag. Expected other tags to be closed first",
		url: false,
	},
	/** @type {ErrorInfo} */
	controlCharacterInInputStream: {
		reason: "Unexpected control character",
		description: "Unexpected control character `%x`. Expected a non-control code point, 0x00, or ASCII whitespace",
	},
	/** @type {ErrorInfo} */
	controlCharacterReference: {
		reason: "Unexpected control character reference",
		description: "Unexpectedly control character in reference. Expected a non-control code point, 0x00, or ASCII whitespace",
	},
	/** @type {ErrorInfo} */
	disallowedContentInNoscriptInHead: {
		reason: "Disallowed content inside `<noscript>` in `<head>`",
		description: "Unexpected text character `%c`. Only use text in `<noscript>`s in `<body>`",
		url: false,
	},
	/** @type {ErrorInfo} */
	duplicateAttribute: {
		reason: "Unexpected duplicate attribute",
		description: "Unexpectedly double attribute. Expected attributes to occur only once",
	},
	/** @type {ErrorInfo} */
	endTagWithAttributes: {
		reason: "Unexpected attribute on closing tag",
		description: "Unexpected attribute. Expected `>` instead",
	},
	/** @type {ErrorInfo} */
	endTagWithTrailingSolidus: {
		reason: "Unexpected slash at end of closing tag",
		description: "Unexpected `%c-1`. Expected `>` instead",
	},
	/** @type {ErrorInfo} */
	endTagWithoutMatchingOpenElement: {
		reason: "Unexpected unopened end tag",
		description: "Unexpected end tag. Expected no end tag or another end tag",
		url: false,
	},
	/** @type {ErrorInfo} */
	eofBeforeTagName: {
		reason: "Unexpected end of file",
		description: "Unexpected end of file. Expected tag name instead",
	},
	/** @type {ErrorInfo} */
	eofInCdata: {
		reason: "Unexpected end of file in CDATA",
		description: "Unexpected end of file. Expected `]]>` to close the CDATA",
	},
	/** @type {ErrorInfo} */
	eofInComment: {
		reason: "Unexpected end of file in comment",
		description: "Unexpected end of file. Expected `-->` to close the comment",
	},
	/** @type {ErrorInfo} */
	eofInDoctype: {
		reason: "Unexpected end of file in doctype",
		description: "Unexpected end of file. Expected a valid doctype (such as `<!doctype html>`)",
	},
	/** @type {ErrorInfo} */
	eofInElementThatCanContainOnlyText: {
		reason: "Unexpected end of file in element that can only contain text",
		description: "Unexpected end of file. Expected text or a closing tag",
		url: false,
	},
	/** @type {ErrorInfo} */
	eofInScriptHtmlCommentLikeText: {
		reason: "Unexpected end of file in comment inside script",
		description: "Unexpected end of file. Expected `-->` to close the comment",
	},
	/** @type {ErrorInfo} */
	eofInTag: {
		reason: "Unexpected end of file in tag",
		description: "Unexpected end of file. Expected `>` to close the tag",
	},
	/** @type {ErrorInfo} */
	incorrectlyClosedComment: {
		reason: "Incorrectly closed comment",
		description: "Unexpected `%c-1`. Expected `-->` to close the comment",
	},
	/** @type {ErrorInfo} */
	incorrectlyOpenedComment: {
		reason: "Incorrectly opened comment",
		description: "Unexpected `%c`. Expected `<!--` to open the comment",
	},
	/** @type {ErrorInfo} */
	invalidCharacterSequenceAfterDoctypeName: {
		reason: "Invalid sequence after doctype name",
		description: "Unexpected sequence at `%c`. Expected `public` or `system`",
	},
	/** @type {ErrorInfo} */
	invalidFirstCharacterOfTagName: {
		reason: "Invalid first character in tag name",
		description: "Unexpected `%c`. Expected an ASCII letter instead",
	},
	/** @type {ErrorInfo} */
	misplacedDoctype: {
		reason: "Misplaced doctype",
		description: "Unexpected doctype. Expected doctype before head",
		url: false,
	},
	/** @type {ErrorInfo} */
	misplacedStartTagForHeadElement: {
		reason: "Misplaced `<head>` start tag",
		description: "Unexpected start tag `<head>`. Expected `<head>` directly after doctype",
		url: false,
	},
	/** @type {ErrorInfo} */
	missingAttributeValue: {
		reason: "Missing attribute value",
		description: "Unexpected `%c-1`. Expected an attribute value or no `%c-1` instead",
	},
	/** @type {ErrorInfo} */
	missingDoctype: {
		reason: "Missing doctype before other content",
		description: "Expected a `<!doctype html>` before anything else",
		url: false,
	},
	/** @type {ErrorInfo} */
	missingDoctypeName: {
		reason: "Missing doctype name",
		description: "Unexpected doctype end at `%c`. Expected `html` instead",
	},
	/** @type {ErrorInfo} */
	missingDoctypePublicIdentifier: {
		reason: "Missing public identifier in doctype",
		description: "Unexpected `%c`. Expected identifier for `public` instead",
	},
	/** @type {ErrorInfo} */
	missingDoctypeSystemIdentifier: {
		reason: "Missing system identifier in doctype",
		description: 'Unexpected `%c`. Expected identifier for `system` instead (suggested: `"about:legacy-compat"`)',
	},
	/** @type {ErrorInfo} */
	missingEndTagName: {
		reason: "Missing name in end tag",
		description: "Unexpected `%c`. Expected an ASCII letter instead",
	},
	/** @type {ErrorInfo} */
	missingQuoteBeforeDoctypePublicIdentifier: {
		reason: "Missing quote before public identifier in doctype",
		description: "Unexpected `%c`. Expected `\"` or `'` instead",
	},
	/** @type {ErrorInfo} */
	missingQuoteBeforeDoctypeSystemIdentifier: {
		reason: "Missing quote before system identifier in doctype",
		description: "Unexpected `%c`. Expected `\"` or `'` instead",
	},
	/** @type {ErrorInfo} */
	missingSemicolonAfterCharacterReference: {
		reason: "Missing semicolon after character reference",
		description: "Unexpected `%c`. Expected `;` instead",
	},
	/** @type {ErrorInfo} */
	missingWhitespaceAfterDoctypePublicKeyword: {
		reason: "Missing whitespace after public identifier in doctype",
		description: "Unexpected `%c`. Expected ASCII whitespace instead",
	},
	/** @type {ErrorInfo} */
	missingWhitespaceAfterDoctypeSystemKeyword: {
		reason: "Missing whitespace after system identifier in doctype",
		description: "Unexpected `%c`. Expected ASCII whitespace instead",
	},
	/** @type {ErrorInfo} */
	missingWhitespaceBeforeDoctypeName: {
		reason: "Missing whitespace before doctype name",
		description: "Unexpected `%c`. Expected ASCII whitespace instead",
	},
	/** @type {ErrorInfo} */
	missingWhitespaceBetweenAttributes: {
		reason: "Missing whitespace between attributes",
		description: "Unexpected `%c`. Expected ASCII whitespace instead",
	},
	/** @type {ErrorInfo} */
	missingWhitespaceBetweenDoctypePublicAndSystemIdentifiers: {
		reason: "Missing whitespace between public and system identifiers in doctype",
		description: "Unexpected `%c`. Expected ASCII whitespace instead",
	},
	/** @type {ErrorInfo} */
	nestedComment: {
		reason: "Unexpected nested comment",
		description: "Unexpected `<!--`. Expected `-->`",
	},
	/** @type {ErrorInfo} */
	nestedNoscriptInHead: {
		reason: "Unexpected nested `<noscript>` in `<head>`",
		description: "Unexpected `<noscript>`. Expected a closing tag or a meta element",
		url: false,
	},
	/** @type {ErrorInfo} */
	nonConformingDoctype: {
		reason: "Unexpected non-conforming doctype declaration",
		description: 'Expected `<!doctype html>` or `<!doctype html system "about:legacy-compat">`',
		url: false,
	},
	/** @type {ErrorInfo} */
	nonVoidHtmlElementStartTagWithTrailingSolidus: {
		reason: "Unexpected trailing slash on start tag of non-void element",
		description: "Unexpected `/`. Expected `>` instead",
	},
	/** @type {ErrorInfo} */
	noncharacterCharacterReference: {
		reason: "Unexpected noncharacter code point referenced by character reference",
		description: "Unexpected code point. Do not use noncharacters in HTML",
	},
	/** @type {ErrorInfo} */
	noncharacterInInputStream: {
		reason: "Unexpected noncharacter character",
		description: "Unexpected code point `%x`. Do not use noncharacters in HTML",
	},
	/** @type {ErrorInfo} */
	nullCharacterReference: {
		reason: "Unexpected NULL character referenced by character reference",
		description: "Unexpected code point. Do not use NULL characters in HTML",
	},
	/** @type {ErrorInfo} */
	openElementsLeftAfterEof: {
		reason: "Unexpected end of file",
		description: "Unexpected end of file. Expected closing tag instead",
		url: false,
	},
	/** @type {ErrorInfo} */
	surrogateCharacterReference: {
		reason: "Unexpected surrogate character referenced by character reference",
		description: "Unexpected code point. Do not use lone surrogate characters in HTML",
	},
	/** @type {ErrorInfo} */
	surrogateInInputStream: {
		reason: "Unexpected surrogate character",
		description: "Unexpected code point `%x`. Do not use lone surrogate characters in HTML",
	},
	/** @type {ErrorInfo} */
	unexpectedCharacterAfterDoctypeSystemIdentifier: {
		reason: "Invalid character after system identifier in doctype",
		description: "Unexpected character at `%c`. Expected `>`",
	},
	/** @type {ErrorInfo} */
	unexpectedCharacterInAttributeName: {
		reason: "Unexpected character in attribute name",
		description: "Unexpected `%c`. Expected whitespace, `/`, `>`, `=`, or probably an ASCII letter",
	},
	/** @type {ErrorInfo} */
	unexpectedCharacterInUnquotedAttributeValue: {
		reason: "Unexpected character in unquoted attribute value",
		description: "Unexpected `%c`. Quote the attribute value to include it",
	},
	/** @type {ErrorInfo} */
	unexpectedEqualsSignBeforeAttributeName: {
		reason: "Unexpected equals sign before attribute name",
		description: "Unexpected `%c`. Add an attribute name before it",
	},
	/** @type {ErrorInfo} */
	unexpectedNullCharacter: {
		reason: "Unexpected NULL character",
		description: "Unexpected code point `%x`. Do not use NULL characters in HTML",
	},
	/** @type {ErrorInfo} */
	unexpectedQuestionMarkInsteadOfTagName: {
		reason: "Unexpected question mark instead of tag name",
		description: "Unexpected `%c`. Expected an ASCII letter instead",
	},
	/** @type {ErrorInfo} */
	unexpectedSolidusInTag: {
		reason: "Unexpected slash in tag",
		description: "Unexpected `%c-1`. Expected it followed by `>` or in a quoted attribute value",
	},
	/** @type {ErrorInfo} */
	unknownNamedCharacterReference: {
		reason: "Unexpected unknown named character reference",
		description: "Unexpected character reference. Expected known named character references",
	},
};

/**
 * @import {Root} from 'hast'
 * @import {ParserError} from 'parse5'
 * @import {Value} from 'vfile'
 * @import {ErrorCode, Options} from './types.js'
 */

const base$1 = "https://html.spec.whatwg.org/multipage/parsing.html#parse-error-";

const dashToCamelRe = /-[a-z]/g;
const formatCRe = /%c(?:([-+])(\d+))?/g;
const formatXRe = /%x/g;

const fatalities = { 2: true, 1: false, 0: null };

/** @type {Readonly<Options>} */
const emptyOptions$6 = {};

/**
 * Turn serialized HTML into a hast tree.
 *
 * @param {VFile | Value} value
 *   Serialized HTML to parse.
 * @param {Readonly<Options> | null | undefined} [options]
 *   Configuration (optional).
 * @returns {Root}
 *   Tree.
 */
function fromHtml(value, options) {
	const settings = options || emptyOptions$6;
	const onerror = settings.onerror;
	const file = value instanceof VFile ? value : new VFile(value);
	const parseFunction = settings.fragment ? parseFragment : parse$2;
	const document = String(file);
	const p5Document = parseFunction(document, {
		sourceCodeLocationInfo: true,
		// Note `parse5` types currently do not allow `undefined`.
		onParseError: settings.onerror ? internalOnerror : null,
		scriptingEnabled: false,
	});

	// `parse5` returns document which are always mapped to roots.
	return /** @type {Root} */ (
		fromParse5(p5Document, {
			file,
			space: settings.space,
			verbose: settings.verbose,
		})
	);

	/**
	 * Handle a parse error.
	 *
	 * @param {ParserError} error
	 *   Parse5 error.
	 * @returns {undefined}
	 *   Nothing.
	 */
	function internalOnerror(error) {
		const code = error.code;
		const name = camelcase(code);
		const setting = settings[name];
		const config = setting === null || setting === undefined ? true : setting;
		const level =
			typeof config === "number" ? config
			: config ? 1
			: 0;

		if (level) {
			const info = errors[name];

			const message = new VFileMessage(format(info.reason), {
				place: {
					start: {
						line: error.startLine,
						column: error.startCol,
						offset: error.startOffset,
					},
					end: {
						line: error.endLine,
						column: error.endCol,
						offset: error.endOffset,
					},
				},
				ruleId: code,
				source: "hast-util-from-html",
			});

			if (file.path) {
				message.file = file.path;
				message.name = file.path + ":" + message.name;
			}

			message.fatal = fatalities[level];
			message.note = format(info.description);
			message.url = info.url === false ? undefined : base$1 + code;
			onerror(message);
		}

		/**
		 * Format a human readable string about an error.
		 *
		 * @param {string} value
		 *   Value to format.
		 * @returns {string}
		 *   Formatted.
		 */
		function format(value) {
			return value.replace(formatCRe, formatC).replace(formatXRe, formatX);

			/**
			 * Format the character.
			 *
			 * @param {string} _
			 *   Match.
			 * @param {string} $1
			 *   Sign (`-` or `+`, optional).
			 * @param {string} $2
			 *   Offset.
			 * @returns {string}
			 *   Formatted.
			 */
			function formatC(_, $1, $2) {
				const offset = ($2 ? Number.parseInt($2, 10) : 0) * ($1 === "-" ? -1 : 1);
				const char = document.charAt(error.startOffset + offset);
				return visualizeCharacter(char);
			}

			/**
			 * Format the character code.
			 *
			 * @returns {string}
			 *   Formatted.
			 */
			function formatX() {
				return visualizeCharacterCode(document.charCodeAt(error.startOffset));
			}
		}
	}
}

/**
 * @param {string} value
 *   Error code in dash case.
 * @returns {ErrorCode}
 *   Error code in camelcase.
 */
function camelcase(value) {
	// This should match an error code.
	return /** @type {ErrorCode} */ (value.replace(dashToCamelRe, dashToCamel));
}

/**
 * @param {string} $0
 *   Match.
 * @returns {string}
 *   Camelcased.
 */
function dashToCamel($0) {
	return $0.charAt(1).toUpperCase();
}

/**
 * @param {string} char
 *   Character.
 * @returns {string}
 *   Formatted.
 */
function visualizeCharacter(char) {
	return char === "`" ? "` ` `" : char;
}

/**
 * @param {number} charCode
 *   Character code.
 * @returns {string}
 *   Formatted.
 */
function visualizeCharacterCode(charCode) {
	return "0x" + charCode.toString(16).toUpperCase();
}

/**
 * @import {Root} from 'hast'
 * @import {Options as FromHtmlOptions} from 'hast-util-from-html'
 * @import {Parser, Processor} from 'unified'
 */

/**
 * Plugin to add support for parsing from HTML.
 *
 * > 👉 **Note**: this is not an XML parser.
 * > It supports SVG as embedded in HTML.
 * > It does not support the features available in XML.
 * > Passing SVG files might break but fragments of modern SVG should be fine.
 * > Use [`xast-util-from-xml`][xast-util-from-xml] to parse XML.
 *
 * @param {Options | null | undefined} [options]
 *   Configuration (optional).
 * @returns {undefined}
 *   Nothing.
 */
function rehypeParse(options) {
	/** @type {Processor<Root>} */
	// @ts-expect-error: TS in JSDoc generates wrong types if `this` is typed regularly.
	const self = this;
	const { emitParseErrors, ...settings } = { ...self.data("settings"), ...options };

	self.parser = parser;

	/**
	 * @type {Parser<Root>}
	 */
	function parser(document, file) {
		return fromHtml(document, {
			...settings,
			onerror:
				emitParseErrors ?
					function (message) {
						if (file.path) {
							message.name = file.path + ":" + message.name;
							message.file = file.path;
						}

						file.messages.push(message);
					}
				:	undefined,
		});
	}
}

const VOID = -1;
const PRIMITIVE = 0;
const ARRAY = 1;
const OBJECT = 2;
const DATE = 3;
const REGEXP = 4;
const MAP = 5;
const SET = 6;
const ERROR = 7;
const BIGINT = 8;
// export const SYMBOL = 9;

const env = typeof self === "object" ? self : globalThis;

const guard = (name, init) => {
	switch (name) {
		case "Function":
		case "SharedWorker":
		case "Worker":
		case "eval":
		case "setInterval":
		case "setTimeout":
			throw new TypeError("unable to deserialize " + name);
	}
	return new env[name](init);
};

const deserializer = ($, _) => {
	const as = (out, index) => {
		$.set(index, out);
		return out;
	};

	const unpair = (index) => {
		if ($.has(index)) return $.get(index);

		const [type, value] = _[index];
		switch (type) {
			case PRIMITIVE:
			case VOID:
				return as(value, index);
			case ARRAY: {
				const arr = as([], index);
				for (const index of value) arr.push(unpair(index));
				return arr;
			}
			case OBJECT: {
				const object = as({}, index);
				for (const [key, index] of value) object[unpair(key)] = unpair(index);
				return object;
			}
			case DATE:
				return as(new Date(value), index);
			case REGEXP: {
				const { source, flags } = value;
				return as(new RegExp(source, flags), index);
			}
			case MAP: {
				const map = as(new Map(), index);
				for (const [key, index] of value) map.set(unpair(key), unpair(index));
				return map;
			}
			case SET: {
				const set = as(new Set(), index);
				for (const index of value) set.add(unpair(index));
				return set;
			}
			case ERROR: {
				const { name, message } = value;
				return as(guard(name, message), index);
			}
			case BIGINT:
				return as(BigInt(value), index);
			case "BigInt":
				return as(Object(BigInt(value)), index);
			case "ArrayBuffer":
				return as(new Uint8Array(value).buffer, value);
			case "DataView": {
				const { buffer } = new Uint8Array(value);
				return as(new DataView(buffer), value);
			}
		}
		return as(guard(type, value), index);
	};

	return unpair;
};

/**
 * @typedef {Array<string,any>} Record a type representation
 */

/**
 * Returns a deserialized value from a serialized array of Records.
 * @param {Record[]} serialized a previously serialized value.
 * @returns {any}
 */
const deserialize = (serialized) => deserializer(new Map(), serialized)(0);

const EMPTY = "";

const { toString: toString$1 } = {};
const { keys } = Object;

const typeOf = (value) => {
	const type = typeof value;
	if (type !== "object" || !value) return [PRIMITIVE, type];

	const asString = toString$1.call(value).slice(8, -1);
	switch (asString) {
		case "Array":
			return [ARRAY, EMPTY];
		case "Object":
			return [OBJECT, EMPTY];
		case "Date":
			return [DATE, EMPTY];
		case "RegExp":
			return [REGEXP, EMPTY];
		case "Map":
			return [MAP, EMPTY];
		case "Set":
			return [SET, EMPTY];
		case "DataView":
			return [ARRAY, asString];
	}

	if (asString.includes("Array")) return [ARRAY, asString];

	if (asString.includes("Error")) return [ERROR, asString];

	return [OBJECT, asString];
};

const shouldSkip = ([TYPE, type]) => TYPE === PRIMITIVE && (type === "function" || type === "symbol");

const serializer = (strict, json, $, _) => {
	const as = (out, value) => {
		const index = _.push(out) - 1;
		$.set(value, index);
		return index;
	};

	const pair = (value) => {
		if ($.has(value)) return $.get(value);

		let [TYPE, type] = typeOf(value);
		switch (TYPE) {
			case PRIMITIVE: {
				let entry = value;
				switch (type) {
					case "bigint":
						TYPE = BIGINT;
						entry = value.toString();
						break;
					case "function":
					case "symbol":
						if (strict) throw new TypeError("unable to serialize " + type);
						entry = null;
						break;
					case "undefined":
						return as([VOID], value);
				}
				return as([TYPE, entry], value);
			}
			case ARRAY: {
				if (type) {
					let spread = value;
					if (type === "DataView") {
						spread = new Uint8Array(value.buffer);
					} else if (type === "ArrayBuffer") {
						spread = new Uint8Array(value);
					}
					return as([type, [...spread]], value);
				}

				const arr = [];
				const index = as([TYPE, arr], value);
				for (const entry of value) arr.push(pair(entry));
				return index;
			}
			case OBJECT: {
				if (type) {
					switch (type) {
						case "BigInt":
							return as([type, value.toString()], value);
						case "Boolean":
						case "Number":
						case "String":
							return as([type, value.valueOf()], value);
					}
				}

				if (json && "toJSON" in value) return pair(value.toJSON());

				const entries = [];
				const index = as([TYPE, entries], value);
				for (const key of keys(value)) {
					if (strict || !shouldSkip(typeOf(value[key]))) entries.push([pair(key), pair(value[key])]);
				}
				return index;
			}
			case DATE:
				return as([TYPE, isNaN(value.getTime()) ? EMPTY : value.toISOString()], value);
			case REGEXP: {
				const { source, flags } = value;
				return as([TYPE, { source, flags }], value);
			}
			case MAP: {
				const entries = [];
				const index = as([TYPE, entries], value);
				for (const [key, entry] of value) {
					if (strict || !(shouldSkip(typeOf(key)) || shouldSkip(typeOf(entry)))) entries.push([pair(key), pair(entry)]);
				}
				return index;
			}
			case SET: {
				const entries = [];
				const index = as([TYPE, entries], value);
				for (const entry of value) {
					if (strict || !shouldSkip(typeOf(entry))) entries.push(pair(entry));
				}
				return index;
			}
		}

		const { message } = value;
		return as([TYPE, { name: type, message }], value);
	};

	return pair;
};

/**
 * @typedef {Array<string,any>} Record a type representation
 */

/**
 * Returns an array of serialized Records.
 * @param {any} value a serializable value.
 * @param {{json?: boolean, lossy?: boolean}?} options an object with a `lossy` or `json` property that,
 *  if `true`, will not throw errors on incompatible types, and behave more
 *  like JSON stringify would behave. Symbol and Function will be discarded.
 * @returns {Record[]}
 */
const serialize$1 = (value, { json, lossy } = {}) => {
	const _ = [];
	return (serializer(!(json || lossy), !!json, new Map(), _)(value), _);
};

/**
 * @typedef {Array<string,any>} Record a type representation
 */

/**
 * Returns an array of serialized Records.
 * @param {any} any a serializable value.
 * @param {{transfer?: any[], json?: boolean, lossy?: boolean}?} options an object with
 * a transfer option (ignored when polyfilled) and/or non standard fields that
 * fallback to the polyfill if present.
 * @returns {Record[]}
 */
const structuredClone$1 =
	typeof structuredClone === "function" ?
		/* c8 ignore start */
		(any, options) => (options && ("json" in options || "lossy" in options) ? deserialize(serialize$1(any, options)) : structuredClone(any))
	:	(any, options) => deserialize(serialize$1(any, options));

/**
 * @typedef {import('hast').Element} Element
 * @typedef {import('hast').Parents} Parents
 */

/**
 * @template Fn
 * @template Fallback
 * @typedef {Fn extends (value: any) => value is infer Thing ? Thing : Fallback} Predicate
 */

/**
 * @callback Check
 *   Check that an arbitrary value is an element.
 * @param {unknown} this
 *   Context object (`this`) to call `test` with
 * @param {unknown} [element]
 *   Anything (typically a node).
 * @param {number | null | undefined} [index]
 *   Position of `element` in its parent.
 * @param {Parents | null | undefined} [parent]
 *   Parent of `element`.
 * @returns {boolean}
 *   Whether this is an element and passes a test.
 *
 * @typedef {Array<TestFunction | string> | TestFunction | string | null | undefined} Test
 *   Check for an arbitrary element.
 *
 *   * when `string`, checks that the element has that tag name
 *   * when `function`, see `TestFunction`
 *   * when `Array`, checks if one of the subtests pass
 *
 * @callback TestFunction
 *   Check if an element passes a test.
 * @param {unknown} this
 *   The given context.
 * @param {Element} element
 *   An element.
 * @param {number | undefined} [index]
 *   Position of `element` in its parent.
 * @param {Parents | undefined} [parent]
 *   Parent of `element`.
 * @returns {boolean | undefined | void}
 *   Whether this element passes the test.
 *
 *   Note: `void` is included until TS sees no return as `undefined`.
 */

/**
 * Check if `element` is an `Element` and whether it passes the given test.
 *
 * @param element
 *   Thing to check, typically `element`.
 * @param test
 *   Check for a specific element.
 * @param index
 *   Position of `element` in its parent.
 * @param parent
 *   Parent of `element`.
 * @param context
 *   Context object (`this`) to call `test` with.
 * @returns
 *   Whether `element` is an `Element` and passes a test.
 * @throws
 *   When an incorrect `test`, `index`, or `parent` is given; there is no error
 *   thrown when `element` is not a node or not an element.
 */
const isElement$1 =
	// Note: overloads in JSDoc can’t yet use different `@template`s.
	/**
	 * @type {(
	 *   (<Condition extends TestFunction>(element: unknown, test: Condition, index?: number | null | undefined, parent?: Parents | null | undefined, context?: unknown) => element is Element & Predicate<Condition, Element>) &
	 *   (<Condition extends string>(element: unknown, test: Condition, index?: number | null | undefined, parent?: Parents | null | undefined, context?: unknown) => element is Element & {tagName: Condition}) &
	 *   ((element?: null | undefined) => false) &
	 *   ((element: unknown, test?: null | undefined, index?: number | null | undefined, parent?: Parents | null | undefined, context?: unknown) => element is Element) &
	 *   ((element: unknown, test?: Test, index?: number | null | undefined, parent?: Parents | null | undefined, context?: unknown) => boolean)
	 * )}
	 */
	(
		/**
		 * @param {unknown} [element]
		 * @param {Test | undefined} [test]
		 * @param {number | null | undefined} [index]
		 * @param {Parents | null | undefined} [parent]
		 * @param {unknown} [context]
		 * @returns {boolean}
		 */
		// eslint-disable-next-line max-params
		function (element, test, index, parent, context) {
			const check = convertElement(test);

			return looksLikeAnElement(element) ? check.call(context, element, index, parent) : false;
		}
	);

/**
 * Generate a check from a test.
 *
 * Useful if you’re going to test many nodes, for example when creating a
 * utility where something else passes a compatible test.
 *
 * The created function is a bit faster because it expects valid input only:
 * an `element`, `index`, and `parent`.
 *
 * @param test
 *   A test for a specific element.
 * @returns
 *   A check.
 */
const convertElement =
	// Note: overloads in JSDoc can’t yet use different `@template`s.
	/**
	 * @type {(
	 *   (<Condition extends TestFunction>(test: Condition) => (element: unknown, index?: number | null | undefined, parent?: Parents | null | undefined, context?: unknown) => element is Element & Predicate<Condition, Element>) &
	 *   (<Condition extends string>(test: Condition) => (element: unknown, index?: number | null | undefined, parent?: Parents | null | undefined, context?: unknown) => element is Element & {tagName: Condition}) &
	 *   ((test?: null | undefined) => (element?: unknown, index?: number | null | undefined, parent?: Parents | null | undefined, context?: unknown) => element is Element) &
	 *   ((test?: Test) => Check)
	 * )}
	 */
	(
		/**
		 * @param {Test | null | undefined} [test]
		 * @returns {Check}
		 */
		function (test) {
			if (test === null || test === undefined) {
				return element$3;
			}

			if (typeof test === "string") {
				return tagNameFactory(test);
			}

			// Assume array.
			if (typeof test === "object") {
				return anyFactory$1(test);
			}

			if (typeof test === "function") {
				return castFactory$1(test);
			}

			throw new Error("Expected function, string, or array as `test`");
		}
	);

/**
 * Handle multiple tests.
 *
 * @param {Array<TestFunction | string>} tests
 * @returns {Check}
 */
function anyFactory$1(tests) {
	/** @type {Array<Check>} */
	const checks = [];
	let index = -1;

	while (++index < tests.length) {
		checks[index] = convertElement(tests[index]);
	}

	return castFactory$1(any);

	/**
	 * @this {unknown}
	 * @type {TestFunction}
	 */
	function any(...parameters) {
		let index = -1;

		while (++index < checks.length) {
			if (checks[index].apply(this, parameters)) return true;
		}

		return false;
	}
}

/**
 * Turn a string into a test for an element with a certain type.
 *
 * @param {string} check
 * @returns {Check}
 */
function tagNameFactory(check) {
	return castFactory$1(tagName);

	/**
	 * @param {Element} element
	 * @returns {boolean}
	 */
	function tagName(element) {
		return element.tagName === check;
	}
}

/**
 * Turn a custom test into a test for an element that passes that test.
 *
 * @param {TestFunction} testFunction
 * @returns {Check}
 */
function castFactory$1(testFunction) {
	return check;

	/**
	 * @this {unknown}
	 * @type {Check}
	 */
	function check(value, index, parent) {
		return Boolean(looksLikeAnElement(value) && testFunction.call(this, value, typeof index === "number" ? index : undefined, parent || undefined));
	}
}

/**
 * Make sure something is an element.
 *
 * @param {unknown} element
 * @returns {element is Element}
 */
function element$3(element) {
	return Boolean(element && typeof element === "object" && "type" in element && element.type === "element" && "tagName" in element && typeof element.tagName === "string");
}

/**
 * @param {unknown} value
 * @returns {value is Element}
 */
function looksLikeAnElement(value) {
	return value !== null && typeof value === "object" && "type" in value && "tagName" in value;
}

/**
 * Check if a node is a *embedded content*.
 *
 * @param value
 *   Thing to check (typically `Node`).
 * @returns
 *   Whether `value` is an element considered embedded content.
 *
 *   The elements `audio`, `canvas`, `embed`, `iframe`, `img`, `math`,
 *   `object`, `picture`, `svg`, and `video` are embedded content.
 */
const embedded = convertElement(
	/**
	 * @param element
	 * @returns {element is {tagName: 'audio' | 'canvas' | 'embed' | 'iframe' | 'img' | 'math' | 'object' | 'picture' | 'svg' | 'video'}}
	 */
	function (element) {
		return (
			element.tagName === "audio" ||
			element.tagName === "canvas" ||
			element.tagName === "embed" ||
			element.tagName === "iframe" ||
			element.tagName === "img" ||
			element.tagName === "math" ||
			element.tagName === "object" ||
			element.tagName === "picture" ||
			element.tagName === "svg" ||
			element.tagName === "video"
		);
	},
);

/**
 * @typedef {import('hast').Nodes} Nodes
 */

// HTML whitespace expression.
// See <https://infra.spec.whatwg.org/#ascii-whitespace>.
const re = /[ \t\n\f\r]/g;

/**
 * Check if the given value is *inter-element whitespace*.
 *
 * @param {Nodes | string} thing
 *   Thing to check (`Node` or `string`).
 * @returns {boolean}
 *   Whether the `value` is inter-element whitespace (`boolean`): consisting of
 *   zero or more of space, tab (`\t`), line feed (`\n`), carriage return
 *   (`\r`), or form feed (`\f`); if a node is passed it must be a `Text` node,
 *   whose `value` field is checked.
 */
function whitespace(thing) {
	return (
		typeof thing === "object" ?
			thing.type === "text" ?
				empty$1(thing.value)
			:	false
		:	empty$1(thing)
	);
}

/**
 * @param {string} value
 * @returns {boolean}
 */
function empty$1(value) {
	return value.replace(re, "") === "";
}

/**
 * @import {Node, Parent} from 'unist'
 */

/**
 * Generate an assertion from a test.
 *
 * Useful if you’re going to test many nodes, for example when creating a
 * utility where something else passes a compatible test.
 *
 * The created function is a bit faster because it expects valid input only:
 * a `node`, `index`, and `parent`.
 *
 * @param {Test} test
 *   *   when nullish, checks if `node` is a `Node`.
 *   *   when `string`, works like passing `(node) => node.type === test`.
 *   *   when `function` checks if function passed the node is true.
 *   *   when `object`, checks that all keys in test are in node, and that they have (strictly) equal values.
 *   *   when `array`, checks if any one of the subtests pass.
 * @returns {Check}
 *   An assertion.
 */
const convert =
	// Note: overloads in JSDoc can’t yet use different `@template`s.
	/**
	 * @type {(
	 *   (<Condition extends string>(test: Condition) => (node: unknown, index?: number | null | undefined, parent?: Parent | null | undefined, context?: unknown) => node is Node & {type: Condition}) &
	 *   (<Condition extends Props>(test: Condition) => (node: unknown, index?: number | null | undefined, parent?: Parent | null | undefined, context?: unknown) => node is Node & Condition) &
	 *   (<Condition extends TestFunction>(test: Condition) => (node: unknown, index?: number | null | undefined, parent?: Parent | null | undefined, context?: unknown) => node is Node & Predicate<Condition, Node>) &
	 *   ((test?: null | undefined) => (node?: unknown, index?: number | null | undefined, parent?: Parent | null | undefined, context?: unknown) => node is Node) &
	 *   ((test?: Test) => Check)
	 * )}
	 */
	(
		/**
		 * @param {Test} [test]
		 * @returns {Check}
		 */
		function (test) {
			if (test === null || test === undefined) {
				return ok;
			}

			if (typeof test === "function") {
				return castFactory(test);
			}

			if (typeof test === "object") {
				return Array.isArray(test) ?
						anyFactory(test)
						// Cast because `ReadonlyArray` goes into the above but `isArray`
						// narrows to `Array`.
					:	propertiesFactory(/** @type {Props} */ (test));
			}

			if (typeof test === "string") {
				return typeFactory(test);
			}

			throw new Error("Expected function, string, or object as test");
		}
	);

/**
 * @param {Array<Props | TestFunction | string>} tests
 * @returns {Check}
 */
function anyFactory(tests) {
	/** @type {Array<Check>} */
	const checks = [];
	let index = -1;

	while (++index < tests.length) {
		checks[index] = convert(tests[index]);
	}

	return castFactory(any);

	/**
	 * @this {unknown}
	 * @type {TestFunction}
	 */
	function any(...parameters) {
		let index = -1;

		while (++index < checks.length) {
			if (checks[index].apply(this, parameters)) return true;
		}

		return false;
	}
}

/**
 * Turn an object into a test for a node with a certain fields.
 *
 * @param {Props} check
 * @returns {Check}
 */
function propertiesFactory(check) {
	const checkAsRecord = /** @type {Record<string, unknown>} */ (check);

	return castFactory(all);

	/**
	 * @param {Node} node
	 * @returns {boolean}
	 */
	function all(node) {
		const nodeAsRecord = /** @type {Record<string, unknown>} */ (/** @type {unknown} */ (node));

		/** @type {string} */
		let key;

		for (key in check) {
			if (nodeAsRecord[key] !== checkAsRecord[key]) return false;
		}

		return true;
	}
}

/**
 * Turn a string into a test for a node with a certain type.
 *
 * @param {string} check
 * @returns {Check}
 */
function typeFactory(check) {
	return castFactory(type);

	/**
	 * @param {Node} node
	 */
	function type(node) {
		return node && node.type === check;
	}
}

/**
 * Turn a custom test into a test for a node that passes that test.
 *
 * @param {TestFunction} testFunction
 * @returns {Check}
 */
function castFactory(testFunction) {
	return check;

	/**
	 * @this {unknown}
	 * @type {Check}
	 */
	function check(value, index, parent) {
		return Boolean(looksLikeANode(value) && testFunction.call(this, value, typeof index === "number" ? index : undefined, parent || undefined));
	}
}

function ok() {
	return true;
}

/**
 * @param {unknown} value
 * @returns {value is Node}
 */
function looksLikeANode(value) {
	return value !== null && typeof value === "object" && "type" in value;
}

// See: <https://html.spec.whatwg.org/#the-css-user-agent-style-sheet-and-presentational-hints>
const blocks = [
	"address", // Flow content.
	"article", // Sections and headings.
	"aside", // Sections and headings.
	"blockquote", // Flow content.
	"body", // Page.
	"br", // Contribute whitespace intrinsically.
	"caption", // Similar to block.
	"center", // Flow content, legacy.
	"col", // Similar to block.
	"colgroup", // Similar to block.
	"dd", // Lists.
	"dialog", // Flow content.
	"dir", // Lists, legacy.
	"div", // Flow content.
	"dl", // Lists.
	"dt", // Lists.
	"figcaption", // Flow content.
	"figure", // Flow content.
	"footer", // Flow content.
	"form", // Flow content.
	"h1", // Sections and headings.
	"h2", // Sections and headings.
	"h3", // Sections and headings.
	"h4", // Sections and headings.
	"h5", // Sections and headings.
	"h6", // Sections and headings.
	"head", // Page.
	"header", // Flow content.
	"hgroup", // Sections and headings.
	"hr", // Flow content.
	"html", // Page.
	"legend", // Flow content.
	"li", // Block-like.
	"li", // Similar to block.
	"listing", // Flow content, legacy
	"main", // Flow content.
	"menu", // Lists.
	"nav", // Sections and headings.
	"ol", // Lists.
	"optgroup", // Similar to block.
	"option", // Similar to block.
	"p", // Flow content.
	"plaintext", // Flow content, legacy
	"pre", // Flow content.
	"section", // Sections and headings.
	"summary", // Similar to block.
	"table", // Similar to block.
	"tbody", // Similar to block.
	"td", // Block-like.
	"td", // Similar to block.
	"tfoot", // Similar to block.
	"th", // Block-like.
	"th", // Similar to block.
	"thead", // Similar to block.
	"tr", // Similar to block.
	"ul", // Lists.
	"wbr", // Contribute whitespace intrinsically.
	"xmp", // Flow content, legacy
];

const content$3 = [
	// Form.
	"button",
	"input",
	"select",
	"textarea",
];

const skippable$1 = ["area", "base", "basefont", "dialog", "datalist", "head", "link", "meta", "noembed", "noframes", "param", "rp", "script", "source", "style", "template", "track", "title"];

/**
 * @import {Nodes, Parents, Text} from 'hast'
 */

/** @type {Options} */
const emptyOptions$5 = {};
const ignorableNode = convert(["comment", "doctype"]);

/**
 * Minify whitespace.
 *
 * @param {Nodes} tree
 *   Tree.
 * @param {Options | null | undefined} [options]
 *   Configuration (optional).
 * @returns {undefined}
 *   Nothing.
 */
function minifyWhitespace(tree, options) {
	const settings = options || emptyOptions$5;

	minify(tree, {
		collapse: collapseFactory(settings.newlines ? replaceNewlines : replaceWhitespace),
		whitespace: "normal",
	});
}

/**
 * @param {Nodes} node
 *   Node.
 * @param {State} state
 *   Info passed around.
 * @returns {Result}
 *   Result.
 */
function minify(node, state) {
	if ("children" in node) {
		const settings = { ...state };

		if (node.type === "root" || blocklike(node)) {
			settings.before = true;
			settings.after = true;
		}

		settings.whitespace = inferWhiteSpace(node, state);

		return all$5(node, settings);
	}

	if (node.type === "text") {
		if (state.whitespace === "normal") {
			return minifyText(node, state);
		}

		// Naïve collapse, but no trimming:
		if (state.whitespace === "nowrap") {
			node.value = state.collapse(node.value);
		}

		// The `pre-wrap` or `pre` whitespace settings are neither collapsed nor
		// trimmed.
	}

	return { ignore: ignorableNode(node), stripAtStart: false, remove: false };
}

/**
 * @param {Text} node
 *   Node.
 * @param {State} state
 *   Info passed around.
 * @returns {Result}
 *   Result.
 */
function minifyText(node, state) {
	const value = state.collapse(node.value);
	const result = { ignore: false, stripAtStart: false, remove: false };
	let start = 0;
	let end = value.length;

	if (state.before && removable(value.charAt(0))) {
		start++;
	}

	if (start !== end && removable(value.charAt(end - 1))) {
		if (state.after) {
			end--;
		} else {
			result.stripAtStart = true;
		}
	}

	if (start === end) {
		result.remove = true;
	} else {
		node.value = value.slice(start, end);
	}

	return result;
}

/**
 * @param {Parents} parent
 *   Node.
 * @param {State} state
 *   Info passed around.
 * @returns {Result}
 *   Result.
 */
function all$5(parent, state) {
	let before = state.before;
	const after = state.after;
	const children = parent.children;
	let length = children.length;
	let index = -1;

	while (++index < length) {
		const result = minify(children[index], {
			...state,
			after: collapsableAfter(children, index, after),
			before,
		});

		if (result.remove) {
			children.splice(index, 1);
			index--;
			length--;
		} else if (!result.ignore) {
			before = result.stripAtStart;
		}

		// If this element, such as a `<select>` or `<img>`, contributes content
		// somehow, allow whitespace again.
		if (content$2(children[index])) {
			before = false;
		}
	}

	return { ignore: false, stripAtStart: Boolean(before || after), remove: false };
}

/**
 * @param {Array<Nodes>} nodes
 *   Nodes.
 * @param {number} index
 *   Index.
 * @param {boolean | undefined} [after]
 *   Whether there is a break after `nodes` (default: `false`).
 * @returns {boolean | undefined}
 *   Whether there is a break after the node at `index`.
 */
function collapsableAfter(nodes, index, after) {
	while (++index < nodes.length) {
		const node = nodes[index];
		let result = inferBoundary(node);

		if (result === undefined && "children" in node && !skippable(node)) {
			result = collapsableAfter(node.children, -1);
		}

		if (typeof result === "boolean") {
			return result;
		}
	}

	return after;
}

/**
 * Infer two types of boundaries:
 *
 * 1. `true` — boundary for which whitespace around it does not contribute
 *    anything
 * 2. `false` — boundary for which whitespace around it *does* contribute
 *
 * No result (`undefined`) is returned if it is unknown.
 *
 * @param {Nodes} node
 *   Node.
 * @returns {boolean | undefined}
 *   Boundary.
 */
function inferBoundary(node) {
	if (node.type === "element") {
		if (content$2(node)) {
			return false;
		}

		if (blocklike(node)) {
			return true;
		}

		// Unknown: either depends on siblings if embedded or metadata, or on
		// children.
	} else if (node.type === "text") {
		if (!whitespace(node)) {
			return false;
		}
	} else if (!ignorableNode(node)) {
		return false;
	}
}

/**
 * Infer whether a node is skippable.
 *
 * @param {Nodes} node
 *   Node.
 * @returns {boolean}
 *   Whether `node` is skippable.
 */
function content$2(node) {
	return embedded(node) || isElement$1(node, content$3);
}

/**
 * See: <https://html.spec.whatwg.org/#the-css-user-agent-style-sheet-and-presentational-hints>
 *
 * @param {Nodes} node
 *   Node.
 * @returns {boolean}
 *   Whether `node` is block-like.
 */
function blocklike(node) {
	return isElement$1(node, blocks);
}

/**
 * @param {Parents} node
 *   Node.
 * @returns {boolean}
 *   Whether `node` is skippable.
 */
function skippable(node) {
	return Boolean(node.type === "element" && node.properties.hidden) || ignorableNode(node) || isElement$1(node, skippable$1);
}

/**
 * @param {string} character
 *   Character.
 * @returns {boolean}
 *   Whether `character` is removable.
 */
function removable(character) {
	return character === " " || character === "\n";
}

/**
 * @type {Collapse}
 */
function replaceNewlines(value) {
	const match = /\r?\n|\r/.exec(value);
	return match ? match[0] : " ";
}

/**
 * @type {Collapse}
 */
function replaceWhitespace() {
	return " ";
}

/**
 * @param {Collapse} replace
 * @returns {Collapse}
 *   Collapse.
 */
function collapseFactory(replace) {
	return collapse;

	/**
	 * @type {Collapse}
	 */
	function collapse(value) {
		return String(value).replace(/[\t\n\v\f\r ]+/g, replace);
	}
}

/**
 * We don’t need to support void elements here (so `nobr wbr` -> `normal` is
 * ignored).
 *
 * @param {Parents} node
 *   Node.
 * @param {State} state
 *   Info passed around.
 * @returns {Whitespace}
 *   Whitespace.
 */
function inferWhiteSpace(node, state) {
	if ("tagName" in node && node.properties) {
		switch (node.tagName) {
			// Whitespace in script/style, while not displayed by CSS as significant,
			// could have some meaning in JS/CSS, so we can’t touch them.
			case "listing":
			case "plaintext":
			case "script":
			case "style":
			case "xmp": {
				return "pre";
			}

			case "nobr": {
				return "nowrap";
			}

			case "pre": {
				return node.properties.wrap ? "pre-wrap" : "pre";
			}

			case "td":
			case "th": {
				return node.properties.noWrap ? "nowrap" : state.whitespace;
			}

			case "textarea": {
				return "pre-wrap";
			}
		}
	}

	return state.whitespace;
}

/**
 * @import {Options} from 'hast-util-minify-whitespace'
 * @import {Root} from 'hast'
 */

/**
 * Minify whitespace.
 *
 * @param {Options | null | undefined} [options]
 *   Configuration (optional).
 * @returns
 *   Transform.
 */
function rehypeMinifyWhitespace(options) {
	/**
	 * @param {Root} tree
	 *   Tree.
	 * @returns {undefined}
	 *   Nothing.
	 */
	return function (tree) {
		minifyWhitespace(tree, options);
	};
}

/**
 * @param {string} d
 * @returns {string}
 */
function color(d) {
	return d;
}

/**
 * @import {Node as UnistNode, Parent as UnistParent} from 'unist'
 */

/** @type {Readonly<ActionTuple>} */
const empty = [];

/**
 * Continue traversing as normal.
 */
const CONTINUE = true;

/**
 * Stop traversing immediately.
 */
const EXIT = false;

/**
 * Do not traverse this node’s children.
 */
const SKIP = "skip";

/**
 * Visit nodes, with ancestral information.
 *
 * This algorithm performs *depth-first* *tree traversal* in *preorder*
 * (**NLR**) or if `reverse` is given, in *reverse preorder* (**NRL**).
 *
 * You can choose for which nodes `visitor` is called by passing a `test`.
 * For complex tests, you should test yourself in `visitor`, as it will be
 * faster and will have improved type information.
 *
 * Walking the tree is an intensive task.
 * Make use of the return values of the visitor when possible.
 * Instead of walking a tree multiple times, walk it once, use `unist-util-is`
 * to check if a node matches, and then perform different operations.
 *
 * You can change the tree.
 * See `Visitor` for more info.
 *
 * @overload
 * @param {Tree} tree
 * @param {Check} check
 * @param {BuildVisitor<Tree, Check>} visitor
 * @param {boolean | null | undefined} [reverse]
 * @returns {undefined}
 *
 * @overload
 * @param {Tree} tree
 * @param {BuildVisitor<Tree>} visitor
 * @param {boolean | null | undefined} [reverse]
 * @returns {undefined}
 *
 * @param {UnistNode} tree
 *   Tree to traverse.
 * @param {Visitor | Test} test
 *   `unist-util-is`-compatible test
 * @param {Visitor | boolean | null | undefined} [visitor]
 *   Handle each node.
 * @param {boolean | null | undefined} [reverse]
 *   Traverse in reverse preorder (NRL) instead of the default preorder (NLR).
 * @returns {undefined}
 *   Nothing.
 *
 * @template {UnistNode} Tree
 *   Node type.
 * @template {Test} Check
 *   `unist-util-is`-compatible test.
 */
function visitParents(tree, test, visitor, reverse) {
	/** @type {Test} */
	let check;

	if (typeof test === "function" && typeof visitor !== "function") {
		reverse = visitor;
		// @ts-expect-error no visitor given, so `visitor` is test.
		visitor = test;
	} else {
		// @ts-expect-error visitor given, so `test` isn’t a visitor.
		check = test;
	}

	const is = convert(check);
	const step = reverse ? -1 : 1;

	factory(tree, undefined, [])();

	/**
	 * @param {UnistNode} node
	 * @param {number | undefined} index
	 * @param {Array<UnistParent>} parents
	 */
	function factory(node, index, parents) {
		const value = /** @type {Record<string, unknown>} */ (node && typeof node === "object" ? node : {});

		if (typeof value.type === "string") {
			const name =
				// `hast`
				typeof value.tagName === "string" ? value.tagName
					// `xast`
				: typeof value.name === "string" ? value.name
				: undefined;

			Object.defineProperty(visit, "name", {
				value: "node (" + color(node.type + (name ? "<" + name + ">" : "")) + ")",
			});
		}

		return visit;

		function visit() {
			/** @type {Readonly<ActionTuple>} */
			let result = empty;
			/** @type {Readonly<ActionTuple>} */
			let subresult;
			/** @type {number} */
			let offset;
			/** @type {Array<UnistParent>} */
			let grandparents;

			if (!test || is(node, index, parents[parents.length - 1] || undefined)) {
				// @ts-expect-error: `visitor` is now a visitor.
				result = toResult(visitor(node, parents));

				if (result[0] === EXIT) {
					return result;
				}
			}

			if ("children" in node && node.children) {
				const nodeAsParent = /** @type {UnistParent} */ (node);

				if (nodeAsParent.children && result[0] !== SKIP) {
					offset = (reverse ? nodeAsParent.children.length : -1) + step;
					grandparents = parents.concat(nodeAsParent);

					while (offset > -1 && offset < nodeAsParent.children.length) {
						const child = nodeAsParent.children[offset];

						subresult = factory(child, offset, grandparents)();

						if (subresult[0] === EXIT) {
							return subresult;
						}

						offset = typeof subresult[1] === "number" ? subresult[1] : offset + step;
					}
				}
			}

			return result;
		}
	}
}

/**
 * Turn a return value into a clean result.
 *
 * @param {VisitorResult} value
 *   Valid return values from visitors.
 * @returns {Readonly<ActionTuple>}
 *   Clean result.
 */
function toResult(value) {
	if (Array.isArray(value)) {
		return value;
	}

	if (typeof value === "number") {
		return [CONTINUE, value];
	}

	return value === null || value === undefined ? empty : [value];
}

/**
 * @import {Node as UnistNode, Parent as UnistParent} from 'unist'
 * @import {VisitorResult} from 'unist-util-visit-parents'
 */

/**
 * Visit nodes.
 *
 * This algorithm performs *depth-first* *tree traversal* in *preorder*
 * (**NLR**) or if `reverse` is given, in *reverse preorder* (**NRL**).
 *
 * You can choose for which nodes `visitor` is called by passing a `test`.
 * For complex tests, you should test yourself in `visitor`, as it will be
 * faster and will have improved type information.
 *
 * Walking the tree is an intensive task.
 * Make use of the return values of the visitor when possible.
 * Instead of walking a tree multiple times, walk it once, use `unist-util-is`
 * to check if a node matches, and then perform different operations.
 *
 * You can change the tree.
 * See `Visitor` for more info.
 *
 * @overload
 * @param {Tree} tree
 * @param {Check} check
 * @param {BuildVisitor<Tree, Check>} visitor
 * @param {boolean | null | undefined} [reverse]
 * @returns {undefined}
 *
 * @overload
 * @param {Tree} tree
 * @param {BuildVisitor<Tree>} visitor
 * @param {boolean | null | undefined} [reverse]
 * @returns {undefined}
 *
 * @param {UnistNode} tree
 *   Tree to traverse.
 * @param {Visitor | Test} testOrVisitor
 *   `unist-util-is`-compatible test (optional, omit to pass a visitor).
 * @param {Visitor | boolean | null | undefined} [visitorOrReverse]
 *   Handle each node (when test is omitted, pass `reverse`).
 * @param {boolean | null | undefined} [maybeReverse=false]
 *   Traverse in reverse preorder (NRL) instead of the default preorder (NLR).
 * @returns {undefined}
 *   Nothing.
 *
 * @template {UnistNode} Tree
 *   Node type.
 * @template {Test} Check
 *   `unist-util-is`-compatible test.
 */
function visit(tree, testOrVisitor, visitorOrReverse, maybeReverse) {
	/** @type {boolean | null | undefined} */
	let reverse;
	/** @type {Test} */
	let test;
	/** @type {Visitor} */
	let visitor;

	if (typeof testOrVisitor === "function" && typeof visitorOrReverse !== "function") {
		test = undefined;
		visitor = testOrVisitor;
		reverse = visitorOrReverse;
	} else {
		// @ts-expect-error: assume the overload with test was given.
		test = testOrVisitor;
		// @ts-expect-error: assume the overload with test was given.
		visitor = visitorOrReverse;
		reverse = maybeReverse;
	}

	visitParents(tree, test, overload, reverse);

	/**
	 * @param {UnistNode} node
	 * @param {Array<UnistParent>} parents
	 */
	function overload(node, parents) {
		const parent = parents[parents.length - 1];
		const index = parent ? parent.children.indexOf(node) : undefined;
		return visitor(node, index, parent);
	}
}

/**
 * @typedef {import('unist').Node} Node
 * @typedef {import('unist').Point} Point
 * @typedef {import('unist').Position} Position
 */

/**
 * @typedef NodeLike
 * @property {string} type
 * @property {PositionLike | null | undefined} [position]
 *
 * @typedef PositionLike
 * @property {PointLike | null | undefined} [start]
 * @property {PointLike | null | undefined} [end]
 *
 * @typedef PointLike
 * @property {number | null | undefined} [line]
 * @property {number | null | undefined} [column]
 * @property {number | null | undefined} [offset]
 */

/**
 * Get the ending point of `node`.
 *
 * @param node
 *   Node.
 * @returns
 *   Point.
 */
const pointEnd = point$1("end");

/**
 * Get the starting point of `node`.
 *
 * @param node
 *   Node.
 * @returns
 *   Point.
 */
const pointStart = point$1("start");

/**
 * Get the positional info of `node`.
 *
 * @param {'end' | 'start'} type
 *   Side.
 * @returns
 *   Getter.
 */
function point$1(type) {
	return point;

	/**
	 * Get the point info of `node` at a bound side.
	 *
	 * @param {Node | NodeLike | null | undefined} [node]
	 * @returns {Point | undefined}
	 */
	function point(node) {
		const point = (node && node.position && node.position[type]) || {};

		if (typeof point.line === "number" && point.line > 0 && typeof point.column === "number" && point.column > 0) {
			return {
				line: point.line,
				column: point.column,
				offset: typeof point.offset === "number" && point.offset > -1 ? point.offset : undefined,
			};
		}
	}
}

/**
 * Get the positional info of `node`.
 *
 * @param {Node | NodeLike | null | undefined} [node]
 *   Node.
 * @returns {Position | undefined}
 *   Position.
 */
function position(node) {
	const start = pointStart(node);
	const end = pointEnd(node);

	if (start && end) {
		return { start, end };
	}
}

/**
 * @import {State} from 'hast-util-to-mdast'
 * @import {Element} from 'hast'
 * @import {Link, PhrasingContent} from 'mdast'
 */

/**
 * @param {State} state
 *   State.
 * @param {Readonly<Element>} node
 *   hast element to transform.
 * @returns {Link}
 *   mdast node.
 */
function a(state, node) {
	const properties = node.properties || {};
	// Allow potentially “invalid” nodes, they might be unknown.
	// We also support straddling later.
	const children = /** @type {Array<PhrasingContent>} */ (state.all(node));

	/** @type {Link} */
	const result = {
		type: "link",
		url: state.resolve(String(properties.href || "") || null),
		title: properties.title ? String(properties.title) : null,
		children,
	};
	state.patch(node, result);
	return result;
}

/**
 * @import {State} from 'hast-util-to-mdast'
 * @import {Element} from 'hast'
 */

/**
 * @param {State} state
 *   State.
 * @param {Readonly<Element>} node
 *   hast element to transform.
 * @returns {undefined}
 *   Nothing.
 */
function base(state, node) {
	if (!state.baseFound) {
		state.frozenBaseUrl = String((node.properties && node.properties.href) || "") || undefined;
		state.baseFound = true;
	}
}

/**
 * @import {State} from 'hast-util-to-mdast'
 * @import {Element} from 'hast'
 * @import {Blockquote} from 'mdast'
 */

/**
 * @param {State} state
 *   State.
 * @param {Readonly<Element>} node
 *   hast element to transform.
 * @returns {Blockquote}
 *   mdast node.
 */
function blockquote$2(state, node) {
	/** @type {Blockquote} */
	const result = { type: "blockquote", children: state.toFlow(state.all(node)) };
	state.patch(node, result);
	return result;
}

/**
 * @import {State} from 'hast-util-to-mdast'
 * @import {Element} from 'hast'
 * @import {Break} from 'mdast'
 */

/**
 * @param {State} state
 *   State.
 * @param {Readonly<Element>} node
 *   hast element to transform.
 * @returns {Break}
 *   mdast node.
 */
function br$1(state, node) {
	/** @type {Break} */
	const result = { type: "break" };
	state.patch(node, result);
	return result;
}

/**
 * @typedef {import('unist').Node} UnistNode
 * @typedef {import('unist').Parent} UnistParent
 */

/**
 * Find the first node in `parent` after another `node` or after an index,
 * that passes `test`.
 *
 * @param parent
 *   Parent node.
 * @param index
 *   Child node or index.
 * @param [test=undefined]
 *   Test for child to look for (optional).
 * @returns
 *   A child (matching `test`, if given) or `undefined`.
 */
const findAfter =
	// Note: overloads like this are needed to support optional generics.
	/**
	 * @type {(
	 *   (<Kind extends UnistParent, Check extends Test>(parent: Kind, index: Child<Kind> | number, test: Check) => Matches<Child<Kind>, Check> | undefined) &
	 *   (<Kind extends UnistParent>(parent: Kind, index: Child<Kind> | number, test?: null | undefined) => Child<Kind> | undefined)
	 * )}
	 */
	(
		/**
		 * @param {UnistParent} parent
		 * @param {UnistNode | number} index
		 * @param {Test} [test]
		 * @returns {UnistNode | undefined}
		 */
		function (parent, index, test) {
			const is = convert(test);

			if (!parent || !parent.type || !parent.children) {
				throw new Error("Expected parent node");
			}

			if (typeof index === "number") {
				if (index < 0 || index === Number.POSITIVE_INFINITY) {
					throw new Error("Expected positive finite number as index");
				}
			} else {
				index = parent.children.indexOf(index);

				if (index < 0) {
					throw new Error("Expected child node or index");
				}
			}

			while (++index < parent.children.length) {
				if (is(parent.children[index], index, parent)) {
					return parent.children[index];
				}
			}

			return undefined;
		}
	);

/**
 * @typedef {import('hast').Comment} Comment
 * @typedef {import('hast').Element} Element
 * @typedef {import('hast').Nodes} Nodes
 * @typedef {import('hast').Parents} Parents
 * @typedef {import('hast').Text} Text
 * @typedef {import('hast-util-is-element').TestFunction} TestFunction
 */

const searchLineFeeds = /\n/g;
const searchTabOrSpaces = /[\t ]+/g;

const br = convertElement("br");
const cell = convertElement(isCell);
const p$2 = convertElement("p");
const row = convertElement("tr");

// Note that we don’t need to include void elements here as they don’t have text.
// See: <https://github.com/wooorm/html-void-elements>
const notRendered = convertElement([
	// List from: <https://html.spec.whatwg.org/multipage/rendering.html#hidden-elements>
	"datalist",
	"head",
	"noembed",
	"noframes",
	"noscript", // Act as if we support scripting.
	"rp",
	"script",
	"style",
	"template",
	"title",
	// Hidden attribute.
	hidden,
	// From: <https://html.spec.whatwg.org/multipage/rendering.html#flow-content-3>
	closedDialog,
]);

// See: <https://html.spec.whatwg.org/multipage/rendering.html#the-css-user-agent-style-sheet-and-presentational-hints>
const blockOrCaption = convertElement([
	"address", // Flow content
	"article", // Sections and headings
	"aside", // Sections and headings
	"blockquote", // Flow content
	"body", // Page
	"caption", // `table-caption`
	"center", // Flow content (legacy)
	"dd", // Lists
	"dialog", // Flow content
	"dir", // Lists (legacy)
	"dl", // Lists
	"dt", // Lists
	"div", // Flow content
	"figure", // Flow content
	"figcaption", // Flow content
	"footer", // Flow content
	"form,", // Flow content
	"h1", // Sections and headings
	"h2", // Sections and headings
	"h3", // Sections and headings
	"h4", // Sections and headings
	"h5", // Sections and headings
	"h6", // Sections and headings
	"header", // Flow content
	"hgroup", // Sections and headings
	"hr", // Flow content
	"html", // Page
	"legend", // Flow content
	"li", // Lists (as `display: list-item`)
	"listing", // Flow content (legacy)
	"main", // Flow content
	"menu", // Lists
	"nav", // Sections and headings
	"ol", // Lists
	"p", // Flow content
	"plaintext", // Flow content (legacy)
	"pre", // Flow content
	"section", // Sections and headings
	"ul", // Lists
	"xmp", // Flow content (legacy)
]);

/**
 * Get the plain-text value of a node.
 *
 * ###### Algorithm
 *
 * *   if `tree` is a comment, returns its `value`
 * *   if `tree` is a text, applies normal whitespace collapsing to its
 *     `value`, as defined by the CSS Text spec
 * *   if `tree` is a root or element, applies an algorithm similar to the
 *     `innerText` getter as defined by HTML
 *
 * ###### Notes
 *
 * > 👉 **Note**: the algorithm acts as if `tree` is being rendered, and as if
 * > we’re a CSS-supporting user agent, with scripting enabled.
 *
 * *   if `tree` is an element that is not displayed (such as a `head`), we’ll
 *     still use the `innerText` algorithm instead of switching to `textContent`
 * *   if descendants of `tree` are elements that are not displayed, they are
 *     ignored
 * *   CSS is not considered, except for the default user agent style sheet
 * *   a line feed is collapsed instead of ignored in cases where Fullwidth, Wide,
 *     or Halfwidth East Asian Width characters are used, the same goes for a case
 *     with Chinese, Japanese, or Yi writing systems
 * *   replaced elements (such as `audio`) are treated like non-replaced elements
 *
 * @param {Nodes} tree
 *   Tree to turn into text.
 * @param {Readonly<Options> | null | undefined} [options]
 *   Configuration (optional).
 * @returns {string}
 *   Serialized `tree`.
 */
function toText(tree, options) {
	const options_ = {};
	const children = "children" in tree ? tree.children : [];
	const block = blockOrCaption(tree);
	const whitespace = inferWhitespace(tree, {
		whitespace: options_.whitespace || "normal",
	});

	/** @type {Array<BreakNumber | string>} */
	const results = [];

	// Treat `text` and `comment` as having normal white-space.
	// This deviates from the spec as in the DOM the node’s `.data` has to be
	// returned.
	// If you want that behavior use `hast-util-to-string`.
	// All other nodes are later handled as if they are `element`s (so the
	// algorithm also works on a `root`).
	// Nodes without children are treated as a void element, so `doctype` is thus
	// ignored.
	if (tree.type === "text" || tree.type === "comment") {
		results.push(
			...collectText(tree, {
				breakBefore: true,
				breakAfter: true,
			}),
		);
	}

	// 1.  If this element is not being rendered, or if the user agent is a
	//     non-CSS user agent, then return the same value as the textContent IDL
	//     attribute on this element.
	//
	//     Note: we’re not supporting stylesheets so we’re acting as if the node
	//     is rendered.
	//
	//     If you want that behavior use `hast-util-to-string`.
	//     Important: we’ll have to account for this later though.

	// 2.  Let results be a new empty list.
	let index = -1;

	// 3.  For each child node node of this element:
	while (++index < children.length) {
		// 3.1. Let current be the list resulting in running the inner text
		//      collection steps with node.
		//      Each item in results will either be a JavaScript string or a
		//      positive integer (a required line break count).
		// 3.2. For each item item in current, append item to results.
		results.push(
			...renderedTextCollection(
				children[index],
				// @ts-expect-error: `tree` is a parent if we’re here.
				tree,
				{
					whitespace,
					breakBefore: index ? undefined : block,
					breakAfter: index < children.length - 1 ? br(children[index + 1]) : block,
				},
			),
		);
	}

	// 4.  Remove any items from results that are the empty string.
	// 5.  Remove any runs of consecutive required line break count items at the
	//     start or end of results.
	// 6.  Replace each remaining run of consecutive required line break count
	//     items with a string consisting of as many U+000A LINE FEED (LF)
	//     characters as the maximum of the values in the required line break
	//     count items.
	/** @type {Array<string>} */
	const result = [];
	/** @type {number | undefined} */
	let count;

	index = -1;

	while (++index < results.length) {
		const value = results[index];

		if (typeof value === "number") {
			if (count !== undefined && value > count) count = value;
		} else if (value) {
			if (count !== undefined && count > -1) {
				result.push("\n".repeat(count) || " ");
			}

			count = -1;
			result.push(value);
		}
	}

	// 7.  Return the concatenation of the string items in results.
	return result.join("");
}

/**
 * <https://html.spec.whatwg.org/multipage/dom.html#rendered-text-collection-steps>
 *
 * @param {Nodes} node
 * @param {Parents} parent
 * @param {CollectionInfo} info
 * @returns {Array<BreakNumber | string>}
 */
function renderedTextCollection(node, parent, info) {
	if (node.type === "element") {
		return collectElement(node, parent, info);
	}

	if (node.type === "text") {
		return info.whitespace === "normal" ? collectText(node, info) : collectPreText(node);
	}

	return [];
}

/**
 * Collect an element.
 *
 * @param {Element} node
 *   Element node.
 * @param {Parents} parent
 * @param {CollectionInfo} info
 *   Info on current collection.
 * @returns {Array<BreakNumber | string>}
 */
function collectElement(node, parent, info) {
	// First we infer the `white-space` property.
	const whitespace = inferWhitespace(node, info);
	const children = node.children || [];
	let index = -1;
	/** @type {Array<BreakNumber | string>} */
	let items = [];

	// We’re ignoring point 3, and exiting without any content here, because we
	// deviated from the spec in `toText` at step 3.
	if (notRendered(node)) {
		return items;
	}

	/** @type {BreakNumber | undefined} */
	let prefix;
	/** @type {BreakForce | BreakNumber | undefined} */
	let suffix;
	// Note: we first detect if there is going to be a break before or after the
	// contents, as that changes the white-space handling.

	// 2.  If node’s computed value of `visibility` is not `visible`, then return
	//     items.
	//
	//     Note: Ignored, as everything is visible by default user agent styles.

	// 3.  If node is not being rendered, then return items. [...]
	//
	//     Note: We already did this above.

	// See `collectText` for step 4.

	// 5.  If node is a `<br>` element, then append a string containing a single
	//     U+000A LINE FEED (LF) character to items.
	if (br(node)) {
		suffix = "\n";
	}

	// 7.  If node’s computed value of `display` is `table-row`, and node’s CSS
	//     box is not the last `table-row` box of the nearest ancestor `table`
	//     box, then append a string containing a single U+000A LINE FEED (LF)
	//     character to items.
	//
	//     See: <https://html.spec.whatwg.org/multipage/rendering.html#tables-2>
	//     Note: needs further investigation as this does not account for implicit
	//     rows.
	else if (
		row(node) &&
		// @ts-expect-error: something up with types of parents.
		findAfter(parent, node, row)
	) {
		suffix = "\n";
	}

	// 8.  If node is a `<p>` element, then append 2 (a required line break count)
	//     at the beginning and end of items.
	else if (p$2(node)) {
		prefix = 2;
		suffix = 2;
	}

	// 9.  If node’s used value of `display` is block-level or `table-caption`,
	//     then append 1 (a required line break count) at the beginning and end of
	//     items.
	else if (blockOrCaption(node)) {
		prefix = 1;
		suffix = 1;
	}

	// 1.  Let items be the result of running the inner text collection steps with
	//     each child node of node in tree order, and then concatenating the
	//     results to a single list.
	while (++index < children.length) {
		items = items.concat(
			renderedTextCollection(children[index], node, {
				whitespace,
				breakBefore: index ? undefined : prefix,
				breakAfter: index < children.length - 1 ? br(children[index + 1]) : suffix,
			}),
		);
	}

	// 6.  If node’s computed value of `display` is `table-cell`, and node’s CSS
	//     box is not the last `table-cell` box of its enclosing `table-row` box,
	//     then append a string containing a single U+0009 CHARACTER TABULATION
	//     (tab) character to items.
	//
	//     See: <https://html.spec.whatwg.org/multipage/rendering.html#tables-2>
	if (
		cell(node) &&
		// @ts-expect-error: something up with types of parents.
		findAfter(parent, node, cell)
	) {
		items.push("\t");
	}

	// Add the pre- and suffix.
	if (prefix) items.unshift(prefix);
	if (suffix) items.push(suffix);

	return items;
}

/**
 * 4.  If node is a Text node, then for each CSS text box produced by node,
 *     in content order, compute the text of the box after application of the
 *     CSS `white-space` processing rules and `text-transform` rules, set
 *     items to the list of the resulting strings, and return items.
 *     The CSS `white-space` processing rules are slightly modified:
 *     collapsible spaces at the end of lines are always collapsed, but they
 *     are only removed if the line is the last line of the block, or it ends
 *     with a br element.
 *     Soft hyphens should be preserved.
 *
 *     Note: See `collectText` and `collectPreText`.
 *     Note: we don’t deal with `text-transform`, no element has that by
 *     default.
 *
 * See: <https://drafts.csswg.org/css-text/#white-space-phase-1>
 *
 * @param {Comment | Text} node
 *   Text node.
 * @param {CollectionInfo} info
 *   Info on current collection.
 * @returns {Array<BreakNumber | string>}
 *   Result.
 */
function collectText(node, info) {
	const value = String(node.value);
	/** @type {Array<string>} */
	const lines = [];
	/** @type {Array<BreakNumber | string>} */
	const result = [];
	let start = 0;

	while (start <= value.length) {
		searchLineFeeds.lastIndex = start;

		const match = searchLineFeeds.exec(value);
		const end = match && "index" in match ? match.index : value.length;

		lines.push(
			// Any sequence of collapsible spaces and tabs immediately preceding or
			// following a segment break is removed.
			trimAndCollapseSpacesAndTabs(
				// […] ignoring bidi formatting characters (characters with the
				// Bidi_Control property [UAX9]: ALM, LTR, RTL, LRE-RLO, LRI-PDI) as if
				// they were not there.
				value.slice(start, end).replace(/[\u061C\u200E\u200F\u202A-\u202E\u2066-\u2069]/g, ""),
				start === 0 ? info.breakBefore : true,
				end === value.length ? info.breakAfter : true,
			),
		);

		start = end + 1;
	}

	// Collapsible segment breaks are transformed for rendering according to the
	// segment break transformation rules.
	// So here we jump to 4.1.2 of [CSSTEXT]:
	// Any collapsible segment break immediately following another collapsible
	// segment break is removed
	let index = -1;
	/** @type {BreakNumber | undefined} */
	let join;

	while (++index < lines.length) {
		// *   If the character immediately before or immediately after the segment
		//     break is the zero-width space character (U+200B), then the break is
		//     removed, leaving behind the zero-width space.
		if (lines[index].charCodeAt(lines[index].length - 1) === 0x20_0b /* ZWSP */ || (index < lines.length - 1 && lines[index + 1].charCodeAt(0) === 0x20_0b) /* ZWSP */) {
			result.push(lines[index]);
			join = undefined;
		}

		// *   Otherwise, if the East Asian Width property [UAX11] of both the
		//     character before and after the segment break is Fullwidth, Wide, or
		//     Halfwidth (not Ambiguous), and neither side is Hangul, then the
		//     segment break is removed.
		//
		//     Note: ignored.
		// *   Otherwise, if the writing system of the segment break is Chinese,
		//     Japanese, or Yi, and the character before or after the segment break
		//     is punctuation or a symbol (Unicode general category P* or S*) and
		//     has an East Asian Width property of Ambiguous, and the character on
		//     the other side of the segment break is Fullwidth, Wide, or Halfwidth,
		//     and not Hangul, then the segment break is removed.
		//
		//     Note: ignored.

		// *   Otherwise, the segment break is converted to a space (U+0020).
		else if (lines[index]) {
			if (typeof join === "number") result.push(join);
			result.push(lines[index]);
			join = 0;
		} else if (index === 0 || index === lines.length - 1) {
			// If this line is empty, and it’s the first or last, add a space.
			// Note that this function is only called in normal whitespace, so we
			// don’t worry about `pre`.
			result.push(0);
		}
	}

	return result;
}

/**
 * Collect a text node as “pre” whitespace.
 *
 * @param {Text} node
 *   Text node.
 * @returns {Array<BreakNumber | string>}
 *   Result.
 */
function collectPreText(node) {
	return [String(node.value)];
}

/**
 * 3.  Every collapsible tab is converted to a collapsible space (U+0020).
 * 4.  Any collapsible space immediately following another collapsible
 *     space—even one outside the boundary of the inline containing that
 *     space, provided both spaces are within the same inline formatting
 *     context—is collapsed to have zero advance width. (It is invisible,
 *     but retains its soft wrap opportunity, if any.)
 *
 * @param {string} value
 *   Value to collapse.
 * @param {BreakBefore} breakBefore
 *   Whether there was a break before.
 * @param {BreakAfter} breakAfter
 *   Whether there was a break after.
 * @returns {string}
 *   Result.
 */
function trimAndCollapseSpacesAndTabs(value, breakBefore, breakAfter) {
	/** @type {Array<string>} */
	const result = [];
	let start = 0;
	/** @type {number | undefined} */
	let end;

	while (start < value.length) {
		searchTabOrSpaces.lastIndex = start;
		const match = searchTabOrSpaces.exec(value);
		end = match ? match.index : value.length;

		// If we’re not directly after a segment break, but there was white space,
		// add an empty value that will be turned into a space.
		if (!start && !end && match && !breakBefore) {
			result.push("");
		}

		if (start !== end) {
			result.push(value.slice(start, end));
		}

		start = match ? end + match[0].length : end;
	}

	// If we reached the end, there was trailing white space, and there’s no
	// segment break after this node, add an empty value that will be turned
	// into a space.
	if (start !== end && !breakAfter) {
		result.push("");
	}

	return result.join(" ");
}

/**
 * Figure out the whitespace of a node.
 *
 * We don’t support void elements here (so `nobr wbr` -> `normal` is ignored).
 *
 * @param {Nodes} node
 *   Node (typically `Element`).
 * @param {CollectionInfo} info
 *   Info on current collection.
 * @returns {Whitespace}
 *   Applied whitespace.
 */
function inferWhitespace(node, info) {
	if (node.type === "element") {
		const properties = node.properties || {};
		switch (node.tagName) {
			case "listing":
			case "plaintext":
			case "xmp": {
				return "pre";
			}

			case "nobr": {
				return "nowrap";
			}

			case "pre": {
				return properties.wrap ? "pre-wrap" : "pre";
			}

			case "td":
			case "th": {
				return properties.noWrap ? "nowrap" : info.whitespace;
			}

			case "textarea": {
				return "pre-wrap";
			}
		}
	}

	return info.whitespace;
}

/**
 * @type {TestFunction}
 * @param {Element} node
 * @returns {node is {properties: {hidden: true}}}
 */
function hidden(node) {
	return Boolean((node.properties || {}).hidden);
}

/**
 * @type {TestFunction}
 * @param {Element} node
 * @returns {node is {tagName: 'td' | 'th'}}
 */
function isCell(node) {
	return node.tagName === "td" || node.tagName === "th";
}

/**
 * @type {TestFunction}
 */
function closedDialog(node) {
	return node.tagName === "dialog" && !(node.properties || {}).open;
}

/**
 * Remove final line endings from `value`
 *
 * @param {unknown} value
 *   Value with trailing line endings, coerced to string.
 * @return {string}
 *   Value without trailing line endings.
 */
function trimTrailingLines(value) {
	const input = String(value);
	let end = input.length;

	while (end > 0) {
		const code = input.codePointAt(end - 1);
		if (code !== undefined && (code === 10 || code === 13)) {
			end--;
		} else {
			break;
		}
	}

	return input.slice(0, end);
}

/**
 * @import {State} from 'hast-util-to-mdast'
 * @import {Element} from 'hast'
 * @import {Code} from 'mdast'
 */

const prefix = "language-";

/**
 * @param {State} state
 *   State.
 * @param {Readonly<Element>} node
 *   hast element to transform.
 * @returns {Code}
 *   mdast node.
 */
function code$3(state, node) {
	const children = node.children;
	let index = -1;
	/** @type {Array<number | string> | undefined} */
	let classList;
	/** @type {string | undefined} */
	let lang;

	if (node.tagName === "pre") {
		while (++index < children.length) {
			const child = children[index];

			if (child.type === "element" && child.tagName === "code" && child.properties && child.properties.className && Array.isArray(child.properties.className)) {
				classList = child.properties.className;
				break;
			}
		}
	}

	if (classList) {
		index = -1;

		while (++index < classList.length) {
			if (String(classList[index]).slice(0, prefix.length) === prefix) {
				lang = String(classList[index]).slice(prefix.length);
				break;
			}
		}
	}

	/** @type {Code} */
	const result = {
		type: "code",
		lang: lang || null,
		meta: null,
		value: trimTrailingLines(toText(node)),
	};
	state.patch(node, result);
	return result;
}

/**
 * @import {State} from 'hast-util-to-mdast'
 * @import {Comment} from 'hast'
 * @import {Html} from 'mdast'
 */

/**
 * @param {State} state
 *   State.
 * @param {Readonly<Comment>} node
 *   hast element to transform.
 * @returns {Html}
 *   mdast node.
 */
function comment$2(state, node) {
	/** @type {Html} */
	const result = {
		type: "html",
		value: "<!--" + node.value + "-->",
	};
	state.patch(node, result);
	return result;
}

/**
 * @import {State} from 'hast-util-to-mdast'
 * @import {Element} from 'hast'
 * @import {Delete, PhrasingContent} from 'mdast'
 */

/**
 * @param {State} state
 *   State.
 * @param {Readonly<Element>} node
 *   hast element to transform.
 * @returns {Delete}
 *   mdast node.
 */
function del(state, node) {
	// Allow potentially “invalid” nodes, they might be unknown.
	// We also support straddling later.
	const children = /** @type {Array<PhrasingContent>} */ (state.all(node));
	/** @type {Delete} */
	const result = { type: "delete", children };
	state.patch(node, result);
	return result;
}

/**
 * @import {ListContent} from 'mdast'
 */

/**
 * Infer whether list items are spread.
 *
 * @param {Readonly<Array<Readonly<ListContent>>>} children
 *   List items.
 * @returns {boolean}
 *   Whether one or more list items are spread.
 */
function listItemsSpread(children) {
	let index = -1;

	if (children.length > 1) {
		while (++index < children.length) {
			if (children[index].spread) {
				return true;
			}
		}
	}

	return false;
}

/**
 * @import {State} from 'hast-util-to-mdast'
 * @import {ElementContent, Element} from 'hast'
 * @import {BlockContent, DefinitionContent, ListContent, ListItem, List} from 'mdast'
 */

/**
 * @param {State} state
 *   State.
 * @param {Readonly<Element>} node
 *   hast element to transform.
 * @returns {List | undefined}
 *   mdast node.
 */
function dl(state, node) {
	/** @type {Array<ElementContent>} */
	const clean = [];
	/** @type {Array<Group>} */
	const groups = [];
	let index = -1;

	// Unwrap `<div>`s
	while (++index < node.children.length) {
		const child = node.children[index];

		if (child.type === "element" && child.tagName === "div") {
			clean.push(...child.children);
		} else {
			clean.push(child);
		}
	}

	/** @type {Group} */
	let group = { definitions: [], titles: [] };
	index = -1;

	// Group titles and definitions.
	while (++index < clean.length) {
		const child = clean[index];

		if (child.type === "element" && child.tagName === "dt") {
			const previous = clean[index - 1];

			if (previous && previous.type === "element" && previous.tagName === "dd") {
				groups.push(group);
				group = { definitions: [], titles: [] };
			}

			group.titles.push(child);
		} else {
			group.definitions.push(child);
		}
	}

	groups.push(group);

	// Create items.
	index = -1;
	/** @type {Array<ListContent>} */
	const content = [];

	while (++index < groups.length) {
		const result = [...handle$2(state, groups[index].titles), ...handle$2(state, groups[index].definitions)];

		if (result.length > 0) {
			content.push({
				type: "listItem",
				spread: result.length > 1,
				checked: null,
				children: result,
			});
		}
	}

	// Create a list if there are items.
	if (content.length > 0) {
		/** @type {List} */
		const result = {
			type: "list",
			ordered: false,
			start: null,
			spread: listItemsSpread(content),
			children: content,
		};
		state.patch(node, result);
		return result;
	}
}

/**
 * @param {State} state
 *   State.
 * @param {Array<ElementContent>} children
 *   hast element children to transform.
 * @returns {Array<BlockContent | DefinitionContent>}
 *   mdast nodes.
 */
function handle$2(state, children) {
	const nodes = state.all({ type: "root", children });
	const listItems = state.toSpecificContent(nodes, create$2);

	if (listItems.length === 0) {
		return [];
	}

	if (listItems.length === 1) {
		return listItems[0].children;
	}

	return [
		{
			type: "list",
			ordered: false,
			start: null,
			spread: listItemsSpread(listItems),
			children: listItems,
		},
	];
}

/**
 * @returns {ListItem}
 */
function create$2() {
	return { type: "listItem", spread: false, checked: null, children: [] };
}

/**
 * @import {State} from 'hast-util-to-mdast'
 * @import {Element} from 'hast'
 * @import {Emphasis, PhrasingContent} from 'mdast'
 */

/**
 * @param {State} state
 *   State.
 * @param {Readonly<Element>} node
 *   hast element to transform.
 * @returns {Emphasis}
 *   mdast node.
 */
function em(state, node) {
	// Allow potentially “invalid” nodes, they might be unknown.
	// We also support straddling later.
	const children = /** @type {Array<PhrasingContent>} */ (state.all(node));

	/** @type {Emphasis} */
	const result = { type: "emphasis", children };
	state.patch(node, result);
	return result;
}

/**
 * @import {Nodes} from 'mdast'
 */

/**
 * Drop trailing initial and final `br`s.
 *
 * @template {Nodes} Node
 *   Node type.
 * @param {Array<Node>} nodes
 *   List of nodes.
 * @returns {Array<Node>}
 *   List of nodes w/o `break`s.
 */
function dropSurroundingBreaks(nodes) {
	let start = 0;
	let end = nodes.length;

	while (start < end && nodes[start].type === "break") start++;
	while (end > start && nodes[end - 1].type === "break") end--;

	return start === 0 && end === nodes.length ? nodes : nodes.slice(start, end);
}

/**
 * @import {State} from 'hast-util-to-mdast'
 * @import {Element} from 'hast'
 * @import {Heading, PhrasingContent} from 'mdast'
 */

/**
 * @param {State} state
 *   State.
 * @param {Readonly<Element>} node
 *   hast element to transform.
 * @returns {Heading}
 *   mdast node.
 */
function heading$2(state, node) {
	const depth = /** @type {Heading['depth']} */ (
		/* c8 ignore next */
		Number(node.tagName.charAt(1)) || 1
	);
	const children = dropSurroundingBreaks(/** @type {Array<PhrasingContent>} */ (state.all(node)));

	/** @type {Heading} */
	const result = { type: "heading", depth, children };
	state.patch(node, result);
	return result;
}

/**
 * @import {State} from 'hast-util-to-mdast'
 * @import {Element} from 'hast'
 * @import {ThematicBreak} from 'mdast'
 */

/**
 * @param {State} state
 *   State.
 * @param {Readonly<Element>} node
 *   hast element to transform.
 * @returns {ThematicBreak}
 *   mdast node.
 */
function hr(state, node) {
	/** @type {ThematicBreak} */
	const result = { type: "thematicBreak" };
	state.patch(node, result);
	return result;
}

/**
 * @import {State} from 'hast-util-to-mdast'
 * @import {Element} from 'hast'
 * @import {Link} from 'mdast'
 */

/**
 * @param {State} state
 *   State.
 * @param {Readonly<Element>} node
 *   hast element to transform.
 * @returns {Link | undefined}
 *   mdast node.
 */
function iframe(state, node) {
	const properties = node.properties || {};
	const source = String(properties.src || "");
	const title = String(properties.title || "");

	// Only create a link if there is a title.
	// We can’t use the content of the frame because conforming HTML parsers treat
	// it as text, whereas legacy parsers treat it as HTML, so it will likely
	// contain tags that will show up in text.
	if (source && title) {
		/** @type {Link} */
		const result = {
			type: "link",
			title: null,
			url: state.resolve(source),
			children: [{ type: "text", value: title }],
		};
		state.patch(node, result);
		return result;
	}
}

/**
 * @import {State} from 'hast-util-to-mdast'
 * @import {Element} from 'hast'
 * @import {Image} from 'mdast'
 */

/**
 * @param {State} state
 *   State.
 * @param {Readonly<Element>} node
 *   hast element to transform.
 * @returns {Image}
 *   mdast node.
 */
function img(state, node) {
	const properties = node.properties || {};

	/** @type {Image} */
	const result = {
		type: "image",
		url: state.resolve(String(properties.src || "") || null),
		title: properties.title ? String(properties.title) : null,
		alt: properties.alt ? String(properties.alt) : "",
	};
	state.patch(node, result);
	return result;
}

/**
 * @import {State} from 'hast-util-to-mdast'
 * @import {Element} from 'hast'
 * @import {InlineCode} from 'mdast'
 */

/**
 * @param {State} state
 *   State.
 * @param {Readonly<Element>} node
 *   hast element to transform.
 * @returns {InlineCode}
 *   mdast node.
 */
function inlineCode$2(state, node) {
	/** @type {InlineCode} */
	const result = { type: "inlineCode", value: toText(node) };
	state.patch(node, result);
	return result;
}

/**
 * @import {Element, Properties} from 'hast'
 */

/**
 * @param {Readonly<Element>} node
 *   hast element to inspect.
 * @param {Properties | undefined} [explicitProperties]
 *   Properties to use, normally taken from `node`, but can be changed.
 * @returns {Options}
 *   Options.
 */
function findSelectedOptions(node, explicitProperties) {
	/** @type {Array<Element>} */
	const selectedOptions = [];
	/** @type {Options} */
	const values = [];
	const properties = explicitProperties || node.properties || {};
	const options = findOptions(node);
	const size = Math.min(Number.parseInt(String(properties.size), 10), 0) || (properties.multiple ? 4 : 1);
	let index = -1;

	while (++index < options.length) {
		const option = options[index];

		if (option && option.properties && option.properties.selected) {
			selectedOptions.push(option);
		}
	}

	const list = selectedOptions.length > 0 ? selectedOptions : options;
	const max = Math.min(list.length, size);
	index = -1;

	while (++index < max) {
		const option = list[index];
		const properties = option.properties || {};
		const content = toText(option);
		const label = content || String(properties.label || "");
		const value = String(properties.value || "") || content;
		values.push([value, label === value ? undefined : label]);
	}

	return values;
}

/**
 * @param {Element} node
 *   Parent to find in.
 * @returns {Array<Element>}
 *   Option elements.
 */
function findOptions(node) {
	/** @type {Array<Element>} */
	const results = [];
	let index = -1;

	while (++index < node.children.length) {
		const child = node.children[index];

		if ("children" in child && Array.isArray(child.children)) {
			results.push(...findOptions(child));
		}

		if (child.type === "element" && child.tagName === "option" && (!child.properties || !child.properties.disabled)) {
			results.push(child);
		}
	}

	return results;
}

/**
 * @import {State} from 'hast-util-to-mdast'
 * @import {Element} from 'hast'
 * @import {Image, Link, Text} from 'mdast'
 * @import {Options} from '../util/find-selected-options.js'
 */

const defaultChecked = "[x]";
const defaultUnchecked = "[ ]";

/**
 * @param {State} state
 *   State.
 * @param {Readonly<Element>} node
 *   hast element to transform.
 * @returns {Array<Link | Text> | Image | Text | undefined}
 *   mdast node.
 */
// eslint-disable-next-line complexity
function input(state, node) {
	const properties = node.properties || {};
	const value = String(properties.value || properties.placeholder || "");

	if (properties.disabled || properties.type === "hidden" || properties.type === "file") {
		return;
	}

	if (properties.type === "checkbox" || properties.type === "radio") {
		/** @type {Text} */
		const result = {
			type: "text",
			value: properties.checked ? state.options.checked || defaultChecked : state.options.unchecked || defaultUnchecked,
		};
		state.patch(node, result);
		return result;
	}

	if (properties.type === "image") {
		const alt = properties.alt || value;

		if (alt) {
			/** @type {Image} */
			const result = {
				type: "image",
				url: state.resolve(String(properties.src || "") || null),
				title: String(properties.title || "") || null,
				alt: String(alt),
			};
			state.patch(node, result);
			return result;
		}

		return;
	}

	/** @type {Options} */
	let values = [];

	if (value) {
		values = [[value, undefined]];
	} else if (
		// `list` is not supported on these types:
		properties.type !== "button" &&
		properties.type !== "file" &&
		properties.type !== "password" &&
		properties.type !== "reset" &&
		properties.type !== "submit" &&
		properties.list
	) {
		const list = String(properties.list);
		const datalist = state.elementById.get(list);

		if (datalist && datalist.tagName === "datalist") {
			values = findSelectedOptions(datalist, properties);
		}
	}

	if (values.length === 0) {
		return;
	}

	// Hide password value.
	if (properties.type === "password") {
		// Passwords don’t support `list`.
		values[0] = ["•".repeat(values[0][0].length), undefined];
	}

	if (properties.type === "email" || properties.type === "url") {
		/** @type {Array<Link | Text>} */
		const results = [];
		let index = -1;

		while (++index < values.length) {
			const value = state.resolve(values[index][0]);
			/** @type {Link} */
			const result = {
				type: "link",
				title: null,
				url: properties.type === "email" ? "mailto:" + value : value,
				children: [{ type: "text", value: values[index][1] || value }],
			};

			results.push(result);

			if (index !== values.length - 1) {
				results.push({ type: "text", value: ", " });
			}
		}

		return results;
	}

	/** @type {Array<string>} */
	const texts = [];
	let index = -1;

	while (++index < values.length) {
		texts.push(values[index][1] ? values[index][1] + " (" + values[index][0] + ")" : values[index][0]);
	}

	/** @type {Text} */
	const result = { type: "text", value: texts.join(", ") };
	state.patch(node, result);
	return result;
}

/**
 * @typedef {import('hast').Element} Element
 * @typedef {import('hast').Nodes} Nodes
 */

const own$8 = {}.hasOwnProperty;

/**
 * Check if `node` is an element and has a `name` property.
 *
 * @template {string} Key
 *   Type of key.
 * @param {Nodes} node
 *   Node to check (typically `Element`).
 * @param {Key} name
 *   Property name to check.
 * @returns {node is Element & {properties: Record<Key, Array<number | string> | number | string | true>}}}
 *   Whether `node` is an element that has a `name` property.
 *
 *   Note: see <https://github.com/DefinitelyTyped/DefinitelyTyped/blob/27c9274/types/hast/index.d.ts#L37C29-L37C98>.
 */
function hasProperty(node, name) {
	const value = node.type === "element" && own$8.call(node.properties, name) && node.properties[name];

	return value !== null && value !== undefined && value !== false;
}

/**
 * @import {Nodes} from 'hast'
 */

const list$5 = new Set(["pingback", "prefetch", "stylesheet"]);

/**
 * Checks whether a node is a “body OK” link.
 *
 * @param {Nodes} node
 *   Node to check.
 * @returns {boolean}
 *   Whether `node` is a “body OK” link.
 */
function isBodyOkLink(node) {
	if (node.type !== "element" || node.tagName !== "link") {
		return false;
	}

	if (node.properties.itemProp) {
		return true;
	}

	const value = node.properties.rel;
	let index = -1;

	if (!Array.isArray(value) || value.length === 0) {
		return false;
	}

	while (++index < value.length) {
		if (!list$5.has(String(value[index]))) {
			return false;
		}
	}

	return true;
}

/**
 * @typedef {import('hast').Nodes} Nodes
 */

const basic = convertElement([
	"a",
	"abbr",
	// `area` is in fact only phrasing if it is inside a `map` element.
	// However, since `area`s are required to be inside a `map` element, and it’s
	// a rather involved check, it’s ignored here for now.
	"area",
	"b",
	"bdi",
	"bdo",
	"br",
	"button",
	"cite",
	"code",
	"data",
	"datalist",
	"del",
	"dfn",
	"em",
	"i",
	"input",
	"ins",
	"kbd",
	"keygen",
	"label",
	"map",
	"mark",
	"meter",
	"noscript",
	"output",
	"progress",
	"q",
	"ruby",
	"s",
	"samp",
	"script",
	"select",
	"small",
	"span",
	"strong",
	"sub",
	"sup",
	"template",
	"textarea",
	"time",
	"u",
	"var",
	"wbr",
]);

const meta = convertElement("meta");

/**
 * Check if the given value is *phrasing* content.
 *
 * @param {Nodes} value
 *   Node to check.
 * @returns {boolean}
 *   Whether `value` is phrasing content.
 */
function phrasing$2(value) {
	return Boolean(value.type === "text" || basic(value) || embedded(value) || isBodyOkLink(value) || (meta(value) && hasProperty(value, "itemProp")));
}

/**
 * @import {State} from 'hast-util-to-mdast'
 * @import {Element} from 'hast'
 * @import {ListItem} from 'mdast'
 */

/**
 * @param {State} state
 *   State.
 * @param {Readonly<Element>} node
 *   hast element to transform.
 * @returns {ListItem}
 *   mdast node.
 */
function li$1(state, node) {
	// If the list item starts with a checkbox, remove the checkbox and mark the
	// list item as a GFM task list item.
	const { rest, checkbox } = extractLeadingCheckbox(node);
	const checked = checkbox ? Boolean(checkbox.properties.checked) : null;
	const spread = spreadout(rest);
	const children = state.toFlow(state.all(rest));

	/** @type {ListItem} */
	const result = { type: "listItem", spread, checked, children };
	state.patch(node, result);
	return result;
}

/**
 * Check if an element should spread out.
 *
 * The reason to spread out a markdown list item is primarily whether writing
 * the equivalent in markdown, would yield a spread out item.
 *
 * A spread out item results in `<p>` and `</p>` tags.
 * Otherwise, the phrasing would be output directly.
 * We can check for that: if there’s a `<p>` element, spread it out.
 *
 * But what if there are no paragraphs?
 * In that case, we can also assume that if two “block” things were written in
 * an item, that it is spread out, because blocks are typically joined by blank
 * lines, which also means a spread item.
 *
 * Lastly, because in HTML things can be wrapped in a `<div>` or similar, we
 * delve into non-phrasing elements here to figure out if they themselves
 * contain paragraphs or 2 or more flow non-phrasing elements.
 *
 * @param {Readonly<Element>} node
 * @returns {boolean}
 */
function spreadout(node) {
	let index = -1;
	let seenFlow = false;

	while (++index < node.children.length) {
		const child = node.children[index];

		if (child.type === "element") {
			if (phrasing$2(child)) continue;

			if (child.tagName === "p" || seenFlow || spreadout(child)) {
				return true;
			}

			seenFlow = true;
		}
	}

	return false;
}

/**
 * Extract a leading checkbox from a list item.
 *
 * If there was a leading checkbox, makes a deep clone of the node w/o the
 * leading checkbox; otherwise a reference to the given, untouched, node is
 * given back.
 *
 * So for example:
 *
 * ```html
 * <li><input type="checkbox">Text</li>
 * ```
 *
 * …becomes:
 *
 * ```html
 * <li>Text</li>
 * ```
 *
 * ```html
 * <li><p><input type="checkbox">Text</p></li>
 * ```
 *
 * …becomes:
 *
 * ```html
 * <li><p>Text</p></li>
 * ```
 *
 * @param {Readonly<Element>} node
 * @returns {ExtractResult}
 */
function extractLeadingCheckbox(node) {
	const head = node.children[0];

	if (head && head.type === "element" && head.tagName === "input" && head.properties && (head.properties.type === "checkbox" || head.properties.type === "radio")) {
		const rest = { ...node, children: node.children.slice(1) };
		return { checkbox: head, rest };
	}

	// The checkbox may be nested in another element.
	// If the first element has children, look for a leading checkbox inside it.
	//
	// This only handles nesting in `<p>` elements, which is most common.
	// It’s possible a leading checkbox might be nested in other types of flow or
	// phrasing elements (and *deeply* nested, which is not possible with `<p>`).
	// Limiting things to `<p>` elements keeps this simpler for now.
	if (head && head.type === "element" && head.tagName === "p") {
		const { checkbox, rest: restHead } = extractLeadingCheckbox(head);

		if (checkbox) {
			const rest = { ...node, children: [restHead, ...node.children.slice(1)] };
			return { checkbox, rest };
		}
	}

	return { checkbox: undefined, rest: node };
}

/**
 * @import {State} from 'hast-util-to-mdast'
 * @import {Element} from 'hast'
 * @import {ListItem, List} from 'mdast'
 */

/**
 * @param {State} state
 *   State.
 * @param {Readonly<Element>} node
 *   hast element to transform.
 * @returns {List}
 *   mdast node.
 */
function list$4(state, node) {
	const ordered = node.tagName === "ol";
	const children = state.toSpecificContent(state.all(node), create$1);
	/** @type {number | null} */
	let start = null;

	if (ordered) {
		start = node.properties && node.properties.start ? Number.parseInt(String(node.properties.start), 10) : 1;
	}

	/** @type {List} */
	const result = {
		type: "list",
		ordered,
		start,
		spread: listItemsSpread(children),
		children,
	};
	state.patch(node, result);
	return result;
}

/**
 * @returns {ListItem}
 */
function create$1() {
	return { type: "listItem", spread: false, checked: null, children: [] };
}

/**
 * @typedef {import('mdast').Nodes} Nodes
 *
 * @typedef Options
 *   Configuration (optional).
 * @property {boolean | null | undefined} [includeImageAlt=true]
 *   Whether to use `alt` for `image`s (default: `true`).
 * @property {boolean | null | undefined} [includeHtml=true]
 *   Whether to use `value` of HTML (default: `true`).
 */

/** @type {Options} */
const emptyOptions$4 = {};

/**
 * Get the text content of a node or list of nodes.
 *
 * Prefers the node’s plain-text fields, otherwise serializes its children,
 * and if the given value is an array, serialize the nodes in it.
 *
 * @param {unknown} [value]
 *   Thing to serialize, typically `Node`.
 * @param {Options | null | undefined} [options]
 *   Configuration (optional).
 * @returns {string}
 *   Serialized `value`.
 */
function toString(value, options) {
	const settings = emptyOptions$4;
	const includeImageAlt = typeof settings.includeImageAlt === "boolean" ? settings.includeImageAlt : true;
	const includeHtml = typeof settings.includeHtml === "boolean" ? settings.includeHtml : true;

	return one$3(value, includeImageAlt, includeHtml);
}

/**
 * One node or several nodes.
 *
 * @param {unknown} value
 *   Thing to serialize.
 * @param {boolean} includeImageAlt
 *   Include image `alt`s.
 * @param {boolean} includeHtml
 *   Include HTML.
 * @returns {string}
 *   Serialized node.
 */
function one$3(value, includeImageAlt, includeHtml) {
	if (node(value)) {
		if ("value" in value) {
			return value.type === "html" && !includeHtml ? "" : value.value;
		}

		if (includeImageAlt && "alt" in value && value.alt) {
			return value.alt;
		}

		if ("children" in value) {
			return all$4(value.children, includeImageAlt, includeHtml);
		}
	}

	if (Array.isArray(value)) {
		return all$4(value, includeImageAlt, includeHtml);
	}

	return "";
}

/**
 * Serialize a list of nodes.
 *
 * @param {Array<unknown>} values
 *   Thing to serialize.
 * @param {boolean} includeImageAlt
 *   Include image `alt`s.
 * @param {boolean} includeHtml
 *   Include HTML.
 * @returns {string}
 *   Serialized nodes.
 */
function all$4(values, includeImageAlt, includeHtml) {
	/** @type {Array<string>} */
	const result = [];
	let index = -1;

	while (++index < values.length) {
		result[index] = one$3(values[index], includeImageAlt, includeHtml);
	}

	return result.join("");
}

/**
 * Check if `value` looks like a node.
 *
 * @param {unknown} value
 *   Thing.
 * @returns {value is Nodes}
 *   Whether `value` is a node.
 */
function node(value) {
	return Boolean(value && typeof value === "object");
}

/**
 * @typedef {import('mdast').Html} Html
 * @typedef {import('mdast').PhrasingContent} PhrasingContent
 */

/**
 * Check if the given value is *phrasing content*.
 *
 * > 👉 **Note**: Excludes `html`, which can be both phrasing or flow.
 *
 * @param node
 *   Thing to check, typically `Node`.
 * @returns
 *   Whether `value` is phrasing content.
 */

const phrasing$1 =
	/** @type {(node?: unknown) => node is Exclude<PhrasingContent, Html>} */
	(
		convert([
			"break",
			"delete",
			"emphasis",
			// To do: next major: removed since footnotes were added to GFM.
			"footnote",
			"footnoteReference",
			"image",
			"imageReference",
			"inlineCode",
			// Enabled by `mdast-util-math`:
			"inlineMath",
			"link",
			"linkReference",
			// Enabled by `mdast-util-mdx`:
			"mdxJsxTextElement",
			// Enabled by `mdast-util-mdx`:
			"mdxTextExpression",
			"strong",
			"text",
			// Enabled by `mdast-util-directive`:
			"textDirective",
		])
	);

/**
 * @import {} from 'mdast-util-to-hast'
 * @import {
 *   BlockContent,
 *   Delete,
 *   Link,
 *   Nodes,
 *   Paragraph,
 *   Parents,
 *   PhrasingContent,
 *   RootContent
 * } from 'mdast'
 */

/**
 * Check if there are phrasing mdast nodes.
 *
 * This is needed if a fragment is given, which could just be a sentence, and
 * doesn’t need a wrapper paragraph.
 *
 * @param {Array<Nodes>} nodes
 * @returns {boolean}
 */
function wrapNeeded(nodes) {
	let index = -1;

	while (++index < nodes.length) {
		const node = nodes[index];

		if (!phrasing(node) || ("children" in node && wrapNeeded(node.children))) {
			return true;
		}
	}

	return false;
}

/**
 * Wrap runs of phrasing content into paragraphs, leaving the non-phrasing
 * content as-is.
 *
 * @param {Array<RootContent>} nodes
 *   Content.
 * @returns {Array<BlockContent>}
 *   Content where phrasing is wrapped in paragraphs.
 */
function wrap$2(nodes) {
	return runs(nodes, onphrasing, function (d) {
		return d;
	});

	/**
	 * @param {Array<PhrasingContent>} nodes
	 * @returns {Array<Paragraph>}
	 */
	function onphrasing(nodes) {
		return (
				nodes.every(function (d) {
					return d.type === "text" ? whitespace(d.value) : false;
				})
			) ?
				[]
			:	[{ type: "paragraph", children: dropSurroundingBreaks(nodes) }];
	}
}

/**
 * @param {Delete | Link} node
 * @returns {Array<BlockContent>}
 */
function split(node) {
	return runs(node.children, onphrasing, onnonphrasing);

	/**
	 * Use `parent`, put the phrasing run inside it.
	 *
	 * @param {Array<PhrasingContent>} nodes
	 * @returns {Array<BlockContent>}
	 */
	function onphrasing(nodes) {
		const newParent = cloneWithoutChildren(node);
		newParent.children = nodes;
		// @ts-expect-error Assume fine.
		return [newParent];
	}

	/**
	 * Use `child`, add `parent` as its first child, put the original children
	 * into `parent`.
	 * If `child` is not a parent, `parent` will not be added.
	 *
	 * @param {BlockContent} child
	 * @returns {BlockContent}
	 */
	function onnonphrasing(child) {
		if ("children" in child && "children" in node) {
			const newParent = cloneWithoutChildren(node);
			const newChild = cloneWithoutChildren(child);
			// @ts-expect-error Assume fine.
			newParent.children = child.children;
			// @ts-expect-error Assume fine.
			newChild.children.push(newParent);
			return newChild;
		}

		return { ...child };
	}
}

/**
 * Wrap all runs of mdast phrasing content in `paragraph` nodes.
 *
 * @param {Array<RootContent>} nodes
 *   List of input nodes.
 * @param {(nodes: Array<PhrasingContent>) => Array<BlockContent>} onphrasing
 *   Turn phrasing content into block content.
 * @param {(node: BlockContent) => BlockContent} onnonphrasing
 *   Map block content (defaults to keeping them as-is).
 * @returns {Array<BlockContent>}
 */
function runs(nodes, onphrasing, onnonphrasing) {
	const flattened = flatten(nodes);
	/** @type {Array<BlockContent>} */
	const result = [];
	/** @type {Array<PhrasingContent>} */
	let queue = [];
	let index = -1;

	while (++index < flattened.length) {
		const node = flattened[index];

		if (phrasing(node)) {
			queue.push(node);
		} else {
			if (queue.length > 0) {
				result.push(...onphrasing(queue));
				queue = [];
			}

			// @ts-expect-error Assume non-phrasing.
			result.push(onnonphrasing(node));
		}
	}

	if (queue.length > 0) {
		result.push(...onphrasing(queue));
		queue = [];
	}

	return result;
}

/**
 * Flatten a list of nodes.
 *
 * @param {Array<RootContent>} nodes
 *   List of nodes, will unravel `delete` and `link`.
 * @returns {Array<RootContent>}
 *   Unraveled nodes.
 */
function flatten(nodes) {
	/** @type {Array<RootContent>} */
	const flattened = [];
	let index = -1;

	while (++index < nodes.length) {
		const node = nodes[index];

		// Straddling: some elements are *weird*.
		// Namely: `map`, `ins`, `del`, and `a`, as they are hybrid elements.
		// See: <https://html.spec.whatwg.org/#paragraphs>.
		// Paragraphs are the weirdest of them all.
		// See the straddling fixture for more info!
		// `ins` is ignored in mdast, so we don’t need to worry about that.
		// `map` maps to its content, so we don’t need to worry about that either.
		// `del` maps to `delete` and `a` to `link`, so we do handle those.
		// What we’ll do is split `node` over each of its children.
		if ((node.type === "delete" || node.type === "link") && wrapNeeded(node.children)) {
			flattened.push(...split(node));
		} else {
			flattened.push(node);
		}
	}

	return flattened;
}

/**
 * Check if an mdast node is phrasing.
 *
 * Also supports checking embedded hast fields.
 *
 * @param {Nodes} node
 *   mdast node to check.
 * @returns {node is PhrasingContent}
 *   Whether `node` is phrasing content (includes nodes with `hName` fields
 *   set to phrasing hast element names).
 */
function phrasing(node) {
	const tagName = node.data && node.data.hName;
	return tagName ? phrasing$2({ type: "element", tagName, properties: {}, children: [] }) : phrasing$1(node);
}

/**
 * @template {Parents} ParentType
 *   Parent type.
 * @param {ParentType} node
 *   Node to clone.
 * @returns {ParentType}
 *   Cloned node, without children.
 */
function cloneWithoutChildren(node) {
	return structuredClone$1({ ...node, children: [] });
}

/**
 * @import {State} from 'hast-util-to-mdast'
 * @import {Element} from 'hast'
 * @import {Image, Link, PhrasingContent, RootContent as MdastRootContent, Root} from 'mdast'
 */

/**
 * @param {State} state
 *   State.
 * @param {Readonly<Element>} node
 *   hast element to transform.
 * @returns {Array<MdastRootContent> | Link}
 *   mdast node.
 */
function media(state, node) {
	const properties = node.properties || {};
	const poster = node.tagName === "video" ? String(properties.poster || "") : "";
	let source = String(properties.src || "");
	let index = -1;
	let linkInFallbackContent = false;
	let nodes = state.all(node);

	/** @type {Root} */
	const fragment = { type: "root", children: nodes };

	visit(fragment, function (node) {
		if (node.type === "link") {
			linkInFallbackContent = true;
			return EXIT;
		}
	});

	// If the content links to something, or if it’s not phrasing…
	if (linkInFallbackContent || wrapNeeded(nodes)) {
		return nodes;
	}

	// Find the source.
	while (!source && ++index < node.children.length) {
		const child = node.children[index];

		if (child.type === "element" && child.tagName === "source" && child.properties) {
			source = String(child.properties.src || "");
		}
	}

	// If there’s a poster defined on the video, create an image.
	if (poster) {
		/** @type {Image} */
		const image = {
			type: "image",
			title: null,
			url: state.resolve(poster),
			alt: toString(nodes),
		};
		state.patch(node, image);
		nodes = [image];
	}

	// Allow potentially “invalid” nodes, they might be unknown.
	// We also support straddling later.
	const children = /** @type {Array<PhrasingContent>} */ (nodes);

	// Link to the media resource.
	/** @type {Link} */
	const result = {
		type: "link",
		title: properties.title ? String(properties.title) : null,
		url: state.resolve(source),
		children,
	};
	state.patch(node, result);
	return result;
}

/**
 * @import {State} from 'hast-util-to-mdast'
 * @import {Element} from 'hast'
 * @import {Paragraph, PhrasingContent} from 'mdast'
 */

/**
 * @param {State} state
 *   State.
 * @param {Readonly<Element>} node
 *   hast element to transform.
 * @returns {Paragraph | undefined}
 *   mdast node.
 */
function p$1(state, node) {
	const children = dropSurroundingBreaks(
		// Allow potentially “invalid” nodes, they might be unknown.
		// We also support straddling later.
		/** @type {Array<PhrasingContent>} */ (state.all(node)),
	);

	if (children.length > 0) {
		/** @type {Paragraph} */
		const result = { type: "paragraph", children };
		state.patch(node, result);
		return result;
	}
}

/**
 * @import {State} from 'hast-util-to-mdast'
 * @import {Element} from 'hast'
 * @import {RootContent as MdastRootContent} from 'mdast'
 */

const defaultQuotes = ['"'];

/**
 * @param {State} state
 *   State.
 * @param {Readonly<Element>} node
 *   hast element to transform.
 * @returns {Array<MdastRootContent>}
 *   mdast nodes.
 */
function q(state, node) {
	const quotes = state.options.quotes || defaultQuotes;

	state.qNesting++;
	const contents = state.all(node);
	state.qNesting--;

	const quote = quotes[state.qNesting % quotes.length];
	const head = contents[0];
	const tail = contents[contents.length - 1];
	const open = quote.charAt(0);
	const close = quote.length > 1 ? quote.charAt(1) : quote;

	if (head && head.type === "text") {
		head.value = open + head.value;
	} else {
		contents.unshift({ type: "text", value: open });
	}

	if (tail && tail.type === "text") {
		tail.value += close;
	} else {
		contents.push({ type: "text", value: close });
	}

	return contents;
}

/**
 * @import {State} from 'hast-util-to-mdast'
 * @import {Root as HastRoot} from 'hast'
 * @import {Root as MdastRoot} from 'mdast'
 */

/**
 * @param {State} state
 *   State.
 * @param {Readonly<HastRoot>} node
 *   hast root to transform.
 * @returns {MdastRoot}
 *   mdast node.
 */
function root$4(state, node) {
	let children = state.all(node);

	if (state.options.document || wrapNeeded(children)) {
		children = wrap$2(children);
	}

	/** @type {MdastRoot} */
	const result = { type: "root", children };
	state.patch(node, result);
	return result;
}

/**
 * @import {State} from 'hast-util-to-mdast'
 * @import {Element} from 'hast'
 * @import {Text} from 'mdast'
 */

/**
 * @param {State} state
 *   State.
 * @param {Readonly<Element>} node
 *   hast element to transform.
 * @returns {Text | undefined}
 *   mdast node.
 */
function select(state, node) {
	const values = findSelectedOptions(node);
	let index = -1;
	/** @type {Array<string>} */
	const results = [];

	while (++index < values.length) {
		const value = values[index];
		results.push(value[1] ? value[1] + " (" + value[0] + ")" : value[0]);
	}

	if (results.length > 0) {
		/** @type {Text} */
		const result = { type: "text", value: results.join(", ") };
		state.patch(node, result);
		return result;
	}
}

/**
 * @import {State} from 'hast-util-to-mdast'
 * @import {Element} from 'hast'
 * @import {PhrasingContent, Strong} from 'mdast'
 */

/**
 * @param {State} state
 *   State.
 * @param {Readonly<Element>} node
 *   hast element to transform.
 * @returns {Strong}
 *   mdast node.
 */
function strong$2(state, node) {
	// Allow potentially “invalid” nodes, they might be unknown.
	// We also support straddling later.
	const children = /** @type {Array<PhrasingContent>} */ (state.all(node));

	/** @type {Strong} */
	const result = { type: "strong", children };
	state.patch(node, result);
	return result;
}

/**
 * @import {State} from 'hast-util-to-mdast'
 * @import {Element} from 'hast'
 * @import {PhrasingContent, TableCell} from 'mdast'
 */

/**
 * @param {State} state
 *   State.
 * @param {Readonly<Element>} node
 *   hast element to transform.
 * @returns {TableCell}
 *   mdast node.
 */
function tableCell$1(state, node) {
	// Allow potentially “invalid” nodes, they might be unknown.
	// We also support straddling later.
	const children = /** @type {Array<PhrasingContent>} */ (state.all(node));

	/** @type {TableCell} */
	const result = { type: "tableCell", children };
	state.patch(node, result);

	if (node.properties) {
		const rowSpan = node.properties.rowSpan;
		const colSpan = node.properties.colSpan;

		if (rowSpan || colSpan) {
			const data = /** @type {Record<string, unknown>} */ (result.data || (result.data = {}));
			if (rowSpan) data.hastUtilToMdastTemporaryRowSpan = rowSpan;
			if (colSpan) data.hastUtilToMdastTemporaryColSpan = colSpan;
		}
	}

	return result;
}

/**
 * @import {State} from 'hast-util-to-mdast'
 * @import {Element} from 'hast'
 * @import {RowContent, TableRow} from 'mdast'
 */

/**
 * @param {State} state
 *   State.
 * @param {Readonly<Element>} node
 *   hast element to transform.
 * @returns {TableRow}
 *   mdast node.
 */
function tableRow$1(state, node) {
	const children = state.toSpecificContent(state.all(node), create);

	/** @type {TableRow} */
	const result = { type: "tableRow", children };
	state.patch(node, result);
	return result;
}

/**
 * @returns {RowContent}
 */
function create() {
	return { type: "tableCell", children: [] };
}

/**
 * @import {State} from 'hast-util-to-mdast'
 * @import {Element} from 'hast'
 * @import {AlignType, RowContent, TableContent, Table, Text} from 'mdast'
 */

/**
 * @param {State} state
 *   State.
 * @param {Readonly<Element>} node
 *   hast element to transform.
 * @returns {Table | Text}
 *   mdast node.
 */
// eslint-disable-next-line complexity
function table$1(state, node) {
	// Ignore nested tables.
	if (state.inTable) {
		/** @type {Text} */
		const result = { type: "text", value: toText(node) };
		state.patch(node, result);
		return result;
	}

	state.inTable = true;

	const { align, headless } = inspect(node);
	const rows = state.toSpecificContent(state.all(node), createRow);

	// Add an empty header row.
	if (headless) {
		rows.unshift(createRow());
	}

	let rowIndex = -1;

	while (++rowIndex < rows.length) {
		const row = rows[rowIndex];
		const cells = state.toSpecificContent(row.children, createCell);
		row.children = cells;
	}

	let columns = 1;
	rowIndex = -1;

	while (++rowIndex < rows.length) {
		const cells = rows[rowIndex].children;
		let cellIndex = -1;

		while (++cellIndex < cells.length) {
			const cell = cells[cellIndex];

			if (cell.data) {
				const data = /** @type {Record<string, unknown>} */ (cell.data);
				const colSpan = Number.parseInt(String(data.hastUtilToMdastTemporaryColSpan), 10) || 1;
				const rowSpan = Number.parseInt(String(data.hastUtilToMdastTemporaryRowSpan), 10) || 1;

				if (colSpan > 1 || rowSpan > 1) {
					let otherRowIndex = rowIndex - 1;

					while (++otherRowIndex < rowIndex + rowSpan) {
						let colIndex = cellIndex - 1;

						while (++colIndex < cellIndex + colSpan) {
							if (!rows[otherRowIndex]) {
								// Don’t add rows that don’t exist.
								// Browsers don’t render them either.
								break;
							}

							/** @type {Array<RowContent>} */
							const newCells = [];

							if (otherRowIndex !== rowIndex || colIndex !== cellIndex) {
								newCells.push({ type: "tableCell", children: [] });
							}

							rows[otherRowIndex].children.splice(colIndex, 0, ...newCells);
						}
					}
				}

				// Clean the data fields.
				if ("hastUtilToMdastTemporaryColSpan" in cell.data) delete cell.data.hastUtilToMdastTemporaryColSpan;
				if ("hastUtilToMdastTemporaryRowSpan" in cell.data) delete cell.data.hastUtilToMdastTemporaryRowSpan;
				if (Object.keys(cell.data).length === 0) delete cell.data;
			}
		}

		if (cells.length > columns) columns = cells.length;
	}

	// Add extra empty cells.
	rowIndex = -1;

	while (++rowIndex < rows.length) {
		const cells = rows[rowIndex].children;
		let cellIndex = cells.length - 1;
		while (++cellIndex < columns) {
			cells.push({ type: "tableCell", children: [] });
		}
	}

	let alignIndex = align.length - 1;
	while (++alignIndex < columns) {
		align.push(null);
	}

	state.inTable = false;

	/** @type {Table} */
	const result = { type: "table", align, children: rows };
	state.patch(node, result);
	return result;
}

/**
 * Infer whether the HTML table has a head and how it aligns.
 *
 * @param {Readonly<Element>} node
 *   Table element to check.
 * @returns {Info}
 *   Info.
 */
function inspect(node) {
	/** @type {Info} */
	const info = { align: [null], headless: true };
	let rowIndex = 0;
	let cellIndex = 0;

	visit(node, function (child) {
		if (child.type === "element") {
			// Don’t enter nested tables.
			if (child.tagName === "table" && node !== child) {
				return SKIP;
			}

			if ((child.tagName === "th" || child.tagName === "td") && child.properties) {
				if (!info.align[cellIndex]) {
					const value = String(child.properties.align || "") || null;

					if (value === "center" || value === "left" || value === "right" || value === null) {
						info.align[cellIndex] = value;
					}
				}

				// If there is a `th` in the first row, assume there is a header row.
				if (info.headless && rowIndex < 2 && child.tagName === "th") {
					info.headless = false;
				}

				cellIndex++;
			}
			// If there is a `thead`, assume there is a header row.
			else if (child.tagName === "thead") {
				info.headless = false;
			} else if (child.tagName === "tr") {
				rowIndex++;
				cellIndex = 0;
			}
		}
	});

	return info;
}

/**
 * @returns {RowContent}
 */
function createCell() {
	return { type: "tableCell", children: [] };
}

/**
 * @returns {TableContent}
 */
function createRow() {
	return { type: "tableRow", children: [] };
}

/**
 * @import {State} from 'hast-util-to-mdast'
 * @import {Text as HastText} from 'hast'
 * @import {Text as MdastText} from 'mdast'
 */

/**
 * @param {State} state
 *   State.
 * @param {Readonly<HastText>} node
 *   hast element to transform.
 * @returns {MdastText}
 *   mdast node.
 */
function text$7(state, node) {
	/** @type {MdastText} */
	const result = { type: "text", value: node.value };
	state.patch(node, result);
	return result;
}

/**
 * @import {State} from 'hast-util-to-mdast'
 * @import {Element} from 'hast'
 * @import {Text} from 'mdast'
 */

/**
 * @param {State} state
 *   State.
 * @param {Readonly<Element>} node
 *   hast element to transform.
 * @returns {Text}
 *   mdast node.
 */
function textarea(state, node) {
	/** @type {Text} */
	const result = { type: "text", value: toText(node) };
	state.patch(node, result);
	return result;
}

/**
 * @import {State} from 'hast-util-to-mdast'
 * @import {Element} from 'hast'
 * @import {Text} from 'mdast'
 */

/**
 * @param {State} state
 *   State.
 * @param {Readonly<Element>} node
 *   hast element to transform.
 * @returns {Text}
 *   mdast node.
 */
function wbr(state, node) {
	/** @type {Text} */
	const result = { type: "text", value: "\u200B" };
	state.patch(node, result);
	return result;
}

/**
 * @import {State} from 'hast-util-to-mdast'
 * @import {Parents} from 'hast'
 */

/**
 * Default handlers for nodes.
 *
 * Each key is a node type, each value is a `NodeHandler`.
 */
const nodeHandlers = {
	comment: comment$2,
	doctype: ignore$1,
	root: root$4,
	text: text$7,
};

/**
 * Default handlers for elements.
 *
 * Each key is an element name, each value is a `Handler`.
 */
const handlers$1 = {
	// Ignore:
	applet: ignore$1,
	area: ignore$1,
	basefont: ignore$1,
	bgsound: ignore$1,
	caption: ignore$1,
	col: ignore$1,
	colgroup: ignore$1,
	command: ignore$1,
	content: ignore$1,
	datalist: ignore$1,
	dialog: ignore$1,
	element: ignore$1,
	embed: ignore$1,
	frame: ignore$1,
	frameset: ignore$1,
	isindex: ignore$1,
	keygen: ignore$1,
	link: ignore$1,
	math: ignore$1,
	menu: ignore$1,
	menuitem: ignore$1,
	meta: ignore$1,
	nextid: ignore$1,
	noembed: ignore$1,
	noframes: ignore$1,
	optgroup: ignore$1,
	option: ignore$1,
	param: ignore$1,
	script: ignore$1,
	shadow: ignore$1,
	source: ignore$1,
	spacer: ignore$1,
	style: ignore$1,
	svg: ignore$1,
	template: ignore$1,
	title: ignore$1,
	track: ignore$1,

	// Use children:
	abbr: all$3,
	acronym: all$3,
	bdi: all$3,
	bdo: all$3,
	big: all$3,
	blink: all$3,
	button: all$3,
	canvas: all$3,
	cite: all$3,
	data: all$3,
	details: all$3,
	dfn: all$3,
	font: all$3,
	ins: all$3,
	label: all$3,
	map: all$3,
	marquee: all$3,
	meter: all$3,
	nobr: all$3,
	noscript: all$3,
	object: all$3,
	output: all$3,
	progress: all$3,
	rb: all$3,
	rbc: all$3,
	rp: all$3,
	rt: all$3,
	rtc: all$3,
	ruby: all$3,
	slot: all$3,
	small: all$3,
	span: all$3,
	sup: all$3,
	sub: all$3,
	tbody: all$3,
	tfoot: all$3,
	thead: all$3,
	time: all$3,

	// Use children as flow.
	address: flow$2,
	article: flow$2,
	aside: flow$2,
	body: flow$2,
	center: flow$2,
	div: flow$2,
	fieldset: flow$2,
	figcaption: flow$2,
	figure: flow$2,
	form: flow$2,
	footer: flow$2,
	header: flow$2,
	hgroup: flow$2,
	html: flow$2,
	legend: flow$2,
	main: flow$2,
	multicol: flow$2,
	nav: flow$2,
	picture: flow$2,
	section: flow$2,

	// Handle.
	a,
	audio: media,
	b: strong$2,
	base,
	blockquote: blockquote$2,
	br: br$1,
	code: inlineCode$2,
	dir: list$4,
	dl,
	dt: li$1,
	dd: li$1,
	del,
	em,
	h1: heading$2,
	h2: heading$2,
	h3: heading$2,
	h4: heading$2,
	h5: heading$2,
	h6: heading$2,
	hr,
	i: em,
	iframe,
	img,
	image: img,
	input,
	kbd: inlineCode$2,
	li: li$1,
	listing: code$3,
	mark: em,
	ol: list$4,
	p: p$1,
	plaintext: code$3,
	pre: code$3,
	q,
	s: del,
	samp: inlineCode$2,
	select,
	strike: del,
	strong: strong$2,
	summary: p$1,
	table: table$1,
	td: tableCell$1,
	textarea,
	th: tableCell$1,
	tr: tableRow$1,
	tt: inlineCode$2,
	u: em,
	ul: list$4,
	var: inlineCode$2,
	video: media,
	wbr,
	xmp: code$3,
};

/**
 * @param {State} state
 *   State.
 * @param {Parents} node
 *   Parent to transform.
 */
function all$3(state, node) {
	return state.all(node);
}

/**
 * @param {State} state
 *   State.
 * @param {Parents} node
 *   Parent to transform.
 */
function flow$2(state, node) {
	return state.toFlow(state.all(node));
}

/**
 * @returns {undefined}
 */
function ignore$1() {}

/**
 * @import {Element, Nodes, Parents} from 'hast'
 * @import {
 *   BlockContent as MdastBlockContent,
 *   DefinitionContent as MdastDefinitionContent,
 *   Nodes as MdastNodes,
 *   Parents as MdastParents,
 *   RootContent as MdastRootContent
 * } from 'mdast'
 */

const own$7 = {}.hasOwnProperty;

/**
 * Create a state.
 *
 * @param {Readonly<Options>} options
 *   User configuration.
 * @returns {State}
 *   State.
 */
function createState$1(options) {
	return {
		all: all$2,
		baseFound: false,
		elementById: new Map(),
		frozenBaseUrl: undefined,
		handlers: { ...handlers$1, ...options.handlers },
		inTable: false,
		nodeHandlers: { ...nodeHandlers, ...options.nodeHandlers },
		one: one$2,
		options,
		patch: patch$1,
		qNesting: 0,
		resolve,
		toFlow,
		toSpecificContent,
	};
}

/**
 * Transform the children of a hast parent to mdast.
 *
 * You might want to combine this with `toFlow` or `toSpecificContent`.
 *
 * @this {State}
 *   Info passed around about the current state.
 * @param {Parents} parent
 *   Parent.
 * @returns {Array<MdastRootContent>}
 *   mdast children.
 */
function all$2(parent) {
	const children = parent.children || [];
	/** @type {Array<MdastRootContent>} */
	const results = [];
	let index = -1;

	while (++index < children.length) {
		const child = children[index];
		// Content -> content.
		const result = /** @type {Array<MdastRootContent> | MdastRootContent | undefined} */ (this.one(child, parent));

		if (Array.isArray(result)) {
			results.push(...result);
		} else if (result) {
			results.push(result);
		}
	}

	return results;
}

/**
 * Transform a hast node to mdast.
 *
 * @this {State}
 *   Info passed around about the current state.
 * @param {Nodes} node
 *   hast node to transform.
 * @param {Parents | undefined} parent
 *   Parent of `node`.
 * @returns {Array<MdastNodes> | MdastNodes | undefined}
 *   mdast result.
 */
function one$2(node, parent) {
	if (node.type === "element") {
		if (node.properties && node.properties.dataMdast === "ignore") {
			return;
		}

		if (own$7.call(this.handlers, node.tagName)) {
			return this.handlers[node.tagName](this, node, parent) || undefined;
		}
	} else if (own$7.call(this.nodeHandlers, node.type)) {
		return this.nodeHandlers[node.type](this, node, parent) || undefined;
	}

	// Unknown literal.
	if ("value" in node && typeof node.value === "string") {
		/** @type {MdastRootContent} */
		const result = { type: "text", value: node.value };
		this.patch(node, result);
		return result;
	}

	// Unknown parent.
	if ("children" in node) {
		return this.all(node);
	}
}

/**
 * Copy a node’s positional info.
 *
 * @param {Nodes} origin
 *   hast node to copy from.
 * @param {MdastNodes} node
 *   mdast node to copy into.
 * @returns {undefined}
 *   Nothing.
 */
function patch$1(origin, node) {
	if (origin.position) node.position = position(origin);
}

/**
 * @this {State}
 *   Info passed around about the current state.
 * @param {string | null | undefined} url
 *   Possible URL value.
 * @returns {string}
 *   URL, resolved to a `base` element, if any.
 */
function resolve(url) {
	const base = this.frozenBaseUrl;

	if (url === null || url === undefined) {
		return "";
	}

	if (base) {
		return String(new URL(url, base));
	}

	return url;
}

/**
 * Transform a list of mdast nodes to flow.
 *
 * @this {State}
 *   Info passed around about the current state.
 * @param {Array<MdastRootContent>} nodes
 *   Parent.
 * @returns {Array<MdastFlowContent>}
 *   mdast flow children.
 */
function toFlow(nodes) {
	return wrap$2(nodes);
}

/**
 * Turn arbitrary content into a particular node type.
 *
 * This is useful for example for lists, which must have list items as content.
 * in this example, when non-items are found, they will be queued, and
 * inserted into an adjacent item.
 * When no actual items exist, one will be made with `build`.
 *
 * @template {MdastNodes} ChildType
 *   Node type of children.
 * @template {MdastParents & {'children': Array<ChildType>}} ParentType
 *   Node type of parent.
 * @param {Array<MdastRootContent>} nodes
 *   Nodes, which are either `ParentType`, or will be wrapped in one.
 * @param {() => ParentType} build
 *   Build a parent if needed (must have empty `children`).
 * @returns {Array<ParentType>}
 *   List of parents.
 */
function toSpecificContent(nodes, build) {
	const reference = build();
	/** @type {Array<ParentType>} */
	const results = [];
	/** @type {Array<ChildType>} */
	let queue = [];
	let index = -1;

	while (++index < nodes.length) {
		const node = nodes[index];

		if (expectedParent(node)) {
			if (queue.length > 0) {
				node.children.unshift(...queue);
				queue = [];
			}

			results.push(node);
		} else {
			// Assume `node` can be a child of `ParentType`.
			// If we start checking nodes, we’d run into problems with unknown nodes,
			// which we do want to support.
			const child = /** @type {ChildType} */ (node);
			queue.push(child);
		}
	}

	if (queue.length > 0) {
		let node = results[results.length - 1];

		if (!node) {
			node = build();
			results.push(node);
		}

		node.children.push(...queue);
		queue = [];
	}

	return results;

	/**
	 * @param {MdastNodes} node
	 * @returns {node is ParentType}
	 */
	function expectedParent(node) {
		return node.type === reference.type;
	}
}

/**
 * @import {Options} from 'hast-util-to-mdast'
 * @import {Nodes} from 'hast'
 * @import {Nodes as MdastNodes, RootContent as MdastRootContent} from 'mdast'
 */

/** @type {Readonly<Options>} */
const emptyOptions$3 = {};

/**
 * Transform hast to mdast.
 *
 * @param {Readonly<Nodes>} tree
 *   hast tree to transform.
 * @param {Readonly<Options> | null | undefined} [options]
 *   Configuration (optional).
 * @returns {MdastNodes}
 *   mdast tree.
 */
function toMdast(tree, options) {
	// We have to clone, cause we’ll use `rehype-minify-whitespace` on the tree,
	// which modifies.
	const cleanTree = structuredClone$1(tree);
	const settings = options || emptyOptions$3;
	const transformWhitespace = rehypeMinifyWhitespace({
		newlines: settings.newlines === true,
	});
	const state = createState$1(settings);
	/** @type {MdastNodes} */
	let mdast;

	// @ts-expect-error: fine to pass an arbitrary node.
	transformWhitespace(cleanTree);

	visit(cleanTree, function (node) {
		if (node && node.type === "element" && node.properties) {
			const id = String(node.properties.id || "") || undefined;

			if (id && !state.elementById.has(id)) {
				state.elementById.set(id, node);
			}
		}
	});

	const result = state.one(cleanTree, undefined);

	if (!result) {
		mdast = { type: "root", children: [] };
	} else if (Array.isArray(result)) {
		// Assume content.
		const children = /** @type {Array<MdastRootContent>} */ (result);
		mdast = { type: "root", children };
	} else {
		mdast = result;
	}

	// Collapse text nodes, and fix whitespace.
	//
	// Most of this is taken care of by `rehype-minify-whitespace`, but
	// we’re generating some whitespace too, and some nodes are in the end
	// ignored.
	// So clean up.
	visit(mdast, function (node, index, parent) {
		if (node.type === "text" && index !== undefined && parent) {
			const previous = parent.children[index - 1];

			if (previous && previous.type === node.type) {
				previous.value += node.value;
				parent.children.splice(index, 1);

				if (previous.position && node.position) {
					previous.position.end = node.position.end;
				}

				// Iterate over the previous node again, to handle its total value.
				return index - 1;
			}

			node.value = node.value.replace(/[\t ]*(\r?\n|\r)[\t ]*/, "$1");

			// We don’t care about other phrasing nodes in between (e.g., `[ asd ]()`),
			// as there the whitespace matters.
			if (parent && (parent.type === "heading" || parent.type === "paragraph" || parent.type === "root")) {
				if (!index) {
					node.value = node.value.replace(/^[\t ]+/, "");
				}

				if (index === parent.children.length - 1) {
					node.value = node.value.replace(/[\t ]+$/, "");
				}
			}

			if (!node.value) {
				parent.children.splice(index, 1);
				return index;
			}
		}
	});

	return mdast;
}

/**
 * @import {Root as HastRoot} from 'hast'
 * @import {Options} from 'hast-util-to-mdast'
 * @import {Root as MdastRoot} from 'mdast'
 * @import {Processor} from 'unified'
 * @import {VFile} from 'vfile'
 */

/** @satisfies {Options} */
const defaults = { document: true };

/**
 * Turn HTML into markdown.
 *
 * ###### Notes
 *
 * *   if a processor is given, runs the (remark) plugins used on it with an
 *     mdast tree, then discards the result (*bridge mode*)
 * *   otherwise, returns an mdast tree, the plugins used after `rehypeRemark`
 *     are remark plugins (*mutate mode*)
 *
 * > 👉 **Note**: It’s highly unlikely that you want to pass a `processor`.
 *
 * @overload
 * @param {Processor} processor
 * @param {Options | null | undefined} [options]
 * @returns {TransformBridge}
 *
 * @overload
 * @param {Options | null | undefined} [options]
 * @returns {TransformMutate}
 *
 * @overload
 * @param {Options | Processor | null | undefined} [destination]
 * @param {Options | null | undefined} [options]
 * @returns {TransformBridge | TransformMutate}
 *
 * @param {Options | Processor | null | undefined} [destination]
 *   Processor or configuration (optional).
 * @param {Options | null | undefined} [options]
 *   When a processor was given, configuration (optional).
 * @returns {TransformBridge | TransformMutate}
 *   Transform.
 */
function rehypeRemark(destination, options) {
	if (destination && "run" in destination) {
		/**
		 * @type {TransformBridge}
		 */
		return async function (tree, file) {
			const mdastTree = toMdast(tree, { ...defaults, ...options });
			await destination.run(mdastTree, file);
		};
	}

	/**
	 * @type {TransformMutate}
	 */
	return function (tree) {
		return /** @type {MdastRoot} */ (toMdast(tree, { ...defaults, ...destination }));
	};
}

/**
 * Count how often a character (or substring) is used in a string.
 *
 * @param {string} value
 *   Value to search in.
 * @param {string} character
 *   Character (or substring) to look for.
 * @return {number}
 *   Number of times `character` occurred in `value`.
 */
function ccount(value, character) {
	const source = String(value);

	if (typeof character !== "string") {
		throw new TypeError("Expected character");
	}

	let count = 0;
	let index = source.indexOf(character);

	while (index !== -1) {
		count++;
		index = source.indexOf(character, index + character.length);
	}

	return count;
}

/**
 * @import {Code} from 'micromark-util-types'
 */

/**
 * Check whether the character code represents an ASCII alpha (`a` through `z`,
 * case insensitive).
 *
 * An **ASCII alpha** is an ASCII upper alpha or ASCII lower alpha.
 *
 * An **ASCII upper alpha** is a character in the inclusive range U+0041 (`A`)
 * to U+005A (`Z`).
 *
 * An **ASCII lower alpha** is a character in the inclusive range U+0061 (`a`)
 * to U+007A (`z`).
 *
 * @param code
 *   Code.
 * @returns {boolean}
 *   Whether it matches.
 */
const asciiAlpha = regexCheck(/[A-Za-z]/);

/**
 * Check whether the character code represents an ASCII alphanumeric (`a`
 * through `z`, case insensitive, or `0` through `9`).
 *
 * An **ASCII alphanumeric** is an ASCII digit (see `asciiDigit`) or ASCII alpha
 * (see `asciiAlpha`).
 *
 * @param code
 *   Code.
 * @returns {boolean}
 *   Whether it matches.
 */
const asciiAlphanumeric = regexCheck(/[\dA-Za-z]/);

/**
 * Check whether the character code represents an ASCII atext.
 *
 * atext is an ASCII alphanumeric (see `asciiAlphanumeric`), or a character in
 * the inclusive ranges U+0023 NUMBER SIGN (`#`) to U+0027 APOSTROPHE (`'`),
 * U+002A ASTERISK (`*`), U+002B PLUS SIGN (`+`), U+002D DASH (`-`), U+002F
 * SLASH (`/`), U+003D EQUALS TO (`=`), U+003F QUESTION MARK (`?`), U+005E
 * CARET (`^`) to U+0060 GRAVE ACCENT (`` ` ``), or U+007B LEFT CURLY BRACE
 * (`{`) to U+007E TILDE (`~`).
 *
 * See:
 * **\[RFC5322]**:
 * [Internet Message Format](https://tools.ietf.org/html/rfc5322).
 * P. Resnick.
 * IETF.
 *
 * @param code
 *   Code.
 * @returns {boolean}
 *   Whether it matches.
 */
const asciiAtext = regexCheck(/[#-'*+\--9=?A-Z^-~]/);

/**
 * Check whether a character code is an ASCII control character.
 *
 * An **ASCII control** is a character in the inclusive range U+0000 NULL (NUL)
 * to U+001F (US), or U+007F (DEL).
 *
 * @param {Code} code
 *   Code.
 * @returns {boolean}
 *   Whether it matches.
 */
function asciiControl(code) {
	return (
		// Special whitespace codes (which have negative values), C0 and Control
		// character DEL
		code !== null && (code < 32 || code === 127)
	);
}

/**
 * Check whether the character code represents an ASCII digit (`0` through `9`).
 *
 * An **ASCII digit** is a character in the inclusive range U+0030 (`0`) to
 * U+0039 (`9`).
 *
 * @param code
 *   Code.
 * @returns {boolean}
 *   Whether it matches.
 */
const asciiDigit = regexCheck(/\d/);

/**
 * Check whether the character code represents an ASCII hex digit (`a` through
 * `f`, case insensitive, or `0` through `9`).
 *
 * An **ASCII hex digit** is an ASCII digit (see `asciiDigit`), ASCII upper hex
 * digit, or an ASCII lower hex digit.
 *
 * An **ASCII upper hex digit** is a character in the inclusive range U+0041
 * (`A`) to U+0046 (`F`).
 *
 * An **ASCII lower hex digit** is a character in the inclusive range U+0061
 * (`a`) to U+0066 (`f`).
 *
 * @param code
 *   Code.
 * @returns {boolean}
 *   Whether it matches.
 */
const asciiHexDigit = regexCheck(/[\dA-Fa-f]/);

/**
 * Check whether the character code represents ASCII punctuation.
 *
 * An **ASCII punctuation** is a character in the inclusive ranges U+0021
 * EXCLAMATION MARK (`!`) to U+002F SLASH (`/`), U+003A COLON (`:`) to U+0040 AT
 * SIGN (`@`), U+005B LEFT SQUARE BRACKET (`[`) to U+0060 GRAVE ACCENT
 * (`` ` ``), or U+007B LEFT CURLY BRACE (`{`) to U+007E TILDE (`~`).
 *
 * @param code
 *   Code.
 * @returns {boolean}
 *   Whether it matches.
 */
const asciiPunctuation = regexCheck(/[!-/:-@[-`{-~]/);

/**
 * Check whether a character code is a markdown line ending.
 *
 * A **markdown line ending** is the virtual characters M-0003 CARRIAGE RETURN
 * LINE FEED (CRLF), M-0004 LINE FEED (LF) and M-0005 CARRIAGE RETURN (CR).
 *
 * In micromark, the actual character U+000A LINE FEED (LF) and U+000D CARRIAGE
 * RETURN (CR) are replaced by these virtual characters depending on whether
 * they occurred together.
 *
 * @param {Code} code
 *   Code.
 * @returns {boolean}
 *   Whether it matches.
 */
function markdownLineEnding(code) {
	return code !== null && code < -2;
}

/**
 * Check whether a character code is a markdown line ending (see
 * `markdownLineEnding`) or markdown space (see `markdownSpace`).
 *
 * @param {Code} code
 *   Code.
 * @returns {boolean}
 *   Whether it matches.
 */
function markdownLineEndingOrSpace(code) {
	return code !== null && (code < 0 || code === 32);
}

/**
 * Check whether a character code is a markdown space.
 *
 * A **markdown space** is the concrete character U+0020 SPACE (SP) and the
 * virtual characters M-0001 VIRTUAL SPACE (VS) and M-0002 HORIZONTAL TAB (HT).
 *
 * In micromark, the actual character U+0009 CHARACTER TABULATION (HT) is
 * replaced by one M-0002 HORIZONTAL TAB (HT) and between 0 and 3 M-0001 VIRTUAL
 * SPACE (VS) characters, depending on the column at which the tab occurred.
 *
 * @param {Code} code
 *   Code.
 * @returns {boolean}
 *   Whether it matches.
 */
function markdownSpace(code) {
	return code === -2 || code === -1 || code === 32;
}

// Size note: removing ASCII from the regex and using `asciiPunctuation` here
// In fact adds to the bundle size.
/**
 * Check whether the character code represents Unicode punctuation.
 *
 * A **Unicode punctuation** is a character in the Unicode `Pc` (Punctuation,
 * Connector), `Pd` (Punctuation, Dash), `Pe` (Punctuation, Close), `Pf`
 * (Punctuation, Final quote), `Pi` (Punctuation, Initial quote), `Po`
 * (Punctuation, Other), or `Ps` (Punctuation, Open) categories, or an ASCII
 * punctuation (see `asciiPunctuation`).
 *
 * See:
 * **\[UNICODE]**:
 * [The Unicode Standard](https://www.unicode.org/versions/).
 * Unicode Consortium.
 *
 * @param code
 *   Code.
 * @returns
 *   Whether it matches.
 */
const unicodePunctuation = regexCheck(/\p{P}|\p{S}/u);

/**
 * Check whether the character code represents Unicode whitespace.
 *
 * Note that this does handle micromark specific markdown whitespace characters.
 * See `markdownLineEndingOrSpace` to check that.
 *
 * A **Unicode whitespace** is a character in the Unicode `Zs` (Separator,
 * Space) category, or U+0009 CHARACTER TABULATION (HT), U+000A LINE FEED (LF),
 * U+000C (FF), or U+000D CARRIAGE RETURN (CR) (**\[UNICODE]**).
 *
 * See:
 * **\[UNICODE]**:
 * [The Unicode Standard](https://www.unicode.org/versions/).
 * Unicode Consortium.
 *
 * @param code
 *   Code.
 * @returns
 *   Whether it matches.
 */
const unicodeWhitespace = regexCheck(/\s/);

/**
 * Create a code check from a regex.
 *
 * @param {RegExp} regex
 *   Expression.
 * @returns {(code: Code) => boolean}
 *   Check.
 */
function regexCheck(regex) {
	return check;

	/**
	 * Check whether a code matches the bound regex.
	 *
	 * @param {Code} code
	 *   Character code.
	 * @returns {boolean}
	 *   Whether the character code matches the bound regex.
	 */
	function check(code) {
		return code !== null && code > -1 && regex.test(String.fromCharCode(code));
	}
}

function escapeStringRegexp(string) {
	if (typeof string !== "string") {
		throw new TypeError("Expected a string");
	}

	// Escape characters with special meaning either inside or outside character sets.
	// Use a simple backslash escape when it’s always valid, and a `\xnn` escape when the simpler form would be disallowed by Unicode patterns’ stricter grammar.
	return string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d");
}

/**
 * @import {Nodes, Parents, PhrasingContent, Root, Text} from 'mdast'
 * @import {BuildVisitor, Test, VisitorResult} from 'unist-util-visit-parents'
 */

/**
 * Find patterns in a tree and replace them.
 *
 * The algorithm searches the tree in *preorder* for complete values in `Text`
 * nodes.
 * Partial matches are not supported.
 *
 * @param {Nodes} tree
 *   Tree to change.
 * @param {FindAndReplaceList | FindAndReplaceTuple} list
 *   Patterns to find.
 * @param {Options | null | undefined} [options]
 *   Configuration (when `find` is not `Find`).
 * @returns {undefined}
 *   Nothing.
 */
function findAndReplace(tree, list, options) {
	const settings = options || {};
	const ignored = convert(settings.ignore || []);
	const pairs = toPairs(list);
	let pairIndex = -1;

	while (++pairIndex < pairs.length) {
		visitParents(tree, "text", visitor);
	}

	/** @type {BuildVisitor<Root, 'text'>} */
	function visitor(node, parents) {
		let index = -1;
		/** @type {Parents | undefined} */
		let grandparent;

		while (++index < parents.length) {
			const parent = parents[index];
			/** @type {Array<Nodes> | undefined} */
			const siblings = grandparent ? grandparent.children : undefined;

			if (ignored(parent, siblings ? siblings.indexOf(parent) : undefined, grandparent)) {
				return;
			}

			grandparent = parent;
		}

		if (grandparent) {
			return handler(node, parents);
		}
	}

	/**
	 * Handle a text node which is not in an ignored parent.
	 *
	 * @param {Text} node
	 *   Text node.
	 * @param {Array<Parents>} parents
	 *   Parents.
	 * @returns {VisitorResult}
	 *   Result.
	 */
	function handler(node, parents) {
		const parent = parents[parents.length - 1];
		const find = pairs[pairIndex][0];
		const replace = pairs[pairIndex][1];
		let start = 0;
		/** @type {Array<Nodes>} */
		const siblings = parent.children;
		const index = siblings.indexOf(node);
		let change = false;
		/** @type {Array<PhrasingContent>} */
		let nodes = [];

		find.lastIndex = 0;

		let match = find.exec(node.value);

		while (match) {
			const position = match.index;
			/** @type {RegExpMatchObject} */
			const matchObject = {
				index: match.index,
				input: match.input,
				stack: [...parents, node],
			};
			let value = replace(...match, matchObject);

			if (typeof value === "string") {
				value = value.length > 0 ? { type: "text", value } : undefined;
			}

			// It wasn’t a match after all.
			if (value === false) {
				// False acts as if there was no match.
				// So we need to reset `lastIndex`, which currently being at the end of
				// the current match, to the beginning.
				find.lastIndex = position + 1;
			} else {
				if (start !== position) {
					nodes.push({
						type: "text",
						value: node.value.slice(start, position),
					});
				}

				if (Array.isArray(value)) {
					nodes.push(...value);
				} else if (value) {
					nodes.push(value);
				}

				start = position + match[0].length;
				change = true;
			}

			if (!find.global) {
				break;
			}

			match = find.exec(node.value);
		}

		if (change) {
			if (start < node.value.length) {
				nodes.push({ type: "text", value: node.value.slice(start) });
			}

			parent.children.splice(index, 1, ...nodes);
		} else {
			nodes = [node];
		}

		return index + nodes.length;
	}
}

/**
 * Turn a tuple or a list of tuples into pairs.
 *
 * @param {FindAndReplaceList | FindAndReplaceTuple} tupleOrList
 *   Schema.
 * @returns {Pairs}
 *   Clean pairs.
 */
function toPairs(tupleOrList) {
	/** @type {Pairs} */
	const result = [];

	if (!Array.isArray(tupleOrList)) {
		throw new TypeError("Expected find and replace tuple or list of tuples");
	}

	/** @type {FindAndReplaceList} */
	// @ts-expect-error: correct.
	const list = !tupleOrList[0] || Array.isArray(tupleOrList[0]) ? tupleOrList : [tupleOrList];

	let index = -1;

	while (++index < list.length) {
		const tuple = list[index];
		result.push([toExpression(tuple[0]), toFunction(tuple[1])]);
	}

	return result;
}

/**
 * Turn a find into an expression.
 *
 * @param {Find} find
 *   Find.
 * @returns {RegExp}
 *   Expression.
 */
function toExpression(find) {
	return typeof find === "string" ? new RegExp(escapeStringRegexp(find), "g") : find;
}

/**
 * Turn a replace into a function.
 *
 * @param {Replace} replace
 *   Replace.
 * @returns {ReplaceFunction}
 *   Function.
 */
function toFunction(replace) {
	return typeof replace === "function" ? replace : (
			function () {
				return replace;
			}
		);
}

/**
 * @import {RegExpMatchObject, ReplaceFunction} from 'mdast-util-find-and-replace'
 * @import {CompileContext, Extension as FromMarkdownExtension, Handle as FromMarkdownHandle, Transform as FromMarkdownTransform} from 'mdast-util-from-markdown'
 * @import {ConstructName, Options as ToMarkdownExtension} from 'mdast-util-to-markdown'
 * @import {Link, PhrasingContent} from 'mdast'
 */

/** @type {ConstructName} */
const inConstruct = "phrasing";
/** @type {Array<ConstructName>} */
const notInConstruct = ["autolink", "link", "image", "label"];

/**
 * Create an extension for `mdast-util-from-markdown` to enable GFM autolink
 * literals in markdown.
 *
 * @returns {FromMarkdownExtension}
 *   Extension for `mdast-util-to-markdown` to enable GFM autolink literals.
 */
function gfmAutolinkLiteralFromMarkdown() {
	return {
		transforms: [transformGfmAutolinkLiterals],
		enter: {
			literalAutolink: enterLiteralAutolink,
			literalAutolinkEmail: enterLiteralAutolinkValue,
			literalAutolinkHttp: enterLiteralAutolinkValue,
			literalAutolinkWww: enterLiteralAutolinkValue,
		},
		exit: {
			literalAutolink: exitLiteralAutolink,
			literalAutolinkEmail: exitLiteralAutolinkEmail,
			literalAutolinkHttp: exitLiteralAutolinkHttp,
			literalAutolinkWww: exitLiteralAutolinkWww,
		},
	};
}

/**
 * Create an extension for `mdast-util-to-markdown` to enable GFM autolink
 * literals in markdown.
 *
 * @returns {ToMarkdownExtension}
 *   Extension for `mdast-util-to-markdown` to enable GFM autolink literals.
 */
function gfmAutolinkLiteralToMarkdown() {
	return {
		unsafe: [
			{
				character: "@",
				before: "[+\\-.\\w]",
				after: "[\\-.\\w]",
				inConstruct,
				notInConstruct,
			},
			{
				character: ".",
				before: "[Ww]",
				after: "[\\-.\\w]",
				inConstruct,
				notInConstruct,
			},
			{
				character: ":",
				before: "[ps]",
				after: "\\/",
				inConstruct,
				notInConstruct,
			},
		],
	};
}

/**
 * @this {CompileContext}
 * @type {FromMarkdownHandle}
 */
function enterLiteralAutolink(token) {
	this.enter({ type: "link", title: null, url: "", children: [] }, token);
}

/**
 * @this {CompileContext}
 * @type {FromMarkdownHandle}
 */
function enterLiteralAutolinkValue(token) {
	this.config.enter.autolinkProtocol.call(this, token);
}

/**
 * @this {CompileContext}
 * @type {FromMarkdownHandle}
 */
function exitLiteralAutolinkHttp(token) {
	this.config.exit.autolinkProtocol.call(this, token);
}

/**
 * @this {CompileContext}
 * @type {FromMarkdownHandle}
 */
function exitLiteralAutolinkWww(token) {
	this.config.exit.data.call(this, token);
	const node = this.stack[this.stack.length - 1];
	ok$1(node.type === "link");
	node.url = "http://" + this.sliceSerialize(token);
}

/**
 * @this {CompileContext}
 * @type {FromMarkdownHandle}
 */
function exitLiteralAutolinkEmail(token) {
	this.config.exit.autolinkEmail.call(this, token);
}

/**
 * @this {CompileContext}
 * @type {FromMarkdownHandle}
 */
function exitLiteralAutolink(token) {
	this.exit(token);
}

/** @type {FromMarkdownTransform} */
function transformGfmAutolinkLiterals(tree) {
	findAndReplace(
		tree,
		[
			[/(https?:\/\/|www(?=\.))([-.\w]+)([^ \t\r\n]*)/gi, findUrl],
			[/(?<=^|\s|\p{P}|\p{S})([-.\w+]+)@([-\w]+(?:\.[-\w]+)+)/gu, findEmail],
		],
		{ ignore: ["link", "linkReference"] },
	);
}

/**
 * @type {ReplaceFunction}
 * @param {string} _
 * @param {string} protocol
 * @param {string} domain
 * @param {string} path
 * @param {RegExpMatchObject} match
 * @returns {Array<PhrasingContent> | Link | false}
 */
// eslint-disable-next-line max-params
function findUrl(_, protocol, domain, path, match) {
	let prefix = "";

	// Not an expected previous character.
	if (!previous$1(match)) {
		return false;
	}

	// Treat `www` as part of the domain.
	if (/^w/i.test(protocol)) {
		domain = protocol + domain;
		protocol = "";
		prefix = "http://";
	}

	if (!isCorrectDomain(domain)) {
		return false;
	}

	const parts = splitUrl(domain + path);

	if (!parts[0]) return false;

	/** @type {Link} */
	const result = {
		type: "link",
		title: null,
		url: prefix + protocol + parts[0],
		children: [{ type: "text", value: protocol + parts[0] }],
	};

	if (parts[1]) {
		return [result, { type: "text", value: parts[1] }];
	}

	return result;
}

/**
 * @type {ReplaceFunction}
 * @param {string} _
 * @param {string} atext
 * @param {string} label
 * @param {RegExpMatchObject} match
 * @returns {Link | false}
 */
function findEmail(_, atext, label, match) {
	if (
		// Not an expected previous character.
		!previous$1(match, true) ||
		// Label ends in not allowed character.
		/[-\d_]$/.test(label)
	) {
		return false;
	}

	return {
		type: "link",
		title: null,
		url: "mailto:" + atext + "@" + label,
		children: [{ type: "text", value: atext + "@" + label }],
	};
}

/**
 * @param {string} domain
 * @returns {boolean}
 */
function isCorrectDomain(domain) {
	const parts = domain.split(".");

	if (
		parts.length < 2 ||
		(parts[parts.length - 1] && (/_/.test(parts[parts.length - 1]) || !/[a-zA-Z\d]/.test(parts[parts.length - 1]))) ||
		(parts[parts.length - 2] && (/_/.test(parts[parts.length - 2]) || !/[a-zA-Z\d]/.test(parts[parts.length - 2])))
	) {
		return false;
	}

	return true;
}

/**
 * @param {string} url
 * @returns {[string, string | undefined]}
 */
function splitUrl(url) {
	const trailExec = /[!"&'),.:;<>?\]}]+$/.exec(url);

	if (!trailExec) {
		return [url, undefined];
	}

	url = url.slice(0, trailExec.index);

	let trail = trailExec[0];
	let closingParenIndex = trail.indexOf(")");
	const openingParens = ccount(url, "(");
	let closingParens = ccount(url, ")");

	while (closingParenIndex !== -1 && openingParens > closingParens) {
		url += trail.slice(0, closingParenIndex + 1);
		trail = trail.slice(closingParenIndex + 1);
		closingParenIndex = trail.indexOf(")");
		closingParens++;
	}

	return [url, trail];
}

/**
 * @param {RegExpMatchObject} match
 * @param {boolean | null | undefined} [email=false]
 * @returns {boolean}
 */
function previous$1(match, email) {
	const code = match.input.charCodeAt(match.index - 1);

	return (
		(match.index === 0 || unicodeWhitespace(code) || unicodePunctuation(code)) &&
		// If it’s an email, the previous character should not be a slash.
		(!email || code !== 47)
	);
}

/**
 * Normalize an identifier (as found in references, definitions).
 *
 * Collapses markdown whitespace, trim, and then lower- and uppercase.
 *
 * Some characters are considered “uppercase”, such as U+03F4 (`ϴ`), but if their
 * lowercase counterpart (U+03B8 (`θ`)) is uppercased will result in a different
 * uppercase character (U+0398 (`Θ`)).
 * So, to get a canonical form, we perform both lower- and uppercase.
 *
 * Using uppercase last makes sure keys will never interact with default
 * prototypal values (such as `constructor`): nothing in the prototype of
 * `Object` is uppercase.
 *
 * @param {string} value
 *   Identifier to normalize.
 * @returns {string}
 *   Normalized identifier.
 */
function normalizeIdentifier(value) {
	return (
		value
			// Collapse markdown whitespace.
			.replace(/[\t\n\r ]+/g, " ")
			// Trim.
			.replace(/^ | $/g, "")
			// Some characters are considered “uppercase”, but if their lowercase
			// counterpart is uppercased will result in a different uppercase
			// character.
			// Hence, to get that form, we perform both lower- and uppercase.
			// Upper case makes sure keys will not interact with default prototypal
			// methods: no method is uppercase.
			.toLowerCase()
			.toUpperCase()
	);
}

/**
 * @import {
 *   CompileContext,
 *   Extension as FromMarkdownExtension,
 *   Handle as FromMarkdownHandle
 * } from 'mdast-util-from-markdown'
 * @import {ToMarkdownOptions} from 'mdast-util-gfm-footnote'
 * @import {
 *   Handle as ToMarkdownHandle,
 *   Map,
 *   Options as ToMarkdownExtension
 * } from 'mdast-util-to-markdown'
 * @import {FootnoteDefinition, FootnoteReference} from 'mdast'
 */

footnoteReference$1.peek = footnoteReferencePeek;

/**
 * @this {CompileContext}
 * @type {FromMarkdownHandle}
 */
function enterFootnoteCallString() {
	this.buffer();
}

/**
 * @this {CompileContext}
 * @type {FromMarkdownHandle}
 */
function enterFootnoteCall(token) {
	this.enter({ type: "footnoteReference", identifier: "", label: "" }, token);
}

/**
 * @this {CompileContext}
 * @type {FromMarkdownHandle}
 */
function enterFootnoteDefinitionLabelString() {
	this.buffer();
}

/**
 * @this {CompileContext}
 * @type {FromMarkdownHandle}
 */
function enterFootnoteDefinition(token) {
	this.enter({ type: "footnoteDefinition", identifier: "", label: "", children: [] }, token);
}

/**
 * @this {CompileContext}
 * @type {FromMarkdownHandle}
 */
function exitFootnoteCallString(token) {
	const label = this.resume();
	const node = this.stack[this.stack.length - 1];
	ok$1(node.type === "footnoteReference");
	node.identifier = normalizeIdentifier(this.sliceSerialize(token)).toLowerCase();
	node.label = label;
}

/**
 * @this {CompileContext}
 * @type {FromMarkdownHandle}
 */
function exitFootnoteCall(token) {
	this.exit(token);
}

/**
 * @this {CompileContext}
 * @type {FromMarkdownHandle}
 */
function exitFootnoteDefinitionLabelString(token) {
	const label = this.resume();
	const node = this.stack[this.stack.length - 1];
	ok$1(node.type === "footnoteDefinition");
	node.identifier = normalizeIdentifier(this.sliceSerialize(token)).toLowerCase();
	node.label = label;
}

/**
 * @this {CompileContext}
 * @type {FromMarkdownHandle}
 */
function exitFootnoteDefinition(token) {
	this.exit(token);
}

/** @type {ToMarkdownHandle} */
function footnoteReferencePeek() {
	return "[";
}

/**
 * @type {ToMarkdownHandle}
 * @param {FootnoteReference} node
 */
function footnoteReference$1(node, _, state, info) {
	const tracker = state.createTracker(info);
	let value = tracker.move("[^");
	const exit = state.enter("footnoteReference");
	const subexit = state.enter("reference");
	value += tracker.move(state.safe(state.associationId(node), { after: "]", before: value }));
	subexit();
	exit();
	value += tracker.move("]");
	return value;
}

/**
 * Create an extension for `mdast-util-from-markdown` to enable GFM footnotes
 * in markdown.
 *
 * @returns {FromMarkdownExtension}
 *   Extension for `mdast-util-from-markdown`.
 */
function gfmFootnoteFromMarkdown() {
	return {
		enter: {
			gfmFootnoteCallString: enterFootnoteCallString,
			gfmFootnoteCall: enterFootnoteCall,
			gfmFootnoteDefinitionLabelString: enterFootnoteDefinitionLabelString,
			gfmFootnoteDefinition: enterFootnoteDefinition,
		},
		exit: {
			gfmFootnoteCallString: exitFootnoteCallString,
			gfmFootnoteCall: exitFootnoteCall,
			gfmFootnoteDefinitionLabelString: exitFootnoteDefinitionLabelString,
			gfmFootnoteDefinition: exitFootnoteDefinition,
		},
	};
}

/**
 * Create an extension for `mdast-util-to-markdown` to enable GFM footnotes
 * in markdown.
 *
 * @param {ToMarkdownOptions | null | undefined} [options]
 *   Configuration (optional).
 * @returns {ToMarkdownExtension}
 *   Extension for `mdast-util-to-markdown`.
 */
function gfmFootnoteToMarkdown(options) {
	// To do: next major: change default.
	let firstLineBlank = false;

	if (options && options.firstLineBlank) {
		firstLineBlank = true;
	}

	return {
		handlers: { footnoteDefinition, footnoteReference: footnoteReference$1 },
		// This is on by default already.
		unsafe: [{ character: "[", inConstruct: ["label", "phrasing", "reference"] }],
	};

	/**
	 * @type {ToMarkdownHandle}
	 * @param {FootnoteDefinition} node
	 */
	function footnoteDefinition(node, _, state, info) {
		const tracker = state.createTracker(info);
		let value = tracker.move("[^");
		const exit = state.enter("footnoteDefinition");
		const subexit = state.enter("label");
		value += tracker.move(state.safe(state.associationId(node), { before: value, after: "]" }));
		subexit();

		value += tracker.move("]:");

		if (node.children && node.children.length > 0) {
			tracker.shift(4);

			value += tracker.move((firstLineBlank ? "\n" : " ") + state.indentLines(state.containerFlow(node, tracker.current()), firstLineBlank ? mapAll : mapExceptFirst));
		}

		exit();

		return value;
	}
}

/** @type {Map} */
function mapExceptFirst(line, index, blank) {
	return index === 0 ? line : mapAll(line, index, blank);
}

/** @type {Map} */
function mapAll(line, index, blank) {
	return (blank ? "" : "    ") + line;
}

/**
 * @typedef {import('mdast').Delete} Delete
 *
 * @typedef {import('mdast-util-from-markdown').CompileContext} CompileContext
 * @typedef {import('mdast-util-from-markdown').Extension} FromMarkdownExtension
 * @typedef {import('mdast-util-from-markdown').Handle} FromMarkdownHandle
 *
 * @typedef {import('mdast-util-to-markdown').ConstructName} ConstructName
 * @typedef {import('mdast-util-to-markdown').Handle} ToMarkdownHandle
 * @typedef {import('mdast-util-to-markdown').Options} ToMarkdownExtension
 */

/**
 * List of constructs that occur in phrasing (paragraphs, headings), but cannot
 * contain strikethrough.
 * So they sort of cancel each other out.
 * Note: could use a better name.
 *
 * Note: keep in sync with: <https://github.com/syntax-tree/mdast-util-to-markdown/blob/8ce8dbf/lib/unsafe.js#L14>
 *
 * @type {Array<ConstructName>}
 */
const constructsWithoutStrikethrough = ["autolink", "destinationLiteral", "destinationRaw", "reference", "titleQuote", "titleApostrophe"];

handleDelete.peek = peekDelete;

/**
 * Create an extension for `mdast-util-from-markdown` to enable GFM
 * strikethrough in markdown.
 *
 * @returns {FromMarkdownExtension}
 *   Extension for `mdast-util-from-markdown` to enable GFM strikethrough.
 */
function gfmStrikethroughFromMarkdown() {
	return {
		canContainEols: ["delete"],
		enter: { strikethrough: enterStrikethrough },
		exit: { strikethrough: exitStrikethrough },
	};
}

/**
 * Create an extension for `mdast-util-to-markdown` to enable GFM
 * strikethrough in markdown.
 *
 * @returns {ToMarkdownExtension}
 *   Extension for `mdast-util-to-markdown` to enable GFM strikethrough.
 */
function gfmStrikethroughToMarkdown() {
	return {
		unsafe: [
			{
				character: "~",
				inConstruct: "phrasing",
				notInConstruct: constructsWithoutStrikethrough,
			},
		],
		handlers: { delete: handleDelete },
	};
}

/**
 * @this {CompileContext}
 * @type {FromMarkdownHandle}
 */
function enterStrikethrough(token) {
	this.enter({ type: "delete", children: [] }, token);
}

/**
 * @this {CompileContext}
 * @type {FromMarkdownHandle}
 */
function exitStrikethrough(token) {
	this.exit(token);
}

/**
 * @type {ToMarkdownHandle}
 * @param {Delete} node
 */
function handleDelete(node, _, state, info) {
	const tracker = state.createTracker(info);
	const exit = state.enter("strikethrough");
	let value = tracker.move("~~");
	value += state.containerPhrasing(node, {
		...tracker.current(),
		before: value,
		after: "~",
	});
	value += tracker.move("~~");
	exit();
	return value;
}

/** @type {ToMarkdownHandle} */
function peekDelete() {
	return "~";
}

// To do: next major: remove.
/**
 * @typedef {Options} MarkdownTableOptions
 *   Configuration.
 */

/**
 * @typedef Options
 *   Configuration.
 * @property {boolean | null | undefined} [alignDelimiters=true]
 *   Whether to align the delimiters (default: `true`);
 *   they are aligned by default:
 *
 *   ```markdown
 *   | Alpha | B     |
 *   | ----- | ----- |
 *   | C     | Delta |
 *   ```
 *
 *   Pass `false` to make them staggered:
 *
 *   ```markdown
 *   | Alpha | B |
 *   | - | - |
 *   | C | Delta |
 *   ```
 * @property {ReadonlyArray<string | null | undefined> | string | null | undefined} [align]
 *   How to align columns (default: `''`);
 *   one style for all columns or styles for their respective columns;
 *   each style is either `'l'` (left), `'r'` (right), or `'c'` (center);
 *   other values are treated as `''`, which doesn’t place the colon in the
 *   alignment row but does align left;
 *   *only the lowercased first character is used, so `Right` is fine.*
 * @property {boolean | null | undefined} [delimiterEnd=true]
 *   Whether to end each row with the delimiter (default: `true`).
 *
 *   > 👉 **Note**: please don’t use this: it could create fragile structures
 *   > that aren’t understandable to some markdown parsers.
 *
 *   When `true`, there are ending delimiters:
 *
 *   ```markdown
 *   | Alpha | B     |
 *   | ----- | ----- |
 *   | C     | Delta |
 *   ```
 *
 *   When `false`, there are no ending delimiters:
 *
 *   ```markdown
 *   | Alpha | B
 *   | ----- | -----
 *   | C     | Delta
 *   ```
 * @property {boolean | null | undefined} [delimiterStart=true]
 *   Whether to begin each row with the delimiter (default: `true`).
 *
 *   > 👉 **Note**: please don’t use this: it could create fragile structures
 *   > that aren’t understandable to some markdown parsers.
 *
 *   When `true`, there are starting delimiters:
 *
 *   ```markdown
 *   | Alpha | B     |
 *   | ----- | ----- |
 *   | C     | Delta |
 *   ```
 *
 *   When `false`, there are no starting delimiters:
 *
 *   ```markdown
 *   Alpha | B     |
 *   ----- | ----- |
 *   C     | Delta |
 *   ```
 * @property {boolean | null | undefined} [padding=true]
 *   Whether to add a space of padding between delimiters and cells
 *   (default: `true`).
 *
 *   When `true`, there is padding:
 *
 *   ```markdown
 *   | Alpha | B     |
 *   | ----- | ----- |
 *   | C     | Delta |
 *   ```
 *
 *   When `false`, there is no padding:
 *
 *   ```markdown
 *   |Alpha|B    |
 *   |-----|-----|
 *   |C    |Delta|
 *   ```
 * @property {((value: string) => number) | null | undefined} [stringLength]
 *   Function to detect the length of table cell content (optional);
 *   this is used when aligning the delimiters (`|`) between table cells;
 *   full-width characters and emoji mess up delimiter alignment when viewing
 *   the markdown source;
 *   to fix this, you can pass this function,
 *   which receives the cell content and returns its “visible” size;
 *   note that what is and isn’t visible depends on where the text is displayed.
 *
 *   Without such a function, the following:
 *
 *   ```js
 *   markdownTable([
 *     ['Alpha', 'Bravo'],
 *     ['中文', 'Charlie'],
 *     ['👩‍❤️‍👩', 'Delta']
 *   ])
 *   ```
 *
 *   Yields:
 *
 *   ```markdown
 *   | Alpha | Bravo |
 *   | - | - |
 *   | 中文 | Charlie |
 *   | 👩‍❤️‍👩 | Delta |
 *   ```
 *
 *   With [`string-width`](https://github.com/sindresorhus/string-width):
 *
 *   ```js
 *   import stringWidth from 'string-width'
 *
 *   markdownTable(
 *     [
 *       ['Alpha', 'Bravo'],
 *       ['中文', 'Charlie'],
 *       ['👩‍❤️‍👩', 'Delta']
 *     ],
 *     {stringLength: stringWidth}
 *   )
 *   ```
 *
 *   Yields:
 *
 *   ```markdown
 *   | Alpha | Bravo   |
 *   | ----- | ------- |
 *   | 中文  | Charlie |
 *   | 👩‍❤️‍👩    | Delta   |
 *   ```
 */

/**
 * @param {string} value
 *   Cell value.
 * @returns {number}
 *   Cell size.
 */
function defaultStringLength(value) {
	return value.length;
}

/**
 * Generate a markdown
 * ([GFM](https://docs.github.com/en/github/writing-on-github/working-with-advanced-formatting/organizing-information-with-tables))
 * table.
 *
 * @param {ReadonlyArray<ReadonlyArray<string | null | undefined>>} table
 *   Table data (matrix of strings).
 * @param {Readonly<Options> | null | undefined} [options]
 *   Configuration (optional).
 * @returns {string}
 *   Result.
 */
function markdownTable(table, options) {
	const settings = options || {};
	// To do: next major: change to spread.
	const align = (settings.align || []).concat();
	const stringLength = settings.stringLength || defaultStringLength;
	/** @type {Array<number>} Character codes as symbols for alignment per column. */
	const alignments = [];
	/** @type {Array<Array<string>>} Cells per row. */
	const cellMatrix = [];
	/** @type {Array<Array<number>>} Sizes of each cell per row. */
	const sizeMatrix = [];
	/** @type {Array<number>} */
	const longestCellByColumn = [];
	let mostCellsPerRow = 0;
	let rowIndex = -1;

	// This is a superfluous loop if we don’t align delimiters, but otherwise we’d
	// do superfluous work when aligning, so optimize for aligning.
	while (++rowIndex < table.length) {
		/** @type {Array<string>} */
		const row = [];
		/** @type {Array<number>} */
		const sizes = [];
		let columnIndex = -1;

		if (table[rowIndex].length > mostCellsPerRow) {
			mostCellsPerRow = table[rowIndex].length;
		}

		while (++columnIndex < table[rowIndex].length) {
			const cell = serialize(table[rowIndex][columnIndex]);

			if (settings.alignDelimiters !== false) {
				const size = stringLength(cell);
				sizes[columnIndex] = size;

				if (longestCellByColumn[columnIndex] === undefined || size > longestCellByColumn[columnIndex]) {
					longestCellByColumn[columnIndex] = size;
				}
			}

			row.push(cell);
		}

		cellMatrix[rowIndex] = row;
		sizeMatrix[rowIndex] = sizes;
	}

	// Figure out which alignments to use.
	let columnIndex = -1;

	if (typeof align === "object" && "length" in align) {
		while (++columnIndex < mostCellsPerRow) {
			alignments[columnIndex] = toAlignment(align[columnIndex]);
		}
	} else {
		const code = toAlignment(align);

		while (++columnIndex < mostCellsPerRow) {
			alignments[columnIndex] = code;
		}
	}

	// Inject the alignment row.
	columnIndex = -1;
	/** @type {Array<string>} */
	const row = [];
	/** @type {Array<number>} */
	const sizes = [];

	while (++columnIndex < mostCellsPerRow) {
		const code = alignments[columnIndex];
		let before = "";
		let after = "";

		if (code === 99 /* `c` */) {
			before = ":";
			after = ":";
		} else if (code === 108 /* `l` */) {
			before = ":";
		} else if (code === 114 /* `r` */) {
			after = ":";
		}

		// There *must* be at least one hyphen-minus in each alignment cell.
		let size = settings.alignDelimiters === false ? 1 : Math.max(1, longestCellByColumn[columnIndex] - before.length - after.length);

		const cell = before + "-".repeat(size) + after;

		if (settings.alignDelimiters !== false) {
			size = before.length + size + after.length;

			if (size > longestCellByColumn[columnIndex]) {
				longestCellByColumn[columnIndex] = size;
			}

			sizes[columnIndex] = size;
		}

		row[columnIndex] = cell;
	}

	// Inject the alignment row.
	cellMatrix.splice(1, 0, row);
	sizeMatrix.splice(1, 0, sizes);

	rowIndex = -1;
	/** @type {Array<string>} */
	const lines = [];

	while (++rowIndex < cellMatrix.length) {
		const row = cellMatrix[rowIndex];
		const sizes = sizeMatrix[rowIndex];
		columnIndex = -1;
		/** @type {Array<string>} */
		const line = [];

		while (++columnIndex < mostCellsPerRow) {
			const cell = row[columnIndex] || "";
			let before = "";
			let after = "";

			if (settings.alignDelimiters !== false) {
				const size = longestCellByColumn[columnIndex] - (sizes[columnIndex] || 0);
				const code = alignments[columnIndex];

				if (code === 114 /* `r` */) {
					before = " ".repeat(size);
				} else if (code === 99 /* `c` */) {
					if (size % 2) {
						before = " ".repeat(size / 2 + 0.5);
						after = " ".repeat(size / 2 - 0.5);
					} else {
						before = " ".repeat(size / 2);
						after = before;
					}
				} else {
					after = " ".repeat(size);
				}
			}

			if (settings.delimiterStart !== false && !columnIndex) {
				line.push("|");
			}

			if (
				settings.padding !== false &&
				// Don’t add the opening space if we’re not aligning and the cell is
				// empty: there will be a closing space.
				!(settings.alignDelimiters === false && cell === "") &&
				(settings.delimiterStart !== false || columnIndex)
			) {
				line.push(" ");
			}

			if (settings.alignDelimiters !== false) {
				line.push(before);
			}

			line.push(cell);

			if (settings.alignDelimiters !== false) {
				line.push(after);
			}

			if (settings.padding !== false) {
				line.push(" ");
			}

			if (settings.delimiterEnd !== false || columnIndex !== mostCellsPerRow - 1) {
				line.push("|");
			}
		}

		lines.push(settings.delimiterEnd === false ? line.join("").replace(/ +$/, "") : line.join(""));
	}

	return lines.join("\n");
}

/**
 * @param {string | null | undefined} [value]
 *   Value to serialize.
 * @returns {string}
 *   Result.
 */
function serialize(value) {
	return value === null || value === undefined ? "" : String(value);
}

/**
 * @param {string | null | undefined} value
 *   Value.
 * @returns {number}
 *   Alignment.
 */
function toAlignment(value) {
	const code = typeof value === "string" ? value.codePointAt(0) : 0;

	return (
		code === 67 /* `C` */ || code === 99 /* `c` */ ? 99 /* `c` */
		: code === 76 /* `L` */ || code === 108 /* `l` */ ? 108 /* `l` */
		: code === 82 /* `R` */ || code === 114 /* `r` */ ? 114 /* `r` */
		: 0
	);
}

/**
 * @callback Handler
 *   Handle a value, with a certain ID field set to a certain value.
 *   The ID field is passed to `zwitch`, and it’s value is this function’s
 *   place on the `handlers` record.
 * @param {...any} parameters
 *   Arbitrary parameters passed to the zwitch.
 *   The first will be an object with a certain ID field set to a certain value.
 * @returns {any}
 *   Anything!
 */

/**
 * @callback UnknownHandler
 *   Handle values that do have a certain ID field, but it’s set to a value
 *   that is not listed in the `handlers` record.
 * @param {unknown} value
 *   An object with a certain ID field set to an unknown value.
 * @param {...any} rest
 *   Arbitrary parameters passed to the zwitch.
 * @returns {any}
 *   Anything!
 */

/**
 * @callback InvalidHandler
 *   Handle values that do not have a certain ID field.
 * @param {unknown} value
 *   Any unknown value.
 * @param {...any} rest
 *   Arbitrary parameters passed to the zwitch.
 * @returns {void|null|undefined|never}
 *   This should crash or return nothing.
 */

/**
 * @template {InvalidHandler} [Invalid=InvalidHandler]
 * @template {UnknownHandler} [Unknown=UnknownHandler]
 * @template {Record<string, Handler>} [Handlers=Record<string, Handler>]
 * @typedef Options
 *   Configuration (required).
 * @property {Invalid} [invalid]
 *   Handler to use for invalid values.
 * @property {Unknown} [unknown]
 *   Handler to use for unknown values.
 * @property {Handlers} [handlers]
 *   Handlers to use.
 */

const own$6 = {}.hasOwnProperty;

/**
 * Handle values based on a field.
 *
 * @template {InvalidHandler} [Invalid=InvalidHandler]
 * @template {UnknownHandler} [Unknown=UnknownHandler]
 * @template {Record<string, Handler>} [Handlers=Record<string, Handler>]
 * @param {string} key
 *   Field to switch on.
 * @param {Options<Invalid, Unknown, Handlers>} [options]
 *   Configuration (required).
 * @returns {{unknown: Unknown, invalid: Invalid, handlers: Handlers, (...parameters: Parameters<Handlers[keyof Handlers]>): ReturnType<Handlers[keyof Handlers]>, (...parameters: Parameters<Unknown>): ReturnType<Unknown>}}
 */
function zwitch(key, options) {
	const settings = options || {};

	/**
	 * Handle one value.
	 *
	 * Based on the bound `key`, a respective handler will be called.
	 * If `value` is not an object, or doesn’t have a `key` property, the special
	 * “invalid” handler will be called.
	 * If `value` has an unknown `key`, the special “unknown” handler will be
	 * called.
	 *
	 * All arguments, and the context object, are passed through to the handler,
	 * and it’s result is returned.
	 *
	 * @this {unknown}
	 *   Any context object.
	 * @param {unknown} [value]
	 *   Any value.
	 * @param {...unknown} parameters
	 *   Arbitrary parameters passed to the zwitch.
	 * @property {Handler} invalid
	 *   Handle for values that do not have a certain ID field.
	 * @property {Handler} unknown
	 *   Handle values that do have a certain ID field, but it’s set to a value
	 *   that is not listed in the `handlers` record.
	 * @property {Handlers} handlers
	 *   Record of handlers.
	 * @returns {unknown}
	 *   Anything.
	 */
	function one(value, ...parameters) {
		/** @type {Handler|undefined} */
		let fn = one.invalid;
		const handlers = one.handlers;

		if (value && own$6.call(value, key)) {
			// @ts-expect-error Indexable.
			const id = String(value[key]);
			// @ts-expect-error Indexable.
			fn = own$6.call(handlers, id) ? handlers[id] : one.unknown;
		}

		if (fn) {
			return fn.call(this, value, ...parameters);
		}
	}

	one.handlers = settings.handlers || {};
	one.invalid = settings.invalid;
	one.unknown = settings.unknown;

	// @ts-expect-error: matches!
	return one;
}

/**
 * @import {Options, State} from './types.js'
 */

const own$5 = {}.hasOwnProperty;

/**
 * @param {State} base
 * @param {Options} extension
 * @returns {State}
 */
function configure$1(base, extension) {
	let index = -1;
	/** @type {keyof Options} */
	let key;

	// First do subextensions.
	if (extension.extensions) {
		while (++index < extension.extensions.length) {
			configure$1(base, extension.extensions[index]);
		}
	}

	for (key in extension) {
		if (own$5.call(extension, key)) {
			switch (key) {
				case "extensions": {
					// Empty.
					break;
				}

				/* c8 ignore next 4 */
				case "unsafe": {
					list$3(base[key], extension[key]);
					break;
				}

				case "join": {
					list$3(base[key], extension[key]);
					break;
				}

				case "handlers": {
					map$2(base[key], extension[key]);
					break;
				}

				default: {
					// @ts-expect-error: matches.
					base.options[key] = extension[key];
				}
			}
		}
	}

	return base;
}

/**
 * @template T
 * @param {Array<T>} left
 * @param {Array<T> | null | undefined} right
 */
function list$3(left, right) {
	if (right) {
		left.push(...right);
	}
}

/**
 * @template T
 * @param {Record<string, T>} left
 * @param {Record<string, T> | null | undefined} right
 */
function map$2(left, right) {
	if (right) {
		Object.assign(left, right);
	}
}

/**
 * @import {Blockquote, Parents} from 'mdast'
 * @import {Info, Map, State} from 'mdast-util-to-markdown'
 */

/**
 * @param {Blockquote} node
 * @param {Parents | undefined} _
 * @param {State} state
 * @param {Info} info
 * @returns {string}
 */
function blockquote$1(node, _, state, info) {
	const exit = state.enter("blockquote");
	const tracker = state.createTracker(info);
	tracker.move("> ");
	tracker.shift(2);
	const value = state.indentLines(state.containerFlow(node, tracker.current()), map$1);
	exit();
	return value;
}

/** @type {Map} */
function map$1(line, _, blank) {
	return ">" + (blank ? "" : " ") + line;
}

/**
 * @import {ConstructName, Unsafe} from 'mdast-util-to-markdown'
 */

/**
 * @param {Array<ConstructName>} stack
 * @param {Unsafe} pattern
 * @returns {boolean}
 */
function patternInScope(stack, pattern) {
	return listInScope(stack, pattern.inConstruct, true) && !listInScope(stack, pattern.notInConstruct, false);
}

/**
 * @param {Array<ConstructName>} stack
 * @param {Unsafe['inConstruct']} list
 * @param {boolean} none
 * @returns {boolean}
 */
function listInScope(stack, list, none) {
	if (typeof list === "string") {
		list = [list];
	}

	if (!list || list.length === 0) {
		return none;
	}

	let index = -1;

	while (++index < list.length) {
		if (stack.includes(list[index])) {
			return true;
		}
	}

	return false;
}

/**
 * @import {Break, Parents} from 'mdast'
 * @import {Info, State} from 'mdast-util-to-markdown'
 */

/**
 * @param {Break} _
 * @param {Parents | undefined} _1
 * @param {State} state
 * @param {Info} info
 * @returns {string}
 */
function hardBreak$1(_, _1, state, info) {
	let index = -1;

	while (++index < state.unsafe.length) {
		// If we can’t put eols in this construct (setext headings, tables), use a
		// space instead.
		if (state.unsafe[index].character === "\n" && patternInScope(state.stack, state.unsafe[index])) {
			return /[ \t]/.test(info.before) ? "" : " ";
		}
	}

	return "\\\n";
}

/**
 * Get the count of the longest repeating streak of `substring` in `value`.
 *
 * @param {string} value
 *   Content to search in.
 * @param {string} substring
 *   Substring to look for, typically one character.
 * @returns {number}
 *   Count of most frequent adjacent `substring`s in `value`.
 */
function longestStreak(value, substring) {
	const source = String(value);
	let index = source.indexOf(substring);
	let expected = index;
	let count = 0;
	let max = 0;

	if (typeof substring !== "string") {
		throw new TypeError("Expected substring");
	}

	while (index !== -1) {
		if (index === expected) {
			if (++count > max) {
				max = count;
			}
		} else {
			count = 1;
		}

		expected = index + substring.length;
		index = source.indexOf(substring, expected);
	}

	return max;
}

/**
 * @import {State} from 'mdast-util-to-markdown'
 * @import {Code} from 'mdast'
 */

/**
 * @param {Code} node
 * @param {State} state
 * @returns {boolean}
 */
function formatCodeAsIndented(node, state) {
	return Boolean(
		state.options.fences === false &&
		node.value &&
		// If there’s no info…
		!node.lang &&
		// And there’s a non-whitespace character…
		/[^ \r\n]/.test(node.value) &&
		// And the value doesn’t start or end in a blank…
		!/^[\t ]*(?:[\r\n]|$)|(?:^|[\r\n])[\t ]*$/.test(node.value),
	);
}

/**
 * @import {Options, State} from 'mdast-util-to-markdown'
 */

/**
 * @param {State} state
 * @returns {Exclude<Options['fence'], null | undefined>}
 */
function checkFence(state) {
	const marker = state.options.fence || "`";

	if (marker !== "`" && marker !== "~") {
		throw new Error("Cannot serialize code with `" + marker + "` for `options.fence`, expected `` ` `` or `~`");
	}

	return marker;
}

/**
 * @import {Info, Map, State} from 'mdast-util-to-markdown'
 * @import {Code, Parents} from 'mdast'
 */

/**
 * @param {Code} node
 * @param {Parents | undefined} _
 * @param {State} state
 * @param {Info} info
 * @returns {string}
 */
function code$2(node, _, state, info) {
	const marker = checkFence(state);
	const raw = node.value || "";
	const suffix = marker === "`" ? "GraveAccent" : "Tilde";

	if (formatCodeAsIndented(node, state)) {
		const exit = state.enter("codeIndented");
		const value = state.indentLines(raw, map);
		exit();
		return value;
	}

	const tracker = state.createTracker(info);
	const sequence = marker.repeat(Math.max(longestStreak(raw, marker) + 1, 3));
	const exit = state.enter("codeFenced");
	let value = tracker.move(sequence);

	if (node.lang) {
		const subexit = state.enter(`codeFencedLang${suffix}`);
		value += tracker.move(
			state.safe(node.lang, {
				before: value,
				after: " ",
				encode: ["`"],
				...tracker.current(),
			}),
		);
		subexit();
	}

	if (node.lang && node.meta) {
		const subexit = state.enter(`codeFencedMeta${suffix}`);
		value += tracker.move(" ");
		value += tracker.move(
			state.safe(node.meta, {
				before: value,
				after: "\n",
				encode: ["`"],
				...tracker.current(),
			}),
		);
		subexit();
	}

	value += tracker.move("\n");

	if (raw) {
		value += tracker.move(raw + "\n");
	}

	value += tracker.move(sequence);
	exit();
	return value;
}

/** @type {Map} */
function map(line, _, blank) {
	return (blank ? "" : "    ") + line;
}

/**
 * @import {Options, State} from 'mdast-util-to-markdown'
 */

/**
 * @param {State} state
 * @returns {Exclude<Options['quote'], null | undefined>}
 */
function checkQuote(state) {
	const marker = state.options.quote || '"';

	if (marker !== '"' && marker !== "'") {
		throw new Error("Cannot serialize title with `" + marker + "` for `options.quote`, expected `\"`, or `'`");
	}

	return marker;
}

/**
 * @import {Info, State} from 'mdast-util-to-markdown'
 * @import {Definition, Parents} from 'mdast'
 */

/**
 * @param {Definition} node
 * @param {Parents | undefined} _
 * @param {State} state
 * @param {Info} info
 * @returns {string}
 */
function definition$1(node, _, state, info) {
	const quote = checkQuote(state);
	const suffix = quote === '"' ? "Quote" : "Apostrophe";
	const exit = state.enter("definition");
	let subexit = state.enter("label");
	const tracker = state.createTracker(info);
	let value = tracker.move("[");
	value += tracker.move(
		state.safe(state.associationId(node), {
			before: value,
			after: "]",
			...tracker.current(),
		}),
	);
	value += tracker.move("]: ");

	subexit();

	if (
		// If there’s no url, or…
		!node.url ||
		// If there are control characters or whitespace.
		/[\0- \u007F]/.test(node.url)
	) {
		subexit = state.enter("destinationLiteral");
		value += tracker.move("<");
		value += tracker.move(state.safe(node.url, { before: value, after: ">", ...tracker.current() }));
		value += tracker.move(">");
	} else {
		// No whitespace, raw is prettier.
		subexit = state.enter("destinationRaw");
		value += tracker.move(
			state.safe(node.url, {
				before: value,
				after: node.title ? " " : "\n",
				...tracker.current(),
			}),
		);
	}

	subexit();

	if (node.title) {
		subexit = state.enter(`title${suffix}`);
		value += tracker.move(" " + quote);
		value += tracker.move(
			state.safe(node.title, {
				before: value,
				after: quote,
				...tracker.current(),
			}),
		);
		value += tracker.move(quote);
		subexit();
	}

	exit();

	return value;
}

/**
 * @import {Options, State} from 'mdast-util-to-markdown'
 */

/**
 * @param {State} state
 * @returns {Exclude<Options['emphasis'], null | undefined>}
 */
function checkEmphasis(state) {
	const marker = state.options.emphasis || "*";

	if (marker !== "*" && marker !== "_") {
		throw new Error("Cannot serialize emphasis with `" + marker + "` for `options.emphasis`, expected `*`, or `_`");
	}

	return marker;
}

/**
 * Encode a code point as a character reference.
 *
 * @param {number} code
 *   Code point to encode.
 * @returns {string}
 *   Encoded character reference.
 */
function encodeCharacterReference(code) {
	return "&#x" + code.toString(16).toUpperCase() + ";";
}

/**
 * @import {Code} from 'micromark-util-types'
 */

/**
 * Classify whether a code represents whitespace, punctuation, or something
 * else.
 *
 * Used for attention (emphasis, strong), whose sequences can open or close
 * based on the class of surrounding characters.
 *
 * > 👉 **Note**: eof (`null`) is seen as whitespace.
 *
 * @param {Code} code
 *   Code.
 * @returns {typeof constants.characterGroupWhitespace | typeof constants.characterGroupPunctuation | undefined}
 *   Group.
 */
function classifyCharacter(code) {
	if (code === null || markdownLineEndingOrSpace(code) || unicodeWhitespace(code)) {
		return 1;
	}
	if (unicodePunctuation(code)) {
		return 2;
	}
}

/**
 * @import {EncodeSides} from '../types.js'
 */

/**
 * Check whether to encode (as a character reference) the characters
 * surrounding an attention run.
 *
 * Which characters are around an attention run influence whether it works or
 * not.
 *
 * See <https://github.com/orgs/syntax-tree/discussions/60> for more info.
 * See this markdown in a particular renderer to see what works:
 *
 * ```markdown
 * |                         | A (letter inside) | B (punctuation inside) | C (whitespace inside) | D (nothing inside) |
 * | ----------------------- | ----------------- | ---------------------- | --------------------- | ------------------ |
 * | 1 (letter outside)      | x*y*z             | x*.*z                  | x* *z                 | x**z               |
 * | 2 (punctuation outside) | .*y*.             | .*.*.                  | .* *.                 | .**.               |
 * | 3 (whitespace outside)  | x *y* z           | x *.* z                | x * * z               | x ** z             |
 * | 4 (nothing outside)     | *x*               | *.*                    | * *                   | **                 |
 * ```
 *
 * @param {number} outside
 *   Code point on the outer side of the run.
 * @param {number} inside
 *   Code point on the inner side of the run.
 * @param {'*' | '_'} marker
 *   Marker of the run.
 *   Underscores are handled more strictly (they form less often) than
 *   asterisks.
 * @returns {EncodeSides}
 *   Whether to encode characters.
 */
// Important: punctuation must never be encoded.
// Punctuation is solely used by markdown constructs.
// And by encoding itself.
// Encoding them will break constructs or double encode things.
function encodeInfo(outside, inside, marker) {
	const outsideKind = classifyCharacter(outside);
	const insideKind = classifyCharacter(inside);

	// Letter outside:
	if (outsideKind === undefined) {
		return (
			insideKind === undefined ?
				// Letter inside:
				// we have to encode *both* letters for `_` as it is looser.
				// it already forms for `*` (and GFMs `~`).
				marker === "_" ?
					{ inside: true, outside: true }
				:	{ inside: false, outside: false }
			: insideKind === 1 ?
				// Whitespace inside: encode both (letter, whitespace).
				{ inside: true, outside: true }
				// Punctuation inside: encode outer (letter)
			:	{ inside: false, outside: true }
		);
	}

	// Whitespace outside:
	if (outsideKind === 1) {
		return (
			insideKind === undefined ?
				// Letter inside: already forms.
				{ inside: false, outside: false }
			: insideKind === 1 ?
				// Whitespace inside: encode both (whitespace).
				{ inside: true, outside: true }
				// Punctuation inside: already forms.
			:	{ inside: false, outside: false }
		);
	}

	// Punctuation outside:
	return (
		insideKind === undefined ?
			// Letter inside: already forms.
			{ inside: false, outside: false }
		: insideKind === 1 ?
			// Whitespace inside: encode inner (whitespace).
			{ inside: true, outside: false }
			// Punctuation inside: already forms.
		:	{ inside: false, outside: false }
	);
}

/**
 * @import {Info, State} from 'mdast-util-to-markdown'
 * @import {Emphasis, Parents} from 'mdast'
 */

emphasis$1.peek = emphasisPeek;

/**
 * @param {Emphasis} node
 * @param {Parents | undefined} _
 * @param {State} state
 * @param {Info} info
 * @returns {string}
 */
function emphasis$1(node, _, state, info) {
	const marker = checkEmphasis(state);
	const exit = state.enter("emphasis");
	const tracker = state.createTracker(info);
	const before = tracker.move(marker);

	let between = tracker.move(
		state.containerPhrasing(node, {
			after: marker,
			before,
			...tracker.current(),
		}),
	);
	const betweenHead = between.charCodeAt(0);
	const open = encodeInfo(info.before.charCodeAt(info.before.length - 1), betweenHead, marker);

	if (open.inside) {
		between = encodeCharacterReference(betweenHead) + between.slice(1);
	}

	const betweenTail = between.charCodeAt(between.length - 1);
	const close = encodeInfo(info.after.charCodeAt(0), betweenTail, marker);

	if (close.inside) {
		between = between.slice(0, -1) + encodeCharacterReference(betweenTail);
	}

	const after = tracker.move(marker);

	exit();

	state.attentionEncodeSurroundingInfo = {
		after: close.outside,
		before: open.outside,
	};
	return before + between + after;
}

/**
 * @param {Emphasis} _
 * @param {Parents | undefined} _1
 * @param {State} state
 * @returns {string}
 */
function emphasisPeek(_, _1, state) {
	return state.options.emphasis || "*";
}

/**
 * @import {State} from 'mdast-util-to-markdown'
 * @import {Heading} from 'mdast'
 */

/**
 * @param {Heading} node
 * @param {State} state
 * @returns {boolean}
 */
function formatHeadingAsSetext(node, state) {
	let literalWithBreak = false;

	// Look for literals with a line break.
	// Note that this also
	visit(node, function (node) {
		if (("value" in node && /\r?\n|\r/.test(node.value)) || node.type === "break") {
			literalWithBreak = true;
			return EXIT;
		}
	});

	return Boolean((!node.depth || node.depth < 3) && toString(node) && (state.options.setext || literalWithBreak));
}

/**
 * @import {Info, State} from 'mdast-util-to-markdown'
 * @import {Heading, Parents} from 'mdast'
 */

/**
 * @param {Heading} node
 * @param {Parents | undefined} _
 * @param {State} state
 * @param {Info} info
 * @returns {string}
 */
function heading$1(node, _, state, info) {
	const rank = Math.max(Math.min(6, node.depth || 1), 1);
	const tracker = state.createTracker(info);

	if (formatHeadingAsSetext(node, state)) {
		const exit = state.enter("headingSetext");
		const subexit = state.enter("phrasing");
		const value = state.containerPhrasing(node, {
			...tracker.current(),
			before: "\n",
			after: "\n",
		});
		subexit();
		exit();

		return (
			value +
			"\n" +
			(rank === 1 ? "=" : "-").repeat(
				// The whole size…
				value.length -
					// Minus the position of the character after the last EOL (or
					// 0 if there is none)…
					(Math.max(value.lastIndexOf("\r"), value.lastIndexOf("\n")) + 1),
			)
		);
	}

	const sequence = "#".repeat(rank);
	const exit = state.enter("headingAtx");
	const subexit = state.enter("phrasing");

	// Note: for proper tracking, we should reset the output positions when there
	// is no content returned, because then the space is not output.
	// Practically, in that case, there is no content, so it doesn’t matter that
	// we’ve tracked one too many characters.
	tracker.move(sequence + " ");

	let value = state.containerPhrasing(node, {
		before: "# ",
		after: "\n",
		...tracker.current(),
	});

	if (/^[\t ]/.test(value)) {
		// To do: what effect has the character reference on tracking?
		value = encodeCharacterReference(value.charCodeAt(0)) + value.slice(1);
	}

	value = value ? sequence + " " + value : sequence;

	if (state.options.closeAtx) {
		value += " " + sequence;
	}

	subexit();
	exit();

	return value;
}

/**
 * @import {Html} from 'mdast'
 */

html$3.peek = htmlPeek;

/**
 * @param {Html} node
 * @returns {string}
 */
function html$3(node) {
	return node.value || "";
}

/**
 * @returns {string}
 */
function htmlPeek() {
	return "<";
}

/**
 * @import {Info, State} from 'mdast-util-to-markdown'
 * @import {Image, Parents} from 'mdast'
 */

image$1.peek = imagePeek;

/**
 * @param {Image} node
 * @param {Parents | undefined} _
 * @param {State} state
 * @param {Info} info
 * @returns {string}
 */
function image$1(node, _, state, info) {
	const quote = checkQuote(state);
	const suffix = quote === '"' ? "Quote" : "Apostrophe";
	const exit = state.enter("image");
	let subexit = state.enter("label");
	const tracker = state.createTracker(info);
	let value = tracker.move("![");
	value += tracker.move(state.safe(node.alt, { before: value, after: "]", ...tracker.current() }));
	value += tracker.move("](");

	subexit();

	if (
		// If there’s no url but there is a title…
		(!node.url && node.title) ||
		// If there are control characters or whitespace.
		/[\0- \u007F]/.test(node.url)
	) {
		subexit = state.enter("destinationLiteral");
		value += tracker.move("<");
		value += tracker.move(state.safe(node.url, { before: value, after: ">", ...tracker.current() }));
		value += tracker.move(">");
	} else {
		// No whitespace, raw is prettier.
		subexit = state.enter("destinationRaw");
		value += tracker.move(
			state.safe(node.url, {
				before: value,
				after: node.title ? " " : ")",
				...tracker.current(),
			}),
		);
	}

	subexit();

	if (node.title) {
		subexit = state.enter(`title${suffix}`);
		value += tracker.move(" " + quote);
		value += tracker.move(
			state.safe(node.title, {
				before: value,
				after: quote,
				...tracker.current(),
			}),
		);
		value += tracker.move(quote);
		subexit();
	}

	value += tracker.move(")");
	exit();

	return value;
}

/**
 * @returns {string}
 */
function imagePeek() {
	return "!";
}

/**
 * @import {Info, State} from 'mdast-util-to-markdown'
 * @import {ImageReference, Parents} from 'mdast'
 */

imageReference$1.peek = imageReferencePeek;

/**
 * @param {ImageReference} node
 * @param {Parents | undefined} _
 * @param {State} state
 * @param {Info} info
 * @returns {string}
 */
function imageReference$1(node, _, state, info) {
	const type = node.referenceType;
	const exit = state.enter("imageReference");
	let subexit = state.enter("label");
	const tracker = state.createTracker(info);
	let value = tracker.move("![");
	const alt = state.safe(node.alt, {
		before: value,
		after: "]",
		...tracker.current(),
	});
	value += tracker.move(alt + "][");

	subexit();
	// Hide the fact that we’re in phrasing, because escapes don’t work.
	const stack = state.stack;
	state.stack = [];
	subexit = state.enter("reference");
	// Note: for proper tracking, we should reset the output positions when we end
	// up making a `shortcut` reference, because then there is no brace output.
	// Practically, in that case, there is no content, so it doesn’t matter that
	// we’ve tracked one too many characters.
	const reference = state.safe(state.associationId(node), {
		before: value,
		after: "]",
		...tracker.current(),
	});
	subexit();
	state.stack = stack;
	exit();

	if (type === "full" || !alt || alt !== reference) {
		value += tracker.move(reference + "]");
	} else if (type === "shortcut") {
		// Remove the unwanted `[`.
		value = value.slice(0, -1);
	} else {
		value += tracker.move("]");
	}

	return value;
}

/**
 * @returns {string}
 */
function imageReferencePeek() {
	return "!";
}

/**
 * @import {State} from 'mdast-util-to-markdown'
 * @import {InlineCode, Parents} from 'mdast'
 */

inlineCode$1.peek = inlineCodePeek;

/**
 * @param {InlineCode} node
 * @param {Parents | undefined} _
 * @param {State} state
 * @returns {string}
 */
function inlineCode$1(node, _, state) {
	let value = node.value || "";
	let sequence = "`";
	let index = -1;

	// If there is a single grave accent on its own in the code, use a fence of
	// two.
	// If there are two in a row, use one.
	while (new RegExp("(^|[^`])" + sequence + "([^`]|$)").test(value)) {
		sequence += "`";
	}

	// If this is not just spaces or eols (tabs don’t count), and either the
	// first or last character are a space, eol, or tick, then pad with spaces.
	if (/[^ \r\n]/.test(value) && ((/^[ \r\n]/.test(value) && /[ \r\n]$/.test(value)) || /^`|`$/.test(value))) {
		value = " " + value + " ";
	}

	// We have a potential problem: certain characters after eols could result in
	// blocks being seen.
	// For example, if someone injected the string `'\n# b'`, then that would
	// result in an ATX heading.
	// We can’t escape characters in `inlineCode`, but because eols are
	// transformed to spaces when going from markdown to HTML anyway, we can swap
	// them out.
	while (++index < state.unsafe.length) {
		const pattern = state.unsafe[index];
		const expression = state.compilePattern(pattern);
		/** @type {RegExpExecArray | null} */
		let match;

		// Only look for `atBreak`s.
		// Btw: note that `atBreak` patterns will always start the regex at LF or
		// CR.
		if (!pattern.atBreak) continue;

		while ((match = expression.exec(value))) {
			let position = match.index;

			// Support CRLF (patterns only look for one of the characters).
			if (value.charCodeAt(position) === 10 /* `\n` */ && value.charCodeAt(position - 1) === 13 /* `\r` */) {
				position--;
			}

			value = value.slice(0, position) + " " + value.slice(match.index + 1);
		}
	}

	return sequence + value + sequence;
}

/**
 * @returns {string}
 */
function inlineCodePeek() {
	return "`";
}

/**
 * @import {State} from 'mdast-util-to-markdown'
 * @import {Link} from 'mdast'
 */

/**
 * @param {Link} node
 * @param {State} state
 * @returns {boolean}
 */
function formatLinkAsAutolink(node, state) {
	const raw = toString(node);

	return Boolean(
		!state.options.resourceLink &&
		// If there’s a url…
		node.url &&
		// And there’s a no title…
		!node.title &&
		// And the content of `node` is a single text node…
		node.children &&
		node.children.length === 1 &&
		node.children[0].type === "text" &&
		// And if the url is the same as the content…
		(raw === node.url || "mailto:" + raw === node.url) &&
		// And that starts w/ a protocol…
		/^[a-z][a-z+.-]+:/i.test(node.url) &&
		// And that doesn’t contain ASCII control codes (character escapes and
		// references don’t work), space, or angle brackets…
		!/[\0- <>\u007F]/.test(node.url),
	);
}

/**
 * @import {Info, State} from 'mdast-util-to-markdown'
 * @import {Link, Parents} from 'mdast'
 * @import {Exit} from '../types.js'
 */

link$1.peek = linkPeek;

/**
 * @param {Link} node
 * @param {Parents | undefined} _
 * @param {State} state
 * @param {Info} info
 * @returns {string}
 */
function link$1(node, _, state, info) {
	const quote = checkQuote(state);
	const suffix = quote === '"' ? "Quote" : "Apostrophe";
	const tracker = state.createTracker(info);
	/** @type {Exit} */
	let exit;
	/** @type {Exit} */
	let subexit;

	if (formatLinkAsAutolink(node, state)) {
		// Hide the fact that we’re in phrasing, because escapes don’t work.
		const stack = state.stack;
		state.stack = [];
		exit = state.enter("autolink");
		let value = tracker.move("<");
		value += tracker.move(
			state.containerPhrasing(node, {
				before: value,
				after: ">",
				...tracker.current(),
			}),
		);
		value += tracker.move(">");
		exit();
		state.stack = stack;
		return value;
	}

	exit = state.enter("link");
	subexit = state.enter("label");
	let value = tracker.move("[");
	value += tracker.move(
		state.containerPhrasing(node, {
			before: value,
			after: "](",
			...tracker.current(),
		}),
	);
	value += tracker.move("](");
	subexit();

	if (
		// If there’s no url but there is a title…
		(!node.url && node.title) ||
		// If there are control characters or whitespace.
		/[\0- \u007F]/.test(node.url)
	) {
		subexit = state.enter("destinationLiteral");
		value += tracker.move("<");
		value += tracker.move(state.safe(node.url, { before: value, after: ">", ...tracker.current() }));
		value += tracker.move(">");
	} else {
		// No whitespace, raw is prettier.
		subexit = state.enter("destinationRaw");
		value += tracker.move(
			state.safe(node.url, {
				before: value,
				after: node.title ? " " : ")",
				...tracker.current(),
			}),
		);
	}

	subexit();

	if (node.title) {
		subexit = state.enter(`title${suffix}`);
		value += tracker.move(" " + quote);
		value += tracker.move(
			state.safe(node.title, {
				before: value,
				after: quote,
				...tracker.current(),
			}),
		);
		value += tracker.move(quote);
		subexit();
	}

	value += tracker.move(")");

	exit();
	return value;
}

/**
 * @param {Link} node
 * @param {Parents | undefined} _
 * @param {State} state
 * @returns {string}
 */
function linkPeek(node, _, state) {
	return formatLinkAsAutolink(node, state) ? "<" : "[";
}

/**
 * @import {Info, State} from 'mdast-util-to-markdown'
 * @import {LinkReference, Parents} from 'mdast'
 */

linkReference$1.peek = linkReferencePeek;

/**
 * @param {LinkReference} node
 * @param {Parents | undefined} _
 * @param {State} state
 * @param {Info} info
 * @returns {string}
 */
function linkReference$1(node, _, state, info) {
	const type = node.referenceType;
	const exit = state.enter("linkReference");
	let subexit = state.enter("label");
	const tracker = state.createTracker(info);
	let value = tracker.move("[");
	const text = state.containerPhrasing(node, {
		before: value,
		after: "]",
		...tracker.current(),
	});
	value += tracker.move(text + "][");

	subexit();
	// Hide the fact that we’re in phrasing, because escapes don’t work.
	const stack = state.stack;
	state.stack = [];
	subexit = state.enter("reference");
	// Note: for proper tracking, we should reset the output positions when we end
	// up making a `shortcut` reference, because then there is no brace output.
	// Practically, in that case, there is no content, so it doesn’t matter that
	// we’ve tracked one too many characters.
	const reference = state.safe(state.associationId(node), {
		before: value,
		after: "]",
		...tracker.current(),
	});
	subexit();
	state.stack = stack;
	exit();

	if (type === "full" || !text || text !== reference) {
		value += tracker.move(reference + "]");
	} else if (type === "shortcut") {
		// Remove the unwanted `[`.
		value = value.slice(0, -1);
	} else {
		value += tracker.move("]");
	}

	return value;
}

/**
 * @returns {string}
 */
function linkReferencePeek() {
	return "[";
}

/**
 * @import {Options, State} from 'mdast-util-to-markdown'
 */

/**
 * @param {State} state
 * @returns {Exclude<Options['bullet'], null | undefined>}
 */
function checkBullet(state) {
	const marker = state.options.bullet || "*";

	if (marker !== "*" && marker !== "+" && marker !== "-") {
		throw new Error("Cannot serialize items with `" + marker + "` for `options.bullet`, expected `*`, `+`, or `-`");
	}

	return marker;
}

/**
 * @import {Options, State} from 'mdast-util-to-markdown'
 */

/**
 * @param {State} state
 * @returns {Exclude<Options['bullet'], null | undefined>}
 */
function checkBulletOther(state) {
	const bullet = checkBullet(state);
	const bulletOther = state.options.bulletOther;

	if (!bulletOther) {
		return bullet === "*" ? "-" : "*";
	}

	if (bulletOther !== "*" && bulletOther !== "+" && bulletOther !== "-") {
		throw new Error("Cannot serialize items with `" + bulletOther + "` for `options.bulletOther`, expected `*`, `+`, or `-`");
	}

	if (bulletOther === bullet) {
		throw new Error("Expected `bullet` (`" + bullet + "`) and `bulletOther` (`" + bulletOther + "`) to be different");
	}

	return bulletOther;
}

/**
 * @import {Options, State} from 'mdast-util-to-markdown'
 */

/**
 * @param {State} state
 * @returns {Exclude<Options['bulletOrdered'], null | undefined>}
 */
function checkBulletOrdered(state) {
	const marker = state.options.bulletOrdered || ".";

	if (marker !== "." && marker !== ")") {
		throw new Error("Cannot serialize items with `" + marker + "` for `options.bulletOrdered`, expected `.` or `)`");
	}

	return marker;
}

/**
 * @import {Options, State} from 'mdast-util-to-markdown'
 */

/**
 * @param {State} state
 * @returns {Exclude<Options['rule'], null | undefined>}
 */
function checkRule(state) {
	const marker = state.options.rule || "*";

	if (marker !== "*" && marker !== "-" && marker !== "_") {
		throw new Error("Cannot serialize rules with `" + marker + "` for `options.rule`, expected `*`, `-`, or `_`");
	}

	return marker;
}

/**
 * @import {Info, State} from 'mdast-util-to-markdown'
 * @import {List, Parents} from 'mdast'
 */

/**
 * @param {List} node
 * @param {Parents | undefined} parent
 * @param {State} state
 * @param {Info} info
 * @returns {string}
 */
function list$2(node, parent, state, info) {
	const exit = state.enter("list");
	const bulletCurrent = state.bulletCurrent;
	/** @type {string} */
	let bullet = node.ordered ? checkBulletOrdered(state) : checkBullet(state);
	/** @type {string} */
	const bulletOther =
		node.ordered ?
			bullet === "." ?
				")"
			:	"."
		:	checkBulletOther(state);
	let useDifferentMarker = parent && state.bulletLastUsed ? bullet === state.bulletLastUsed : false;

	if (!node.ordered) {
		const firstListItem = node.children ? node.children[0] : undefined;

		// If there’s an empty first list item directly in two list items,
		// we have to use a different bullet:
		//
		// ```markdown
		// * - *
		// ```
		//
		// …because otherwise it would become one big thematic break.
		if (
			// Bullet could be used as a thematic break marker:
			(bullet === "*" || bullet === "-") &&
			// Empty first list item:
			firstListItem &&
			(!firstListItem.children || !firstListItem.children[0]) &&
			// Directly in two other list items:
			state.stack[state.stack.length - 1] === "list" &&
			state.stack[state.stack.length - 2] === "listItem" &&
			state.stack[state.stack.length - 3] === "list" &&
			state.stack[state.stack.length - 4] === "listItem" &&
			// That are each the first child.
			state.indexStack[state.indexStack.length - 1] === 0 &&
			state.indexStack[state.indexStack.length - 2] === 0 &&
			state.indexStack[state.indexStack.length - 3] === 0
		) {
			useDifferentMarker = true;
		}

		// If there’s a thematic break at the start of the first list item,
		// we have to use a different bullet:
		//
		// ```markdown
		// * ---
		// ```
		//
		// …because otherwise it would become one big thematic break.
		if (checkRule(state) === bullet && firstListItem) {
			let index = -1;

			while (++index < node.children.length) {
				const item = node.children[index];

				if (item && item.type === "listItem" && item.children && item.children[0] && item.children[0].type === "thematicBreak") {
					useDifferentMarker = true;
					break;
				}
			}
		}
	}

	if (useDifferentMarker) {
		bullet = bulletOther;
	}

	state.bulletCurrent = bullet;
	const value = state.containerFlow(node, info);
	state.bulletLastUsed = bullet;
	state.bulletCurrent = bulletCurrent;
	exit();
	return value;
}

/**
 * @import {Options, State} from 'mdast-util-to-markdown'
 */

/**
 * @param {State} state
 * @returns {Exclude<Options['listItemIndent'], null | undefined>}
 */
function checkListItemIndent(state) {
	const style = state.options.listItemIndent || "one";

	if (style !== "tab" && style !== "one" && style !== "mixed") {
		throw new Error("Cannot serialize items with `" + style + "` for `options.listItemIndent`, expected `tab`, `one`, or `mixed`");
	}

	return style;
}

/**
 * @import {Info, Map, State} from 'mdast-util-to-markdown'
 * @import {ListItem, Parents} from 'mdast'
 */

/**
 * @param {ListItem} node
 * @param {Parents | undefined} parent
 * @param {State} state
 * @param {Info} info
 * @returns {string}
 */
function listItem$1(node, parent, state, info) {
	const listItemIndent = checkListItemIndent(state);
	let bullet = state.bulletCurrent || checkBullet(state);

	// Add the marker value for ordered lists.
	if (parent && parent.type === "list" && parent.ordered) {
		bullet = (typeof parent.start === "number" && parent.start > -1 ? parent.start : 1) + (state.options.incrementListMarker === false ? 0 : parent.children.indexOf(node)) + bullet;
	}

	let size = bullet.length + 1;

	if (listItemIndent === "tab" || (listItemIndent === "mixed" && ((parent && parent.type === "list" && parent.spread) || node.spread))) {
		size = Math.ceil(size / 4) * 4;
	}

	const tracker = state.createTracker(info);
	tracker.move(bullet + " ".repeat(size - bullet.length));
	tracker.shift(size);
	const exit = state.enter("listItem");
	const value = state.indentLines(state.containerFlow(node, tracker.current()), map);
	exit();

	return value;

	/** @type {Map} */
	function map(line, index, blank) {
		if (index) {
			return (blank ? "" : " ".repeat(size)) + line;
		}

		return (blank ? bullet : bullet + " ".repeat(size - bullet.length)) + line;
	}
}

/**
 * @import {Info, State} from 'mdast-util-to-markdown'
 * @import {Paragraph, Parents} from 'mdast'
 */

/**
 * @param {Paragraph} node
 * @param {Parents | undefined} _
 * @param {State} state
 * @param {Info} info
 * @returns {string}
 */
function paragraph$1(node, _, state, info) {
	const exit = state.enter("paragraph");
	const subexit = state.enter("phrasing");
	const value = state.containerPhrasing(node, info);
	subexit();
	exit();
	return value;
}

/**
 * @import {Info, State} from 'mdast-util-to-markdown'
 * @import {Parents, Root} from 'mdast'
 */

/**
 * @param {Root} node
 * @param {Parents | undefined} _
 * @param {State} state
 * @param {Info} info
 * @returns {string}
 */
function root$3(node, _, state, info) {
	// Note: `html` nodes are ambiguous.
	const hasPhrasing = node.children.some(function (d) {
		return phrasing$1(d);
	});

	const container = hasPhrasing ? state.containerPhrasing : state.containerFlow;
	return container.call(state, node, info);
}

/**
 * @import {Options, State} from 'mdast-util-to-markdown'
 */

/**
 * @param {State} state
 * @returns {Exclude<Options['strong'], null | undefined>}
 */
function checkStrong(state) {
	const marker = state.options.strong || "*";

	if (marker !== "*" && marker !== "_") {
		throw new Error("Cannot serialize strong with `" + marker + "` for `options.strong`, expected `*`, or `_`");
	}

	return marker;
}

/**
 * @import {Info, State} from 'mdast-util-to-markdown'
 * @import {Parents, Strong} from 'mdast'
 */

strong$1.peek = strongPeek;

/**
 * @param {Strong} node
 * @param {Parents | undefined} _
 * @param {State} state
 * @param {Info} info
 * @returns {string}
 */
function strong$1(node, _, state, info) {
	const marker = checkStrong(state);
	const exit = state.enter("strong");
	const tracker = state.createTracker(info);
	const before = tracker.move(marker + marker);

	let between = tracker.move(
		state.containerPhrasing(node, {
			after: marker,
			before,
			...tracker.current(),
		}),
	);
	const betweenHead = between.charCodeAt(0);
	const open = encodeInfo(info.before.charCodeAt(info.before.length - 1), betweenHead, marker);

	if (open.inside) {
		between = encodeCharacterReference(betweenHead) + between.slice(1);
	}

	const betweenTail = between.charCodeAt(between.length - 1);
	const close = encodeInfo(info.after.charCodeAt(0), betweenTail, marker);

	if (close.inside) {
		between = between.slice(0, -1) + encodeCharacterReference(betweenTail);
	}

	const after = tracker.move(marker + marker);

	exit();

	state.attentionEncodeSurroundingInfo = {
		after: close.outside,
		before: open.outside,
	};
	return before + between + after;
}

/**
 * @param {Strong} _
 * @param {Parents | undefined} _1
 * @param {State} state
 * @returns {string}
 */
function strongPeek(_, _1, state) {
	return state.options.strong || "*";
}

/**
 * @import {Info, State} from 'mdast-util-to-markdown'
 * @import {Parents, Text} from 'mdast'
 */

/**
 * @param {Text} node
 * @param {Parents | undefined} _
 * @param {State} state
 * @param {Info} info
 * @returns {string}
 */
function text$6(node, _, state, info) {
	return state.safe(node.value, info);
}

/**
 * @import {Options, State} from 'mdast-util-to-markdown'
 */

/**
 * @param {State} state
 * @returns {Exclude<Options['ruleRepetition'], null | undefined>}
 */
function checkRuleRepetition(state) {
	const repetition = state.options.ruleRepetition || 3;

	if (repetition < 3) {
		throw new Error("Cannot serialize rules with repetition `" + repetition + "` for `options.ruleRepetition`, expected `3` or more");
	}

	return repetition;
}

/**
 * @import {State} from 'mdast-util-to-markdown'
 * @import {Parents, ThematicBreak} from 'mdast'
 */

/**
 * @param {ThematicBreak} _
 * @param {Parents | undefined} _1
 * @param {State} state
 * @returns {string}
 */
function thematicBreak$2(_, _1, state) {
	const value = (checkRule(state) + (state.options.ruleSpaces ? " " : "")).repeat(checkRuleRepetition(state));

	return state.options.ruleSpaces ? value.slice(0, -1) : value;
}

/**
 * Default (CommonMark) handlers.
 */
const handle$1 = {
	blockquote: blockquote$1,
	break: hardBreak$1,
	code: code$2,
	definition: definition$1,
	emphasis: emphasis$1,
	hardBreak: hardBreak$1,
	heading: heading$1,
	html: html$3,
	image: image$1,
	imageReference: imageReference$1,
	inlineCode: inlineCode$1,
	link: link$1,
	linkReference: linkReference$1,
	list: list$2,
	listItem: listItem$1,
	paragraph: paragraph$1,
	root: root$3,
	strong: strong$1,
	text: text$6,
	thematicBreak: thematicBreak$2,
};

/**
 * @import {Join} from 'mdast-util-to-markdown'
 */

/** @type {Array<Join>} */
const join = [joinDefaults];

/** @type {Join} */
function joinDefaults(left, right, parent, state) {
	// Indented code after list or another indented code.
	if (right.type === "code" && formatCodeAsIndented(right, state) && (left.type === "list" || (left.type === right.type && formatCodeAsIndented(left, state)))) {
		return false;
	}

	// Join children of a list or an item.
	// In which case, `parent` has a `spread` field.
	if ("spread" in parent && typeof parent.spread === "boolean") {
		if (
			left.type === "paragraph" &&
			// Two paragraphs.
			(left.type === right.type ||
				right.type === "definition" ||
				// Paragraph followed by a setext heading.
				(right.type === "heading" && formatHeadingAsSetext(right, state)))
		) {
			return;
		}

		return parent.spread ? 1 : 0;
	}
}

/**
 * @import {ConstructName, Unsafe} from 'mdast-util-to-markdown'
 */

/**
 * List of constructs that occur in phrasing (paragraphs, headings), but cannot
 * contain things like attention (emphasis, strong), images, or links.
 * So they sort of cancel each other out.
 * Note: could use a better name.
 *
 * @type {Array<ConstructName>}
 */
const fullPhrasingSpans = ["autolink", "destinationLiteral", "destinationRaw", "reference", "titleQuote", "titleApostrophe"];

/** @type {Array<Unsafe>} */
const unsafe = [
	{ character: "\t", after: "[\\r\\n]", inConstruct: "phrasing" },
	{ character: "\t", before: "[\\r\\n]", inConstruct: "phrasing" },
	{
		character: "\t",
		inConstruct: ["codeFencedLangGraveAccent", "codeFencedLangTilde"],
	},
	{
		character: "\r",
		inConstruct: ["codeFencedLangGraveAccent", "codeFencedLangTilde", "codeFencedMetaGraveAccent", "codeFencedMetaTilde", "destinationLiteral", "headingAtx"],
	},
	{
		character: "\n",
		inConstruct: ["codeFencedLangGraveAccent", "codeFencedLangTilde", "codeFencedMetaGraveAccent", "codeFencedMetaTilde", "destinationLiteral", "headingAtx"],
	},
	{ character: " ", after: "[\\r\\n]", inConstruct: "phrasing" },
	{ character: " ", before: "[\\r\\n]", inConstruct: "phrasing" },
	{
		character: " ",
		inConstruct: ["codeFencedLangGraveAccent", "codeFencedLangTilde"],
	},
	// An exclamation mark can start an image, if it is followed by a link or
	// a link reference.
	{
		character: "!",
		after: "\\[",
		inConstruct: "phrasing",
		notInConstruct: fullPhrasingSpans,
	},
	// A quote can break out of a title.
	{ character: '"', inConstruct: "titleQuote" },
	// A number sign could start an ATX heading if it starts a line.
	{ atBreak: true, character: "#" },
	{ character: "#", inConstruct: "headingAtx", after: "(?:[\r\n]|$)" },
	// Dollar sign and percentage are not used in markdown.
	// An ampersand could start a character reference.
	{ character: "&", after: "[#A-Za-z]", inConstruct: "phrasing" },
	// An apostrophe can break out of a title.
	{ character: "'", inConstruct: "titleApostrophe" },
	// A left paren could break out of a destination raw.
	{ character: "(", inConstruct: "destinationRaw" },
	// A left paren followed by `]` could make something into a link or image.
	{
		before: "\\]",
		character: "(",
		inConstruct: "phrasing",
		notInConstruct: fullPhrasingSpans,
	},
	// A right paren could start a list item or break out of a destination
	// raw.
	{ atBreak: true, before: "\\d+", character: ")" },
	{ character: ")", inConstruct: "destinationRaw" },
	// An asterisk can start thematic breaks, list items, emphasis, strong.
	{ atBreak: true, character: "*", after: "(?:[ \t\r\n*])" },
	{ character: "*", inConstruct: "phrasing", notInConstruct: fullPhrasingSpans },
	// A plus sign could start a list item.
	{ atBreak: true, character: "+", after: "(?:[ \t\r\n])" },
	// A dash can start thematic breaks, list items, and setext heading
	// underlines.
	{ atBreak: true, character: "-", after: "(?:[ \t\r\n-])" },
	// A dot could start a list item.
	{ atBreak: true, before: "\\d+", character: ".", after: "(?:[ \t\r\n]|$)" },
	// Slash, colon, and semicolon are not used in markdown for constructs.
	// A less than can start html (flow or text) or an autolink.
	// HTML could start with an exclamation mark (declaration, cdata, comment),
	// slash (closing tag), question mark (instruction), or a letter (tag).
	// An autolink also starts with a letter.
	// Finally, it could break out of a destination literal.
	{ atBreak: true, character: "<", after: "[!/?A-Za-z]" },
	{
		character: "<",
		after: "[!/?A-Za-z]",
		inConstruct: "phrasing",
		notInConstruct: fullPhrasingSpans,
	},
	{ character: "<", inConstruct: "destinationLiteral" },
	// An equals to can start setext heading underlines.
	{ atBreak: true, character: "=" },
	// A greater than can start block quotes and it can break out of a
	// destination literal.
	{ atBreak: true, character: ">" },
	{ character: ">", inConstruct: "destinationLiteral" },
	// Question mark and at sign are not used in markdown for constructs.
	// A left bracket can start definitions, references, labels,
	{ atBreak: true, character: "[" },
	{ character: "[", inConstruct: "phrasing", notInConstruct: fullPhrasingSpans },
	{ character: "[", inConstruct: ["label", "reference"] },
	// A backslash can start an escape (when followed by punctuation) or a
	// hard break (when followed by an eol).
	// Note: typical escapes are handled in `safe`!
	{ character: "\\", after: "[\\r\\n]", inConstruct: "phrasing" },
	// A right bracket can exit labels.
	{ character: "]", inConstruct: ["label", "reference"] },
	// Caret is not used in markdown for constructs.
	// An underscore can start emphasis, strong, or a thematic break.
	{ atBreak: true, character: "_" },
	{ character: "_", inConstruct: "phrasing", notInConstruct: fullPhrasingSpans },
	// A grave accent can start code (fenced or text), or it can break out of
	// a grave accent code fence.
	{ atBreak: true, character: "`" },
	{
		character: "`",
		inConstruct: ["codeFencedLangGraveAccent", "codeFencedMetaGraveAccent"],
	},
	{ character: "`", inConstruct: "phrasing", notInConstruct: fullPhrasingSpans },
	// Left brace, vertical bar, right brace are not used in markdown for
	// constructs.
	// A tilde can start code (fenced).
	{ atBreak: true, character: "~" },
];

/// <reference lib="dom" />

/* global document */

const element$2 = document.createElement("i");

/**
 * @param {string} value
 * @returns {string | false}
 */
function decodeNamedCharacterReference(value) {
	const characterReference = "&" + value + ";";
	element$2.innerHTML = characterReference;
	const character = element$2.textContent;

	// Some named character references do not require the closing semicolon
	// (`&not`, for instance), which leads to situations where parsing the assumed
	// named reference of `&notit;` will result in the string `¬it;`.
	// When we encounter a trailing semicolon after parsing, and the character
	// reference to decode was not a semicolon (`&semi;`), we can assume that the
	// matching was not complete.
	if (character.charCodeAt(character.length - 1) === 59 /* `;` */ && value !== "semi") {
		return false;
	}

	// If the decoded string is equal to the input, the character reference was
	// not valid.
	return character === characterReference ? false : character;
}

/**
 * Turn the number (in string form as either hexa- or plain decimal) coming from
 * a numeric character reference into a character.
 *
 * Sort of like `String.fromCodePoint(Number.parseInt(value, base))`, but makes
 * non-characters and control characters safe.
 *
 * @param {string} value
 *   Value to decode.
 * @param {number} base
 *   Numeric base.
 * @returns {string}
 *   Character.
 */
function decodeNumericCharacterReference(value, base) {
	const code = Number.parseInt(value, base);
	if (
		// C0 except for HT, LF, FF, CR, space.
		code < 9 ||
		code === 11 ||
		(code > 13 && code < 32) ||
		// Control character (DEL) of C0, and C1 controls.
		(code > 126 && code < 160) ||
		// Lone high surrogates and low surrogates.
		(code > 55_295 && code < 57_344) ||
		// Noncharacters.
		(code > 64_975 && code < 65_008) /* eslint-disable no-bitwise */ ||
		(code & 65_535) === 65_535 ||
		(code & 65_535) === 65_534 /* eslint-enable no-bitwise */ ||
		// Out of range
		code > 1_114_111
	) {
		return "\uFFFD";
	}
	return String.fromCodePoint(code);
}

const characterEscapeOrReference = /\\([!-/:-@[-`{-~])|&(#(?:\d{1,7}|x[\da-f]{1,6})|[\da-z]{1,31});/gi;

/**
 * Decode markdown strings (which occur in places such as fenced code info
 * strings, destinations, labels, and titles).
 *
 * The “string” content type allows character escapes and -references.
 * This decodes those.
 *
 * @param {string} value
 *   Value to decode.
 * @returns {string}
 *   Decoded value.
 */
function decodeString(value) {
	return value.replace(characterEscapeOrReference, decode);
}

/**
 * @param {string} $0
 *   Match.
 * @param {string} $1
 *   Character escape.
 * @param {string} $2
 *   Character reference.
 * @returns {string}
 *   Decoded value
 */
function decode($0, $1, $2) {
	if ($1) {
		// Escape.
		return $1;
	}

	// Reference.
	const head = $2.charCodeAt(0);
	if (head === 35) {
		const head = $2.charCodeAt(1);
		const hex = head === 120 || head === 88;
		return decodeNumericCharacterReference($2.slice(hex ? 2 : 1), hex ? 16 : 10);
	}
	return decodeNamedCharacterReference($2) || $0;
}

/**
 * @import {AssociationId} from '../types.js'
 */

/**
 * Get an identifier from an association to match it to others.
 *
 * Associations are nodes that match to something else through an ID:
 * <https://github.com/syntax-tree/mdast#association>.
 *
 * The `label` of an association is the string value: character escapes and
 * references work, and casing is intact.
 * The `identifier` is used to match one association to another:
 * controversially, character escapes and references don’t work in this
 * matching: `&copy;` does not match `©`, and `\+` does not match `+`.
 *
 * But casing is ignored (and whitespace) is trimmed and collapsed: ` A\nb`
 * matches `a b`.
 * So, we do prefer the label when figuring out how we’re going to serialize:
 * it has whitespace, casing, and we can ignore most useless character
 * escapes and all character references.
 *
 * @type {AssociationId}
 */
function association(node) {
	if (node.label || !node.identifier) {
		return node.label || "";
	}

	return decodeString(node.identifier);
}

/**
 * @import {CompilePattern} from '../types.js'
 */

/**
 * @type {CompilePattern}
 */
function compilePattern(pattern) {
	if (!pattern._compiled) {
		const before = (pattern.atBreak ? "[\\r\\n][\\t ]*" : "") + (pattern.before ? "(?:" + pattern.before + ")" : "");

		pattern._compiled = new RegExp(
			(before ? "(" + before + ")" : "") + (/[|\\{}()[\]^$+*?.-]/.test(pattern.character) ? "\\" : "") + pattern.character + (pattern.after ? "(?:" + pattern.after + ")" : ""),
			"g",
		);
	}

	return pattern._compiled;
}

/**
 * @import {Handle, Info, State} from 'mdast-util-to-markdown'
 * @import {PhrasingParents} from '../types.js'
 */

/**
 * Serialize the children of a parent that contains phrasing children.
 *
 * These children will be joined flush together.
 *
 * @param {PhrasingParents} parent
 *   Parent of flow nodes.
 * @param {State} state
 *   Info passed around about the current state.
 * @param {Info} info
 *   Info on where we are in the document we are generating.
 * @returns {string}
 *   Serialized children, joined together.
 */
function containerPhrasing(parent, state, info) {
	const indexStack = state.indexStack;
	const children = parent.children || [];
	/** @type {Array<string>} */
	const results = [];
	let index = -1;
	let before = info.before;
	/** @type {string | undefined} */
	let encodeAfter;

	indexStack.push(-1);
	let tracker = state.createTracker(info);

	while (++index < children.length) {
		const child = children[index];
		/** @type {string} */
		let after;

		indexStack[indexStack.length - 1] = index;

		if (index + 1 < children.length) {
			/** @type {Handle} */
			// @ts-expect-error: hush, it’s actually a `zwitch`.
			let handle = state.handle.handlers[children[index + 1].type];
			/** @type {Handle} */
			// @ts-expect-error: hush, it’s actually a `zwitch`.
			if (handle && handle.peek) handle = handle.peek;
			after =
				handle ?
					handle(children[index + 1], parent, state, {
						before: "",
						after: "",
						...tracker.current(),
					}).charAt(0)
				:	"";
		} else {
			after = info.after;
		}

		// In some cases, html (text) can be found in phrasing right after an eol.
		// When we’d serialize that, in most cases that would be seen as html
		// (flow).
		// As we can’t escape or so to prevent it from happening, we take a somewhat
		// reasonable approach: replace that eol with a space.
		// See: <https://github.com/syntax-tree/mdast-util-to-markdown/issues/15>
		if (results.length > 0 && (before === "\r" || before === "\n") && child.type === "html") {
			results[results.length - 1] = results[results.length - 1].replace(/(\r?\n|\r)$/, " ");
			before = " ";

			// To do: does this work to reset tracker?
			tracker = state.createTracker(info);
			tracker.move(results.join(""));
		}

		let value = state.handle(child, parent, state, {
			...tracker.current(),
			after,
			before,
		});

		// If we had to encode the first character after the previous node and it’s
		// still the same character,
		// encode it.
		if (encodeAfter && encodeAfter === value.slice(0, 1)) {
			value = encodeCharacterReference(encodeAfter.charCodeAt(0)) + value.slice(1);
		}

		const encodingInfo = state.attentionEncodeSurroundingInfo;
		state.attentionEncodeSurroundingInfo = undefined;
		encodeAfter = undefined;

		// If we have to encode the first character before the current node and
		// it’s still the same character,
		// encode it.
		if (encodingInfo) {
			if (results.length > 0 && encodingInfo.before && before === results[results.length - 1].slice(-1)) {
				results[results.length - 1] = results[results.length - 1].slice(0, -1) + encodeCharacterReference(before.charCodeAt(0));
			}

			if (encodingInfo.after) encodeAfter = after;
		}

		tracker.move(value);
		results.push(value);
		before = value.slice(-1);
	}

	indexStack.pop();

	return results.join("");
}

/**
 * @import {State} from 'mdast-util-to-markdown'
 * @import {FlowChildren, FlowParents, TrackFields} from '../types.js'
 */

/**
 * @param {FlowParents} parent
 *   Parent of flow nodes.
 * @param {State} state
 *   Info passed around about the current state.
 * @param {TrackFields} info
 *   Info on where we are in the document we are generating.
 * @returns {string}
 *   Serialized children, joined by (blank) lines.
 */
function containerFlow(parent, state, info) {
	const indexStack = state.indexStack;
	const children = parent.children || [];
	const tracker = state.createTracker(info);
	/** @type {Array<string>} */
	const results = [];
	let index = -1;

	indexStack.push(-1);

	while (++index < children.length) {
		const child = children[index];

		indexStack[indexStack.length - 1] = index;

		results.push(
			tracker.move(
				state.handle(child, parent, state, {
					before: "\n",
					after: "\n",
					...tracker.current(),
				}),
			),
		);

		if (child.type !== "list") {
			state.bulletLastUsed = undefined;
		}

		if (index < children.length - 1) {
			results.push(tracker.move(between(child, children[index + 1], parent, state)));
		}
	}

	indexStack.pop();

	return results.join("");
}

/**
 * @param {FlowChildren} left
 * @param {FlowChildren} right
 * @param {FlowParents} parent
 * @param {State} state
 * @returns {string}
 */
function between(left, right, parent, state) {
	let index = state.join.length;

	while (index--) {
		const result = state.join[index](left, right, parent, state);

		if (result === true || result === 1) {
			break;
		}

		if (typeof result === "number") {
			return "\n".repeat(1 + result);
		}

		if (result === false) {
			return "\n\n<!---->\n\n";
		}
	}

	return "\n\n";
}

/**
 * @import {IndentLines} from '../types.js'
 */

const eol = /\r?\n|\r/g;

/**
 * @type {IndentLines}
 */
function indentLines(value, map) {
	/** @type {Array<string>} */
	const result = [];
	let start = 0;
	let line = 0;
	/** @type {RegExpExecArray | null} */
	let match;

	while ((match = eol.exec(value))) {
		one(value.slice(start, match.index));
		result.push(match[0]);
		start = match.index + match[0].length;
		line++;
	}

	one(value.slice(start));

	return result.join("");

	/**
	 * @param {string} value
	 */
	function one(value) {
		result.push(map(value, line, !value));
	}
}

/**
 * @import {SafeConfig, State} from 'mdast-util-to-markdown'
 */

/**
 * Make a string safe for embedding in markdown constructs.
 *
 * In markdown, almost all punctuation characters can, in certain cases,
 * result in something.
 * Whether they do is highly subjective to where they happen and in what
 * they happen.
 *
 * To solve this, `mdast-util-to-markdown` tracks:
 *
 * * Characters before and after something;
 * * What “constructs” we are in.
 *
 * This information is then used by this function to escape or encode
 * special characters.
 *
 * @param {State} state
 *   Info passed around about the current state.
 * @param {string | null | undefined} input
 *   Raw value to make safe.
 * @param {SafeConfig} config
 *   Configuration.
 * @returns {string}
 *   Serialized markdown safe for embedding.
 */
function safe(state, input, config) {
	const value = (config.before || "") + (input || "") + (config.after || "");
	/** @type {Array<number>} */
	const positions = [];
	/** @type {Array<string>} */
	const result = [];
	/** @type {Record<number, {before: boolean, after: boolean}>} */
	const infos = {};
	let index = -1;

	while (++index < state.unsafe.length) {
		const pattern = state.unsafe[index];

		if (!patternInScope(state.stack, pattern)) {
			continue;
		}

		const expression = state.compilePattern(pattern);
		/** @type {RegExpExecArray | null} */
		let match;

		while ((match = expression.exec(value))) {
			const before = "before" in pattern || Boolean(pattern.atBreak);
			const after = "after" in pattern;
			const position = match.index + (before ? match[1].length : 0);

			if (positions.includes(position)) {
				if (infos[position].before && !before) {
					infos[position].before = false;
				}

				if (infos[position].after && !after) {
					infos[position].after = false;
				}
			} else {
				positions.push(position);
				infos[position] = { before, after };
			}
		}
	}

	positions.sort(numerical);

	let start = config.before ? config.before.length : 0;
	const end = value.length - (config.after ? config.after.length : 0);
	index = -1;

	while (++index < positions.length) {
		const position = positions[index];

		// Character before or after matched:
		if (position < start || position >= end) {
			continue;
		}

		// If this character is supposed to be escaped because it has a condition on
		// the next character, and the next character is definitly being escaped,
		// then skip this escape.
		if (
			(position + 1 < end && positions[index + 1] === position + 1 && infos[position].after && !infos[position + 1].before && !infos[position + 1].after) ||
			(positions[index - 1] === position - 1 && infos[position].before && !infos[position - 1].before && !infos[position - 1].after)
		) {
			continue;
		}

		if (start !== position) {
			// If we have to use a character reference, an ampersand would be more
			// correct, but as backslashes only care about punctuation, either will
			// do the trick
			result.push(escapeBackslashes(value.slice(start, position), "\\"));
		}

		start = position;

		if (/[!-/:-@[-`{-~]/.test(value.charAt(position)) && (!config.encode || !config.encode.includes(value.charAt(position)))) {
			// Character escape.
			result.push("\\");
		} else {
			// Character reference.
			result.push(encodeCharacterReference(value.charCodeAt(position)));
			start++;
		}
	}

	result.push(escapeBackslashes(value.slice(start, end), config.after));

	return result.join("");
}

/**
 * @param {number} a
 * @param {number} b
 * @returns {number}
 */
function numerical(a, b) {
	return a - b;
}

/**
 * @param {string} value
 * @param {string} after
 * @returns {string}
 */
function escapeBackslashes(value, after) {
	const expression = /\\(?=[!-/:-@[-`{-~])/g;
	/** @type {Array<number>} */
	const positions = [];
	/** @type {Array<string>} */
	const results = [];
	const whole = value + after;
	let index = -1;
	let start = 0;
	/** @type {RegExpExecArray | null} */
	let match;

	while ((match = expression.exec(whole))) {
		positions.push(match.index);
	}

	while (++index < positions.length) {
		if (start !== positions[index]) {
			results.push(value.slice(start, positions[index]));
		}

		results.push("\\");
		start = positions[index];
	}

	results.push(value.slice(start));

	return results.join("");
}

/**
 * @import {CreateTracker, TrackCurrent, TrackMove, TrackShift} from '../types.js'
 */

/**
 * Track positional info in the output.
 *
 * @type {CreateTracker}
 */
function track(config) {
	// Defaults are used to prevent crashes when older utilities somehow activate
	// this code.
	/* c8 ignore next 5 */
	const options = config || {};
	const now = options.now || {};
	let lineShift = options.lineShift || 0;
	let line = now.line || 1;
	let column = now.column || 1;

	return { move, current, shift };

	/**
	 * Get the current tracked info.
	 *
	 * @type {TrackCurrent}
	 */
	function current() {
		return { now: { line, column }, lineShift };
	}

	/**
	 * Define an increased line shift (the typical indent for lines).
	 *
	 * @type {TrackShift}
	 */
	function shift(value) {
		lineShift += value;
	}

	/**
	 * Move past some generated markdown.
	 *
	 * @type {TrackMove}
	 */
	function move(input) {
		// eslint-disable-next-line unicorn/prefer-default-parameters
		const value = input || "";
		const chunks = value.split(/\r?\n|\r/g);
		const tail = chunks[chunks.length - 1];
		line += chunks.length - 1;
		column = chunks.length === 1 ? column + tail.length : 1 + tail.length + lineShift;
		return value;
	}
}

/**
 * @import {Info, Join, Options, SafeConfig, State} from 'mdast-util-to-markdown'
 * @import {Nodes} from 'mdast'
 * @import {Enter, FlowParents, PhrasingParents, TrackFields} from './types.js'
 */

/**
 * Turn an mdast syntax tree into markdown.
 *
 * @param {Nodes} tree
 *   Tree to serialize.
 * @param {Options | null | undefined} [options]
 *   Configuration (optional).
 * @returns {string}
 *   Serialized markdown representing `tree`.
 */
function toMarkdown(tree, options) {
	const settings = options || {};
	/** @type {State} */
	const state = {
		associationId: association,
		containerPhrasing: containerPhrasingBound,
		containerFlow: containerFlowBound,
		createTracker: track,
		compilePattern,
		enter,
		// @ts-expect-error: GFM / frontmatter are typed in `mdast` but not defined
		// here.
		handlers: { ...handle$1 },
		// @ts-expect-error: add `handle` in a second.
		handle: undefined,
		indentLines,
		indexStack: [],
		join: [...join],
		options: {},
		safe: safeBound,
		stack: [],
		unsafe: [...unsafe],
	};

	configure$1(state, settings);

	if (state.options.tightDefinitions) {
		state.join.push(joinDefinition);
	}

	state.handle = zwitch("type", {
		invalid: invalid$1,
		unknown: unknown$1,
		handlers: state.handlers,
	});

	let result = state.handle(tree, undefined, state, {
		before: "\n",
		after: "\n",
		now: { line: 1, column: 1 },
		lineShift: 0,
	});

	if (result && result.charCodeAt(result.length - 1) !== 10 && result.charCodeAt(result.length - 1) !== 13) {
		result += "\n";
	}

	return result;

	/** @type {Enter} */
	function enter(name) {
		state.stack.push(name);
		return exit;

		/**
		 * @returns {undefined}
		 */
		function exit() {
			state.stack.pop();
		}
	}
}

/**
 * @param {unknown} value
 * @returns {never}
 */
function invalid$1(value) {
	throw new Error("Cannot handle value `" + value + "`, expected node");
}

/**
 * @param {unknown} value
 * @returns {never}
 */
function unknown$1(value) {
	// Always a node.
	const node = /** @type {Nodes} */ (value);
	throw new Error("Cannot handle unknown node `" + node.type + "`");
}

/** @type {Join} */
function joinDefinition(left, right) {
	// No blank line between adjacent definitions.
	if (left.type === "definition" && left.type === right.type) {
		return 0;
	}
}

/**
 * Serialize the children of a parent that contains phrasing children.
 *
 * These children will be joined flush together.
 *
 * @this {State}
 *   Info passed around about the current state.
 * @param {PhrasingParents} parent
 *   Parent of flow nodes.
 * @param {Info} info
 *   Info on where we are in the document we are generating.
 * @returns {string}
 *   Serialized children, joined together.
 */
function containerPhrasingBound(parent, info) {
	return containerPhrasing(parent, this, info);
}

/**
 * Serialize the children of a parent that contains flow children.
 *
 * These children will typically be joined by blank lines.
 * What they are joined by exactly is defined by `Join` functions.
 *
 * @this {State}
 *   Info passed around about the current state.
 * @param {FlowParents} parent
 *   Parent of flow nodes.
 * @param {TrackFields} info
 *   Info on where we are in the document we are generating.
 * @returns {string}
 *   Serialized children, joined by (blank) lines.
 */
function containerFlowBound(parent, info) {
	return containerFlow(parent, this, info);
}

/**
 * Make a string safe for embedding in markdown constructs.
 *
 * In markdown, almost all punctuation characters can, in certain cases,
 * result in something.
 * Whether they do is highly subjective to where they happen and in what
 * they happen.
 *
 * To solve this, `mdast-util-to-markdown` tracks:
 *
 * * Characters before and after something;
 * * What “constructs” we are in.
 *
 * This information is then used by this function to escape or encode
 * special characters.
 *
 * @this {State}
 *   Info passed around about the current state.
 * @param {string | null | undefined} value
 *   Raw value to make safe.
 * @param {SafeConfig} config
 *   Configuration.
 * @returns {string}
 *   Serialized markdown safe for embedding.
 */
function safeBound(value, config) {
	return safe(this, value, config);
}

/**
 * @typedef {import('mdast').InlineCode} InlineCode
 * @typedef {import('mdast').Table} Table
 * @typedef {import('mdast').TableCell} TableCell
 * @typedef {import('mdast').TableRow} TableRow
 *
 * @typedef {import('markdown-table').Options} MarkdownTableOptions
 *
 * @typedef {import('mdast-util-from-markdown').CompileContext} CompileContext
 * @typedef {import('mdast-util-from-markdown').Extension} FromMarkdownExtension
 * @typedef {import('mdast-util-from-markdown').Handle} FromMarkdownHandle
 *
 * @typedef {import('mdast-util-to-markdown').Options} ToMarkdownExtension
 * @typedef {import('mdast-util-to-markdown').Handle} ToMarkdownHandle
 * @typedef {import('mdast-util-to-markdown').State} State
 * @typedef {import('mdast-util-to-markdown').Info} Info
 */

/**
 * Create an extension for `mdast-util-from-markdown` to enable GFM tables in
 * markdown.
 *
 * @returns {FromMarkdownExtension}
 *   Extension for `mdast-util-from-markdown` to enable GFM tables.
 */
function gfmTableFromMarkdown() {
	return {
		enter: {
			table: enterTable,
			tableData: enterCell,
			tableHeader: enterCell,
			tableRow: enterRow,
		},
		exit: {
			codeText: exitCodeText,
			table: exitTable,
			tableData: exit$1,
			tableHeader: exit$1,
			tableRow: exit$1,
		},
	};
}

/**
 * @this {CompileContext}
 * @type {FromMarkdownHandle}
 */
function enterTable(token) {
	const align = token._align;
	this.enter(
		{
			type: "table",
			align: align.map(function (d) {
				return d === "none" ? null : d;
			}),
			children: [],
		},
		token,
	);
	this.data.inTable = true;
}

/**
 * @this {CompileContext}
 * @type {FromMarkdownHandle}
 */
function exitTable(token) {
	this.exit(token);
	this.data.inTable = undefined;
}

/**
 * @this {CompileContext}
 * @type {FromMarkdownHandle}
 */
function enterRow(token) {
	this.enter({ type: "tableRow", children: [] }, token);
}

/**
 * @this {CompileContext}
 * @type {FromMarkdownHandle}
 */
function exit$1(token) {
	this.exit(token);
}

/**
 * @this {CompileContext}
 * @type {FromMarkdownHandle}
 */
function enterCell(token) {
	this.enter({ type: "tableCell", children: [] }, token);
}

// Overwrite the default code text data handler to unescape escaped pipes when
// they are in tables.
/**
 * @this {CompileContext}
 * @type {FromMarkdownHandle}
 */
function exitCodeText(token) {
	let value = this.resume();

	if (this.data.inTable) {
		value = value.replace(/\\([\\|])/g, replace);
	}

	const node = this.stack[this.stack.length - 1];
	ok$1(node.type === "inlineCode");
	node.value = value;
	this.exit(token);
}

/**
 * @param {string} $0
 * @param {string} $1
 * @returns {string}
 */
function replace($0, $1) {
	// Pipes work, backslashes don’t (but can’t escape pipes).
	return $1 === "|" ? $1 : $0;
}

/**
 * Create an extension for `mdast-util-to-markdown` to enable GFM tables in
 * markdown.
 *
 * @param {Options | null | undefined} [options]
 *   Configuration.
 * @returns {ToMarkdownExtension}
 *   Extension for `mdast-util-to-markdown` to enable GFM tables.
 */
function gfmTableToMarkdown(options) {
	const settings = options || {};
	const padding = settings.tableCellPadding;
	const alignDelimiters = settings.tablePipeAlign;
	const stringLength = settings.stringLength;
	const around = padding ? " " : "|";

	return {
		unsafe: [
			{ character: "\r", inConstruct: "tableCell" },
			{ character: "\n", inConstruct: "tableCell" },
			// A pipe, when followed by a tab or space (padding), or a dash or colon
			// (unpadded delimiter row), could result in a table.
			{ atBreak: true, character: "|", after: "[\t :-]" },
			// A pipe in a cell must be encoded.
			{ character: "|", inConstruct: "tableCell" },
			// A colon must be followed by a dash, in which case it could start a
			// delimiter row.
			{ atBreak: true, character: ":", after: "-" },
			// A delimiter row can also start with a dash, when followed by more
			// dashes, a colon, or a pipe.
			// This is a stricter version than the built in check for lists, thematic
			// breaks, and setex heading underlines though:
			// <https://github.com/syntax-tree/mdast-util-to-markdown/blob/51a2038/lib/unsafe.js#L57>
			{ atBreak: true, character: "-", after: "[:|-]" },
		],
		handlers: {
			inlineCode: inlineCodeWithTable,
			table: handleTable,
			tableCell: handleTableCell,
			tableRow: handleTableRow,
		},
	};

	/**
	 * @type {ToMarkdownHandle}
	 * @param {Table} node
	 */
	function handleTable(node, _, state, info) {
		return serializeData(handleTableAsData(node, state, info), node.align);
	}

	/**
	 * This function isn’t really used normally, because we handle rows at the
	 * table level.
	 * But, if someone passes in a table row, this ensures we make somewhat sense.
	 *
	 * @type {ToMarkdownHandle}
	 * @param {TableRow} node
	 */
	function handleTableRow(node, _, state, info) {
		const row = handleTableRowAsData(node, state, info);
		const value = serializeData([row]);
		// `markdown-table` will always add an align row
		return value.slice(0, value.indexOf("\n"));
	}

	/**
	 * @type {ToMarkdownHandle}
	 * @param {TableCell} node
	 */
	function handleTableCell(node, _, state, info) {
		const exit = state.enter("tableCell");
		const subexit = state.enter("phrasing");
		const value = state.containerPhrasing(node, {
			...info,
			before: around,
			after: around,
		});
		subexit();
		exit();
		return value;
	}

	/**
	 * @param {Array<Array<string>>} matrix
	 * @param {Array<string | null | undefined> | null | undefined} [align]
	 */
	function serializeData(matrix, align) {
		return markdownTable(matrix, {
			align,
			// @ts-expect-error: `markdown-table` types should support `null`.
			alignDelimiters,
			// @ts-expect-error: `markdown-table` types should support `null`.
			padding,
			// @ts-expect-error: `markdown-table` types should support `null`.
			stringLength,
		});
	}

	/**
	 * @param {Table} node
	 * @param {State} state
	 * @param {Info} info
	 */
	function handleTableAsData(node, state, info) {
		const children = node.children;
		let index = -1;
		/** @type {Array<Array<string>>} */
		const result = [];
		const subexit = state.enter("table");

		while (++index < children.length) {
			result[index] = handleTableRowAsData(children[index], state, info);
		}

		subexit();

		return result;
	}

	/**
	 * @param {TableRow} node
	 * @param {State} state
	 * @param {Info} info
	 */
	function handleTableRowAsData(node, state, info) {
		const children = node.children;
		let index = -1;
		/** @type {Array<string>} */
		const result = [];
		const subexit = state.enter("tableRow");

		while (++index < children.length) {
			// Note: the positional info as used here is incorrect.
			// Making it correct would be impossible due to aligning cells?
			// And it would need copy/pasting `markdown-table` into this project.
			result[index] = handleTableCell(children[index], node, state, info);
		}

		subexit();

		return result;
	}

	/**
	 * @type {ToMarkdownHandle}
	 * @param {InlineCode} node
	 */
	function inlineCodeWithTable(node, parent, state) {
		let value = handle$1.inlineCode(node, parent, state);

		if (state.stack.includes("tableCell")) {
			value = value.replace(/\|/g, "\\$&");
		}

		return value;
	}
}

/**
 * @typedef {import('mdast').ListItem} ListItem
 * @typedef {import('mdast').Paragraph} Paragraph
 * @typedef {import('mdast-util-from-markdown').CompileContext} CompileContext
 * @typedef {import('mdast-util-from-markdown').Extension} FromMarkdownExtension
 * @typedef {import('mdast-util-from-markdown').Handle} FromMarkdownHandle
 * @typedef {import('mdast-util-to-markdown').Options} ToMarkdownExtension
 * @typedef {import('mdast-util-to-markdown').Handle} ToMarkdownHandle
 */

/**
 * Create an extension for `mdast-util-from-markdown` to enable GFM task
 * list items in markdown.
 *
 * @returns {FromMarkdownExtension}
 *   Extension for `mdast-util-from-markdown` to enable GFM task list items.
 */
function gfmTaskListItemFromMarkdown() {
	return {
		exit: {
			taskListCheckValueChecked: exitCheck,
			taskListCheckValueUnchecked: exitCheck,
			paragraph: exitParagraphWithTaskListItem,
		},
	};
}

/**
 * Create an extension for `mdast-util-to-markdown` to enable GFM task list
 * items in markdown.
 *
 * @returns {ToMarkdownExtension}
 *   Extension for `mdast-util-to-markdown` to enable GFM task list items.
 */
function gfmTaskListItemToMarkdown() {
	return {
		unsafe: [{ atBreak: true, character: "-", after: "[:|-]" }],
		handlers: { listItem: listItemWithTaskListItem },
	};
}

/**
 * @this {CompileContext}
 * @type {FromMarkdownHandle}
 */
function exitCheck(token) {
	// We’re always in a paragraph, in a list item.
	const node = this.stack[this.stack.length - 2];
	ok$1(node.type === "listItem");
	node.checked = token.type === "taskListCheckValueChecked";
}

/**
 * @this {CompileContext}
 * @type {FromMarkdownHandle}
 */
function exitParagraphWithTaskListItem(token) {
	const parent = this.stack[this.stack.length - 2];

	if (parent && parent.type === "listItem" && typeof parent.checked === "boolean") {
		const node = this.stack[this.stack.length - 1];
		ok$1(node.type === "paragraph");
		const head = node.children[0];

		if (head && head.type === "text") {
			const siblings = parent.children;
			let index = -1;
			/** @type {Paragraph | undefined} */
			let firstParaghraph;

			while (++index < siblings.length) {
				const sibling = siblings[index];
				if (sibling.type === "paragraph") {
					firstParaghraph = sibling;
					break;
				}
			}

			if (firstParaghraph === node) {
				// Must start with a space or a tab.
				head.value = head.value.slice(1);

				if (head.value.length === 0) {
					node.children.shift();
				} else if (node.position && head.position && typeof head.position.start.offset === "number") {
					head.position.start.column++;
					head.position.start.offset++;
					node.position.start = Object.assign({}, head.position.start);
				}
			}
		}
	}

	this.exit(token);
}

/**
 * @type {ToMarkdownHandle}
 * @param {ListItem} node
 */
function listItemWithTaskListItem(node, parent, state, info) {
	const head = node.children[0];
	const checkable = typeof node.checked === "boolean" && head && head.type === "paragraph";
	const checkbox = "[" + (node.checked ? "x" : " ") + "] ";
	const tracker = state.createTracker(info);

	if (checkable) {
		tracker.move(checkbox);
	}

	let value = handle$1.listItem(node, parent, state, {
		...info,
		...tracker.current(),
	});

	if (checkable) {
		value = value.replace(/^(?:[*+-]|\d+\.)([\r\n]| {1,3})/, check);
	}

	return value;

	/**
	 * @param {string} $0
	 * @returns {string}
	 */
	function check($0) {
		return $0 + checkbox;
	}
}

/**
 * @import {Extension as FromMarkdownExtension} from 'mdast-util-from-markdown'
 * @import {Options} from 'mdast-util-gfm'
 * @import {Options as ToMarkdownExtension} from 'mdast-util-to-markdown'
 */

/**
 * Create an extension for `mdast-util-from-markdown` to enable GFM (autolink
 * literals, footnotes, strikethrough, tables, tasklists).
 *
 * @returns {Array<FromMarkdownExtension>}
 *   Extension for `mdast-util-from-markdown` to enable GFM (autolink literals,
 *   footnotes, strikethrough, tables, tasklists).
 */
function gfmFromMarkdown() {
	return [gfmAutolinkLiteralFromMarkdown(), gfmFootnoteFromMarkdown(), gfmStrikethroughFromMarkdown(), gfmTableFromMarkdown(), gfmTaskListItemFromMarkdown()];
}

/**
 * Create an extension for `mdast-util-to-markdown` to enable GFM (autolink
 * literals, footnotes, strikethrough, tables, tasklists).
 *
 * @param {Options | null | undefined} [options]
 *   Configuration (optional).
 * @returns {ToMarkdownExtension}
 *   Extension for `mdast-util-to-markdown` to enable GFM (autolink literals,
 *   footnotes, strikethrough, tables, tasklists).
 */
function gfmToMarkdown(options) {
	return {
		extensions: [gfmAutolinkLiteralToMarkdown(), gfmFootnoteToMarkdown(options), gfmStrikethroughToMarkdown(), gfmTableToMarkdown(options), gfmTaskListItemToMarkdown()],
	};
}

/**
 * Like `Array#splice`, but smarter for giant arrays.
 *
 * `Array#splice` takes all items to be inserted as individual argument which
 * causes a stack overflow in V8 when trying to insert 100k items for instance.
 *
 * Otherwise, this does not return the removed items, and takes `items` as an
 * array instead of rest parameters.
 *
 * @template {unknown} T
 *   Item type.
 * @param {Array<T>} list
 *   List to operate on.
 * @param {number} start
 *   Index to remove/insert at (can be negative).
 * @param {number} remove
 *   Number of items to remove.
 * @param {Array<T>} items
 *   Items to inject into `list`.
 * @returns {undefined}
 *   Nothing.
 */
function splice(list, start, remove, items) {
	const end = list.length;
	let chunkStart = 0;
	/** @type {Array<unknown>} */
	let parameters;

	// Make start between zero and `end` (included).
	if (start < 0) {
		start = -start > end ? 0 : end + start;
	} else {
		start = start > end ? end : start;
	}
	remove = remove > 0 ? remove : 0;

	// No need to chunk the items if there’s only a couple (10k) items.
	if (items.length < 10000) {
		parameters = Array.from(items);
		parameters.unshift(start, remove);
		// @ts-expect-error Hush, it’s fine.
		list.splice(...parameters);
	} else {
		// Delete `remove` items starting from `start`
		if (remove) list.splice(start, remove);

		// Insert the items in chunks to not cause stack overflows.
		while (chunkStart < items.length) {
			parameters = items.slice(chunkStart, chunkStart + 10000);
			parameters.unshift(start, 0);
			// @ts-expect-error Hush, it’s fine.
			list.splice(...parameters);
			chunkStart += 10000;
			start += 10000;
		}
	}
}

/**
 * Append `items` (an array) at the end of `list` (another array).
 * When `list` was empty, returns `items` instead.
 *
 * This prevents a potentially expensive operation when `list` is empty,
 * and adds items in batches to prevent V8 from hanging.
 *
 * @template {unknown} T
 *   Item type.
 * @param {Array<T>} list
 *   List to operate on.
 * @param {Array<T>} items
 *   Items to add to `list`.
 * @returns {Array<T>}
 *   Either `list` or `items`.
 */
function push(list, items) {
	if (list.length > 0) {
		splice(list, list.length, 0, items);
		return list;
	}
	return items;
}

/**
 * @import {
 *   Extension,
 *   Handles,
 *   HtmlExtension,
 *   NormalizedExtension
 * } from 'micromark-util-types'
 */

const hasOwnProperty = {}.hasOwnProperty;

/**
 * Combine multiple syntax extensions into one.
 *
 * @param {ReadonlyArray<Extension>} extensions
 *   List of syntax extensions.
 * @returns {NormalizedExtension}
 *   A single combined extension.
 */
function combineExtensions(extensions) {
	/** @type {NormalizedExtension} */
	const all = {};
	let index = -1;

	while (++index < extensions.length) {
		syntaxExtension(all, extensions[index]);
	}

	return all;
}

/**
 * Merge `extension` into `all`.
 *
 * @param {NormalizedExtension} all
 *   Extension to merge into.
 * @param {Extension} extension
 *   Extension to merge.
 * @returns {undefined}
 *   Nothing.
 */
function syntaxExtension(all, extension) {
	/** @type {keyof Extension} */
	let hook;

	for (hook in extension) {
		const maybe = hasOwnProperty.call(all, hook) ? all[hook] : undefined;
		/** @type {Record<string, unknown>} */
		const left = maybe || (all[hook] = {});
		/** @type {Record<string, unknown> | undefined} */
		const right = extension[hook];
		/** @type {string} */
		let code;

		if (right) {
			for (code in right) {
				if (!hasOwnProperty.call(left, code)) left[code] = [];
				const value = right[code];
				constructs(
					// @ts-expect-error Looks like a list.
					left[code],
					Array.isArray(value) ? value
					: value ? [value]
					: [],
				);
			}
		}
	}
}

/**
 * Merge `list` into `existing` (both lists of constructs).
 * Mutates `existing`.
 *
 * @param {Array<unknown>} existing
 *   List of constructs to merge into.
 * @param {Array<unknown>} list
 *   List of constructs to merge.
 * @returns {undefined}
 *   Nothing.
 */
function constructs(existing, list) {
	let index = -1;
	/** @type {Array<unknown>} */
	const before = [];

	while (++index < list.length) {
		(list[index].add === "after" ? existing : before).push(list[index]);
	}

	splice(existing, 0, 0, before);
}

/**
 * @import {Code, ConstructRecord, Event, Extension, Previous, State, TokenizeContext, Tokenizer} from 'micromark-util-types'
 */

const wwwPrefix = {
	tokenize: tokenizeWwwPrefix,
	partial: true,
};
const domain = {
	tokenize: tokenizeDomain,
	partial: true,
};
const path = {
	tokenize: tokenizePath,
	partial: true,
};
const trail = {
	tokenize: tokenizeTrail,
	partial: true,
};
const emailDomainDotTrail = {
	tokenize: tokenizeEmailDomainDotTrail,
	partial: true,
};
const wwwAutolink = {
	name: "wwwAutolink",
	tokenize: tokenizeWwwAutolink,
	previous: previousWww,
};
const protocolAutolink = {
	name: "protocolAutolink",
	tokenize: tokenizeProtocolAutolink,
	previous: previousProtocol,
};
const emailAutolink = {
	name: "emailAutolink",
	tokenize: tokenizeEmailAutolink,
	previous: previousEmail,
};

/** @type {ConstructRecord} */
const text$5 = {};

/**
 * Create an extension for `micromark` to support GitHub autolink literal
 * syntax.
 *
 * @returns {Extension}
 *   Extension for `micromark` that can be passed in `extensions` to enable GFM
 *   autolink literal syntax.
 */
function gfmAutolinkLiteral() {
	return {
		text: text$5,
	};
}

/** @type {Code} */
let code$1 = 48;

// Add alphanumerics.
while (code$1 < 123) {
	text$5[code$1] = emailAutolink;
	code$1++;
	if (code$1 === 58) code$1 = 65;
	else if (code$1 === 91) code$1 = 97;
}
text$5[43] = emailAutolink;
text$5[45] = emailAutolink;
text$5[46] = emailAutolink;
text$5[95] = emailAutolink;
text$5[72] = [emailAutolink, protocolAutolink];
text$5[104] = [emailAutolink, protocolAutolink];
text$5[87] = [emailAutolink, wwwAutolink];
text$5[119] = [emailAutolink, wwwAutolink];

// To do: perform email autolink literals on events, afterwards.
// That’s where `markdown-rs` and `cmark-gfm` perform it.
// It should look for `@`, then for atext backwards, and then for a label
// forwards.
// To do: `mailto:`, `xmpp:` protocol as prefix.

/**
 * Email autolink literal.
 *
 * ```markdown
 * > | a contact@example.org b
 *       ^^^^^^^^^^^^^^^^^^^
 * ```
 *
 * @this {TokenizeContext}
 * @type {Tokenizer}
 */
function tokenizeEmailAutolink(effects, ok, nok) {
	const self = this;
	/** @type {boolean | undefined} */
	let dot;
	/** @type {boolean} */
	let data;
	return start;

	/**
	 * Start of email autolink literal.
	 *
	 * ```markdown
	 * > | a contact@example.org b
	 *       ^
	 * ```
	 *
	 * @type {State}
	 */
	function start(code) {
		if (!gfmAtext(code) || !previousEmail.call(self, self.previous) || previousUnbalanced(self.events)) {
			return nok(code);
		}
		effects.enter("literalAutolink");
		effects.enter("literalAutolinkEmail");
		return atext(code);
	}

	/**
	 * In email atext.
	 *
	 * ```markdown
	 * > | a contact@example.org b
	 *       ^
	 * ```
	 *
	 * @type {State}
	 */
	function atext(code) {
		if (gfmAtext(code)) {
			effects.consume(code);
			return atext;
		}
		if (code === 64) {
			effects.consume(code);
			return emailDomain;
		}
		return nok(code);
	}

	/**
	 * In email domain.
	 *
	 * The reference code is a bit overly complex as it handles the `@`, of which
	 * there may be just one.
	 * Source: <https://github.com/github/cmark-gfm/blob/ef1cfcb/extensions/autolink.c#L318>
	 *
	 * ```markdown
	 * > | a contact@example.org b
	 *               ^
	 * ```
	 *
	 * @type {State}
	 */
	function emailDomain(code) {
		// Dot followed by alphanumerical (not `-` or `_`).
		if (code === 46) {
			return effects.check(emailDomainDotTrail, emailDomainAfter, emailDomainDot)(code);
		}

		// Alphanumerical, `-`, and `_`.
		if (code === 45 || code === 95 || asciiAlphanumeric(code)) {
			data = true;
			effects.consume(code);
			return emailDomain;
		}

		// To do: `/` if xmpp.

		// Note: normally we’d truncate trailing punctuation from the link.
		// However, email autolink literals cannot contain any of those markers,
		// except for `.`, but that can only occur if it isn’t trailing.
		// So we can ignore truncating!
		return emailDomainAfter(code);
	}

	/**
	 * In email domain, on dot that is not a trail.
	 *
	 * ```markdown
	 * > | a contact@example.org b
	 *                      ^
	 * ```
	 *
	 * @type {State}
	 */
	function emailDomainDot(code) {
		effects.consume(code);
		dot = true;
		return emailDomain;
	}

	/**
	 * After email domain.
	 *
	 * ```markdown
	 * > | a contact@example.org b
	 *                          ^
	 * ```
	 *
	 * @type {State}
	 */
	function emailDomainAfter(code) {
		// Domain must not be empty, must include a dot, and must end in alphabetical.
		// Source: <https://github.com/github/cmark-gfm/blob/ef1cfcb/extensions/autolink.c#L332>.
		if (data && dot && asciiAlpha(self.previous)) {
			effects.exit("literalAutolinkEmail");
			effects.exit("literalAutolink");
			return ok(code);
		}
		return nok(code);
	}
}

/**
 * `www` autolink literal.
 *
 * ```markdown
 * > | a www.example.org b
 *       ^^^^^^^^^^^^^^^
 * ```
 *
 * @this {TokenizeContext}
 * @type {Tokenizer}
 */
function tokenizeWwwAutolink(effects, ok, nok) {
	const self = this;
	return wwwStart;

	/**
	 * Start of www autolink literal.
	 *
	 * ```markdown
	 * > | www.example.com/a?b#c
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function wwwStart(code) {
		if ((code !== 87 && code !== 119) || !previousWww.call(self, self.previous) || previousUnbalanced(self.events)) {
			return nok(code);
		}
		effects.enter("literalAutolink");
		effects.enter("literalAutolinkWww");
		// Note: we *check*, so we can discard the `www.` we parsed.
		// If it worked, we consider it as a part of the domain.
		return effects.check(wwwPrefix, effects.attempt(domain, effects.attempt(path, wwwAfter), nok), nok)(code);
	}

	/**
	 * After a www autolink literal.
	 *
	 * ```markdown
	 * > | www.example.com/a?b#c
	 *                          ^
	 * ```
	 *
	 * @type {State}
	 */
	function wwwAfter(code) {
		effects.exit("literalAutolinkWww");
		effects.exit("literalAutolink");
		return ok(code);
	}
}

/**
 * Protocol autolink literal.
 *
 * ```markdown
 * > | a https://example.org b
 *       ^^^^^^^^^^^^^^^^^^^
 * ```
 *
 * @this {TokenizeContext}
 * @type {Tokenizer}
 */
function tokenizeProtocolAutolink(effects, ok, nok) {
	const self = this;
	let buffer = "";
	let seen = false;
	return protocolStart;

	/**
	 * Start of protocol autolink literal.
	 *
	 * ```markdown
	 * > | https://example.com/a?b#c
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function protocolStart(code) {
		if ((code === 72 || code === 104) && previousProtocol.call(self, self.previous) && !previousUnbalanced(self.events)) {
			effects.enter("literalAutolink");
			effects.enter("literalAutolinkHttp");
			buffer += String.fromCodePoint(code);
			effects.consume(code);
			return protocolPrefixInside;
		}
		return nok(code);
	}

	/**
	 * In protocol.
	 *
	 * ```markdown
	 * > | https://example.com/a?b#c
	 *     ^^^^^
	 * ```
	 *
	 * @type {State}
	 */
	function protocolPrefixInside(code) {
		// `5` is size of `https`
		if (asciiAlpha(code) && buffer.length < 5) {
			// @ts-expect-error: definitely number.
			buffer += String.fromCodePoint(code);
			effects.consume(code);
			return protocolPrefixInside;
		}
		if (code === 58) {
			const protocol = buffer.toLowerCase();
			if (protocol === "http" || protocol === "https") {
				effects.consume(code);
				return protocolSlashesInside;
			}
		}
		return nok(code);
	}

	/**
	 * In slashes.
	 *
	 * ```markdown
	 * > | https://example.com/a?b#c
	 *           ^^
	 * ```
	 *
	 * @type {State}
	 */
	function protocolSlashesInside(code) {
		if (code === 47) {
			effects.consume(code);
			if (seen) {
				return afterProtocol;
			}
			seen = true;
			return protocolSlashesInside;
		}
		return nok(code);
	}

	/**
	 * After protocol, before domain.
	 *
	 * ```markdown
	 * > | https://example.com/a?b#c
	 *             ^
	 * ```
	 *
	 * @type {State}
	 */
	function afterProtocol(code) {
		// To do: this is different from `markdown-rs`:
		// https://github.com/wooorm/markdown-rs/blob/b3a921c761309ae00a51fe348d8a43adbc54b518/src/construct/gfm_autolink_literal.rs#L172-L182
		return code === null || asciiControl(code) || markdownLineEndingOrSpace(code) || unicodeWhitespace(code) || unicodePunctuation(code) ?
				nok(code)
			:	effects.attempt(domain, effects.attempt(path, protocolAfter), nok)(code);
	}

	/**
	 * After a protocol autolink literal.
	 *
	 * ```markdown
	 * > | https://example.com/a?b#c
	 *                              ^
	 * ```
	 *
	 * @type {State}
	 */
	function protocolAfter(code) {
		effects.exit("literalAutolinkHttp");
		effects.exit("literalAutolink");
		return ok(code);
	}
}

/**
 * `www` prefix.
 *
 * ```markdown
 * > | a www.example.org b
 *       ^^^^
 * ```
 *
 * @this {TokenizeContext}
 * @type {Tokenizer}
 */
function tokenizeWwwPrefix(effects, ok, nok) {
	let size = 0;
	return wwwPrefixInside;

	/**
	 * In www prefix.
	 *
	 * ```markdown
	 * > | www.example.com
	 *     ^^^^
	 * ```
	 *
	 * @type {State}
	 */
	function wwwPrefixInside(code) {
		if ((code === 87 || code === 119) && size < 3) {
			size++;
			effects.consume(code);
			return wwwPrefixInside;
		}
		if (code === 46 && size === 3) {
			effects.consume(code);
			return wwwPrefixAfter;
		}
		return nok(code);
	}

	/**
	 * After www prefix.
	 *
	 * ```markdown
	 * > | www.example.com
	 *         ^
	 * ```
	 *
	 * @type {State}
	 */
	function wwwPrefixAfter(code) {
		// If there is *anything*, we can link.
		return code === null ? nok(code) : ok(code);
	}
}

/**
 * Domain.
 *
 * ```markdown
 * > | a https://example.org b
 *               ^^^^^^^^^^^
 * ```
 *
 * @this {TokenizeContext}
 * @type {Tokenizer}
 */
function tokenizeDomain(effects, ok, nok) {
	/** @type {boolean | undefined} */
	let underscoreInLastSegment;
	/** @type {boolean | undefined} */
	let underscoreInLastLastSegment;
	/** @type {boolean | undefined} */
	let seen;
	return domainInside;

	/**
	 * In domain.
	 *
	 * ```markdown
	 * > | https://example.com/a
	 *             ^^^^^^^^^^^
	 * ```
	 *
	 * @type {State}
	 */
	function domainInside(code) {
		// Check whether this marker, which is a trailing punctuation
		// marker, optionally followed by more trailing markers, and then
		// followed by an end.
		if (code === 46 || code === 95) {
			return effects.check(trail, domainAfter, domainAtPunctuation)(code);
		}

		// GH documents that only alphanumerics (other than `-`, `.`, and `_`) can
		// occur, which sounds like ASCII only, but they also support `www.點看.com`,
		// so that’s Unicode.
		// Instead of some new production for Unicode alphanumerics, markdown
		// already has that for Unicode punctuation and whitespace, so use those.
		// Source: <https://github.com/github/cmark-gfm/blob/ef1cfcb/extensions/autolink.c#L12>.
		if (code === null || markdownLineEndingOrSpace(code) || unicodeWhitespace(code) || (code !== 45 && unicodePunctuation(code))) {
			return domainAfter(code);
		}
		seen = true;
		effects.consume(code);
		return domainInside;
	}

	/**
	 * In domain, at potential trailing punctuation, that was not trailing.
	 *
	 * ```markdown
	 * > | https://example.com
	 *                    ^
	 * ```
	 *
	 * @type {State}
	 */
	function domainAtPunctuation(code) {
		// There is an underscore in the last segment of the domain
		if (code === 95) {
			underscoreInLastSegment = true;
		}
		// Otherwise, it’s a `.`: save the last segment underscore in the
		// penultimate segment slot.
		else {
			underscoreInLastLastSegment = underscoreInLastSegment;
			underscoreInLastSegment = undefined;
		}
		effects.consume(code);
		return domainInside;
	}

	/**
	 * After domain.
	 *
	 * ```markdown
	 * > | https://example.com/a
	 *                        ^
	 * ```
	 *
	 * @type {State} */
	function domainAfter(code) {
		// Note: that’s GH says a dot is needed, but it’s not true:
		// <https://github.com/github/cmark-gfm/issues/279>
		if (underscoreInLastLastSegment || underscoreInLastSegment || !seen) {
			return nok(code);
		}
		return ok(code);
	}
}

/**
 * Path.
 *
 * ```markdown
 * > | a https://example.org/stuff b
 *                          ^^^^^^
 * ```
 *
 * @this {TokenizeContext}
 * @type {Tokenizer}
 */
function tokenizePath(effects, ok) {
	let sizeOpen = 0;
	let sizeClose = 0;
	return pathInside;

	/**
	 * In path.
	 *
	 * ```markdown
	 * > | https://example.com/a
	 *                        ^^
	 * ```
	 *
	 * @type {State}
	 */
	function pathInside(code) {
		if (code === 40) {
			sizeOpen++;
			effects.consume(code);
			return pathInside;
		}

		// To do: `markdown-rs` also needs this.
		// If this is a paren, and there are less closings than openings,
		// we don’t check for a trail.
		if (code === 41 && sizeClose < sizeOpen) {
			return pathAtPunctuation(code);
		}

		// Check whether this trailing punctuation marker is optionally
		// followed by more trailing markers, and then followed
		// by an end.
		if (
			code === 33 ||
			code === 34 ||
			code === 38 ||
			code === 39 ||
			code === 41 ||
			code === 42 ||
			code === 44 ||
			code === 46 ||
			code === 58 ||
			code === 59 ||
			code === 60 ||
			code === 63 ||
			code === 93 ||
			code === 95 ||
			code === 126
		) {
			return effects.check(trail, ok, pathAtPunctuation)(code);
		}
		if (code === null || markdownLineEndingOrSpace(code) || unicodeWhitespace(code)) {
			return ok(code);
		}
		effects.consume(code);
		return pathInside;
	}

	/**
	 * In path, at potential trailing punctuation, that was not trailing.
	 *
	 * ```markdown
	 * > | https://example.com/a"b
	 *                          ^
	 * ```
	 *
	 * @type {State}
	 */
	function pathAtPunctuation(code) {
		// Count closing parens.
		if (code === 41) {
			sizeClose++;
		}
		effects.consume(code);
		return pathInside;
	}
}

/**
 * Trail.
 *
 * This calls `ok` if this *is* the trail, followed by an end, which means
 * the entire trail is not part of the link.
 * It calls `nok` if this *is* part of the link.
 *
 * ```markdown
 * > | https://example.com").
 *                        ^^^
 * ```
 *
 * @this {TokenizeContext}
 * @type {Tokenizer}
 */
function tokenizeTrail(effects, ok, nok) {
	return trail;

	/**
	 * In trail of domain or path.
	 *
	 * ```markdown
	 * > | https://example.com").
	 *                        ^
	 * ```
	 *
	 * @type {State}
	 */
	function trail(code) {
		// Regular trailing punctuation.
		if (code === 33 || code === 34 || code === 39 || code === 41 || code === 42 || code === 44 || code === 46 || code === 58 || code === 59 || code === 63 || code === 95 || code === 126) {
			effects.consume(code);
			return trail;
		}

		// `&` followed by one or more alphabeticals and then a `;`, is
		// as a whole considered as trailing punctuation.
		// In all other cases, it is considered as continuation of the URL.
		if (code === 38) {
			effects.consume(code);
			return trailCharacterReferenceStart;
		}

		// Needed because we allow literals after `[`, as we fix:
		// <https://github.com/github/cmark-gfm/issues/278>.
		// Check that it is not followed by `(` or `[`.
		if (code === 93) {
			effects.consume(code);
			return trailBracketAfter;
		}
		if (
			// `<` is an end.
			code === 60 ||
			// So is whitespace.
			code === null ||
			markdownLineEndingOrSpace(code) ||
			unicodeWhitespace(code)
		) {
			return ok(code);
		}
		return nok(code);
	}

	/**
	 * In trail, after `]`.
	 *
	 * > 👉 **Note**: this deviates from `cmark-gfm` to fix a bug.
	 * > See end of <https://github.com/github/cmark-gfm/issues/278> for more.
	 *
	 * ```markdown
	 * > | https://example.com](
	 *                         ^
	 * ```
	 *
	 * @type {State}
	 */
	function trailBracketAfter(code) {
		// Whitespace or something that could start a resource or reference is the end.
		// Switch back to trail otherwise.
		if (code === null || code === 40 || code === 91 || markdownLineEndingOrSpace(code) || unicodeWhitespace(code)) {
			return ok(code);
		}
		return trail(code);
	}

	/**
	 * In character-reference like trail, after `&`.
	 *
	 * ```markdown
	 * > | https://example.com&amp;).
	 *                         ^
	 * ```
	 *
	 * @type {State}
	 */
	function trailCharacterReferenceStart(code) {
		// When non-alpha, it’s not a trail.
		return asciiAlpha(code) ? trailCharacterReferenceInside(code) : nok(code);
	}

	/**
	 * In character-reference like trail.
	 *
	 * ```markdown
	 * > | https://example.com&amp;).
	 *                         ^
	 * ```
	 *
	 * @type {State}
	 */
	function trailCharacterReferenceInside(code) {
		// Switch back to trail if this is well-formed.
		if (code === 59) {
			effects.consume(code);
			return trail;
		}
		if (asciiAlpha(code)) {
			effects.consume(code);
			return trailCharacterReferenceInside;
		}

		// It’s not a trail.
		return nok(code);
	}
}

/**
 * Dot in email domain trail.
 *
 * This calls `ok` if this *is* the trail, followed by an end, which means
 * the trail is not part of the link.
 * It calls `nok` if this *is* part of the link.
 *
 * ```markdown
 * > | contact@example.org.
 *                        ^
 * ```
 *
 * @this {TokenizeContext}
 * @type {Tokenizer}
 */
function tokenizeEmailDomainDotTrail(effects, ok, nok) {
	return start;

	/**
	 * Dot.
	 *
	 * ```markdown
	 * > | contact@example.org.
	 *                    ^   ^
	 * ```
	 *
	 * @type {State}
	 */
	function start(code) {
		// Must be dot.
		effects.consume(code);
		return after;
	}

	/**
	 * After dot.
	 *
	 * ```markdown
	 * > | contact@example.org.
	 *                     ^   ^
	 * ```
	 *
	 * @type {State}
	 */
	function after(code) {
		// Not a trail if alphanumeric.
		return asciiAlphanumeric(code) ? nok(code) : ok(code);
	}
}

/**
 * See:
 * <https://github.com/github/cmark-gfm/blob/ef1cfcb/extensions/autolink.c#L156>.
 *
 * @type {Previous}
 */
function previousWww(code) {
	return code === null || code === 40 || code === 42 || code === 95 || code === 91 || code === 93 || code === 126 || markdownLineEndingOrSpace(code);
}

/**
 * See:
 * <https://github.com/github/cmark-gfm/blob/ef1cfcb/extensions/autolink.c#L214>.
 *
 * @type {Previous}
 */
function previousProtocol(code) {
	return !asciiAlpha(code);
}

/**
 * @this {TokenizeContext}
 * @type {Previous}
 */
function previousEmail(code) {
	// Do not allow a slash “inside” atext.
	// The reference code is a bit weird, but that’s what it results in.
	// Source: <https://github.com/github/cmark-gfm/blob/ef1cfcb/extensions/autolink.c#L307>.
	// Other than slash, every preceding character is allowed.
	return !(code === 47 || gfmAtext(code));
}

/**
 * @param {Code} code
 * @returns {boolean}
 */
function gfmAtext(code) {
	return code === 43 || code === 45 || code === 46 || code === 95 || asciiAlphanumeric(code);
}

/**
 * @param {Array<Event>} events
 * @returns {boolean}
 */
function previousUnbalanced(events) {
	let index = events.length;
	let result = false;
	while (index--) {
		const token = events[index][1];
		if ((token.type === "labelLink" || token.type === "labelImage") && !token._balanced) {
			result = true;
			break;
		}

		// If we’ve seen this token, and it was marked as not having any unbalanced
		// bracket before it, we can exit.
		if (token._gfmAutolinkLiteralWalkedInto) {
			result = false;
			break;
		}
	}
	if (events.length > 0 && !result) {
		// Mark the last token as “walked into” w/o finding
		// anything.
		events[events.length - 1][1]._gfmAutolinkLiteralWalkedInto = true;
	}
	return result;
}

/**
 * Normalize a URL.
 *
 * Encode unsafe characters with percent-encoding, skipping already encoded
 * sequences.
 *
 * @param {string} value
 *   URI to normalize.
 * @returns {string}
 *   Normalized URI.
 */
function normalizeUri(value) {
	/** @type {Array<string>} */
	const result = [];
	let index = -1;
	let start = 0;
	let skip = 0;
	while (++index < value.length) {
		const code = value.charCodeAt(index);
		/** @type {string} */
		let replace = "";

		// A correct percent encoded value.
		if (code === 37 && asciiAlphanumeric(value.charCodeAt(index + 1)) && asciiAlphanumeric(value.charCodeAt(index + 2))) {
			skip = 2;
		}
		// ASCII.
		else if (code < 128) {
			if (!/[!#$&-;=?-Z_a-z~]/.test(String.fromCharCode(code))) {
				replace = String.fromCharCode(code);
			}
		}
		// Astral.
		else if (code > 55_295 && code < 57_344) {
			const next = value.charCodeAt(index + 1);

			// A correct surrogate pair.
			if (code < 56_320 && next > 56_319 && next < 57_344) {
				replace = String.fromCharCode(code, next);
				skip = 1;
			}
			// Lone surrogate.
			else {
				replace = "\uFFFD";
			}
		}
		// Unicode.
		else {
			replace = String.fromCharCode(code);
		}
		if (replace) {
			result.push(value.slice(start, index), encodeURIComponent(replace));
			start = index + skip + 1;
			replace = "";
		}
		if (skip) {
			index += skip;
			skip = 0;
		}
	}
	return result.join("") + value.slice(start);
}

/**
 * @import {Event, Resolver, TokenizeContext} from 'micromark-util-types'
 */

/**
 * Call all `resolveAll`s.
 *
 * @param {ReadonlyArray<{resolveAll?: Resolver | undefined}>} constructs
 *   List of constructs, optionally with `resolveAll`s.
 * @param {Array<Event>} events
 *   List of events.
 * @param {TokenizeContext} context
 *   Context used by `tokenize`.
 * @returns {Array<Event>}
 *   Changed events.
 */
function resolveAll(constructs, events, context) {
	/** @type {Array<Resolver>} */
	const called = [];
	let index = -1;

	while (++index < constructs.length) {
		const resolve = constructs[index].resolveAll;

		if (resolve && !called.includes(resolve)) {
			events = resolve(events, context);
			called.push(resolve);
		}
	}

	return events;
}

/**
 * @import {
 *   Code,
 *   Construct,
 *   Event,
 *   Point,
 *   Resolver,
 *   State,
 *   TokenizeContext,
 *   Tokenizer,
 *   Token
 * } from 'micromark-util-types'
 */

/** @type {Construct} */
const attention = {
	name: "attention",
	resolveAll: resolveAllAttention,
	tokenize: tokenizeAttention,
};

/**
 * Take all events and resolve attention to emphasis or strong.
 *
 * @type {Resolver}
 */
// eslint-disable-next-line complexity
function resolveAllAttention(events, context) {
	let index = -1;
	/** @type {number} */
	let open;
	/** @type {Token} */
	let group;
	/** @type {Token} */
	let text;
	/** @type {Token} */
	let openingSequence;
	/** @type {Token} */
	let closingSequence;
	/** @type {number} */
	let use;
	/** @type {Array<Event>} */
	let nextEvents;
	/** @type {number} */
	let offset;

	// Walk through all events.
	//
	// Note: performance of this is fine on an mb of normal markdown, but it’s
	// a bottleneck for malicious stuff.
	while (++index < events.length) {
		// Find a token that can close.
		if (events[index][0] === "enter" && events[index][1].type === "attentionSequence" && events[index][1]._close) {
			open = index;

			// Now walk back to find an opener.
			while (open--) {
				// Find a token that can open the closer.
				if (
					events[open][0] === "exit" &&
					events[open][1].type === "attentionSequence" &&
					events[open][1]._open &&
					// If the markers are the same:
					context.sliceSerialize(events[open][1]).charCodeAt(0) === context.sliceSerialize(events[index][1]).charCodeAt(0)
				) {
					// If the opening can close or the closing can open,
					// and the close size *is not* a multiple of three,
					// but the sum of the opening and closing size *is* multiple of three,
					// then don’t match.
					if (
						(events[open][1]._close || events[index][1]._open) &&
						(events[index][1].end.offset - events[index][1].start.offset) % 3 &&
						!((events[open][1].end.offset - events[open][1].start.offset + events[index][1].end.offset - events[index][1].start.offset) % 3)
					) {
						continue;
					}

					// Number of markers to use from the sequence.
					use = events[open][1].end.offset - events[open][1].start.offset > 1 && events[index][1].end.offset - events[index][1].start.offset > 1 ? 2 : 1;
					const start = {
						...events[open][1].end,
					};
					const end = {
						...events[index][1].start,
					};
					movePoint(start, -use);
					movePoint(end, use);
					openingSequence = {
						type: use > 1 ? "strongSequence" : "emphasisSequence",
						start,
						end: {
							...events[open][1].end,
						},
					};
					closingSequence = {
						type: use > 1 ? "strongSequence" : "emphasisSequence",
						start: {
							...events[index][1].start,
						},
						end,
					};
					text = {
						type: use > 1 ? "strongText" : "emphasisText",
						start: {
							...events[open][1].end,
						},
						end: {
							...events[index][1].start,
						},
					};
					group = {
						type: use > 1 ? "strong" : "emphasis",
						start: {
							...openingSequence.start,
						},
						end: {
							...closingSequence.end,
						},
					};
					events[open][1].end = {
						...openingSequence.start,
					};
					events[index][1].start = {
						...closingSequence.end,
					};
					nextEvents = [];

					// If there are more markers in the opening, add them before.
					if (events[open][1].end.offset - events[open][1].start.offset) {
						nextEvents = push(nextEvents, [
							["enter", events[open][1], context],
							["exit", events[open][1], context],
						]);
					}

					// Opening.
					nextEvents = push(nextEvents, [
						["enter", group, context],
						["enter", openingSequence, context],
						["exit", openingSequence, context],
						["enter", text, context],
					]);

					// Always populated by defaults.

					// Between.
					nextEvents = push(nextEvents, resolveAll(context.parser.constructs.insideSpan.null, events.slice(open + 1, index), context));

					// Closing.
					nextEvents = push(nextEvents, [
						["exit", text, context],
						["enter", closingSequence, context],
						["exit", closingSequence, context],
						["exit", group, context],
					]);

					// If there are more markers in the closing, add them after.
					if (events[index][1].end.offset - events[index][1].start.offset) {
						offset = 2;
						nextEvents = push(nextEvents, [
							["enter", events[index][1], context],
							["exit", events[index][1], context],
						]);
					} else {
						offset = 0;
					}
					splice(events, open - 1, index - open + 3, nextEvents);
					index = open + nextEvents.length - offset - 2;
					break;
				}
			}
		}
	}

	// Remove remaining sequences.
	index = -1;
	while (++index < events.length) {
		if (events[index][1].type === "attentionSequence") {
			events[index][1].type = "data";
		}
	}
	return events;
}

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeAttention(effects, ok) {
	const attentionMarkers = this.parser.constructs.attentionMarkers.null;
	const previous = this.previous;
	const before = classifyCharacter(previous);

	/** @type {NonNullable<Code>} */
	let marker;
	return start;

	/**
	 * Before a sequence.
	 *
	 * ```markdown
	 * > | **
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function start(code) {
		marker = code;
		effects.enter("attentionSequence");
		return inside(code);
	}

	/**
	 * In a sequence.
	 *
	 * ```markdown
	 * > | **
	 *     ^^
	 * ```
	 *
	 * @type {State}
	 */
	function inside(code) {
		if (code === marker) {
			effects.consume(code);
			return inside;
		}
		const token = effects.exit("attentionSequence");

		// To do: next major: move this to resolver, just like `markdown-rs`.
		const after = classifyCharacter(code);

		// Always populated by defaults.

		const open = !after || (after === 2 && before) || attentionMarkers.includes(code);
		const close = !before || (before === 2 && after) || attentionMarkers.includes(previous);
		token._open = Boolean(marker === 42 ? open : open && (before || !close));
		token._close = Boolean(marker === 42 ? close : close && (after || !open));
		return ok(code);
	}
}

/**
 * Move a point a bit.
 *
 * Note: `move` only works inside lines! It’s not possible to move past other
 * chunks (replacement characters, tabs, or line endings).
 *
 * @param {Point} point
 *   Point.
 * @param {number} offset
 *   Amount to move.
 * @returns {undefined}
 *   Nothing.
 */
function movePoint(point, offset) {
	point.column += offset;
	point.offset += offset;
	point._bufferIndex += offset;
}

/**
 * @import {
 *   Construct,
 *   State,
 *   TokenizeContext,
 *   Tokenizer
 * } from 'micromark-util-types'
 */

/** @type {Construct} */
const autolink = {
	name: "autolink",
	tokenize: tokenizeAutolink,
};

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeAutolink(effects, ok, nok) {
	let size = 0;
	return start;

	/**
	 * Start of an autolink.
	 *
	 * ```markdown
	 * > | a<https://example.com>b
	 *      ^
	 * > | a<user@example.com>b
	 *      ^
	 * ```
	 *
	 * @type {State}
	 */
	function start(code) {
		effects.enter("autolink");
		effects.enter("autolinkMarker");
		effects.consume(code);
		effects.exit("autolinkMarker");
		effects.enter("autolinkProtocol");
		return open;
	}

	/**
	 * After `<`, at protocol or atext.
	 *
	 * ```markdown
	 * > | a<https://example.com>b
	 *       ^
	 * > | a<user@example.com>b
	 *       ^
	 * ```
	 *
	 * @type {State}
	 */
	function open(code) {
		if (asciiAlpha(code)) {
			effects.consume(code);
			return schemeOrEmailAtext;
		}
		if (code === 64) {
			return nok(code);
		}
		return emailAtext(code);
	}

	/**
	 * At second byte of protocol or atext.
	 *
	 * ```markdown
	 * > | a<https://example.com>b
	 *        ^
	 * > | a<user@example.com>b
	 *        ^
	 * ```
	 *
	 * @type {State}
	 */
	function schemeOrEmailAtext(code) {
		// ASCII alphanumeric and `+`, `-`, and `.`.
		if (code === 43 || code === 45 || code === 46 || asciiAlphanumeric(code)) {
			// Count the previous alphabetical from `open` too.
			size = 1;
			return schemeInsideOrEmailAtext(code);
		}
		return emailAtext(code);
	}

	/**
	 * In ambiguous protocol or atext.
	 *
	 * ```markdown
	 * > | a<https://example.com>b
	 *        ^
	 * > | a<user@example.com>b
	 *        ^
	 * ```
	 *
	 * @type {State}
	 */
	function schemeInsideOrEmailAtext(code) {
		if (code === 58) {
			effects.consume(code);
			size = 0;
			return urlInside;
		}

		// ASCII alphanumeric and `+`, `-`, and `.`.
		if ((code === 43 || code === 45 || code === 46 || asciiAlphanumeric(code)) && size++ < 32) {
			effects.consume(code);
			return schemeInsideOrEmailAtext;
		}
		size = 0;
		return emailAtext(code);
	}

	/**
	 * After protocol, in URL.
	 *
	 * ```markdown
	 * > | a<https://example.com>b
	 *             ^
	 * ```
	 *
	 * @type {State}
	 */
	function urlInside(code) {
		if (code === 62) {
			effects.exit("autolinkProtocol");
			effects.enter("autolinkMarker");
			effects.consume(code);
			effects.exit("autolinkMarker");
			effects.exit("autolink");
			return ok;
		}

		// ASCII control, space, or `<`.
		if (code === null || code === 32 || code === 60 || asciiControl(code)) {
			return nok(code);
		}
		effects.consume(code);
		return urlInside;
	}

	/**
	 * In email atext.
	 *
	 * ```markdown
	 * > | a<user.name@example.com>b
	 *              ^
	 * ```
	 *
	 * @type {State}
	 */
	function emailAtext(code) {
		if (code === 64) {
			effects.consume(code);
			return emailAtSignOrDot;
		}
		if (asciiAtext(code)) {
			effects.consume(code);
			return emailAtext;
		}
		return nok(code);
	}

	/**
	 * In label, after at-sign or dot.
	 *
	 * ```markdown
	 * > | a<user.name@example.com>b
	 *                 ^       ^
	 * ```
	 *
	 * @type {State}
	 */
	function emailAtSignOrDot(code) {
		return asciiAlphanumeric(code) ? emailLabel(code) : nok(code);
	}

	/**
	 * In label, where `.` and `>` are allowed.
	 *
	 * ```markdown
	 * > | a<user.name@example.com>b
	 *                   ^
	 * ```
	 *
	 * @type {State}
	 */
	function emailLabel(code) {
		if (code === 46) {
			effects.consume(code);
			size = 0;
			return emailAtSignOrDot;
		}
		if (code === 62) {
			// Exit, then change the token type.
			effects.exit("autolinkProtocol").type = "autolinkEmail";
			effects.enter("autolinkMarker");
			effects.consume(code);
			effects.exit("autolinkMarker");
			effects.exit("autolink");
			return ok;
		}
		return emailValue(code);
	}

	/**
	 * In label, where `.` and `>` are *not* allowed.
	 *
	 * Though, this is also used in `emailLabel` to parse other values.
	 *
	 * ```markdown
	 * > | a<user.name@ex-ample.com>b
	 *                    ^
	 * ```
	 *
	 * @type {State}
	 */
	function emailValue(code) {
		// ASCII alphanumeric or `-`.
		if ((code === 45 || asciiAlphanumeric(code)) && size++ < 63) {
			const next = code === 45 ? emailValue : emailLabel;
			effects.consume(code);
			return next;
		}
		return nok(code);
	}
}

/**
 * @import {Effects, State, TokenType} from 'micromark-util-types'
 */

// To do: implement `spaceOrTab`, `spaceOrTabMinMax`, `spaceOrTabWithOptions`.

/**
 * Parse spaces and tabs.
 *
 * There is no `nok` parameter:
 *
 * *   spaces in markdown are often optional, in which case this factory can be
 *     used and `ok` will be switched to whether spaces were found or not
 * *   one line ending or space can be detected with `markdownSpace(code)` right
 *     before using `factorySpace`
 *
 * ###### Examples
 *
 * Where `␉` represents a tab (plus how much it expands) and `␠` represents a
 * single space.
 *
 * ```markdown
 * ␉
 * ␠␠␠␠
 * ␉␠
 * ```
 *
 * @param {Effects} effects
 *   Context.
 * @param {State} ok
 *   State switched to when successful.
 * @param {TokenType} type
 *   Type (`' \t'`).
 * @param {number | undefined} [max=Infinity]
 *   Max (exclusive).
 * @returns {State}
 *   Start state.
 */
function factorySpace(effects, ok, type, max) {
	const limit = max ? max - 1 : Number.POSITIVE_INFINITY;
	let size = 0;
	return start;

	/** @type {State} */
	function start(code) {
		if (markdownSpace(code)) {
			effects.enter(type);
			return prefix(code);
		}
		return ok(code);
	}

	/** @type {State} */
	function prefix(code) {
		if (markdownSpace(code) && size++ < limit) {
			effects.consume(code);
			return prefix;
		}
		effects.exit(type);
		return ok(code);
	}
}

/**
 * @import {
 *   Construct,
 *   State,
 *   TokenizeContext,
 *   Tokenizer
 * } from 'micromark-util-types'
 */

/** @type {Construct} */
const blankLine = {
	partial: true,
	tokenize: tokenizeBlankLine,
};

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeBlankLine(effects, ok, nok) {
	return start;

	/**
	 * Start of blank line.
	 *
	 * > 👉 **Note**: `␠` represents a space character.
	 *
	 * ```markdown
	 * > | ␠␠␊
	 *     ^
	 * > | ␊
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function start(code) {
		return markdownSpace(code) ? factorySpace(effects, after, "linePrefix")(code) : after(code);
	}

	/**
	 * At eof/eol, after optional whitespace.
	 *
	 * > 👉 **Note**: `␠` represents a space character.
	 *
	 * ```markdown
	 * > | ␠␠␊
	 *       ^
	 * > | ␊
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function after(code) {
		return code === null || markdownLineEnding(code) ? ok(code) : nok(code);
	}
}

/**
 * @import {
 *   Construct,
 *   Exiter,
 *   State,
 *   TokenizeContext,
 *   Tokenizer
 * } from 'micromark-util-types'
 */

/** @type {Construct} */
const blockQuote = {
	continuation: {
		tokenize: tokenizeBlockQuoteContinuation,
	},
	exit,
	name: "blockQuote",
	tokenize: tokenizeBlockQuoteStart,
};

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeBlockQuoteStart(effects, ok, nok) {
	const self = this;
	return start;

	/**
	 * Start of block quote.
	 *
	 * ```markdown
	 * > | > a
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function start(code) {
		if (code === 62) {
			const state = self.containerState;
			if (!state.open) {
				effects.enter("blockQuote", {
					_container: true,
				});
				state.open = true;
			}
			effects.enter("blockQuotePrefix");
			effects.enter("blockQuoteMarker");
			effects.consume(code);
			effects.exit("blockQuoteMarker");
			return after;
		}
		return nok(code);
	}

	/**
	 * After `>`, before optional whitespace.
	 *
	 * ```markdown
	 * > | > a
	 *      ^
	 * ```
	 *
	 * @type {State}
	 */
	function after(code) {
		if (markdownSpace(code)) {
			effects.enter("blockQuotePrefixWhitespace");
			effects.consume(code);
			effects.exit("blockQuotePrefixWhitespace");
			effects.exit("blockQuotePrefix");
			return ok;
		}
		effects.exit("blockQuotePrefix");
		return ok(code);
	}
}

/**
 * Start of block quote continuation.
 *
 * ```markdown
 *   | > a
 * > | > b
 *     ^
 * ```
 *
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeBlockQuoteContinuation(effects, ok, nok) {
	const self = this;
	return contStart;

	/**
	 * Start of block quote continuation.
	 *
	 * Also used to parse the first block quote opening.
	 *
	 * ```markdown
	 *   | > a
	 * > | > b
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function contStart(code) {
		if (markdownSpace(code)) {
			// Always populated by defaults.

			return factorySpace(effects, contBefore, "linePrefix", self.parser.constructs.disable.null.includes("codeIndented") ? undefined : 4)(code);
		}
		return contBefore(code);
	}

	/**
	 * At `>`, after optional whitespace.
	 *
	 * Also used to parse the first block quote opening.
	 *
	 * ```markdown
	 *   | > a
	 * > | > b
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function contBefore(code) {
		return effects.attempt(blockQuote, ok, nok)(code);
	}
}

/** @type {Exiter} */
function exit(effects) {
	effects.exit("blockQuote");
}

/**
 * @import {
 *   Construct,
 *   State,
 *   TokenizeContext,
 *   Tokenizer
 * } from 'micromark-util-types'
 */

/** @type {Construct} */
const characterEscape = {
	name: "characterEscape",
	tokenize: tokenizeCharacterEscape,
};

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeCharacterEscape(effects, ok, nok) {
	return start;

	/**
	 * Start of character escape.
	 *
	 * ```markdown
	 * > | a\*b
	 *      ^
	 * ```
	 *
	 * @type {State}
	 */
	function start(code) {
		effects.enter("characterEscape");
		effects.enter("escapeMarker");
		effects.consume(code);
		effects.exit("escapeMarker");
		return inside;
	}

	/**
	 * After `\`, at punctuation.
	 *
	 * ```markdown
	 * > | a\*b
	 *       ^
	 * ```
	 *
	 * @type {State}
	 */
	function inside(code) {
		// ASCII punctuation.
		if (asciiPunctuation(code)) {
			effects.enter("characterEscapeValue");
			effects.consume(code);
			effects.exit("characterEscapeValue");
			effects.exit("characterEscape");
			return ok;
		}
		return nok(code);
	}
}

/**
 * @import {
 *   Code,
 *   Construct,
 *   State,
 *   TokenizeContext,
 *   Tokenizer
 * } from 'micromark-util-types'
 */

/** @type {Construct} */
const characterReference = {
	name: "characterReference",
	tokenize: tokenizeCharacterReference,
};

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeCharacterReference(effects, ok, nok) {
	const self = this;
	let size = 0;
	/** @type {number} */
	let max;
	/** @type {(code: Code) => boolean} */
	let test;
	return start;

	/**
	 * Start of character reference.
	 *
	 * ```markdown
	 * > | a&amp;b
	 *      ^
	 * > | a&#123;b
	 *      ^
	 * > | a&#x9;b
	 *      ^
	 * ```
	 *
	 * @type {State}
	 */
	function start(code) {
		effects.enter("characterReference");
		effects.enter("characterReferenceMarker");
		effects.consume(code);
		effects.exit("characterReferenceMarker");
		return open;
	}

	/**
	 * After `&`, at `#` for numeric references or alphanumeric for named
	 * references.
	 *
	 * ```markdown
	 * > | a&amp;b
	 *       ^
	 * > | a&#123;b
	 *       ^
	 * > | a&#x9;b
	 *       ^
	 * ```
	 *
	 * @type {State}
	 */
	function open(code) {
		if (code === 35) {
			effects.enter("characterReferenceMarkerNumeric");
			effects.consume(code);
			effects.exit("characterReferenceMarkerNumeric");
			return numeric;
		}
		effects.enter("characterReferenceValue");
		max = 31;
		test = asciiAlphanumeric;
		return value(code);
	}

	/**
	 * After `#`, at `x` for hexadecimals or digit for decimals.
	 *
	 * ```markdown
	 * > | a&#123;b
	 *        ^
	 * > | a&#x9;b
	 *        ^
	 * ```
	 *
	 * @type {State}
	 */
	function numeric(code) {
		if (code === 88 || code === 120) {
			effects.enter("characterReferenceMarkerHexadecimal");
			effects.consume(code);
			effects.exit("characterReferenceMarkerHexadecimal");
			effects.enter("characterReferenceValue");
			max = 6;
			test = asciiHexDigit;
			return value;
		}
		effects.enter("characterReferenceValue");
		max = 7;
		test = asciiDigit;
		return value(code);
	}

	/**
	 * After markers (`&#x`, `&#`, or `&`), in value, before `;`.
	 *
	 * The character reference kind defines what and how many characters are
	 * allowed.
	 *
	 * ```markdown
	 * > | a&amp;b
	 *       ^^^
	 * > | a&#123;b
	 *        ^^^
	 * > | a&#x9;b
	 *         ^
	 * ```
	 *
	 * @type {State}
	 */
	function value(code) {
		if (code === 59 && size) {
			const token = effects.exit("characterReferenceValue");
			if (test === asciiAlphanumeric && !decodeNamedCharacterReference(self.sliceSerialize(token))) {
				return nok(code);
			}

			// To do: `markdown-rs` uses a different name:
			// `CharacterReferenceMarkerSemi`.
			effects.enter("characterReferenceMarker");
			effects.consume(code);
			effects.exit("characterReferenceMarker");
			effects.exit("characterReference");
			return ok;
		}
		if (test(code) && size++ < max) {
			effects.consume(code);
			return value;
		}
		return nok(code);
	}
}

/**
 * @import {
 *   Code,
 *   Construct,
 *   State,
 *   TokenizeContext,
 *   Tokenizer
 * } from 'micromark-util-types'
 */

/** @type {Construct} */
const nonLazyContinuation = {
	partial: true,
	tokenize: tokenizeNonLazyContinuation,
};

/** @type {Construct} */
const codeFenced = {
	concrete: true,
	name: "codeFenced",
	tokenize: tokenizeCodeFenced,
};

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeCodeFenced(effects, ok, nok) {
	const self = this;
	/** @type {Construct} */
	const closeStart = {
		partial: true,
		tokenize: tokenizeCloseStart,
	};
	let initialPrefix = 0;
	let sizeOpen = 0;
	/** @type {NonNullable<Code>} */
	let marker;
	return start;

	/**
	 * Start of code.
	 *
	 * ```markdown
	 * > | ~~~js
	 *     ^
	 *   | alert(1)
	 *   | ~~~
	 * ```
	 *
	 * @type {State}
	 */
	function start(code) {
		// To do: parse whitespace like `markdown-rs`.
		return beforeSequenceOpen(code);
	}

	/**
	 * In opening fence, after prefix, at sequence.
	 *
	 * ```markdown
	 * > | ~~~js
	 *     ^
	 *   | alert(1)
	 *   | ~~~
	 * ```
	 *
	 * @type {State}
	 */
	function beforeSequenceOpen(code) {
		const tail = self.events[self.events.length - 1];
		initialPrefix = tail && tail[1].type === "linePrefix" ? tail[2].sliceSerialize(tail[1], true).length : 0;
		marker = code;
		effects.enter("codeFenced");
		effects.enter("codeFencedFence");
		effects.enter("codeFencedFenceSequence");
		return sequenceOpen(code);
	}

	/**
	 * In opening fence sequence.
	 *
	 * ```markdown
	 * > | ~~~js
	 *      ^
	 *   | alert(1)
	 *   | ~~~
	 * ```
	 *
	 * @type {State}
	 */
	function sequenceOpen(code) {
		if (code === marker) {
			sizeOpen++;
			effects.consume(code);
			return sequenceOpen;
		}
		if (sizeOpen < 3) {
			return nok(code);
		}
		effects.exit("codeFencedFenceSequence");
		return markdownSpace(code) ? factorySpace(effects, infoBefore, "whitespace")(code) : infoBefore(code);
	}

	/**
	 * In opening fence, after the sequence (and optional whitespace), before info.
	 *
	 * ```markdown
	 * > | ~~~js
	 *        ^
	 *   | alert(1)
	 *   | ~~~
	 * ```
	 *
	 * @type {State}
	 */
	function infoBefore(code) {
		if (code === null || markdownLineEnding(code)) {
			effects.exit("codeFencedFence");
			return self.interrupt ? ok(code) : effects.check(nonLazyContinuation, atNonLazyBreak, after)(code);
		}
		effects.enter("codeFencedFenceInfo");
		effects.enter("chunkString", {
			contentType: "string",
		});
		return info(code);
	}

	/**
	 * In info.
	 *
	 * ```markdown
	 * > | ~~~js
	 *        ^
	 *   | alert(1)
	 *   | ~~~
	 * ```
	 *
	 * @type {State}
	 */
	function info(code) {
		if (code === null || markdownLineEnding(code)) {
			effects.exit("chunkString");
			effects.exit("codeFencedFenceInfo");
			return infoBefore(code);
		}
		if (markdownSpace(code)) {
			effects.exit("chunkString");
			effects.exit("codeFencedFenceInfo");
			return factorySpace(effects, metaBefore, "whitespace")(code);
		}
		if (code === 96 && code === marker) {
			return nok(code);
		}
		effects.consume(code);
		return info;
	}

	/**
	 * In opening fence, after info and whitespace, before meta.
	 *
	 * ```markdown
	 * > | ~~~js eval
	 *           ^
	 *   | alert(1)
	 *   | ~~~
	 * ```
	 *
	 * @type {State}
	 */
	function metaBefore(code) {
		if (code === null || markdownLineEnding(code)) {
			return infoBefore(code);
		}
		effects.enter("codeFencedFenceMeta");
		effects.enter("chunkString", {
			contentType: "string",
		});
		return meta(code);
	}

	/**
	 * In meta.
	 *
	 * ```markdown
	 * > | ~~~js eval
	 *           ^
	 *   | alert(1)
	 *   | ~~~
	 * ```
	 *
	 * @type {State}
	 */
	function meta(code) {
		if (code === null || markdownLineEnding(code)) {
			effects.exit("chunkString");
			effects.exit("codeFencedFenceMeta");
			return infoBefore(code);
		}
		if (code === 96 && code === marker) {
			return nok(code);
		}
		effects.consume(code);
		return meta;
	}

	/**
	 * At eol/eof in code, before a non-lazy closing fence or content.
	 *
	 * ```markdown
	 * > | ~~~js
	 *          ^
	 * > | alert(1)
	 *             ^
	 *   | ~~~
	 * ```
	 *
	 * @type {State}
	 */
	function atNonLazyBreak(code) {
		return effects.attempt(closeStart, after, contentBefore)(code);
	}

	/**
	 * Before code content, not a closing fence, at eol.
	 *
	 * ```markdown
	 *   | ~~~js
	 * > | alert(1)
	 *             ^
	 *   | ~~~
	 * ```
	 *
	 * @type {State}
	 */
	function contentBefore(code) {
		effects.enter("lineEnding");
		effects.consume(code);
		effects.exit("lineEnding");
		return contentStart;
	}

	/**
	 * Before code content, not a closing fence.
	 *
	 * ```markdown
	 *   | ~~~js
	 * > | alert(1)
	 *     ^
	 *   | ~~~
	 * ```
	 *
	 * @type {State}
	 */
	function contentStart(code) {
		return initialPrefix > 0 && markdownSpace(code) ? factorySpace(effects, beforeContentChunk, "linePrefix", initialPrefix + 1)(code) : beforeContentChunk(code);
	}

	/**
	 * Before code content, after optional prefix.
	 *
	 * ```markdown
	 *   | ~~~js
	 * > | alert(1)
	 *     ^
	 *   | ~~~
	 * ```
	 *
	 * @type {State}
	 */
	function beforeContentChunk(code) {
		if (code === null || markdownLineEnding(code)) {
			return effects.check(nonLazyContinuation, atNonLazyBreak, after)(code);
		}
		effects.enter("codeFlowValue");
		return contentChunk(code);
	}

	/**
	 * In code content.
	 *
	 * ```markdown
	 *   | ~~~js
	 * > | alert(1)
	 *     ^^^^^^^^
	 *   | ~~~
	 * ```
	 *
	 * @type {State}
	 */
	function contentChunk(code) {
		if (code === null || markdownLineEnding(code)) {
			effects.exit("codeFlowValue");
			return beforeContentChunk(code);
		}
		effects.consume(code);
		return contentChunk;
	}

	/**
	 * After code.
	 *
	 * ```markdown
	 *   | ~~~js
	 *   | alert(1)
	 * > | ~~~
	 *        ^
	 * ```
	 *
	 * @type {State}
	 */
	function after(code) {
		effects.exit("codeFenced");
		return ok(code);
	}

	/**
	 * @this {TokenizeContext}
	 *   Context.
	 * @type {Tokenizer}
	 */
	function tokenizeCloseStart(effects, ok, nok) {
		let size = 0;
		return startBefore;

		/**
		 *
		 *
		 * @type {State}
		 */
		function startBefore(code) {
			effects.enter("lineEnding");
			effects.consume(code);
			effects.exit("lineEnding");
			return start;
		}

		/**
		 * Before closing fence, at optional whitespace.
		 *
		 * ```markdown
		 *   | ~~~js
		 *   | alert(1)
		 * > | ~~~
		 *     ^
		 * ```
		 *
		 * @type {State}
		 */
		function start(code) {
			// Always populated by defaults.

			// To do: `enter` here or in next state?
			effects.enter("codeFencedFence");
			return markdownSpace(code) ?
					factorySpace(effects, beforeSequenceClose, "linePrefix", self.parser.constructs.disable.null.includes("codeIndented") ? undefined : 4)(code)
				:	beforeSequenceClose(code);
		}

		/**
		 * In closing fence, after optional whitespace, at sequence.
		 *
		 * ```markdown
		 *   | ~~~js
		 *   | alert(1)
		 * > | ~~~
		 *     ^
		 * ```
		 *
		 * @type {State}
		 */
		function beforeSequenceClose(code) {
			if (code === marker) {
				effects.enter("codeFencedFenceSequence");
				return sequenceClose(code);
			}
			return nok(code);
		}

		/**
		 * In closing fence sequence.
		 *
		 * ```markdown
		 *   | ~~~js
		 *   | alert(1)
		 * > | ~~~
		 *     ^
		 * ```
		 *
		 * @type {State}
		 */
		function sequenceClose(code) {
			if (code === marker) {
				size++;
				effects.consume(code);
				return sequenceClose;
			}
			if (size >= sizeOpen) {
				effects.exit("codeFencedFenceSequence");
				return markdownSpace(code) ? factorySpace(effects, sequenceCloseAfter, "whitespace")(code) : sequenceCloseAfter(code);
			}
			return nok(code);
		}

		/**
		 * After closing fence sequence, after optional whitespace.
		 *
		 * ```markdown
		 *   | ~~~js
		 *   | alert(1)
		 * > | ~~~
		 *        ^
		 * ```
		 *
		 * @type {State}
		 */
		function sequenceCloseAfter(code) {
			if (code === null || markdownLineEnding(code)) {
				effects.exit("codeFencedFence");
				return ok(code);
			}
			return nok(code);
		}
	}
}

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeNonLazyContinuation(effects, ok, nok) {
	const self = this;
	return start;

	/**
	 *
	 *
	 * @type {State}
	 */
	function start(code) {
		if (code === null) {
			return nok(code);
		}
		effects.enter("lineEnding");
		effects.consume(code);
		effects.exit("lineEnding");
		return lineStart;
	}

	/**
	 *
	 *
	 * @type {State}
	 */
	function lineStart(code) {
		return self.parser.lazy[self.now().line] ? nok(code) : ok(code);
	}
}

/**
 * @import {
 *   Construct,
 *   State,
 *   TokenizeContext,
 *   Tokenizer
 * } from 'micromark-util-types'
 */

/** @type {Construct} */
const codeIndented = {
	name: "codeIndented",
	tokenize: tokenizeCodeIndented,
};

/** @type {Construct} */
const furtherStart = {
	partial: true,
	tokenize: tokenizeFurtherStart,
};

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeCodeIndented(effects, ok, nok) {
	const self = this;
	return start;

	/**
	 * Start of code (indented).
	 *
	 * > **Parsing note**: it is not needed to check if this first line is a
	 * > filled line (that it has a non-whitespace character), because blank lines
	 * > are parsed already, so we never run into that.
	 *
	 * ```markdown
	 * > |     aaa
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function start(code) {
		// To do: manually check if interrupting like `markdown-rs`.

		effects.enter("codeIndented");
		// To do: use an improved `space_or_tab` function like `markdown-rs`,
		// so that we can drop the next state.
		return factorySpace(effects, afterPrefix, "linePrefix", 4 + 1)(code);
	}

	/**
	 * At start, after 1 or 4 spaces.
	 *
	 * ```markdown
	 * > |     aaa
	 *         ^
	 * ```
	 *
	 * @type {State}
	 */
	function afterPrefix(code) {
		const tail = self.events[self.events.length - 1];
		return tail && tail[1].type === "linePrefix" && tail[2].sliceSerialize(tail[1], true).length >= 4 ? atBreak(code) : nok(code);
	}

	/**
	 * At a break.
	 *
	 * ```markdown
	 * > |     aaa
	 *         ^  ^
	 * ```
	 *
	 * @type {State}
	 */
	function atBreak(code) {
		if (code === null) {
			return after(code);
		}
		if (markdownLineEnding(code)) {
			return effects.attempt(furtherStart, atBreak, after)(code);
		}
		effects.enter("codeFlowValue");
		return inside(code);
	}

	/**
	 * In code content.
	 *
	 * ```markdown
	 * > |     aaa
	 *         ^^^^
	 * ```
	 *
	 * @type {State}
	 */
	function inside(code) {
		if (code === null || markdownLineEnding(code)) {
			effects.exit("codeFlowValue");
			return atBreak(code);
		}
		effects.consume(code);
		return inside;
	}

	/** @type {State} */
	function after(code) {
		effects.exit("codeIndented");
		// To do: allow interrupting like `markdown-rs`.
		// Feel free to interrupt.
		// tokenizer.interrupt = false
		return ok(code);
	}
}

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeFurtherStart(effects, ok, nok) {
	const self = this;
	return furtherStart;

	/**
	 * At eol, trying to parse another indent.
	 *
	 * ```markdown
	 * > |     aaa
	 *            ^
	 *   |     bbb
	 * ```
	 *
	 * @type {State}
	 */
	function furtherStart(code) {
		// To do: improve `lazy` / `pierce` handling.
		// If this is a lazy line, it can’t be code.
		if (self.parser.lazy[self.now().line]) {
			return nok(code);
		}
		if (markdownLineEnding(code)) {
			effects.enter("lineEnding");
			effects.consume(code);
			effects.exit("lineEnding");
			return furtherStart;
		}

		// To do: the code here in `micromark-js` is a bit different from
		// `markdown-rs` because there it can attempt spaces.
		// We can’t yet.
		//
		// To do: use an improved `space_or_tab` function like `markdown-rs`,
		// so that we can drop the next state.
		return factorySpace(effects, afterPrefix, "linePrefix", 4 + 1)(code);
	}

	/**
	 * At start, after 1 or 4 spaces.
	 *
	 * ```markdown
	 * > |     aaa
	 *         ^
	 * ```
	 *
	 * @type {State}
	 */
	function afterPrefix(code) {
		const tail = self.events[self.events.length - 1];
		return (
			tail && tail[1].type === "linePrefix" && tail[2].sliceSerialize(tail[1], true).length >= 4 ? ok(code)
			: markdownLineEnding(code) ? furtherStart(code)
			: nok(code)
		);
	}
}

/**
 * @import {
 *   Construct,
 *   Previous,
 *   Resolver,
 *   State,
 *   TokenizeContext,
 *   Tokenizer,
 *   Token
 * } from 'micromark-util-types'
 */

/** @type {Construct} */
const codeText = {
	name: "codeText",
	previous,
	resolve: resolveCodeText,
	tokenize: tokenizeCodeText,
};

// To do: next major: don’t resolve, like `markdown-rs`.
/** @type {Resolver} */
function resolveCodeText(events) {
	let tailExitIndex = events.length - 4;
	let headEnterIndex = 3;
	/** @type {number} */
	let index;
	/** @type {number | undefined} */
	let enter;

	// If we start and end with an EOL or a space.
	if (
		(events[headEnterIndex][1].type === "lineEnding" || events[headEnterIndex][1].type === "space") &&
		(events[tailExitIndex][1].type === "lineEnding" || events[tailExitIndex][1].type === "space")
	) {
		index = headEnterIndex;

		// And we have data.
		while (++index < tailExitIndex) {
			if (events[index][1].type === "codeTextData") {
				// Then we have padding.
				events[headEnterIndex][1].type = "codeTextPadding";
				events[tailExitIndex][1].type = "codeTextPadding";
				headEnterIndex += 2;
				tailExitIndex -= 2;
				break;
			}
		}
	}

	// Merge adjacent spaces and data.
	index = headEnterIndex - 1;
	tailExitIndex++;
	while (++index <= tailExitIndex) {
		if (enter === undefined) {
			if (index !== tailExitIndex && events[index][1].type !== "lineEnding") {
				enter = index;
			}
		} else if (index === tailExitIndex || events[index][1].type === "lineEnding") {
			events[enter][1].type = "codeTextData";
			if (index !== enter + 2) {
				events[enter][1].end = events[index - 1][1].end;
				events.splice(enter + 2, index - enter - 2);
				tailExitIndex -= index - enter - 2;
				index = enter + 2;
			}
			enter = undefined;
		}
	}
	return events;
}

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Previous}
 */
function previous(code) {
	// If there is a previous code, there will always be a tail.
	return code !== 96 || this.events[this.events.length - 1][1].type === "characterEscape";
}

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeCodeText(effects, ok, nok) {
	let sizeOpen = 0;
	/** @type {number} */
	let size;
	/** @type {Token} */
	let token;
	return start;

	/**
	 * Start of code (text).
	 *
	 * ```markdown
	 * > | `a`
	 *     ^
	 * > | \`a`
	 *      ^
	 * ```
	 *
	 * @type {State}
	 */
	function start(code) {
		effects.enter("codeText");
		effects.enter("codeTextSequence");
		return sequenceOpen(code);
	}

	/**
	 * In opening sequence.
	 *
	 * ```markdown
	 * > | `a`
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function sequenceOpen(code) {
		if (code === 96) {
			effects.consume(code);
			sizeOpen++;
			return sequenceOpen;
		}
		effects.exit("codeTextSequence");
		return between(code);
	}

	/**
	 * Between something and something else.
	 *
	 * ```markdown
	 * > | `a`
	 *      ^^
	 * ```
	 *
	 * @type {State}
	 */
	function between(code) {
		// EOF.
		if (code === null) {
			return nok(code);
		}

		// To do: next major: don’t do spaces in resolve, but when compiling,
		// like `markdown-rs`.
		// Tabs don’t work, and virtual spaces don’t make sense.
		if (code === 32) {
			effects.enter("space");
			effects.consume(code);
			effects.exit("space");
			return between;
		}

		// Closing fence? Could also be data.
		if (code === 96) {
			token = effects.enter("codeTextSequence");
			size = 0;
			return sequenceClose(code);
		}
		if (markdownLineEnding(code)) {
			effects.enter("lineEnding");
			effects.consume(code);
			effects.exit("lineEnding");
			return between;
		}

		// Data.
		effects.enter("codeTextData");
		return data(code);
	}

	/**
	 * In data.
	 *
	 * ```markdown
	 * > | `a`
	 *      ^
	 * ```
	 *
	 * @type {State}
	 */
	function data(code) {
		if (code === null || code === 32 || code === 96 || markdownLineEnding(code)) {
			effects.exit("codeTextData");
			return between(code);
		}
		effects.consume(code);
		return data;
	}

	/**
	 * In closing sequence.
	 *
	 * ```markdown
	 * > | `a`
	 *       ^
	 * ```
	 *
	 * @type {State}
	 */
	function sequenceClose(code) {
		// More.
		if (code === 96) {
			effects.consume(code);
			size++;
			return sequenceClose;
		}

		// Done!
		if (size === sizeOpen) {
			effects.exit("codeTextSequence");
			effects.exit("codeText");
			return ok(code);
		}

		// More or less accents: mark as data.
		token.type = "codeTextData";
		return data(code);
	}
}

/**
 * Some of the internal operations of micromark do lots of editing
 * operations on very large arrays. This runs into problems with two
 * properties of most circa-2020 JavaScript interpreters:
 *
 *  - Array-length modifications at the high end of an array (push/pop) are
 *    expected to be common and are implemented in (amortized) time
 *    proportional to the number of elements added or removed, whereas
 *    other operations (shift/unshift and splice) are much less efficient.
 *  - Function arguments are passed on the stack, so adding tens of thousands
 *    of elements to an array with `arr.push(...newElements)` will frequently
 *    cause stack overflows. (see <https://stackoverflow.com/questions/22123769/rangeerror-maximum-call-stack-size-exceeded-why>)
 *
 * SpliceBuffers are an implementation of gap buffers, which are a
 * generalization of the "queue made of two stacks" idea. The splice buffer
 * maintains a cursor, and moving the cursor has cost proportional to the
 * distance the cursor moves, but inserting, deleting, or splicing in
 * new information at the cursor is as efficient as the push/pop operation.
 * This allows for an efficient sequence of splices (or pushes, pops, shifts,
 * or unshifts) as long such edits happen at the same part of the array or
 * generally sweep through the array from the beginning to the end.
 *
 * The interface for splice buffers also supports large numbers of inputs by
 * passing a single array argument rather passing multiple arguments on the
 * function call stack.
 *
 * @template T
 *   Item type.
 */
class SpliceBuffer {
	/**
	 * @param {ReadonlyArray<T> | null | undefined} [initial]
	 *   Initial items (optional).
	 * @returns
	 *   Splice buffer.
	 */
	constructor(initial) {
		/** @type {Array<T>} */
		this.left = initial ? [...initial] : [];
		/** @type {Array<T>} */
		this.right = [];
	}

	/**
	 * Array access;
	 * does not move the cursor.
	 *
	 * @param {number} index
	 *   Index.
	 * @return {T}
	 *   Item.
	 */
	get(index) {
		if (index < 0 || index >= this.left.length + this.right.length) {
			throw new RangeError("Cannot access index `" + index + "` in a splice buffer of size `" + (this.left.length + this.right.length) + "`");
		}
		if (index < this.left.length) return this.left[index];
		return this.right[this.right.length - index + this.left.length - 1];
	}

	/**
	 * The length of the splice buffer, one greater than the largest index in the
	 * array.
	 */
	get length() {
		return this.left.length + this.right.length;
	}

	/**
	 * Remove and return `list[0]`;
	 * moves the cursor to `0`.
	 *
	 * @returns {T | undefined}
	 *   Item, optional.
	 */
	shift() {
		this.setCursor(0);
		return this.right.pop();
	}

	/**
	 * Slice the buffer to get an array;
	 * does not move the cursor.
	 *
	 * @param {number} start
	 *   Start.
	 * @param {number | null | undefined} [end]
	 *   End (optional).
	 * @returns {Array<T>}
	 *   Array of items.
	 */
	slice(start, end) {
		/** @type {number} */
		const stop = end === null || end === undefined ? Number.POSITIVE_INFINITY : end;
		if (stop < this.left.length) {
			return this.left.slice(start, stop);
		}
		if (start > this.left.length) {
			return this.right.slice(this.right.length - stop + this.left.length, this.right.length - start + this.left.length).reverse();
		}
		return this.left.slice(start).concat(this.right.slice(this.right.length - stop + this.left.length).reverse());
	}

	/**
	 * Mimics the behavior of Array.prototype.splice() except for the change of
	 * interface necessary to avoid segfaults when patching in very large arrays.
	 *
	 * This operation moves cursor is moved to `start` and results in the cursor
	 * placed after any inserted items.
	 *
	 * @param {number} start
	 *   Start;
	 *   zero-based index at which to start changing the array;
	 *   negative numbers count backwards from the end of the array and values
	 *   that are out-of bounds are clamped to the appropriate end of the array.
	 * @param {number | null | undefined} [deleteCount=0]
	 *   Delete count (default: `0`);
	 *   maximum number of elements to delete, starting from start.
	 * @param {Array<T> | null | undefined} [items=[]]
	 *   Items to include in place of the deleted items (default: `[]`).
	 * @return {Array<T>}
	 *   Any removed items.
	 */
	splice(start, deleteCount, items) {
		/** @type {number} */
		const count = deleteCount || 0;
		this.setCursor(Math.trunc(start));
		const removed = this.right.splice(this.right.length - count, Number.POSITIVE_INFINITY);
		if (items) chunkedPush(this.left, items);
		return removed.reverse();
	}

	/**
	 * Remove and return the highest-numbered item in the array, so
	 * `list[list.length - 1]`;
	 * Moves the cursor to `length`.
	 *
	 * @returns {T | undefined}
	 *   Item, optional.
	 */
	pop() {
		this.setCursor(Number.POSITIVE_INFINITY);
		return this.left.pop();
	}

	/**
	 * Inserts a single item to the high-numbered side of the array;
	 * moves the cursor to `length`.
	 *
	 * @param {T} item
	 *   Item.
	 * @returns {undefined}
	 *   Nothing.
	 */
	push(item) {
		this.setCursor(Number.POSITIVE_INFINITY);
		this.left.push(item);
	}

	/**
	 * Inserts many items to the high-numbered side of the array.
	 * Moves the cursor to `length`.
	 *
	 * @param {Array<T>} items
	 *   Items.
	 * @returns {undefined}
	 *   Nothing.
	 */
	pushMany(items) {
		this.setCursor(Number.POSITIVE_INFINITY);
		chunkedPush(this.left, items);
	}

	/**
	 * Inserts a single item to the low-numbered side of the array;
	 * Moves the cursor to `0`.
	 *
	 * @param {T} item
	 *   Item.
	 * @returns {undefined}
	 *   Nothing.
	 */
	unshift(item) {
		this.setCursor(0);
		this.right.push(item);
	}

	/**
	 * Inserts many items to the low-numbered side of the array;
	 * moves the cursor to `0`.
	 *
	 * @param {Array<T>} items
	 *   Items.
	 * @returns {undefined}
	 *   Nothing.
	 */
	unshiftMany(items) {
		this.setCursor(0);
		chunkedPush(this.right, items.reverse());
	}

	/**
	 * Move the cursor to a specific position in the array. Requires
	 * time proportional to the distance moved.
	 *
	 * If `n < 0`, the cursor will end up at the beginning.
	 * If `n > length`, the cursor will end up at the end.
	 *
	 * @param {number} n
	 *   Position.
	 * @return {undefined}
	 *   Nothing.
	 */
	setCursor(n) {
		if (n === this.left.length || (n > this.left.length && this.right.length === 0) || (n < 0 && this.left.length === 0)) return;
		if (n < this.left.length) {
			// Move cursor to the this.left
			const removed = this.left.splice(n, Number.POSITIVE_INFINITY);
			chunkedPush(this.right, removed.reverse());
		} else {
			// Move cursor to the this.right
			const removed = this.right.splice(this.left.length + this.right.length - n, Number.POSITIVE_INFINITY);
			chunkedPush(this.left, removed.reverse());
		}
	}
}

/**
 * Avoid stack overflow by pushing items onto the stack in segments
 *
 * @template T
 *   Item type.
 * @param {Array<T>} list
 *   List to inject into.
 * @param {ReadonlyArray<T>} right
 *   Items to inject.
 * @return {undefined}
 *   Nothing.
 */
function chunkedPush(list, right) {
	/** @type {number} */
	let chunkStart = 0;
	if (right.length < 10000) {
		list.push(...right);
	} else {
		while (chunkStart < right.length) {
			list.push(...right.slice(chunkStart, chunkStart + 10000));
			chunkStart += 10000;
		}
	}
}

/**
 * @import {Chunk, Event, Token} from 'micromark-util-types'
 */

/**
 * Tokenize subcontent.
 *
 * @param {Array<Event>} eventsArray
 *   List of events.
 * @returns {boolean}
 *   Whether subtokens were found.
 */
// eslint-disable-next-line complexity
function subtokenize(eventsArray) {
	/** @type {Record<string, number>} */
	const jumps = {};
	let index = -1;
	/** @type {Event} */
	let event;
	/** @type {number | undefined} */
	let lineIndex;
	/** @type {number} */
	let otherIndex;
	/** @type {Event} */
	let otherEvent;
	/** @type {Array<Event>} */
	let parameters;
	/** @type {Array<Event>} */
	let subevents;
	/** @type {boolean | undefined} */
	let more;
	const events = new SpliceBuffer(eventsArray);
	while (++index < events.length) {
		while (index in jumps) {
			index = jumps[index];
		}
		event = events.get(index);

		// Add a hook for the GFM tasklist extension, which needs to know if text
		// is in the first content of a list item.
		if (index && event[1].type === "chunkFlow" && events.get(index - 1)[1].type === "listItemPrefix") {
			subevents = event[1]._tokenizer.events;
			otherIndex = 0;
			if (otherIndex < subevents.length && subevents[otherIndex][1].type === "lineEndingBlank") {
				otherIndex += 2;
			}
			if (otherIndex < subevents.length && subevents[otherIndex][1].type === "content") {
				while (++otherIndex < subevents.length) {
					if (subevents[otherIndex][1].type === "content") {
						break;
					}
					if (subevents[otherIndex][1].type === "chunkText") {
						subevents[otherIndex][1]._isInFirstContentOfListItem = true;
						otherIndex++;
					}
				}
			}
		}

		// Enter.
		if (event[0] === "enter") {
			if (event[1].contentType) {
				Object.assign(jumps, subcontent(events, index));
				index = jumps[index];
				more = true;
			}
		}
		// Exit.
		else if (event[1]._container) {
			otherIndex = index;
			lineIndex = undefined;
			while (otherIndex--) {
				otherEvent = events.get(otherIndex);
				if (otherEvent[1].type === "lineEnding" || otherEvent[1].type === "lineEndingBlank") {
					if (otherEvent[0] === "enter") {
						if (lineIndex) {
							events.get(lineIndex)[1].type = "lineEndingBlank";
						}
						otherEvent[1].type = "lineEnding";
						lineIndex = otherIndex;
					}
				} else if (otherEvent[1].type === "linePrefix" || otherEvent[1].type === "listItemIndent");
				else {
					break;
				}
			}
			if (lineIndex) {
				// Fix position.
				event[1].end = {
					...events.get(lineIndex)[1].start,
				};

				// Switch container exit w/ line endings.
				parameters = events.slice(lineIndex, index);
				parameters.unshift(event);
				events.splice(lineIndex, index - lineIndex + 1, parameters);
			}
		}
	}

	// The changes to the `events` buffer must be copied back into the eventsArray
	splice(eventsArray, 0, Number.POSITIVE_INFINITY, events.slice(0));
	return !more;
}

/**
 * Tokenize embedded tokens.
 *
 * @param {SpliceBuffer<Event>} events
 *   Events.
 * @param {number} eventIndex
 *   Index.
 * @returns {Record<string, number>}
 *   Gaps.
 */
function subcontent(events, eventIndex) {
	const token = events.get(eventIndex)[1];
	const context = events.get(eventIndex)[2];
	let startPosition = eventIndex - 1;
	/** @type {Array<number>} */
	const startPositions = [];
	let tokenizer = token._tokenizer;
	if (!tokenizer) {
		tokenizer = context.parser[token.contentType](token.start);
		if (token._contentTypeTextTrailing) {
			tokenizer._contentTypeTextTrailing = true;
		}
	}
	const childEvents = tokenizer.events;
	/** @type {Array<[number, number]>} */
	const jumps = [];
	/** @type {Record<string, number>} */
	const gaps = {};
	/** @type {Array<Chunk>} */
	let stream;
	/** @type {Token | undefined} */
	let previous;
	let index = -1;
	/** @type {Token | undefined} */
	let current = token;
	let adjust = 0;
	let start = 0;
	const breaks = [start];

	// Loop forward through the linked tokens to pass them in order to the
	// subtokenizer.
	while (current) {
		// Find the position of the event for this token.
		while (events.get(++startPosition)[1] !== current) {
			// Empty.
		}
		startPositions.push(startPosition);
		if (!current._tokenizer) {
			stream = context.sliceStream(current);
			if (!current.next) {
				stream.push(null);
			}
			if (previous) {
				tokenizer.defineSkip(current.start);
			}
			if (current._isInFirstContentOfListItem) {
				tokenizer._gfmTasklistFirstContentOfListItem = true;
			}
			tokenizer.write(stream);
			if (current._isInFirstContentOfListItem) {
				tokenizer._gfmTasklistFirstContentOfListItem = undefined;
			}
		}

		// Unravel the next token.
		previous = current;
		current = current.next;
	}

	// Now, loop back through all events (and linked tokens), to figure out which
	// parts belong where.
	current = token;
	while (++index < childEvents.length) {
		if (
			// Find a void token that includes a break.
			childEvents[index][0] === "exit" &&
			childEvents[index - 1][0] === "enter" &&
			childEvents[index][1].type === childEvents[index - 1][1].type &&
			childEvents[index][1].start.line !== childEvents[index][1].end.line
		) {
			start = index + 1;
			breaks.push(start);
			// Help GC.
			current._tokenizer = undefined;
			current.previous = undefined;
			current = current.next;
		}
	}

	// Help GC.
	tokenizer.events = [];

	// If there’s one more token (which is the cases for lines that end in an
	// EOF), that’s perfect: the last point we found starts it.
	// If there isn’t then make sure any remaining content is added to it.
	if (current) {
		// Help GC.
		current._tokenizer = undefined;
		current.previous = undefined;
	} else {
		breaks.pop();
	}

	// Now splice the events from the subtokenizer into the current events,
	// moving back to front so that splice indices aren’t affected.
	index = breaks.length;
	while (index--) {
		const slice = childEvents.slice(breaks[index], breaks[index + 1]);
		const start = startPositions.pop();
		jumps.push([start, start + slice.length - 1]);
		events.splice(start, 2, slice);
	}
	jumps.reverse();
	index = -1;
	while (++index < jumps.length) {
		gaps[adjust + jumps[index][0]] = adjust + jumps[index][1];
		adjust += jumps[index][1] - jumps[index][0] - 1;
	}
	return gaps;
}

/**
 * @import {
 *   Construct,
 *   Resolver,
 *   State,
 *   TokenizeContext,
 *   Tokenizer,
 *   Token
 * } from 'micromark-util-types'
 */

/**
 * No name because it must not be turned off.
 * @type {Construct}
 */
const content$1 = {
	resolve: resolveContent,
	tokenize: tokenizeContent,
};

/** @type {Construct} */
const continuationConstruct = {
	partial: true,
	tokenize: tokenizeContinuation,
};

/**
 * Content is transparent: it’s parsed right now. That way, definitions are also
 * parsed right now: before text in paragraphs (specifically, media) are parsed.
 *
 * @type {Resolver}
 */
function resolveContent(events) {
	subtokenize(events);
	return events;
}

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeContent(effects, ok) {
	/** @type {Token | undefined} */
	let previous;
	return chunkStart;

	/**
	 * Before a content chunk.
	 *
	 * ```markdown
	 * > | abc
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function chunkStart(code) {
		effects.enter("content");
		previous = effects.enter("chunkContent", {
			contentType: "content",
		});
		return chunkInside(code);
	}

	/**
	 * In a content chunk.
	 *
	 * ```markdown
	 * > | abc
	 *     ^^^
	 * ```
	 *
	 * @type {State}
	 */
	function chunkInside(code) {
		if (code === null) {
			return contentEnd(code);
		}

		// To do: in `markdown-rs`, each line is parsed on its own, and everything
		// is stitched together resolving.
		if (markdownLineEnding(code)) {
			return effects.check(continuationConstruct, contentContinue, contentEnd)(code);
		}

		// Data.
		effects.consume(code);
		return chunkInside;
	}

	/**
	 *
	 *
	 * @type {State}
	 */
	function contentEnd(code) {
		effects.exit("chunkContent");
		effects.exit("content");
		return ok(code);
	}

	/**
	 *
	 *
	 * @type {State}
	 */
	function contentContinue(code) {
		effects.consume(code);
		effects.exit("chunkContent");
		previous.next = effects.enter("chunkContent", {
			contentType: "content",
			previous,
		});
		previous = previous.next;
		return chunkInside;
	}
}

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeContinuation(effects, ok, nok) {
	const self = this;
	return startLookahead;

	/**
	 *
	 *
	 * @type {State}
	 */
	function startLookahead(code) {
		effects.exit("chunkContent");
		effects.enter("lineEnding");
		effects.consume(code);
		effects.exit("lineEnding");
		return factorySpace(effects, prefixed, "linePrefix");
	}

	/**
	 *
	 *
	 * @type {State}
	 */
	function prefixed(code) {
		if (code === null || markdownLineEnding(code)) {
			return nok(code);
		}

		// Always populated by defaults.

		const tail = self.events[self.events.length - 1];
		if (!self.parser.constructs.disable.null.includes("codeIndented") && tail && tail[1].type === "linePrefix" && tail[2].sliceSerialize(tail[1], true).length >= 4) {
			return ok(code);
		}
		return effects.interrupt(self.parser.constructs.flow, nok, ok)(code);
	}
}

/**
 * @import {Effects, State, TokenType} from 'micromark-util-types'
 */

/**
 * Parse destinations.
 *
 * ###### Examples
 *
 * ```markdown
 * <a>
 * <a\>b>
 * <a b>
 * <a)>
 * a
 * a\)b
 * a(b)c
 * a(b)
 * ```
 *
 * @param {Effects} effects
 *   Context.
 * @param {State} ok
 *   State switched to when successful.
 * @param {State} nok
 *   State switched to when unsuccessful.
 * @param {TokenType} type
 *   Type for whole (`<a>` or `b`).
 * @param {TokenType} literalType
 *   Type when enclosed (`<a>`).
 * @param {TokenType} literalMarkerType
 *   Type for enclosing (`<` and `>`).
 * @param {TokenType} rawType
 *   Type when not enclosed (`b`).
 * @param {TokenType} stringType
 *   Type for the value (`a` or `b`).
 * @param {number | undefined} [max=Infinity]
 *   Depth of nested parens (inclusive).
 * @returns {State}
 *   Start state.
 */
function factoryDestination(effects, ok, nok, type, literalType, literalMarkerType, rawType, stringType, max) {
	const limit = max || Number.POSITIVE_INFINITY;
	let balance = 0;
	return start;

	/**
	 * Start of destination.
	 *
	 * ```markdown
	 * > | <aa>
	 *     ^
	 * > | aa
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function start(code) {
		if (code === 60) {
			effects.enter(type);
			effects.enter(literalType);
			effects.enter(literalMarkerType);
			effects.consume(code);
			effects.exit(literalMarkerType);
			return enclosedBefore;
		}

		// ASCII control, space, closing paren.
		if (code === null || code === 32 || code === 41 || asciiControl(code)) {
			return nok(code);
		}
		effects.enter(type);
		effects.enter(rawType);
		effects.enter(stringType);
		effects.enter("chunkString", {
			contentType: "string",
		});
		return raw(code);
	}

	/**
	 * After `<`, at an enclosed destination.
	 *
	 * ```markdown
	 * > | <aa>
	 *      ^
	 * ```
	 *
	 * @type {State}
	 */
	function enclosedBefore(code) {
		if (code === 62) {
			effects.enter(literalMarkerType);
			effects.consume(code);
			effects.exit(literalMarkerType);
			effects.exit(literalType);
			effects.exit(type);
			return ok;
		}
		effects.enter(stringType);
		effects.enter("chunkString", {
			contentType: "string",
		});
		return enclosed(code);
	}

	/**
	 * In enclosed destination.
	 *
	 * ```markdown
	 * > | <aa>
	 *      ^
	 * ```
	 *
	 * @type {State}
	 */
	function enclosed(code) {
		if (code === 62) {
			effects.exit("chunkString");
			effects.exit(stringType);
			return enclosedBefore(code);
		}
		if (code === null || code === 60 || markdownLineEnding(code)) {
			return nok(code);
		}
		effects.consume(code);
		return code === 92 ? enclosedEscape : enclosed;
	}

	/**
	 * After `\`, at a special character.
	 *
	 * ```markdown
	 * > | <a\*a>
	 *        ^
	 * ```
	 *
	 * @type {State}
	 */
	function enclosedEscape(code) {
		if (code === 60 || code === 62 || code === 92) {
			effects.consume(code);
			return enclosed;
		}
		return enclosed(code);
	}

	/**
	 * In raw destination.
	 *
	 * ```markdown
	 * > | aa
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function raw(code) {
		if (!balance && (code === null || code === 41 || markdownLineEndingOrSpace(code))) {
			effects.exit("chunkString");
			effects.exit(stringType);
			effects.exit(rawType);
			effects.exit(type);
			return ok(code);
		}
		if (balance < limit && code === 40) {
			effects.consume(code);
			balance++;
			return raw;
		}
		if (code === 41) {
			effects.consume(code);
			balance--;
			return raw;
		}

		// ASCII control (but *not* `\0`) and space and `(`.
		// Note: in `markdown-rs`, `\0` exists in codes, in `micromark-js` it
		// doesn’t.
		if (code === null || code === 32 || code === 40 || asciiControl(code)) {
			return nok(code);
		}
		effects.consume(code);
		return code === 92 ? rawEscape : raw;
	}

	/**
	 * After `\`, at special character.
	 *
	 * ```markdown
	 * > | a\*a
	 *       ^
	 * ```
	 *
	 * @type {State}
	 */
	function rawEscape(code) {
		if (code === 40 || code === 41 || code === 92) {
			effects.consume(code);
			return raw;
		}
		return raw(code);
	}
}

/**
 * @import {
 *   Effects,
 *   State,
 *   TokenizeContext,
 *   TokenType
 * } from 'micromark-util-types'
 */

/**
 * Parse labels.
 *
 * > 👉 **Note**: labels in markdown are capped at 999 characters in the string.
 *
 * ###### Examples
 *
 * ```markdown
 * [a]
 * [a
 * b]
 * [a\]b]
 * ```
 *
 * @this {TokenizeContext}
 *   Tokenize context.
 * @param {Effects} effects
 *   Context.
 * @param {State} ok
 *   State switched to when successful.
 * @param {State} nok
 *   State switched to when unsuccessful.
 * @param {TokenType} type
 *   Type of the whole label (`[a]`).
 * @param {TokenType} markerType
 *   Type for the markers (`[` and `]`).
 * @param {TokenType} stringType
 *   Type for the identifier (`a`).
 * @returns {State}
 *   Start state.
 */
function factoryLabel(effects, ok, nok, type, markerType, stringType) {
	const self = this;
	let size = 0;
	/** @type {boolean} */
	let seen;
	return start;

	/**
	 * Start of label.
	 *
	 * ```markdown
	 * > | [a]
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function start(code) {
		effects.enter(type);
		effects.enter(markerType);
		effects.consume(code);
		effects.exit(markerType);
		effects.enter(stringType);
		return atBreak;
	}

	/**
	 * In label, at something, before something else.
	 *
	 * ```markdown
	 * > | [a]
	 *      ^
	 * ```
	 *
	 * @type {State}
	 */
	function atBreak(code) {
		if (
			size > 999 ||
			code === null ||
			code === 91 ||
			(code === 93 && !seen) ||
			// To do: remove in the future once we’ve switched from
			// `micromark-extension-footnote` to `micromark-extension-gfm-footnote`,
			// which doesn’t need this.
			// Hidden footnotes hook.
			/* c8 ignore next 3 */
			(code === 94 && !size && "_hiddenFootnoteSupport" in self.parser.constructs)
		) {
			return nok(code);
		}
		if (code === 93) {
			effects.exit(stringType);
			effects.enter(markerType);
			effects.consume(code);
			effects.exit(markerType);
			effects.exit(type);
			return ok;
		}

		// To do: indent? Link chunks and EOLs together?
		if (markdownLineEnding(code)) {
			effects.enter("lineEnding");
			effects.consume(code);
			effects.exit("lineEnding");
			return atBreak;
		}
		effects.enter("chunkString", {
			contentType: "string",
		});
		return labelInside(code);
	}

	/**
	 * In label, in text.
	 *
	 * ```markdown
	 * > | [a]
	 *      ^
	 * ```
	 *
	 * @type {State}
	 */
	function labelInside(code) {
		if (code === null || code === 91 || code === 93 || markdownLineEnding(code) || size++ > 999) {
			effects.exit("chunkString");
			return atBreak(code);
		}
		effects.consume(code);
		if (!seen) seen = !markdownSpace(code);
		return code === 92 ? labelEscape : labelInside;
	}

	/**
	 * After `\`, at a special character.
	 *
	 * ```markdown
	 * > | [a\*a]
	 *        ^
	 * ```
	 *
	 * @type {State}
	 */
	function labelEscape(code) {
		if (code === 91 || code === 92 || code === 93) {
			effects.consume(code);
			size++;
			return labelInside;
		}
		return labelInside(code);
	}
}

/**
 * @import {
 *   Code,
 *   Effects,
 *   State,
 *   TokenType
 * } from 'micromark-util-types'
 */

/**
 * Parse titles.
 *
 * ###### Examples
 *
 * ```markdown
 * "a"
 * 'b'
 * (c)
 * "a
 * b"
 * 'a
 *     b'
 * (a\)b)
 * ```
 *
 * @param {Effects} effects
 *   Context.
 * @param {State} ok
 *   State switched to when successful.
 * @param {State} nok
 *   State switched to when unsuccessful.
 * @param {TokenType} type
 *   Type of the whole title (`"a"`, `'b'`, `(c)`).
 * @param {TokenType} markerType
 *   Type for the markers (`"`, `'`, `(`, and `)`).
 * @param {TokenType} stringType
 *   Type for the value (`a`).
 * @returns {State}
 *   Start state.
 */
function factoryTitle(effects, ok, nok, type, markerType, stringType) {
	/** @type {NonNullable<Code>} */
	let marker;
	return start;

	/**
	 * Start of title.
	 *
	 * ```markdown
	 * > | "a"
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function start(code) {
		if (code === 34 || code === 39 || code === 40) {
			effects.enter(type);
			effects.enter(markerType);
			effects.consume(code);
			effects.exit(markerType);
			marker = code === 40 ? 41 : code;
			return begin;
		}
		return nok(code);
	}

	/**
	 * After opening marker.
	 *
	 * This is also used at the closing marker.
	 *
	 * ```markdown
	 * > | "a"
	 *      ^
	 * ```
	 *
	 * @type {State}
	 */
	function begin(code) {
		if (code === marker) {
			effects.enter(markerType);
			effects.consume(code);
			effects.exit(markerType);
			effects.exit(type);
			return ok;
		}
		effects.enter(stringType);
		return atBreak(code);
	}

	/**
	 * At something, before something else.
	 *
	 * ```markdown
	 * > | "a"
	 *      ^
	 * ```
	 *
	 * @type {State}
	 */
	function atBreak(code) {
		if (code === marker) {
			effects.exit(stringType);
			return begin(marker);
		}
		if (code === null) {
			return nok(code);
		}

		// Note: blank lines can’t exist in content.
		if (markdownLineEnding(code)) {
			// To do: use `space_or_tab_eol_with_options`, connect.
			effects.enter("lineEnding");
			effects.consume(code);
			effects.exit("lineEnding");
			return factorySpace(effects, atBreak, "linePrefix");
		}
		effects.enter("chunkString", {
			contentType: "string",
		});
		return inside(code);
	}

	/**
	 *
	 *
	 * @type {State}
	 */
	function inside(code) {
		if (code === marker || code === null || markdownLineEnding(code)) {
			effects.exit("chunkString");
			return atBreak(code);
		}
		effects.consume(code);
		return code === 92 ? escape : inside;
	}

	/**
	 * After `\`, at a special character.
	 *
	 * ```markdown
	 * > | "a\*b"
	 *      ^
	 * ```
	 *
	 * @type {State}
	 */
	function escape(code) {
		if (code === marker || code === 92) {
			effects.consume(code);
			return inside;
		}
		return inside(code);
	}
}

/**
 * @import {Effects, State} from 'micromark-util-types'
 */

/**
 * Parse spaces and tabs.
 *
 * There is no `nok` parameter:
 *
 * *   line endings or spaces in markdown are often optional, in which case this
 *     factory can be used and `ok` will be switched to whether spaces were found
 *     or not
 * *   one line ending or space can be detected with
 *     `markdownLineEndingOrSpace(code)` right before using `factoryWhitespace`
 *
 * @param {Effects} effects
 *   Context.
 * @param {State} ok
 *   State switched to when successful.
 * @returns {State}
 *   Start state.
 */
function factoryWhitespace(effects, ok) {
	/** @type {boolean} */
	let seen;
	return start;

	/** @type {State} */
	function start(code) {
		if (markdownLineEnding(code)) {
			effects.enter("lineEnding");
			effects.consume(code);
			effects.exit("lineEnding");
			seen = true;
			return start;
		}
		if (markdownSpace(code)) {
			return factorySpace(effects, start, seen ? "linePrefix" : "lineSuffix")(code);
		}
		return ok(code);
	}
}

/**
 * @import {
 *   Construct,
 *   State,
 *   TokenizeContext,
 *   Tokenizer
 * } from 'micromark-util-types'
 */

/** @type {Construct} */
const definition = {
	name: "definition",
	tokenize: tokenizeDefinition,
};

/** @type {Construct} */
const titleBefore = {
	partial: true,
	tokenize: tokenizeTitleBefore,
};

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeDefinition(effects, ok, nok) {
	const self = this;
	/** @type {string} */
	let identifier;
	return start;

	/**
	 * At start of a definition.
	 *
	 * ```markdown
	 * > | [a]: b "c"
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function start(code) {
		// Do not interrupt paragraphs (but do follow definitions).
		// To do: do `interrupt` the way `markdown-rs` does.
		// To do: parse whitespace the way `markdown-rs` does.
		effects.enter("definition");
		return before(code);
	}

	/**
	 * After optional whitespace, at `[`.
	 *
	 * ```markdown
	 * > | [a]: b "c"
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function before(code) {
		// To do: parse whitespace the way `markdown-rs` does.

		return factoryLabel.call(
			self,
			effects,
			labelAfter,
			// Note: we don’t need to reset the way `markdown-rs` does.
			nok,
			"definitionLabel",
			"definitionLabelMarker",
			"definitionLabelString",
		)(code);
	}

	/**
	 * After label.
	 *
	 * ```markdown
	 * > | [a]: b "c"
	 *        ^
	 * ```
	 *
	 * @type {State}
	 */
	function labelAfter(code) {
		identifier = normalizeIdentifier(self.sliceSerialize(self.events[self.events.length - 1][1]).slice(1, -1));
		if (code === 58) {
			effects.enter("definitionMarker");
			effects.consume(code);
			effects.exit("definitionMarker");
			return markerAfter;
		}
		return nok(code);
	}

	/**
	 * After marker.
	 *
	 * ```markdown
	 * > | [a]: b "c"
	 *         ^
	 * ```
	 *
	 * @type {State}
	 */
	function markerAfter(code) {
		// Note: whitespace is optional.
		return markdownLineEndingOrSpace(code) ? factoryWhitespace(effects, destinationBefore)(code) : destinationBefore(code);
	}

	/**
	 * Before destination.
	 *
	 * ```markdown
	 * > | [a]: b "c"
	 *          ^
	 * ```
	 *
	 * @type {State}
	 */
	function destinationBefore(code) {
		return factoryDestination(
			effects,
			destinationAfter,
			// Note: we don’t need to reset the way `markdown-rs` does.
			nok,
			"definitionDestination",
			"definitionDestinationLiteral",
			"definitionDestinationLiteralMarker",
			"definitionDestinationRaw",
			"definitionDestinationString",
		)(code);
	}

	/**
	 * After destination.
	 *
	 * ```markdown
	 * > | [a]: b "c"
	 *           ^
	 * ```
	 *
	 * @type {State}
	 */
	function destinationAfter(code) {
		return effects.attempt(titleBefore, after, after)(code);
	}

	/**
	 * After definition.
	 *
	 * ```markdown
	 * > | [a]: b
	 *           ^
	 * > | [a]: b "c"
	 *               ^
	 * ```
	 *
	 * @type {State}
	 */
	function after(code) {
		return markdownSpace(code) ? factorySpace(effects, afterWhitespace, "whitespace")(code) : afterWhitespace(code);
	}

	/**
	 * After definition, after optional whitespace.
	 *
	 * ```markdown
	 * > | [a]: b
	 *           ^
	 * > | [a]: b "c"
	 *               ^
	 * ```
	 *
	 * @type {State}
	 */
	function afterWhitespace(code) {
		if (code === null || markdownLineEnding(code)) {
			effects.exit("definition");

			// Note: we don’t care about uniqueness.
			// It’s likely that that doesn’t happen very frequently.
			// It is more likely that it wastes precious time.
			self.parser.defined.push(identifier);

			// To do: `markdown-rs` interrupt.
			// // You’d be interrupting.
			// tokenizer.interrupt = true
			return ok(code);
		}
		return nok(code);
	}
}

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeTitleBefore(effects, ok, nok) {
	return titleBefore;

	/**
	 * After destination, at whitespace.
	 *
	 * ```markdown
	 * > | [a]: b
	 *           ^
	 * > | [a]: b "c"
	 *           ^
	 * ```
	 *
	 * @type {State}
	 */
	function titleBefore(code) {
		return markdownLineEndingOrSpace(code) ? factoryWhitespace(effects, beforeMarker)(code) : nok(code);
	}

	/**
	 * At title.
	 *
	 * ```markdown
	 *   | [a]: b
	 * > | "c"
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function beforeMarker(code) {
		return factoryTitle(effects, titleAfter, nok, "definitionTitle", "definitionTitleMarker", "definitionTitleString")(code);
	}

	/**
	 * After title.
	 *
	 * ```markdown
	 * > | [a]: b "c"
	 *               ^
	 * ```
	 *
	 * @type {State}
	 */
	function titleAfter(code) {
		return markdownSpace(code) ? factorySpace(effects, titleAfterOptionalWhitespace, "whitespace")(code) : titleAfterOptionalWhitespace(code);
	}

	/**
	 * After title, after optional whitespace.
	 *
	 * ```markdown
	 * > | [a]: b "c"
	 *               ^
	 * ```
	 *
	 * @type {State}
	 */
	function titleAfterOptionalWhitespace(code) {
		return code === null || markdownLineEnding(code) ? ok(code) : nok(code);
	}
}

/**
 * @import {
 *   Construct,
 *   State,
 *   TokenizeContext,
 *   Tokenizer
 * } from 'micromark-util-types'
 */

/** @type {Construct} */
const hardBreakEscape = {
	name: "hardBreakEscape",
	tokenize: tokenizeHardBreakEscape,
};

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeHardBreakEscape(effects, ok, nok) {
	return start;

	/**
	 * Start of a hard break (escape).
	 *
	 * ```markdown
	 * > | a\
	 *      ^
	 *   | b
	 * ```
	 *
	 * @type {State}
	 */
	function start(code) {
		effects.enter("hardBreakEscape");
		effects.consume(code);
		return after;
	}

	/**
	 * After `\`, at eol.
	 *
	 * ```markdown
	 * > | a\
	 *       ^
	 *   | b
	 * ```
	 *
	 *  @type {State}
	 */
	function after(code) {
		if (markdownLineEnding(code)) {
			effects.exit("hardBreakEscape");
			return ok(code);
		}
		return nok(code);
	}
}

/**
 * @import {
 *   Construct,
 *   Resolver,
 *   State,
 *   TokenizeContext,
 *   Tokenizer,
 *   Token
 * } from 'micromark-util-types'
 */

/** @type {Construct} */
const headingAtx = {
	name: "headingAtx",
	resolve: resolveHeadingAtx,
	tokenize: tokenizeHeadingAtx,
};

/** @type {Resolver} */
function resolveHeadingAtx(events, context) {
	let contentEnd = events.length - 2;
	let contentStart = 3;
	/** @type {Token} */
	let content;
	/** @type {Token} */
	let text;

	// Prefix whitespace, part of the opening.
	if (events[contentStart][1].type === "whitespace") {
		contentStart += 2;
	}

	// Suffix whitespace, part of the closing.
	if (contentEnd - 2 > contentStart && events[contentEnd][1].type === "whitespace") {
		contentEnd -= 2;
	}
	if (events[contentEnd][1].type === "atxHeadingSequence" && (contentStart === contentEnd - 1 || (contentEnd - 4 > contentStart && events[contentEnd - 2][1].type === "whitespace"))) {
		contentEnd -= contentStart + 1 === contentEnd ? 2 : 4;
	}
	if (contentEnd > contentStart) {
		content = {
			type: "atxHeadingText",
			start: events[contentStart][1].start,
			end: events[contentEnd][1].end,
		};
		text = {
			type: "chunkText",
			start: events[contentStart][1].start,
			end: events[contentEnd][1].end,
			contentType: "text",
		};
		splice(events, contentStart, contentEnd - contentStart + 1, [
			["enter", content, context],
			["enter", text, context],
			["exit", text, context],
			["exit", content, context],
		]);
	}
	return events;
}

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeHeadingAtx(effects, ok, nok) {
	let size = 0;
	return start;

	/**
	 * Start of a heading (atx).
	 *
	 * ```markdown
	 * > | ## aa
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function start(code) {
		// To do: parse indent like `markdown-rs`.
		effects.enter("atxHeading");
		return before(code);
	}

	/**
	 * After optional whitespace, at `#`.
	 *
	 * ```markdown
	 * > | ## aa
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function before(code) {
		effects.enter("atxHeadingSequence");
		return sequenceOpen(code);
	}

	/**
	 * In opening sequence.
	 *
	 * ```markdown
	 * > | ## aa
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function sequenceOpen(code) {
		if (code === 35 && size++ < 6) {
			effects.consume(code);
			return sequenceOpen;
		}

		// Always at least one `#`.
		if (code === null || markdownLineEndingOrSpace(code)) {
			effects.exit("atxHeadingSequence");
			return atBreak(code);
		}
		return nok(code);
	}

	/**
	 * After something, before something else.
	 *
	 * ```markdown
	 * > | ## aa
	 *       ^
	 * ```
	 *
	 * @type {State}
	 */
	function atBreak(code) {
		if (code === 35) {
			effects.enter("atxHeadingSequence");
			return sequenceFurther(code);
		}
		if (code === null || markdownLineEnding(code)) {
			effects.exit("atxHeading");
			// To do: interrupt like `markdown-rs`.
			// // Feel free to interrupt.
			// tokenizer.interrupt = false
			return ok(code);
		}
		if (markdownSpace(code)) {
			return factorySpace(effects, atBreak, "whitespace")(code);
		}

		// To do: generate `data` tokens, add the `text` token later.
		// Needs edit map, see: `markdown.rs`.
		effects.enter("atxHeadingText");
		return data(code);
	}

	/**
	 * In further sequence (after whitespace).
	 *
	 * Could be normal “visible” hashes in the heading or a final sequence.
	 *
	 * ```markdown
	 * > | ## aa ##
	 *           ^
	 * ```
	 *
	 * @type {State}
	 */
	function sequenceFurther(code) {
		if (code === 35) {
			effects.consume(code);
			return sequenceFurther;
		}
		effects.exit("atxHeadingSequence");
		return atBreak(code);
	}

	/**
	 * In text.
	 *
	 * ```markdown
	 * > | ## aa
	 *        ^
	 * ```
	 *
	 * @type {State}
	 */
	function data(code) {
		if (code === null || code === 35 || markdownLineEndingOrSpace(code)) {
			effects.exit("atxHeadingText");
			return atBreak(code);
		}
		effects.consume(code);
		return data;
	}
}

/**
 * List of lowercase HTML “block” tag names.
 *
 * The list, when parsing HTML (flow), results in more relaxed rules (condition
 * 6).
 * Because they are known blocks, the HTML-like syntax doesn’t have to be
 * strictly parsed.
 * For tag names not in this list, a more strict algorithm (condition 7) is used
 * to detect whether the HTML-like syntax is seen as HTML (flow) or not.
 *
 * This is copied from:
 * <https://spec.commonmark.org/0.30/#html-blocks>.
 *
 * > 👉 **Note**: `search` was added in `CommonMark@0.31`.
 */
const htmlBlockNames = [
	"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",
	"h1",
	"h2",
	"h3",
	"h4",
	"h5",
	"h6",
	"head",
	"header",
	"hr",
	"html",
	"iframe",
	"legend",
	"li",
	"link",
	"main",
	"menu",
	"menuitem",
	"nav",
	"noframes",
	"ol",
	"optgroup",
	"option",
	"p",
	"param",
	"search",
	"section",
	"summary",
	"table",
	"tbody",
	"td",
	"tfoot",
	"th",
	"thead",
	"title",
	"tr",
	"track",
	"ul",
];

/**
 * List of lowercase HTML “raw” tag names.
 *
 * The list, when parsing HTML (flow), results in HTML that can include lines
 * without exiting, until a closing tag also in this list is found (condition
 * 1).
 *
 * This module is copied from:
 * <https://spec.commonmark.org/0.30/#html-blocks>.
 *
 * > 👉 **Note**: `textarea` was added in `CommonMark@0.30`.
 */
const htmlRawNames = ["pre", "script", "style", "textarea"];

/**
 * @import {
 *   Code,
 *   Construct,
 *   Resolver,
 *   State,
 *   TokenizeContext,
 *   Tokenizer
 * } from 'micromark-util-types'
 */

/** @type {Construct} */
const htmlFlow = {
	concrete: true,
	name: "htmlFlow",
	resolveTo: resolveToHtmlFlow,
	tokenize: tokenizeHtmlFlow,
};

/** @type {Construct} */
const blankLineBefore = {
	partial: true,
	tokenize: tokenizeBlankLineBefore,
};
const nonLazyContinuationStart = {
	partial: true,
	tokenize: tokenizeNonLazyContinuationStart,
};

/** @type {Resolver} */
function resolveToHtmlFlow(events) {
	let index = events.length;
	while (index--) {
		if (events[index][0] === "enter" && events[index][1].type === "htmlFlow") {
			break;
		}
	}
	if (index > 1 && events[index - 2][1].type === "linePrefix") {
		// Add the prefix start to the HTML token.
		events[index][1].start = events[index - 2][1].start;
		// Add the prefix start to the HTML line token.
		events[index + 1][1].start = events[index - 2][1].start;
		// Remove the line prefix.
		events.splice(index - 2, 2);
	}
	return events;
}

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeHtmlFlow(effects, ok, nok) {
	const self = this;
	/** @type {number} */
	let marker;
	/** @type {boolean} */
	let closingTag;
	/** @type {string} */
	let buffer;
	/** @type {number} */
	let index;
	/** @type {Code} */
	let markerB;
	return start;

	/**
	 * Start of HTML (flow).
	 *
	 * ```markdown
	 * > | <x />
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function start(code) {
		// To do: parse indent like `markdown-rs`.
		return before(code);
	}

	/**
	 * At `<`, after optional whitespace.
	 *
	 * ```markdown
	 * > | <x />
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function before(code) {
		effects.enter("htmlFlow");
		effects.enter("htmlFlowData");
		effects.consume(code);
		return open;
	}

	/**
	 * After `<`, at tag name or other stuff.
	 *
	 * ```markdown
	 * > | <x />
	 *      ^
	 * > | <!doctype>
	 *      ^
	 * > | <!--xxx-->
	 *      ^
	 * ```
	 *
	 * @type {State}
	 */
	function open(code) {
		if (code === 33) {
			effects.consume(code);
			return declarationOpen;
		}
		if (code === 47) {
			effects.consume(code);
			closingTag = true;
			return tagCloseStart;
		}
		if (code === 63) {
			effects.consume(code);
			marker = 3;
			// To do:
			// tokenizer.concrete = true
			// To do: use `markdown-rs` style interrupt.
			// While we’re in an instruction instead of a declaration, we’re on a `?`
			// right now, so we do need to search for `>`, similar to declarations.
			return self.interrupt ? ok : continuationDeclarationInside;
		}

		// ASCII alphabetical.
		if (asciiAlpha(code)) {
			// Always the case.
			effects.consume(code);
			buffer = String.fromCharCode(code);
			return tagName;
		}
		return nok(code);
	}

	/**
	 * After `<!`, at declaration, comment, or CDATA.
	 *
	 * ```markdown
	 * > | <!doctype>
	 *       ^
	 * > | <!--xxx-->
	 *       ^
	 * > | <![CDATA[>&<]]>
	 *       ^
	 * ```
	 *
	 * @type {State}
	 */
	function declarationOpen(code) {
		if (code === 45) {
			effects.consume(code);
			marker = 2;
			return commentOpenInside;
		}
		if (code === 91) {
			effects.consume(code);
			marker = 5;
			index = 0;
			return cdataOpenInside;
		}

		// ASCII alphabetical.
		if (asciiAlpha(code)) {
			effects.consume(code);
			marker = 4;
			// // Do not form containers.
			// tokenizer.concrete = true
			return self.interrupt ? ok : continuationDeclarationInside;
		}
		return nok(code);
	}

	/**
	 * After `<!-`, inside a comment, at another `-`.
	 *
	 * ```markdown
	 * > | <!--xxx-->
	 *        ^
	 * ```
	 *
	 * @type {State}
	 */
	function commentOpenInside(code) {
		if (code === 45) {
			effects.consume(code);
			// // Do not form containers.
			// tokenizer.concrete = true
			return self.interrupt ? ok : continuationDeclarationInside;
		}
		return nok(code);
	}

	/**
	 * After `<![`, inside CDATA, expecting `CDATA[`.
	 *
	 * ```markdown
	 * > | <![CDATA[>&<]]>
	 *        ^^^^^^
	 * ```
	 *
	 * @type {State}
	 */
	function cdataOpenInside(code) {
		const value = "CDATA[";
		if (code === value.charCodeAt(index++)) {
			effects.consume(code);
			if (index === value.length) {
				// // Do not form containers.
				// tokenizer.concrete = true
				return self.interrupt ? ok : continuation;
			}
			return cdataOpenInside;
		}
		return nok(code);
	}

	/**
	 * After `</`, in closing tag, at tag name.
	 *
	 * ```markdown
	 * > | </x>
	 *       ^
	 * ```
	 *
	 * @type {State}
	 */
	function tagCloseStart(code) {
		if (asciiAlpha(code)) {
			// Always the case.
			effects.consume(code);
			buffer = String.fromCharCode(code);
			return tagName;
		}
		return nok(code);
	}

	/**
	 * In tag name.
	 *
	 * ```markdown
	 * > | <ab>
	 *      ^^
	 * > | </ab>
	 *       ^^
	 * ```
	 *
	 * @type {State}
	 */
	function tagName(code) {
		if (code === null || code === 47 || code === 62 || markdownLineEndingOrSpace(code)) {
			const slash = code === 47;
			const name = buffer.toLowerCase();
			if (!slash && !closingTag && htmlRawNames.includes(name)) {
				marker = 1;
				// // Do not form containers.
				// tokenizer.concrete = true
				return self.interrupt ? ok(code) : continuation(code);
			}
			if (htmlBlockNames.includes(buffer.toLowerCase())) {
				marker = 6;
				if (slash) {
					effects.consume(code);
					return basicSelfClosing;
				}

				// // Do not form containers.
				// tokenizer.concrete = true
				return self.interrupt ? ok(code) : continuation(code);
			}
			marker = 7;
			// Do not support complete HTML when interrupting.
			return (
				self.interrupt && !self.parser.lazy[self.now().line] ? nok(code)
				: closingTag ? completeClosingTagAfter(code)
				: completeAttributeNameBefore(code)
			);
		}

		// ASCII alphanumerical and `-`.
		if (code === 45 || asciiAlphanumeric(code)) {
			effects.consume(code);
			buffer += String.fromCharCode(code);
			return tagName;
		}
		return nok(code);
	}

	/**
	 * After closing slash of a basic tag name.
	 *
	 * ```markdown
	 * > | <div/>
	 *          ^
	 * ```
	 *
	 * @type {State}
	 */
	function basicSelfClosing(code) {
		if (code === 62) {
			effects.consume(code);
			// // Do not form containers.
			// tokenizer.concrete = true
			return self.interrupt ? ok : continuation;
		}
		return nok(code);
	}

	/**
	 * After closing slash of a complete tag name.
	 *
	 * ```markdown
	 * > | <x/>
	 *        ^
	 * ```
	 *
	 * @type {State}
	 */
	function completeClosingTagAfter(code) {
		if (markdownSpace(code)) {
			effects.consume(code);
			return completeClosingTagAfter;
		}
		return completeEnd(code);
	}

	/**
	 * At an attribute name.
	 *
	 * At first, this state is used after a complete tag name, after whitespace,
	 * where it expects optional attributes or the end of the tag.
	 * It is also reused after attributes, when expecting more optional
	 * attributes.
	 *
	 * ```markdown
	 * > | <a />
	 *        ^
	 * > | <a :b>
	 *        ^
	 * > | <a _b>
	 *        ^
	 * > | <a b>
	 *        ^
	 * > | <a >
	 *        ^
	 * ```
	 *
	 * @type {State}
	 */
	function completeAttributeNameBefore(code) {
		if (code === 47) {
			effects.consume(code);
			return completeEnd;
		}

		// ASCII alphanumerical and `:` and `_`.
		if (code === 58 || code === 95 || asciiAlpha(code)) {
			effects.consume(code);
			return completeAttributeName;
		}
		if (markdownSpace(code)) {
			effects.consume(code);
			return completeAttributeNameBefore;
		}
		return completeEnd(code);
	}

	/**
	 * In attribute name.
	 *
	 * ```markdown
	 * > | <a :b>
	 *         ^
	 * > | <a _b>
	 *         ^
	 * > | <a b>
	 *         ^
	 * ```
	 *
	 * @type {State}
	 */
	function completeAttributeName(code) {
		// ASCII alphanumerical and `-`, `.`, `:`, and `_`.
		if (code === 45 || code === 46 || code === 58 || code === 95 || asciiAlphanumeric(code)) {
			effects.consume(code);
			return completeAttributeName;
		}
		return completeAttributeNameAfter(code);
	}

	/**
	 * After attribute name, at an optional initializer, the end of the tag, or
	 * whitespace.
	 *
	 * ```markdown
	 * > | <a b>
	 *         ^
	 * > | <a b=c>
	 *         ^
	 * ```
	 *
	 * @type {State}
	 */
	function completeAttributeNameAfter(code) {
		if (code === 61) {
			effects.consume(code);
			return completeAttributeValueBefore;
		}
		if (markdownSpace(code)) {
			effects.consume(code);
			return completeAttributeNameAfter;
		}
		return completeAttributeNameBefore(code);
	}

	/**
	 * Before unquoted, double quoted, or single quoted attribute value, allowing
	 * whitespace.
	 *
	 * ```markdown
	 * > | <a b=c>
	 *          ^
	 * > | <a b="c">
	 *          ^
	 * ```
	 *
	 * @type {State}
	 */
	function completeAttributeValueBefore(code) {
		if (code === null || code === 60 || code === 61 || code === 62 || code === 96) {
			return nok(code);
		}
		if (code === 34 || code === 39) {
			effects.consume(code);
			markerB = code;
			return completeAttributeValueQuoted;
		}
		if (markdownSpace(code)) {
			effects.consume(code);
			return completeAttributeValueBefore;
		}
		return completeAttributeValueUnquoted(code);
	}

	/**
	 * In double or single quoted attribute value.
	 *
	 * ```markdown
	 * > | <a b="c">
	 *           ^
	 * > | <a b='c'>
	 *           ^
	 * ```
	 *
	 * @type {State}
	 */
	function completeAttributeValueQuoted(code) {
		if (code === markerB) {
			effects.consume(code);
			markerB = null;
			return completeAttributeValueQuotedAfter;
		}
		if (code === null || markdownLineEnding(code)) {
			return nok(code);
		}
		effects.consume(code);
		return completeAttributeValueQuoted;
	}

	/**
	 * In unquoted attribute value.
	 *
	 * ```markdown
	 * > | <a b=c>
	 *          ^
	 * ```
	 *
	 * @type {State}
	 */
	function completeAttributeValueUnquoted(code) {
		if (code === null || code === 34 || code === 39 || code === 47 || code === 60 || code === 61 || code === 62 || code === 96 || markdownLineEndingOrSpace(code)) {
			return completeAttributeNameAfter(code);
		}
		effects.consume(code);
		return completeAttributeValueUnquoted;
	}

	/**
	 * After double or single quoted attribute value, before whitespace or the
	 * end of the tag.
	 *
	 * ```markdown
	 * > | <a b="c">
	 *            ^
	 * ```
	 *
	 * @type {State}
	 */
	function completeAttributeValueQuotedAfter(code) {
		if (code === 47 || code === 62 || markdownSpace(code)) {
			return completeAttributeNameBefore(code);
		}
		return nok(code);
	}

	/**
	 * In certain circumstances of a complete tag where only an `>` is allowed.
	 *
	 * ```markdown
	 * > | <a b="c">
	 *             ^
	 * ```
	 *
	 * @type {State}
	 */
	function completeEnd(code) {
		if (code === 62) {
			effects.consume(code);
			return completeAfter;
		}
		return nok(code);
	}

	/**
	 * After `>` in a complete tag.
	 *
	 * ```markdown
	 * > | <x>
	 *        ^
	 * ```
	 *
	 * @type {State}
	 */
	function completeAfter(code) {
		if (code === null || markdownLineEnding(code)) {
			// // Do not form containers.
			// tokenizer.concrete = true
			return continuation(code);
		}
		if (markdownSpace(code)) {
			effects.consume(code);
			return completeAfter;
		}
		return nok(code);
	}

	/**
	 * In continuation of any HTML kind.
	 *
	 * ```markdown
	 * > | <!--xxx-->
	 *          ^
	 * ```
	 *
	 * @type {State}
	 */
	function continuation(code) {
		if (code === 45 && marker === 2) {
			effects.consume(code);
			return continuationCommentInside;
		}
		if (code === 60 && marker === 1) {
			effects.consume(code);
			return continuationRawTagOpen;
		}
		if (code === 62 && marker === 4) {
			effects.consume(code);
			return continuationClose;
		}
		if (code === 63 && marker === 3) {
			effects.consume(code);
			return continuationDeclarationInside;
		}
		if (code === 93 && marker === 5) {
			effects.consume(code);
			return continuationCdataInside;
		}
		if (markdownLineEnding(code) && (marker === 6 || marker === 7)) {
			effects.exit("htmlFlowData");
			return effects.check(blankLineBefore, continuationAfter, continuationStart)(code);
		}
		if (code === null || markdownLineEnding(code)) {
			effects.exit("htmlFlowData");
			return continuationStart(code);
		}
		effects.consume(code);
		return continuation;
	}

	/**
	 * In continuation, at eol.
	 *
	 * ```markdown
	 * > | <x>
	 *        ^
	 *   | asd
	 * ```
	 *
	 * @type {State}
	 */
	function continuationStart(code) {
		return effects.check(nonLazyContinuationStart, continuationStartNonLazy, continuationAfter)(code);
	}

	/**
	 * In continuation, at eol, before non-lazy content.
	 *
	 * ```markdown
	 * > | <x>
	 *        ^
	 *   | asd
	 * ```
	 *
	 * @type {State}
	 */
	function continuationStartNonLazy(code) {
		effects.enter("lineEnding");
		effects.consume(code);
		effects.exit("lineEnding");
		return continuationBefore;
	}

	/**
	 * In continuation, before non-lazy content.
	 *
	 * ```markdown
	 *   | <x>
	 * > | asd
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function continuationBefore(code) {
		if (code === null || markdownLineEnding(code)) {
			return continuationStart(code);
		}
		effects.enter("htmlFlowData");
		return continuation(code);
	}

	/**
	 * In comment continuation, after one `-`, expecting another.
	 *
	 * ```markdown
	 * > | <!--xxx-->
	 *             ^
	 * ```
	 *
	 * @type {State}
	 */
	function continuationCommentInside(code) {
		if (code === 45) {
			effects.consume(code);
			return continuationDeclarationInside;
		}
		return continuation(code);
	}

	/**
	 * In raw continuation, after `<`, at `/`.
	 *
	 * ```markdown
	 * > | <script>console.log(1)</script>
	 *                            ^
	 * ```
	 *
	 * @type {State}
	 */
	function continuationRawTagOpen(code) {
		if (code === 47) {
			effects.consume(code);
			buffer = "";
			return continuationRawEndTag;
		}
		return continuation(code);
	}

	/**
	 * In raw continuation, after `</`, in a raw tag name.
	 *
	 * ```markdown
	 * > | <script>console.log(1)</script>
	 *                             ^^^^^^
	 * ```
	 *
	 * @type {State}
	 */
	function continuationRawEndTag(code) {
		if (code === 62) {
			const name = buffer.toLowerCase();
			if (htmlRawNames.includes(name)) {
				effects.consume(code);
				return continuationClose;
			}
			return continuation(code);
		}
		if (asciiAlpha(code) && buffer.length < 8) {
			// Always the case.
			effects.consume(code);
			buffer += String.fromCharCode(code);
			return continuationRawEndTag;
		}
		return continuation(code);
	}

	/**
	 * In cdata continuation, after `]`, expecting `]>`.
	 *
	 * ```markdown
	 * > | <![CDATA[>&<]]>
	 *                  ^
	 * ```
	 *
	 * @type {State}
	 */
	function continuationCdataInside(code) {
		if (code === 93) {
			effects.consume(code);
			return continuationDeclarationInside;
		}
		return continuation(code);
	}

	/**
	 * In declaration or instruction continuation, at `>`.
	 *
	 * ```markdown
	 * > | <!-->
	 *         ^
	 * > | <?>
	 *       ^
	 * > | <!q>
	 *        ^
	 * > | <!--ab-->
	 *             ^
	 * > | <![CDATA[>&<]]>
	 *                   ^
	 * ```
	 *
	 * @type {State}
	 */
	function continuationDeclarationInside(code) {
		if (code === 62) {
			effects.consume(code);
			return continuationClose;
		}

		// More dashes.
		if (code === 45 && marker === 2) {
			effects.consume(code);
			return continuationDeclarationInside;
		}
		return continuation(code);
	}

	/**
	 * In closed continuation: everything we get until the eol/eof is part of it.
	 *
	 * ```markdown
	 * > | <!doctype>
	 *               ^
	 * ```
	 *
	 * @type {State}
	 */
	function continuationClose(code) {
		if (code === null || markdownLineEnding(code)) {
			effects.exit("htmlFlowData");
			return continuationAfter(code);
		}
		effects.consume(code);
		return continuationClose;
	}

	/**
	 * Done.
	 *
	 * ```markdown
	 * > | <!doctype>
	 *               ^
	 * ```
	 *
	 * @type {State}
	 */
	function continuationAfter(code) {
		effects.exit("htmlFlow");
		// // Feel free to interrupt.
		// tokenizer.interrupt = false
		// // No longer concrete.
		// tokenizer.concrete = false
		return ok(code);
	}
}

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeNonLazyContinuationStart(effects, ok, nok) {
	const self = this;
	return start;

	/**
	 * At eol, before continuation.
	 *
	 * ```markdown
	 * > | * ```js
	 *            ^
	 *   | b
	 * ```
	 *
	 * @type {State}
	 */
	function start(code) {
		if (markdownLineEnding(code)) {
			effects.enter("lineEnding");
			effects.consume(code);
			effects.exit("lineEnding");
			return after;
		}
		return nok(code);
	}

	/**
	 * A continuation.
	 *
	 * ```markdown
	 *   | * ```js
	 * > | b
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function after(code) {
		return self.parser.lazy[self.now().line] ? nok(code) : ok(code);
	}
}

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeBlankLineBefore(effects, ok, nok) {
	return start;

	/**
	 * Before eol, expecting blank line.
	 *
	 * ```markdown
	 * > | <div>
	 *          ^
	 *   |
	 * ```
	 *
	 * @type {State}
	 */
	function start(code) {
		effects.enter("lineEnding");
		effects.consume(code);
		effects.exit("lineEnding");
		return effects.attempt(blankLine, ok, nok);
	}
}

/**
 * @import {
 *   Code,
 *   Construct,
 *   State,
 *   TokenizeContext,
 *   Tokenizer
 * } from 'micromark-util-types'
 */

/** @type {Construct} */
const htmlText = {
	name: "htmlText",
	tokenize: tokenizeHtmlText,
};

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeHtmlText(effects, ok, nok) {
	const self = this;
	/** @type {NonNullable<Code> | undefined} */
	let marker;
	/** @type {number} */
	let index;
	/** @type {State} */
	let returnState;
	return start;

	/**
	 * Start of HTML (text).
	 *
	 * ```markdown
	 * > | a <b> c
	 *       ^
	 * ```
	 *
	 * @type {State}
	 */
	function start(code) {
		effects.enter("htmlText");
		effects.enter("htmlTextData");
		effects.consume(code);
		return open;
	}

	/**
	 * After `<`, at tag name or other stuff.
	 *
	 * ```markdown
	 * > | a <b> c
	 *        ^
	 * > | a <!doctype> c
	 *        ^
	 * > | a <!--b--> c
	 *        ^
	 * ```
	 *
	 * @type {State}
	 */
	function open(code) {
		if (code === 33) {
			effects.consume(code);
			return declarationOpen;
		}
		if (code === 47) {
			effects.consume(code);
			return tagCloseStart;
		}
		if (code === 63) {
			effects.consume(code);
			return instruction;
		}

		// ASCII alphabetical.
		if (asciiAlpha(code)) {
			effects.consume(code);
			return tagOpen;
		}
		return nok(code);
	}

	/**
	 * After `<!`, at declaration, comment, or CDATA.
	 *
	 * ```markdown
	 * > | a <!doctype> c
	 *         ^
	 * > | a <!--b--> c
	 *         ^
	 * > | a <![CDATA[>&<]]> c
	 *         ^
	 * ```
	 *
	 * @type {State}
	 */
	function declarationOpen(code) {
		if (code === 45) {
			effects.consume(code);
			return commentOpenInside;
		}
		if (code === 91) {
			effects.consume(code);
			index = 0;
			return cdataOpenInside;
		}
		if (asciiAlpha(code)) {
			effects.consume(code);
			return declaration;
		}
		return nok(code);
	}

	/**
	 * In a comment, after `<!-`, at another `-`.
	 *
	 * ```markdown
	 * > | a <!--b--> c
	 *          ^
	 * ```
	 *
	 * @type {State}
	 */
	function commentOpenInside(code) {
		if (code === 45) {
			effects.consume(code);
			return commentEnd;
		}
		return nok(code);
	}

	/**
	 * In comment.
	 *
	 * ```markdown
	 * > | a <!--b--> c
	 *           ^
	 * ```
	 *
	 * @type {State}
	 */
	function comment(code) {
		if (code === null) {
			return nok(code);
		}
		if (code === 45) {
			effects.consume(code);
			return commentClose;
		}
		if (markdownLineEnding(code)) {
			returnState = comment;
			return lineEndingBefore(code);
		}
		effects.consume(code);
		return comment;
	}

	/**
	 * In comment, after `-`.
	 *
	 * ```markdown
	 * > | a <!--b--> c
	 *             ^
	 * ```
	 *
	 * @type {State}
	 */
	function commentClose(code) {
		if (code === 45) {
			effects.consume(code);
			return commentEnd;
		}
		return comment(code);
	}

	/**
	 * In comment, after `--`.
	 *
	 * ```markdown
	 * > | a <!--b--> c
	 *              ^
	 * ```
	 *
	 * @type {State}
	 */
	function commentEnd(code) {
		return (
			code === 62 ? end(code)
			: code === 45 ? commentClose(code)
			: comment(code)
		);
	}

	/**
	 * After `<![`, in CDATA, expecting `CDATA[`.
	 *
	 * ```markdown
	 * > | a <![CDATA[>&<]]> b
	 *          ^^^^^^
	 * ```
	 *
	 * @type {State}
	 */
	function cdataOpenInside(code) {
		const value = "CDATA[";
		if (code === value.charCodeAt(index++)) {
			effects.consume(code);
			return index === value.length ? cdata : cdataOpenInside;
		}
		return nok(code);
	}

	/**
	 * In CDATA.
	 *
	 * ```markdown
	 * > | a <![CDATA[>&<]]> b
	 *                ^^^
	 * ```
	 *
	 * @type {State}
	 */
	function cdata(code) {
		if (code === null) {
			return nok(code);
		}
		if (code === 93) {
			effects.consume(code);
			return cdataClose;
		}
		if (markdownLineEnding(code)) {
			returnState = cdata;
			return lineEndingBefore(code);
		}
		effects.consume(code);
		return cdata;
	}

	/**
	 * In CDATA, after `]`, at another `]`.
	 *
	 * ```markdown
	 * > | a <![CDATA[>&<]]> b
	 *                    ^
	 * ```
	 *
	 * @type {State}
	 */
	function cdataClose(code) {
		if (code === 93) {
			effects.consume(code);
			return cdataEnd;
		}
		return cdata(code);
	}

	/**
	 * In CDATA, after `]]`, at `>`.
	 *
	 * ```markdown
	 * > | a <![CDATA[>&<]]> b
	 *                     ^
	 * ```
	 *
	 * @type {State}
	 */
	function cdataEnd(code) {
		if (code === 62) {
			return end(code);
		}
		if (code === 93) {
			effects.consume(code);
			return cdataEnd;
		}
		return cdata(code);
	}

	/**
	 * In declaration.
	 *
	 * ```markdown
	 * > | a <!b> c
	 *          ^
	 * ```
	 *
	 * @type {State}
	 */
	function declaration(code) {
		if (code === null || code === 62) {
			return end(code);
		}
		if (markdownLineEnding(code)) {
			returnState = declaration;
			return lineEndingBefore(code);
		}
		effects.consume(code);
		return declaration;
	}

	/**
	 * In instruction.
	 *
	 * ```markdown
	 * > | a <?b?> c
	 *         ^
	 * ```
	 *
	 * @type {State}
	 */
	function instruction(code) {
		if (code === null) {
			return nok(code);
		}
		if (code === 63) {
			effects.consume(code);
			return instructionClose;
		}
		if (markdownLineEnding(code)) {
			returnState = instruction;
			return lineEndingBefore(code);
		}
		effects.consume(code);
		return instruction;
	}

	/**
	 * In instruction, after `?`, at `>`.
	 *
	 * ```markdown
	 * > | a <?b?> c
	 *           ^
	 * ```
	 *
	 * @type {State}
	 */
	function instructionClose(code) {
		return code === 62 ? end(code) : instruction(code);
	}

	/**
	 * After `</`, in closing tag, at tag name.
	 *
	 * ```markdown
	 * > | a </b> c
	 *         ^
	 * ```
	 *
	 * @type {State}
	 */
	function tagCloseStart(code) {
		// ASCII alphabetical.
		if (asciiAlpha(code)) {
			effects.consume(code);
			return tagClose;
		}
		return nok(code);
	}

	/**
	 * After `</x`, in a tag name.
	 *
	 * ```markdown
	 * > | a </b> c
	 *          ^
	 * ```
	 *
	 * @type {State}
	 */
	function tagClose(code) {
		// ASCII alphanumerical and `-`.
		if (code === 45 || asciiAlphanumeric(code)) {
			effects.consume(code);
			return tagClose;
		}
		return tagCloseBetween(code);
	}

	/**
	 * In closing tag, after tag name.
	 *
	 * ```markdown
	 * > | a </b> c
	 *          ^
	 * ```
	 *
	 * @type {State}
	 */
	function tagCloseBetween(code) {
		if (markdownLineEnding(code)) {
			returnState = tagCloseBetween;
			return lineEndingBefore(code);
		}
		if (markdownSpace(code)) {
			effects.consume(code);
			return tagCloseBetween;
		}
		return end(code);
	}

	/**
	 * After `<x`, in opening tag name.
	 *
	 * ```markdown
	 * > | a <b> c
	 *         ^
	 * ```
	 *
	 * @type {State}
	 */
	function tagOpen(code) {
		// ASCII alphanumerical and `-`.
		if (code === 45 || asciiAlphanumeric(code)) {
			effects.consume(code);
			return tagOpen;
		}
		if (code === 47 || code === 62 || markdownLineEndingOrSpace(code)) {
			return tagOpenBetween(code);
		}
		return nok(code);
	}

	/**
	 * In opening tag, after tag name.
	 *
	 * ```markdown
	 * > | a <b> c
	 *         ^
	 * ```
	 *
	 * @type {State}
	 */
	function tagOpenBetween(code) {
		if (code === 47) {
			effects.consume(code);
			return end;
		}

		// ASCII alphabetical and `:` and `_`.
		if (code === 58 || code === 95 || asciiAlpha(code)) {
			effects.consume(code);
			return tagOpenAttributeName;
		}
		if (markdownLineEnding(code)) {
			returnState = tagOpenBetween;
			return lineEndingBefore(code);
		}
		if (markdownSpace(code)) {
			effects.consume(code);
			return tagOpenBetween;
		}
		return end(code);
	}

	/**
	 * In attribute name.
	 *
	 * ```markdown
	 * > | a <b c> d
	 *          ^
	 * ```
	 *
	 * @type {State}
	 */
	function tagOpenAttributeName(code) {
		// ASCII alphabetical and `-`, `.`, `:`, and `_`.
		if (code === 45 || code === 46 || code === 58 || code === 95 || asciiAlphanumeric(code)) {
			effects.consume(code);
			return tagOpenAttributeName;
		}
		return tagOpenAttributeNameAfter(code);
	}

	/**
	 * After attribute name, before initializer, the end of the tag, or
	 * whitespace.
	 *
	 * ```markdown
	 * > | a <b c> d
	 *           ^
	 * ```
	 *
	 * @type {State}
	 */
	function tagOpenAttributeNameAfter(code) {
		if (code === 61) {
			effects.consume(code);
			return tagOpenAttributeValueBefore;
		}
		if (markdownLineEnding(code)) {
			returnState = tagOpenAttributeNameAfter;
			return lineEndingBefore(code);
		}
		if (markdownSpace(code)) {
			effects.consume(code);
			return tagOpenAttributeNameAfter;
		}
		return tagOpenBetween(code);
	}

	/**
	 * Before unquoted, double quoted, or single quoted attribute value, allowing
	 * whitespace.
	 *
	 * ```markdown
	 * > | a <b c=d> e
	 *            ^
	 * ```
	 *
	 * @type {State}
	 */
	function tagOpenAttributeValueBefore(code) {
		if (code === null || code === 60 || code === 61 || code === 62 || code === 96) {
			return nok(code);
		}
		if (code === 34 || code === 39) {
			effects.consume(code);
			marker = code;
			return tagOpenAttributeValueQuoted;
		}
		if (markdownLineEnding(code)) {
			returnState = tagOpenAttributeValueBefore;
			return lineEndingBefore(code);
		}
		if (markdownSpace(code)) {
			effects.consume(code);
			return tagOpenAttributeValueBefore;
		}
		effects.consume(code);
		return tagOpenAttributeValueUnquoted;
	}

	/**
	 * In double or single quoted attribute value.
	 *
	 * ```markdown
	 * > | a <b c="d"> e
	 *             ^
	 * ```
	 *
	 * @type {State}
	 */
	function tagOpenAttributeValueQuoted(code) {
		if (code === marker) {
			effects.consume(code);
			marker = undefined;
			return tagOpenAttributeValueQuotedAfter;
		}
		if (code === null) {
			return nok(code);
		}
		if (markdownLineEnding(code)) {
			returnState = tagOpenAttributeValueQuoted;
			return lineEndingBefore(code);
		}
		effects.consume(code);
		return tagOpenAttributeValueQuoted;
	}

	/**
	 * In unquoted attribute value.
	 *
	 * ```markdown
	 * > | a <b c=d> e
	 *            ^
	 * ```
	 *
	 * @type {State}
	 */
	function tagOpenAttributeValueUnquoted(code) {
		if (code === null || code === 34 || code === 39 || code === 60 || code === 61 || code === 96) {
			return nok(code);
		}
		if (code === 47 || code === 62 || markdownLineEndingOrSpace(code)) {
			return tagOpenBetween(code);
		}
		effects.consume(code);
		return tagOpenAttributeValueUnquoted;
	}

	/**
	 * After double or single quoted attribute value, before whitespace or the end
	 * of the tag.
	 *
	 * ```markdown
	 * > | a <b c="d"> e
	 *               ^
	 * ```
	 *
	 * @type {State}
	 */
	function tagOpenAttributeValueQuotedAfter(code) {
		if (code === 47 || code === 62 || markdownLineEndingOrSpace(code)) {
			return tagOpenBetween(code);
		}
		return nok(code);
	}

	/**
	 * In certain circumstances of a tag where only an `>` is allowed.
	 *
	 * ```markdown
	 * > | a <b c="d"> e
	 *               ^
	 * ```
	 *
	 * @type {State}
	 */
	function end(code) {
		if (code === 62) {
			effects.consume(code);
			effects.exit("htmlTextData");
			effects.exit("htmlText");
			return ok;
		}
		return nok(code);
	}

	/**
	 * At eol.
	 *
	 * > 👉 **Note**: we can’t have blank lines in text, so no need to worry about
	 * > empty tokens.
	 *
	 * ```markdown
	 * > | a <!--a
	 *            ^
	 *   | b-->
	 * ```
	 *
	 * @type {State}
	 */
	function lineEndingBefore(code) {
		effects.exit("htmlTextData");
		effects.enter("lineEnding");
		effects.consume(code);
		effects.exit("lineEnding");
		return lineEndingAfter;
	}

	/**
	 * After eol, at optional whitespace.
	 *
	 * > 👉 **Note**: we can’t have blank lines in text, so no need to worry about
	 * > empty tokens.
	 *
	 * ```markdown
	 *   | a <!--a
	 * > | b-->
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function lineEndingAfter(code) {
		// Always populated by defaults.

		return markdownSpace(code) ?
				factorySpace(effects, lineEndingAfterPrefix, "linePrefix", self.parser.constructs.disable.null.includes("codeIndented") ? undefined : 4)(code)
			:	lineEndingAfterPrefix(code);
	}

	/**
	 * After eol, after optional whitespace.
	 *
	 * > 👉 **Note**: we can’t have blank lines in text, so no need to worry about
	 * > empty tokens.
	 *
	 * ```markdown
	 *   | a <!--a
	 * > | b-->
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function lineEndingAfterPrefix(code) {
		effects.enter("htmlTextData");
		return returnState(code);
	}
}

/**
 * @import {
 *   Construct,
 *   Event,
 *   Resolver,
 *   State,
 *   TokenizeContext,
 *   Tokenizer,
 *   Token
 * } from 'micromark-util-types'
 */

/** @type {Construct} */
const labelEnd = {
	name: "labelEnd",
	resolveAll: resolveAllLabelEnd,
	resolveTo: resolveToLabelEnd,
	tokenize: tokenizeLabelEnd,
};

/** @type {Construct} */
const resourceConstruct = {
	tokenize: tokenizeResource,
};
/** @type {Construct} */
const referenceFullConstruct = {
	tokenize: tokenizeReferenceFull,
};
/** @type {Construct} */
const referenceCollapsedConstruct = {
	tokenize: tokenizeReferenceCollapsed,
};

/** @type {Resolver} */
function resolveAllLabelEnd(events) {
	let index = -1;
	/** @type {Array<Event>} */
	const newEvents = [];
	while (++index < events.length) {
		const token = events[index][1];
		newEvents.push(events[index]);
		if (token.type === "labelImage" || token.type === "labelLink" || token.type === "labelEnd") {
			// Remove the marker.
			const offset = token.type === "labelImage" ? 4 : 2;
			token.type = "data";
			index += offset;
		}
	}

	// If the events are equal, we don't have to copy newEvents to events
	if (events.length !== newEvents.length) {
		splice(events, 0, events.length, newEvents);
	}
	return events;
}

/** @type {Resolver} */
function resolveToLabelEnd(events, context) {
	let index = events.length;
	let offset = 0;
	/** @type {Token} */
	let token;
	/** @type {number | undefined} */
	let open;
	/** @type {number | undefined} */
	let close;
	/** @type {Array<Event>} */
	let media;

	// Find an opening.
	while (index--) {
		token = events[index][1];
		if (open) {
			// If we see another link, or inactive link label, we’ve been here before.
			if (token.type === "link" || (token.type === "labelLink" && token._inactive)) {
				break;
			}

			// Mark other link openings as inactive, as we can’t have links in
			// links.
			if (events[index][0] === "enter" && token.type === "labelLink") {
				token._inactive = true;
			}
		} else if (close) {
			if (events[index][0] === "enter" && (token.type === "labelImage" || token.type === "labelLink") && !token._balanced) {
				open = index;
				if (token.type !== "labelLink") {
					offset = 2;
					break;
				}
			}
		} else if (token.type === "labelEnd") {
			close = index;
		}
	}
	const group = {
		type: events[open][1].type === "labelLink" ? "link" : "image",
		start: {
			...events[open][1].start,
		},
		end: {
			...events[events.length - 1][1].end,
		},
	};
	const label = {
		type: "label",
		start: {
			...events[open][1].start,
		},
		end: {
			...events[close][1].end,
		},
	};
	const text = {
		type: "labelText",
		start: {
			...events[open + offset + 2][1].end,
		},
		end: {
			...events[close - 2][1].start,
		},
	};
	media = [
		["enter", group, context],
		["enter", label, context],
	];

	// Opening marker.
	media = push(media, events.slice(open + 1, open + offset + 3));

	// Text open.
	media = push(media, [["enter", text, context]]);

	// Always populated by defaults.

	// Between.
	media = push(media, resolveAll(context.parser.constructs.insideSpan.null, events.slice(open + offset + 4, close - 3), context));

	// Text close, marker close, label close.
	media = push(media, [["exit", text, context], events[close - 2], events[close - 1], ["exit", label, context]]);

	// Reference, resource, or so.
	media = push(media, events.slice(close + 1));

	// Media close.
	media = push(media, [["exit", group, context]]);
	splice(events, open, events.length, media);
	return events;
}

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeLabelEnd(effects, ok, nok) {
	const self = this;
	let index = self.events.length;
	/** @type {Token} */
	let labelStart;
	/** @type {boolean} */
	let defined;

	// Find an opening.
	while (index--) {
		if ((self.events[index][1].type === "labelImage" || self.events[index][1].type === "labelLink") && !self.events[index][1]._balanced) {
			labelStart = self.events[index][1];
			break;
		}
	}
	return start;

	/**
	 * Start of label end.
	 *
	 * ```markdown
	 * > | [a](b) c
	 *       ^
	 * > | [a][b] c
	 *       ^
	 * > | [a][] b
	 *       ^
	 * > | [a] b
	 * ```
	 *
	 * @type {State}
	 */
	function start(code) {
		// If there is not an okay opening.
		if (!labelStart) {
			return nok(code);
		}

		// If the corresponding label (link) start is marked as inactive,
		// it means we’d be wrapping a link, like this:
		//
		// ```markdown
		// > | a [b [c](d) e](f) g.
		//                  ^
		// ```
		//
		// We can’t have that, so it’s just balanced brackets.
		if (labelStart._inactive) {
			return labelEndNok(code);
		}
		defined = self.parser.defined.includes(
			normalizeIdentifier(
				self.sliceSerialize({
					start: labelStart.end,
					end: self.now(),
				}),
			),
		);
		effects.enter("labelEnd");
		effects.enter("labelMarker");
		effects.consume(code);
		effects.exit("labelMarker");
		effects.exit("labelEnd");
		return after;
	}

	/**
	 * After `]`.
	 *
	 * ```markdown
	 * > | [a](b) c
	 *       ^
	 * > | [a][b] c
	 *       ^
	 * > | [a][] b
	 *       ^
	 * > | [a] b
	 *       ^
	 * ```
	 *
	 * @type {State}
	 */
	function after(code) {
		// Note: `markdown-rs` also parses GFM footnotes here, which for us is in
		// an extension.

		// Resource (`[asd](fgh)`)?
		if (code === 40) {
			return effects.attempt(resourceConstruct, labelEndOk, defined ? labelEndOk : labelEndNok)(code);
		}

		// Full (`[asd][fgh]`) or collapsed (`[asd][]`) reference?
		if (code === 91) {
			return effects.attempt(referenceFullConstruct, labelEndOk, defined ? referenceNotFull : labelEndNok)(code);
		}

		// Shortcut (`[asd]`) reference?
		return defined ? labelEndOk(code) : labelEndNok(code);
	}

	/**
	 * After `]`, at `[`, but not at a full reference.
	 *
	 * > 👉 **Note**: we only get here if the label is defined.
	 *
	 * ```markdown
	 * > | [a][] b
	 *        ^
	 * > | [a] b
	 *        ^
	 * ```
	 *
	 * @type {State}
	 */
	function referenceNotFull(code) {
		return effects.attempt(referenceCollapsedConstruct, labelEndOk, labelEndNok)(code);
	}

	/**
	 * Done, we found something.
	 *
	 * ```markdown
	 * > | [a](b) c
	 *           ^
	 * > | [a][b] c
	 *           ^
	 * > | [a][] b
	 *          ^
	 * > | [a] b
	 *        ^
	 * ```
	 *
	 * @type {State}
	 */
	function labelEndOk(code) {
		// Note: `markdown-rs` does a bunch of stuff here.
		return ok(code);
	}

	/**
	 * Done, it’s nothing.
	 *
	 * There was an okay opening, but we didn’t match anything.
	 *
	 * ```markdown
	 * > | [a](b c
	 *        ^
	 * > | [a][b c
	 *        ^
	 * > | [a] b
	 *        ^
	 * ```
	 *
	 * @type {State}
	 */
	function labelEndNok(code) {
		labelStart._balanced = true;
		return nok(code);
	}
}

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeResource(effects, ok, nok) {
	return resourceStart;

	/**
	 * At a resource.
	 *
	 * ```markdown
	 * > | [a](b) c
	 *        ^
	 * ```
	 *
	 * @type {State}
	 */
	function resourceStart(code) {
		effects.enter("resource");
		effects.enter("resourceMarker");
		effects.consume(code);
		effects.exit("resourceMarker");
		return resourceBefore;
	}

	/**
	 * In resource, after `(`, at optional whitespace.
	 *
	 * ```markdown
	 * > | [a](b) c
	 *         ^
	 * ```
	 *
	 * @type {State}
	 */
	function resourceBefore(code) {
		return markdownLineEndingOrSpace(code) ? factoryWhitespace(effects, resourceOpen)(code) : resourceOpen(code);
	}

	/**
	 * In resource, after optional whitespace, at `)` or a destination.
	 *
	 * ```markdown
	 * > | [a](b) c
	 *         ^
	 * ```
	 *
	 * @type {State}
	 */
	function resourceOpen(code) {
		if (code === 41) {
			return resourceEnd(code);
		}
		return factoryDestination(
			effects,
			resourceDestinationAfter,
			resourceDestinationMissing,
			"resourceDestination",
			"resourceDestinationLiteral",
			"resourceDestinationLiteralMarker",
			"resourceDestinationRaw",
			"resourceDestinationString",
			32,
		)(code);
	}

	/**
	 * In resource, after destination, at optional whitespace.
	 *
	 * ```markdown
	 * > | [a](b) c
	 *          ^
	 * ```
	 *
	 * @type {State}
	 */
	function resourceDestinationAfter(code) {
		return markdownLineEndingOrSpace(code) ? factoryWhitespace(effects, resourceBetween)(code) : resourceEnd(code);
	}

	/**
	 * At invalid destination.
	 *
	 * ```markdown
	 * > | [a](<<) b
	 *         ^
	 * ```
	 *
	 * @type {State}
	 */
	function resourceDestinationMissing(code) {
		return nok(code);
	}

	/**
	 * In resource, after destination and whitespace, at `(` or title.
	 *
	 * ```markdown
	 * > | [a](b ) c
	 *           ^
	 * ```
	 *
	 * @type {State}
	 */
	function resourceBetween(code) {
		if (code === 34 || code === 39 || code === 40) {
			return factoryTitle(effects, resourceTitleAfter, nok, "resourceTitle", "resourceTitleMarker", "resourceTitleString")(code);
		}
		return resourceEnd(code);
	}

	/**
	 * In resource, after title, at optional whitespace.
	 *
	 * ```markdown
	 * > | [a](b "c") d
	 *              ^
	 * ```
	 *
	 * @type {State}
	 */
	function resourceTitleAfter(code) {
		return markdownLineEndingOrSpace(code) ? factoryWhitespace(effects, resourceEnd)(code) : resourceEnd(code);
	}

	/**
	 * In resource, at `)`.
	 *
	 * ```markdown
	 * > | [a](b) d
	 *          ^
	 * ```
	 *
	 * @type {State}
	 */
	function resourceEnd(code) {
		if (code === 41) {
			effects.enter("resourceMarker");
			effects.consume(code);
			effects.exit("resourceMarker");
			effects.exit("resource");
			return ok;
		}
		return nok(code);
	}
}

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeReferenceFull(effects, ok, nok) {
	const self = this;
	return referenceFull;

	/**
	 * In a reference (full), at the `[`.
	 *
	 * ```markdown
	 * > | [a][b] d
	 *        ^
	 * ```
	 *
	 * @type {State}
	 */
	function referenceFull(code) {
		return factoryLabel.call(self, effects, referenceFullAfter, referenceFullMissing, "reference", "referenceMarker", "referenceString")(code);
	}

	/**
	 * In a reference (full), after `]`.
	 *
	 * ```markdown
	 * > | [a][b] d
	 *          ^
	 * ```
	 *
	 * @type {State}
	 */
	function referenceFullAfter(code) {
		return self.parser.defined.includes(normalizeIdentifier(self.sliceSerialize(self.events[self.events.length - 1][1]).slice(1, -1))) ? ok(code) : nok(code);
	}

	/**
	 * In reference (full) that was missing.
	 *
	 * ```markdown
	 * > | [a][b d
	 *        ^
	 * ```
	 *
	 * @type {State}
	 */
	function referenceFullMissing(code) {
		return nok(code);
	}
}

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeReferenceCollapsed(effects, ok, nok) {
	return referenceCollapsedStart;

	/**
	 * In reference (collapsed), at `[`.
	 *
	 * > 👉 **Note**: we only get here if the label is defined.
	 *
	 * ```markdown
	 * > | [a][] d
	 *        ^
	 * ```
	 *
	 * @type {State}
	 */
	function referenceCollapsedStart(code) {
		// We only attempt a collapsed label if there’s a `[`.

		effects.enter("reference");
		effects.enter("referenceMarker");
		effects.consume(code);
		effects.exit("referenceMarker");
		return referenceCollapsedOpen;
	}

	/**
	 * In reference (collapsed), at `]`.
	 *
	 * > 👉 **Note**: we only get here if the label is defined.
	 *
	 * ```markdown
	 * > | [a][] d
	 *         ^
	 * ```
	 *
	 *  @type {State}
	 */
	function referenceCollapsedOpen(code) {
		if (code === 93) {
			effects.enter("referenceMarker");
			effects.consume(code);
			effects.exit("referenceMarker");
			effects.exit("reference");
			return ok;
		}
		return nok(code);
	}
}

/**
 * @import {
 *   Construct,
 *   State,
 *   TokenizeContext,
 *   Tokenizer
 * } from 'micromark-util-types'
 */

/** @type {Construct} */
const labelStartImage = {
	name: "labelStartImage",
	resolveAll: labelEnd.resolveAll,
	tokenize: tokenizeLabelStartImage,
};

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeLabelStartImage(effects, ok, nok) {
	const self = this;
	return start;

	/**
	 * Start of label (image) start.
	 *
	 * ```markdown
	 * > | a ![b] c
	 *       ^
	 * ```
	 *
	 * @type {State}
	 */
	function start(code) {
		effects.enter("labelImage");
		effects.enter("labelImageMarker");
		effects.consume(code);
		effects.exit("labelImageMarker");
		return open;
	}

	/**
	 * After `!`, at `[`.
	 *
	 * ```markdown
	 * > | a ![b] c
	 *        ^
	 * ```
	 *
	 * @type {State}
	 */
	function open(code) {
		if (code === 91) {
			effects.enter("labelMarker");
			effects.consume(code);
			effects.exit("labelMarker");
			effects.exit("labelImage");
			return after;
		}
		return nok(code);
	}

	/**
	 * After `![`.
	 *
	 * ```markdown
	 * > | a ![b] c
	 *         ^
	 * ```
	 *
	 * This is needed in because, when GFM footnotes are enabled, images never
	 * form when started with a `^`.
	 * Instead, links form:
	 *
	 * ```markdown
	 * ![^a](b)
	 *
	 * ![^a][b]
	 *
	 * [b]: c
	 * ```
	 *
	 * ```html
	 * <p>!<a href=\"b\">^a</a></p>
	 * <p>!<a href=\"c\">^a</a></p>
	 * ```
	 *
	 * @type {State}
	 */
	function after(code) {
		// To do: use a new field to do this, this is still needed for
		// `micromark-extension-gfm-footnote`, but the `label-start-link`
		// behavior isn’t.
		// Hidden footnotes hook.
		/* c8 ignore next 3 */
		return code === 94 && "_hiddenFootnoteSupport" in self.parser.constructs ? nok(code) : ok(code);
	}
}

/**
 * @import {
 *   Construct,
 *   State,
 *   TokenizeContext,
 *   Tokenizer
 * } from 'micromark-util-types'
 */

/** @type {Construct} */
const labelStartLink = {
	name: "labelStartLink",
	resolveAll: labelEnd.resolveAll,
	tokenize: tokenizeLabelStartLink,
};

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeLabelStartLink(effects, ok, nok) {
	const self = this;
	return start;

	/**
	 * Start of label (link) start.
	 *
	 * ```markdown
	 * > | a [b] c
	 *       ^
	 * ```
	 *
	 * @type {State}
	 */
	function start(code) {
		effects.enter("labelLink");
		effects.enter("labelMarker");
		effects.consume(code);
		effects.exit("labelMarker");
		effects.exit("labelLink");
		return after;
	}

	/** @type {State} */
	function after(code) {
		// To do: this isn’t needed in `micromark-extension-gfm-footnote`,
		// remove.
		// Hidden footnotes hook.
		/* c8 ignore next 3 */
		return code === 94 && "_hiddenFootnoteSupport" in self.parser.constructs ? nok(code) : ok(code);
	}
}

/**
 * @import {
 *   Construct,
 *   State,
 *   TokenizeContext,
 *   Tokenizer
 * } from 'micromark-util-types'
 */

/** @type {Construct} */
const lineEnding = {
	name: "lineEnding",
	tokenize: tokenizeLineEnding,
};

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeLineEnding(effects, ok) {
	return start;

	/** @type {State} */
	function start(code) {
		effects.enter("lineEnding");
		effects.consume(code);
		effects.exit("lineEnding");
		return factorySpace(effects, ok, "linePrefix");
	}
}

/**
 * @import {
 *   Code,
 *   Construct,
 *   State,
 *   TokenizeContext,
 *   Tokenizer
 * } from 'micromark-util-types'
 */

/** @type {Construct} */
const thematicBreak$1 = {
	name: "thematicBreak",
	tokenize: tokenizeThematicBreak,
};

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeThematicBreak(effects, ok, nok) {
	let size = 0;
	/** @type {NonNullable<Code>} */
	let marker;
	return start;

	/**
	 * Start of thematic break.
	 *
	 * ```markdown
	 * > | ***
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function start(code) {
		effects.enter("thematicBreak");
		// To do: parse indent like `markdown-rs`.
		return before(code);
	}

	/**
	 * After optional whitespace, at marker.
	 *
	 * ```markdown
	 * > | ***
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function before(code) {
		marker = code;
		return atBreak(code);
	}

	/**
	 * After something, before something else.
	 *
	 * ```markdown
	 * > | ***
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function atBreak(code) {
		if (code === marker) {
			effects.enter("thematicBreakSequence");
			return sequence(code);
		}
		if (size >= 3 && (code === null || markdownLineEnding(code))) {
			effects.exit("thematicBreak");
			return ok(code);
		}
		return nok(code);
	}

	/**
	 * In sequence.
	 *
	 * ```markdown
	 * > | ***
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function sequence(code) {
		if (code === marker) {
			effects.consume(code);
			size++;
			return sequence;
		}
		effects.exit("thematicBreakSequence");
		return markdownSpace(code) ? factorySpace(effects, atBreak, "whitespace")(code) : atBreak(code);
	}
}

/**
 * @import {
 *   Code,
 *   Construct,
 *   Exiter,
 *   State,
 *   TokenizeContext,
 *   Tokenizer
 * } from 'micromark-util-types'
 */

/** @type {Construct} */
const list$1 = {
	continuation: {
		tokenize: tokenizeListContinuation,
	},
	exit: tokenizeListEnd,
	name: "list",
	tokenize: tokenizeListStart,
};

/** @type {Construct} */
const listItemPrefixWhitespaceConstruct = {
	partial: true,
	tokenize: tokenizeListItemPrefixWhitespace,
};

/** @type {Construct} */
const indentConstruct = {
	partial: true,
	tokenize: tokenizeIndent$1,
};

// To do: `markdown-rs` parses list items on their own and later stitches them
// together.

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeListStart(effects, ok, nok) {
	const self = this;
	const tail = self.events[self.events.length - 1];
	let initialSize = tail && tail[1].type === "linePrefix" ? tail[2].sliceSerialize(tail[1], true).length : 0;
	let size = 0;
	return start;

	/** @type {State} */
	function start(code) {
		const kind = self.containerState.type || (code === 42 || code === 43 || code === 45 ? "listUnordered" : "listOrdered");
		if (kind === "listUnordered" ? !self.containerState.marker || code === self.containerState.marker : asciiDigit(code)) {
			if (!self.containerState.type) {
				self.containerState.type = kind;
				effects.enter(kind, {
					_container: true,
				});
			}
			if (kind === "listUnordered") {
				effects.enter("listItemPrefix");
				return code === 42 || code === 45 ? effects.check(thematicBreak$1, nok, atMarker)(code) : atMarker(code);
			}
			if (!self.interrupt || code === 49) {
				effects.enter("listItemPrefix");
				effects.enter("listItemValue");
				return inside(code);
			}
		}
		return nok(code);
	}

	/** @type {State} */
	function inside(code) {
		if (asciiDigit(code) && ++size < 10) {
			effects.consume(code);
			return inside;
		}
		if ((!self.interrupt || size < 2) && (self.containerState.marker ? code === self.containerState.marker : code === 41 || code === 46)) {
			effects.exit("listItemValue");
			return atMarker(code);
		}
		return nok(code);
	}

	/**
	 * @type {State}
	 **/
	function atMarker(code) {
		effects.enter("listItemMarker");
		effects.consume(code);
		effects.exit("listItemMarker");
		self.containerState.marker = self.containerState.marker || code;
		return effects.check(
			blankLine,
			// Can’t be empty when interrupting.
			self.interrupt ? nok : onBlank,
			effects.attempt(listItemPrefixWhitespaceConstruct, endOfPrefix, otherPrefix),
		);
	}

	/** @type {State} */
	function onBlank(code) {
		self.containerState.initialBlankLine = true;
		initialSize++;
		return endOfPrefix(code);
	}

	/** @type {State} */
	function otherPrefix(code) {
		if (markdownSpace(code)) {
			effects.enter("listItemPrefixWhitespace");
			effects.consume(code);
			effects.exit("listItemPrefixWhitespace");
			return endOfPrefix;
		}
		return nok(code);
	}

	/** @type {State} */
	function endOfPrefix(code) {
		self.containerState.size = initialSize + self.sliceSerialize(effects.exit("listItemPrefix"), true).length;
		return ok(code);
	}
}

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeListContinuation(effects, ok, nok) {
	const self = this;
	self.containerState._closeFlow = undefined;
	return effects.check(blankLine, onBlank, notBlank);

	/** @type {State} */
	function onBlank(code) {
		self.containerState.furtherBlankLines = self.containerState.furtherBlankLines || self.containerState.initialBlankLine;

		// We have a blank line.
		// Still, try to consume at most the items size.
		return factorySpace(effects, ok, "listItemIndent", self.containerState.size + 1)(code);
	}

	/** @type {State} */
	function notBlank(code) {
		if (self.containerState.furtherBlankLines || !markdownSpace(code)) {
			self.containerState.furtherBlankLines = undefined;
			self.containerState.initialBlankLine = undefined;
			return notInCurrentItem(code);
		}
		self.containerState.furtherBlankLines = undefined;
		self.containerState.initialBlankLine = undefined;
		return effects.attempt(indentConstruct, ok, notInCurrentItem)(code);
	}

	/** @type {State} */
	function notInCurrentItem(code) {
		// While we do continue, we signal that the flow should be closed.
		self.containerState._closeFlow = true;
		// As we’re closing flow, we’re no longer interrupting.
		self.interrupt = undefined;
		// Always populated by defaults.

		return factorySpace(effects, effects.attempt(list$1, ok, nok), "linePrefix", self.parser.constructs.disable.null.includes("codeIndented") ? undefined : 4)(code);
	}
}

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeIndent$1(effects, ok, nok) {
	const self = this;
	return factorySpace(effects, afterPrefix, "listItemIndent", self.containerState.size + 1);

	/** @type {State} */
	function afterPrefix(code) {
		const tail = self.events[self.events.length - 1];
		return tail && tail[1].type === "listItemIndent" && tail[2].sliceSerialize(tail[1], true).length === self.containerState.size ? ok(code) : nok(code);
	}
}

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Exiter}
 */
function tokenizeListEnd(effects) {
	effects.exit(this.containerState.type);
}

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeListItemPrefixWhitespace(effects, ok, nok) {
	const self = this;

	// Always populated by defaults.

	return factorySpace(effects, afterPrefix, "listItemPrefixWhitespace", self.parser.constructs.disable.null.includes("codeIndented") ? undefined : 4 + 1);

	/** @type {State} */
	function afterPrefix(code) {
		const tail = self.events[self.events.length - 1];
		return !markdownSpace(code) && tail && tail[1].type === "listItemPrefixWhitespace" ? ok(code) : nok(code);
	}
}

/**
 * @import {
 *   Code,
 *   Construct,
 *   Resolver,
 *   State,
 *   TokenizeContext,
 *   Tokenizer
 * } from 'micromark-util-types'
 */

/** @type {Construct} */
const setextUnderline = {
	name: "setextUnderline",
	resolveTo: resolveToSetextUnderline,
	tokenize: tokenizeSetextUnderline,
};

/** @type {Resolver} */
function resolveToSetextUnderline(events, context) {
	// To do: resolve like `markdown-rs`.
	let index = events.length;
	/** @type {number | undefined} */
	let content;
	/** @type {number | undefined} */
	let text;
	/** @type {number | undefined} */
	let definition;

	// Find the opening of the content.
	// It’ll always exist: we don’t tokenize if it isn’t there.
	while (index--) {
		if (events[index][0] === "enter") {
			if (events[index][1].type === "content") {
				content = index;
				break;
			}
			if (events[index][1].type === "paragraph") {
				text = index;
			}
		}
		// Exit
		else {
			if (events[index][1].type === "content") {
				// Remove the content end (if needed we’ll add it later)
				events.splice(index, 1);
			}
			if (!definition && events[index][1].type === "definition") {
				definition = index;
			}
		}
	}
	const heading = {
		type: "setextHeading",
		start: {
			...events[content][1].start,
		},
		end: {
			...events[events.length - 1][1].end,
		},
	};

	// Change the paragraph to setext heading text.
	events[text][1].type = "setextHeadingText";

	// If we have definitions in the content, we’ll keep on having content,
	// but we need move it.
	if (definition) {
		events.splice(text, 0, ["enter", heading, context]);
		events.splice(definition + 1, 0, ["exit", events[content][1], context]);
		events[content][1].end = {
			...events[definition][1].end,
		};
	} else {
		events[content][1] = heading;
	}

	// Add the heading exit at the end.
	events.push(["exit", heading, context]);
	return events;
}

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 */
function tokenizeSetextUnderline(effects, ok, nok) {
	const self = this;
	/** @type {NonNullable<Code>} */
	let marker;
	return start;

	/**
	 * At start of heading (setext) underline.
	 *
	 * ```markdown
	 *   | aa
	 * > | ==
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function start(code) {
		let index = self.events.length;
		/** @type {boolean | undefined} */
		let paragraph;
		// Find an opening.
		while (index--) {
			// Skip enter/exit of line ending, line prefix, and content.
			// We can now either have a definition or a paragraph.
			if (self.events[index][1].type !== "lineEnding" && self.events[index][1].type !== "linePrefix" && self.events[index][1].type !== "content") {
				paragraph = self.events[index][1].type === "paragraph";
				break;
			}
		}

		// To do: handle lazy/pierce like `markdown-rs`.
		// To do: parse indent like `markdown-rs`.
		if (!self.parser.lazy[self.now().line] && (self.interrupt || paragraph)) {
			effects.enter("setextHeadingLine");
			marker = code;
			return before(code);
		}
		return nok(code);
	}

	/**
	 * After optional whitespace, at `-` or `=`.
	 *
	 * ```markdown
	 *   | aa
	 * > | ==
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function before(code) {
		effects.enter("setextHeadingLineSequence");
		return inside(code);
	}

	/**
	 * In sequence.
	 *
	 * ```markdown
	 *   | aa
	 * > | ==
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function inside(code) {
		if (code === marker) {
			effects.consume(code);
			return inside;
		}
		effects.exit("setextHeadingLineSequence");
		return markdownSpace(code) ? factorySpace(effects, after, "lineSuffix")(code) : after(code);
	}

	/**
	 * After sequence, after optional whitespace.
	 *
	 * ```markdown
	 *   | aa
	 * > | ==
	 *       ^
	 * ```
	 *
	 * @type {State}
	 */
	function after(code) {
		if (code === null || markdownLineEnding(code)) {
			effects.exit("setextHeadingLine");
			return ok(code);
		}
		return nok(code);
	}
}

/**
 * @import {Event, Exiter, Extension, Resolver, State, Token, TokenizeContext, Tokenizer} from 'micromark-util-types'
 */

const indent = {
	tokenize: tokenizeIndent,
	partial: true,
};

// To do: micromark should support a `_hiddenGfmFootnoteSupport`, which only
// affects label start (image).
// That will let us drop `tokenizePotentialGfmFootnote*`.
// It currently has a `_hiddenFootnoteSupport`, which affects that and more.
// That can be removed when `micromark-extension-footnote` is archived.

/**
 * Create an extension for `micromark` to enable GFM footnote syntax.
 *
 * @returns {Extension}
 *   Extension for `micromark` that can be passed in `extensions` to
 *   enable GFM footnote syntax.
 */
function gfmFootnote() {
	/** @type {Extension} */
	return {
		document: {
			[91]: {
				name: "gfmFootnoteDefinition",
				tokenize: tokenizeDefinitionStart,
				continuation: {
					tokenize: tokenizeDefinitionContinuation,
				},
				exit: gfmFootnoteDefinitionEnd,
			},
		},
		text: {
			[91]: {
				name: "gfmFootnoteCall",
				tokenize: tokenizeGfmFootnoteCall,
			},
			[93]: {
				name: "gfmPotentialFootnoteCall",
				add: "after",
				tokenize: tokenizePotentialGfmFootnoteCall,
				resolveTo: resolveToPotentialGfmFootnoteCall,
			},
		},
	};
}

// To do: remove after micromark update.
/**
 * @this {TokenizeContext}
 * @type {Tokenizer}
 */
function tokenizePotentialGfmFootnoteCall(effects, ok, nok) {
	const self = this;
	let index = self.events.length;
	const defined = self.parser.gfmFootnotes || (self.parser.gfmFootnotes = []);
	/** @type {Token} */
	let labelStart;

	// Find an opening.
	while (index--) {
		const token = self.events[index][1];
		if (token.type === "labelImage") {
			labelStart = token;
			break;
		}

		// Exit if we’ve walked far enough.
		if (token.type === "gfmFootnoteCall" || token.type === "labelLink" || token.type === "label" || token.type === "image" || token.type === "link") {
			break;
		}
	}
	return start;

	/**
	 * @type {State}
	 */
	function start(code) {
		if (!labelStart || !labelStart._balanced) {
			return nok(code);
		}
		const id = normalizeIdentifier(
			self.sliceSerialize({
				start: labelStart.end,
				end: self.now(),
			}),
		);
		if (id.codePointAt(0) !== 94 || !defined.includes(id.slice(1))) {
			return nok(code);
		}
		effects.enter("gfmFootnoteCallLabelMarker");
		effects.consume(code);
		effects.exit("gfmFootnoteCallLabelMarker");
		return ok(code);
	}
}

// To do: remove after micromark update.
/** @type {Resolver} */
function resolveToPotentialGfmFootnoteCall(events, context) {
	let index = events.length;

	// Find an opening.
	while (index--) {
		if (events[index][1].type === "labelImage" && events[index][0] === "enter") {
			events[index][1];
			break;
		}
	}
	// Change the `labelImageMarker` to a `data`.
	events[index + 1][1].type = "data";
	events[index + 3][1].type = "gfmFootnoteCallLabelMarker";

	// The whole (without `!`):
	/** @type {Token} */
	const call = {
		type: "gfmFootnoteCall",
		start: Object.assign({}, events[index + 3][1].start),
		end: Object.assign({}, events[events.length - 1][1].end),
	};
	// The `^` marker
	/** @type {Token} */
	const marker = {
		type: "gfmFootnoteCallMarker",
		start: Object.assign({}, events[index + 3][1].end),
		end: Object.assign({}, events[index + 3][1].end),
	};
	// Increment the end 1 character.
	marker.end.column++;
	marker.end.offset++;
	marker.end._bufferIndex++;
	/** @type {Token} */
	const string = {
		type: "gfmFootnoteCallString",
		start: Object.assign({}, marker.end),
		end: Object.assign({}, events[events.length - 1][1].start),
	};
	/** @type {Token} */
	const chunk = {
		type: "chunkString",
		contentType: "string",
		start: Object.assign({}, string.start),
		end: Object.assign({}, string.end),
	};

	/** @type {Array<Event>} */
	const replacement = [
		// Take the `labelImageMarker` (now `data`, the `!`)
		events[index + 1],
		events[index + 2],
		["enter", call, context],
		// The `[`
		events[index + 3],
		events[index + 4],
		// The `^`.
		["enter", marker, context],
		["exit", marker, context],
		// Everything in between.
		["enter", string, context],
		["enter", chunk, context],
		["exit", chunk, context],
		["exit", string, context],
		// The ending (`]`, properly parsed and labelled).
		events[events.length - 2],
		events[events.length - 1],
		["exit", call, context],
	];
	events.splice(index, events.length - index + 1, ...replacement);
	return events;
}

/**
 * @this {TokenizeContext}
 * @type {Tokenizer}
 */
function tokenizeGfmFootnoteCall(effects, ok, nok) {
	const self = this;
	const defined = self.parser.gfmFootnotes || (self.parser.gfmFootnotes = []);
	let size = 0;
	/** @type {boolean} */
	let data;

	// Note: the implementation of `markdown-rs` is different, because it houses
	// core *and* extensions in one project.
	// Therefore, it can include footnote logic inside `label-end`.
	// We can’t do that, but luckily, we can parse footnotes in a simpler way than
	// needed for labels.
	return start;

	/**
	 * Start of footnote label.
	 *
	 * ```markdown
	 * > | a [^b] c
	 *       ^
	 * ```
	 *
	 * @type {State}
	 */
	function start(code) {
		effects.enter("gfmFootnoteCall");
		effects.enter("gfmFootnoteCallLabelMarker");
		effects.consume(code);
		effects.exit("gfmFootnoteCallLabelMarker");
		return callStart;
	}

	/**
	 * After `[`, at `^`.
	 *
	 * ```markdown
	 * > | a [^b] c
	 *        ^
	 * ```
	 *
	 * @type {State}
	 */
	function callStart(code) {
		if (code !== 94) return nok(code);
		effects.enter("gfmFootnoteCallMarker");
		effects.consume(code);
		effects.exit("gfmFootnoteCallMarker");
		effects.enter("gfmFootnoteCallString");
		effects.enter("chunkString").contentType = "string";
		return callData;
	}

	/**
	 * In label.
	 *
	 * ```markdown
	 * > | a [^b] c
	 *         ^
	 * ```
	 *
	 * @type {State}
	 */
	function callData(code) {
		if (
			// Too long.
			size > 999 ||
			// Closing brace with nothing.
			(code === 93 && !data) ||
			// Space or tab is not supported by GFM for some reason.
			// `\n` and `[` not being supported makes sense.
			code === null ||
			code === 91 ||
			markdownLineEndingOrSpace(code)
		) {
			return nok(code);
		}
		if (code === 93) {
			effects.exit("chunkString");
			const token = effects.exit("gfmFootnoteCallString");
			if (!defined.includes(normalizeIdentifier(self.sliceSerialize(token)))) {
				return nok(code);
			}
			effects.enter("gfmFootnoteCallLabelMarker");
			effects.consume(code);
			effects.exit("gfmFootnoteCallLabelMarker");
			effects.exit("gfmFootnoteCall");
			return ok;
		}
		if (!markdownLineEndingOrSpace(code)) {
			data = true;
		}
		size++;
		effects.consume(code);
		return code === 92 ? callEscape : callData;
	}

	/**
	 * On character after escape.
	 *
	 * ```markdown
	 * > | a [^b\c] d
	 *           ^
	 * ```
	 *
	 * @type {State}
	 */
	function callEscape(code) {
		if (code === 91 || code === 92 || code === 93) {
			effects.consume(code);
			size++;
			return callData;
		}
		return callData(code);
	}
}

/**
 * @this {TokenizeContext}
 * @type {Tokenizer}
 */
function tokenizeDefinitionStart(effects, ok, nok) {
	const self = this;
	const defined = self.parser.gfmFootnotes || (self.parser.gfmFootnotes = []);
	/** @type {string} */
	let identifier;
	let size = 0;
	/** @type {boolean | undefined} */
	let data;
	return start;

	/**
	 * Start of GFM footnote definition.
	 *
	 * ```markdown
	 * > | [^a]: b
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function start(code) {
		effects.enter("gfmFootnoteDefinition")._container = true;
		effects.enter("gfmFootnoteDefinitionLabel");
		effects.enter("gfmFootnoteDefinitionLabelMarker");
		effects.consume(code);
		effects.exit("gfmFootnoteDefinitionLabelMarker");
		return labelAtMarker;
	}

	/**
	 * In label, at caret.
	 *
	 * ```markdown
	 * > | [^a]: b
	 *      ^
	 * ```
	 *
	 * @type {State}
	 */
	function labelAtMarker(code) {
		if (code === 94) {
			effects.enter("gfmFootnoteDefinitionMarker");
			effects.consume(code);
			effects.exit("gfmFootnoteDefinitionMarker");
			effects.enter("gfmFootnoteDefinitionLabelString");
			effects.enter("chunkString").contentType = "string";
			return labelInside;
		}
		return nok(code);
	}

	/**
	 * In label.
	 *
	 * > 👉 **Note**: `cmark-gfm` prevents whitespace from occurring in footnote
	 * > definition labels.
	 *
	 * ```markdown
	 * > | [^a]: b
	 *       ^
	 * ```
	 *
	 * @type {State}
	 */
	function labelInside(code) {
		if (
			// Too long.
			size > 999 ||
			// Closing brace with nothing.
			(code === 93 && !data) ||
			// Space or tab is not supported by GFM for some reason.
			// `\n` and `[` not being supported makes sense.
			code === null ||
			code === 91 ||
			markdownLineEndingOrSpace(code)
		) {
			return nok(code);
		}
		if (code === 93) {
			effects.exit("chunkString");
			const token = effects.exit("gfmFootnoteDefinitionLabelString");
			identifier = normalizeIdentifier(self.sliceSerialize(token));
			effects.enter("gfmFootnoteDefinitionLabelMarker");
			effects.consume(code);
			effects.exit("gfmFootnoteDefinitionLabelMarker");
			effects.exit("gfmFootnoteDefinitionLabel");
			return labelAfter;
		}
		if (!markdownLineEndingOrSpace(code)) {
			data = true;
		}
		size++;
		effects.consume(code);
		return code === 92 ? labelEscape : labelInside;
	}

	/**
	 * After `\`, at a special character.
	 *
	 * > 👉 **Note**: `cmark-gfm` currently does not support escaped brackets:
	 * > <https://github.com/github/cmark-gfm/issues/240>
	 *
	 * ```markdown
	 * > | [^a\*b]: c
	 *         ^
	 * ```
	 *
	 * @type {State}
	 */
	function labelEscape(code) {
		if (code === 91 || code === 92 || code === 93) {
			effects.consume(code);
			size++;
			return labelInside;
		}
		return labelInside(code);
	}

	/**
	 * After definition label.
	 *
	 * ```markdown
	 * > | [^a]: b
	 *         ^
	 * ```
	 *
	 * @type {State}
	 */
	function labelAfter(code) {
		if (code === 58) {
			effects.enter("definitionMarker");
			effects.consume(code);
			effects.exit("definitionMarker");
			if (!defined.includes(identifier)) {
				defined.push(identifier);
			}

			// Any whitespace after the marker is eaten, forming indented code
			// is not possible.
			// No space is also fine, just like a block quote marker.
			return factorySpace(effects, whitespaceAfter, "gfmFootnoteDefinitionWhitespace");
		}
		return nok(code);
	}

	/**
	 * After definition prefix.
	 *
	 * ```markdown
	 * > | [^a]: b
	 *           ^
	 * ```
	 *
	 * @type {State}
	 */
	function whitespaceAfter(code) {
		// `markdown-rs` has a wrapping token for the prefix that is closed here.
		return ok(code);
	}
}

/**
 * @this {TokenizeContext}
 * @type {Tokenizer}
 */
function tokenizeDefinitionContinuation(effects, ok, nok) {
	/// Start of footnote definition continuation.
	///
	/// ```markdown
	///   | [^a]: b
	/// > |     c
	///     ^
	/// ```
	//
	// Either a blank line, which is okay, or an indented thing.
	return effects.check(blankLine, ok, effects.attempt(indent, ok, nok));
}

/** @type {Exiter} */
function gfmFootnoteDefinitionEnd(effects) {
	effects.exit("gfmFootnoteDefinition");
}

/**
 * @this {TokenizeContext}
 * @type {Tokenizer}
 */
function tokenizeIndent(effects, ok, nok) {
	const self = this;
	return factorySpace(effects, afterPrefix, "gfmFootnoteDefinitionIndent", 4 + 1);

	/**
	 * @type {State}
	 */
	function afterPrefix(code) {
		const tail = self.events[self.events.length - 1];
		return tail && tail[1].type === "gfmFootnoteDefinitionIndent" && tail[2].sliceSerialize(tail[1], true).length === 4 ? ok(code) : nok(code);
	}
}

/**
 * @import {Options} from 'micromark-extension-gfm-strikethrough'
 * @import {Event, Extension, Resolver, State, Token, TokenizeContext, Tokenizer} from 'micromark-util-types'
 */

/**
 * Create an extension for `micromark` to enable GFM strikethrough syntax.
 *
 * @param {Options | null | undefined} [options={}]
 *   Configuration.
 * @returns {Extension}
 *   Extension for `micromark` that can be passed in `extensions`, to
 *   enable GFM strikethrough syntax.
 */
function gfmStrikethrough(options) {
	const options_ = options || {};
	let single = options_.singleTilde;
	const tokenizer = {
		name: "strikethrough",
		tokenize: tokenizeStrikethrough,
		resolveAll: resolveAllStrikethrough,
	};
	if (single === null || single === undefined) {
		single = true;
	}
	return {
		text: {
			[126]: tokenizer,
		},
		insideSpan: {
			null: [tokenizer],
		},
		attentionMarkers: {
			null: [126],
		},
	};

	/**
	 * Take events and resolve strikethrough.
	 *
	 * @type {Resolver}
	 */
	function resolveAllStrikethrough(events, context) {
		let index = -1;

		// Walk through all events.
		while (++index < events.length) {
			// Find a token that can close.
			if (events[index][0] === "enter" && events[index][1].type === "strikethroughSequenceTemporary" && events[index][1]._close) {
				let open = index;

				// Now walk back to find an opener.
				while (open--) {
					// Find a token that can open the closer.
					if (
						events[open][0] === "exit" &&
						events[open][1].type === "strikethroughSequenceTemporary" &&
						events[open][1]._open &&
						// If the sizes are the same:
						events[index][1].end.offset - events[index][1].start.offset === events[open][1].end.offset - events[open][1].start.offset
					) {
						events[index][1].type = "strikethroughSequence";
						events[open][1].type = "strikethroughSequence";

						/** @type {Token} */
						const strikethrough = {
							type: "strikethrough",
							start: Object.assign({}, events[open][1].start),
							end: Object.assign({}, events[index][1].end),
						};

						/** @type {Token} */
						const text = {
							type: "strikethroughText",
							start: Object.assign({}, events[open][1].end),
							end: Object.assign({}, events[index][1].start),
						};

						// Opening.
						/** @type {Array<Event>} */
						const nextEvents = [
							["enter", strikethrough, context],
							["enter", events[open][1], context],
							["exit", events[open][1], context],
							["enter", text, context],
						];
						const insideSpan = context.parser.constructs.insideSpan.null;
						if (insideSpan) {
							// Between.
							splice(nextEvents, nextEvents.length, 0, resolveAll(insideSpan, events.slice(open + 1, index), context));
						}

						// Closing.
						splice(nextEvents, nextEvents.length, 0, [
							["exit", text, context],
							["enter", events[index][1], context],
							["exit", events[index][1], context],
							["exit", strikethrough, context],
						]);
						splice(events, open - 1, index - open + 3, nextEvents);
						index = open + nextEvents.length - 2;
						break;
					}
				}
			}
		}
		index = -1;
		while (++index < events.length) {
			if (events[index][1].type === "strikethroughSequenceTemporary") {
				events[index][1].type = "data";
			}
		}
		return events;
	}

	/**
	 * @this {TokenizeContext}
	 * @type {Tokenizer}
	 */
	function tokenizeStrikethrough(effects, ok, nok) {
		const previous = this.previous;
		const events = this.events;
		let size = 0;
		return start;

		/** @type {State} */
		function start(code) {
			if (previous === 126 && events[events.length - 1][1].type !== "characterEscape") {
				return nok(code);
			}
			effects.enter("strikethroughSequenceTemporary");
			return more(code);
		}

		/** @type {State} */
		function more(code) {
			const before = classifyCharacter(previous);
			if (code === 126) {
				// If this is the third marker, exit.
				if (size > 1) return nok(code);
				effects.consume(code);
				size++;
				return more;
			}
			if (size < 2 && !single) return nok(code);
			const token = effects.exit("strikethroughSequenceTemporary");
			const after = classifyCharacter(code);
			token._open = !after || (after === 2 && Boolean(before));
			token._close = !before || (before === 2 && Boolean(after));
			return ok(code);
		}
	}
}

/**
 * @import {Event} from 'micromark-util-types'
 */

// Port of `edit_map.rs` from `markdown-rs`.
// This should move to `markdown-js` later.

// Deal with several changes in events, batching them together.
//
// Preferably, changes should be kept to a minimum.
// Sometimes, it’s needed to change the list of events, because parsing can be
// messy, and it helps to expose a cleaner interface of events to the compiler
// and other users.
// It can also help to merge many adjacent similar events.
// And, in other cases, it’s needed to parse subcontent: pass some events
// through another tokenizer and inject the result.

/**
 * @typedef {[number, number, Array<Event>]} Change
 * @typedef {[number, number, number]} Jump
 */

/**
 * Tracks a bunch of edits.
 */
class EditMap {
	/**
	 * Create a new edit map.
	 */
	constructor() {
		/**
		 * Record of changes.
		 *
		 * @type {Array<Change>}
		 */
		this.map = [];
	}

	/**
	 * Create an edit: a remove and/or add at a certain place.
	 *
	 * @param {number} index
	 * @param {number} remove
	 * @param {Array<Event>} add
	 * @returns {undefined}
	 */
	add(index, remove, add) {
		addImplementation(this, index, remove, add);
	}

	// To do: add this when moving to `micromark`.
	// /**
	//  * Create an edit: but insert `add` before existing additions.
	//  *
	//  * @param {number} index
	//  * @param {number} remove
	//  * @param {Array<Event>} add
	//  * @returns {undefined}
	//  */
	// addBefore(index, remove, add) {
	//   addImplementation(this, index, remove, add, true)
	// }

	/**
	 * Done, change the events.
	 *
	 * @param {Array<Event>} events
	 * @returns {undefined}
	 */
	consume(events) {
		this.map.sort(function (a, b) {
			return a[0] - b[0];
		});

		/* c8 ignore next 3 -- `resolve` is never called without tables, so without edits. */
		if (this.map.length === 0) {
			return;
		}

		// To do: if links are added in events, like they are in `markdown-rs`,
		// this is needed.
		// // Calculate jumps: where items in the current list move to.
		// /** @type {Array<Jump>} */
		// const jumps = []
		// let index = 0
		// let addAcc = 0
		// let removeAcc = 0
		// while (index < this.map.length) {
		//   const [at, remove, add] = this.map[index]
		//   removeAcc += remove
		//   addAcc += add.length
		//   jumps.push([at, removeAcc, addAcc])
		//   index += 1
		// }
		//
		// . shiftLinks(events, jumps)

		let index = this.map.length;
		/** @type {Array<Array<Event>>} */
		const vecs = [];
		while (index > 0) {
			index -= 1;
			vecs.push(events.slice(this.map[index][0] + this.map[index][1]), this.map[index][2]);

			// Truncate rest.
			events.length = this.map[index][0];
		}
		vecs.push(events.slice());
		events.length = 0;
		let slice = vecs.pop();
		while (slice) {
			for (const element of slice) {
				events.push(element);
			}
			slice = vecs.pop();
		}

		// Truncate everything.
		this.map.length = 0;
	}
}

/**
 * Create an edit.
 *
 * @param {EditMap} editMap
 * @param {number} at
 * @param {number} remove
 * @param {Array<Event>} add
 * @returns {undefined}
 */
function addImplementation(editMap, at, remove, add) {
	let index = 0;

	/* c8 ignore next 3 -- `resolve` is never called without tables, so without edits. */
	if (remove === 0 && add.length === 0) {
		return;
	}
	while (index < editMap.map.length) {
		if (editMap.map[index][0] === at) {
			editMap.map[index][1] += remove;

			// To do: before not used by tables, use when moving to micromark.
			// if (before) {
			//   add.push(...editMap.map[index][2])
			//   editMap.map[index][2] = add
			// } else {
			editMap.map[index][2].push(...add);
			// }

			return;
		}
		index += 1;
	}
	editMap.map.push([at, remove, add]);
}

// /**
//  * Shift `previous` and `next` links according to `jumps`.
//  *
//  * This fixes links in case there are events removed or added between them.
//  *
//  * @param {Array<Event>} events
//  * @param {Array<Jump>} jumps
//  */
// function shiftLinks(events, jumps) {
//   let jumpIndex = 0
//   let index = 0
//   let add = 0
//   let rm = 0

//   while (index < events.length) {
//     const rmCurr = rm

//     while (jumpIndex < jumps.length && jumps[jumpIndex][0] <= index) {
//       add = jumps[jumpIndex][2]
//       rm = jumps[jumpIndex][1]
//       jumpIndex += 1
//     }

//     // Ignore items that will be removed.
//     if (rm > rmCurr) {
//       index += rm - rmCurr
//     } else {
//       // ?
//       // if let Some(link) = &events[index].link {
//       //     if let Some(next) = link.next {
//       //         events[next].link.as_mut().unwrap().previous = Some(index + add - rm);
//       //         while jumpIndex < jumps.len() && jumps[jumpIndex].0 <= next {
//       //             add = jumps[jumpIndex].2;
//       //             rm = jumps[jumpIndex].1;
//       //             jumpIndex += 1;
//       //         }
//       //         events[index].link.as_mut().unwrap().next = Some(next + add - rm);
//       //         index = next;
//       //         continue;
//       //     }
//       // }
//       index += 1
//     }
//   }
// }

/**
 * @import {Event} from 'micromark-util-types'
 */

/**
 * @typedef {'center' | 'left' | 'none' | 'right'} Align
 */

/**
 * Figure out the alignment of a GFM table.
 *
 * @param {Readonly<Array<Event>>} events
 *   List of events.
 * @param {number} index
 *   Table enter event.
 * @returns {Array<Align>}
 *   List of aligns.
 */
function gfmTableAlign(events, index) {
	let inDelimiterRow = false;
	/** @type {Array<Align>} */
	const align = [];
	while (index < events.length) {
		const event = events[index];
		if (inDelimiterRow) {
			if (event[0] === "enter") {
				// Start of alignment value: set a new column.
				// To do: `markdown-rs` uses `tableDelimiterCellValue`.
				if (event[1].type === "tableContent") {
					align.push(events[index + 1][1].type === "tableDelimiterMarker" ? "left" : "none");
				}
			}
			// Exits:
			// End of alignment value: change the column.
			// To do: `markdown-rs` uses `tableDelimiterCellValue`.
			else if (event[1].type === "tableContent") {
				if (events[index - 1][1].type === "tableDelimiterMarker") {
					const alignIndex = align.length - 1;
					align[alignIndex] = align[alignIndex] === "left" ? "center" : "right";
				}
			}
			// Done!
			else if (event[1].type === "tableDelimiterRow") {
				break;
			}
		} else if (event[0] === "enter" && event[1].type === "tableDelimiterRow") {
			inDelimiterRow = true;
		}
		index += 1;
	}
	return align;
}

/**
 * @import {Event, Extension, Point, Resolver, State, Token, TokenizeContext, Tokenizer} from 'micromark-util-types'
 */

/**
 * Create an HTML extension for `micromark` to support GitHub tables syntax.
 *
 * @returns {Extension}
 *   Extension for `micromark` that can be passed in `extensions` to enable GFM
 *   table syntax.
 */
function gfmTable() {
	return {
		flow: {
			null: {
				name: "table",
				tokenize: tokenizeTable,
				resolveAll: resolveTable,
			},
		},
	};
}

/**
 * @this {TokenizeContext}
 * @type {Tokenizer}
 */
function tokenizeTable(effects, ok, nok) {
	const self = this;
	let size = 0;
	let sizeB = 0;
	/** @type {boolean | undefined} */
	let seen;
	return start;

	/**
	 * Start of a GFM table.
	 *
	 * If there is a valid table row or table head before, then we try to parse
	 * another row.
	 * Otherwise, we try to parse a head.
	 *
	 * ```markdown
	 * > | | a |
	 *     ^
	 *   | | - |
	 * > | | b |
	 *     ^
	 * ```
	 * @type {State}
	 */
	function start(code) {
		let index = self.events.length - 1;
		while (index > -1) {
			const type = self.events[index][1].type;
			if (
				type === "lineEnding" ||
				// Note: markdown-rs uses `whitespace` instead of `linePrefix`
				type === "linePrefix"
			)
				index--;
			else break;
		}
		const tail = index > -1 ? self.events[index][1].type : null;
		const next = tail === "tableHead" || tail === "tableRow" ? bodyRowStart : headRowBefore;

		// Don’t allow lazy body rows.
		if (next === bodyRowStart && self.parser.lazy[self.now().line]) {
			return nok(code);
		}
		return next(code);
	}

	/**
	 * Before table head row.
	 *
	 * ```markdown
	 * > | | a |
	 *     ^
	 *   | | - |
	 *   | | b |
	 * ```
	 *
	 * @type {State}
	 */
	function headRowBefore(code) {
		effects.enter("tableHead");
		effects.enter("tableRow");
		return headRowStart(code);
	}

	/**
	 * Before table head row, after whitespace.
	 *
	 * ```markdown
	 * > | | a |
	 *     ^
	 *   | | - |
	 *   | | b |
	 * ```
	 *
	 * @type {State}
	 */
	function headRowStart(code) {
		if (code === 124) {
			return headRowBreak(code);
		}

		// To do: micromark-js should let us parse our own whitespace in extensions,
		// like `markdown-rs`:
		//
		// ```js
		// // 4+ spaces.
		// if (markdownSpace(code)) {
		//   return nok(code)
		// }
		// ```

		seen = true;
		// Count the first character, that isn’t a pipe, double.
		sizeB += 1;
		return headRowBreak(code);
	}

	/**
	 * At break in table head row.
	 *
	 * ```markdown
	 * > | | a |
	 *     ^
	 *       ^
	 *         ^
	 *   | | - |
	 *   | | b |
	 * ```
	 *
	 * @type {State}
	 */
	function headRowBreak(code) {
		if (code === null) {
			// Note: in `markdown-rs`, we need to reset, in `micromark-js` we don‘t.
			return nok(code);
		}
		if (markdownLineEnding(code)) {
			// If anything other than one pipe (ignoring whitespace) was used, it’s fine.
			if (sizeB > 1) {
				sizeB = 0;
				// To do: check if this works.
				// Feel free to interrupt:
				self.interrupt = true;
				effects.exit("tableRow");
				effects.enter("lineEnding");
				effects.consume(code);
				effects.exit("lineEnding");
				return headDelimiterStart;
			}

			// Note: in `markdown-rs`, we need to reset, in `micromark-js` we don‘t.
			return nok(code);
		}
		if (markdownSpace(code)) {
			// To do: check if this is fine.
			// effects.attempt(State::Next(StateName::GfmTableHeadRowBreak), State::Nok)
			// State::Retry(space_or_tab(tokenizer))
			return factorySpace(effects, headRowBreak, "whitespace")(code);
		}
		sizeB += 1;
		if (seen) {
			seen = false;
			// Header cell count.
			size += 1;
		}
		if (code === 124) {
			effects.enter("tableCellDivider");
			effects.consume(code);
			effects.exit("tableCellDivider");
			// Whether a delimiter was seen.
			seen = true;
			return headRowBreak;
		}

		// Anything else is cell data.
		effects.enter("data");
		return headRowData(code);
	}

	/**
	 * In table head row data.
	 *
	 * ```markdown
	 * > | | a |
	 *       ^
	 *   | | - |
	 *   | | b |
	 * ```
	 *
	 * @type {State}
	 */
	function headRowData(code) {
		if (code === null || code === 124 || markdownLineEndingOrSpace(code)) {
			effects.exit("data");
			return headRowBreak(code);
		}
		effects.consume(code);
		return code === 92 ? headRowEscape : headRowData;
	}

	/**
	 * In table head row escape.
	 *
	 * ```markdown
	 * > | | a\-b |
	 *         ^
	 *   | | ---- |
	 *   | | c    |
	 * ```
	 *
	 * @type {State}
	 */
	function headRowEscape(code) {
		if (code === 92 || code === 124) {
			effects.consume(code);
			return headRowData;
		}
		return headRowData(code);
	}

	/**
	 * Before delimiter row.
	 *
	 * ```markdown
	 *   | | a |
	 * > | | - |
	 *     ^
	 *   | | b |
	 * ```
	 *
	 * @type {State}
	 */
	function headDelimiterStart(code) {
		// Reset `interrupt`.
		self.interrupt = false;

		// Note: in `markdown-rs`, we need to handle piercing here too.
		if (self.parser.lazy[self.now().line]) {
			return nok(code);
		}
		effects.enter("tableDelimiterRow");
		// Track if we’ve seen a `:` or `|`.
		seen = false;
		if (markdownSpace(code)) {
			return factorySpace(effects, headDelimiterBefore, "linePrefix", self.parser.constructs.disable.null.includes("codeIndented") ? undefined : 4)(code);
		}
		return headDelimiterBefore(code);
	}

	/**
	 * Before delimiter row, after optional whitespace.
	 *
	 * Reused when a `|` is found later, to parse another cell.
	 *
	 * ```markdown
	 *   | | a |
	 * > | | - |
	 *     ^
	 *   | | b |
	 * ```
	 *
	 * @type {State}
	 */
	function headDelimiterBefore(code) {
		if (code === 45 || code === 58) {
			return headDelimiterValueBefore(code);
		}
		if (code === 124) {
			seen = true;
			// If we start with a pipe, we open a cell marker.
			effects.enter("tableCellDivider");
			effects.consume(code);
			effects.exit("tableCellDivider");
			return headDelimiterCellBefore;
		}

		// More whitespace / empty row not allowed at start.
		return headDelimiterNok(code);
	}

	/**
	 * After `|`, before delimiter cell.
	 *
	 * ```markdown
	 *   | | a |
	 * > | | - |
	 *      ^
	 * ```
	 *
	 * @type {State}
	 */
	function headDelimiterCellBefore(code) {
		if (markdownSpace(code)) {
			return factorySpace(effects, headDelimiterValueBefore, "whitespace")(code);
		}
		return headDelimiterValueBefore(code);
	}

	/**
	 * Before delimiter cell value.
	 *
	 * ```markdown
	 *   | | a |
	 * > | | - |
	 *       ^
	 * ```
	 *
	 * @type {State}
	 */
	function headDelimiterValueBefore(code) {
		// Align: left.
		if (code === 58) {
			sizeB += 1;
			seen = true;
			effects.enter("tableDelimiterMarker");
			effects.consume(code);
			effects.exit("tableDelimiterMarker");
			return headDelimiterLeftAlignmentAfter;
		}

		// Align: none.
		if (code === 45) {
			sizeB += 1;
			// To do: seems weird that this *isn’t* left aligned, but that state is used?
			return headDelimiterLeftAlignmentAfter(code);
		}
		if (code === null || markdownLineEnding(code)) {
			return headDelimiterCellAfter(code);
		}
		return headDelimiterNok(code);
	}

	/**
	 * After delimiter cell left alignment marker.
	 *
	 * ```markdown
	 *   | | a  |
	 * > | | :- |
	 *        ^
	 * ```
	 *
	 * @type {State}
	 */
	function headDelimiterLeftAlignmentAfter(code) {
		if (code === 45) {
			effects.enter("tableDelimiterFiller");
			return headDelimiterFiller(code);
		}

		// Anything else is not ok after the left-align colon.
		return headDelimiterNok(code);
	}

	/**
	 * In delimiter cell filler.
	 *
	 * ```markdown
	 *   | | a |
	 * > | | - |
	 *       ^
	 * ```
	 *
	 * @type {State}
	 */
	function headDelimiterFiller(code) {
		if (code === 45) {
			effects.consume(code);
			return headDelimiterFiller;
		}

		// Align is `center` if it was `left`, `right` otherwise.
		if (code === 58) {
			seen = true;
			effects.exit("tableDelimiterFiller");
			effects.enter("tableDelimiterMarker");
			effects.consume(code);
			effects.exit("tableDelimiterMarker");
			return headDelimiterRightAlignmentAfter;
		}
		effects.exit("tableDelimiterFiller");
		return headDelimiterRightAlignmentAfter(code);
	}

	/**
	 * After delimiter cell right alignment marker.
	 *
	 * ```markdown
	 *   | |  a |
	 * > | | -: |
	 *         ^
	 * ```
	 *
	 * @type {State}
	 */
	function headDelimiterRightAlignmentAfter(code) {
		if (markdownSpace(code)) {
			return factorySpace(effects, headDelimiterCellAfter, "whitespace")(code);
		}
		return headDelimiterCellAfter(code);
	}

	/**
	 * After delimiter cell.
	 *
	 * ```markdown
	 *   | |  a |
	 * > | | -: |
	 *          ^
	 * ```
	 *
	 * @type {State}
	 */
	function headDelimiterCellAfter(code) {
		if (code === 124) {
			return headDelimiterBefore(code);
		}
		if (code === null || markdownLineEnding(code)) {
			// Exit when:
			// * there was no `:` or `|` at all (it’s a thematic break or setext
			//   underline instead)
			// * the header cell count is not the delimiter cell count
			if (!seen || size !== sizeB) {
				return headDelimiterNok(code);
			}

			// Note: in markdown-rs`, a reset is needed here.
			effects.exit("tableDelimiterRow");
			effects.exit("tableHead");
			// To do: in `markdown-rs`, resolvers need to be registered manually.
			// effects.register_resolver(ResolveName::GfmTable)
			return ok(code);
		}
		return headDelimiterNok(code);
	}

	/**
	 * In delimiter row, at a disallowed byte.
	 *
	 * ```markdown
	 *   | | a |
	 * > | | x |
	 *       ^
	 * ```
	 *
	 * @type {State}
	 */
	function headDelimiterNok(code) {
		// Note: in `markdown-rs`, we need to reset, in `micromark-js` we don‘t.
		return nok(code);
	}

	/**
	 * Before table body row.
	 *
	 * ```markdown
	 *   | | a |
	 *   | | - |
	 * > | | b |
	 *     ^
	 * ```
	 *
	 * @type {State}
	 */
	function bodyRowStart(code) {
		// Note: in `markdown-rs` we need to manually take care of a prefix,
		// but in `micromark-js` that is done for us, so if we’re here, we’re
		// never at whitespace.
		effects.enter("tableRow");
		return bodyRowBreak(code);
	}

	/**
	 * At break in table body row.
	 *
	 * ```markdown
	 *   | | a |
	 *   | | - |
	 * > | | b |
	 *     ^
	 *       ^
	 *         ^
	 * ```
	 *
	 * @type {State}
	 */
	function bodyRowBreak(code) {
		if (code === 124) {
			effects.enter("tableCellDivider");
			effects.consume(code);
			effects.exit("tableCellDivider");
			return bodyRowBreak;
		}
		if (code === null || markdownLineEnding(code)) {
			effects.exit("tableRow");
			return ok(code);
		}
		if (markdownSpace(code)) {
			return factorySpace(effects, bodyRowBreak, "whitespace")(code);
		}

		// Anything else is cell content.
		effects.enter("data");
		return bodyRowData(code);
	}

	/**
	 * In table body row data.
	 *
	 * ```markdown
	 *   | | a |
	 *   | | - |
	 * > | | b |
	 *       ^
	 * ```
	 *
	 * @type {State}
	 */
	function bodyRowData(code) {
		if (code === null || code === 124 || markdownLineEndingOrSpace(code)) {
			effects.exit("data");
			return bodyRowBreak(code);
		}
		effects.consume(code);
		return code === 92 ? bodyRowEscape : bodyRowData;
	}

	/**
	 * In table body row escape.
	 *
	 * ```markdown
	 *   | | a    |
	 *   | | ---- |
	 * > | | b\-c |
	 *         ^
	 * ```
	 *
	 * @type {State}
	 */
	function bodyRowEscape(code) {
		if (code === 92 || code === 124) {
			effects.consume(code);
			return bodyRowData;
		}
		return bodyRowData(code);
	}
}

/** @type {Resolver} */

function resolveTable(events, context) {
	let index = -1;
	let inFirstCellAwaitingPipe = true;
	/** @type {RowKind} */
	let rowKind = 0;
	/** @type {Range} */
	let lastCell = [0, 0, 0, 0];
	/** @type {Range} */
	let cell = [0, 0, 0, 0];
	let afterHeadAwaitingFirstBodyRow = false;
	let lastTableEnd = 0;
	/** @type {Token | undefined} */
	let currentTable;
	/** @type {Token | undefined} */
	let currentBody;
	/** @type {Token | undefined} */
	let currentCell;
	const map = new EditMap();
	while (++index < events.length) {
		const event = events[index];
		const token = event[1];
		if (event[0] === "enter") {
			// Start of head.
			if (token.type === "tableHead") {
				afterHeadAwaitingFirstBodyRow = false;

				// Inject previous (body end and) table end.
				if (lastTableEnd !== 0) {
					flushTableEnd(map, context, lastTableEnd, currentTable, currentBody);
					currentBody = undefined;
					lastTableEnd = 0;
				}

				// Inject table start.
				currentTable = {
					type: "table",
					start: Object.assign({}, token.start),
					// Note: correct end is set later.
					end: Object.assign({}, token.end),
				};
				map.add(index, 0, [["enter", currentTable, context]]);
			} else if (token.type === "tableRow" || token.type === "tableDelimiterRow") {
				inFirstCellAwaitingPipe = true;
				currentCell = undefined;
				lastCell = [0, 0, 0, 0];
				cell = [0, index + 1, 0, 0];

				// Inject table body start.
				if (afterHeadAwaitingFirstBodyRow) {
					afterHeadAwaitingFirstBodyRow = false;
					currentBody = {
						type: "tableBody",
						start: Object.assign({}, token.start),
						// Note: correct end is set later.
						end: Object.assign({}, token.end),
					};
					map.add(index, 0, [["enter", currentBody, context]]);
				}
				rowKind =
					token.type === "tableDelimiterRow" ? 2
					: currentBody ? 3
					: 1;
			}
			// Cell data.
			else if (rowKind && (token.type === "data" || token.type === "tableDelimiterMarker" || token.type === "tableDelimiterFiller")) {
				inFirstCellAwaitingPipe = false;

				// First value in cell.
				if (cell[2] === 0) {
					if (lastCell[1] !== 0) {
						cell[0] = cell[1];
						currentCell = flushCell(map, context, lastCell, rowKind, undefined, currentCell);
						lastCell = [0, 0, 0, 0];
					}
					cell[2] = index;
				}
			} else if (token.type === "tableCellDivider") {
				if (inFirstCellAwaitingPipe) {
					inFirstCellAwaitingPipe = false;
				} else {
					if (lastCell[1] !== 0) {
						cell[0] = cell[1];
						currentCell = flushCell(map, context, lastCell, rowKind, undefined, currentCell);
					}
					lastCell = cell;
					cell = [lastCell[1], index, 0, 0];
				}
			}
		}
		// Exit events.
		else if (token.type === "tableHead") {
			afterHeadAwaitingFirstBodyRow = true;
			lastTableEnd = index;
		} else if (token.type === "tableRow" || token.type === "tableDelimiterRow") {
			lastTableEnd = index;
			if (lastCell[1] !== 0) {
				cell[0] = cell[1];
				currentCell = flushCell(map, context, lastCell, rowKind, index, currentCell);
			} else if (cell[1] !== 0) {
				currentCell = flushCell(map, context, cell, rowKind, index, currentCell);
			}
			rowKind = 0;
		} else if (rowKind && (token.type === "data" || token.type === "tableDelimiterMarker" || token.type === "tableDelimiterFiller")) {
			cell[3] = index;
		}
	}
	if (lastTableEnd !== 0) {
		flushTableEnd(map, context, lastTableEnd, currentTable, currentBody);
	}
	map.consume(context.events);

	// To do: move this into `html`, when events are exposed there.
	// That’s what `markdown-rs` does.
	// That needs updates to `mdast-util-gfm-table`.
	index = -1;
	while (++index < context.events.length) {
		const event = context.events[index];
		if (event[0] === "enter" && event[1].type === "table") {
			event[1]._align = gfmTableAlign(context.events, index);
		}
	}
	return events;
}

/**
 * Generate a cell.
 *
 * @param {EditMap} map
 * @param {Readonly<TokenizeContext>} context
 * @param {Readonly<Range>} range
 * @param {RowKind} rowKind
 * @param {number | undefined} rowEnd
 * @param {Token | undefined} previousCell
 * @returns {Token | undefined}
 */
// eslint-disable-next-line max-params
function flushCell(map, context, range, rowKind, rowEnd, previousCell) {
	// `markdown-rs` uses:
	// rowKind === 2 ? 'tableDelimiterCell' : 'tableCell'
	const groupName =
		rowKind === 1 ? "tableHeader"
		: rowKind === 2 ? "tableDelimiter"
		: "tableData";
	// `markdown-rs` uses:
	// rowKind === 2 ? 'tableDelimiterCellValue' : 'tableCellText'
	const valueName = "tableContent";

	// Insert an exit for the previous cell, if there is one.
	//
	// ```markdown
	// > | | aa | bb | cc |
	//          ^-- exit
	//           ^^^^-- this cell
	// ```
	if (range[0] !== 0) {
		previousCell.end = Object.assign({}, getPoint(context.events, range[0]));
		map.add(range[0], 0, [["exit", previousCell, context]]);
	}

	// Insert enter of this cell.
	//
	// ```markdown
	// > | | aa | bb | cc |
	//           ^-- enter
	//           ^^^^-- this cell
	// ```
	const now = getPoint(context.events, range[1]);
	previousCell = {
		type: groupName,
		start: Object.assign({}, now),
		// Note: correct end is set later.
		end: Object.assign({}, now),
	};
	map.add(range[1], 0, [["enter", previousCell, context]]);

	// Insert text start at first data start and end at last data end, and
	// remove events between.
	//
	// ```markdown
	// > | | aa | bb | cc |
	//            ^-- enter
	//             ^-- exit
	//           ^^^^-- this cell
	// ```
	if (range[2] !== 0) {
		const relatedStart = getPoint(context.events, range[2]);
		const relatedEnd = getPoint(context.events, range[3]);
		/** @type {Token} */
		const valueToken = {
			type: valueName,
			start: Object.assign({}, relatedStart),
			end: Object.assign({}, relatedEnd),
		};
		map.add(range[2], 0, [["enter", valueToken, context]]);
		if (rowKind !== 2) {
			// Fix positional info on remaining events
			const start = context.events[range[2]];
			const end = context.events[range[3]];
			start[1].end = Object.assign({}, end[1].end);
			start[1].type = "chunkText";
			start[1].contentType = "text";

			// Remove if needed.
			if (range[3] > range[2] + 1) {
				const a = range[2] + 1;
				const b = range[3] - range[2] - 1;
				map.add(a, b, []);
			}
		}
		map.add(range[3] + 1, 0, [["exit", valueToken, context]]);
	}

	// Insert an exit for the last cell, if at the row end.
	//
	// ```markdown
	// > | | aa | bb | cc |
	//                    ^-- exit
	//               ^^^^^^-- this cell (the last one contains two “between” parts)
	// ```
	if (rowEnd !== undefined) {
		previousCell.end = Object.assign({}, getPoint(context.events, rowEnd));
		map.add(rowEnd, 0, [["exit", previousCell, context]]);
		previousCell = undefined;
	}
	return previousCell;
}

/**
 * Generate table end (and table body end).
 *
 * @param {Readonly<EditMap>} map
 * @param {Readonly<TokenizeContext>} context
 * @param {number} index
 * @param {Token} table
 * @param {Token | undefined} tableBody
 */
// eslint-disable-next-line max-params
function flushTableEnd(map, context, index, table, tableBody) {
	/** @type {Array<Event>} */
	const exits = [];
	const related = getPoint(context.events, index);
	if (tableBody) {
		tableBody.end = Object.assign({}, related);
		exits.push(["exit", tableBody, context]);
	}
	table.end = Object.assign({}, related);
	exits.push(["exit", table, context]);
	map.add(index + 1, 0, exits);
}

/**
 * @param {Readonly<Array<Event>>} events
 * @param {number} index
 * @returns {Readonly<Point>}
 */
function getPoint(events, index) {
	const event = events[index];
	const side = event[0] === "enter" ? "start" : "end";
	return event[1][side];
}

/**
 * @import {Extension, State, TokenizeContext, Tokenizer} from 'micromark-util-types'
 */

const tasklistCheck = {
	name: "tasklistCheck",
	tokenize: tokenizeTasklistCheck,
};

/**
 * Create an HTML extension for `micromark` to support GFM task list items
 * syntax.
 *
 * @returns {Extension}
 *   Extension for `micromark` that can be passed in `htmlExtensions` to
 *   support GFM task list items when serializing to HTML.
 */
function gfmTaskListItem() {
	return {
		text: {
			[91]: tasklistCheck,
		},
	};
}

/**
 * @this {TokenizeContext}
 * @type {Tokenizer}
 */
function tokenizeTasklistCheck(effects, ok, nok) {
	const self = this;
	return open;

	/**
	 * At start of task list item check.
	 *
	 * ```markdown
	 * > | * [x] y.
	 *       ^
	 * ```
	 *
	 * @type {State}
	 */
	function open(code) {
		if (
			// Exit if there’s stuff before.
			self.previous !== null ||
			// Exit if not in the first content that is the first child of a list
			// item.
			!self._gfmTasklistFirstContentOfListItem
		) {
			return nok(code);
		}
		effects.enter("taskListCheck");
		effects.enter("taskListCheckMarker");
		effects.consume(code);
		effects.exit("taskListCheckMarker");
		return inside;
	}

	/**
	 * In task list item check.
	 *
	 * ```markdown
	 * > | * [x] y.
	 *        ^
	 * ```
	 *
	 * @type {State}
	 */
	function inside(code) {
		// Currently we match how GH works in files.
		// To match how GH works in comments, use `markdownSpace` (`[\t ]`) instead
		// of `markdownLineEndingOrSpace` (`[\t\n\r ]`).
		if (markdownLineEndingOrSpace(code)) {
			effects.enter("taskListCheckValueUnchecked");
			effects.consume(code);
			effects.exit("taskListCheckValueUnchecked");
			return close;
		}
		if (code === 88 || code === 120) {
			effects.enter("taskListCheckValueChecked");
			effects.consume(code);
			effects.exit("taskListCheckValueChecked");
			return close;
		}
		return nok(code);
	}

	/**
	 * At close of task list item check.
	 *
	 * ```markdown
	 * > | * [x] y.
	 *         ^
	 * ```
	 *
	 * @type {State}
	 */
	function close(code) {
		if (code === 93) {
			effects.enter("taskListCheckMarker");
			effects.consume(code);
			effects.exit("taskListCheckMarker");
			effects.exit("taskListCheck");
			return after;
		}
		return nok(code);
	}

	/**
	 * @type {State}
	 */
	function after(code) {
		// EOL in paragraph means there must be something else after it.
		if (markdownLineEnding(code)) {
			return ok(code);
		}

		// Space or tab?
		// Check what comes after.
		if (markdownSpace(code)) {
			return effects.check(
				{
					tokenize: spaceThenNonSpace,
				},
				ok,
				nok,
			)(code);
		}

		// EOF, or non-whitespace, both wrong.
		return nok(code);
	}
}

/**
 * @this {TokenizeContext}
 * @type {Tokenizer}
 */
function spaceThenNonSpace(effects, ok, nok) {
	return factorySpace(effects, after, "whitespace");

	/**
	 * After whitespace, after task list item check.
	 *
	 * ```markdown
	 * > | * [x] y.
	 *           ^
	 * ```
	 *
	 * @type {State}
	 */
	function after(code) {
		// EOF means there was nothing, so bad.
		// EOL means there’s content after it, so good.
		// Impossible to have more spaces.
		// Anything else is good.
		return code === null ? nok(code) : ok(code);
	}
}

/**
 * @typedef {import('micromark-extension-gfm-footnote').HtmlOptions} HtmlOptions
 * @typedef {import('micromark-extension-gfm-strikethrough').Options} Options
 * @typedef {import('micromark-util-types').Extension} Extension
 * @typedef {import('micromark-util-types').HtmlExtension} HtmlExtension
 */

/**
 * Create an extension for `micromark` to enable GFM syntax.
 *
 * @param {Options | null | undefined} [options]
 *   Configuration (optional).
 *
 *   Passed to `micromark-extens-gfm-strikethrough`.
 * @returns {Extension}
 *   Extension for `micromark` that can be passed in `extensions` to enable GFM
 *   syntax.
 */
function gfm(options) {
	return combineExtensions([gfmAutolinkLiteral(), gfmFootnote(), gfmStrikethrough(options), gfmTable(), gfmTaskListItem()]);
}

/**
 * @import {Root} from 'mdast'
 * @import {Options} from 'remark-gfm'
 * @import {} from 'remark-parse'
 * @import {} from 'remark-stringify'
 * @import {Processor} from 'unified'
 */

/** @type {Options} */
const emptyOptions$2 = {};

/**
 * Add support GFM (autolink literals, footnotes, strikethrough, tables,
 * tasklists).
 *
 * @param {Options | null | undefined} [options]
 *   Configuration (optional).
 * @returns {undefined}
 *   Nothing.
 */
function remarkGfm(options) {
	// @ts-expect-error: TS is wrong about `this`.
	// eslint-disable-next-line unicorn/no-this-assignment
	const self = /** @type {Processor<Root>} */ (this);
	const settings = options || emptyOptions$2;
	const data = self.data();

	const micromarkExtensions = data.micromarkExtensions || (data.micromarkExtensions = []);
	const fromMarkdownExtensions = data.fromMarkdownExtensions || (data.fromMarkdownExtensions = []);
	const toMarkdownExtensions = data.toMarkdownExtensions || (data.toMarkdownExtensions = []);

	micromarkExtensions.push(gfm(settings));
	fromMarkdownExtensions.push(gfmFromMarkdown());
	toMarkdownExtensions.push(gfmToMarkdown(settings));
}

/**
 * @typedef {import('mdast').Root} Root
 * @typedef {import('mdast-util-to-markdown').Options} ToMarkdownOptions
 * @typedef {import('unified').Compiler<Root, string>} Compiler
 * @typedef {import('unified').Processor<undefined, undefined, undefined, Root, string>} Processor
 */

/**
 * Add support for serializing to markdown.
 *
 * @param {Readonly<Options> | null | undefined} [options]
 *   Configuration (optional).
 * @returns {undefined}
 *   Nothing.
 */
function remarkStringify(options) {
	/** @type {Processor} */
	// @ts-expect-error: TS in JSDoc generates wrong types if `this` is typed regularly.
	const self = this;

	self.compiler = compiler;

	/**
	 * @type {Compiler}
	 */
	function compiler(tree) {
		return toMarkdown(tree, {
			...self.data("settings"),
			...options,
			// Note: this option is not in the readme.
			// The goal is for it to be set by plugins on `data` instead of being
			// passed by users.
			extensions: self.data("toMarkdownExtensions") || [],
		});
	}
}

/**
 * Throw a given error.
 *
 * @param {Error|null|undefined} [error]
 *   Maybe error.
 * @returns {asserts error is null|undefined}
 */
function bail(error) {
	if (error) {
		throw error;
	}
}

var extend$1;
var hasRequiredExtend;

function requireExtend() {
	if (hasRequiredExtend) return extend$1;
	hasRequiredExtend = 1;

	var hasOwn = Object.prototype.hasOwnProperty;
	var toStr = Object.prototype.toString;
	var defineProperty = Object.defineProperty;
	var gOPD = Object.getOwnPropertyDescriptor;

	var isArray = function isArray(arr) {
		if (typeof Array.isArray === "function") {
			return Array.isArray(arr);
		}

		return toStr.call(arr) === "[object Array]";
	};

	var isPlainObject = function isPlainObject(obj) {
		if (!obj || toStr.call(obj) !== "[object Object]") {
			return false;
		}

		var hasOwnConstructor = hasOwn.call(obj, "constructor");
		var hasIsPrototypeOf = obj.constructor && obj.constructor.prototype && hasOwn.call(obj.constructor.prototype, "isPrototypeOf");
		// Not own constructor property must be Object
		if (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf) {
			return false;
		}

		// Own properties are enumerated firstly, so to speed up,
		// if last one is own, then all properties are own.
		var key;
		for (key in obj) {
			/**/
		}

		return typeof key === "undefined" || hasOwn.call(obj, key);
	};

	// If name is '__proto__', and Object.defineProperty is available, define __proto__ as an own property on target
	var setProperty = function setProperty(target, options) {
		if (defineProperty && options.name === "__proto__") {
			defineProperty(target, options.name, {
				enumerable: true,
				configurable: true,
				value: options.newValue,
				writable: true,
			});
		} else {
			target[options.name] = options.newValue;
		}
	};

	// Return undefined instead of __proto__ if '__proto__' is not an own property
	var getProperty = function getProperty(obj, name) {
		if (name === "__proto__") {
			if (!hasOwn.call(obj, name)) {
				return void 0;
			} else if (gOPD) {
				// In early versions of node, obj['__proto__'] is buggy when obj has
				// __proto__ as an own property. Object.getOwnPropertyDescriptor() works.
				return gOPD(obj, name).value;
			}
		}

		return obj[name];
	};

	extend$1 = function extend() {
		var options, name, src, copy, copyIsArray, clone;
		var target = arguments[0];
		var i = 1;
		var length = arguments.length;
		var deep = false;

		// Handle a deep copy situation
		if (typeof target === "boolean") {
			deep = target;
			target = arguments[1] || {};
			// skip the boolean and the target
			i = 2;
		}
		if (target == null || (typeof target !== "object" && typeof target !== "function")) {
			target = {};
		}

		for (; i < length; ++i) {
			options = arguments[i];
			// Only deal with non-null/undefined values
			if (options != null) {
				// Extend the base object
				for (name in options) {
					src = getProperty(target, name);
					copy = getProperty(options, name);

					// Prevent never-ending loop
					if (target !== copy) {
						// Recurse if we're merging plain objects or arrays
						if (deep && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) {
							if (copyIsArray) {
								copyIsArray = false;
								clone = src && isArray(src) ? src : [];
							} else {
								clone = src && isPlainObject(src) ? src : {};
							}

							// Never move original objects, clone them
							setProperty(target, { name: name, newValue: extend(deep, clone, copy) });

							// Don't bring in undefined values
						} else if (typeof copy !== "undefined") {
							setProperty(target, { name: name, newValue: copy });
						}
					}
				}
			}
		}

		// Return the modified object
		return target;
	};
	return extend$1;
}

var extendExports = requireExtend();
const extend = /*@__PURE__*/ getDefaultExportFromCjs(extendExports);

function isPlainObject(value) {
	if (typeof value !== "object" || value === null) {
		return false;
	}

	const prototype = Object.getPrototypeOf(value);
	return (prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null) && !(Symbol.toStringTag in value) && !(Symbol.iterator in value);
}

// To do: remove `void`s
// To do: remove `null` from output of our APIs, allow it as user APIs.

/**
 * @typedef {(error?: Error | null | undefined, ...output: Array<any>) => void} Callback
 *   Callback.
 *
 * @typedef {(...input: Array<any>) => any} Middleware
 *   Ware.
 *
 * @typedef Pipeline
 *   Pipeline.
 * @property {Run} run
 *   Run the pipeline.
 * @property {Use} use
 *   Add middleware.
 *
 * @typedef {(...input: Array<any>) => void} Run
 *   Call all middleware.
 *
 *   Calls `done` on completion with either an error or the output of the
 *   last middleware.
 *
 *   > 👉 **Note**: as the length of input defines whether async functions get a
 *   > `next` function,
 *   > it’s recommended to keep `input` at one value normally.

 *
 * @typedef {(fn: Middleware) => Pipeline} Use
 *   Add middleware.
 */

/**
 * Create new middleware.
 *
 * @returns {Pipeline}
 *   Pipeline.
 */
function trough() {
	/** @type {Array<Middleware>} */
	const fns = [];
	/** @type {Pipeline} */
	const pipeline = { run, use };

	return pipeline;

	/** @type {Run} */
	function run(...values) {
		let middlewareIndex = -1;
		/** @type {Callback} */
		const callback = values.pop();

		if (typeof callback !== "function") {
			throw new TypeError("Expected function as last argument, not " + callback);
		}

		next(null, ...values);

		/**
		 * Run the next `fn`, or we’re done.
		 *
		 * @param {Error | null | undefined} error
		 * @param {Array<any>} output
		 */
		function next(error, ...output) {
			const fn = fns[++middlewareIndex];
			let index = -1;

			if (error) {
				callback(error);
				return;
			}

			// Copy non-nullish input into values.
			while (++index < values.length) {
				if (output[index] === null || output[index] === undefined) {
					output[index] = values[index];
				}
			}

			// Save the newly created `output` for the next call.
			values = output;

			// Next or done.
			if (fn) {
				wrap$1(fn, next)(...output);
			} else {
				callback(null, ...output);
			}
		}
	}

	/** @type {Use} */
	function use(middelware) {
		if (typeof middelware !== "function") {
			throw new TypeError("Expected `middelware` to be a function, not " + middelware);
		}

		fns.push(middelware);
		return pipeline;
	}
}

/**
 * Wrap `middleware` into a uniform interface.
 *
 * You can pass all input to the resulting function.
 * `callback` is then called with the output of `middleware`.
 *
 * If `middleware` accepts more arguments than the later given in input,
 * an extra `done` function is passed to it after that input,
 * which must be called by `middleware`.
 *
 * The first value in `input` is the main input value.
 * All other input values are the rest input values.
 * The values given to `callback` are the input values,
 * merged with every non-nullish output value.
 *
 * * if `middleware` throws an error,
 *   returns a promise that is rejected,
 *   or calls the given `done` function with an error,
 *   `callback` is called with that error
 * * if `middleware` returns a value or returns a promise that is resolved,
 *   that value is the main output value
 * * if `middleware` calls `done`,
 *   all non-nullish values except for the first one (the error) overwrite the
 *   output values
 *
 * @param {Middleware} middleware
 *   Function to wrap.
 * @param {Callback} callback
 *   Callback called with the output of `middleware`.
 * @returns {Run}
 *   Wrapped middleware.
 */
function wrap$1(middleware, callback) {
	/** @type {boolean} */
	let called;

	return wrapped;

	/**
	 * Call `middleware`.
	 * @this {any}
	 * @param {Array<any>} parameters
	 * @returns {void}
	 */
	function wrapped(...parameters) {
		const fnExpectsCallback = middleware.length > parameters.length;
		/** @type {any} */
		let result;

		if (fnExpectsCallback) {
			parameters.push(done);
		}

		try {
			result = middleware.apply(this, parameters);
		} catch (error) {
			const exception = /** @type {Error} */ (error);

			// Well, this is quite the pickle.
			// `middleware` received a callback and called it synchronously, but that
			// threw an error.
			// The only thing left to do is to throw the thing instead.
			if (fnExpectsCallback && called) {
				throw exception;
			}

			return done(exception);
		}

		if (!fnExpectsCallback) {
			if (result && result.then && typeof result.then === "function") {
				result.then(then, done);
			} else if (result instanceof Error) {
				done(result);
			} else {
				then(result);
			}
		}
	}

	/**
	 * Call `callback`, only once.
	 *
	 * @type {Callback}
	 */
	function done(error, ...output) {
		if (!called) {
			called = true;
			callback(error, ...output);
		}
	}

	/**
	 * Call `done` with one value.
	 *
	 * @param {any} [value]
	 */
	function then(value) {
		done(null, value);
	}
}

const CallableInstance =
	/**
	 * @type {new <Parameters extends Array<unknown>, Result>(property: string | symbol) => (...parameters: Parameters) => Result}
	 */
	(
		/** @type {unknown} */
		(
			/**
			 * @this {Function}
			 * @param {string | symbol} property
			 * @returns {(...parameters: Array<unknown>) => unknown}
			 */
			function (property) {
				const self = this;
				const constr = self.constructor;
				const proto = /** @type {Record<string | symbol, Function>} */ (
					// Prototypes do exist.
					// type-coverage:ignore-next-line
					constr.prototype
				);
				const value = proto[property];
				/** @type {(...parameters: Array<unknown>) => unknown} */
				const apply = function () {
					return value.apply(apply, arguments);
				};

				Object.setPrototypeOf(apply, proto);

				// Not needed for us in `unified`: we only call this on the `copy`
				// function,
				// and we don't need to add its fields (`length`, `name`)
				// over.
				// See also: GH-246.
				// const names = Object.getOwnPropertyNames(value)
				//
				// for (const p of names) {
				//   const descriptor = Object.getOwnPropertyDescriptor(value, p)
				//   if (descriptor) Object.defineProperty(apply, p, descriptor)
				// }

				return apply;
			}
		)
	);

/**
 * @typedef {import('trough').Pipeline} Pipeline
 *
 * @typedef {import('unist').Node} Node
 *
 * @typedef {import('vfile').Compatible} Compatible
 * @typedef {import('vfile').Value} Value
 *
 * @typedef {import('../index.js').CompileResultMap} CompileResultMap
 * @typedef {import('../index.js').Data} Data
 * @typedef {import('../index.js').Settings} Settings
 */

// To do: next major: drop `Compiler`, `Parser`: prefer lowercase.

// To do: we could start yielding `never` in TS when a parser is missing and
// `parse` is called.
// Currently, we allow directly setting `processor.parser`, which is untyped.

const own$4 = {}.hasOwnProperty;

/**
 * @template {Node | undefined} [ParseTree=undefined]
 *   Output of `parse` (optional).
 * @template {Node | undefined} [HeadTree=undefined]
 *   Input for `run` (optional).
 * @template {Node | undefined} [TailTree=undefined]
 *   Output for `run` (optional).
 * @template {Node | undefined} [CompileTree=undefined]
 *   Input of `stringify` (optional).
 * @template {CompileResults | undefined} [CompileResult=undefined]
 *   Output of `stringify` (optional).
 * @extends {CallableInstance<[], Processor<ParseTree, HeadTree, TailTree, CompileTree, CompileResult>>}
 */
class Processor extends CallableInstance {
	/**
	 * Create a processor.
	 */
	constructor() {
		// If `Processor()` is called (w/o new), `copy` is called instead.
		super("copy");

		/**
		 * Compiler to use (deprecated).
		 *
		 * @deprecated
		 *   Use `compiler` instead.
		 * @type {(
		 *   Compiler<
		 *     CompileTree extends undefined ? Node : CompileTree,
		 *     CompileResult extends undefined ? CompileResults : CompileResult
		 *   > |
		 *   undefined
		 * )}
		 */
		this.Compiler = undefined;

		/**
		 * Parser to use (deprecated).
		 *
		 * @deprecated
		 *   Use `parser` instead.
		 * @type {(
		 *   Parser<ParseTree extends undefined ? Node : ParseTree> |
		 *   undefined
		 * )}
		 */
		this.Parser = undefined;

		// Note: the following fields are considered private.
		// However, they are needed for tests, and TSC generates an untyped
		// `private freezeIndex` field for, which trips `type-coverage` up.
		// Instead, we use `@deprecated` to visualize that they shouldn’t be used.
		/**
		 * Internal list of configured plugins.
		 *
		 * @deprecated
		 *   This is a private internal property and should not be used.
		 * @type {Array<PluginTuple<Array<unknown>>>}
		 */
		this.attachers = [];

		/**
		 * Compiler to use.
		 *
		 * @type {(
		 *   Compiler<
		 *     CompileTree extends undefined ? Node : CompileTree,
		 *     CompileResult extends undefined ? CompileResults : CompileResult
		 *   > |
		 *   undefined
		 * )}
		 */
		this.compiler = undefined;

		/**
		 * Internal state to track where we are while freezing.
		 *
		 * @deprecated
		 *   This is a private internal property and should not be used.
		 * @type {number}
		 */
		this.freezeIndex = -1;

		/**
		 * Internal state to track whether we’re frozen.
		 *
		 * @deprecated
		 *   This is a private internal property and should not be used.
		 * @type {boolean | undefined}
		 */
		this.frozen = undefined;

		/**
		 * Internal state.
		 *
		 * @deprecated
		 *   This is a private internal property and should not be used.
		 * @type {Data}
		 */
		this.namespace = {};

		/**
		 * Parser to use.
		 *
		 * @type {(
		 *   Parser<ParseTree extends undefined ? Node : ParseTree> |
		 *   undefined
		 * )}
		 */
		this.parser = undefined;

		/**
		 * Internal list of configured transformers.
		 *
		 * @deprecated
		 *   This is a private internal property and should not be used.
		 * @type {Pipeline}
		 */
		this.transformers = trough();
	}

	/**
	 * Copy a processor.
	 *
	 * @deprecated
	 *   This is a private internal method and should not be used.
	 * @returns {Processor<ParseTree, HeadTree, TailTree, CompileTree, CompileResult>}
	 *   New *unfrozen* processor ({@linkcode Processor}) that is
	 *   configured to work the same as its ancestor.
	 *   When the descendant processor is configured in the future it does not
	 *   affect the ancestral processor.
	 */
	copy() {
		// Cast as the type parameters will be the same after attaching.
		const destination = /** @type {Processor<ParseTree, HeadTree, TailTree, CompileTree, CompileResult>} */ (new Processor());
		let index = -1;

		while (++index < this.attachers.length) {
			const attacher = this.attachers[index];
			destination.use(...attacher);
		}

		destination.data(extend(true, {}, this.namespace));

		return destination;
	}

	/**
	 * Configure the processor with info available to all plugins.
	 * Information is stored in an object.
	 *
	 * Typically, options can be given to a specific plugin, but sometimes it
	 * makes sense to have information shared with several plugins.
	 * For example, a list of HTML elements that are self-closing, which is
	 * needed during all phases.
	 *
	 * > **Note**: setting information cannot occur on *frozen* processors.
	 * > Call the processor first to create a new unfrozen processor.
	 *
	 * > **Note**: to register custom data in TypeScript, augment the
	 * > {@linkcode Data} interface.
	 *
	 * @example
	 *   This example show how to get and set info:
	 *
	 *   ```js
	 *   import {unified} from 'unified'
	 *
	 *   const processor = unified().data('alpha', 'bravo')
	 *
	 *   processor.data('alpha') // => 'bravo'
	 *
	 *   processor.data() // => {alpha: 'bravo'}
	 *
	 *   processor.data({charlie: 'delta'})
	 *
	 *   processor.data() // => {charlie: 'delta'}
	 *   ```
	 *
	 * @template {keyof Data} Key
	 *
	 * @overload
	 * @returns {Data}
	 *
	 * @overload
	 * @param {Data} dataset
	 * @returns {Processor<ParseTree, HeadTree, TailTree, CompileTree, CompileResult>}
	 *
	 * @overload
	 * @param {Key} key
	 * @returns {Data[Key]}
	 *
	 * @overload
	 * @param {Key} key
	 * @param {Data[Key]} value
	 * @returns {Processor<ParseTree, HeadTree, TailTree, CompileTree, CompileResult>}
	 *
	 * @param {Data | Key} [key]
	 *   Key to get or set, or entire dataset to set, or nothing to get the
	 *   entire dataset (optional).
	 * @param {Data[Key]} [value]
	 *   Value to set (optional).
	 * @returns {unknown}
	 *   The current processor when setting, the value at `key` when getting, or
	 *   the entire dataset when getting without key.
	 */
	data(key, value) {
		if (typeof key === "string") {
			// Set `key`.
			if (arguments.length === 2) {
				assertUnfrozen("data", this.frozen);
				this.namespace[key] = value;
				return this;
			}

			// Get `key`.
			return (own$4.call(this.namespace, key) && this.namespace[key]) || undefined;
		}

		// Set space.
		if (key) {
			assertUnfrozen("data", this.frozen);
			this.namespace = key;
			return this;
		}

		// Get space.
		return this.namespace;
	}

	/**
	 * Freeze a processor.
	 *
	 * Frozen processors are meant to be extended and not to be configured
	 * directly.
	 *
	 * When a processor is frozen it cannot be unfrozen.
	 * New processors working the same way can be created by calling the
	 * processor.
	 *
	 * It’s possible to freeze processors explicitly by calling `.freeze()`.
	 * Processors freeze automatically when `.parse()`, `.run()`, `.runSync()`,
	 * `.stringify()`, `.process()`, or `.processSync()` are called.
	 *
	 * @returns {Processor<ParseTree, HeadTree, TailTree, CompileTree, CompileResult>}
	 *   The current processor.
	 */
	freeze() {
		if (this.frozen) {
			return this;
		}

		// Cast so that we can type plugins easier.
		// Plugins are supposed to be usable on different processors, not just on
		// this exact processor.
		const self = /** @type {Processor} */ (/** @type {unknown} */ (this));

		while (++this.freezeIndex < this.attachers.length) {
			const [attacher, ...options] = this.attachers[this.freezeIndex];

			if (options[0] === false) {
				continue;
			}

			if (options[0] === true) {
				options[0] = undefined;
			}

			const transformer = attacher.call(self, ...options);

			if (typeof transformer === "function") {
				this.transformers.use(transformer);
			}
		}

		this.frozen = true;
		this.freezeIndex = Number.POSITIVE_INFINITY;

		return this;
	}

	/**
	 * Parse text to a syntax tree.
	 *
	 * > **Note**: `parse` freezes the processor if not already *frozen*.
	 *
	 * > **Note**: `parse` performs the parse phase, not the run phase or other
	 * > phases.
	 *
	 * @param {Compatible | undefined} [file]
	 *   file to parse (optional); typically `string` or `VFile`; any value
	 *   accepted as `x` in `new VFile(x)`.
	 * @returns {ParseTree extends undefined ? Node : ParseTree}
	 *   Syntax tree representing `file`.
	 */
	parse(file) {
		this.freeze();
		const realFile = vfile(file);
		const parser = this.parser || this.Parser;
		assertParser("parse", parser);
		return parser(String(realFile), realFile);
	}

	/**
	 * Process the given file as configured on the processor.
	 *
	 * > **Note**: `process` freezes the processor if not already *frozen*.
	 *
	 * > **Note**: `process` performs the parse, run, and stringify phases.
	 *
	 * @overload
	 * @param {Compatible | undefined} file
	 * @param {ProcessCallback<VFileWithOutput<CompileResult>>} done
	 * @returns {undefined}
	 *
	 * @overload
	 * @param {Compatible | undefined} [file]
	 * @returns {Promise<VFileWithOutput<CompileResult>>}
	 *
	 * @param {Compatible | undefined} [file]
	 *   File (optional); typically `string` or `VFile`]; any value accepted as
	 *   `x` in `new VFile(x)`.
	 * @param {ProcessCallback<VFileWithOutput<CompileResult>> | undefined} [done]
	 *   Callback (optional).
	 * @returns {Promise<VFile> | undefined}
	 *   Nothing if `done` is given.
	 *   Otherwise a promise, rejected with a fatal error or resolved with the
	 *   processed file.
	 *
	 *   The parsed, transformed, and compiled value is available at
	 *   `file.value` (see note).
	 *
	 *   > **Note**: unified typically compiles by serializing: most
	 *   > compilers return `string` (or `Uint8Array`).
	 *   > Some compilers, such as the one configured with
	 *   > [`rehype-react`][rehype-react], return other values (in this case, a
	 *   > React tree).
	 *   > If you’re using a compiler that doesn’t serialize, expect different
	 *   > result values.
	 *   >
	 *   > To register custom results in TypeScript, add them to
	 *   > {@linkcode CompileResultMap}.
	 *
	 *   [rehype-react]: https://github.com/rehypejs/rehype-react
	 */
	process(file, done) {
		const self = this;

		this.freeze();
		assertParser("process", this.parser || this.Parser);
		assertCompiler("process", this.compiler || this.Compiler);

		return done ? executor(undefined, done) : new Promise(executor);

		// Note: `void`s needed for TS.
		/**
		 * @param {((file: VFileWithOutput<CompileResult>) => undefined | void) | undefined} resolve
		 * @param {(error: Error | undefined) => undefined | void} reject
		 * @returns {undefined}
		 */
		function executor(resolve, reject) {
			const realFile = vfile(file);
			// Assume `ParseTree` (the result of the parser) matches `HeadTree` (the
			// input of the first transform).
			const parseTree = /** @type {HeadTree extends undefined ? Node : HeadTree} */ (/** @type {unknown} */ (self.parse(realFile)));

			self.run(parseTree, realFile, function (error, tree, file) {
				if (error || !tree || !file) {
					return realDone(error);
				}

				// Assume `TailTree` (the output of the last transform) matches
				// `CompileTree` (the input of the compiler).
				const compileTree = /** @type {CompileTree extends undefined ? Node : CompileTree} */ (/** @type {unknown} */ (tree));

				const compileResult = self.stringify(compileTree, file);

				if (looksLikeAValue(compileResult)) {
					file.value = compileResult;
				} else {
					file.result = compileResult;
				}

				realDone(error, /** @type {VFileWithOutput<CompileResult>} */ (file));
			});

			/**
			 * @param {Error | undefined} error
			 * @param {VFileWithOutput<CompileResult> | undefined} [file]
			 * @returns {undefined}
			 */
			function realDone(error, file) {
				if (error || !file) {
					reject(error);
				} else if (resolve) {
					resolve(file);
				} else {
					done(undefined, file);
				}
			}
		}
	}

	/**
	 * Process the given file as configured on the processor.
	 *
	 * An error is thrown if asynchronous transforms are configured.
	 *
	 * > **Note**: `processSync` freezes the processor if not already *frozen*.
	 *
	 * > **Note**: `processSync` performs the parse, run, and stringify phases.
	 *
	 * @param {Compatible | undefined} [file]
	 *   File (optional); typically `string` or `VFile`; any value accepted as
	 *   `x` in `new VFile(x)`.
	 * @returns {VFileWithOutput<CompileResult>}
	 *   The processed file.
	 *
	 *   The parsed, transformed, and compiled value is available at
	 *   `file.value` (see note).
	 *
	 *   > **Note**: unified typically compiles by serializing: most
	 *   > compilers return `string` (or `Uint8Array`).
	 *   > Some compilers, such as the one configured with
	 *   > [`rehype-react`][rehype-react], return other values (in this case, a
	 *   > React tree).
	 *   > If you’re using a compiler that doesn’t serialize, expect different
	 *   > result values.
	 *   >
	 *   > To register custom results in TypeScript, add them to
	 *   > {@linkcode CompileResultMap}.
	 *
	 *   [rehype-react]: https://github.com/rehypejs/rehype-react
	 */
	processSync(file) {
		/** @type {boolean} */
		let complete = false;
		/** @type {VFileWithOutput<CompileResult> | undefined} */
		let result;

		this.freeze();
		assertParser("processSync", this.parser || this.Parser);
		assertCompiler("processSync", this.compiler || this.Compiler);

		this.process(file, realDone);
		assertDone("processSync", "process", complete);

		return result;

		/**
		 * @type {ProcessCallback<VFileWithOutput<CompileResult>>}
		 */
		function realDone(error, file) {
			complete = true;
			bail(error);
			result = file;
		}
	}

	/**
	 * Run *transformers* on a syntax tree.
	 *
	 * > **Note**: `run` freezes the processor if not already *frozen*.
	 *
	 * > **Note**: `run` performs the run phase, not other phases.
	 *
	 * @overload
	 * @param {HeadTree extends undefined ? Node : HeadTree} tree
	 * @param {RunCallback<TailTree extends undefined ? Node : TailTree>} done
	 * @returns {undefined}
	 *
	 * @overload
	 * @param {HeadTree extends undefined ? Node : HeadTree} tree
	 * @param {Compatible | undefined} file
	 * @param {RunCallback<TailTree extends undefined ? Node : TailTree>} done
	 * @returns {undefined}
	 *
	 * @overload
	 * @param {HeadTree extends undefined ? Node : HeadTree} tree
	 * @param {Compatible | undefined} [file]
	 * @returns {Promise<TailTree extends undefined ? Node : TailTree>}
	 *
	 * @param {HeadTree extends undefined ? Node : HeadTree} tree
	 *   Tree to transform and inspect.
	 * @param {(
	 *   RunCallback<TailTree extends undefined ? Node : TailTree> |
	 *   Compatible
	 * )} [file]
	 *   File associated with `node` (optional); any value accepted as `x` in
	 *   `new VFile(x)`.
	 * @param {RunCallback<TailTree extends undefined ? Node : TailTree>} [done]
	 *   Callback (optional).
	 * @returns {Promise<TailTree extends undefined ? Node : TailTree> | undefined}
	 *   Nothing if `done` is given.
	 *   Otherwise, a promise rejected with a fatal error or resolved with the
	 *   transformed tree.
	 */
	run(tree, file, done) {
		assertNode(tree);
		this.freeze();

		const transformers = this.transformers;

		if (!done && typeof file === "function") {
			done = file;
			file = undefined;
		}

		return done ? executor(undefined, done) : new Promise(executor);

		// Note: `void`s needed for TS.
		/**
		 * @param {(
		 *   ((tree: TailTree extends undefined ? Node : TailTree) => undefined | void) |
		 *   undefined
		 * )} resolve
		 * @param {(error: Error) => undefined | void} reject
		 * @returns {undefined}
		 */
		function executor(resolve, reject) {
			const realFile = vfile(file);
			transformers.run(tree, realFile, realDone);

			/**
			 * @param {Error | undefined} error
			 * @param {Node} outputTree
			 * @param {VFile} file
			 * @returns {undefined}
			 */
			function realDone(error, outputTree, file) {
				const resultingTree = /** @type {TailTree extends undefined ? Node : TailTree} */ (outputTree || tree);

				if (error) {
					reject(error);
				} else if (resolve) {
					resolve(resultingTree);
				} else {
					done(undefined, resultingTree, file);
				}
			}
		}
	}

	/**
	 * Run *transformers* on a syntax tree.
	 *
	 * An error is thrown if asynchronous transforms are configured.
	 *
	 * > **Note**: `runSync` freezes the processor if not already *frozen*.
	 *
	 * > **Note**: `runSync` performs the run phase, not other phases.
	 *
	 * @param {HeadTree extends undefined ? Node : HeadTree} tree
	 *   Tree to transform and inspect.
	 * @param {Compatible | undefined} [file]
	 *   File associated with `node` (optional); any value accepted as `x` in
	 *   `new VFile(x)`.
	 * @returns {TailTree extends undefined ? Node : TailTree}
	 *   Transformed tree.
	 */
	runSync(tree, file) {
		/** @type {boolean} */
		let complete = false;
		/** @type {(TailTree extends undefined ? Node : TailTree) | undefined} */
		let result;

		this.run(tree, file, realDone);

		assertDone("runSync", "run", complete);
		return result;

		/**
		 * @type {RunCallback<TailTree extends undefined ? Node : TailTree>}
		 */
		function realDone(error, tree) {
			bail(error);
			result = tree;
			complete = true;
		}
	}

	/**
	 * Compile a syntax tree.
	 *
	 * > **Note**: `stringify` freezes the processor if not already *frozen*.
	 *
	 * > **Note**: `stringify` performs the stringify phase, not the run phase
	 * > or other phases.
	 *
	 * @param {CompileTree extends undefined ? Node : CompileTree} tree
	 *   Tree to compile.
	 * @param {Compatible | undefined} [file]
	 *   File associated with `node` (optional); any value accepted as `x` in
	 *   `new VFile(x)`.
	 * @returns {CompileResult extends undefined ? Value : CompileResult}
	 *   Textual representation of the tree (see note).
	 *
	 *   > **Note**: unified typically compiles by serializing: most compilers
	 *   > return `string` (or `Uint8Array`).
	 *   > Some compilers, such as the one configured with
	 *   > [`rehype-react`][rehype-react], return other values (in this case, a
	 *   > React tree).
	 *   > If you’re using a compiler that doesn’t serialize, expect different
	 *   > result values.
	 *   >
	 *   > To register custom results in TypeScript, add them to
	 *   > {@linkcode CompileResultMap}.
	 *
	 *   [rehype-react]: https://github.com/rehypejs/rehype-react
	 */
	stringify(tree, file) {
		this.freeze();
		const realFile = vfile(file);
		const compiler = this.compiler || this.Compiler;
		assertCompiler("stringify", compiler);
		assertNode(tree);

		return compiler(tree, realFile);
	}

	/**
	 * Configure the processor to use a plugin, a list of usable values, or a
	 * preset.
	 *
	 * If the processor is already using a plugin, the previous plugin
	 * configuration is changed based on the options that are passed in.
	 * In other words, the plugin is not added a second time.
	 *
	 * > **Note**: `use` cannot be called on *frozen* processors.
	 * > Call the processor first to create a new unfrozen processor.
	 *
	 * @example
	 *   There are many ways to pass plugins to `.use()`.
	 *   This example gives an overview:
	 *
	 *   ```js
	 *   import {unified} from 'unified'
	 *
	 *   unified()
	 *     // Plugin with options:
	 *     .use(pluginA, {x: true, y: true})
	 *     // Passing the same plugin again merges configuration (to `{x: true, y: false, z: true}`):
	 *     .use(pluginA, {y: false, z: true})
	 *     // Plugins:
	 *     .use([pluginB, pluginC])
	 *     // Two plugins, the second with options:
	 *     .use([pluginD, [pluginE, {}]])
	 *     // Preset with plugins and settings:
	 *     .use({plugins: [pluginF, [pluginG, {}]], settings: {position: false}})
	 *     // Settings only:
	 *     .use({settings: {position: false}})
	 *   ```
	 *
	 * @template {Array<unknown>} [Parameters=[]]
	 * @template {Node | string | undefined} [Input=undefined]
	 * @template [Output=Input]
	 *
	 * @overload
	 * @param {Preset | null | undefined} [preset]
	 * @returns {Processor<ParseTree, HeadTree, TailTree, CompileTree, CompileResult>}
	 *
	 * @overload
	 * @param {PluggableList} list
	 * @returns {Processor<ParseTree, HeadTree, TailTree, CompileTree, CompileResult>}
	 *
	 * @overload
	 * @param {Plugin<Parameters, Input, Output>} plugin
	 * @param {...(Parameters | [boolean])} parameters
	 * @returns {UsePlugin<ParseTree, HeadTree, TailTree, CompileTree, CompileResult, Input, Output>}
	 *
	 * @param {PluggableList | Plugin | Preset | null | undefined} value
	 *   Usable value.
	 * @param {...unknown} parameters
	 *   Parameters, when a plugin is given as a usable value.
	 * @returns {Processor<ParseTree, HeadTree, TailTree, CompileTree, CompileResult>}
	 *   Current processor.
	 */
	use(value, ...parameters) {
		const attachers = this.attachers;
		const namespace = this.namespace;

		assertUnfrozen("use", this.frozen);

		if (value === null || value === undefined);
		else if (typeof value === "function") {
			addPlugin(value, parameters);
		} else if (typeof value === "object") {
			if (Array.isArray(value)) {
				addList(value);
			} else {
				addPreset(value);
			}
		} else {
			throw new TypeError("Expected usable value, not `" + value + "`");
		}

		return this;

		/**
		 * @param {Pluggable} value
		 * @returns {undefined}
		 */
		function add(value) {
			if (typeof value === "function") {
				addPlugin(value, []);
			} else if (typeof value === "object") {
				if (Array.isArray(value)) {
					const [plugin, ...parameters] = /** @type {PluginTuple<Array<unknown>>} */ (value);
					addPlugin(plugin, parameters);
				} else {
					addPreset(value);
				}
			} else {
				throw new TypeError("Expected usable value, not `" + value + "`");
			}
		}

		/**
		 * @param {Preset} result
		 * @returns {undefined}
		 */
		function addPreset(result) {
			if (!("plugins" in result) && !("settings" in result)) {
				throw new Error(
					"Expected usable value but received an empty preset, which is probably a mistake: presets typically come with `plugins` and sometimes with `settings`, but this has neither",
				);
			}

			addList(result.plugins);

			if (result.settings) {
				namespace.settings = extend(true, namespace.settings, result.settings);
			}
		}

		/**
		 * @param {PluggableList | null | undefined} plugins
		 * @returns {undefined}
		 */
		function addList(plugins) {
			let index = -1;

			if (plugins === null || plugins === undefined);
			else if (Array.isArray(plugins)) {
				while (++index < plugins.length) {
					const thing = plugins[index];
					add(thing);
				}
			} else {
				throw new TypeError("Expected a list of plugins, not `" + plugins + "`");
			}
		}

		/**
		 * @param {Plugin} plugin
		 * @param {Array<unknown>} parameters
		 * @returns {undefined}
		 */
		function addPlugin(plugin, parameters) {
			let index = -1;
			let entryIndex = -1;

			while (++index < attachers.length) {
				if (attachers[index][0] === plugin) {
					entryIndex = index;
					break;
				}
			}

			if (entryIndex === -1) {
				attachers.push([plugin, ...parameters]);
			}
			// Only set if there was at least a `primary` value, otherwise we’d change
			// `arguments.length`.
			else if (parameters.length > 0) {
				let [primary, ...rest] = parameters;
				const currentPrimary = attachers[entryIndex][1];
				if (isPlainObject(currentPrimary) && isPlainObject(primary)) {
					primary = extend(true, currentPrimary, primary);
				}

				attachers[entryIndex] = [plugin, primary, ...rest];
			}
		}
	}
}

// Note: this returns a *callable* instance.
// That’s why it’s documented as a function.
/**
 * Create a new processor.
 *
 * @example
 *   This example shows how a new processor can be created (from `remark`) and linked
 *   to **stdin**(4) and **stdout**(4).
 *
 *   ```js
 *   import process from 'node:process'
 *   import concatStream from 'concat-stream'
 *   import {remark} from 'remark'
 *
 *   process.stdin.pipe(
 *     concatStream(function (buf) {
 *       process.stdout.write(String(remark().processSync(buf)))
 *     })
 *   )
 *   ```
 *
 * @returns
 *   New *unfrozen* processor (`processor`).
 *
 *   This processor is configured to work the same as its ancestor.
 *   When the descendant processor is configured in the future it does not
 *   affect the ancestral processor.
 */
const unified = new Processor().freeze();

/**
 * Assert a parser is available.
 *
 * @param {string} name
 * @param {unknown} value
 * @returns {asserts value is Parser}
 */
function assertParser(name, value) {
	if (typeof value !== "function") {
		throw new TypeError("Cannot `" + name + "` without `parser`");
	}
}

/**
 * Assert a compiler is available.
 *
 * @param {string} name
 * @param {unknown} value
 * @returns {asserts value is Compiler}
 */
function assertCompiler(name, value) {
	if (typeof value !== "function") {
		throw new TypeError("Cannot `" + name + "` without `compiler`");
	}
}

/**
 * Assert the processor is not frozen.
 *
 * @param {string} name
 * @param {unknown} frozen
 * @returns {asserts frozen is false}
 */
function assertUnfrozen(name, frozen) {
	if (frozen) {
		throw new Error("Cannot call `" + name + "` on a frozen processor.\nCreate a new processor first, by calling it: use `processor()` instead of `processor`.");
	}
}

/**
 * Assert `node` is a unist node.
 *
 * @param {unknown} node
 * @returns {asserts node is Node}
 */
function assertNode(node) {
	// `isPlainObj` unfortunately uses `any` instead of `unknown`.
	// type-coverage:ignore-next-line
	if (!isPlainObject(node) || typeof node.type !== "string") {
		throw new TypeError("Expected node, got `" + node + "`");
		// Fine.
	}
}

/**
 * Assert that `complete` is `true`.
 *
 * @param {string} name
 * @param {string} asyncName
 * @param {unknown} complete
 * @returns {asserts complete is true}
 */
function assertDone(name, asyncName, complete) {
	if (!complete) {
		throw new Error("`" + name + "` finished async. Use `" + asyncName + "` instead");
	}
}

/**
 * @param {Compatible | undefined} [value]
 * @returns {VFile}
 */
function vfile(value) {
	return looksLikeAVFile(value) ? value : new VFile(value);
}

/**
 * @param {Compatible | undefined} [value]
 * @returns {value is VFile}
 */
function looksLikeAVFile(value) {
	return Boolean(value && typeof value === "object" && "message" in value && "messages" in value);
}

/**
 * @param {unknown} [value]
 * @returns {value is Value}
 */
function looksLikeAValue(value) {
	return typeof value === "string" || isUint8Array(value);
}

/**
 * Assert `value` is an `Uint8Array`.
 *
 * @param {unknown} value
 *   thing.
 * @returns {value is Uint8Array}
 *   Whether `value` is an `Uint8Array`.
 */
function isUint8Array(value) {
	return Boolean(value && typeof value === "object" && "byteLength" in value && "byteOffset" in value);
}

/**
 * @import {
 *   Comment as HastComment,
 *   Doctype as HastDoctype,
 *   Element as HastElement,
 *   Nodes as HastNodes,
 *   RootContent as HastRootContent,
 *   Root as HastRoot,
 *   Text as HastText,
 * } from 'hast'
 */

/**
 * Transform a DOM tree to a hast tree.
 *
 * @param {Node} tree
 *   DOM tree to transform.
 * @param {Options | null | undefined} [options]
 *   Configuration (optional).
 * @returns {HastNodes}
 *   Equivalent hast node.
 */
function fromDom(tree, options) {
	return transform(tree, {}) || { type: "root", children: [] };
}

/**
 * @param {Node} node
 *   DOM node to transform.
 * @param {Options} options
 *   Configuration.
 * @returns {HastNodes | undefined}
 *   Equivalent hast node.
 *
 *   Note that certain legacy DOM nodes (i.e., Attr nodes (2),  CDATA, processing instructions)
 */
function transform(node, options) {
	const transformed = one$1(node, options);
	if (transformed && options.afterTransform) options.afterTransform(node, transformed);
	return transformed;
}

/**
 * @param {Node} node
 *   DOM node to transform.
 * @param {Options} options
 *   Configuration.
 * @returns {HastNodes | undefined}
 *   Equivalent hast node.
 */
function one$1(node, options) {
	switch (node.nodeType) {
		case 1 /* Element */: {
			const domNode = /** @type {Element} */ (node);
			return element$1(domNode, options);
		}

		// Ignore: Attr (2).

		case 3 /* Text */: {
			const domNode = /** @type {Text} */ (node);
			return text$4(domNode);
		}

		// Ignore: CDATA (4).
		// Removed: Entity reference (5)
		// Removed: Entity (6)
		// Ignore: Processing instruction (7).

		case 8 /* Comment */: {
			const domNode = /** @type {Comment} */ (node);
			return comment$1(domNode);
		}

		case 9 /* Document */: {
			const domNode = /** @type {Document} */ (node);
			return root$2(domNode, options);
		}

		case 10 /* Document type */: {
			return doctype$1();
		}

		case 11 /* Document fragment */: {
			const domNode = /** @type {DocumentFragment} */ (node);
			return root$2(domNode, options);
		}

		default: {
			return undefined;
		}
	}
}

/**
 * Transform a document.
 *
 * @param {Document | DocumentFragment} node
 *   DOM node to transform.
 * @param {Options} options
 *   Configuration.
 * @returns {HastRoot}
 *   Equivalent hast node.
 */
function root$2(node, options) {
	return { type: "root", children: all$1(node, options) };
}

/**
 * Transform a doctype.
 *
 * @returns {HastDoctype}
 *   Equivalent hast node.
 */
function doctype$1() {
	return { type: "doctype" };
}

/**
 * Transform a text.
 *
 * @param {Text} node
 *   DOM node to transform.
 * @returns {HastText}
 *   Equivalent hast node.
 */
function text$4(node) {
	return { type: "text", value: node.nodeValue || "" };
}

/**
 * Transform a comment.
 *
 * @param {Comment} node
 *   DOM node to transform.
 * @returns {HastComment}
 *   Equivalent hast node.
 */
function comment$1(node) {
	return { type: "comment", value: node.nodeValue || "" };
}

/**
 * Transform an element.
 *
 * @param {Element} node
 *   DOM node to transform.
 * @param {Options} options
 *   Configuration.
 * @returns {HastElement}
 *   Equivalent hast node.
 */
function element$1(node, options) {
	const space = node.namespaceURI;
	const x = space === webNamespaces.svg ? s$1 : h$2;
	const tagName = space === webNamespaces.html ? node.tagName.toLowerCase() : node.tagName;
	/** @type {DocumentFragment | Element} */
	const content =
		// @ts-expect-error: DOM types are wrong, content can exist.
		space === webNamespaces.html && tagName === "template" ? node.content : node;
	const attributes = node.getAttributeNames();
	/** @type {Record<string, string>} */
	const properties = {};
	let index = -1;

	while (++index < attributes.length) {
		properties[attributes[index]] = node.getAttribute(attributes[index]) || "";
	}

	return x(tagName, properties, all$1(content, options));
}

/**
 * Transform child nodes in a parent.
 *
 * @param {Document | DocumentFragment | Element} node
 *   DOM node to transform.
 * @param {Options} options
 *   Configuration.
 * @returns {Array<HastRootContent>}
 *   Equivalent hast nodes.
 */
function all$1(node, options) {
	const nodes = node.childNodes;
	/** @type {Array<HastRootContent>} */
	const children = [];
	let index = -1;

	while (++index < nodes.length) {
		const child = transform(nodes[index], options);

		if (child !== undefined) {
			// @ts-expect-error Assume no document inside document.
			children.push(child);
		}
	}

	return children;
}

var Ae$1 = Object.defineProperty;
var Le$1 = (n, e, o) => (e in n ? Ae$1(n, e, { enumerable: true, configurable: true, writable: true, value: o }) : (n[e] = o));
var k$2 = (n, e, o) => Le$1(n, typeof e != "symbol" ? e + "" : e, o);
function xe$1(n) {
	const e = Array.from(n.classList).filter((o) => !o.startsWith("bn-")) || [];
	e.length > 0 ? (n.className = e.join(" ")) : n.removeAttribute("class");
}
function Ee$1(n, e, o, t) {
	var a;
	let r;
	if (e)
		if (typeof e == "string") r = T$1([e], n.pmSchema);
		else if (Array.isArray(e)) r = T$1(e, n.pmSchema);
		else if (e.type === "tableContent") r = kt$1(e, n.pmSchema);
		else throw new O(e.type);
	else throw new Error("blockContent is required");
	const i = ((t == null ? void 0 : t.document) ?? document).createDocumentFragment();
	for (const c of r)
		if (c.type.name !== "text" && n.schema.inlineContentSchema[c.type.name]) {
			const l = n.schema.inlineContentSpecs[c.type.name].implementation;
			if (l) {
				const u = wt$1(c, n.schema.inlineContentSchema, n.schema.styleSchema),
					h =
						l.toExternalHTML ?
							l.toExternalHTML(u, n)
						:	l.render.call(
								{
									renderType: "dom",
									props: void 0,
								},
								u,
								() => {},
								n,
							);
				if (h) {
					if ((i.appendChild(h.dom), h.contentDOM)) {
						const f = o.serializeFragment(c.content, t);
						((h.contentDOM.dataset.editable = ""), h.contentDOM.appendChild(f));
					}
					continue;
				}
			}
		} else if (c.type.name === "text") {
			let l = document.createTextNode(c.textContent);
			for (const u of c.marks.toReversed())
				if (u.type.name in n.schema.styleSpecs) {
					const h = (n.schema.styleSpecs[u.type.name].implementation.toExternalHTML ?? n.schema.styleSpecs[u.type.name].implementation.render)(u.attrs.stringValue, n);
					(h.contentDOM.appendChild(l), (l = h.dom));
				} else {
					const h = u.type.spec.toDOM(u, true),
						f = DOMSerializer.renderSpec(document, h);
					(f.contentDOM.appendChild(l), (l = f.dom));
				}
			i.appendChild(l);
		} else {
			const l = o.serializeFragment(Fragment.from([c]), t);
			i.appendChild(l);
		}
	return (i.childNodes.length === 1 && ((a = i.firstChild) == null ? void 0 : a.nodeType) === 1 && xe$1(i.firstChild), i);
}
function Bt(n, e, o, t, r, s, i, a) {
	var b, y, w, v, x, I, O, M, T;
	const c = (a == null ? void 0 : a.document) ?? document,
		l = e.pmSchema.nodes.blockContainer,
		u = o.props || {};
	for (const [C, S] of Object.entries(e.schema.blockSchema[o.type].propSchema)) !(C in u) && S.default !== void 0 && (u[C] = S.default);
	const h =
			(y = (b = l.spec) == null ? void 0 : b.toDOM) == null ?
				void 0
			:	y.call(
					b,
					l.create({
						id: o.id,
						...u,
					}),
				),
		f = Array.from(h.dom.attributes),
		m = e.blockImplementations[o.type].implementation,
		p =
			((w = m.toExternalHTML) == null ?
				void 0
			:	w.call({}, { ...o, props: u }, e, {
					nestingLevel: i,
				})) || m.render.call({}, { ...o, props: u }, e),
		d = c.createDocumentFragment();
	if (p.dom.classList.contains("bn-block-content")) {
		const C = [...f, ...Array.from(p.dom.attributes)].filter(
			(S) =>
				S.name.startsWith("data") &&
				S.name !== "data-content-type" &&
				S.name !== "data-file-block" &&
				S.name !== "data-node-view-wrapper" &&
				S.name !== "data-node-type" &&
				S.name !== "data-id" &&
				S.name !== "data-editable",
		);
		for (const S of C) p.dom.firstChild.setAttribute(S.name, S.value);
		(xe$1(p.dom.firstChild), i > 0 && p.dom.firstChild.setAttribute("data-nesting-level", i.toString()), d.append(...Array.from(p.dom.childNodes)));
	} else (d.append(p.dom), i > 0 && p.dom.setAttribute("data-nesting-level", i.toString()));
	if (p.contentDOM && o.content) {
		const C = Ee$1(
			e,
			o.content,
			// TODO
			t,
			a,
		);
		p.contentDOM.appendChild(C);
	}
	let g;
	if ((r.has(o.type) ? (g = "OL") : s.has(o.type) && (g = "UL"), g)) {
		if (((v = n.lastChild) == null ? void 0 : v.nodeName) !== g) {
			const C = c.createElement(g);
			(g === "OL" && "start" in u && u.start && (u == null ? void 0 : u.start) !== 1 && C.setAttribute("start", u.start + ""), n.append(C));
		}
		n.lastChild.appendChild(d);
	} else n.append(d);
	if (o.children && o.children.length > 0) {
		const C = c.createDocumentFragment();
		if ((Ie$1(C, e, o.children, t, r, s, i + 1, a), ((x = n.lastChild) == null ? void 0 : x.nodeName) === "UL" || ((I = n.lastChild) == null ? void 0 : I.nodeName) === "OL"))
			for (; ((O = C.firstChild) == null ? void 0 : O.nodeName) === "UL" || ((M = C.firstChild) == null ? void 0 : M.nodeName) === "OL"; ) n.lastChild.lastChild.appendChild(C.firstChild);
		"childrenDOM" in p && p.childrenDOM ? p.childrenDOM.append(C)
		: e.pmSchema.nodes[o.type].isInGroup("blockContent") ? n.append(C)
		: (T = p.contentDOM) == null || T.append(C);
	}
}
const Ie$1 = (n, e, o, t, r, s, i = 0, a) => {
		for (const c of o) Bt(n, e, c, t, r, s, i, a);
	},
	Tt = (n, e, o, t, r, s) => {
		const a = ((s == null ? void 0 : s.document) ?? document).createDocumentFragment();
		return (Ie$1(a, n, e, o, t, r, 0, s), a);
	},
	Be = (n, e) => {
		const o = DOMSerializer.fromSchema(n);
		return {
			exportBlocks: (t, r) => {
				const s = Tt(e, t, o, /* @__PURE__ */ new Set(["numberedListItem"]), /* @__PURE__ */ new Set(["bulletListItem", "checkListItem", "toggleListItem"]), r),
					i = document.createElement("div");
				return (i.append(s), i.innerHTML);
			},
			exportInlineContent: (t, r) => {
				const s = Ee$1(e, t, o, r),
					i = document.createElement("div");
				return (i.append(s.cloneNode(true)), i.innerHTML);
			},
		};
	};
function Dt(n, e) {
	if (e === 0) return;
	const o = n.resolve(e);
	for (let t = o.depth; t > 0; t--) {
		const r = o.node(t);
		if (At$1(r)) return r.attrs.id;
	}
}
function Ot(n) {
	return (
		n.getMeta("paste") ? { type: "paste" }
		: n.getMeta("uiEvent") === "drop" ? { type: "drop" }
		: n.getMeta("history$") ?
			{
				type: n.getMeta("history$").redo ? "redo" : "undo",
			}
		: n.getMeta("y-sync$") ?
			n.getMeta("y-sync$").isUndoRedoOperation ?
				{ type: "undo-redo" }
			:	{ type: "yjs-remote" }
		:	{ type: "local" }
	);
}
function se$1(n) {
	const e = "__root__",
		o = {},
		t = {},
		r = ht(n);
	return (
		n.descendants((s, i) => {
			if (!At$1(s)) return true;
			const a = Dt(n, i),
				c = a ?? e;
			t[c] || (t[c] = []);
			const l = L$2(s, r);
			return ((o[s.attrs.id] = { block: l, parentId: a }), t[c].push(s.attrs.id), true);
		}),
		{ byId: o, childrenByParent: t }
	);
}
function Pt(n, e) {
	const o = /* @__PURE__ */ new Set();
	if (!n || !e) return o;
	const t = new Set(n),
		r = e.filter((d) => t.has(d)),
		s = n.filter((d) => r.includes(d));
	if (s.length <= 1 || r.length <= 1) return o;
	const i = {};
	for (let d = 0; d < s.length; d++) i[s[d]] = d;
	const a = r.map((d) => i[d]),
		c = a.length,
		l = [],
		u = [],
		h = new Array(c).fill(-1),
		f = (d, g) => {
			let b = 0,
				y = d.length;
			for (; b < y; ) {
				const w = (b + y) >>> 1;
				d[w] < g ? (b = w + 1) : (y = w);
			}
			return b;
		};
	for (let d = 0; d < c; d++) {
		const g = a[d],
			b = f(l, g);
		(b > 0 && (h[d] = u[b - 1]), b === l.length ? (l.push(g), u.push(d)) : ((l[b] = g), (u[b] = d)));
	}
	const m = /* @__PURE__ */ new Set();
	let p = u[u.length - 1] ?? -1;
	for (; p !== -1; ) (m.add(p), (p = h[p]));
	for (let d = 0; d < r.length; d++) m.has(d) || o.add(r[d]);
	return o;
}
function Mt(n, e = []) {
	const o = Ot(n),
		t = combineTransactionSteps(n.before, [n, ...e]),
		r = se$1(t.before),
		s = se$1(t.doc),
		i = [],
		a = /* @__PURE__ */ new Set();
	(Object.keys(s.byId)
		.filter((m) => !(m in r.byId))
		.forEach((m) => {
			(i.push({
				type: "insert",
				block: s.byId[m].block,
				source: o,
				prevBlock: void 0,
			}),
				a.add(m));
		}),
		Object.keys(r.byId)
			.filter((m) => !(m in s.byId))
			.forEach((m) => {
				(i.push({
					type: "delete",
					block: r.byId[m].block,
					source: o,
					prevBlock: void 0,
				}),
					a.add(m));
			}),
		Object.keys(s.byId)
			.filter((m) => m in r.byId)
			.forEach((m) => {
				var b, y;
				const p = r.byId[m],
					d = s.byId[m];
				p.parentId !== d.parentId ?
					(i.push({
						type: "move",
						block: d.block,
						prevBlock: p.block,
						source: o,
						prevParent:
							p.parentId ?
								(b = r.byId[p.parentId]) == null ?
									void 0
								:	b.block
							:	void 0,
						currentParent:
							d.parentId ?
								(y = s.byId[d.parentId]) == null ?
									void 0
								:	y.block
							:	void 0,
					}),
					a.add(m))
				:	He({ ...p.block, children: void 0 }, { ...d.block, children: void 0 }) ||
					(i.push({
						type: "update",
						block: d.block,
						prevBlock: p.block,
						source: o,
					}),
					a.add(m));
			}));
	const c = r.childrenByParent,
		l = s.childrenByParent,
		u = "__root__",
		h = /* @__PURE__ */ new Set([...Object.keys(c), ...Object.keys(l)]),
		f = /* @__PURE__ */ new Set();
	return (
		h.forEach((m) => {
			const p = Pt(c[m], l[m]);
			p.size !== 0 &&
				p.forEach((d) => {
					var w, v;
					const g = r.byId[d],
						b = s.byId[d];
					!g ||
						!b ||
						g.parentId !== b.parentId ||
						a.has(d) ||
						(g.parentId ?? u) !== m ||
						f.has(d) ||
						(f.add(d),
						i.push({
							type: "move",
							block: b.block,
							prevBlock: g.block,
							source: o,
							prevParent:
								g.parentId ?
									(w = r.byId[g.parentId]) == null ?
										void 0
									:	w.block
								:	void 0,
							currentParent:
								b.parentId ?
									(v = s.byId[b.parentId]) == null ?
										void 0
									:	v.block
								:	void 0,
						}),
						a.add(d));
				});
		}),
		i
	);
}
const Do$1 = a$1(() => {
	const n = [];
	return {
		key: "blockChange",
		prosemirrorPlugins: [
			new Plugin({
				key: new PluginKey("blockChange"),
				filterTransaction: (e) => {
					let o;
					return n.reduce(
						(t, r) =>
							t === false ? t : (
								r({
									getChanges() {
										return o || ((o = Mt(e)), o);
									},
									tr: e,
								}) !== false
							),
						true,
					);
				},
			}),
		],
		/**
		 * Subscribe to the block change events.
		 */
		subscribe(e) {
			return (
				n.push(e),
				() => {
					n.splice(n.indexOf(e), 1);
				}
			);
		},
	};
});
function ie(n) {
	const e = n.charAt(0) === "#" ? n.substring(1, 7) : n,
		o = parseInt(e.substring(0, 2), 16),
		t = parseInt(e.substring(2, 4), 16),
		r = parseInt(e.substring(4, 6), 16),
		i = [o / 255, t / 255, r / 255].map((c) => (c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4)));
	return 0.2126 * i[0] + 0.7152 * i[1] + 0.0722 * i[2] <= 0.179;
}
function At(n) {
	const e = document.createElement("span");
	e.classList.add("bn-collaboration-cursor__base");
	const o = document.createElement("span");
	(o.setAttribute("contentedEditable", "false"),
		o.classList.add("bn-collaboration-cursor__caret"),
		o.setAttribute("style", `background-color: ${n.color}; color: ${ie(n.color) ? "white" : "black"}`));
	const t = document.createElement("span");
	return (
		t.classList.add("bn-collaboration-cursor__label"),
		t.setAttribute("style", `background-color: ${n.color}; color: ${ie(n.color) ? "white" : "black"}`),
		t.insertBefore(document.createTextNode(n.name), null),
		o.insertBefore(t, null),
		e.insertBefore(document.createTextNode("⁠"), null),
		e.insertBefore(o, null),
		e.insertBefore(document.createTextNode("⁠"), null),
		e
	);
}
const ae$1 = a$1(({ options: n }) => {
		const e = /* @__PURE__ */ new Map(),
			o = n.provider && "awareness" in n.provider && typeof n.provider.awareness == "object" ? n.provider.awareness : void 0;
		return (
			o &&
				("setLocalStateField" in o && typeof o.setLocalStateField == "function" && o.setLocalStateField("user", n.user),
				"on" in o &&
					typeof o.on == "function" &&
					n.showCursorLabels !== "always" &&
					o.on("change", ({ updated: t }) => {
						for (const r of t) {
							const s = e.get(r);
							s &&
								(setTimeout(() => {
									s.element.setAttribute("data-active", "");
								}, 10),
								s.hideTimeout && clearTimeout(s.hideTimeout),
								e.set(r, {
									element: s.element,
									hideTimeout: setTimeout(() => {
										s.element.removeAttribute("data-active");
									}, 2e3),
								}));
						}
					})),
			{
				key: "yCursor",
				prosemirrorPlugins: [
					o ?
						yCursorPlugin(o, {
							selectionBuilder: defaultSelectionBuilder,
							cursorBuilder(t, r) {
								let s = e.get(r);
								if (!s) {
									const i = (n.renderCursor ?? At)(t);
									(n.showCursorLabels !== "always" &&
										(i.addEventListener("mouseenter", () => {
											const a = e.get(r);
											(a.element.setAttribute("data-active", ""),
												a.hideTimeout &&
													(clearTimeout(a.hideTimeout),
													e.set(r, {
														element: a.element,
														hideTimeout: void 0,
													})));
										}),
										i.addEventListener("mouseleave", () => {
											const a = e.get(r);
											e.set(r, {
												element: a.element,
												hideTimeout: setTimeout(() => {
													a.element.removeAttribute("data-active");
												}, 2e3),
											});
										})),
										(s = {
											element: i,
											hideTimeout: void 0,
										}),
										e.set(r, s));
								}
								return s.element;
							},
						})
					:	void 0,
				].filter(Boolean),
				dependsOn: ["ySync"],
				updateUser(t) {
					o == null || o.setLocalStateField("user", t);
				},
			}
		);
	}),
	K = a$1(({ options: n }) => ({
		key: "ySync",
		prosemirrorPlugins: [ySyncPlugin(n.fragment)],
		runsBefore: ["default"],
	})),
	X$1 = a$1(() => ({
		key: "yUndo",
		prosemirrorPlugins: [yUndoPlugin()],
		dependsOn: ["yCursor", "ySync"],
		undoCommand: undoCommand,
		redoCommand: redoCommand,
	}));
function Lt(n, e) {
	const o = n.doc;
	if (n._item === null) {
		const t = Array.from(o.share.keys()).find((r) => o.share.get(r) === n);
		if (t == null) throw new Error("type does not exist in other ydoc");
		return e.get(t, n.constructor);
	} else {
		const t = n._item,
			r = e.store.clients.get(t.id.client) ?? [],
			s = findIndexSS(r, t.id.clock);
		return r[s].content.type;
	}
}
const Oo$1 = a$1(({ editor: n, options: e }) => {
		let o;
		const t = f$2({ isForked: false });
		return {
			key: "yForkDoc",
			store: t,
			/**
			 * Fork the Y.js document from syncing to the remote,
			 * allowing modifications to the document without affecting the remote.
			 * These changes can later be rolled back or applied to the remote.
			 */
			fork() {
				if (o) return;
				const r = e.fragment;
				if (!r) throw new Error("No fragment to fork from");
				const s = new Doc();
				applyUpdate(s, encodeStateAsUpdate(r.doc));
				const i = Lt(r, s);
				((o = {
					undoStack: yUndoPluginKey.getState(n.prosemirrorState).undoManager.undoStack,
					originalFragment: r,
					forkedFragment: i,
				}),
					n.unregisterExtension([X$1, ae$1, K]));
				const a = {
					...e,
					fragment: i,
				};
				(n.registerExtension([
					K(a),
					// No need to register the cursor plugin again, it's a local fork
					X$1(),
				]),
					t.setState({ isForked: true }));
			},
			/**
			 * Resume syncing the Y.js document to the remote
			 * If `keepChanges` is true, any changes that have been made to the forked document will be applied to the original document.
			 * Otherwise, the original document will be restored and the changes will be discarded.
			 */
			merge({ keepChanges: r }) {
				if (!o) return;
				n.unregisterExtension(["ySync", "yCursor", "yUndo"]);
				const { originalFragment: s, forkedFragment: i, undoStack: a } = o;
				if ((n.registerExtension([K(e), ae$1(e), X$1()]), (yUndoPluginKey.getState(n.prosemirrorState).undoManager.undoStack = a), r)) {
					const c = encodeStateAsUpdate(i.doc, encodeStateVector(s.doc));
					applyUpdate(s.doc, c, n);
				}
				((o = void 0), t.setState({ isForked: false }));
			},
		};
	}),
	Te$1 = (n, e) => {
		(e(n),
			n.forEach((o) => {
				o instanceof YXmlElement && Te$1(o, e);
			}));
	},
	Nt = (n, e) => {
		const o = /* @__PURE__ */ new Map();
		return (
			n.forEach((t) => {
				t instanceof YXmlElement &&
					Te$1(t, (r) => {
						if (r.nodeName === "blockContainer" && r.hasAttribute("id")) {
							const s = r.getAttribute("textColor"),
								i = r.getAttribute("backgroundColor"),
								a = {
									textColor: s === m$1.textColor.default ? void 0 : s,
									backgroundColor: i === m$1.backgroundColor.default ? void 0 : i,
								};
							(a.textColor || a.backgroundColor) && o.set(r.getAttribute("id"), a);
						}
					});
			}),
			o.size === 0 ?
				false
			:	(e.doc.descendants((t, r) => {
					if (t.type.name === "blockContainer" && o.has(t.attrs.id)) {
						const s = e.doc.nodeAt(r + 1);
						if (!s) throw new Error("No element found");
						e.setNodeMarkup(r + 1, void 0, {
							// preserve existing attributes
							...s.attrs,
							// add the textColor and backgroundColor attributes
							...o.get(t.attrs.id),
						});
					}
				}),
				true)
		);
	},
	Rt = [Nt],
	Po$1 = a$1(({ options: n }) => {
		let e = false;
		const o = new PluginKey("schemaMigration");
		return {
			key: "schemaMigration",
			prosemirrorPlugins: [
				new Plugin({
					key: o,
					appendTransaction: (t, r, s) => {
						if (
							e || // If any of the transactions are not due to a yjs sync, we don't need to run the migration
							!t.some((a) => a.getMeta("y-sync$")) || // If none of the transactions result in a document change, we don't need to run the migration
							t.every((a) => !a.docChanged) || // If the fragment is still empty, we can't run the migration (since it has not yet been applied to the Y.Doc)
							!n.fragment.firstChild
						)
							return;
						const i = s.tr;
						for (const a of Rt) a(n.fragment, i);
						if (((e = true), !!i.docChanged)) return i;
					},
				}),
			],
		};
	});
function le$1(n, e) {
	return !n || !e ? false : !!n.closest(`.${e}`);
}
function Vt(n, e, o, t, r) {
	const s = n.state.doc.resolve(e.pos);
	if (!!s.parent.inlineContent || e.orientation === "inline") return null;
	const a = s.nodeBefore,
		c = s.nodeAfter;
	if (!a && !c) return null;
	const l = e.orientation === "block-vertical-left" || e.orientation === "block-vertical-right",
		u = l ? e.pos : e.pos - (a ? a.nodeSize : 0),
		h = n.nodeDOM(u);
	if (!h) return null;
	const f = h.getBoundingClientRect();
	if (l) {
		const d = (o / 2) * t,
			g = e.orientation === "block-vertical-left" ? f.left : f.right;
		return {
			left: g - d,
			right: g + d,
			top: f.top,
			bottom: f.bottom,
		};
	}
	let m = a ? f.bottom : f.top;
	a && c && (m = (m + n.nodeDOM(e.pos).getBoundingClientRect().top) / 2);
	const p = (o / 2) * r;
	return {
		left: f.left,
		right: f.right,
		top: m - p,
		bottom: m + p,
	};
}
function Ht(n, e, o, t) {
	const r = n.coordsAtPos(e.pos),
		s = (o / 2) * t;
	return {
		left: r.left - s,
		right: r.left + s,
		top: r.top,
		bottom: r.bottom,
	};
}
function $t(n, e) {
	(n.classList.toggle("prosemirror-dropcursor-inline", e === "inline"),
		n.classList.toggle("prosemirror-dropcursor-block-horizontal", e === "block-horizontal"),
		n.classList.toggle("prosemirror-dropcursor-block-vertical-left", e === "block-vertical-left"),
		n.classList.toggle("prosemirror-dropcursor-block-vertical-right", e === "block-vertical-right"),
		n.classList.toggle("prosemirror-dropcursor-block", e === "block-horizontal"),
		n.classList.toggle("prosemirror-dropcursor-vertical", e === "block-vertical-left" || e === "block-vertical-right"));
}
function Ft(n) {
	if (!n || (n === document.body && getComputedStyle(n).position === "static"))
		return {
			parentLeft: -window.pageXOffset,
			parentTop: -window.pageYOffset,
		};
	const e = n.getBoundingClientRect(),
		o = e.width / n.offsetWidth,
		t = e.height / n.offsetHeight;
	return {
		parentLeft: e.left - n.scrollLeft * o,
		parentTop: e.top - n.scrollTop * t,
	};
}
const Ut = "bn-drag-exclude",
	Mo$1 = a$1(({ editor: n, options: e }) => {
		var d, g, b, y;
		let o = null,
			t = null,
			r = -1,
			s = null;
		const i = {
				width: ((d = e.dropCursor) == null ? void 0 : d.width) ?? 5,
				color: ((g = e.dropCursor) == null ? void 0 : g.color) ?? "#ddeeff",
				exclude: ((b = e.dropCursor) == null ? void 0 : b.exclude) ?? Ut,
				hooks: (y = e.dropCursor) == null ? void 0 : y.hooks,
			},
			a = (w) => {
				((w == null ? void 0 : w.pos) === (o == null ? void 0 : o.pos) && (w == null ? void 0 : w.orientation) === (o == null ? void 0 : o.orientation)) ||
					((o = w), w == null ? (t && t.parentNode && t.parentNode.removeChild(t), (t = null)) : c());
			},
			c = () => {
				if (!o) return;
				const w = n.prosemirrorView,
					v = w.dom,
					x = v.getBoundingClientRect(),
					I = x.width / v.offsetWidth,
					O = x.height / v.offsetHeight,
					T = Vt(w, o, i.width, I, O) ?? Ht(w, o, i.width, I),
					C = w.dom.offsetParent;
				(t ||
					((t = C.appendChild(document.createElement("div"))), (t.style.cssText = "position: absolute; z-index: 50; pointer-events: none;"), i.color && (t.style.backgroundColor = i.color)),
					$t(t, o.orientation));
				const { parentLeft: S, parentTop: q } = Ft(C);
				((t.style.left = (T.left - S) / I + "px"), (t.style.top = (T.top - q) / O + "px"), (t.style.width = (T.right - T.left) / I + "px"), (t.style.height = (T.bottom - T.top) / O + "px"));
			},
			l = (w) => {
				(clearTimeout(r), (r = window.setTimeout(() => a(null), w)));
			},
			u = (w) => {
				const v = w;
				s = v.target instanceof Element ? v.target : null;
			},
			h = (w) => {
				var C;
				const v = w;
				if ((s && le$1(s, i.exclude)) || (v.target instanceof Element && le$1(v.target, i.exclude))) return;
				const x = n.prosemirrorView;
				if (!x.editable) return;
				const I = x.posAtCoords({
						left: v.clientX,
						top: v.clientY,
					}),
					O = I && I.inside >= 0 && x.state.doc.nodeAt(I.inside),
					M = O && O.type.spec.disableDropCursor,
					T = typeof M == "function" ? M(x, I, v) : M;
				if (I && !T) {
					let S = I.pos;
					if (x.dragging && x.dragging.slice) {
						const R = dropPoint(x.state.doc, S, x.dragging.slice);
						R != null && (S = R);
					}
					const Me = !x.state.doc.resolve(S).parent.inlineContent,
						G = {
							pos: S,
							orientation: Me ? "block-horizontal" : "inline",
						};
					let J = G;
					if ((C = i.hooks) != null && C.computeDropPosition) {
						const R = i.hooks.computeDropPosition({
							editor: n,
							event: v,
							view: x,
							defaultPosition: G,
						});
						if (R === null) {
							a(null);
							return;
						}
						J = R;
					}
					(a(J), l(5e3));
				}
			},
			f = (w) => {
				const v = w;
				(!(v.relatedTarget instanceof Node) || !n.prosemirrorView.dom.contains(v.relatedTarget)) && a(null);
			},
			m = () => {
				l(20);
			},
			p = () => {
				(l(20), (s = null));
			};
		return {
			key: "dropCursor",
			mount({ signal: w, dom: v, root: x }) {
				(x.addEventListener("dragstart", u, {
					capture: true,
					signal: w,
				}),
					v.addEventListener("dragover", h, { signal: w }),
					v.addEventListener("dragleave", f, { signal: w }),
					v.addEventListener("drop", m, { signal: w }),
					v.addEventListener("dragend", p, { signal: w }),
					w.addEventListener("abort", () => {
						(clearTimeout(r), a(null));
					}));
			},
		};
	}),
	Ao$1 = a$1(({ editor: n }) => {
		const e = f$2(false),
			o = () =>
				n.transact((t) => {
					var s;
					if (
						t.selection.empty ||
						(t.selection instanceof NodeSelection &&
							(t.selection.node.type.spec.content === "inline*" || ((s = t.selection.node.firstChild) == null ? void 0 : s.type.spec.content) === "inline*")) ||
						(t.selection instanceof TextSelection && t.doc.textBetween(t.selection.from, t.selection.to).length === 0)
					)
						return false;
					let r = false;
					return (t.selection.content().content.descendants((i) => (i.type.spec.code && (r = true), !r)), !r);
				});
		return {
			key: "formattingToolbar",
			store: e,
			mount({ dom: t, signal: r }) {
				let s = false;
				const i = n.onChange(() => {
						s || e.setState(o());
					}),
					a = n.onSelectionChange(() => {
						s || e.setState(o());
					});
				(t.addEventListener(
					"pointerdown",
					() => {
						((s = true), e.setState(false));
					},
					{ signal: r },
				),
					n.prosemirrorView.root.addEventListener(
						"pointerup",
						() => {
							((s = false), n.isFocused() && e.setState(o()));
						},
						{ signal: r, capture: true },
					),
					t.addEventListener(
						"pointercancel",
						() => {
							s = false;
						},
						{
							signal: r,
							capture: true,
						},
					),
					r.addEventListener("abort", () => {
						(i(), a());
					}));
			},
		};
	}),
	Lo$1 = a$1(() => ({
		key: "history",
		prosemirrorPlugins: [history()],
		undoCommand: undo$1,
		redoCommand: redo$1,
	})),
	No$1 = a$1(({ editor: n }) => {
		function e(r) {
			let s = n.prosemirrorView.nodeDOM(r);
			for (; s && s.parentElement; ) {
				if (s.nodeName === "A") return s;
				s = s.parentElement;
			}
			return null;
		}
		function o(r, s) {
			return n.transact((i) => {
				const a = i.doc.resolve(r),
					c = a.marks().find((u) => u.type.name === s);
				if (!c) return;
				const l = getMarkRange(a, c.type);
				if (l)
					return {
						range: l,
						mark: c,
						get text() {
							return i.doc.textBetween(l.from, l.to);
						},
						get position() {
							return posToDOMRect(n.prosemirrorView, l.from, l.to).toJSON();
						},
					};
			});
		}
		function t() {
			return n.transact((r) => {
				const s = r.selection;
				if (s.empty) return o(s.anchor, "link");
			});
		}
		return {
			key: "linkToolbar",
			getLinkAtSelection: t,
			getLinkElementAtPos: e,
			getMarkAtPos: o,
			getLinkAtElement(r) {
				return n.transact(() => {
					const s = n.prosemirrorView.posAtDOM(r, 0) + 1;
					return o(s, "link");
				});
			},
			editLink(r, s, i = n.transact((a) => a.selection.anchor)) {
				(n.transact((a) => {
					const c = ht(a),
						{ range: l } = o(i + 1, "link") || {
							range: {
								from: a.selection.from,
								to: a.selection.to,
							},
						};
					l && (a.insertText(s, l.from, l.to), a.addMark(l.from, l.from + s.length, c.mark("link", { href: r })));
				}),
					n.prosemirrorView.focus());
			},
			deleteLink(r = n.transact((s) => s.selection.anchor)) {
				(n.transact((s) => {
					const i = ht(s),
						{ range: a } = o(r + 1, "link") || {
							range: {
								from: s.selection.from,
								to: s.selection.to,
							},
						};
					a && s.removeMark(a.from, a.to, i.marks.link).setMeta("preventAutolink", true);
				}),
					n.prosemirrorView.focus());
			},
		};
	}),
	Ro$1 = ["http", "https", "ftp", "ftps", "mailto", "tel", "callto", "sms", "cid", "xmpp"],
	Vo$1 = "https",
	_t = new PluginKey("node-selection-keyboard"),
	Ho$1 = a$1(() => ({
		key: "nodeSelectionKeyboard",
		prosemirrorPlugins: [
			new Plugin({
				key: _t,
				props: {
					handleKeyDown: (n, e) => {
						if ("node" in n.state.selection) {
							if (e.ctrlKey || e.metaKey) return false;
							if (e.key.length === 1) return (e.preventDefault(), true);
							if (e.key === "Enter" && !e.isComposing && !e.shiftKey && !e.altKey && !e.ctrlKey && !e.metaKey) {
								const o = n.state.tr;
								return (
									n.dispatch(
										o
											.insert(n.state.tr.selection.$to.after(), n.state.schema.nodes.paragraph.createChecked())
											.setSelection(new TextSelection(o.doc.resolve(n.state.tr.selection.$to.after() + 1))),
									),
									true
								);
							}
						}
						return false;
					},
				},
			}),
		],
	})),
	zt = new PluginKey("blocknote-placeholder"),
	$o$1 = a$1(({ editor: n, options: e }) => {
		const o = e.placeholders;
		return {
			key: "placeholder",
			prosemirrorPlugins: [
				new Plugin({
					key: zt,
					view: (t) => {
						const r = `placeholder-selector-${v4()}`;
						t.dom.classList.add(r);
						const s = document.createElement("style"),
							i = n._tiptapEditor.options.injectNonce;
						(i && s.setAttribute("nonce", i), t.root instanceof window.ShadowRoot ? t.root.append(s) : t.root.head.appendChild(s));
						const a = s.sheet,
							c = (l = "") => `.${r} .bn-block-content${l} .bn-inline-content:has(> .ProseMirror-trailingBreak:only-child):before`;
						try {
							const { default: l, emptyDocument: u, ...h } = o || {};
							for (const [p, d] of Object.entries(h)) {
								const g = `[data-content-type="${p}"]`;
								a.insertRule(`${c(g)} { content: ${JSON.stringify(d)}; }`);
							}
							const f = "[data-is-only-empty-block]",
								m = "[data-is-empty-and-focused]";
							(a.insertRule(`${c(f)} { content: ${JSON.stringify(u)}; }`), a.insertRule(`${c(m)} { content: ${JSON.stringify(l)}; }`));
						} catch (l) {
							console.warn(
								"Failed to insert placeholder CSS rule - this is likely due to the browser not supporting certain CSS pseudo-element selectors (:has, :only-child:, or :before)",
								l,
							);
						}
						return {
							destroy: () => {
								t.root instanceof window.ShadowRoot ? t.root.removeChild(s) : t.root.head.removeChild(s);
							},
						};
					},
					props: {
						decorations: (t) => {
							const { doc: r, selection: s } = t;
							if (!n.isEditable || !s.empty || s.$from.parent.type.spec.code) return;
							const i = [];
							t.doc.content.size === 6 &&
								i.push(
									Decoration.node(2, 4, {
										"data-is-only-empty-block": "true",
									}),
								);
							const a = s.$anchor,
								c = a.parent;
							if (c.content.size === 0) {
								const l = a.before();
								i.push(
									Decoration.node(l, l + c.nodeSize, {
										"data-is-empty-and-focused": "true",
									}),
								);
							}
							return DecorationSet.create(r, i);
						},
					},
				}),
			],
		};
	}),
	ce$1 = new PluginKey("previous-blocks"),
	Yt = {
		// Numbered List Items
		"index": "index",
		// Headings
		"level": "level",
		// All Blocks
		"type": "type",
		"depth": "depth",
		"depth-change": "depth-change",
	},
	Fo$1 = a$1(() => {
		let n;
		return {
			key: "previousBlockType",
			prosemirrorPlugins: [
				new Plugin({
					key: ce$1,
					view(e) {
						return {
							update: async (o, t) => {
								var r;
								((r = this.key) == null ? void 0 : r.getState(o.state).updatedBlocks.size) > 0 &&
									(n = setTimeout(() => {
										o.dispatch(o.state.tr.setMeta(ce$1, { clearUpdate: true }));
									}, 0));
							},
							destroy: () => {
								n && clearTimeout(n);
							},
						};
					},
					state: {
						init() {
							return {
								// Block attributes, by block ID, from just before the previous transaction.
								prevTransactionOldBlockAttrs: {},
								// Block attributes, by block ID, from just before the current transaction.
								currentTransactionOldBlockAttrs: {},
								// Set of IDs of blocks whose attributes changed from the current transaction.
								updatedBlocks: /* @__PURE__ */ new Set(),
							};
						},
						apply(e, o, t, r) {
							if (((o.currentTransactionOldBlockAttrs = {}), o.updatedBlocks.clear(), !e.docChanged || t.doc.eq(r.doc))) return o;
							const s = {},
								i = findChildren(t.doc, (l) => l.attrs.id),
								a = new Map(i.map((l) => [l.node.attrs.id, l])),
								c = findChildren(r.doc, (l) => l.attrs.id);
							for (const l of c) {
								const u = a.get(l.node.attrs.id),
									h = u == null ? void 0 : u.node.firstChild,
									f = l.node.firstChild;
								if (u && h && f) {
									const m = {
											index: f.attrs.index,
											level: f.attrs.level,
											type: f.type.name,
											depth: r.doc.resolve(l.pos).depth,
										},
										p = {
											index: h.attrs.index,
											level: h.attrs.level,
											type: h.type.name,
											depth: t.doc.resolve(u.pos).depth,
										};
									((s[l.node.attrs.id] = p),
										(o.currentTransactionOldBlockAttrs[l.node.attrs.id] = p),
										JSON.stringify(p) !== JSON.stringify(m) && ((p["depth-change"] = p.depth - m.depth), o.updatedBlocks.add(l.node.attrs.id)));
								}
							}
							return ((o.prevTransactionOldBlockAttrs = s), o);
						},
					},
					props: {
						decorations(e) {
							const o = this.getState(e);
							if (o.updatedBlocks.size === 0) return;
							const t = [];
							return (
								e.doc.descendants((r, s) => {
									if (!r.attrs.id || !o.updatedBlocks.has(r.attrs.id)) return;
									const i = o.currentTransactionOldBlockAttrs[r.attrs.id],
										a = {};
									for (const [l, u] of Object.entries(i)) a["data-prev-" + Yt[l]] = u || "none";
									const c = Decoration.node(s, s + r.nodeSize, {
										...a,
									});
									t.push(c);
								}),
								DecorationSet.create(e.doc, t)
							);
						},
					},
				}),
			],
		};
	});
function De$1(n, e) {
	var o, t;
	for (; n && n.parentElement && n.parentElement !== e.dom && ((o = n.getAttribute) == null ? void 0 : o.call(n, "data-node-type")) !== "blockContainer"; ) n = n.parentElement;
	if (((t = n.getAttribute) == null ? void 0 : t.call(n, "data-node-type")) === "blockContainer") return { node: n, id: n.getAttribute("data-id") };
}
function jt() {
	const n = (e) => {
		let o = e.children.length;
		for (let t = 0; t < o; t++) {
			const r = e.children[t];
			if (r.type === "element" && (n(r), r.tagName === "u"))
				if (r.children.length > 0) {
					e.children.splice(t, 1, ...r.children);
					const s = r.children.length - 1;
					((o += s), (t += s));
				} else (e.children.splice(t, 1), o--, t--);
		}
	};
	return n;
}
function Kt() {
	const n = (e) => {
		var o;
		if (e.children && "length" in e.children && e.children.length)
			for (let t = e.children.length - 1; t >= 0; t--) {
				const r = e.children[t],
					s = t + 1 < e.children.length ? e.children[t + 1] : void 0;
				r.type === "element" && r.tagName === "input" && ((o = r.properties) == null ? void 0 : o.type) === "checkbox" && (s == null ? void 0 : s.type) === "element" && s.tagName === "p" ?
					((s.tagName = "span"), s.children.splice(0, 0, fromDom(document.createTextNode(" "))))
				:	n(r);
			}
	};
	return n;
}
function Xt() {
	return (n) => {
		visit(n, "element", (e, o, t) => {
			var r, s, i, a;
			if (t && e.tagName === "video") {
				const c = ((r = e.properties) == null ? void 0 : r.src) || ((s = e.properties) == null ? void 0 : s["data-url"]) || "",
					l = ((i = e.properties) == null ? void 0 : i.title) || ((a = e.properties) == null ? void 0 : a["data-name"]) || "";
				t.children[o] = {
					type: "text",
					value: `![${l}](${c})`,
				};
			}
		});
	};
}
function Oe$1(n) {
	return unified()
		.use(rehypeParse, { fragment: true })
		.use(Xt)
		.use(jt)
		.use(Kt)
		.use(rehypeRemark)
		.use(remarkGfm)
		.use(remarkStringify, {
			handlers: { text: (o) => o.value },
		})
		.processSync(n).value;
}
function Uo$1(n, e, o, t) {
	const s = Be(e, o).exportBlocks(n, t);
	return Oe$1(s);
}
function Wt(n) {
	const e = [];
	return (
		n.descendants((o) => {
			var r, s;
			const t = ht(o);
			return (
				o.type.name === "blockContainer" && ((r = o.firstChild) == null ? void 0 : r.type.name) === "blockGroup" ? true
				: o.type.name === "columnList" && o.childCount === 1 ?
					((s = o.firstChild) == null ||
						s.forEach((i) => {
							e.push(L$2(i, t));
						}),
					false)
				: o.type.isInGroup("bnBlock") ? (e.push(L$2(o, t)), false)
				: true
			);
		}),
		e
	);
}
let A$1 = class A extends Selection {
	constructor(o, t) {
		super(o, t);
		k$2(this, "nodes");
		const r = o.node();
		((this.nodes = []),
			o.doc.nodesBetween(o.pos, t.pos, (s, i, a) => {
				if (a !== null && a.eq(r)) return (this.nodes.push(s), false);
			}));
	}
	static create(o, t, r = t) {
		return new A(o.resolve(t), o.resolve(r));
	}
	content() {
		return new Slice(Fragment.from(this.nodes), 0, 0);
	}
	eq(o) {
		if (!(o instanceof A) || this.nodes.length !== o.nodes.length || this.from !== o.from || this.to !== o.to) return false;
		for (let t = 0; t < this.nodes.length; t++) if (!this.nodes[t].eq(o.nodes[t])) return false;
		return true;
	}
	map(o, t) {
		const r = t.mapResult(this.from),
			s = t.mapResult(this.to);
		return (
			s.deleted ? Selection.near(o.resolve(r.pos))
			: r.deleted ? Selection.near(o.resolve(s.pos))
			: new A(o.resolve(r.pos), o.resolve(s.pos))
		);
	}
	toJSON() {
		return { type: "multiple-node", anchor: this.anchor, head: this.head };
	}
};
Selection.jsonID("multiple-node", A$1);
let D$1;
function qt(n, e) {
	let o, t;
	const r = e.resolve(n.from).node().type.spec.group === "blockContent",
		s = e.resolve(n.to).node().type.spec.group === "blockContent",
		i = Math.min(n.$anchor.depth, n.$head.depth);
	if (r && s) {
		const a = n.$from.start(i - 1),
			c = n.$to.end(i - 1);
		((o = e.resolve(a - 1).pos), (t = e.resolve(c + 1).pos));
	} else ((o = n.from), (t = n.to));
	return { from: o, to: t };
}
function de$1(n, e, o = e) {
	e === o && (o += n.state.doc.resolve(e + 1).node().nodeSize);
	const t = n.domAtPos(e).node.cloneNode(true),
		r = n.domAtPos(e).node,
		s = (h, f) => Array.prototype.indexOf.call(h.children, f),
		i = s(
			r,
			// Expects from position to be just before the first selected block.
			n.domAtPos(e + 1).node.parentElement,
		),
		a = s(
			r,
			// Expects to position to be just after the last selected block.
			n.domAtPos(o - 1).node.parentElement,
		);
	for (let h = r.childElementCount - 1; h >= 0; h--) (h > a || h < i) && t.removeChild(t.children[h]);
	(Pe$1(n.root), (D$1 = t));
	const c = D$1.getElementsByTagName("iframe");
	for (let h = 0; h < c.length; h++) {
		const f = c[h],
			m = f.parentElement;
		m && m.removeChild(f);
	}
	const u = n.dom.className
		.split(" ")
		.filter((h) => h !== "ProseMirror" && h !== "bn-root" && h !== "bn-editor")
		.join(" ");
	((D$1.className = D$1.className + " bn-drag-preview " + u), n.root instanceof ShadowRoot ? n.root.appendChild(D$1) : n.root.body.appendChild(D$1));
}
function Pe$1(n) {
	D$1 !== void 0 && (n instanceof ShadowRoot ? n.removeChild(D$1) : n.body.removeChild(D$1), (D$1 = void 0));
}
function Gt(n, e, o) {
	if (!n.dataTransfer || o.headless) return;
	const t = o.prosemirrorView,
		r = Bt$1(e.id, t.state.doc);
	if (!r) throw new Error(`Block with ID ${e.id} not found`);
	const s = r.posBeforeNode;
	if (s != null) {
		const i = t.state.selection,
			a = t.state.doc,
			{ from: c, to: l } = qt(i, a),
			u = c <= s && s < l,
			h = i.$anchor.node() !== i.$head.node() || i instanceof A$1;
		u && h ? (t.dispatch(t.state.tr.setSelection(A$1.create(a, c, l))), de$1(t, c, l)) : (t.dispatch(t.state.tr.setSelection(NodeSelection.create(t.state.doc, s))), de$1(t, s));
		const f = t.state.selection.content(),
			m = o.pmSchema,
			p = t.serializeForClipboard(f).dom.innerHTML,
			d = Be(m, o),
			g = Wt(f.content),
			b = d.exportBlocks(g, {}),
			y = Oe$1(b);
		(n.dataTransfer.clearData(),
			n.dataTransfer.setData("blocknote/html", p),
			n.dataTransfer.setData("text/html", b),
			n.dataTransfer.setData("text/plain", y),
			(n.dataTransfer.effectAllowed = "move"),
			n.dataTransfer.setDragImage(D$1, 0, 0));
	}
}
const ue$1 = 250;
function W(n, e, o = true) {
	const t = n.root.elementsFromPoint(e.left, e.top);
	for (const r of t)
		if (n.dom.contains(r))
			return o && r.closest("[data-node-type=columnList]") ?
					W(
						n,
						{
							// TODO can we do better than this?
							left: e.left + 50,
							// bit hacky, but if we're inside a column, offset x position to right to account for the width of sidemenu itself
							top: e.top,
						},
						false,
					)
				:	De$1(r, n);
}
function Jt(n, e) {
	if (!e.dom.firstChild) return;
	const o = e.dom.firstChild.getBoundingClientRect(),
		t = {
			// Clamps the x position to the editor's bounding box.
			left: Math.min(Math.max(o.left + 10, n.x), o.right - 10),
			top: n.y,
		},
		r = W(e, t);
	if (!r) return;
	const s = r.node.getBoundingClientRect();
	return W(
		e,
		{
			left: s.right - 10,
			top: n.y,
		},
		false,
	);
}
class Qt {
	constructor(e, o, t) {
		k$2(this, "state");
		k$2(this, "emitUpdate");
		k$2(this, "mousePos");
		k$2(this, "hoveredBlock");
		k$2(this, "menuFrozen", false);
		k$2(this, "isDragOrigin", false);
		k$2(this, "updateState", (e) => {
			((this.state = e), this.emitUpdate(this.state));
		});
		k$2(this, "updateStateFromMousePos", () => {
			var t, r, s, i, a;
			if (this.menuFrozen || !this.mousePos) return;
			const e = this.findClosestEditorElement({
				clientX: this.mousePos.x,
				clientY: this.mousePos.y,
			});
			if ((e == null ? void 0 : e.element) !== this.pmView.dom || e.distance > ue$1) {
				(t = this.state) != null && t.show && ((this.state.show = false), this.updateState(this.state));
				return;
			}
			const o = Jt(this.mousePos, this.pmView);
			if (!o || !this.editor.isEditable) {
				(r = this.state) != null && r.show && ((this.state.show = false), this.updateState(this.state));
				return;
			}
			if (
				!(
					(s = this.state) != null &&
					s.show &&
					(i = this.hoveredBlock) != null &&
					i.hasAttribute("data-id") &&
					((a = this.hoveredBlock) == null ? void 0 : a.getAttribute("data-id")) === o.id
				) &&
				((this.hoveredBlock = o.node), this.editor.isEditable)
			) {
				const c = o.node.getBoundingClientRect(),
					l = o.node.closest("[data-node-type=column]");
				((this.state = {
					show: true,
					referencePos: new DOMRect(
						l ?
							// We take the first child as column elements have some default
							// padding. This is a little weird since this child element will
							// be the first block, but since it's always non-nested and we
							// only take the x coordinate, it's ok.
							l.firstElementChild.getBoundingClientRect().x
						:	this.pmView.dom.firstChild.getBoundingClientRect().x,
						c.y,
						c.width,
						c.height,
					),
					block: this.editor.getBlock(this.hoveredBlock.getAttribute("data-id")),
				}),
					this.updateState(this.state));
			}
		});
		/**
		 * If a block is being dragged, ProseMirror usually gets the context of what's
		 * being dragged from `view.dragging`, which is automatically set when a
		 * `dragstart` event fires in the editor. However, if the user tries to drag
		 * and drop blocks between multiple editors, only the one in which the drag
		 * began has that context, so we need to set it on the others manually. This
		 * ensures that PM always drops the blocks in between other blocks, and not
		 * inside them.
		 *
		 * After the `dragstart` event fires on the drag handle, it sets
		 * `blocknote/html` data on the clipboard. This handler fires right after,
		 * parsing the `blocknote/html` data into nodes and setting them on
		 * `view.dragging`.
		 *
		 * Note: Setting `view.dragging` on `dragover` would be better as the user
		 * could then drag between editors in different windows, but you can only
		 * access `dataTransfer` contents on `dragstart` and `drop` events.
		 */
		k$2(this, "onDragStart", (e) => {
			var i;
			const o = (i = e.dataTransfer) == null ? void 0 : i.getData("blocknote/html");
			if (!o || this.pmView.dragging) return;
			const t = document.createElement("div");
			t.innerHTML = o;
			const s = DOMParser$1.fromSchema(this.pmView.state.schema).parse(t, {
				topNode: this.pmView.state.schema.nodes.blockGroup.create(),
			});
			this.pmView.dragging = {
				slice: new Slice(s.content, 0, 0),
				move: true,
			};
		});
		/**
		 * Finds the closest editor visually to the given coordinates
		 */
		k$2(this, "findClosestEditorElement", (e) => {
			const o = Array.from(this.pmView.root.querySelectorAll(".bn-editor"));
			if (o.length === 0) return null;
			let t = o[0],
				r = Number.MAX_VALUE;
			return (
				o.forEach((s) => {
					const i = s.querySelector(".bn-block-group").getBoundingClientRect(),
						a =
							e.clientX < i.left ? i.left - e.clientX
							: e.clientX > i.right ? e.clientX - i.right
							: 0,
						c =
							e.clientY < i.top ? i.top - e.clientY
							: e.clientY > i.bottom ? e.clientY - i.bottom
							: 0,
						l = Math.sqrt(Math.pow(a, 2) + Math.pow(c, 2));
					l < r && ((r = l), (t = s));
				}),
				{
					element: t,
					distance: r,
				}
			);
		});
		/**
		 * This dragover event handler listens at the document level,
		 * and is trying to handle dragover events for all editors.
		 *
		 * It specifically is trying to handle the following cases:
		 *  - If the dragover event is within the bounds of any editor, then it does nothing
		 *  - If the dragover event is outside the bounds of any editor, but close enough (within DISTANCE_TO_CONSIDER_EDITOR_BOUNDS) to the closest editor,
		 *    then it dispatches a synthetic dragover event to the closest editor (which will trigger the drop-cursor to be shown on that editor)
		 *  - If the dragover event is outside the bounds of the current editor, then it will dispatch a synthetic dragleave event to the current editor
		 *    (which will trigger the drop-cursor to be removed from the current editor)
		 *
		 * The synthetic event is a necessary evil because we do not control prosemirror-dropcursor to be able to show the drop-cursor within the range we want
		 */
		k$2(this, "onDragOver", (e) => {
			var r;
			if (
				e.synthetic ||
				!(
					this.pmView.dragging !== null ||
					this.isDragOrigin ||
					((r = e.dataTransfer) == null ? void 0 : r.types.includes("blocknote/html")) ||
					(e.target instanceof Node && this.pmView.dom.contains(e.target))
				)
			)
				return;
			const t = this.getDragEventContext(e);
			if (!t || !t.isDropPoint) {
				this.closeDropCursor();
				return;
			}
			t.isDropPoint && !t.isDropWithinEditorBounds && this.dispatchSyntheticEvent(e);
		});
		/**
		 * Closes the drop-cursor for the current editor
		 */
		k$2(this, "closeDropCursor", () => {
			const e = new Event("dragleave", { bubbles: false });
			((e.synthetic = true), this.pmView.dom.dispatchEvent(e));
		});
		/**
		 * It is surprisingly difficult to determine the information we need to know about a drag event
		 *
		 * This function is trying to determine the following:
		 *  - Whether the current editor instance is the drop point
		 *  - Whether the current editor instance is the drag origin
		 *  - Whether the drop event is within the bounds of the current editor instance
		 */
		k$2(this, "getDragEventContext", (e) => {
			var l, u;
			if (
				!(
					this.pmView.dragging !== null ||
					this.isDragOrigin ||
					((l = e.dataTransfer) == null ? void 0 : l.types.includes("blocknote/html")) ||
					(e.target instanceof Node && this.pmView.dom.contains(e.target))
				)
			)
				return;
			const t = !((u = e.dataTransfer) != null && u.types.includes("blocknote/html")) && !!this.pmView.dragging,
				r = !!this.isDragOrigin,
				s = t || r,
				i = this.findClosestEditorElement(e);
			if (!i || i.distance > ue$1) return;
			const a = i.element === this.pmView.dom,
				c = a && i.distance === 0;
			if (!(!a && !s))
				return {
					isDropPoint: a,
					isDropWithinEditorBounds: c,
					isDragOrigin: s,
				};
		});
		/**
		 * The drop event handler listens at the document level,
		 * and handles drop events for all editors.
		 *
		 * It specifically handles the following cases:
		 *  - If we are both the drag origin and drop point:
		 *    - Let normal drop handling take over
		 *  - If we are the drop point but not the drag origin:
		 *    - Collapse selection to prevent PM from deleting unrelated content
		 *    - If drop event is outside our editor bounds, dispatch synthetic drop event to our editor
		 *  - If we are the drag origin but not the drop point:
		 *    - Delete the dragged content from our editor after a delay
		 */
		k$2(this, "onDrop", (e) => {
			var a;
			if (
				e.synthetic ||
				!(
					this.pmView.dragging !== null ||
					this.isDragOrigin ||
					((a = e.dataTransfer) == null ? void 0 : a.types.includes("blocknote/html")) ||
					(e.target instanceof Node && this.pmView.dom.contains(e.target))
				)
			)
				return;
			const t = this.getDragEventContext(e);
			if (!t) {
				this.closeDropCursor();
				return;
			}
			const { isDropPoint: r, isDropWithinEditorBounds: s, isDragOrigin: i } = t;
			if ((!s && r && this.dispatchSyntheticEvent(e), r)) {
				if (this.pmView.dragging) return;
				this.pmView.dispatch(this.pmView.state.tr.setSelection(TextSelection.create(this.pmView.state.tr.doc, this.pmView.state.tr.selection.anchor)));
				return;
			} else if (i) {
				setTimeout(() => this.pmView.dispatch(this.pmView.state.tr.deleteSelection()), 0);
				return;
			}
		});
		k$2(this, "onDragEnd", (e) => {
			e.synthetic || (this.pmView.dragging = null);
		});
		k$2(this, "onKeyDown", (e) => {
			var o;
			(o = this.state) != null && o.show && this.editor.isFocused() && ((this.state.show = false), this.emitUpdate(this.state));
		});
		k$2(this, "onMouseMove", (e) => {
			var s;
			if (this.menuFrozen) return;
			this.mousePos = { x: e.clientX, y: e.clientY };
			const o = this.pmView.dom.getBoundingClientRect(),
				t = this.mousePos.x > o.left && this.mousePos.x < o.right && this.mousePos.y > o.top && this.mousePos.y < o.bottom,
				r = this.pmView.dom.parentElement;
			if (
				// Cursor is within the editor area
				t && // An element is hovered
				e &&
				e.target && // Element is outside the editor
				!(r === e.target || r.contains(e.target))
			) {
				(s = this.state) != null && s.show && ((this.state.show = false), this.emitUpdate(this.state));
				return;
			}
			this.updateStateFromMousePos();
		});
		((this.editor = e),
			(this.pmView = o),
			(this.emitUpdate = () => {
				if (!this.state) throw new Error("Attempting to update uninitialized side menu");
				t(this.state);
			}),
			this.pmView.root.addEventListener("dragstart", this.onDragStart),
			this.pmView.root.addEventListener("dragover", this.onDragOver),
			this.pmView.root.addEventListener("drop", this.onDrop, true),
			this.pmView.root.addEventListener("dragend", this.onDragEnd, true),
			this.pmView.root.addEventListener("mousemove", this.onMouseMove, true),
			this.pmView.root.addEventListener("keydown", this.onKeyDown, true));
	}
	dispatchSyntheticEvent(e) {
		const o = new Event(e.type, e),
			t = this.pmView.dom.firstChild.getBoundingClientRect();
		((o.clientX = e.clientX),
			(o.clientY = e.clientY),
			(o.clientX = Math.min(Math.max(e.clientX, t.left), t.left + t.width)),
			(o.clientY = Math.min(Math.max(e.clientY, t.top), t.top + t.height)),
			(o.dataTransfer = e.dataTransfer),
			(o.preventDefault = () => e.preventDefault()),
			(o.synthetic = true),
			this.pmView.dom.dispatchEvent(o));
	}
	// Needed in cases where the editor state updates without the mouse cursor
	// moving, as some state updates can require a side menu update. For example,
	// adding a button to the side menu which removes the block can cause the
	// block below to jump up into the place of the removed block when clicked,
	// allowing the user to click the button again without moving the cursor. This
	// would otherwise not update the side menu, and so clicking the button again
	// would attempt to remove the same block again, causing an error.
	update(e, o) {
		var r;
		!o.doc.eq(this.pmView.state.doc) && (r = this.state) != null && r.show && this.updateStateFromMousePos();
	}
	destroy() {
		var e;
		((e = this.state) != null && e.show && ((this.state.show = false), this.emitUpdate(this.state)),
			this.pmView.root.removeEventListener("mousemove", this.onMouseMove, true),
			this.pmView.root.removeEventListener("dragstart", this.onDragStart),
			this.pmView.root.removeEventListener("dragover", this.onDragOver),
			this.pmView.root.removeEventListener("drop", this.onDrop, true),
			this.pmView.root.removeEventListener("dragend", this.onDragEnd, true),
			this.pmView.root.removeEventListener("keydown", this.onKeyDown, true));
	}
}
const Zt$1 = new PluginKey("SideMenuPlugin"),
	_o$1 = a$1(({ editor: n }) => {
		let e;
		const o = f$2(void 0);
		return {
			key: "sideMenu",
			store: o,
			prosemirrorPlugins: [
				new Plugin({
					key: Zt$1,
					view: (t) => (
						(e = new Qt(n, t, (r) => {
							o.setState({ ...r });
						})),
						e
					),
				}),
			],
			/**
			 * Handles drag & drop events for blocks.
			 */
			blockDragStart(t, r) {
				(e && (e.isDragOrigin = true), Gt(t, r, n));
			},
			/**
			 * Handles drag & drop events for blocks.
			 */
			blockDragEnd() {
				(Pe$1(n.prosemirrorView.root), e && (e.isDragOrigin = false), n.blur());
			},
			/**
			 * Freezes the side menu. When frozen, the side menu will stay
			 * attached to the same block regardless of which block is hovered by the
			 * mouse cursor.
			 */
			freezeMenu() {
				((e.menuFrozen = true), (e.state.show = true), e.emitUpdate(e.state));
			},
			/**
			 * Unfreezes the side menu. When frozen, the side menu will stay
			 * attached to the same block regardless of which block is hovered by the
			 * mouse cursor.
			 */
			unfreezeMenu() {
				((e.menuFrozen = false), (e.state.show = false), e.emitUpdate(e.state));
			},
		};
	});
let $;
async function eo$1() {
	return (
		$ ||
		(($ = (async () => {
			const [n, e] = await Promise.all([
					__vitePreload(() => import("./module-CfopjtOo.js"), true ? [] : void 0),
					// use a dynamic import to encourage bundle-splitting
					// and a smaller initial client bundle size
					__vitePreload(() => import("./native-DB2Z31Rx.js"), true ? [] : void 0),
				]),
				o = "default" in n ? n.default : n,
				t = "default" in e ? e.default : e;
			return (await o.init({ data: t }), { emojiMart: o, emojiData: t });
		})()),
		$)
	);
}
async function zo$1(n, e) {
	if (!("text" in n.schema.inlineContentSchema) || n.schema.inlineContentSchema.text !== Ho$2.text) return [];
	const { emojiData: o, emojiMart: t } = await eo$1();
	return (e.trim() === "" ? Object.values(o.emojis) : await t.SearchIndex.search(e)).map((s) => ({
		id: s.skins[0].native,
		onItemClick: () => n.insertInlineContent(s.skins[0].native + " "),
	}));
}
let B;
function he$1(n) {
	B ||
		((B = document.createElement("div")),
		(B.innerHTML = "_"),
		(B.style.opacity = "0"),
		(B.style.height = "1px"),
		(B.style.width = "1px"),
		n instanceof Document ? n.body.appendChild(B) : n.appendChild(B));
}
function to$1(n) {
	B && (n instanceof Document ? n.body.removeChild(B) : n.removeChild(B), (B = void 0));
}
function F(n) {
	return Array.prototype.indexOf.call(n.parentElement.childNodes, n);
}
function oo$1(n) {
	let e = n;
	for (; e && e.nodeName !== "TD" && e.nodeName !== "TH" && !e.classList.contains("tableWrapper"); ) {
		if (e.classList.contains("ProseMirror")) return;
		const o = e.parentNode;
		if (!o || !(o instanceof Element)) return;
		e = o;
	}
	return e.nodeName === "TD" || e.nodeName === "TH" ?
			{
				type: "cell",
				domNode: e,
				tbodyNode: e.closest("tbody"),
			}
		:	{
				type: "wrapper",
				domNode: e,
				tbodyNode: e.querySelector("tbody"),
			};
}
function no$1(n, e) {
	const o = e.querySelectorAll(n);
	for (let t = 0; t < o.length; t++) o[t].style.visibility = "hidden";
}
let ro$1 = class ro {
	constructor(e, o, t) {
		k$2(this, "state");
		k$2(this, "emitUpdate");
		k$2(this, "tableId");
		k$2(this, "tablePos");
		k$2(this, "tableElement");
		k$2(this, "menuFrozen", false);
		k$2(this, "mouseState", "up");
		k$2(this, "prevWasEditable", null);
		k$2(this, "viewMousedownHandler", () => {
			this.mouseState = "down";
		});
		k$2(this, "mouseUpHandler", (e) => {
			((this.mouseState = "up"), this.mouseMoveHandler(e));
		});
		k$2(this, "mouseMoveHandler", (e) => {
			var l, u, h, f, m, p, d, g;
			if (this.menuFrozen || this.mouseState === "selecting" || !(e.target instanceof Element) || !this.pmView.dom.contains(e.target)) return;
			const o = oo$1(e.target);
			if ((o == null ? void 0 : o.type) === "cell" && this.mouseState === "down" && !((l = this.state) != null && l.draggingState)) {
				((this.mouseState = "selecting"),
					(u = this.state) != null &&
						u.show &&
						((this.state.show = false), (this.state.showAddOrRemoveRowsButton = false), (this.state.showAddOrRemoveColumnsButton = false), this.emitUpdate()));
				return;
			}
			if (!o || !this.editor.isEditable) {
				(h = this.state) != null && h.show && ((this.state.show = false), (this.state.showAddOrRemoveRowsButton = false), (this.state.showAddOrRemoveColumnsButton = false), this.emitUpdate());
				return;
			}
			if (!o.tbodyNode) return;
			const t = o.tbodyNode.getBoundingClientRect(),
				r = De$1(o.domNode, this.pmView);
			if (!r) return;
			this.tableElement = r.node;
			let s;
			const i = this.editor.transact((b) => Bt$1(r.id, b.doc));
			if (!i) throw new Error(`Block with ID ${r.id} not found`);
			const a = L$2(i.node, this.editor.pmSchema, this.editor.schema.blockSchema, this.editor.schema.inlineContentSchema, this.editor.schema.styleSchema);
			if ((b$2(this.editor, "table") && ((this.tablePos = i.posBeforeNode + 1), (s = a)), !s)) return;
			this.tableId = r.id;
			const c = (f = o.domNode.closest(".tableWrapper")) == null ? void 0 : f.querySelector(".table-widgets-container");
			if ((o == null ? void 0 : o.type) === "wrapper") {
				const b =
						e.clientY >= t.bottom - 1 && // -1 to account for fractions of pixels in "bottom"
						e.clientY < t.bottom + 20,
					y = e.clientX >= t.right - 1 && e.clientX < t.right + 20,
					w =
						// always hide handles when the actively hovered table changed
						((m = this.state) == null ? void 0 : m.block.id) !== s.id || // make sure we don't hide existing handles (keep col / row index) when
						// we're hovering just above or to the right of a table
						e.clientX > t.right ||
						e.clientY > t.bottom;
				this.state = {
					...this.state,
					show: true,
					showAddOrRemoveRowsButton: b,
					showAddOrRemoveColumnsButton: y,
					referencePosTable: t,
					block: s,
					widgetContainer: c,
					colIndex: w || (p = this.state) == null ? void 0 : p.colIndex,
					rowIndex: w || (d = this.state) == null ? void 0 : d.rowIndex,
					referencePosCell: w || (g = this.state) == null ? void 0 : g.referencePosCell,
				};
			} else {
				const b = F(o.domNode),
					y = F(o.domNode.parentElement),
					w = o.domNode.getBoundingClientRect();
				if (this.state !== void 0 && this.state.show && this.tableId === r.id && this.state.rowIndex === y && this.state.colIndex === b) return;
				this.state = {
					show: true,
					showAddOrRemoveColumnsButton: b === s.content.rows[0].cells.length - 1,
					showAddOrRemoveRowsButton: y === s.content.rows.length - 1,
					referencePosTable: t,
					block: s,
					draggingState: void 0,
					referencePosCell: w,
					colIndex: b,
					rowIndex: y,
					widgetContainer: c,
				};
			}
			return (this.emitUpdate(), false);
		});
		k$2(this, "dragOverHandler", (e) => {
			var f;
			if (((f = this.state) == null ? void 0 : f.draggingState) === void 0) return;
			(e.preventDefault(), (e.dataTransfer.dropEffect = "move"), no$1(".prosemirror-dropcursor-block, .prosemirror-dropcursor-inline", this.pmView.root));
			const o = {
					left: Math.min(Math.max(e.clientX, this.state.referencePosTable.left + 1), this.state.referencePosTable.right - 1),
					top: Math.min(Math.max(e.clientY, this.state.referencePosTable.top + 1), this.state.referencePosTable.bottom - 1),
				},
				t = this.pmView.root.elementsFromPoint(o.left, o.top).filter((m) => m.tagName === "TD" || m.tagName === "TH");
			if (t.length === 0) return;
			const r = t[0];
			let s = false;
			const i = F(r.parentElement),
				a = F(r),
				c = this.state.draggingState.draggedCellOrientation === "row" ? this.state.rowIndex : this.state.colIndex,
				u = (this.state.draggingState.draggedCellOrientation === "row" ? i : a) !== c;
			(this.state.rowIndex !== i || this.state.colIndex !== a) && ((this.state.rowIndex = i), (this.state.colIndex = a), (this.state.referencePosCell = r.getBoundingClientRect()), (s = true));
			const h = this.state.draggingState.draggedCellOrientation === "row" ? o.top : o.left;
			(this.state.draggingState.mousePos !== h && ((this.state.draggingState.mousePos = h), (s = true)), s && this.emitUpdate(), u && this.editor.transact((m) => m.setMeta(V, true)));
		});
		k$2(this, "dropHandler", (e) => {
			if (((this.mouseState = "up"), this.state === void 0 || this.state.draggingState === void 0)) return false;
			if (this.state.rowIndex === void 0 || this.state.colIndex === void 0) throw new Error("Attempted to drop table row or column, but no table block was hovered prior.");
			e.preventDefault();
			const { draggingState: o, colIndex: t, rowIndex: r } = this.state,
				s = this.state.block.content.columnWidths;
			if (o.draggedCellOrientation === "row") {
				if (!Jt$2(this.state.block, o.originalIndex, r)) return false;
				const i = Pt$2(this.state.block, o.originalIndex, r);
				this.editor.updateBlock(this.state.block, {
					type: "table",
					content: {
						...this.state.block.content,
						rows: i,
					},
				});
			} else {
				if (!_t$3(this.state.block, o.originalIndex, t)) return false;
				const i = Mt$2(this.state.block, o.originalIndex, t),
					[a] = s.splice(o.originalIndex, 1);
				(s.splice(t, 0, a),
					this.editor.updateBlock(this.state.block, {
						type: "table",
						content: {
							...this.state.block.content,
							columnWidths: s,
							rows: i,
						},
					}));
			}
			return (this.editor.setTextCursorPosition(this.state.block.id), true);
		});
		((this.editor = e),
			(this.pmView = o),
			(this.emitUpdate = () => {
				if (!this.state) throw new Error("Attempting to update uninitialized image toolbar");
				t(this.state);
			}),
			o.dom.addEventListener("mousemove", this.mouseMoveHandler),
			o.dom.addEventListener("mousedown", this.viewMousedownHandler),
			window.addEventListener("mouseup", this.mouseUpHandler),
			o.root.addEventListener("dragover", this.dragOverHandler),
			o.root.addEventListener("drop", this.dropHandler));
	}
	// Updates drag handles when the table is modified or removed.
	update() {
		var r;
		if (!this.state || !this.state.show) return;
		if (
			((this.state.block = this.editor.getBlock(this.state.block.id)),
			!this.state.block ||
				this.state.block.type !== "table" || // when collaborating, the table element might be replaced and out of date
				// because yjs replaces the element when for example you change the color via the side menu
				!((r = this.tableElement) != null && r.isConnected))
		) {
			((this.state.show = false), (this.state.showAddOrRemoveRowsButton = false), (this.state.showAddOrRemoveColumnsButton = false), this.emitUpdate());
			return;
		}
		const { height: e, width: o } = tt(this.state.block);
		this.state.rowIndex !== void 0 && this.state.colIndex !== void 0 && (this.state.rowIndex >= e && (this.state.rowIndex = e - 1), this.state.colIndex >= o && (this.state.colIndex = o - 1));
		const t = this.tableElement.querySelector("tbody");
		if (!t) throw new Error("Table block does not contain a 'tbody' HTML element. This should never happen.");
		if (this.state.rowIndex !== void 0 && this.state.colIndex !== void 0) {
			const i = t.children[this.state.rowIndex].children[this.state.colIndex];
			i ? (this.state.referencePosCell = i.getBoundingClientRect()) : ((this.state.rowIndex = void 0), (this.state.colIndex = void 0));
		}
		((this.state.referencePosTable = t.getBoundingClientRect()), this.emitUpdate());
	}
	destroy() {
		(this.pmView.dom.removeEventListener("mousemove", this.mouseMoveHandler),
			window.removeEventListener("mouseup", this.mouseUpHandler),
			this.pmView.dom.removeEventListener("mousedown", this.viewMousedownHandler),
			this.pmView.root.removeEventListener("dragover", this.dragOverHandler),
			this.pmView.root.removeEventListener("drop", this.dropHandler));
	}
};
const V = new PluginKey("TableHandlesPlugin"),
	Yo$1 = a$1(({ editor: n }) => {
		let e;
		const o = f$2(void 0);
		return {
			key: "tableHandles",
			store: o,
			prosemirrorPlugins: [
				new Plugin({
					key: V,
					view: (t) => (
						(e = new ro$1(n, t, (r) => {
							o.setState(
								r.block ?
									{
										...r,
										draggingState: r.draggingState ? { ...r.draggingState } : void 0,
									}
								:	void 0,
							);
						})),
						e
					),
					// We use decorations to render the drop cursor when dragging a table row
					// or column. The decorations are updated in the `dragOverHandler` method.
					props: {
						decorations: (t) => {
							if (e === void 0 || e.state === void 0 || e.state.draggingState === void 0 || e.tablePos === void 0) return;
							const r = e.state.draggingState.draggedCellOrientation === "row" ? e.state.rowIndex : e.state.colIndex;
							if (r === void 0) return;
							const s = [],
								{ block: i, draggingState: a } = e.state,
								{ originalIndex: c, draggedCellOrientation: l } = a;
							if (r === c || !i || (l === "row" && !Jt$2(i, c, r)) || (l === "col" && !_t$3(i, c, r))) return DecorationSet.create(t.doc, s);
							const u = t.doc.resolve(e.tablePos + 1);
							return (
								e.state.draggingState.draggedCellOrientation === "row" ?
									yt$1(e.state.block, r).forEach(({ row: f, col: m }) => {
										const p = t.doc.resolve(u.posAtIndex(f) + 1),
											d = t.doc.resolve(p.posAtIndex(m) + 1),
											g = d.node(),
											b = d.pos + (r > c ? g.nodeSize - 2 : 0);
										s.push(
											// The widget is a small bar which spans the width of the cell.
											Decoration.widget(b, () => {
												const y = document.createElement("div");
												return (
													(y.className = "bn-table-drop-cursor"),
													(y.style.left = "0"),
													(y.style.right = "0"),
													r > c ? (y.style.bottom = "-2px") : (y.style.top = "-3px"),
													(y.style.height = "4px"),
													y
												);
											}),
										);
									})
								:	Ct$1(e.state.block, r).forEach(({ row: f, col: m }) => {
										const p = t.doc.resolve(u.posAtIndex(f) + 1),
											d = t.doc.resolve(p.posAtIndex(m) + 1),
											g = d.node(),
											b = d.pos + (r > c ? g.nodeSize - 2 : 0);
										s.push(
											// The widget is a small bar which spans the height of the cell.
											Decoration.widget(b, () => {
												const y = document.createElement("div");
												return (
													(y.className = "bn-table-drop-cursor"),
													(y.style.top = "0"),
													(y.style.bottom = "0"),
													r > c ? (y.style.right = "-2px") : (y.style.left = "-3px"),
													(y.style.width = "4px"),
													y
												);
											}),
										);
									}),
								DecorationSet.create(t.doc, s)
							);
						},
					},
				}),
			],
			/**
			 * Callback that should be set on the `dragStart` event for whichever element
			 * is used as the column drag handle.
			 */
			colDragStart(t) {
				if (e === void 0 || e.state === void 0 || e.state.colIndex === void 0) throw new Error("Attempted to drag table column, but no table block was hovered prior.");
				((e.state.draggingState = {
					draggedCellOrientation: "col",
					originalIndex: e.state.colIndex,
					mousePos: t.clientX,
				}),
					e.emitUpdate(),
					n.transact((r) =>
						r.setMeta(V, {
							draggedCellOrientation: e.state.draggingState.draggedCellOrientation,
							originalIndex: e.state.colIndex,
							newIndex: e.state.colIndex,
							tablePos: e.tablePos,
						}),
					),
					!n.headless && (he$1(n.prosemirrorView.root), t.dataTransfer.setDragImage(B, 0, 0), (t.dataTransfer.effectAllowed = "move")));
			},
			/**
			 * Callback that should be set on the `dragStart` event for whichever element
			 * is used as the row drag handle.
			 */
			rowDragStart(t) {
				if (e.state === void 0 || e.state.rowIndex === void 0) throw new Error("Attempted to drag table row, but no table block was hovered prior.");
				((e.state.draggingState = {
					draggedCellOrientation: "row",
					originalIndex: e.state.rowIndex,
					mousePos: t.clientY,
				}),
					e.emitUpdate(),
					n.transact((r) =>
						r.setMeta(V, {
							draggedCellOrientation: e.state.draggingState.draggedCellOrientation,
							originalIndex: e.state.rowIndex,
							newIndex: e.state.rowIndex,
							tablePos: e.tablePos,
						}),
					),
					!n.headless && (he$1(n.prosemirrorView.root), t.dataTransfer.setDragImage(B, 0, 0), (t.dataTransfer.effectAllowed = "copyMove")));
			},
			/**
			 * Callback that should be set on the `dragEnd` event for both the element
			 * used as the row drag handle, and the one used as the column drag handle.
			 */
			dragEnd() {
				if (e.state === void 0) throw new Error("Attempted to drag table row, but no table block was hovered prior.");
				((e.state.draggingState = void 0), e.emitUpdate(), n.transact((t) => t.setMeta(V, null)), !n.headless && to$1(n.prosemirrorView.root));
			},
			/**
			 * Freezes the drag handles. When frozen, they will stay attached to the same
			 * cell regardless of which cell is hovered by the mouse cursor.
			 */
			freezeHandles() {
				e.menuFrozen = true;
			},
			/**
			 * Unfreezes the drag handles. When frozen, they will stay attached to the
			 * same cell regardless of which cell is hovered by the mouse cursor.
			 */
			unfreezeHandles() {
				e.menuFrozen = false;
			},
			getCellsAtRowHandle(t, r) {
				return yt$1(t, r);
			},
			/**
			 * Get all the cells in a column of the table block.
			 */
			getCellsAtColumnHandle(t, r) {
				return Ct$1(t, r);
			},
			/**
			 * Sets the selection to the given cell or a range of cells.
			 * @returns The new state after the selection has been set.
			 */
			setCellSelection(t, r, s = r) {
				if (!e) throw new Error("Table handles view not initialized");
				const i = t.doc.resolve(e.tablePos + 1),
					a = t.doc.resolve(i.posAtIndex(r.row) + 1),
					c = t.doc.resolve(
						// No need for +1, since CellSelection expects the position before the cell
						a.posAtIndex(r.col),
					),
					l = t.doc.resolve(i.posAtIndex(s.row) + 1),
					u = t.doc.resolve(
						// No need for +1, since CellSelection expects the position before the cell
						l.posAtIndex(s.col),
					),
					h = t.tr;
				return (h.setSelection(new CellSelection(c, u)), t.apply(h));
			},
			/**
			 * Adds a row or column to the table using prosemirror-table commands
			 */
			addRowOrColumn(t, r) {
				n.exec((s, i) => {
					const a = this.setCellSelection(s, r.orientation === "row" ? { row: t, col: 0 } : { row: 0, col: t });
					return (
						r.orientation === "row" ?
							r.side === "above" ?
								addRowBefore(a, i)
							:	addRowAfter(a, i)
						: r.side === "left" ? addColumnBefore(a, i)
						: addColumnAfter(a, i)
					);
				});
			},
			/**
			 * Removes a row or column from the table using prosemirror-table commands
			 */
			removeRowOrColumn(t, r) {
				return r === "row" ?
						n.exec((s, i) => {
							const a = this.setCellSelection(s, {
								row: t,
								col: 0,
							});
							return deleteRow(a, i);
						})
					:	n.exec((s, i) => {
							const a = this.setCellSelection(s, {
								row: 0,
								col: t,
							});
							return deleteColumn(a, i);
						});
			},
			/**
			 * Merges the cells in the table block.
			 */
			mergeCells(t) {
				return n.exec((r, s) => {
					const i = t ? this.setCellSelection(r, t.relativeStartCell, t.relativeEndCell) : r;
					return mergeCells(i, s);
				});
			},
			/**
			 * Splits the cell in the table block.
			 * If no cell is provided, the current cell selected will be split.
			 */
			splitCell(t) {
				return n.exec((r, s) => {
					const i = t ? this.setCellSelection(r, t) : r;
					return splitCell(i, s);
				});
			},
			/**
			 * Gets the start and end cells of the current cell selection.
			 * @returns The start and end cells of the current cell selection.
			 */
			getCellSelection() {
				return n.transact((t) => {
					const r = t.selection;
					let s = r.$from,
						i = r.$to;
					if (Bo$1(r)) {
						const { ranges: d } = r;
						d.forEach((g) => {
							((s = g.$from.min(s ?? g.$from)), (i = g.$to.max(i ?? g.$to)));
						});
					} else if (((s = t.doc.resolve(r.$from.pos - r.$from.parentOffset - 1)), (i = t.doc.resolve(r.$to.pos - r.$to.parentOffset - 1)), s.pos === 0 || i.pos === 0)) return;
					const a = t.doc.resolve(s.pos - s.parentOffset - 1),
						c = t.doc.resolve(i.pos - i.parentOffset - 1),
						l = t.doc.resolve(a.pos - a.parentOffset - 1),
						u = s.index(a.depth),
						h = a.index(l.depth),
						f = i.index(c.depth),
						m = c.index(l.depth),
						p = [];
					for (let d = h; d <= m; d++) for (let g = u; g <= f; g++) p.push({ row: d, col: g });
					return {
						from: {
							row: h,
							col: u,
						},
						to: {
							row: m,
							col: f,
						},
						cells: p,
					};
				});
			},
			/**
			 * Gets the direction of the merge based on the current cell selection.
			 *
			 * Returns undefined when there is no cell selection, or the selection is not within a table.
			 */
			getMergeDirection(t) {
				return n.transact((r) => {
					const s = Bo$1(r.selection) ? r.selection : void 0;
					if (
						!s ||
						!t || // Only offer the merge button if there is more than one cell selected.
						s.ranges.length <= 1
					)
						return;
					const i = this.getCellSelection();
					if (i) return Ht$2(i.from, i.to, t) ? "vertical" : "horizontal";
				});
			},
			cropEmptyRowsOrColumns(t, r) {
				return Rt$2(t, r);
			},
			addRowsOrColumns(t, r, s) {
				return $t$2(t, r, s);
			},
		};
	}),
	me = new PluginKey("trailingNode"),
	jo$1 = a$1(() => ({
		key: "trailingNode",
		prosemirrorPlugins: [
			new Plugin({
				key: me,
				appendTransaction: (n, e, o) => {
					const { doc: t, tr: r, schema: s } = o,
						i = me.getState(o),
						a = t.content.size - 2,
						c = s.nodes.blockContainer,
						l = s.nodes.paragraph;
					if (i) return r.insert(a, c.create(void 0, l.create()));
				},
				state: {
					init: (n, e) => {},
					apply: (n, e) => {
						if (!n.docChanged) return e;
						let o = n.doc.lastChild;
						if (!o || o.type.name !== "blockGroup") throw new Error("Expected blockGroup");
						if (((o = o.lastChild), !o || o.type.name !== "blockContainer")) return true;
						const t = o.firstChild;
						if (!t) throw new Error("Expected blockContent");
						return o.nodeSize > 4 || t.type.spec.content !== "inline*";
					},
				},
			}),
		],
	}));

var u = Object.defineProperty;
var k$1 = (t, e, n) => (e in t ? u(t, e, { enumerable: true, configurable: true, writable: true, value: n }) : (t[e] = n));
var i$1 = (t, e, n) => k$1(t, typeof e != "symbol" ? e + "" : e, n);
function E(t) {
	const e = v(t);
	let { roots: n, nonRoots: r } = f$1(e);
	const s = [];
	for (; n.size; ) {
		s.push(n);
		const o = /* @__PURE__ */ new Set();
		for (const c of n) {
			const a = t.get(c);
			if (a)
				for (const l of a) {
					const p = e.get(l);
					if (p === void 0) continue;
					const d = p - 1;
					(e.set(l, d), d === 0 && o.add(l));
				}
		}
		n = o;
	}
	if (((r = f$1(e).nonRoots), r.size)) throw new Error(`Cycle(s) detected; toposort only works on acyclic graphs. Cyclic nodes: ${Array.from(r).join(", ")}`);
	return s;
}
function D(t) {
	const e = I(t);
	return E(e);
}
function v(t) {
	const e = /* @__PURE__ */ new Map();
	for (const [n, r] of t.entries()) {
		e.has(n) || e.set(n, 0);
		for (const s of r) {
			const o = e.get(s) ?? 0;
			e.set(s, o + 1);
		}
	}
	return e;
}
function f$1(t) {
	const e = /* @__PURE__ */ new Set(),
		n = /* @__PURE__ */ new Set();
	for (const [r, s] of t.entries()) s === 0 ? e.add(r) : n.add(r);
	return { roots: e, nonRoots: n };
}
function I(t) {
	const e = /* @__PURE__ */ new Map();
	for (const [n, r] of t.entries()) {
		e.has(n) || e.set(n, /* @__PURE__ */ new Set());
		for (const s of r) (e.has(s) || e.set(s, /* @__PURE__ */ new Set()), e.get(s).add(n));
	}
	return e;
}
function A() {
	return /* @__PURE__ */ new Map();
}
function m(t, e, n) {
	return (t.has(e) || t.set(e, /* @__PURE__ */ new Set()), t.get(e).add(n), t);
}
function P(t) {
	const e = A();
	for (const s of t)
		Array.isArray(s.runsBefore) && s.runsBefore.length > 0 ?
			s.runsBefore.forEach((o) => {
				m(e, s.key, o);
			})
		:	m(e, "default", s.key);
	const n = D(e),
		r = n.findIndex((s) => s.has("default"));
	return (s) => 91 + (n.findIndex((c) => c.has(s)) + r) * 10;
}
function S(t) {
	return t && Object.fromEntries(Object.entries(t).filter(([, e]) => e !== void 0));
}
class N {
	constructor(e) {
		// Helper so that you can use typeof schema.BlockNoteEditor
		i$1(this, "BlockNoteEditor", "only for types");
		i$1(this, "Block", "only for types");
		i$1(this, "PartialBlock", "only for types");
		i$1(this, "inlineContentSpecs");
		i$1(this, "styleSpecs");
		i$1(this, "blockSpecs");
		i$1(this, "blockSchema");
		i$1(this, "inlineContentSchema");
		i$1(this, "styleSchema");
		this.opts = e;
		const { blockSpecs: n, inlineContentSpecs: r, styleSpecs: s, blockSchema: o, inlineContentSchema: c, styleSchema: a } = this.init();
		((this.blockSpecs = n), (this.styleSpecs = s), (this.styleSchema = a), (this.inlineContentSpecs = r), (this.blockSchema = o), (this.inlineContentSchema = c));
	}
	init() {
		const e = P(
				Object.entries({
					...this.opts.blockSpecs,
					...this.opts.inlineContentSpecs,
					...this.opts.styleSpecs,
				}).map(([o, c]) => {
					var a;
					return {
						key: o,
						runsBefore: ((a = c.implementation) == null ? void 0 : a.runsBefore) ?? [],
					};
				}),
			),
			n = Object.fromEntries(Object.entries(this.opts.blockSpecs).map(([o, c]) => [o, ho$1(c.config, c.implementation, c.extensions, e(o))])),
			r = Object.fromEntries(
				Object.entries(this.opts.inlineContentSpecs).map(([o, c]) => {
					var a;
					return typeof c.config != "object" ?
							[o, c]
						:	[
								o,
								{
									...c,
									implementation: {
										...c.implementation,
										node:
											(a = c.implementation) == null ?
												void 0
											:	a.node.extend({
													priority: e(o),
												}),
									},
								},
							];
				}),
			),
			s = Object.fromEntries(
				Object.entries(this.opts.styleSpecs).map(([o, c]) => {
					var a;
					return [
						o,
						{
							...c,
							implementation: {
								...c.implementation,
								mark:
									(a = c.implementation) == null ?
										void 0
									:	a.mark.extend({
											priority: e(o),
										}),
							},
						},
					];
				}),
			);
		return {
			blockSpecs: n,
			blockSchema: Object.fromEntries(Object.entries(n).map(([o, c]) => [o, c.config])),
			inlineContentSpecs: S(r),
			styleSpecs: S(s),
			inlineContentSchema: Mt$1(r),
			styleSchema: Lt$1(s),
		};
	}
	/**
	 * Adds additional block specs to the current schema in a builder pattern.
	 * This method allows extending the schema after it has been created.
	 *
	 * @param additionalBlockSpecs - Additional block specs to add to the schema
	 * @returns The current schema instance for chaining
	 */
	extend(e) {
		(Object.assign(this.opts.blockSpecs, e.blockSpecs), Object.assign(this.opts.inlineContentSpecs, e.inlineContentSpecs), Object.assign(this.opts.styleSpecs, e.styleSpecs));
		const { blockSpecs: n, inlineContentSpecs: r, styleSpecs: s, blockSchema: o, inlineContentSchema: c, styleSchema: a } = this.init();
		return ((this.blockSpecs = n), (this.styleSpecs = s), (this.styleSchema = a), (this.inlineContentSpecs = r), (this.blockSchema = o), (this.inlineContentSchema = c), this);
	}
}
let h$1 = class h extends N {
	static create(e) {
		return new h({
			blockSpecs: (e == null ? void 0 : e.blockSpecs) ?? Po$2,
			inlineContentSpecs: (e == null ? void 0 : e.inlineContentSpecs) ?? Un,
			styleSpecs: (e == null ? void 0 : e.styleSpecs) ?? $n$1,
		});
	}
};

var h = Object.defineProperty;
var b$1 = (a, s, l) => (s in a ? h(a, s, { enumerable: true, configurable: true, writable: true, value: l }) : (a[s] = l));
var t = (a, s, l) => b$1(a, s + "", l);
class f {
	constructor() {
		// eslint-disable-next-line @typescript-eslint/ban-types
		t(this, "callbacks", {});
	}
	on(s, l) {
		return (this.callbacks[s] || (this.callbacks[s] = []), this.callbacks[s].push(l), () => this.off(s, l));
	}
	emit(s, ...l) {
		const c = this.callbacks[s];
		c && c.forEach((i) => i.apply(this, l));
	}
	off(s, l) {
		const c = this.callbacks[s];
		c && (l ? (this.callbacks[s] = c.filter((i) => i !== l)) : delete this.callbacks[s]);
	}
	removeAllListeners() {
		this.callbacks = {};
	}
}

const i = {
	slash_menu: {
		heading: {
			title: "Heading 1",
			subtext: "Top-level heading",
			aliases: ["h", "heading1", "h1"],
			group: "Headings",
		},
		heading_2: {
			title: "Heading 2",
			subtext: "Key section heading",
			aliases: ["h2", "heading2", "subheading"],
			group: "Headings",
		},
		heading_3: {
			title: "Heading 3",
			subtext: "Subsection and group heading",
			aliases: ["h3", "heading3", "subheading"],
			group: "Headings",
		},
		heading_4: {
			title: "Heading 4",
			subtext: "Minor subsection heading",
			aliases: ["h4", "heading4", "subheading4"],
			group: "Subheadings",
		},
		heading_5: {
			title: "Heading 5",
			subtext: "Small subsection heading",
			aliases: ["h5", "heading5", "subheading5"],
			group: "Subheadings",
		},
		heading_6: {
			title: "Heading 6",
			subtext: "Lowest-level heading",
			aliases: ["h6", "heading6", "subheading6"],
			group: "Subheadings",
		},
		toggle_heading: {
			title: "Toggle Heading 1",
			subtext: "Toggleable top-level heading",
			aliases: ["h", "heading1", "h1", "collapsable"],
			group: "Subheadings",
		},
		toggle_heading_2: {
			title: "Toggle Heading 2",
			subtext: "Toggleable key section heading",
			aliases: ["h2", "heading2", "subheading", "collapsable"],
			group: "Subheadings",
		},
		toggle_heading_3: {
			title: "Toggle Heading 3",
			subtext: "Toggleable subsection and group heading",
			aliases: ["h3", "heading3", "subheading", "collapsable"],
			group: "Subheadings",
		},
		quote: {
			title: "Quote",
			subtext: "Quote or excerpt",
			aliases: ["quotation", "blockquote", "bq"],
			group: "Basic blocks",
		},
		toggle_list: {
			title: "Toggle List",
			subtext: "List with hideable sub-items",
			aliases: ["li", "list", "toggleList", "toggle list", "collapsable list"],
			group: "Basic blocks",
		},
		numbered_list: {
			title: "Numbered List",
			subtext: "List with ordered items",
			aliases: ["ol", "li", "list", "numberedlist", "numbered list"],
			group: "Basic blocks",
		},
		bullet_list: {
			title: "Bullet List",
			subtext: "List with unordered items",
			aliases: ["ul", "li", "list", "bulletlist", "bullet list"],
			group: "Basic blocks",
		},
		check_list: {
			title: "Check List",
			subtext: "List with checkboxes",
			aliases: ["ul", "li", "list", "checklist", "check list", "checked list", "checkbox"],
			group: "Basic blocks",
		},
		paragraph: {
			title: "Paragraph",
			subtext: "The body of your document",
			aliases: ["p", "paragraph"],
			group: "Basic blocks",
		},
		code_block: {
			title: "Code Block",
			subtext: "Code block with syntax highlighting",
			aliases: ["code", "pre"],
			group: "Basic blocks",
		},
		page_break: {
			title: "Page Break",
			subtext: "Page separator",
			aliases: ["page", "break", "separator"],
			group: "Basic blocks",
		},
		table: {
			title: "Table",
			subtext: "Table with editable cells",
			aliases: ["table"],
			group: "Advanced",
		},
		image: {
			title: "Image",
			subtext: "Resizable image with caption",
			aliases: ["image", "imageUpload", "upload", "img", "picture", "media", "url"],
			group: "Media",
		},
		video: {
			title: "Video",
			subtext: "Resizable video with caption",
			aliases: ["video", "videoUpload", "upload", "mp4", "film", "media", "url"],
			group: "Media",
		},
		audio: {
			title: "Audio",
			subtext: "Embedded audio with caption",
			aliases: ["audio", "audioUpload", "upload", "mp3", "sound", "media", "url"],
			group: "Media",
		},
		file: {
			title: "File",
			subtext: "Embedded file",
			aliases: ["file", "upload", "embed", "media", "url"],
			group: "Media",
		},
		emoji: {
			title: "Emoji",
			subtext: "Search for and insert an emoji",
			aliases: ["emoji", "emote", "emotion", "face"],
			group: "Others",
		},
		divider: {
			title: "Divider",
			subtext: "Visually divide blocks",
			aliases: ["divider", "hr", "line", "horizontal rule"],
			group: "Basic blocks",
		},
	},
	placeholders: {
		default: "Enter text or type '/' for commands",
		heading: "Heading",
		toggleListItem: "Toggle",
		bulletListItem: "List",
		numberedListItem: "List",
		checkListItem: "List",
		emptyDocument: void 0,
		new_comment: "Write a comment...",
		edit_comment: "Edit comment...",
		comment_reply: "Add comment...",
	},
	file_blocks: {
		add_button_text: {
			image: "Add image",
			video: "Add video",
			audio: "Add audio",
			file: "Add file",
		},
	},
	toggle_blocks: {
		add_block_button: "Empty toggle. Click to add a block.",
	},
	// from react package:
	side_menu: {
		add_block_label: "Add block",
		drag_handle_label: "Open block menu",
	},
	drag_handle: {
		delete_menuitem: "Delete",
		colors_menuitem: "Colors",
		header_row_menuitem: "Header row",
		header_column_menuitem: "Header column",
	},
	table_handle: {
		delete_column_menuitem: "Delete column",
		delete_row_menuitem: "Delete row",
		add_left_menuitem: "Add column left",
		add_right_menuitem: "Add column right",
		add_above_menuitem: "Add row above",
		add_below_menuitem: "Add row below",
		split_cell_menuitem: "Split cell",
		merge_cells_menuitem: "Merge cells",
		background_color_menuitem: "Background color",
	},
	suggestion_menu: {
		no_items_title: "No items found",
	},
	color_picker: {
		text_title: "Text",
		background_title: "Background",
		colors: {
			default: "Default",
			gray: "Gray",
			brown: "Brown",
			red: "Red",
			orange: "Orange",
			yellow: "Yellow",
			green: "Green",
			blue: "Blue",
			purple: "Purple",
			pink: "Pink",
		},
	},
	formatting_toolbar: {
		bold: {
			tooltip: "Bold",
			secondary_tooltip: "Mod+B",
		},
		italic: {
			tooltip: "Italic",
			secondary_tooltip: "Mod+I",
		},
		underline: {
			tooltip: "Underline",
			secondary_tooltip: "Mod+U",
		},
		strike: {
			tooltip: "Strike",
			secondary_tooltip: "Mod+Shift+S",
		},
		code: {
			tooltip: "Code",
			secondary_tooltip: "",
		},
		colors: {
			tooltip: "Colors",
		},
		link: {
			tooltip: "Create link",
			secondary_tooltip: "Mod+K",
		},
		file_caption: {
			tooltip: "Edit caption",
			input_placeholder: "Edit caption",
		},
		file_replace: {
			tooltip: {
				image: "Replace image",
				video: "Replace video",
				audio: "Replace audio",
				file: "Replace file",
			},
		},
		file_rename: {
			tooltip: {
				image: "Rename image",
				video: "Rename video",
				audio: "Rename audio",
				file: "Rename file",
			},
			input_placeholder: {
				image: "Rename image",
				video: "Rename video",
				audio: "Rename audio",
				file: "Rename file",
			},
		},
		file_download: {
			tooltip: {
				image: "Download image",
				video: "Download video",
				audio: "Download audio",
				file: "Download file",
			},
		},
		file_delete: {
			tooltip: {
				image: "Delete image",
				video: "Delete video",
				audio: "Delete audio",
				file: "Delete file",
			},
		},
		file_preview_toggle: {
			tooltip: "Toggle preview",
		},
		nest: {
			tooltip: "Nest block",
			secondary_tooltip: "Tab",
		},
		unnest: {
			tooltip: "Unnest block",
			secondary_tooltip: "Shift+Tab",
		},
		align_left: {
			tooltip: "Align text left",
		},
		align_center: {
			tooltip: "Align text center",
		},
		align_right: {
			tooltip: "Align text right",
		},
		align_justify: {
			tooltip: "Justify text",
		},
		table_cell_merge: {
			tooltip: "Merge cells",
		},
		comment: {
			tooltip: "Add comment",
		},
	},
	file_panel: {
		upload: {
			title: "Upload",
			file_placeholder: {
				image: "Upload image",
				video: "Upload video",
				audio: "Upload audio",
				file: "Upload file",
			},
			upload_error: "Error: Upload failed",
		},
		embed: {
			title: "Embed",
			embed_button: {
				image: "Embed image",
				video: "Embed video",
				audio: "Embed audio",
				file: "Embed file",
			},
			url_placeholder: "Enter URL",
		},
	},
	link_toolbar: {
		delete: {
			tooltip: "Remove link",
		},
		edit: {
			text: "Edit link",
			tooltip: "Edit",
		},
		open: {
			tooltip: "Open in new tab",
		},
		form: {
			title_placeholder: "Edit title",
			url_placeholder: "Edit URL",
		},
	},
	comments: {
		edited: "edited",
		save_button_text: "Save",
		cancel_button_text: "Cancel",
		actions: {
			add_reaction: "Add reaction",
			resolve: "Resolve",
			edit_comment: "Edit comment",
			delete_comment: "Delete comment",
			more_actions: "More actions",
		},
		reactions: {
			reacted_by: "Reacted by",
		},
		sidebar: {
			marked_as_resolved: "Marked as resolved",
			more_replies: (e) => `${e} more replies`,
		},
	},
	generic: {
		ctrl_shortcut: "Ctrl",
	},
};

/// Input rules are regular expressions describing a piece of text
/// that, when typed, causes something to happen. This might be
/// changing two dashes into an emdash, wrapping a paragraph starting
/// with `"> "` into a blockquote, or something entirely different.
class InputRule {
	match;
	/// @internal
	handler;
	/// @internal
	undoable;
	inCode;
	inCodeMark;
	/// Create an input rule. The rule applies when the user typed
	/// something and the text directly in front of the cursor matches
	/// `match`, which should end with `$`.
	///
	/// The `handler` can be a string, in which case the matched text, or
	/// the first matched group in the regexp, is replaced by that
	/// string.
	///
	/// Or a it can be a function, which will be called with the match
	/// array produced by
	/// [`RegExp.exec`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec),
	/// as well as the start and end of the matched range, and which can
	/// return a [transaction](#state.Transaction) that describes the
	/// rule's effect, or null to indicate the input was not handled.
	constructor(
		/// @internal
		match,
		handler,
		options = {},
	) {
		this.match = match;
		this.match = match;
		this.handler = typeof handler == "string" ? stringHandler(handler) : handler;
		this.undoable = options.undoable !== false;
		this.inCode = options.inCode || false;
		this.inCodeMark = options.inCodeMark !== false;
	}
}
function stringHandler(string) {
	return function (state, match, start, end) {
		let insert = string;
		if (match[1]) {
			let offset = match[0].lastIndexOf(match[1]);
			insert += match[0].slice(offset + match[1].length);
			start += offset;
			let cutOff = start - end;
			if (cutOff > 0) {
				insert = match[0].slice(offset - cutOff, offset) + insert;
				start = end;
			}
		}
		return state.tr.insertText(insert, start, end);
	};
}
const MAX_MATCH = 500;
/// Create an input rules plugin. When enabled, it will cause text
/// input that matches any of the given rules to trigger the rule's
/// action.
function inputRules({ rules }) {
	let plugin = new Plugin({
		state: {
			init() {
				return null;
			},
			apply(tr, prev) {
				let stored = tr.getMeta(this);
				if (stored) return stored;
				return tr.selectionSet || tr.docChanged ? null : prev;
			},
		},
		props: {
			handleTextInput(view, from, to, text) {
				return run(view, from, to, text, rules, plugin);
			},
			handleDOMEvents: {
				compositionend: (view) => {
					setTimeout(() => {
						let { $cursor } = view.state.selection;
						if ($cursor) run(view, $cursor.pos, $cursor.pos, "", rules, plugin);
					});
				},
			},
		},
		isInputRules: true,
	});
	return plugin;
}
function run(view, from, to, text, rules, plugin) {
	if (view.composing) return false;
	// Actually insert the text so that its insertion
	// can be tracked by, e.g., the history plugin
	const insertTr = view.state.tr.insertText(text, from, to);
	const mappedFrom = insertTr.mapping.map(from);
	const mappedTo = insertTr.mapping.map(to);
	let state = null,
		$from = insertTr.doc.resolve(mappedFrom);
	let textBefore = $from.parent.textBetween(Math.max(0, $from.parentOffset - MAX_MATCH), $from.parentOffset, null, "\ufffc");
	for (let i = 0; i < rules.length; i++) {
		let rule = rules[i];
		if (!rule.inCodeMark && $from.marks().some((m) => m.type.spec.code)) continue;
		if ($from.parent.type.spec.code) {
			if (!rule.inCode) continue;
		} else if (rule.inCode === "only") {
			continue;
		}
		let match = rule.match.exec(textBefore);
		if (!match || match[0].length < text.length) continue;
		state ??= view.state.apply(insertTr);
		let tr = rule.handler(state, match, mappedFrom - match[0].length, mappedTo);
		if (!tr) continue;
		view.dispatch(insertTr);
		view.dispatch(closeHistory(view.state.tr));
		if (rule.undoable)
			tr.setMeta(plugin, {
				transform: tr,
				from: mappedFrom,
				to: mappedTo,
				text,
			});
		view.dispatch(tr);
		return true;
	}
	return false;
}

/// Converts double dashes to an emdash.
new InputRule(/--$/, "—", { inCodeMark: false });
/// Converts three dots to an ellipsis character.
new InputRule(/\.\.\.$/, "…", { inCodeMark: false });
/// “Smart” opening double quotes.
new InputRule(/(?:^|[\s\{\[\(\<'"\u2018\u201C])(")$/, "“", { inCodeMark: false });
/// “Smart” closing double quotes.
new InputRule(/"$/, "”", { inCodeMark: false });
/// “Smart” opening single quotes.
new InputRule(/(?:^|[\s\{\[\(\<'"\u2018\u201C])(')$/, "‘", { inCodeMark: false });
/// “Smart” closing single quotes.
new InputRule(/'$/, "’", { inCodeMark: false });

// src/gap-cursor/gap-cursor.ts
var Gapcursor = Extension.create({
	name: "gapCursor",
	addProseMirrorPlugins() {
		return [gapCursor()];
	},
	extendNodeSchema(extension) {
		var _a;
		const context = {
			name: extension.name,
			options: extension.options,
			storage: extension.storage,
		};
		return {
			allowGapCursor: (_a = callOrReturn(getExtensionField(extension, "allowGapCursor", context))) != null ? _a : null,
		};
	},
});

const s = new PluginKey("blocknote-show-selection"),
	b = a$1(({ editor: a }) => {
		const e = f$2(
			{ enabledSet: /* @__PURE__ */ new Set() },
			{
				onUpdate() {
					a.transact((t) => t.setMeta(s, {}));
				},
			},
		);
		return {
			key: "showSelection",
			store: e,
			prosemirrorPlugins: [
				new Plugin({
					key: s,
					props: {
						decorations: (t) => {
							const { doc: o, selection: n } = t;
							if (e.state.enabledSet.size === 0) return DecorationSet.empty;
							const i = Decoration.inline(n.from, n.to, {
								"data-show-selection": "true",
							});
							return DecorationSet.create(o, [i]);
						},
					},
				}),
			],
			/**
			 * Show or hide the selection decoration
			 *
			 * @param shouldShow - Whether to show the selection decoration
			 * @param key - The key of the selection to show or hide,
			 * this is necessary to prevent disabling ShowSelection from one place
			 * will interfere with other parts of the code that need to show the selection decoration
			 * (e.g.: CreateLinkButton and AIExtension)
			 */
			showSelection(t, o) {
				e.setState({
					enabledSet: t ? /* @__PURE__ */ new Set([...e.state.enabledSet, o]) : new Set([...e.state.enabledSet].filter((n) => n !== o)),
				});
			},
		};
	});

/**
 * @import {
 *   InitialConstruct,
 *   Initializer,
 *   State,
 *   TokenizeContext,
 *   Token
 * } from 'micromark-util-types'
 */

/** @type {InitialConstruct} */
const content = {
	tokenize: initializeContent,
};

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Initializer}
 *   Content.
 */
function initializeContent(effects) {
	const contentStart = effects.attempt(this.parser.constructs.contentInitial, afterContentStartConstruct, paragraphInitial);
	/** @type {Token} */
	let previous;
	return contentStart;

	/** @type {State} */
	function afterContentStartConstruct(code) {
		if (code === null) {
			effects.consume(code);
			return;
		}
		effects.enter("lineEnding");
		effects.consume(code);
		effects.exit("lineEnding");
		return factorySpace(effects, contentStart, "linePrefix");
	}

	/** @type {State} */
	function paragraphInitial(code) {
		effects.enter("paragraph");
		return lineStart(code);
	}

	/** @type {State} */
	function lineStart(code) {
		const token = effects.enter("chunkText", {
			contentType: "text",
			previous,
		});
		if (previous) {
			previous.next = token;
		}
		previous = token;
		return data(code);
	}

	/** @type {State} */
	function data(code) {
		if (code === null) {
			effects.exit("chunkText");
			effects.exit("paragraph");
			effects.consume(code);
			return;
		}
		if (markdownLineEnding(code)) {
			effects.consume(code);
			effects.exit("chunkText");
			return lineStart;
		}

		// Data.
		effects.consume(code);
		return data;
	}
}

/**
 * @import {
 *   Construct,
 *   ContainerState,
 *   InitialConstruct,
 *   Initializer,
 *   Point,
 *   State,
 *   TokenizeContext,
 *   Tokenizer,
 *   Token
 * } from 'micromark-util-types'
 */

/** @type {InitialConstruct} */
const document$2 = {
	tokenize: initializeDocument,
};

/** @type {Construct} */
const containerConstruct = {
	tokenize: tokenizeContainer,
};

/**
 * @this {TokenizeContext}
 *   Self.
 * @type {Initializer}
 *   Initializer.
 */
function initializeDocument(effects) {
	const self = this;
	/** @type {Array<StackItem>} */
	const stack = [];
	let continued = 0;
	/** @type {TokenizeContext | undefined} */
	let childFlow;
	/** @type {Token | undefined} */
	let childToken;
	/** @type {number} */
	let lineStartOffset;
	return start;

	/** @type {State} */
	function start(code) {
		// First we iterate through the open blocks, starting with the root
		// document, and descending through last children down to the last open
		// block.
		// Each block imposes a condition that the line must satisfy if the block is
		// to remain open.
		// For example, a block quote requires a `>` character.
		// A paragraph requires a non-blank line.
		// In this phase we may match all or just some of the open blocks.
		// But we cannot close unmatched blocks yet, because we may have a lazy
		// continuation line.
		if (continued < stack.length) {
			const item = stack[continued];
			self.containerState = item[1];
			return effects.attempt(item[0].continuation, documentContinue, checkNewContainers)(code);
		}

		// Done.
		return checkNewContainers(code);
	}

	/** @type {State} */
	function documentContinue(code) {
		continued++;

		// Note: this field is called `_closeFlow` but it also closes containers.
		// Perhaps a good idea to rename it but it’s already used in the wild by
		// extensions.
		if (self.containerState._closeFlow) {
			self.containerState._closeFlow = undefined;
			if (childFlow) {
				closeFlow();
			}

			// Note: this algorithm for moving events around is similar to the
			// algorithm when dealing with lazy lines in `writeToChild`.
			const indexBeforeExits = self.events.length;
			let indexBeforeFlow = indexBeforeExits;
			/** @type {Point | undefined} */
			let point;

			// Find the flow chunk.
			while (indexBeforeFlow--) {
				if (self.events[indexBeforeFlow][0] === "exit" && self.events[indexBeforeFlow][1].type === "chunkFlow") {
					point = self.events[indexBeforeFlow][1].end;
					break;
				}
			}
			exitContainers(continued);

			// Fix positions.
			let index = indexBeforeExits;
			while (index < self.events.length) {
				self.events[index][1].end = {
					...point,
				};
				index++;
			}

			// Inject the exits earlier (they’re still also at the end).
			splice(self.events, indexBeforeFlow + 1, 0, self.events.slice(indexBeforeExits));

			// Discard the duplicate exits.
			self.events.length = index;
			return checkNewContainers(code);
		}
		return start(code);
	}

	/** @type {State} */
	function checkNewContainers(code) {
		// Next, after consuming the continuation markers for existing blocks, we
		// look for new block starts (e.g. `>` for a block quote).
		// If we encounter a new block start, we close any blocks unmatched in
		// step 1 before creating the new block as a child of the last matched
		// block.
		if (continued === stack.length) {
			// No need to `check` whether there’s a container, of `exitContainers`
			// would be moot.
			// We can instead immediately `attempt` to parse one.
			if (!childFlow) {
				return documentContinued(code);
			}

			// If we have concrete content, such as block HTML or fenced code,
			// we can’t have containers “pierce” into them, so we can immediately
			// start.
			if (childFlow.currentConstruct && childFlow.currentConstruct.concrete) {
				return flowStart(code);
			}

			// If we do have flow, it could still be a blank line,
			// but we’d be interrupting it w/ a new container if there’s a current
			// construct.
			// To do: next major: remove `_gfmTableDynamicInterruptHack` (no longer
			// needed in micromark-extension-gfm-table@1.0.6).
			self.interrupt = Boolean(childFlow.currentConstruct && !childFlow._gfmTableDynamicInterruptHack);
		}

		// Check if there is a new container.
		self.containerState = {};
		return effects.check(containerConstruct, thereIsANewContainer, thereIsNoNewContainer)(code);
	}

	/** @type {State} */
	function thereIsANewContainer(code) {
		if (childFlow) closeFlow();
		exitContainers(continued);
		return documentContinued(code);
	}

	/** @type {State} */
	function thereIsNoNewContainer(code) {
		self.parser.lazy[self.now().line] = continued !== stack.length;
		lineStartOffset = self.now().offset;
		return flowStart(code);
	}

	/** @type {State} */
	function documentContinued(code) {
		// Try new containers.
		self.containerState = {};
		return effects.attempt(containerConstruct, containerContinue, flowStart)(code);
	}

	/** @type {State} */
	function containerContinue(code) {
		continued++;
		stack.push([self.currentConstruct, self.containerState]);
		// Try another.
		return documentContinued(code);
	}

	/** @type {State} */
	function flowStart(code) {
		if (code === null) {
			if (childFlow) closeFlow();
			exitContainers(0);
			effects.consume(code);
			return;
		}
		childFlow = childFlow || self.parser.flow(self.now());
		effects.enter("chunkFlow", {
			_tokenizer: childFlow,
			contentType: "flow",
			previous: childToken,
		});
		return flowContinue(code);
	}

	/** @type {State} */
	function flowContinue(code) {
		if (code === null) {
			writeToChild(effects.exit("chunkFlow"), true);
			exitContainers(0);
			effects.consume(code);
			return;
		}
		if (markdownLineEnding(code)) {
			effects.consume(code);
			writeToChild(effects.exit("chunkFlow"));
			// Get ready for the next line.
			continued = 0;
			self.interrupt = undefined;
			return start;
		}
		effects.consume(code);
		return flowContinue;
	}

	/**
	 * @param {Token} token
	 *   Token.
	 * @param {boolean | undefined} [endOfFile]
	 *   Whether the token is at the end of the file (default: `false`).
	 * @returns {undefined}
	 *   Nothing.
	 */
	function writeToChild(token, endOfFile) {
		const stream = self.sliceStream(token);
		if (endOfFile) stream.push(null);
		token.previous = childToken;
		if (childToken) childToken.next = token;
		childToken = token;
		childFlow.defineSkip(token.start);
		childFlow.write(stream);

		// Alright, so we just added a lazy line:
		//
		// ```markdown
		// > a
		// b.
		//
		// Or:
		//
		// > ~~~c
		// d
		//
		// Or:
		//
		// > | e |
		// f
		// ```
		//
		// The construct in the second example (fenced code) does not accept lazy
		// lines, so it marked itself as done at the end of its first line, and
		// then the content construct parses `d`.
		// Most constructs in markdown match on the first line: if the first line
		// forms a construct, a non-lazy line can’t “unmake” it.
		//
		// The construct in the third example is potentially a GFM table, and
		// those are *weird*.
		// It *could* be a table, from the first line, if the following line
		// matches a condition.
		// In this case, that second line is lazy, which “unmakes” the first line
		// and turns the whole into one content block.
		//
		// We’ve now parsed the non-lazy and the lazy line, and can figure out
		// whether the lazy line started a new flow block.
		// If it did, we exit the current containers between the two flow blocks.
		if (self.parser.lazy[token.start.line]) {
			let index = childFlow.events.length;
			while (index--) {
				if (
					// The token starts before the line ending…
					childFlow.events[index][1].start.offset < lineStartOffset &&
					// …and either is not ended yet…
					(!childFlow.events[index][1].end ||
						// …or ends after it.
						childFlow.events[index][1].end.offset > lineStartOffset)
				) {
					// Exit: there’s still something open, which means it’s a lazy line
					// part of something.
					return;
				}
			}

			// Note: this algorithm for moving events around is similar to the
			// algorithm when closing flow in `documentContinue`.
			const indexBeforeExits = self.events.length;
			let indexBeforeFlow = indexBeforeExits;
			/** @type {boolean | undefined} */
			let seen;
			/** @type {Point | undefined} */
			let point;

			// Find the previous chunk (the one before the lazy line).
			while (indexBeforeFlow--) {
				if (self.events[indexBeforeFlow][0] === "exit" && self.events[indexBeforeFlow][1].type === "chunkFlow") {
					if (seen) {
						point = self.events[indexBeforeFlow][1].end;
						break;
					}
					seen = true;
				}
			}
			exitContainers(continued);

			// Fix positions.
			index = indexBeforeExits;
			while (index < self.events.length) {
				self.events[index][1].end = {
					...point,
				};
				index++;
			}

			// Inject the exits earlier (they’re still also at the end).
			splice(self.events, indexBeforeFlow + 1, 0, self.events.slice(indexBeforeExits));

			// Discard the duplicate exits.
			self.events.length = index;
		}
	}

	/**
	 * @param {number} size
	 *   Size.
	 * @returns {undefined}
	 *   Nothing.
	 */
	function exitContainers(size) {
		let index = stack.length;

		// Exit open containers.
		while (index-- > size) {
			const entry = stack[index];
			self.containerState = entry[1];
			entry[0].exit.call(self, effects);
		}
		stack.length = size;
	}
	function closeFlow() {
		childFlow.write([null]);
		childToken = undefined;
		childFlow = undefined;
		self.containerState._closeFlow = undefined;
	}
}

/**
 * @this {TokenizeContext}
 *   Context.
 * @type {Tokenizer}
 *   Tokenizer.
 */
function tokenizeContainer(effects, ok, nok) {
	// Always populated by defaults.

	return factorySpace(effects, effects.attempt(this.parser.constructs.document, ok, nok), "linePrefix", this.parser.constructs.disable.null.includes("codeIndented") ? undefined : 4);
}

/**
 * @import {
 *   InitialConstruct,
 *   Initializer,
 *   State,
 *   TokenizeContext
 * } from 'micromark-util-types'
 */

/** @type {InitialConstruct} */
const flow$1 = {
	tokenize: initializeFlow,
};

/**
 * @this {TokenizeContext}
 *   Self.
 * @type {Initializer}
 *   Initializer.
 */
function initializeFlow(effects) {
	const self = this;
	const initial = effects.attempt(
		// Try to parse a blank line.
		blankLine,
		atBlankEnding,
		// Try to parse initial flow (essentially, only code).
		effects.attempt(
			this.parser.constructs.flowInitial,
			afterConstruct,
			factorySpace(effects, effects.attempt(this.parser.constructs.flow, afterConstruct, effects.attempt(content$1, afterConstruct)), "linePrefix"),
		),
	);
	return initial;

	/** @type {State} */
	function atBlankEnding(code) {
		if (code === null) {
			effects.consume(code);
			return;
		}
		effects.enter("lineEndingBlank");
		effects.consume(code);
		effects.exit("lineEndingBlank");
		self.currentConstruct = undefined;
		return initial;
	}

	/** @type {State} */
	function afterConstruct(code) {
		if (code === null) {
			effects.consume(code);
			return;
		}
		effects.enter("lineEnding");
		effects.consume(code);
		effects.exit("lineEnding");
		self.currentConstruct = undefined;
		return initial;
	}
}

/**
 * @import {
 *   Code,
 *   InitialConstruct,
 *   Initializer,
 *   Resolver,
 *   State,
 *   TokenizeContext
 * } from 'micromark-util-types'
 */

const resolver = {
	resolveAll: createResolver(),
};
const string$1 = initializeFactory("string");
const text$3 = initializeFactory("text");

/**
 * @param {'string' | 'text'} field
 *   Field.
 * @returns {InitialConstruct}
 *   Construct.
 */
function initializeFactory(field) {
	return {
		resolveAll: createResolver(field === "text" ? resolveAllLineSuffixes : undefined),
		tokenize: initializeText,
	};

	/**
	 * @this {TokenizeContext}
	 *   Context.
	 * @type {Initializer}
	 */
	function initializeText(effects) {
		const self = this;
		const constructs = this.parser.constructs[field];
		const text = effects.attempt(constructs, start, notText);
		return start;

		/** @type {State} */
		function start(code) {
			return atBreak(code) ? text(code) : notText(code);
		}

		/** @type {State} */
		function notText(code) {
			if (code === null) {
				effects.consume(code);
				return;
			}
			effects.enter("data");
			effects.consume(code);
			return data;
		}

		/** @type {State} */
		function data(code) {
			if (atBreak(code)) {
				effects.exit("data");
				return text(code);
			}

			// Data.
			effects.consume(code);
			return data;
		}

		/**
		 * @param {Code} code
		 *   Code.
		 * @returns {boolean}
		 *   Whether the code is a break.
		 */
		function atBreak(code) {
			if (code === null) {
				return true;
			}
			const list = constructs[code];
			let index = -1;
			if (list) {
				// Always populated by defaults.

				while (++index < list.length) {
					const item = list[index];
					if (!item.previous || item.previous.call(self, self.previous)) {
						return true;
					}
				}
			}
			return false;
		}
	}
}

/**
 * @param {Resolver | undefined} [extraResolver]
 *   Resolver.
 * @returns {Resolver}
 *   Resolver.
 */
function createResolver(extraResolver) {
	return resolveAllText;

	/** @type {Resolver} */
	function resolveAllText(events, context) {
		let index = -1;
		/** @type {number | undefined} */
		let enter;

		// A rather boring computation (to merge adjacent `data` events) which
		// improves mm performance by 29%.
		while (++index <= events.length) {
			if (enter === undefined) {
				if (events[index] && events[index][1].type === "data") {
					enter = index;
					index++;
				}
			} else if (!events[index] || events[index][1].type !== "data") {
				// Don’t do anything if there is one data token.
				if (index !== enter + 2) {
					events[enter][1].end = events[index - 1][1].end;
					events.splice(enter + 2, index - enter - 2);
					index = enter + 2;
				}
				enter = undefined;
			}
		}
		return extraResolver ? extraResolver(events, context) : events;
	}
}

/**
 * A rather ugly set of instructions which again looks at chunks in the input
 * stream.
 * The reason to do this here is that it is *much* faster to parse in reverse.
 * And that we can’t hook into `null` to split the line suffix before an EOF.
 * To do: figure out if we can make this into a clean utility, or even in core.
 * As it will be useful for GFMs literal autolink extension (and maybe even
 * tables?)
 *
 * @type {Resolver}
 */
function resolveAllLineSuffixes(events, context) {
	let eventIndex = 0; // Skip first.

	while (++eventIndex <= events.length) {
		if ((eventIndex === events.length || events[eventIndex][1].type === "lineEnding") && events[eventIndex - 1][1].type === "data") {
			const data = events[eventIndex - 1][1];
			const chunks = context.sliceStream(data);
			let index = chunks.length;
			let bufferIndex = -1;
			let size = 0;
			/** @type {boolean | undefined} */
			let tabs;
			while (index--) {
				const chunk = chunks[index];
				if (typeof chunk === "string") {
					bufferIndex = chunk.length;
					while (chunk.charCodeAt(bufferIndex - 1) === 32) {
						size++;
						bufferIndex--;
					}
					if (bufferIndex) break;
					bufferIndex = -1;
				}
				// Number
				else if (chunk === -2) {
					tabs = true;
					size++;
				} else if (chunk === -1);
				else {
					// Replacement character, exit.
					index++;
					break;
				}
			}

			// Allow final trailing whitespace.
			if (context._contentTypeTextTrailing && eventIndex === events.length) {
				size = 0;
			}
			if (size) {
				const token = {
					type: eventIndex === events.length || tabs || size < 2 ? "lineSuffix" : "hardBreakTrailing",
					start: {
						_bufferIndex: index ? bufferIndex : data.start._bufferIndex + bufferIndex,
						_index: data.start._index + index,
						line: data.end.line,
						column: data.end.column - size,
						offset: data.end.offset - size,
					},
					end: {
						...data.end,
					},
				};
				data.end = {
					...token.start,
				};
				if (data.start.offset === data.end.offset) {
					Object.assign(data, token);
				} else {
					events.splice(eventIndex, 0, ["enter", token, context], ["exit", token, context]);
					eventIndex += 2;
				}
			}
			eventIndex++;
		}
	}
	return events;
}

/**
 * @import {Extension} from 'micromark-util-types'
 */

/** @satisfies {Extension['document']} */
const document$1 = {
	[42]: list$1,
	[43]: list$1,
	[45]: list$1,
	[48]: list$1,
	[49]: list$1,
	[50]: list$1,
	[51]: list$1,
	[52]: list$1,
	[53]: list$1,
	[54]: list$1,
	[55]: list$1,
	[56]: list$1,
	[57]: list$1,
	[62]: blockQuote,
};

/** @satisfies {Extension['contentInitial']} */
const contentInitial = {
	[91]: definition,
};

/** @satisfies {Extension['flowInitial']} */
const flowInitial = {
	[-2]: codeIndented,
	[-1]: codeIndented,
	[32]: codeIndented,
};

/** @satisfies {Extension['flow']} */
const flow = {
	[35]: headingAtx,
	[42]: thematicBreak$1,
	[45]: [setextUnderline, thematicBreak$1],
	[60]: htmlFlow,
	[61]: setextUnderline,
	[95]: thematicBreak$1,
	[96]: codeFenced,
	[126]: codeFenced,
};

/** @satisfies {Extension['string']} */
const string = {
	[38]: characterReference,
	[92]: characterEscape,
};

/** @satisfies {Extension['text']} */
const text$2 = {
	[-5]: lineEnding,
	[-4]: lineEnding,
	[-3]: lineEnding,
	[33]: labelStartImage,
	[38]: characterReference,
	[42]: attention,
	[60]: [autolink, htmlText],
	[91]: labelStartLink,
	[92]: [hardBreakEscape, characterEscape],
	[93]: labelEnd,
	[95]: attention,
	[96]: codeText,
};

/** @satisfies {Extension['insideSpan']} */
const insideSpan = {
	null: [attention, resolver],
};

/** @satisfies {Extension['attentionMarkers']} */
const attentionMarkers = {
	null: [42, 95],
};

/** @satisfies {Extension['disable']} */
const disable = {
	null: [],
};

const defaultConstructs = /*#__PURE__*/ Object.freeze(
	/*#__PURE__*/ Object.defineProperty(
		{
			__proto__: null,
			attentionMarkers,
			contentInitial,
			disable,
			document: document$1,
			flow,
			flowInitial,
			insideSpan,
			string,
			text: text$2,
		},
		Symbol.toStringTag,
		{ value: "Module" },
	),
);

/**
 * @import {
 *   Chunk,
 *   Code,
 *   ConstructRecord,
 *   Construct,
 *   Effects,
 *   InitialConstruct,
 *   ParseContext,
 *   Point,
 *   State,
 *   TokenizeContext,
 *   Token
 * } from 'micromark-util-types'
 */

/**
 * Create a tokenizer.
 * Tokenizers deal with one type of data (e.g., containers, flow, text).
 * The parser is the object dealing with it all.
 * `initialize` works like other constructs, except that only its `tokenize`
 * function is used, in which case it doesn’t receive an `ok` or `nok`.
 * `from` can be given to set the point before the first character, although
 * when further lines are indented, they must be set with `defineSkip`.
 *
 * @param {ParseContext} parser
 *   Parser.
 * @param {InitialConstruct} initialize
 *   Construct.
 * @param {Omit<Point, '_bufferIndex' | '_index'> | undefined} [from]
 *   Point (optional).
 * @returns {TokenizeContext}
 *   Context.
 */
function createTokenizer(parser, initialize, from) {
	/** @type {Point} */
	let point = {
		_bufferIndex: -1,
		_index: 0,
		line: (from && from.line) || 1,
		column: (from && from.column) || 1,
		offset: (from && from.offset) || 0,
	};
	/** @type {Record<string, number>} */
	const columnStart = {};
	/** @type {Array<Construct>} */
	const resolveAllConstructs = [];
	/** @type {Array<Chunk>} */
	let chunks = [];
	/** @type {Array<Token>} */
	let stack = [];

	/**
	 * Tools used for tokenizing.
	 *
	 * @type {Effects}
	 */
	const effects = {
		attempt: constructFactory(onsuccessfulconstruct),
		check: constructFactory(onsuccessfulcheck),
		consume,
		enter,
		exit,
		interrupt: constructFactory(onsuccessfulcheck, {
			interrupt: true,
		}),
	};

	/**
	 * State and tools for resolving and serializing.
	 *
	 * @type {TokenizeContext}
	 */
	const context = {
		code: null,
		containerState: {},
		defineSkip,
		events: [],
		now,
		parser,
		previous: null,
		sliceSerialize,
		sliceStream,
		write,
	};

	/**
	 * The state function.
	 *
	 * @type {State | undefined}
	 */
	let state = initialize.tokenize.call(context, effects);
	if (initialize.resolveAll) {
		resolveAllConstructs.push(initialize);
	}
	return context;

	/** @type {TokenizeContext['write']} */
	function write(slice) {
		chunks = push(chunks, slice);
		main();

		// Exit if we’re not done, resolve might change stuff.
		if (chunks[chunks.length - 1] !== null) {
			return [];
		}
		addResult(initialize, 0);

		// Otherwise, resolve, and exit.
		context.events = resolveAll(resolveAllConstructs, context.events, context);
		return context.events;
	}

	//
	// Tools.
	//

	/** @type {TokenizeContext['sliceSerialize']} */
	function sliceSerialize(token, expandTabs) {
		return serializeChunks(sliceStream(token), expandTabs);
	}

	/** @type {TokenizeContext['sliceStream']} */
	function sliceStream(token) {
		return sliceChunks(chunks, token);
	}

	/** @type {TokenizeContext['now']} */
	function now() {
		// This is a hot path, so we clone manually instead of `Object.assign({}, point)`
		const { _bufferIndex, _index, line, column, offset } = point;
		return {
			_bufferIndex,
			_index,
			line,
			column,
			offset,
		};
	}

	/** @type {TokenizeContext['defineSkip']} */
	function defineSkip(value) {
		columnStart[value.line] = value.column;
		accountForPotentialSkip();
	}

	//
	// State management.
	//

	/**
	 * Main loop (note that `_index` and `_bufferIndex` in `point` are modified by
	 * `consume`).
	 * Here is where we walk through the chunks, which either include strings of
	 * several characters, or numerical character codes.
	 * The reason to do this in a loop instead of a call is so the stack can
	 * drain.
	 *
	 * @returns {undefined}
	 *   Nothing.
	 */
	function main() {
		/** @type {number} */
		let chunkIndex;
		while (point._index < chunks.length) {
			const chunk = chunks[point._index];

			// If we’re in a buffer chunk, loop through it.
			if (typeof chunk === "string") {
				chunkIndex = point._index;
				if (point._bufferIndex < 0) {
					point._bufferIndex = 0;
				}
				while (point._index === chunkIndex && point._bufferIndex < chunk.length) {
					go(chunk.charCodeAt(point._bufferIndex));
				}
			} else {
				go(chunk);
			}
		}
	}

	/**
	 * Deal with one code.
	 *
	 * @param {Code} code
	 *   Code.
	 * @returns {undefined}
	 *   Nothing.
	 */
	function go(code) {
		state = state(code);
	}

	/** @type {Effects['consume']} */
	function consume(code) {
		if (markdownLineEnding(code)) {
			point.line++;
			point.column = 1;
			point.offset += code === -3 ? 2 : 1;
			accountForPotentialSkip();
		} else if (code !== -1) {
			point.column++;
			point.offset++;
		}

		// Not in a string chunk.
		if (point._bufferIndex < 0) {
			point._index++;
		} else {
			point._bufferIndex++;

			// At end of string chunk.
			if (
				point._bufferIndex ===
				// Points w/ non-negative `_bufferIndex` reference
				// strings.
				/** @type {string} */
				chunks[point._index].length
			) {
				point._bufferIndex = -1;
				point._index++;
			}
		}

		// Expose the previous character.
		context.previous = code;
	}

	/** @type {Effects['enter']} */
	function enter(type, fields) {
		/** @type {Token} */
		// @ts-expect-error Patch instead of assign required fields to help GC.
		const token = fields || {};
		token.type = type;
		token.start = now();
		context.events.push(["enter", token, context]);
		stack.push(token);
		return token;
	}

	/** @type {Effects['exit']} */
	function exit(type) {
		const token = stack.pop();
		token.end = now();
		context.events.push(["exit", token, context]);
		return token;
	}

	/**
	 * Use results.
	 *
	 * @type {ReturnHandle}
	 */
	function onsuccessfulconstruct(construct, info) {
		addResult(construct, info.from);
	}

	/**
	 * Discard results.
	 *
	 * @type {ReturnHandle}
	 */
	function onsuccessfulcheck(_, info) {
		info.restore();
	}

	/**
	 * Factory to attempt/check/interrupt.
	 *
	 * @param {ReturnHandle} onreturn
	 *   Callback.
	 * @param {{interrupt?: boolean | undefined} | undefined} [fields]
	 *   Fields.
	 */
	function constructFactory(onreturn, fields) {
		return hook;

		/**
		 * Handle either an object mapping codes to constructs, a list of
		 * constructs, or a single construct.
		 *
		 * @param {Array<Construct> | ConstructRecord | Construct} constructs
		 *   Constructs.
		 * @param {State} returnState
		 *   State.
		 * @param {State | undefined} [bogusState]
		 *   State.
		 * @returns {State}
		 *   State.
		 */
		function hook(constructs, returnState, bogusState) {
			/** @type {ReadonlyArray<Construct>} */
			let listOfConstructs;
			/** @type {number} */
			let constructIndex;
			/** @type {Construct} */
			let currentConstruct;
			/** @type {Info} */
			let info;
			return (
				Array.isArray(constructs) /* c8 ignore next 1 */ ? handleListOfConstructs(constructs)
				: "tokenize" in constructs ?
					// Looks like a construct.
					handleListOfConstructs([/** @type {Construct} */ constructs])
				:	handleMapOfConstructs(constructs)
			);

			/**
			 * Handle a list of construct.
			 *
			 * @param {ConstructRecord} map
			 *   Constructs.
			 * @returns {State}
			 *   State.
			 */
			function handleMapOfConstructs(map) {
				return start;

				/** @type {State} */
				function start(code) {
					const left = code !== null && map[code];
					const all = code !== null && map.null;
					const list = [
						// To do: add more extension tests.
						/* c8 ignore next 2 */
						...(Array.isArray(left) ? left
						: left ? [left]
						: []),
						...(Array.isArray(all) ? all
						: all ? [all]
						: []),
					];
					return handleListOfConstructs(list)(code);
				}
			}

			/**
			 * Handle a list of construct.
			 *
			 * @param {ReadonlyArray<Construct>} list
			 *   Constructs.
			 * @returns {State}
			 *   State.
			 */
			function handleListOfConstructs(list) {
				listOfConstructs = list;
				constructIndex = 0;
				if (list.length === 0) {
					return bogusState;
				}
				return handleConstruct(list[constructIndex]);
			}

			/**
			 * Handle a single construct.
			 *
			 * @param {Construct} construct
			 *   Construct.
			 * @returns {State}
			 *   State.
			 */
			function handleConstruct(construct) {
				return start;

				/** @type {State} */
				function start(code) {
					// To do: not needed to store if there is no bogus state, probably?
					// Currently doesn’t work because `inspect` in document does a check
					// w/o a bogus, which doesn’t make sense. But it does seem to help perf
					// by not storing.
					info = store();
					currentConstruct = construct;
					if (!construct.partial) {
						context.currentConstruct = construct;
					}

					// Always populated by defaults.

					if (construct.name && context.parser.constructs.disable.null.includes(construct.name)) {
						return nok();
					}
					return construct.tokenize.call(
						// If we do have fields, create an object w/ `context` as its
						// prototype.
						// This allows a “live binding”, which is needed for `interrupt`.
						fields ? Object.assign(Object.create(context), fields) : context,
						effects,
						ok,
						nok,
					)(code);
				}
			}

			/** @type {State} */
			function ok(code) {
				onreturn(currentConstruct, info);
				return returnState;
			}

			/** @type {State} */
			function nok(code) {
				info.restore();
				if (++constructIndex < listOfConstructs.length) {
					return handleConstruct(listOfConstructs[constructIndex]);
				}
				return bogusState;
			}
		}
	}

	/**
	 * @param {Construct} construct
	 *   Construct.
	 * @param {number} from
	 *   From.
	 * @returns {undefined}
	 *   Nothing.
	 */
	function addResult(construct, from) {
		if (construct.resolveAll && !resolveAllConstructs.includes(construct)) {
			resolveAllConstructs.push(construct);
		}
		if (construct.resolve) {
			splice(context.events, from, context.events.length - from, construct.resolve(context.events.slice(from), context));
		}
		if (construct.resolveTo) {
			context.events = construct.resolveTo(context.events, context);
		}
	}

	/**
	 * Store state.
	 *
	 * @returns {Info}
	 *   Info.
	 */
	function store() {
		const startPoint = now();
		const startPrevious = context.previous;
		const startCurrentConstruct = context.currentConstruct;
		const startEventsIndex = context.events.length;
		const startStack = Array.from(stack);
		return {
			from: startEventsIndex,
			restore,
		};

		/**
		 * Restore state.
		 *
		 * @returns {undefined}
		 *   Nothing.
		 */
		function restore() {
			point = startPoint;
			context.previous = startPrevious;
			context.currentConstruct = startCurrentConstruct;
			context.events.length = startEventsIndex;
			stack = startStack;
			accountForPotentialSkip();
		}
	}

	/**
	 * Move the current point a bit forward in the line when it’s on a column
	 * skip.
	 *
	 * @returns {undefined}
	 *   Nothing.
	 */
	function accountForPotentialSkip() {
		if (point.line in columnStart && point.column < 2) {
			point.column = columnStart[point.line];
			point.offset += columnStart[point.line] - 1;
		}
	}
}

/**
 * Get the chunks from a slice of chunks in the range of a token.
 *
 * @param {ReadonlyArray<Chunk>} chunks
 *   Chunks.
 * @param {Pick<Token, 'end' | 'start'>} token
 *   Token.
 * @returns {Array<Chunk>}
 *   Chunks.
 */
function sliceChunks(chunks, token) {
	const startIndex = token.start._index;
	const startBufferIndex = token.start._bufferIndex;
	const endIndex = token.end._index;
	const endBufferIndex = token.end._bufferIndex;
	/** @type {Array<Chunk>} */
	let view;
	if (startIndex === endIndex) {
		// @ts-expect-error `_bufferIndex` is used on string chunks.
		view = [chunks[startIndex].slice(startBufferIndex, endBufferIndex)];
	} else {
		view = chunks.slice(startIndex, endIndex);
		if (startBufferIndex > -1) {
			const head = view[0];
			if (typeof head === "string") {
				view[0] = head.slice(startBufferIndex);
				/* c8 ignore next 4 -- used to be used, no longer */
			} else {
				view.shift();
			}
		}
		if (endBufferIndex > 0) {
			// @ts-expect-error `_bufferIndex` is used on string chunks.
			view.push(chunks[endIndex].slice(0, endBufferIndex));
		}
	}
	return view;
}

/**
 * Get the string value of a slice of chunks.
 *
 * @param {ReadonlyArray<Chunk>} chunks
 *   Chunks.
 * @param {boolean | undefined} [expandTabs=false]
 *   Whether to expand tabs (default: `false`).
 * @returns {string}
 *   Result.
 */
function serializeChunks(chunks, expandTabs) {
	let index = -1;
	/** @type {Array<string>} */
	const result = [];
	/** @type {boolean | undefined} */
	let atTab;
	while (++index < chunks.length) {
		const chunk = chunks[index];
		/** @type {string} */
		let value;
		if (typeof chunk === "string") {
			value = chunk;
		} else
			switch (chunk) {
				case -5: {
					value = "\r";
					break;
				}
				case -4: {
					value = "\n";
					break;
				}
				case -3: {
					value = "\r" + "\n";
					break;
				}
				case -2: {
					value = expandTabs ? " " : "\t";
					break;
				}
				case -1: {
					if (!expandTabs && atTab) continue;
					value = " ";
					break;
				}
				default: {
					// Currently only replacement character.
					value = String.fromCharCode(chunk);
				}
			}
		atTab = chunk === -2;
		result.push(value);
	}
	return result.join("");
}

/**
 * @import {
 *   Create,
 *   FullNormalizedExtension,
 *   InitialConstruct,
 *   ParseContext,
 *   ParseOptions
 * } from 'micromark-util-types'
 */

/**
 * @param {ParseOptions | null | undefined} [options]
 *   Configuration (optional).
 * @returns {ParseContext}
 *   Parser.
 */
function parse$1(options) {
	const settings = options || {};
	const constructs = /** @type {FullNormalizedExtension} */ combineExtensions([defaultConstructs, ...(settings.extensions || [])]);

	/** @type {ParseContext} */
	const parser = {
		constructs,
		content: create(content),
		defined: [],
		document: create(document$2),
		flow: create(flow$1),
		lazy: {},
		string: create(string$1),
		text: create(text$3),
	};
	return parser;

	/**
	 * @param {InitialConstruct} initial
	 *   Construct to start with.
	 * @returns {Create}
	 *   Create a tokenizer.
	 */
	function create(initial) {
		return creator;
		/** @type {Create} */
		function creator(from) {
			return createTokenizer(parser, initial, from);
		}
	}
}

/**
 * @import {Event} from 'micromark-util-types'
 */

/**
 * @param {Array<Event>} events
 *   Events.
 * @returns {Array<Event>}
 *   Events.
 */
function postprocess(events) {
	while (!subtokenize(events)) {
		// Empty
	}
	return events;
}

/**
 * @import {Chunk, Code, Encoding, Value} from 'micromark-util-types'
 */

/**
 * @callback Preprocessor
 *   Preprocess a value.
 * @param {Value} value
 *   Value.
 * @param {Encoding | null | undefined} [encoding]
 *   Encoding when `value` is a typed array (optional).
 * @param {boolean | null | undefined} [end=false]
 *   Whether this is the last chunk (default: `false`).
 * @returns {Array<Chunk>}
 *   Chunks.
 */

const search = /[\0\t\n\r]/g;

/**
 * @returns {Preprocessor}
 *   Preprocess a value.
 */
function preprocess() {
	let column = 1;
	let buffer = "";
	/** @type {boolean | undefined} */
	let start = true;
	/** @type {boolean | undefined} */
	let atCarriageReturn;
	return preprocessor;

	/** @type {Preprocessor} */
	// eslint-disable-next-line complexity
	function preprocessor(value, encoding, end) {
		/** @type {Array<Chunk>} */
		const chunks = [];
		/** @type {RegExpMatchArray | null} */
		let match;
		/** @type {number} */
		let next;
		/** @type {number} */
		let startPosition;
		/** @type {number} */
		let endPosition;
		/** @type {Code} */
		let code;
		value = buffer + (typeof value === "string" ? value.toString() : new TextDecoder(encoding || undefined).decode(value));
		startPosition = 0;
		buffer = "";
		if (start) {
			// To do: `markdown-rs` actually parses BOMs (byte order mark).
			if (value.charCodeAt(0) === 65279) {
				startPosition++;
			}
			start = undefined;
		}
		while (startPosition < value.length) {
			search.lastIndex = startPosition;
			match = search.exec(value);
			endPosition = match && match.index !== undefined ? match.index : value.length;
			code = value.charCodeAt(endPosition);
			if (!match) {
				buffer = value.slice(startPosition);
				break;
			}
			if (code === 10 && startPosition === endPosition && atCarriageReturn) {
				chunks.push(-3);
				atCarriageReturn = undefined;
			} else {
				if (atCarriageReturn) {
					chunks.push(-5);
					atCarriageReturn = undefined;
				}
				if (startPosition < endPosition) {
					chunks.push(value.slice(startPosition, endPosition));
					column += endPosition - startPosition;
				}
				switch (code) {
					case 0: {
						chunks.push(65533);
						column++;
						break;
					}
					case 9: {
						next = Math.ceil(column / 4) * 4;
						chunks.push(-2);
						while (column++ < next) chunks.push(-1);
						break;
					}
					case 10: {
						chunks.push(-4);
						column = 1;
						break;
					}
					default: {
						atCarriageReturn = true;
						column = 1;
					}
				}
			}
			startPosition = endPosition + 1;
		}
		if (end) {
			if (atCarriageReturn) chunks.push(-5);
			if (buffer) chunks.push(buffer);
			chunks.push(null);
		}
		return chunks;
	}
}

/**
 * @import {
 *   Break,
 *   Blockquote,
 *   Code,
 *   Definition,
 *   Emphasis,
 *   Heading,
 *   Html,
 *   Image,
 *   InlineCode,
 *   Link,
 *   ListItem,
 *   List,
 *   Nodes,
 *   Paragraph,
 *   PhrasingContent,
 *   ReferenceType,
 *   Root,
 *   Strong,
 *   Text,
 *   ThematicBreak
 * } from 'mdast'
 * @import {
 *   Encoding,
 *   Event,
 *   Token,
 *   Value
 * } from 'micromark-util-types'
 * @import {Point} from 'unist'
 * @import {
 *   CompileContext,
 *   CompileData,
 *   Config,
 *   Extension,
 *   Handle,
 *   OnEnterError,
 *   Options
 * } from './types.js'
 */

const own$3 = {}.hasOwnProperty;

/**
 * Turn markdown into a syntax tree.
 *
 * @overload
 * @param {Value} value
 * @param {Encoding | null | undefined} [encoding]
 * @param {Options | null | undefined} [options]
 * @returns {Root}
 *
 * @overload
 * @param {Value} value
 * @param {Options | null | undefined} [options]
 * @returns {Root}
 *
 * @param {Value} value
 *   Markdown to parse.
 * @param {Encoding | Options | null | undefined} [encoding]
 *   Character encoding for when `value` is `Buffer`.
 * @param {Options | null | undefined} [options]
 *   Configuration.
 * @returns {Root}
 *   mdast tree.
 */
function fromMarkdown(value, encoding, options) {
	if (encoding && typeof encoding === "object") {
		options = encoding;
		encoding = undefined;
	}
	return compiler(options)(
		postprocess(
			parse$1(options)
				.document()
				.write(preprocess()(value, encoding, true)),
		),
	);
}

/**
 * Note this compiler only understand complete buffering, not streaming.
 *
 * @param {Options | null | undefined} [options]
 */
function compiler(options) {
	/** @type {Config} */
	const config = {
		transforms: [],
		canContainEols: ["emphasis", "fragment", "heading", "paragraph", "strong"],
		enter: {
			autolink: opener(link),
			autolinkProtocol: onenterdata,
			autolinkEmail: onenterdata,
			atxHeading: opener(heading),
			blockQuote: opener(blockQuote),
			characterEscape: onenterdata,
			characterReference: onenterdata,
			codeFenced: opener(codeFlow),
			codeFencedFenceInfo: buffer,
			codeFencedFenceMeta: buffer,
			codeIndented: opener(codeFlow, buffer),
			codeText: opener(codeText, buffer),
			codeTextData: onenterdata,
			data: onenterdata,
			codeFlowValue: onenterdata,
			definition: opener(definition),
			definitionDestinationString: buffer,
			definitionLabelString: buffer,
			definitionTitleString: buffer,
			emphasis: opener(emphasis),
			hardBreakEscape: opener(hardBreak),
			hardBreakTrailing: opener(hardBreak),
			htmlFlow: opener(html, buffer),
			htmlFlowData: onenterdata,
			htmlText: opener(html, buffer),
			htmlTextData: onenterdata,
			image: opener(image),
			label: buffer,
			link: opener(link),
			listItem: opener(listItem),
			listItemValue: onenterlistitemvalue,
			listOrdered: opener(list, onenterlistordered),
			listUnordered: opener(list),
			paragraph: opener(paragraph),
			reference: onenterreference,
			referenceString: buffer,
			resourceDestinationString: buffer,
			resourceTitleString: buffer,
			setextHeading: opener(heading),
			strong: opener(strong),
			thematicBreak: opener(thematicBreak),
		},
		exit: {
			atxHeading: closer(),
			atxHeadingSequence: onexitatxheadingsequence,
			autolink: closer(),
			autolinkEmail: onexitautolinkemail,
			autolinkProtocol: onexitautolinkprotocol,
			blockQuote: closer(),
			characterEscapeValue: onexitdata,
			characterReferenceMarkerHexadecimal: onexitcharacterreferencemarker,
			characterReferenceMarkerNumeric: onexitcharacterreferencemarker,
			characterReferenceValue: onexitcharacterreferencevalue,
			characterReference: onexitcharacterreference,
			codeFenced: closer(onexitcodefenced),
			codeFencedFence: onexitcodefencedfence,
			codeFencedFenceInfo: onexitcodefencedfenceinfo,
			codeFencedFenceMeta: onexitcodefencedfencemeta,
			codeFlowValue: onexitdata,
			codeIndented: closer(onexitcodeindented),
			codeText: closer(onexitcodetext),
			codeTextData: onexitdata,
			data: onexitdata,
			definition: closer(),
			definitionDestinationString: onexitdefinitiondestinationstring,
			definitionLabelString: onexitdefinitionlabelstring,
			definitionTitleString: onexitdefinitiontitlestring,
			emphasis: closer(),
			hardBreakEscape: closer(onexithardbreak),
			hardBreakTrailing: closer(onexithardbreak),
			htmlFlow: closer(onexithtmlflow),
			htmlFlowData: onexitdata,
			htmlText: closer(onexithtmltext),
			htmlTextData: onexitdata,
			image: closer(onexitimage),
			label: onexitlabel,
			labelText: onexitlabeltext,
			lineEnding: onexitlineending,
			link: closer(onexitlink),
			listItem: closer(),
			listOrdered: closer(),
			listUnordered: closer(),
			paragraph: closer(),
			referenceString: onexitreferencestring,
			resourceDestinationString: onexitresourcedestinationstring,
			resourceTitleString: onexitresourcetitlestring,
			resource: onexitresource,
			setextHeading: closer(onexitsetextheading),
			setextHeadingLineSequence: onexitsetextheadinglinesequence,
			setextHeadingText: onexitsetextheadingtext,
			strong: closer(),
			thematicBreak: closer(),
		},
	};
	configure(config, (options || {}).mdastExtensions || []);

	/** @type {CompileData} */
	const data = {};
	return compile;

	/**
	 * Turn micromark events into an mdast tree.
	 *
	 * @param {Array<Event>} events
	 *   Events.
	 * @returns {Root}
	 *   mdast tree.
	 */
	function compile(events) {
		/** @type {Root} */
		let tree = {
			type: "root",
			children: [],
		};
		/** @type {Omit<CompileContext, 'sliceSerialize'>} */
		const context = {
			stack: [tree],
			tokenStack: [],
			config,
			enter,
			exit,
			buffer,
			resume,
			data,
		};
		/** @type {Array<number>} */
		const listStack = [];
		let index = -1;
		while (++index < events.length) {
			// We preprocess lists to add `listItem` tokens, and to infer whether
			// items the list itself are spread out.
			if (events[index][1].type === "listOrdered" || events[index][1].type === "listUnordered") {
				if (events[index][0] === "enter") {
					listStack.push(index);
				} else {
					const tail = listStack.pop();
					index = prepareList(events, tail, index);
				}
			}
		}
		index = -1;
		while (++index < events.length) {
			const handler = config[events[index][0]];
			if (own$3.call(handler, events[index][1].type)) {
				handler[events[index][1].type].call(
					Object.assign(
						{
							sliceSerialize: events[index][2].sliceSerialize,
						},
						context,
					),
					events[index][1],
				);
			}
		}

		// Handle tokens still being open.
		if (context.tokenStack.length > 0) {
			const tail = context.tokenStack[context.tokenStack.length - 1];
			const handler = tail[1] || defaultOnError;
			handler.call(context, undefined, tail[0]);
		}

		// Figure out `root` position.
		tree.position = {
			start: point(
				events.length > 0 ?
					events[0][1].start
				:	{
						line: 1,
						column: 1,
						offset: 0,
					},
			),
			end: point(
				events.length > 0 ?
					events[events.length - 2][1].end
				:	{
						line: 1,
						column: 1,
						offset: 0,
					},
			),
		};

		// Call transforms.
		index = -1;
		while (++index < config.transforms.length) {
			tree = config.transforms[index](tree) || tree;
		}
		return tree;
	}

	/**
	 * @param {Array<Event>} events
	 * @param {number} start
	 * @param {number} length
	 * @returns {number}
	 */
	function prepareList(events, start, length) {
		let index = start - 1;
		let containerBalance = -1;
		let listSpread = false;
		/** @type {Token | undefined} */
		let listItem;
		/** @type {number | undefined} */
		let lineIndex;
		/** @type {number | undefined} */
		let firstBlankLineIndex;
		/** @type {boolean | undefined} */
		let atMarker;
		while (++index <= length) {
			const event = events[index];
			switch (event[1].type) {
				case "listUnordered":
				case "listOrdered":
				case "blockQuote": {
					if (event[0] === "enter") {
						containerBalance++;
					} else {
						containerBalance--;
					}
					atMarker = undefined;
					break;
				}
				case "lineEndingBlank": {
					if (event[0] === "enter") {
						if (listItem && !atMarker && !containerBalance && !firstBlankLineIndex) {
							firstBlankLineIndex = index;
						}
						atMarker = undefined;
					}
					break;
				}
				case "linePrefix":
				case "listItemValue":
				case "listItemMarker":
				case "listItemPrefix":
				case "listItemPrefixWhitespace": {
					// Empty.

					break;
				}
				default: {
					atMarker = undefined;
				}
			}
			if (
				(!containerBalance && event[0] === "enter" && event[1].type === "listItemPrefix") ||
				(containerBalance === -1 && event[0] === "exit" && (event[1].type === "listUnordered" || event[1].type === "listOrdered"))
			) {
				if (listItem) {
					let tailIndex = index;
					lineIndex = undefined;
					while (tailIndex--) {
						const tailEvent = events[tailIndex];
						if (tailEvent[1].type === "lineEnding" || tailEvent[1].type === "lineEndingBlank") {
							if (tailEvent[0] === "exit") continue;
							if (lineIndex) {
								events[lineIndex][1].type = "lineEndingBlank";
								listSpread = true;
							}
							tailEvent[1].type = "lineEnding";
							lineIndex = tailIndex;
						} else if (
							tailEvent[1].type === "linePrefix" ||
							tailEvent[1].type === "blockQuotePrefix" ||
							tailEvent[1].type === "blockQuotePrefixWhitespace" ||
							tailEvent[1].type === "blockQuoteMarker" ||
							tailEvent[1].type === "listItemIndent"
						);
						else {
							break;
						}
					}
					if (firstBlankLineIndex && (!lineIndex || firstBlankLineIndex < lineIndex)) {
						listItem._spread = true;
					}

					// Fix position.
					listItem.end = Object.assign({}, lineIndex ? events[lineIndex][1].start : event[1].end);
					events.splice(lineIndex || index, 0, ["exit", listItem, event[2]]);
					index++;
					length++;
				}

				// Create a new list item.
				if (event[1].type === "listItemPrefix") {
					/** @type {Token} */
					const item = {
						type: "listItem",
						_spread: false,
						start: Object.assign({}, event[1].start),
						// @ts-expect-error: we’ll add `end` in a second.
						end: undefined,
					};
					listItem = item;
					events.splice(index, 0, ["enter", item, event[2]]);
					index++;
					length++;
					firstBlankLineIndex = undefined;
					atMarker = true;
				}
			}
		}
		events[start][1]._spread = listSpread;
		return length;
	}

	/**
	 * Create an opener handle.
	 *
	 * @param {(token: Token) => Nodes} create
	 *   Create a node.
	 * @param {Handle | undefined} [and]
	 *   Optional function to also run.
	 * @returns {Handle}
	 *   Handle.
	 */
	function opener(create, and) {
		return open;

		/**
		 * @this {CompileContext}
		 * @param {Token} token
		 * @returns {undefined}
		 */
		function open(token) {
			enter.call(this, create(token), token);
			if (and) and.call(this, token);
		}
	}

	/**
	 * @type {CompileContext['buffer']}
	 */
	function buffer() {
		this.stack.push({
			type: "fragment",
			children: [],
		});
	}

	/**
	 * @type {CompileContext['enter']}
	 */
	function enter(node, token, errorHandler) {
		const parent = this.stack[this.stack.length - 1];
		/** @type {Array<Nodes>} */
		const siblings = parent.children;
		siblings.push(node);
		this.stack.push(node);
		this.tokenStack.push([token, errorHandler || undefined]);
		node.position = {
			start: point(token.start),
			// @ts-expect-error: `end` will be patched later.
			end: undefined,
		};
	}

	/**
	 * Create a closer handle.
	 *
	 * @param {Handle | undefined} [and]
	 *   Optional function to also run.
	 * @returns {Handle}
	 *   Handle.
	 */
	function closer(and) {
		return close;

		/**
		 * @this {CompileContext}
		 * @param {Token} token
		 * @returns {undefined}
		 */
		function close(token) {
			if (and) and.call(this, token);
			exit.call(this, token);
		}
	}

	/**
	 * @type {CompileContext['exit']}
	 */
	function exit(token, onExitError) {
		const node = this.stack.pop();
		const open = this.tokenStack.pop();
		if (!open) {
			throw new Error(
				"Cannot close `" +
					token.type +
					"` (" +
					stringifyPosition({
						start: token.start,
						end: token.end,
					}) +
					"): it’s not open",
			);
		} else if (open[0].type !== token.type) {
			if (onExitError) {
				onExitError.call(this, token, open[0]);
			} else {
				const handler = open[1] || defaultOnError;
				handler.call(this, token, open[0]);
			}
		}
		node.position.end = point(token.end);
	}

	/**
	 * @type {CompileContext['resume']}
	 */
	function resume() {
		return toString(this.stack.pop());
	}

	//
	// Handlers.
	//

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */
	function onenterlistordered() {
		this.data.expectingFirstListItemValue = true;
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */
	function onenterlistitemvalue(token) {
		if (this.data.expectingFirstListItemValue) {
			const ancestor = this.stack[this.stack.length - 2];
			ancestor.start = Number.parseInt(this.sliceSerialize(token), 10);
			this.data.expectingFirstListItemValue = undefined;
		}
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */
	function onexitcodefencedfenceinfo() {
		const data = this.resume();
		const node = this.stack[this.stack.length - 1];
		node.lang = data;
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */
	function onexitcodefencedfencemeta() {
		const data = this.resume();
		const node = this.stack[this.stack.length - 1];
		node.meta = data;
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */
	function onexitcodefencedfence() {
		// Exit if this is the closing fence.
		if (this.data.flowCodeInside) return;
		this.buffer();
		this.data.flowCodeInside = true;
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */
	function onexitcodefenced() {
		const data = this.resume();
		const node = this.stack[this.stack.length - 1];
		node.value = data.replace(/^(\r?\n|\r)|(\r?\n|\r)$/g, "");
		this.data.flowCodeInside = undefined;
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */
	function onexitcodeindented() {
		const data = this.resume();
		const node = this.stack[this.stack.length - 1];
		node.value = data.replace(/(\r?\n|\r)$/g, "");
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */
	function onexitdefinitionlabelstring(token) {
		const label = this.resume();
		const node = this.stack[this.stack.length - 1];
		node.label = label;
		node.identifier = normalizeIdentifier(this.sliceSerialize(token)).toLowerCase();
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */
	function onexitdefinitiontitlestring() {
		const data = this.resume();
		const node = this.stack[this.stack.length - 1];
		node.title = data;
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */
	function onexitdefinitiondestinationstring() {
		const data = this.resume();
		const node = this.stack[this.stack.length - 1];
		node.url = data;
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */
	function onexitatxheadingsequence(token) {
		const node = this.stack[this.stack.length - 1];
		if (!node.depth) {
			const depth = this.sliceSerialize(token).length;
			node.depth = depth;
		}
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */
	function onexitsetextheadingtext() {
		this.data.setextHeadingSlurpLineEnding = true;
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */
	function onexitsetextheadinglinesequence(token) {
		const node = this.stack[this.stack.length - 1];
		node.depth = this.sliceSerialize(token).codePointAt(0) === 61 ? 1 : 2;
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */
	function onexitsetextheading() {
		this.data.setextHeadingSlurpLineEnding = undefined;
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */

	function onenterdata(token) {
		const node = this.stack[this.stack.length - 1];
		/** @type {Array<Nodes>} */
		const siblings = node.children;
		let tail = siblings[siblings.length - 1];
		if (!tail || tail.type !== "text") {
			// Add a new text node.
			tail = text();
			tail.position = {
				start: point(token.start),
				// @ts-expect-error: we’ll add `end` later.
				end: undefined,
			};
			siblings.push(tail);
		}
		this.stack.push(tail);
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */

	function onexitdata(token) {
		const tail = this.stack.pop();
		tail.value += this.sliceSerialize(token);
		tail.position.end = point(token.end);
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */

	function onexitlineending(token) {
		const context = this.stack[this.stack.length - 1];
		// If we’re at a hard break, include the line ending in there.
		if (this.data.atHardBreak) {
			const tail = context.children[context.children.length - 1];
			tail.position.end = point(token.end);
			this.data.atHardBreak = undefined;
			return;
		}
		if (!this.data.setextHeadingSlurpLineEnding && config.canContainEols.includes(context.type)) {
			onenterdata.call(this, token);
			onexitdata.call(this, token);
		}
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */

	function onexithardbreak() {
		this.data.atHardBreak = true;
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */

	function onexithtmlflow() {
		const data = this.resume();
		const node = this.stack[this.stack.length - 1];
		node.value = data;
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */

	function onexithtmltext() {
		const data = this.resume();
		const node = this.stack[this.stack.length - 1];
		node.value = data;
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */

	function onexitcodetext() {
		const data = this.resume();
		const node = this.stack[this.stack.length - 1];
		node.value = data;
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */

	function onexitlink() {
		const node = this.stack[this.stack.length - 1];
		// Note: there are also `identifier` and `label` fields on this link node!
		// These are used / cleaned here.

		// To do: clean.
		if (this.data.inReference) {
			/** @type {ReferenceType} */
			const referenceType = this.data.referenceType || "shortcut";
			node.type += "Reference";
			// @ts-expect-error: mutate.
			node.referenceType = referenceType;
			// @ts-expect-error: mutate.
			delete node.url;
			delete node.title;
		} else {
			// @ts-expect-error: mutate.
			delete node.identifier;
			// @ts-expect-error: mutate.
			delete node.label;
		}
		this.data.referenceType = undefined;
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */

	function onexitimage() {
		const node = this.stack[this.stack.length - 1];
		// Note: there are also `identifier` and `label` fields on this link node!
		// These are used / cleaned here.

		// To do: clean.
		if (this.data.inReference) {
			/** @type {ReferenceType} */
			const referenceType = this.data.referenceType || "shortcut";
			node.type += "Reference";
			// @ts-expect-error: mutate.
			node.referenceType = referenceType;
			// @ts-expect-error: mutate.
			delete node.url;
			delete node.title;
		} else {
			// @ts-expect-error: mutate.
			delete node.identifier;
			// @ts-expect-error: mutate.
			delete node.label;
		}
		this.data.referenceType = undefined;
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */

	function onexitlabeltext(token) {
		const string = this.sliceSerialize(token);
		const ancestor = this.stack[this.stack.length - 2];
		// @ts-expect-error: stash this on the node, as it might become a reference
		// later.
		ancestor.label = decodeString(string);
		// @ts-expect-error: same as above.
		ancestor.identifier = normalizeIdentifier(string).toLowerCase();
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */

	function onexitlabel() {
		const fragment = this.stack[this.stack.length - 1];
		const value = this.resume();
		const node = this.stack[this.stack.length - 1];
		// Assume a reference.
		this.data.inReference = true;
		if (node.type === "link") {
			/** @type {Array<PhrasingContent>} */
			const children = fragment.children;
			node.children = children;
		} else {
			node.alt = value;
		}
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */

	function onexitresourcedestinationstring() {
		const data = this.resume();
		const node = this.stack[this.stack.length - 1];
		node.url = data;
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */

	function onexitresourcetitlestring() {
		const data = this.resume();
		const node = this.stack[this.stack.length - 1];
		node.title = data;
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */

	function onexitresource() {
		this.data.inReference = undefined;
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */

	function onenterreference() {
		this.data.referenceType = "collapsed";
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */

	function onexitreferencestring(token) {
		const label = this.resume();
		const node = this.stack[this.stack.length - 1];
		// @ts-expect-error: stash this on the node, as it might become a reference
		// later.
		node.label = label;
		// @ts-expect-error: same as above.
		node.identifier = normalizeIdentifier(this.sliceSerialize(token)).toLowerCase();
		this.data.referenceType = "full";
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */

	function onexitcharacterreferencemarker(token) {
		this.data.characterReferenceType = token.type;
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */
	function onexitcharacterreferencevalue(token) {
		const data = this.sliceSerialize(token);
		const type = this.data.characterReferenceType;
		/** @type {string} */
		let value;
		if (type) {
			value = decodeNumericCharacterReference(data, type === "characterReferenceMarkerNumeric" ? 10 : 16);
			this.data.characterReferenceType = undefined;
		} else {
			const result = decodeNamedCharacterReference(data);
			value = result;
		}
		const tail = this.stack[this.stack.length - 1];
		tail.value += value;
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */
	function onexitcharacterreference(token) {
		const tail = this.stack.pop();
		tail.position.end = point(token.end);
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */
	function onexitautolinkprotocol(token) {
		onexitdata.call(this, token);
		const node = this.stack[this.stack.length - 1];
		node.url = this.sliceSerialize(token);
	}

	/**
	 * @this {CompileContext}
	 * @type {Handle}
	 */
	function onexitautolinkemail(token) {
		onexitdata.call(this, token);
		const node = this.stack[this.stack.length - 1];
		node.url = "mailto:" + this.sliceSerialize(token);
	}

	//
	// Creaters.
	//

	/** @returns {Blockquote} */
	function blockQuote() {
		return {
			type: "blockquote",
			children: [],
		};
	}

	/** @returns {Code} */
	function codeFlow() {
		return {
			type: "code",
			lang: null,
			meta: null,
			value: "",
		};
	}

	/** @returns {InlineCode} */
	function codeText() {
		return {
			type: "inlineCode",
			value: "",
		};
	}

	/** @returns {Definition} */
	function definition() {
		return {
			type: "definition",
			identifier: "",
			label: null,
			title: null,
			url: "",
		};
	}

	/** @returns {Emphasis} */
	function emphasis() {
		return {
			type: "emphasis",
			children: [],
		};
	}

	/** @returns {Heading} */
	function heading() {
		return {
			type: "heading",
			// @ts-expect-error `depth` will be set later.
			depth: 0,
			children: [],
		};
	}

	/** @returns {Break} */
	function hardBreak() {
		return {
			type: "break",
		};
	}

	/** @returns {Html} */
	function html() {
		return {
			type: "html",
			value: "",
		};
	}

	/** @returns {Image} */
	function image() {
		return {
			type: "image",
			title: null,
			url: "",
			alt: null,
		};
	}

	/** @returns {Link} */
	function link() {
		return {
			type: "link",
			title: null,
			url: "",
			children: [],
		};
	}

	/**
	 * @param {Token} token
	 * @returns {List}
	 */
	function list(token) {
		return {
			type: "list",
			ordered: token.type === "listOrdered",
			start: null,
			spread: token._spread,
			children: [],
		};
	}

	/**
	 * @param {Token} token
	 * @returns {ListItem}
	 */
	function listItem(token) {
		return {
			type: "listItem",
			spread: token._spread,
			checked: null,
			children: [],
		};
	}

	/** @returns {Paragraph} */
	function paragraph() {
		return {
			type: "paragraph",
			children: [],
		};
	}

	/** @returns {Strong} */
	function strong() {
		return {
			type: "strong",
			children: [],
		};
	}

	/** @returns {Text} */
	function text() {
		return {
			type: "text",
			value: "",
		};
	}

	/** @returns {ThematicBreak} */
	function thematicBreak() {
		return {
			type: "thematicBreak",
		};
	}
}

/**
 * Copy a point-like value.
 *
 * @param {Point} d
 *   Point-like value.
 * @returns {Point}
 *   unist point.
 */
function point(d) {
	return {
		line: d.line,
		column: d.column,
		offset: d.offset,
	};
}

/**
 * @param {Config} combined
 * @param {Array<Array<Extension> | Extension>} extensions
 * @returns {undefined}
 */
function configure(combined, extensions) {
	let index = -1;
	while (++index < extensions.length) {
		const value = extensions[index];
		if (Array.isArray(value)) {
			configure(combined, value);
		} else {
			extension(combined, value);
		}
	}
}

/**
 * @param {Config} combined
 * @param {Extension} extension
 * @returns {undefined}
 */
function extension(combined, extension) {
	/** @type {keyof Extension} */
	let key;
	for (key in extension) {
		if (own$3.call(extension, key)) {
			switch (key) {
				case "canContainEols": {
					const right = extension[key];
					if (right) {
						combined[key].push(...right);
					}
					break;
				}
				case "transforms": {
					const right = extension[key];
					if (right) {
						combined[key].push(...right);
					}
					break;
				}
				case "enter":
				case "exit": {
					const right = extension[key];
					if (right) {
						Object.assign(combined[key], right);
					}
					break;
				}
				// No default
			}
		}
	}
}

/** @type {OnEnterError} */
function defaultOnError(left, right) {
	if (left) {
		throw new Error(
			"Cannot close `" +
				left.type +
				"` (" +
				stringifyPosition({
					start: left.start,
					end: left.end,
				}) +
				"): a different token (`" +
				right.type +
				"`, " +
				stringifyPosition({
					start: right.start,
					end: right.end,
				}) +
				") is open",
		);
	} else {
		throw new Error(
			"Cannot close document, a token (`" +
				right.type +
				"`, " +
				stringifyPosition({
					start: right.start,
					end: right.end,
				}) +
				") is still open",
		);
	}
}

/**
 * @typedef {import('mdast').Root} Root
 * @typedef {import('mdast-util-from-markdown').Options} FromMarkdownOptions
 * @typedef {import('unified').Parser<Root>} Parser
 * @typedef {import('unified').Processor<Root>} Processor
 */

/**
 * Aadd support for parsing from markdown.
 *
 * @param {Readonly<Options> | null | undefined} [options]
 *   Configuration (optional).
 * @returns {undefined}
 *   Nothing.
 */
function remarkParse(options) {
	/** @type {Processor} */
	// @ts-expect-error: TS in JSDoc generates wrong types if `this` is typed regularly.
	const self = this;

	self.parser = parser;

	/**
	 * @type {Parser}
	 */
	function parser(doc) {
		return fromMarkdown(doc, {
			...self.data("settings"),
			...options,
			// Note: these options are not in the readme.
			// The goal is for them to be set by plugins on `data` instead of being
			// passed by users.
			extensions: self.data("micromarkExtensions") || [],
			mdastExtensions: self.data("fromMarkdownExtensions") || [],
		});
	}
}

/**
 * @import {Element} from 'hast'
 * @import {Blockquote} from 'mdast'
 * @import {State} from '../state.js'
 */

/**
 * Turn an mdast `blockquote` node into hast.
 *
 * @param {State} state
 *   Info passed around.
 * @param {Blockquote} node
 *   mdast node.
 * @returns {Element}
 *   hast node.
 */
function blockquote(state, node) {
	/** @type {Element} */
	const result = {
		type: "element",
		tagName: "blockquote",
		properties: {},
		children: state.wrap(state.all(node), true),
	};
	state.patch(node, result);
	return state.applyData(node, result);
}

/**
 * @import {Element, Text} from 'hast'
 * @import {Break} from 'mdast'
 * @import {State} from '../state.js'
 */

/**
 * Turn an mdast `break` node into hast.
 *
 * @param {State} state
 *   Info passed around.
 * @param {Break} node
 *   mdast node.
 * @returns {Array<Element | Text>}
 *   hast element content.
 */
function hardBreak(state, node) {
	/** @type {Element} */
	const result = { type: "element", tagName: "br", properties: {}, children: [] };
	state.patch(node, result);
	return [state.applyData(node, result), { type: "text", value: "\n" }];
}

/**
 * @import {Element, Properties} from 'hast'
 * @import {Code} from 'mdast'
 * @import {State} from '../state.js'
 */

/**
 * Turn an mdast `code` node into hast.
 *
 * @param {State} state
 *   Info passed around.
 * @param {Code} node
 *   mdast node.
 * @returns {Element}
 *   hast node.
 */
function code(state, node) {
	const value = node.value ? node.value + "\n" : "";
	/** @type {Properties} */
	const properties = {};
	// Someone can write `js&#x20;python&#x9;ruby`.
	const language = node.lang ? node.lang.split(/\s+/) : [];

	// GH/CM still drop the non-first languages.
	if (language.length > 0) {
		properties.className = ["language-" + language[0]];
	}

	// Create `<code>`.
	/** @type {Element} */
	let result = {
		type: "element",
		tagName: "code",
		properties,
		children: [{ type: "text", value }],
	};

	if (node.meta) {
		result.data = { meta: node.meta };
	}

	state.patch(node, result);
	result = state.applyData(node, result);

	// Create `<pre>`.
	result = { type: "element", tagName: "pre", properties: {}, children: [result] };
	state.patch(node, result);
	return result;
}

/**
 * @import {Element} from 'hast'
 * @import {Delete} from 'mdast'
 * @import {State} from '../state.js'
 */

/**
 * Turn an mdast `delete` node into hast.
 *
 * @param {State} state
 *   Info passed around.
 * @param {Delete} node
 *   mdast node.
 * @returns {Element}
 *   hast node.
 */
function strikethrough(state, node) {
	/** @type {Element} */
	const result = {
		type: "element",
		tagName: "del",
		properties: {},
		children: state.all(node),
	};
	state.patch(node, result);
	return state.applyData(node, result);
}

/**
 * @import {Element} from 'hast'
 * @import {Emphasis} from 'mdast'
 * @import {State} from '../state.js'
 */

/**
 * Turn an mdast `emphasis` node into hast.
 *
 * @param {State} state
 *   Info passed around.
 * @param {Emphasis} node
 *   mdast node.
 * @returns {Element}
 *   hast node.
 */
function emphasis(state, node) {
	/** @type {Element} */
	const result = {
		type: "element",
		tagName: "em",
		properties: {},
		children: state.all(node),
	};
	state.patch(node, result);
	return state.applyData(node, result);
}

/**
 * @import {Element} from 'hast'
 * @import {FootnoteReference} from 'mdast'
 * @import {State} from '../state.js'
 */

/**
 * Turn an mdast `footnoteReference` node into hast.
 *
 * @param {State} state
 *   Info passed around.
 * @param {FootnoteReference} node
 *   mdast node.
 * @returns {Element}
 *   hast node.
 */
function footnoteReference(state, node) {
	const clobberPrefix = typeof state.options.clobberPrefix === "string" ? state.options.clobberPrefix : "user-content-";
	const id = String(node.identifier).toUpperCase();
	const safeId = normalizeUri(id.toLowerCase());
	const index = state.footnoteOrder.indexOf(id);
	/** @type {number} */
	let counter;

	let reuseCounter = state.footnoteCounts.get(id);

	if (reuseCounter === undefined) {
		reuseCounter = 0;
		state.footnoteOrder.push(id);
		counter = state.footnoteOrder.length;
	} else {
		counter = index + 1;
	}

	reuseCounter += 1;
	state.footnoteCounts.set(id, reuseCounter);

	/** @type {Element} */
	const link = {
		type: "element",
		tagName: "a",
		properties: {
			href: "#" + clobberPrefix + "fn-" + safeId,
			id: clobberPrefix + "fnref-" + safeId + (reuseCounter > 1 ? "-" + reuseCounter : ""),
			dataFootnoteRef: true,
			ariaDescribedBy: ["footnote-label"],
		},
		children: [{ type: "text", value: String(counter) }],
	};
	state.patch(node, link);

	/** @type {Element} */
	const sup = {
		type: "element",
		tagName: "sup",
		properties: {},
		children: [link],
	};
	state.patch(node, sup);
	return state.applyData(node, sup);
}

/**
 * @import {Element} from 'hast'
 * @import {Heading} from 'mdast'
 * @import {State} from '../state.js'
 */

/**
 * Turn an mdast `heading` node into hast.
 *
 * @param {State} state
 *   Info passed around.
 * @param {Heading} node
 *   mdast node.
 * @returns {Element}
 *   hast node.
 */
function heading(state, node) {
	/** @type {Element} */
	const result = {
		type: "element",
		tagName: "h" + node.depth,
		properties: {},
		children: state.all(node),
	};
	state.patch(node, result);
	return state.applyData(node, result);
}

/**
 * @import {Element} from 'hast'
 * @import {Html} from 'mdast'
 * @import {State} from '../state.js'
 * @import {Raw} from '../../index.js'
 */

/**
 * Turn an mdast `html` node into hast (`raw` node in dangerous mode, otherwise
 * nothing).
 *
 * @param {State} state
 *   Info passed around.
 * @param {Html} node
 *   mdast node.
 * @returns {Element | Raw | undefined}
 *   hast node.
 */
function html$2(state, node) {
	if (state.options.allowDangerousHtml) {
		/** @type {Raw} */
		const result = { type: "raw", value: node.value };
		state.patch(node, result);
		return state.applyData(node, result);
	}

	return undefined;
}

/**
 * @import {ElementContent} from 'hast'
 * @import {Reference, Nodes} from 'mdast'
 * @import {State} from './state.js'
 */

/**
 * Return the content of a reference without definition as plain text.
 *
 * @param {State} state
 *   Info passed around.
 * @param {Extract<Nodes, Reference>} node
 *   Reference node (image, link).
 * @returns {Array<ElementContent>}
 *   hast content.
 */
function revert(state, node) {
	const subtype = node.referenceType;
	let suffix = "]";

	if (subtype === "collapsed") {
		suffix += "[]";
	} else if (subtype === "full") {
		suffix += "[" + (node.label || node.identifier) + "]";
	}

	if (node.type === "imageReference") {
		return [{ type: "text", value: "![" + node.alt + suffix }];
	}

	const contents = state.all(node);
	const head = contents[0];

	if (head && head.type === "text") {
		head.value = "[" + head.value;
	} else {
		contents.unshift({ type: "text", value: "[" });
	}

	const tail = contents[contents.length - 1];

	if (tail && tail.type === "text") {
		tail.value += suffix;
	} else {
		contents.push({ type: "text", value: suffix });
	}

	return contents;
}

/**
 * @import {ElementContent, Element, Properties} from 'hast'
 * @import {ImageReference} from 'mdast'
 * @import {State} from '../state.js'
 */

/**
 * Turn an mdast `imageReference` node into hast.
 *
 * @param {State} state
 *   Info passed around.
 * @param {ImageReference} node
 *   mdast node.
 * @returns {Array<ElementContent> | ElementContent}
 *   hast node.
 */
function imageReference(state, node) {
	const id = String(node.identifier).toUpperCase();
	const definition = state.definitionById.get(id);

	if (!definition) {
		return revert(state, node);
	}

	/** @type {Properties} */
	const properties = { src: normalizeUri(definition.url || ""), alt: node.alt };

	if (definition.title !== null && definition.title !== undefined) {
		properties.title = definition.title;
	}

	/** @type {Element} */
	const result = { type: "element", tagName: "img", properties, children: [] };
	state.patch(node, result);
	return state.applyData(node, result);
}

/**
 * @import {Element, Properties} from 'hast'
 * @import {Image} from 'mdast'
 * @import {State} from '../state.js'
 */

/**
 * Turn an mdast `image` node into hast.
 *
 * @param {State} state
 *   Info passed around.
 * @param {Image} node
 *   mdast node.
 * @returns {Element}
 *   hast node.
 */
function image(state, node) {
	/** @type {Properties} */
	const properties = { src: normalizeUri(node.url) };

	if (node.alt !== null && node.alt !== undefined) {
		properties.alt = node.alt;
	}

	if (node.title !== null && node.title !== undefined) {
		properties.title = node.title;
	}

	/** @type {Element} */
	const result = { type: "element", tagName: "img", properties, children: [] };
	state.patch(node, result);
	return state.applyData(node, result);
}

/**
 * @import {Element, Text} from 'hast'
 * @import {InlineCode} from 'mdast'
 * @import {State} from '../state.js'
 */

/**
 * Turn an mdast `inlineCode` node into hast.
 *
 * @param {State} state
 *   Info passed around.
 * @param {InlineCode} node
 *   mdast node.
 * @returns {Element}
 *   hast node.
 */
function inlineCode(state, node) {
	/** @type {Text} */
	const text = { type: "text", value: node.value.replace(/\r?\n|\r/g, " ") };
	state.patch(node, text);

	/** @type {Element} */
	const result = {
		type: "element",
		tagName: "code",
		properties: {},
		children: [text],
	};
	state.patch(node, result);
	return state.applyData(node, result);
}

/**
 * @import {ElementContent, Element, Properties} from 'hast'
 * @import {LinkReference} from 'mdast'
 * @import {State} from '../state.js'
 */

/**
 * Turn an mdast `linkReference` node into hast.
 *
 * @param {State} state
 *   Info passed around.
 * @param {LinkReference} node
 *   mdast node.
 * @returns {Array<ElementContent> | ElementContent}
 *   hast node.
 */
function linkReference(state, node) {
	const id = String(node.identifier).toUpperCase();
	const definition = state.definitionById.get(id);

	if (!definition) {
		return revert(state, node);
	}

	/** @type {Properties} */
	const properties = { href: normalizeUri(definition.url || "") };

	if (definition.title !== null && definition.title !== undefined) {
		properties.title = definition.title;
	}

	/** @type {Element} */
	const result = {
		type: "element",
		tagName: "a",
		properties,
		children: state.all(node),
	};
	state.patch(node, result);
	return state.applyData(node, result);
}

/**
 * @import {Element, Properties} from 'hast'
 * @import {Link} from 'mdast'
 * @import {State} from '../state.js'
 */

/**
 * Turn an mdast `link` node into hast.
 *
 * @param {State} state
 *   Info passed around.
 * @param {Link} node
 *   mdast node.
 * @returns {Element}
 *   hast node.
 */
function link(state, node) {
	/** @type {Properties} */
	const properties = { href: normalizeUri(node.url) };

	if (node.title !== null && node.title !== undefined) {
		properties.title = node.title;
	}

	/** @type {Element} */
	const result = {
		type: "element",
		tagName: "a",
		properties,
		children: state.all(node),
	};
	state.patch(node, result);
	return state.applyData(node, result);
}

/**
 * @import {ElementContent, Element, Properties} from 'hast'
 * @import {ListItem, Parents} from 'mdast'
 * @import {State} from '../state.js'
 */

/**
 * Turn an mdast `listItem` node into hast.
 *
 * @param {State} state
 *   Info passed around.
 * @param {ListItem} node
 *   mdast node.
 * @param {Parents | undefined} parent
 *   Parent of `node`.
 * @returns {Element}
 *   hast node.
 */
function listItem(state, node, parent) {
	const results = state.all(node);
	const loose = parent ? listLoose(parent) : listItemLoose(node);
	/** @type {Properties} */
	const properties = {};
	/** @type {Array<ElementContent>} */
	const children = [];

	if (typeof node.checked === "boolean") {
		const head = results[0];
		/** @type {Element} */
		let paragraph;

		if (head && head.type === "element" && head.tagName === "p") {
			paragraph = head;
		} else {
			paragraph = { type: "element", tagName: "p", properties: {}, children: [] };
			results.unshift(paragraph);
		}

		if (paragraph.children.length > 0) {
			paragraph.children.unshift({ type: "text", value: " " });
		}

		paragraph.children.unshift({
			type: "element",
			tagName: "input",
			properties: { type: "checkbox", checked: node.checked, disabled: true },
			children: [],
		});

		// According to github-markdown-css, this class hides bullet.
		// See: <https://github.com/sindresorhus/github-markdown-css>.
		properties.className = ["task-list-item"];
	}

	let index = -1;

	while (++index < results.length) {
		const child = results[index];

		// Add eols before nodes, except if this is a loose, first paragraph.
		if (loose || index !== 0 || child.type !== "element" || child.tagName !== "p") {
			children.push({ type: "text", value: "\n" });
		}

		if (child.type === "element" && child.tagName === "p" && !loose) {
			children.push(...child.children);
		} else {
			children.push(child);
		}
	}

	const tail = results[results.length - 1];

	// Add a final eol.
	if (tail && (loose || tail.type !== "element" || tail.tagName !== "p")) {
		children.push({ type: "text", value: "\n" });
	}

	/** @type {Element} */
	const result = { type: "element", tagName: "li", properties, children };
	state.patch(node, result);
	return state.applyData(node, result);
}

/**
 * @param {Parents} node
 * @return {Boolean}
 */
function listLoose(node) {
	let loose = false;
	if (node.type === "list") {
		loose = node.spread || false;
		const children = node.children;
		let index = -1;

		while (!loose && ++index < children.length) {
			loose = listItemLoose(children[index]);
		}
	}

	return loose;
}

/**
 * @param {ListItem} node
 * @return {Boolean}
 */
function listItemLoose(node) {
	const spread = node.spread;

	return spread === null || spread === undefined ? node.children.length > 1 : spread;
}

/**
 * @import {Element, Properties} from 'hast'
 * @import {List} from 'mdast'
 * @import {State} from '../state.js'
 */

/**
 * Turn an mdast `list` node into hast.
 *
 * @param {State} state
 *   Info passed around.
 * @param {List} node
 *   mdast node.
 * @returns {Element}
 *   hast node.
 */
function list(state, node) {
	/** @type {Properties} */
	const properties = {};
	const results = state.all(node);
	let index = -1;

	if (typeof node.start === "number" && node.start !== 1) {
		properties.start = node.start;
	}

	// Like GitHub, add a class for custom styling.
	while (++index < results.length) {
		const child = results[index];

		if (child.type === "element" && child.tagName === "li" && child.properties && Array.isArray(child.properties.className) && child.properties.className.includes("task-list-item")) {
			properties.className = ["contains-task-list"];
			break;
		}
	}

	/** @type {Element} */
	const result = {
		type: "element",
		tagName: node.ordered ? "ol" : "ul",
		properties,
		children: state.wrap(results, true),
	};
	state.patch(node, result);
	return state.applyData(node, result);
}

/**
 * @import {Element} from 'hast'
 * @import {Paragraph} from 'mdast'
 * @import {State} from '../state.js'
 */

/**
 * Turn an mdast `paragraph` node into hast.
 *
 * @param {State} state
 *   Info passed around.
 * @param {Paragraph} node
 *   mdast node.
 * @returns {Element}
 *   hast node.
 */
function paragraph(state, node) {
	/** @type {Element} */
	const result = {
		type: "element",
		tagName: "p",
		properties: {},
		children: state.all(node),
	};
	state.patch(node, result);
	return state.applyData(node, result);
}

/**
 * @import {Parents as HastParents, Root as HastRoot} from 'hast'
 * @import {Root as MdastRoot} from 'mdast'
 * @import {State} from '../state.js'
 */

/**
 * Turn an mdast `root` node into hast.
 *
 * @param {State} state
 *   Info passed around.
 * @param {MdastRoot} node
 *   mdast node.
 * @returns {HastParents}
 *   hast node.
 */
function root$1(state, node) {
	/** @type {HastRoot} */
	const result = { type: "root", children: state.wrap(state.all(node)) };
	state.patch(node, result);
	return state.applyData(node, result);
}

/**
 * @import {Element} from 'hast'
 * @import {Strong} from 'mdast'
 * @import {State} from '../state.js'
 */

/**
 * Turn an mdast `strong` node into hast.
 *
 * @param {State} state
 *   Info passed around.
 * @param {Strong} node
 *   mdast node.
 * @returns {Element}
 *   hast node.
 */
function strong(state, node) {
	/** @type {Element} */
	const result = {
		type: "element",
		tagName: "strong",
		properties: {},
		children: state.all(node),
	};
	state.patch(node, result);
	return state.applyData(node, result);
}

/**
 * @import {Table} from 'mdast'
 * @import {Element} from 'hast'
 * @import {State} from '../state.js'
 */

/**
 * Turn an mdast `table` node into hast.
 *
 * @param {State} state
 *   Info passed around.
 * @param {Table} node
 *   mdast node.
 * @returns {Element}
 *   hast node.
 */
function table(state, node) {
	const rows = state.all(node);
	const firstRow = rows.shift();
	/** @type {Array<Element>} */
	const tableContent = [];

	if (firstRow) {
		/** @type {Element} */
		const head = {
			type: "element",
			tagName: "thead",
			properties: {},
			children: state.wrap([firstRow], true),
		};
		state.patch(node.children[0], head);
		tableContent.push(head);
	}

	if (rows.length > 0) {
		/** @type {Element} */
		const body = {
			type: "element",
			tagName: "tbody",
			properties: {},
			children: state.wrap(rows, true),
		};

		const start = pointStart(node.children[1]);
		const end = pointEnd(node.children[node.children.length - 1]);
		if (start && end) body.position = { start, end };
		tableContent.push(body);
	}

	/** @type {Element} */
	const result = {
		type: "element",
		tagName: "table",
		properties: {},
		children: state.wrap(tableContent, true),
	};
	state.patch(node, result);
	return state.applyData(node, result);
}

/**
 * @import {Element, ElementContent, Properties} from 'hast'
 * @import {Parents, TableRow} from 'mdast'
 * @import {State} from '../state.js'
 */

/**
 * Turn an mdast `tableRow` node into hast.
 *
 * @param {State} state
 *   Info passed around.
 * @param {TableRow} node
 *   mdast node.
 * @param {Parents | undefined} parent
 *   Parent of `node`.
 * @returns {Element}
 *   hast node.
 */
function tableRow(state, node, parent) {
	const siblings = parent ? parent.children : undefined;
	// Generate a body row when without parent.
	const rowIndex = siblings ? siblings.indexOf(node) : 1;
	const tagName = rowIndex === 0 ? "th" : "td";
	// To do: option to use `style`?
	const align = parent && parent.type === "table" ? parent.align : undefined;
	const length = align ? align.length : node.children.length;
	let cellIndex = -1;
	/** @type {Array<ElementContent>} */
	const cells = [];

	while (++cellIndex < length) {
		// Note: can also be undefined.
		const cell = node.children[cellIndex];
		/** @type {Properties} */
		const properties = {};
		const alignValue = align ? align[cellIndex] : undefined;

		if (alignValue) {
			properties.align = alignValue;
		}

		/** @type {Element} */
		let result = { type: "element", tagName, properties, children: [] };

		if (cell) {
			result.children = state.all(cell);
			state.patch(cell, result);
			result = state.applyData(cell, result);
		}

		cells.push(result);
	}

	/** @type {Element} */
	const result = {
		type: "element",
		tagName: "tr",
		properties: {},
		children: state.wrap(cells, true),
	};
	state.patch(node, result);
	return state.applyData(node, result);
}

/**
 * @import {Element} from 'hast'
 * @import {TableCell} from 'mdast'
 * @import {State} from '../state.js'
 */

/**
 * Turn an mdast `tableCell` node into hast.
 *
 * @param {State} state
 *   Info passed around.
 * @param {TableCell} node
 *   mdast node.
 * @returns {Element}
 *   hast node.
 */
function tableCell(state, node) {
	// Note: this function is normally not called: see `table-row` for how rows
	// and their cells are compiled.
	/** @type {Element} */
	const result = {
		type: "element",
		tagName: "td", // Assume body cell.
		properties: {},
		children: state.all(node),
	};
	state.patch(node, result);
	return state.applyData(node, result);
}

const tab = 9; /* `\t` */
const space = 32; /* ` ` */

/**
 * Remove initial and final spaces and tabs at the line breaks in `value`.
 * Does not trim initial and final spaces and tabs of the value itself.
 *
 * @param {string} value
 *   Value to trim.
 * @returns {string}
 *   Trimmed value.
 */
function trimLines(value) {
	const source = String(value);
	const search = /\r?\n|\r/g;
	let match = search.exec(source);
	let last = 0;
	/** @type {Array<string>} */
	const lines = [];

	while (match) {
		lines.push(trimLine(source.slice(last, match.index), last > 0, true), match[0]);

		last = match.index + match[0].length;
		match = search.exec(source);
	}

	lines.push(trimLine(source.slice(last), last > 0, false));

	return lines.join("");
}

/**
 * @param {string} value
 *   Line to trim.
 * @param {boolean} start
 *   Whether to trim the start of the line.
 * @param {boolean} end
 *   Whether to trim the end of the line.
 * @returns {string}
 *   Trimmed line.
 */
function trimLine(value, start, end) {
	let startIndex = 0;
	let endIndex = value.length;

	if (start) {
		let code = value.codePointAt(startIndex);

		while (code === tab || code === space) {
			startIndex++;
			code = value.codePointAt(startIndex);
		}
	}

	if (end) {
		let code = value.codePointAt(endIndex - 1);

		while (code === tab || code === space) {
			endIndex--;
			code = value.codePointAt(endIndex - 1);
		}
	}

	return endIndex > startIndex ? value.slice(startIndex, endIndex) : "";
}

/**
 * @import {Element as HastElement, Text as HastText} from 'hast'
 * @import {Text as MdastText} from 'mdast'
 * @import {State} from '../state.js'
 */

/**
 * Turn an mdast `text` node into hast.
 *
 * @param {State} state
 *   Info passed around.
 * @param {MdastText} node
 *   mdast node.
 * @returns {HastElement | HastText}
 *   hast node.
 */
function text$1(state, node) {
	/** @type {HastText} */
	const result = { type: "text", value: trimLines(String(node.value)) };
	state.patch(node, result);
	return state.applyData(node, result);
}

/**
 * @import {Element} from 'hast'
 * @import {ThematicBreak} from 'mdast'
 * @import {State} from '../state.js'
 */

/**
 * Turn an mdast `thematicBreak` node into hast.
 *
 * @param {State} state
 *   Info passed around.
 * @param {ThematicBreak} node
 *   mdast node.
 * @returns {Element}
 *   hast node.
 */
function thematicBreak(state, node) {
	/** @type {Element} */
	const result = {
		type: "element",
		tagName: "hr",
		properties: {},
		children: [],
	};
	state.patch(node, result);
	return state.applyData(node, result);
}

/**
 * @import {Handlers} from '../state.js'
 */

/**
 * Default handlers for nodes.
 *
 * @satisfies {Handlers}
 */
const handlers = {
	blockquote,
	break: hardBreak,
	code,
	delete: strikethrough,
	emphasis,
	footnoteReference,
	heading,
	html: html$2,
	imageReference,
	image,
	inlineCode,
	linkReference,
	link,
	listItem,
	list,
	paragraph,
	// @ts-expect-error: root is different, but hard to type.
	root: root$1,
	strong,
	table,
	tableCell,
	tableRow,
	text: text$1,
	thematicBreak,
	toml: ignore,
	yaml: ignore,
	definition: ignore,
	footnoteDefinition: ignore,
};

// Return nothing for nodes that are ignored.
function ignore() {
	return undefined;
}

/**
 * @import {ElementContent, Element} from 'hast'
 * @import {State} from './state.js'
 */

/**
 * Generate the default content that GitHub uses on backreferences.
 *
 * @param {number} _
 *   Index of the definition in the order that they are first referenced,
 *   0-indexed.
 * @param {number} rereferenceIndex
 *   Index of calls to the same definition, 0-indexed.
 * @returns {Array<ElementContent>}
 *   Content.
 */
function defaultFootnoteBackContent(_, rereferenceIndex) {
	/** @type {Array<ElementContent>} */
	const result = [{ type: "text", value: "↩" }];

	if (rereferenceIndex > 1) {
		result.push({
			type: "element",
			tagName: "sup",
			properties: {},
			children: [{ type: "text", value: String(rereferenceIndex) }],
		});
	}

	return result;
}

/**
 * Generate the default label that GitHub uses on backreferences.
 *
 * @param {number} referenceIndex
 *   Index of the definition in the order that they are first referenced,
 *   0-indexed.
 * @param {number} rereferenceIndex
 *   Index of calls to the same definition, 0-indexed.
 * @returns {string}
 *   Label.
 */
function defaultFootnoteBackLabel(referenceIndex, rereferenceIndex) {
	return "Back to reference " + (referenceIndex + 1) + (rereferenceIndex > 1 ? "-" + rereferenceIndex : "");
}

/**
 * Generate a hast footer for called footnote definitions.
 *
 * @param {State} state
 *   Info passed around.
 * @returns {Element | undefined}
 *   `section` element or `undefined`.
 */
// eslint-disable-next-line complexity
function footer(state) {
	const clobberPrefix = typeof state.options.clobberPrefix === "string" ? state.options.clobberPrefix : "user-content-";
	const footnoteBackContent = state.options.footnoteBackContent || defaultFootnoteBackContent;
	const footnoteBackLabel = state.options.footnoteBackLabel || defaultFootnoteBackLabel;
	const footnoteLabel = state.options.footnoteLabel || "Footnotes";
	const footnoteLabelTagName = state.options.footnoteLabelTagName || "h2";
	const footnoteLabelProperties = state.options.footnoteLabelProperties || {
		className: ["sr-only"],
	};
	/** @type {Array<ElementContent>} */
	const listItems = [];
	let referenceIndex = -1;

	while (++referenceIndex < state.footnoteOrder.length) {
		const definition = state.footnoteById.get(state.footnoteOrder[referenceIndex]);

		if (!definition) {
			continue;
		}

		const content = state.all(definition);
		const id = String(definition.identifier).toUpperCase();
		const safeId = normalizeUri(id.toLowerCase());
		let rereferenceIndex = 0;
		/** @type {Array<ElementContent>} */
		const backReferences = [];
		const counts = state.footnoteCounts.get(id);

		// eslint-disable-next-line no-unmodified-loop-condition
		while (counts !== undefined && ++rereferenceIndex <= counts) {
			if (backReferences.length > 0) {
				backReferences.push({ type: "text", value: " " });
			}

			let children = typeof footnoteBackContent === "string" ? footnoteBackContent : footnoteBackContent(referenceIndex, rereferenceIndex);

			if (typeof children === "string") {
				children = { type: "text", value: children };
			}

			backReferences.push({
				type: "element",
				tagName: "a",
				properties: {
					href: "#" + clobberPrefix + "fnref-" + safeId + (rereferenceIndex > 1 ? "-" + rereferenceIndex : ""),
					dataFootnoteBackref: "",
					ariaLabel: typeof footnoteBackLabel === "string" ? footnoteBackLabel : footnoteBackLabel(referenceIndex, rereferenceIndex),
					className: ["data-footnote-backref"],
				},
				children: Array.isArray(children) ? children : [children],
			});
		}

		const tail = content[content.length - 1];

		if (tail && tail.type === "element" && tail.tagName === "p") {
			const tailTail = tail.children[tail.children.length - 1];
			if (tailTail && tailTail.type === "text") {
				tailTail.value += " ";
			} else {
				tail.children.push({ type: "text", value: " " });
			}

			tail.children.push(...backReferences);
		} else {
			content.push(...backReferences);
		}

		/** @type {Element} */
		const listItem = {
			type: "element",
			tagName: "li",
			properties: { id: clobberPrefix + "fn-" + safeId },
			children: state.wrap(content, true),
		};

		state.patch(definition, listItem);

		listItems.push(listItem);
	}

	if (listItems.length === 0) {
		return;
	}

	return {
		type: "element",
		tagName: "section",
		properties: { dataFootnotes: true, className: ["footnotes"] },
		children: [
			{
				type: "element",
				tagName: footnoteLabelTagName,
				properties: {
					...structuredClone$1(footnoteLabelProperties),
					id: "footnote-label",
				},
				children: [{ type: "text", value: footnoteLabel }],
			},
			{ type: "text", value: "\n" },
			{
				type: "element",
				tagName: "ol",
				properties: {},
				children: state.wrap(listItems, true),
			},
			{ type: "text", value: "\n" },
		],
	};
}

/**
 * @import {
 *   ElementContent as HastElementContent,
 *   Element as HastElement,
 *   Nodes as HastNodes,
 *   Properties as HastProperties,
 *   RootContent as HastRootContent,
 *   Text as HastText
 * } from 'hast'
 * @import {
 *   Definition as MdastDefinition,
 *   FootnoteDefinition as MdastFootnoteDefinition,
 *   Nodes as MdastNodes,
 *   Parents as MdastParents
 * } from 'mdast'
 * @import {VFile} from 'vfile'
 * @import {
 *   FootnoteBackContentTemplate,
 *   FootnoteBackLabelTemplate
 * } from './footer.js'
 */

const own$2 = {}.hasOwnProperty;

/** @type {Options} */
const emptyOptions$1 = {};

/**
 * Create `state` from an mdast tree.
 *
 * @param {MdastNodes} tree
 *   mdast node to transform.
 * @param {Options | null | undefined} [options]
 *   Configuration (optional).
 * @returns {State}
 *   `state` function.
 */
function createState(tree, options) {
	const settings = options || emptyOptions$1;
	/** @type {Map<string, MdastDefinition>} */
	const definitionById = new Map();
	/** @type {Map<string, MdastFootnoteDefinition>} */
	const footnoteById = new Map();
	/** @type {Map<string, number>} */
	const footnoteCounts = new Map();
	/** @type {Handlers} */
	// @ts-expect-error: the root handler returns a root.
	// Hard to type.
	const handlers$1 = { ...handlers, ...settings.handlers };

	/** @type {State} */
	const state = {
		all,
		applyData,
		definitionById,
		footnoteById,
		footnoteCounts,
		footnoteOrder: [],
		handlers: handlers$1,
		one,
		options: settings,
		patch,
		wrap,
	};

	visit(tree, function (node) {
		if (node.type === "definition" || node.type === "footnoteDefinition") {
			const map = node.type === "definition" ? definitionById : footnoteById;
			const id = String(node.identifier).toUpperCase();

			// Mimick CM behavior of link definitions.
			// See: <https://github.com/syntax-tree/mdast-util-definitions/blob/9032189/lib/index.js#L20-L21>.
			if (!map.has(id)) {
				// @ts-expect-error: node type matches map.
				map.set(id, node);
			}
		}
	});

	return state;

	/**
	 * Transform an mdast node into a hast node.
	 *
	 * @param {MdastNodes} node
	 *   mdast node.
	 * @param {MdastParents | undefined} [parent]
	 *   Parent of `node`.
	 * @returns {Array<HastElementContent> | HastElementContent | undefined}
	 *   Resulting hast node.
	 */
	function one(node, parent) {
		const type = node.type;
		const handle = state.handlers[type];

		if (own$2.call(state.handlers, type) && handle) {
			return handle(state, node, parent);
		}

		if (state.options.passThrough && state.options.passThrough.includes(type)) {
			if ("children" in node) {
				const { children, ...shallow } = node;
				const result = structuredClone$1(shallow);
				// @ts-expect-error: TS doesn’t understand…
				result.children = state.all(node);
				// @ts-expect-error: TS doesn’t understand…
				return result;
			}

			// @ts-expect-error: it’s custom.
			return structuredClone$1(node);
		}

		const unknown = state.options.unknownHandler || defaultUnknownHandler;

		return unknown(state, node, parent);
	}

	/**
	 * Transform the children of an mdast node into hast nodes.
	 *
	 * @param {MdastNodes} parent
	 *   mdast node to compile
	 * @returns {Array<HastElementContent>}
	 *   Resulting hast nodes.
	 */
	function all(parent) {
		/** @type {Array<HastElementContent>} */
		const values = [];

		if ("children" in parent) {
			const nodes = parent.children;
			let index = -1;
			while (++index < nodes.length) {
				const result = state.one(nodes[index], parent);

				// To do: see if we van clean this? Can we merge texts?
				if (result) {
					if (index && nodes[index - 1].type === "break") {
						if (!Array.isArray(result) && result.type === "text") {
							result.value = trimMarkdownSpaceStart(result.value);
						}

						if (!Array.isArray(result) && result.type === "element") {
							const head = result.children[0];

							if (head && head.type === "text") {
								head.value = trimMarkdownSpaceStart(head.value);
							}
						}
					}

					if (Array.isArray(result)) {
						values.push(...result);
					} else {
						values.push(result);
					}
				}
			}
		}

		return values;
	}
}

/**
 * Copy a node’s positional info.
 *
 * @param {MdastNodes} from
 *   mdast node to copy from.
 * @param {HastNodes} to
 *   hast node to copy into.
 * @returns {undefined}
 *   Nothing.
 */
function patch(from, to) {
	if (from.position) to.position = position(from);
}

/**
 * Honor the `data` of `from` and maybe generate an element instead of `to`.
 *
 * @template {HastNodes} Type
 *   Node type.
 * @param {MdastNodes} from
 *   mdast node to use data from.
 * @param {Type} to
 *   hast node to change.
 * @returns {HastElement | Type}
 *   Nothing.
 */
function applyData(from, to) {
	/** @type {HastElement | Type} */
	let result = to;

	// Handle `data.hName`, `data.hProperties, `data.hChildren`.
	if (from && from.data) {
		const hName = from.data.hName;
		const hChildren = from.data.hChildren;
		const hProperties = from.data.hProperties;

		if (typeof hName === "string") {
			// Transforming the node resulted in an element with a different name
			// than wanted:
			if (result.type === "element") {
				result.tagName = hName;
			}
			// Transforming the node resulted in a non-element, which happens for
			// raw, text, and root nodes (unless custom handlers are passed).
			// The intent of `hName` is to create an element, but likely also to keep
			// the content around (otherwise: pass `hChildren`).
			else {
				/** @type {Array<HastElementContent>} */
				// @ts-expect-error: assume no doctypes in `root`.
				const children = "children" in result ? result.children : [result];
				result = { type: "element", tagName: hName, properties: {}, children };
			}
		}

		if (result.type === "element" && hProperties) {
			Object.assign(result.properties, structuredClone$1(hProperties));
		}

		if ("children" in result && result.children && hChildren !== null && hChildren !== undefined) {
			result.children = hChildren;
		}
	}

	return result;
}

/**
 * Transform an unknown node.
 *
 * @param {State} state
 *   Info passed around.
 * @param {MdastNodes} node
 *   Unknown mdast node.
 * @returns {HastElement | HastText}
 *   Resulting hast node.
 */
function defaultUnknownHandler(state, node) {
	const data = node.data || {};
	/** @type {HastElement | HastText} */
	const result =
		"value" in node && !(own$2.call(data, "hProperties") || own$2.call(data, "hChildren")) ?
			{ type: "text", value: node.value }
		:	{
				type: "element",
				tagName: "div",
				properties: {},
				children: state.all(node),
			};

	state.patch(node, result);
	return state.applyData(node, result);
}

/**
 * Wrap `nodes` with line endings between each node.
 *
 * @template {HastRootContent} Type
 *   Node type.
 * @param {Array<Type>} nodes
 *   List of nodes to wrap.
 * @param {boolean | undefined} [loose=false]
 *   Whether to add line endings at start and end (default: `false`).
 * @returns {Array<HastText | Type>}
 *   Wrapped nodes.
 */
function wrap(nodes, loose) {
	/** @type {Array<HastText | Type>} */
	const result = [];
	let index = -1;

	if (loose) {
		result.push({ type: "text", value: "\n" });
	}

	while (++index < nodes.length) {
		if (index) result.push({ type: "text", value: "\n" });
		result.push(nodes[index]);
	}

	if (loose && nodes.length > 0) {
		result.push({ type: "text", value: "\n" });
	}

	return result;
}

/**
 * Trim spaces and tabs at the start of `value`.
 *
 * @param {string} value
 *   Value to trim.
 * @returns {string}
 *   Result.
 */
function trimMarkdownSpaceStart(value) {
	let index = 0;
	let code = value.charCodeAt(index);

	while (code === 9 || code === 32) {
		index++;
		code = value.charCodeAt(index);
	}

	return value.slice(index);
}

/**
 * @import {Nodes as HastNodes} from 'hast'
 * @import {Nodes as MdastNodes} from 'mdast'
 * @import {Options} from './state.js'
 */

/**
 * Transform mdast to hast.
 *
 * ##### Notes
 *
 * ###### HTML
 *
 * Raw HTML is available in mdast as `html` nodes and can be embedded in hast
 * as semistandard `raw` nodes.
 * Most utilities ignore `raw` nodes but two notable ones don’t:
 *
 * *   `hast-util-to-html` also has an option `allowDangerousHtml` which will
 *     output the raw HTML.
 *     This is typically discouraged as noted by the option name but is useful
 *     if you completely trust authors
 * *   `hast-util-raw` can handle the raw embedded HTML strings by parsing them
 *     into standard hast nodes (`element`, `text`, etc).
 *     This is a heavy task as it needs a full HTML parser, but it is the only
 *     way to support untrusted content
 *
 * ###### Footnotes
 *
 * Many options supported here relate to footnotes.
 * Footnotes are not specified by CommonMark, which we follow by default.
 * They are supported by GitHub, so footnotes can be enabled in markdown with
 * `mdast-util-gfm`.
 *
 * The options `footnoteBackLabel` and `footnoteLabel` define natural language
 * that explains footnotes, which is hidden for sighted users but shown to
 * assistive technology.
 * When your page is not in English, you must define translated values.
 *
 * Back references use ARIA attributes, but the section label itself uses a
 * heading that is hidden with an `sr-only` class.
 * To show it to sighted users, define different attributes in
 * `footnoteLabelProperties`.
 *
 * ###### Clobbering
 *
 * Footnotes introduces a problem, as it links footnote calls to footnote
 * definitions on the page through `id` attributes generated from user content,
 * which results in DOM clobbering.
 *
 * DOM clobbering is this:
 *
 * ```html
 * <p id=x></p>
 * <script>alert(x) // `x` now refers to the DOM `p#x` element</script>
 * ```
 *
 * Elements by their ID are made available by browsers on the `window` object,
 * which is a security risk.
 * Using a prefix solves this problem.
 *
 * More information on how to handle clobbering and the prefix is explained in
 * Example: headings (DOM clobbering) in `rehype-sanitize`.
 *
 * ###### Unknown nodes
 *
 * Unknown nodes are nodes with a type that isn’t in `handlers` or `passThrough`.
 * The default behavior for unknown nodes is:
 *
 * *   when the node has a `value` (and doesn’t have `data.hName`,
 *     `data.hProperties`, or `data.hChildren`, see later), create a hast `text`
 *     node
 * *   otherwise, create a `<div>` element (which could be changed with
 *     `data.hName`), with its children mapped from mdast to hast as well
 *
 * This behavior can be changed by passing an `unknownHandler`.
 *
 * @param {MdastNodes} tree
 *   mdast tree.
 * @param {Options | null | undefined} [options]
 *   Configuration (optional).
 * @returns {HastNodes}
 *   hast tree.
 */
function toHast(tree, options) {
	const state = createState(tree, options);
	const node = state.one(tree, undefined);
	const foot = footer(state);
	/** @type {HastNodes} */
	const result = Array.isArray(node) ? { type: "root", children: node } : node || { type: "root", children: [] };

	if (foot) {
		result.children.push({ type: "text", value: "\n" }, foot);
	}

	return result;
}

/**
 * @import {Root as HastRoot} from 'hast'
 * @import {Root as MdastRoot} from 'mdast'
 * @import {Options as ToHastOptions} from 'mdast-util-to-hast'
 * @import {Processor} from 'unified'
 * @import {VFile} from 'vfile'
 */

/**
 * Turn markdown into HTML.
 *
 * ##### Notes
 *
 * ###### Signature
 *
 * * if a processor is given,
 *   runs the (rehype) plugins used on it with a hast tree,
 *   then discards the result (*bridge mode*)
 * * otherwise,
 *   returns a hast tree,
 *   the plugins used after `remarkRehype` are rehype plugins (*mutate mode*)
 *
 * > 👉 **Note**:
 * > It’s highly unlikely that you want to pass a `processor`.
 *
 * ###### HTML
 *
 * Raw HTML is available in mdast as `html` nodes and can be embedded in hast
 * as semistandard `raw` nodes.
 * Most plugins ignore `raw` nodes but two notable ones don’t:
 *
 * * `rehype-stringify` also has an option `allowDangerousHtml` which will
 *   output the raw HTML.
 *   This is typically discouraged as noted by the option name but is useful if
 *   you completely trust authors
 * * `rehype-raw` can handle the raw embedded HTML strings by parsing them
 *   into standard hast nodes (`element`, `text`, etc);
 *   this is a heavy task as it needs a full HTML parser,
 *   but it is the only way to support untrusted content
 *
 * ###### Footnotes
 *
 * Many options supported here relate to footnotes.
 * Footnotes are not specified by CommonMark,
 * which we follow by default.
 * They are supported by GitHub,
 * so footnotes can be enabled in markdown with `remark-gfm`.
 *
 * The options `footnoteBackLabel` and `footnoteLabel` define natural language
 * that explains footnotes,
 * which is hidden for sighted users but shown to assistive technology.
 * When your page is not in English,
 * you must define translated values.
 *
 * Back references use ARIA attributes,
 * but the section label itself uses a heading that is hidden with an
 * `sr-only` class.
 * To show it to sighted users,
 * define different attributes in `footnoteLabelProperties`.
 *
 * ###### Clobbering
 *
 * Footnotes introduces a problem,
 * as it links footnote calls to footnote definitions on the page through `id`
 * attributes generated from user content,
 * which results in DOM clobbering.
 *
 * DOM clobbering is this:
 *
 * ```html
 * <p id=x></p>
 * <script>alert(x) // `x` now refers to the DOM `p#x` element</script>
 * ```
 *
 * Elements by their ID are made available by browsers on the `window` object,
 * which is a security risk.
 * Using a prefix solves this problem.
 *
 * More information on how to handle clobbering and the prefix is explained in
 * *Example: headings (DOM clobbering)* in `rehype-sanitize`.
 *
 * ###### Unknown nodes
 *
 * Unknown nodes are nodes with a type that isn’t in `handlers` or `passThrough`.
 * The default behavior for unknown nodes is:
 *
 * * when the node has a `value`
 *   (and doesn’t have `data.hName`, `data.hProperties`, or `data.hChildren`,
 *   see later),
 *   create a hast `text` node
 * * otherwise,
 *   create a `<div>` element (which could be changed with `data.hName`),
 *   with its children mapped from mdast to hast as well
 *
 * This behavior can be changed by passing an `unknownHandler`.
 *
 * @overload
 * @param {Processor} processor
 * @param {Readonly<Options> | null | undefined} [options]
 * @returns {TransformBridge}
 *
 * @overload
 * @param {Readonly<Options> | null | undefined} [options]
 * @returns {TransformMutate}
 *
 * @overload
 * @param {Readonly<Options> | Processor | null | undefined} [destination]
 * @param {Readonly<Options> | null | undefined} [options]
 * @returns {TransformBridge | TransformMutate}
 *
 * @param {Readonly<Options> | Processor | null | undefined} [destination]
 *   Processor or configuration (optional).
 * @param {Readonly<Options> | null | undefined} [options]
 *   When a processor was given,
 *   configuration (optional).
 * @returns {TransformBridge | TransformMutate}
 *   Transform.
 */
function remarkRehype(destination, options) {
	if (destination && "run" in destination) {
		/**
		 * @type {TransformBridge}
		 */
		return async function (tree, file) {
			// Cast because root in -> root out.
			const hastTree = /** @type {HastRoot} */ (toHast(tree, { file, ...options }));
			await destination.run(hastTree, file);
		};
	}

	/**
	 * @type {TransformMutate}
	 */
	return function (tree, file) {
		// Cast because root in -> root out.
		// To do: in the future, disallow ` || options` fallback.
		// With `unified-engine`, `destination` can be `undefined` but
		// `options` will be the file set.
		// We should not pass that as `options`.
		return /** @type {HastRoot} */ (toHast(tree, { file, ...(destination || options) }));
	};
}

/**
 * List of HTML void tag names.
 *
 * @type {Array<string>}
 */
const htmlVoidElements = ["area", "base", "basefont", "bgsound", "br", "col", "command", "embed", "frame", "hr", "image", "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr"];

/**
 * @typedef CoreOptions
 * @property {ReadonlyArray<string>} [subset=[]]
 *   Whether to only escape the given subset of characters.
 * @property {boolean} [escapeOnly=false]
 *   Whether to only escape possibly dangerous characters.
 *   Those characters are `"`, `&`, `'`, `<`, `>`, and `` ` ``.
 *
 * @typedef FormatOptions
 * @property {(code: number, next: number, options: CoreWithFormatOptions) => string} format
 *   Format strategy.
 *
 * @typedef {CoreOptions & FormatOptions & import('./util/format-smart.js').FormatSmartOptions} CoreWithFormatOptions
 */

const defaultSubsetRegex = /["&'<>`]/g;
const surrogatePairsRegex = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g;
const controlCharactersRegex =
	// eslint-disable-next-line no-control-regex, unicorn/no-hex-escape
	/[\x01-\t\v\f\x0E-\x1F\x7F\x81\x8D\x8F\x90\x9D\xA0-\uFFFF]/g;
const regexEscapeRegex = /[|\\{}()[\]^$+*?.]/g;

/** @type {WeakMap<ReadonlyArray<string>, RegExp>} */
const subsetToRegexCache = new WeakMap();

/**
 * Encode certain characters in `value`.
 *
 * @param {string} value
 * @param {CoreWithFormatOptions} options
 * @returns {string}
 */
function core(value, options) {
	value = value.replace(options.subset ? charactersToExpressionCached(options.subset) : defaultSubsetRegex, basic);

	if (options.subset || options.escapeOnly) {
		return value;
	}

	return (
		value
			// Surrogate pairs.
			.replace(surrogatePairsRegex, surrogate)
			// BMP control characters (C0 except for LF, CR, SP; DEL; and some more
			// non-ASCII ones).
			.replace(controlCharactersRegex, basic)
	);

	/**
	 * @param {string} pair
	 * @param {number} index
	 * @param {string} all
	 */
	function surrogate(pair, index, all) {
		return options.format((pair.charCodeAt(0) - 0xd800) * 0x400 + pair.charCodeAt(1) - 0xdc00 + 0x10000, all.charCodeAt(index + 2), options);
	}

	/**
	 * @param {string} character
	 * @param {number} index
	 * @param {string} all
	 */
	function basic(character, index, all) {
		return options.format(character.charCodeAt(0), all.charCodeAt(index + 1), options);
	}
}

/**
 * A wrapper function that caches the result of `charactersToExpression` with a WeakMap.
 * This can improve performance when tooling calls `charactersToExpression` repeatedly
 * with the same subset.
 *
 * @param {ReadonlyArray<string>} subset
 * @returns {RegExp}
 */
function charactersToExpressionCached(subset) {
	let cached = subsetToRegexCache.get(subset);

	if (!cached) {
		cached = charactersToExpression(subset);
		subsetToRegexCache.set(subset, cached);
	}

	return cached;
}

/**
 * @param {ReadonlyArray<string>} subset
 * @returns {RegExp}
 */
function charactersToExpression(subset) {
	/** @type {Array<string>} */
	const groups = [];
	let index = -1;

	while (++index < subset.length) {
		groups.push(subset[index].replace(regexEscapeRegex, "\\$&"));
	}

	return new RegExp("(?:" + groups.join("|") + ")", "g");
}

const hexadecimalRegex = /[\dA-Fa-f]/;

/**
 * Configurable ways to encode characters as hexadecimal references.
 *
 * @param {number} code
 * @param {number} next
 * @param {boolean|undefined} omit
 * @returns {string}
 */
function toHexadecimal(code, next, omit) {
	const value = "&#x" + code.toString(16).toUpperCase();
	return omit && next && !hexadecimalRegex.test(String.fromCharCode(next)) ? value : value + ";";
}

const decimalRegex = /\d/;

/**
 * Configurable ways to encode characters as decimal references.
 *
 * @param {number} code
 * @param {number} next
 * @param {boolean|undefined} omit
 * @returns {string}
 */
function toDecimal(code, next, omit) {
	const value = "&#" + String(code);
	return omit && next && !decimalRegex.test(String.fromCharCode(next)) ? value : value + ";";
}

/**
 * List of legacy HTML named character references that don’t need a trailing semicolon.
 *
 * @type {Array<string>}
 */
const characterEntitiesLegacy = [
	"AElig",
	"AMP",
	"Aacute",
	"Acirc",
	"Agrave",
	"Aring",
	"Atilde",
	"Auml",
	"COPY",
	"Ccedil",
	"ETH",
	"Eacute",
	"Ecirc",
	"Egrave",
	"Euml",
	"GT",
	"Iacute",
	"Icirc",
	"Igrave",
	"Iuml",
	"LT",
	"Ntilde",
	"Oacute",
	"Ocirc",
	"Ograve",
	"Oslash",
	"Otilde",
	"Ouml",
	"QUOT",
	"REG",
	"THORN",
	"Uacute",
	"Ucirc",
	"Ugrave",
	"Uuml",
	"Yacute",
	"aacute",
	"acirc",
	"acute",
	"aelig",
	"agrave",
	"amp",
	"aring",
	"atilde",
	"auml",
	"brvbar",
	"ccedil",
	"cedil",
	"cent",
	"copy",
	"curren",
	"deg",
	"divide",
	"eacute",
	"ecirc",
	"egrave",
	"eth",
	"euml",
	"frac12",
	"frac14",
	"frac34",
	"gt",
	"iacute",
	"icirc",
	"iexcl",
	"igrave",
	"iquest",
	"iuml",
	"laquo",
	"lt",
	"macr",
	"micro",
	"middot",
	"nbsp",
	"not",
	"ntilde",
	"oacute",
	"ocirc",
	"ograve",
	"ordf",
	"ordm",
	"oslash",
	"otilde",
	"ouml",
	"para",
	"plusmn",
	"pound",
	"quot",
	"raquo",
	"reg",
	"sect",
	"shy",
	"sup1",
	"sup2",
	"sup3",
	"szlig",
	"thorn",
	"times",
	"uacute",
	"ucirc",
	"ugrave",
	"uml",
	"uuml",
	"yacute",
	"yen",
	"yuml",
];

/**
 * Map of named character references from HTML 4.
 *
 * @type {Record<string, string>}
 */
const characterEntitiesHtml4 = {
	nbsp: " ",
	iexcl: "¡",
	cent: "¢",
	pound: "£",
	curren: "¤",
	yen: "¥",
	brvbar: "¦",
	sect: "§",
	uml: "¨",
	copy: "©",
	ordf: "ª",
	laquo: "«",
	not: "¬",
	shy: "­",
	reg: "®",
	macr: "¯",
	deg: "°",
	plusmn: "±",
	sup2: "²",
	sup3: "³",
	acute: "´",
	micro: "µ",
	para: "¶",
	middot: "·",
	cedil: "¸",
	sup1: "¹",
	ordm: "º",
	raquo: "»",
	frac14: "¼",
	frac12: "½",
	frac34: "¾",
	iquest: "¿",
	Agrave: "À",
	Aacute: "Á",
	Acirc: "Â",
	Atilde: "Ã",
	Auml: "Ä",
	Aring: "Å",
	AElig: "Æ",
	Ccedil: "Ç",
	Egrave: "È",
	Eacute: "É",
	Ecirc: "Ê",
	Euml: "Ë",
	Igrave: "Ì",
	Iacute: "Í",
	Icirc: "Î",
	Iuml: "Ï",
	ETH: "Ð",
	Ntilde: "Ñ",
	Ograve: "Ò",
	Oacute: "Ó",
	Ocirc: "Ô",
	Otilde: "Õ",
	Ouml: "Ö",
	times: "×",
	Oslash: "Ø",
	Ugrave: "Ù",
	Uacute: "Ú",
	Ucirc: "Û",
	Uuml: "Ü",
	Yacute: "Ý",
	THORN: "Þ",
	szlig: "ß",
	agrave: "à",
	aacute: "á",
	acirc: "â",
	atilde: "ã",
	auml: "ä",
	aring: "å",
	aelig: "æ",
	ccedil: "ç",
	egrave: "è",
	eacute: "é",
	ecirc: "ê",
	euml: "ë",
	igrave: "ì",
	iacute: "í",
	icirc: "î",
	iuml: "ï",
	eth: "ð",
	ntilde: "ñ",
	ograve: "ò",
	oacute: "ó",
	ocirc: "ô",
	otilde: "õ",
	ouml: "ö",
	divide: "÷",
	oslash: "ø",
	ugrave: "ù",
	uacute: "ú",
	ucirc: "û",
	uuml: "ü",
	yacute: "ý",
	thorn: "þ",
	yuml: "ÿ",
	fnof: "ƒ",
	Alpha: "Α",
	Beta: "Β",
	Gamma: "Γ",
	Delta: "Δ",
	Epsilon: "Ε",
	Zeta: "Ζ",
	Eta: "Η",
	Theta: "Θ",
	Iota: "Ι",
	Kappa: "Κ",
	Lambda: "Λ",
	Mu: "Μ",
	Nu: "Ν",
	Xi: "Ξ",
	Omicron: "Ο",
	Pi: "Π",
	Rho: "Ρ",
	Sigma: "Σ",
	Tau: "Τ",
	Upsilon: "Υ",
	Phi: "Φ",
	Chi: "Χ",
	Psi: "Ψ",
	Omega: "Ω",
	alpha: "α",
	beta: "β",
	gamma: "γ",
	delta: "δ",
	epsilon: "ε",
	zeta: "ζ",
	eta: "η",
	theta: "θ",
	iota: "ι",
	kappa: "κ",
	lambda: "λ",
	mu: "μ",
	nu: "ν",
	xi: "ξ",
	omicron: "ο",
	pi: "π",
	rho: "ρ",
	sigmaf: "ς",
	sigma: "σ",
	tau: "τ",
	upsilon: "υ",
	phi: "φ",
	chi: "χ",
	psi: "ψ",
	omega: "ω",
	thetasym: "ϑ",
	upsih: "ϒ",
	piv: "ϖ",
	bull: "•",
	hellip: "…",
	prime: "′",
	Prime: "″",
	oline: "‾",
	frasl: "⁄",
	weierp: "℘",
	image: "ℑ",
	real: "ℜ",
	trade: "™",
	alefsym: "ℵ",
	larr: "←",
	uarr: "↑",
	rarr: "→",
	darr: "↓",
	harr: "↔",
	crarr: "↵",
	lArr: "⇐",
	uArr: "⇑",
	rArr: "⇒",
	dArr: "⇓",
	hArr: "⇔",
	forall: "∀",
	part: "∂",
	exist: "∃",
	empty: "∅",
	nabla: "∇",
	isin: "∈",
	notin: "∉",
	ni: "∋",
	prod: "∏",
	sum: "∑",
	minus: "−",
	lowast: "∗",
	radic: "√",
	prop: "∝",
	infin: "∞",
	ang: "∠",
	and: "∧",
	or: "∨",
	cap: "∩",
	cup: "∪",
	int: "∫",
	there4: "∴",
	sim: "∼",
	cong: "≅",
	asymp: "≈",
	ne: "≠",
	equiv: "≡",
	le: "≤",
	ge: "≥",
	sub: "⊂",
	sup: "⊃",
	nsub: "⊄",
	sube: "⊆",
	supe: "⊇",
	oplus: "⊕",
	otimes: "⊗",
	perp: "⊥",
	sdot: "⋅",
	lceil: "⌈",
	rceil: "⌉",
	lfloor: "⌊",
	rfloor: "⌋",
	lang: "〈",
	rang: "〉",
	loz: "◊",
	spades: "♠",
	clubs: "♣",
	hearts: "♥",
	diams: "♦",
	quot: '"',
	amp: "&",
	lt: "<",
	gt: ">",
	OElig: "Œ",
	oelig: "œ",
	Scaron: "Š",
	scaron: "š",
	Yuml: "Ÿ",
	circ: "ˆ",
	tilde: "˜",
	ensp: " ",
	emsp: " ",
	thinsp: " ",
	zwnj: "‌",
	zwj: "‍",
	lrm: "‎",
	rlm: "‏",
	ndash: "–",
	mdash: "—",
	lsquo: "‘",
	rsquo: "’",
	sbquo: "‚",
	ldquo: "“",
	rdquo: "”",
	bdquo: "„",
	dagger: "†",
	Dagger: "‡",
	permil: "‰",
	lsaquo: "‹",
	rsaquo: "›",
	euro: "€",
};

/**
 * List of legacy (that don’t need a trailing `;`) named references which could,
 * depending on what follows them, turn into a different meaning
 *
 * @type {Array<string>}
 */
const dangerous = ["cent", "copy", "divide", "gt", "lt", "not", "para", "times"];

const own$1 = {}.hasOwnProperty;

/**
 * `characterEntitiesHtml4` but inverted.
 *
 * @type {Record<string, string>}
 */
const characters = {};

/** @type {string} */
let key;

for (key in characterEntitiesHtml4) {
	if (own$1.call(characterEntitiesHtml4, key)) {
		characters[characterEntitiesHtml4[key]] = key;
	}
}

const notAlphanumericRegex = /[^\dA-Za-z]/;

/**
 * Configurable ways to encode characters as named references.
 *
 * @param {number} code
 * @param {number} next
 * @param {boolean|undefined} omit
 * @param {boolean|undefined} attribute
 * @returns {string}
 */
function toNamed(code, next, omit, attribute) {
	const character = String.fromCharCode(code);

	if (own$1.call(characters, character)) {
		const name = characters[character];
		const value = "&" + name;

		if (omit && characterEntitiesLegacy.includes(name) && !dangerous.includes(name) && (!attribute || (next && next !== 61 /* `=` */ && notAlphanumericRegex.test(String.fromCharCode(next))))) {
			return value;
		}

		return value + ";";
	}

	return "";
}

/**
 * @typedef FormatSmartOptions
 * @property {boolean} [useNamedReferences=false]
 *   Prefer named character references (`&amp;`) where possible.
 * @property {boolean} [useShortestReferences=false]
 *   Prefer the shortest possible reference, if that results in less bytes.
 *   **Note**: `useNamedReferences` can be omitted when using `useShortestReferences`.
 * @property {boolean} [omitOptionalSemicolons=false]
 *   Whether to omit semicolons when possible.
 *   **Note**: This creates what HTML calls “parse errors” but is otherwise still valid HTML — don’t use this except when building a minifier.
 *   Omitting semicolons is possible for certain named and numeric references in some cases.
 * @property {boolean} [attribute=false]
 *   Create character references which don’t fail in attributes.
 *   **Note**: `attribute` only applies when operating dangerously with
 *   `omitOptionalSemicolons: true`.
 */

/**
 * Configurable ways to encode a character yielding pretty or small results.
 *
 * @param {number} code
 * @param {number} next
 * @param {FormatSmartOptions} options
 * @returns {string}
 */
function formatSmart(code, next, options) {
	let numeric = toHexadecimal(code, next, options.omitOptionalSemicolons);
	/** @type {string|undefined} */
	let named;

	if (options.useNamedReferences || options.useShortestReferences) {
		named = toNamed(code, next, options.omitOptionalSemicolons, options.attribute);
	}

	// Use the shortest numeric reference when requested.
	// A simple algorithm would use decimal for all code points under 100, as
	// those are shorter than hexadecimal:
	//
	// * `&#99;` vs `&#x63;` (decimal shorter)
	// * `&#100;` vs `&#x64;` (equal)
	//
	// However, because we take `next` into consideration when `omit` is used,
	// And it would be possible that decimals are shorter on bigger values as
	// well if `next` is hexadecimal but not decimal, we instead compare both.
	if ((options.useShortestReferences || !named) && options.useShortestReferences) {
		const decimal = toDecimal(code, next, options.omitOptionalSemicolons);

		if (decimal.length < numeric.length) {
			numeric = decimal;
		}
	}

	return named && (!options.useShortestReferences || named.length < numeric.length) ? named : numeric;
}

/**
 * @typedef {import('./core.js').CoreOptions & import('./util/format-smart.js').FormatSmartOptions} Options
 * @typedef {import('./core.js').CoreOptions} LightOptions
 */

/**
 * Encode special characters in `value`.
 *
 * @param {string} value
 *   Value to encode.
 * @param {Options} [options]
 *   Configuration.
 * @returns {string}
 *   Encoded value.
 */
function stringifyEntities(value, options) {
	return core(value, Object.assign({ format: formatSmart }, options));
}

/**
 * @import {Comment, Parents} from 'hast'
 * @import {State} from '../index.js'
 */

const htmlCommentRegex = /^>|^->|<!--|-->|--!>|<!-$/g;

// Declare arrays as variables so it can be cached by `stringifyEntities`
const bogusCommentEntitySubset = [">"];
const commentEntitySubset = ["<", ">"];

/**
 * Serialize a comment.
 *
 * @param {Comment} node
 *   Node to handle.
 * @param {number | undefined} _1
 *   Index of `node` in `parent.
 * @param {Parents | undefined} _2
 *   Parent of `node`.
 * @param {State} state
 *   Info passed around about the current state.
 * @returns {string}
 *   Serialized node.
 */
function comment(node, _1, _2, state) {
	// See: <https://html.spec.whatwg.org/multipage/syntax.html#comments>
	return state.settings.bogusComments ?
			"<?" +
				stringifyEntities(
					node.value,
					Object.assign({}, state.settings.characterReferences, {
						subset: bogusCommentEntitySubset,
					}),
				) +
				">"
		:	"<!--" + node.value.replace(htmlCommentRegex, encode) + "-->";

	/**
	 * @param {string} $0
	 */
	function encode($0) {
		return stringifyEntities(
			$0,
			Object.assign({}, state.settings.characterReferences, {
				subset: commentEntitySubset,
			}),
		);
	}
}

/**
 * @import {Doctype, Parents} from 'hast'
 * @import {State} from '../index.js'
 */

/**
 * Serialize a doctype.
 *
 * @param {Doctype} _1
 *   Node to handle.
 * @param {number | undefined} _2
 *   Index of `node` in `parent.
 * @param {Parents | undefined} _3
 *   Parent of `node`.
 * @param {State} state
 *   Info passed around about the current state.
 * @returns {string}
 *   Serialized node.
 */
function doctype(_1, _2, _3, state) {
	return "<!" + (state.settings.upperDoctype ? "DOCTYPE" : "doctype") + (state.settings.tightDoctype ? "" : " ") + "html>";
}

/**
 * @import {Parents, RootContent} from 'hast'
 */

const siblingAfter = siblings(1);
const siblingBefore = siblings(-1);

/** @type {Array<RootContent>} */
const emptyChildren$1 = [];

/**
 * Factory to check siblings in a direction.
 *
 * @param {number} increment
 */
function siblings(increment) {
	return sibling;

	/**
	 * Find applicable siblings in a direction.
	 *
	 * @template {Parents} Parent
	 *   Parent type.
	 * @param {Parent | undefined} parent
	 *   Parent.
	 * @param {number | undefined} index
	 *   Index of child in `parent`.
	 * @param {boolean | undefined} [includeWhitespace=false]
	 *   Whether to include whitespace (default: `false`).
	 * @returns {Parent extends {children: Array<infer Child>} ? Child | undefined : never}
	 *   Child of parent.
	 */
	function sibling(parent, index, includeWhitespace) {
		const siblings = parent ? parent.children : emptyChildren$1;
		let offset = (index || 0) + increment;
		let next = siblings[offset];

		if (!includeWhitespace) {
			while (next && whitespace(next)) {
				offset += increment;
				next = siblings[offset];
			}
		}

		// @ts-expect-error: it’s a correct child.
		return next;
	}
}

/**
 * @import {Element, Parents} from 'hast'
 */

/**
 * @callback OmitHandle
 *   Check if a tag can be omitted.
 * @param {Element} element
 *   Element to check.
 * @param {number | undefined} index
 *   Index of element in parent.
 * @param {Parents | undefined} parent
 *   Parent of element.
 * @returns {boolean}
 *   Whether to omit a tag.
 *
 */

const own = {}.hasOwnProperty;

/**
 * Factory to check if a given node can have a tag omitted.
 *
 * @param {Record<string, OmitHandle>} handlers
 *   Omission handlers, where each key is a tag name, and each value is the
 *   corresponding handler.
 * @returns {OmitHandle}
 *   Whether to omit a tag of an element.
 */
function omission(handlers) {
	return omit;

	/**
	 * Check if a given node can have a tag omitted.
	 *
	 * @type {OmitHandle}
	 */
	function omit(node, index, parent) {
		return own.call(handlers, node.tagName) && handlers[node.tagName](node, index, parent);
	}
}

/**
 * @import {Element, Parents} from 'hast'
 */

const closing = omission({
	body: body$1,
	caption: headOrColgroupOrCaption,
	colgroup: headOrColgroupOrCaption,
	dd,
	dt,
	head: headOrColgroupOrCaption,
	html: html$1,
	li,
	optgroup,
	option,
	p,
	rp: rubyElement,
	rt: rubyElement,
	tbody: tbody$1,
	td: cells,
	tfoot,
	th: cells,
	thead,
	tr,
});

/**
 * Macro for `</head>`, `</colgroup>`, and `</caption>`.
 *
 * @param {Element} _
 *   Element.
 * @param {number | undefined} index
 *   Index of element in parent.
 * @param {Parents | undefined} parent
 *   Parent of element.
 * @returns {boolean}
 *   Whether the closing tag can be omitted.
 */
function headOrColgroupOrCaption(_, index, parent) {
	const next = siblingAfter(parent, index, true);
	return !next || (next.type !== "comment" && !(next.type === "text" && whitespace(next.value.charAt(0))));
}

/**
 * Whether to omit `</html>`.
 *
 * @param {Element} _
 *   Element.
 * @param {number | undefined} index
 *   Index of element in parent.
 * @param {Parents | undefined} parent
 *   Parent of element.
 * @returns {boolean}
 *   Whether the closing tag can be omitted.
 */
function html$1(_, index, parent) {
	const next = siblingAfter(parent, index);
	return !next || next.type !== "comment";
}

/**
 * Whether to omit `</body>`.
 *
 * @param {Element} _
 *   Element.
 * @param {number | undefined} index
 *   Index of element in parent.
 * @param {Parents | undefined} parent
 *   Parent of element.
 * @returns {boolean}
 *   Whether the closing tag can be omitted.
 */
function body$1(_, index, parent) {
	const next = siblingAfter(parent, index);
	return !next || next.type !== "comment";
}

/**
 * Whether to omit `</p>`.
 *
 * @param {Element} _
 *   Element.
 * @param {number | undefined} index
 *   Index of element in parent.
 * @param {Parents | undefined} parent
 *   Parent of element.
 * @returns {boolean}
 *   Whether the closing tag can be omitted.
 */
function p(_, index, parent) {
	const next = siblingAfter(parent, index);
	return next ?
			next.type === "element" &&
				(next.tagName === "address" ||
					next.tagName === "article" ||
					next.tagName === "aside" ||
					next.tagName === "blockquote" ||
					next.tagName === "details" ||
					next.tagName === "div" ||
					next.tagName === "dl" ||
					next.tagName === "fieldset" ||
					next.tagName === "figcaption" ||
					next.tagName === "figure" ||
					next.tagName === "footer" ||
					next.tagName === "form" ||
					next.tagName === "h1" ||
					next.tagName === "h2" ||
					next.tagName === "h3" ||
					next.tagName === "h4" ||
					next.tagName === "h5" ||
					next.tagName === "h6" ||
					next.tagName === "header" ||
					next.tagName === "hgroup" ||
					next.tagName === "hr" ||
					next.tagName === "main" ||
					next.tagName === "menu" ||
					next.tagName === "nav" ||
					next.tagName === "ol" ||
					next.tagName === "p" ||
					next.tagName === "pre" ||
					next.tagName === "section" ||
					next.tagName === "table" ||
					next.tagName === "ul")
		:	!parent ||
				// Confusing parent.
				!(
					parent.type === "element" &&
					(parent.tagName === "a" ||
						parent.tagName === "audio" ||
						parent.tagName === "del" ||
						parent.tagName === "ins" ||
						parent.tagName === "map" ||
						parent.tagName === "noscript" ||
						parent.tagName === "video")
				);
}

/**
 * Whether to omit `</li>`.
 *
 * @param {Element} _
 *   Element.
 * @param {number | undefined} index
 *   Index of element in parent.
 * @param {Parents | undefined} parent
 *   Parent of element.
 * @returns {boolean}
 *   Whether the closing tag can be omitted.
 */
function li(_, index, parent) {
	const next = siblingAfter(parent, index);
	return !next || (next.type === "element" && next.tagName === "li");
}

/**
 * Whether to omit `</dt>`.
 *
 * @param {Element} _
 *   Element.
 * @param {number | undefined} index
 *   Index of element in parent.
 * @param {Parents | undefined} parent
 *   Parent of element.
 * @returns {boolean}
 *   Whether the closing tag can be omitted.
 */
function dt(_, index, parent) {
	const next = siblingAfter(parent, index);
	return Boolean(next && next.type === "element" && (next.tagName === "dt" || next.tagName === "dd"));
}

/**
 * Whether to omit `</dd>`.
 *
 * @param {Element} _
 *   Element.
 * @param {number | undefined} index
 *   Index of element in parent.
 * @param {Parents | undefined} parent
 *   Parent of element.
 * @returns {boolean}
 *   Whether the closing tag can be omitted.
 */
function dd(_, index, parent) {
	const next = siblingAfter(parent, index);
	return !next || (next.type === "element" && (next.tagName === "dt" || next.tagName === "dd"));
}

/**
 * Whether to omit `</rt>` or `</rp>`.
 *
 * @param {Element} _
 *   Element.
 * @param {number | undefined} index
 *   Index of element in parent.
 * @param {Parents | undefined} parent
 *   Parent of element.
 * @returns {boolean}
 *   Whether the closing tag can be omitted.
 */
function rubyElement(_, index, parent) {
	const next = siblingAfter(parent, index);
	return !next || (next.type === "element" && (next.tagName === "rp" || next.tagName === "rt"));
}

/**
 * Whether to omit `</optgroup>`.
 *
 * @param {Element} _
 *   Element.
 * @param {number | undefined} index
 *   Index of element in parent.
 * @param {Parents | undefined} parent
 *   Parent of element.
 * @returns {boolean}
 *   Whether the closing tag can be omitted.
 */
function optgroup(_, index, parent) {
	const next = siblingAfter(parent, index);
	return !next || (next.type === "element" && next.tagName === "optgroup");
}

/**
 * Whether to omit `</option>`.
 *
 * @param {Element} _
 *   Element.
 * @param {number | undefined} index
 *   Index of element in parent.
 * @param {Parents | undefined} parent
 *   Parent of element.
 * @returns {boolean}
 *   Whether the closing tag can be omitted.
 */
function option(_, index, parent) {
	const next = siblingAfter(parent, index);
	return !next || (next.type === "element" && (next.tagName === "option" || next.tagName === "optgroup"));
}

/**
 * Whether to omit `</thead>`.
 *
 * @param {Element} _
 *   Element.
 * @param {number | undefined} index
 *   Index of element in parent.
 * @param {Parents | undefined} parent
 *   Parent of element.
 * @returns {boolean}
 *   Whether the closing tag can be omitted.
 */
function thead(_, index, parent) {
	const next = siblingAfter(parent, index);
	return Boolean(next && next.type === "element" && (next.tagName === "tbody" || next.tagName === "tfoot"));
}

/**
 * Whether to omit `</tbody>`.
 *
 * @param {Element} _
 *   Element.
 * @param {number | undefined} index
 *   Index of element in parent.
 * @param {Parents | undefined} parent
 *   Parent of element.
 * @returns {boolean}
 *   Whether the closing tag can be omitted.
 */
function tbody$1(_, index, parent) {
	const next = siblingAfter(parent, index);
	return !next || (next.type === "element" && (next.tagName === "tbody" || next.tagName === "tfoot"));
}

/**
 * Whether to omit `</tfoot>`.
 *
 * @param {Element} _
 *   Element.
 * @param {number | undefined} index
 *   Index of element in parent.
 * @param {Parents | undefined} parent
 *   Parent of element.
 * @returns {boolean}
 *   Whether the closing tag can be omitted.
 */
function tfoot(_, index, parent) {
	return !siblingAfter(parent, index);
}

/**
 * Whether to omit `</tr>`.
 *
 * @param {Element} _
 *   Element.
 * @param {number | undefined} index
 *   Index of element in parent.
 * @param {Parents | undefined} parent
 *   Parent of element.
 * @returns {boolean}
 *   Whether the closing tag can be omitted.
 */
function tr(_, index, parent) {
	const next = siblingAfter(parent, index);
	return !next || (next.type === "element" && next.tagName === "tr");
}

/**
 * Whether to omit `</td>` or `</th>`.
 *
 * @param {Element} _
 *   Element.
 * @param {number | undefined} index
 *   Index of element in parent.
 * @param {Parents | undefined} parent
 *   Parent of element.
 * @returns {boolean}
 *   Whether the closing tag can be omitted.
 */
function cells(_, index, parent) {
	const next = siblingAfter(parent, index);
	return !next || (next.type === "element" && (next.tagName === "td" || next.tagName === "th"));
}

/**
 * @import {Element, Parents} from 'hast'
 */

const opening = omission({
	body,
	colgroup,
	head,
	html,
	tbody,
});

/**
 * Whether to omit `<html>`.
 *
 * @param {Element} node
 *   Element.
 * @returns {boolean}
 *   Whether the opening tag can be omitted.
 */
function html(node) {
	const head = siblingAfter(node, -1);
	return !head || head.type !== "comment";
}

/**
 * Whether to omit `<head>`.
 *
 * @param {Element} node
 *   Element.
 * @returns {boolean}
 *   Whether the opening tag can be omitted.
 */
function head(node) {
	/** @type {Set<string>} */
	const seen = new Set();

	// Whether `srcdoc` or not,
	// make sure the content model at least doesn’t have too many `base`s/`title`s.
	for (const child of node.children) {
		if (child.type === "element" && (child.tagName === "base" || child.tagName === "title")) {
			if (seen.has(child.tagName)) return false;
			seen.add(child.tagName);
		}
	}

	// “May be omitted if the element is empty,
	// or if the first thing inside the head element is an element.”
	const child = node.children[0];
	return !child || child.type === "element";
}

/**
 * Whether to omit `<body>`.
 *
 * @param {Element} node
 *   Element.
 * @returns {boolean}
 *   Whether the opening tag can be omitted.
 */
function body(node) {
	const head = siblingAfter(node, -1, true);

	return (
		!head ||
		(head.type !== "comment" &&
			!(head.type === "text" && whitespace(head.value.charAt(0))) &&
			!(head.type === "element" && (head.tagName === "meta" || head.tagName === "link" || head.tagName === "script" || head.tagName === "style" || head.tagName === "template")))
	);
}

/**
 * Whether to omit `<colgroup>`.
 * The spec describes some logic for the opening tag, but it’s easier to
 * implement in the closing tag, to the same effect, so we handle it there
 * instead.
 *
 * @param {Element} node
 *   Element.
 * @param {number | undefined} index
 *   Index of element in parent.
 * @param {Parents | undefined} parent
 *   Parent of element.
 * @returns {boolean}
 *   Whether the opening tag can be omitted.
 */
function colgroup(node, index, parent) {
	const previous = siblingBefore(parent, index);
	const head = siblingAfter(node, -1, true);

	// Previous colgroup was already omitted.
	if (parent && previous && previous.type === "element" && previous.tagName === "colgroup" && closing(previous, parent.children.indexOf(previous), parent)) {
		return false;
	}

	return Boolean(head && head.type === "element" && head.tagName === "col");
}

/**
 * Whether to omit `<tbody>`.
 *
 * @param {Element} node
 *   Element.
 * @param {number | undefined} index
 *   Index of element in parent.
 * @param {Parents | undefined} parent
 *   Parent of element.
 * @returns {boolean}
 *   Whether the opening tag can be omitted.
 */
function tbody(node, index, parent) {
	const previous = siblingBefore(parent, index);
	const head = siblingAfter(node, -1);

	// Previous table section was already omitted.
	if (parent && previous && previous.type === "element" && (previous.tagName === "thead" || previous.tagName === "tbody") && closing(previous, parent.children.indexOf(previous), parent)) {
		return false;
	}

	return Boolean(head && head.type === "element" && head.tagName === "tr");
}

/**
 * @import {Element, Parents, Properties} from 'hast'
 * @import {State} from '../index.js'
 */

/**
 * Maps of subsets.
 *
 * Each value is a matrix of tuples.
 * The value at `0` causes parse errors, the value at `1` is valid.
 * Of both, the value at `0` is unsafe, and the value at `1` is safe.
 *
 * @type {Record<'double' | 'name' | 'single' | 'unquoted', Array<[Array<string>, Array<string>]>>}
 */
const constants = {
	// See: <https://html.spec.whatwg.org/#attribute-name-state>.
	name: [
		["\t\n\f\r &/=>".split(""), "\t\n\f\r \"&'/=>`".split("")],
		["\0\t\n\f\r \"&'/<=>".split(""), "\0\t\n\f\r \"&'/<=>`".split("")],
	],
	// See: <https://html.spec.whatwg.org/#attribute-value-(unquoted)-state>.
	unquoted: [
		["\t\n\f\r &>".split(""), "\0\t\n\f\r \"&'<=>`".split("")],
		["\0\t\n\f\r \"&'<=>`".split(""), "\0\t\n\f\r \"&'<=>`".split("")],
	],
	// See: <https://html.spec.whatwg.org/#attribute-value-(single-quoted)-state>.
	single: [
		["&'".split(""), "\"&'`".split("")],
		["\0&'".split(""), "\0\"&'`".split("")],
	],
	// See: <https://html.spec.whatwg.org/#attribute-value-(double-quoted)-state>.
	double: [
		['"&'.split(""), "\"&'`".split("")],
		['\0"&'.split(""), "\0\"&'`".split("")],
	],
};

/**
 * Serialize an element node.
 *
 * @param {Element} node
 *   Node to handle.
 * @param {number | undefined} index
 *   Index of `node` in `parent.
 * @param {Parents | undefined} parent
 *   Parent of `node`.
 * @param {State} state
 *   Info passed around about the current state.
 * @returns {string}
 *   Serialized node.
 */
function element(node, index, parent, state) {
	const schema = state.schema;
	const omit = schema.space === "svg" ? false : state.settings.omitOptionalTags;
	let selfClosing = schema.space === "svg" ? state.settings.closeEmptyElements : state.settings.voids.includes(node.tagName.toLowerCase());
	/** @type {Array<string>} */
	const parts = [];
	/** @type {string} */
	let last;

	if (schema.space === "html" && node.tagName === "svg") {
		state.schema = svg;
	}

	const attributes = serializeAttributes(state, node.properties);

	const content = state.all(schema.space === "html" && node.tagName === "template" ? node.content : node);

	state.schema = schema;

	// If the node is categorised as void, but it has children, remove the
	// categorisation.
	// This enables for example `menuitem`s, which are void in W3C HTML but not
	// void in WHATWG HTML, to be stringified properly.
	// Note: `menuitem` has since been removed from the HTML spec, and so is no
	// longer void.
	if (content) selfClosing = false;

	if (attributes || !omit || !opening(node, index, parent)) {
		parts.push("<", node.tagName, attributes ? " " + attributes : "");

		if (selfClosing && (schema.space === "svg" || state.settings.closeSelfClosing)) {
			last = attributes.charAt(attributes.length - 1);
			if (!state.settings.tightSelfClosing || last === "/" || (last && last !== '"' && last !== "'")) {
				parts.push(" ");
			}

			parts.push("/");
		}

		parts.push(">");
	}

	parts.push(content);

	if (!selfClosing && (!omit || !closing(node, index, parent))) {
		parts.push("</" + node.tagName + ">");
	}

	return parts.join("");
}

/**
 * @param {State} state
 * @param {Properties | null | undefined} properties
 * @returns {string}
 */
function serializeAttributes(state, properties) {
	/** @type {Array<string>} */
	const values = [];
	let index = -1;
	/** @type {string} */
	let key;

	if (properties) {
		for (key in properties) {
			if (properties[key] !== null && properties[key] !== undefined) {
				const value = serializeAttribute(state, key, properties[key]);
				if (value) values.push(value);
			}
		}
	}

	while (++index < values.length) {
		const last = state.settings.tightAttributes ? values[index].charAt(values[index].length - 1) : undefined;

		// In tight mode, don’t add a space after quoted attributes.
		if (index !== values.length - 1 && last !== '"' && last !== "'") {
			values[index] += " ";
		}
	}

	return values.join("");
}

/**
 * @param {State} state
 * @param {string} key
 * @param {Properties[keyof Properties]} value
 * @returns {string}
 */
function serializeAttribute(state, key, value) {
	const info = find(state.schema, key);
	const x = state.settings.allowParseErrors && state.schema.space === "html" ? 0 : 1;
	const y = state.settings.allowDangerousCharacters ? 0 : 1;
	let quote = state.quote;
	/** @type {string | undefined} */
	let result;

	if (info.overloadedBoolean && (value === info.attribute || value === "")) {
		value = true;
	} else if ((info.boolean || info.overloadedBoolean) && (typeof value !== "string" || value === info.attribute || value === "")) {
		value = Boolean(value);
	}

	if (value === null || value === undefined || value === false || (typeof value === "number" && Number.isNaN(value))) {
		return "";
	}

	const name = stringifyEntities(
		info.attribute,
		Object.assign({}, state.settings.characterReferences, {
			// Always encode without parse errors in non-HTML.
			subset: constants.name[x][y],
		}),
	);

	// No value.
	// There is currently only one boolean property in SVG: `[download]` on
	// `<a>`.
	// This property does not seem to work in browsers (Firefox, Safari, Chrome),
	// so I can’t test if dropping the value works.
	// But I assume that it should:
	//
	// ```html
	// <!doctype html>
	// <svg viewBox="0 0 100 100">
	//   <a href=https://example.com download>
	//     <circle cx=50 cy=40 r=35 />
	//   </a>
	// </svg>
	// ```
	//
	// See: <https://github.com/wooorm/property-information/blob/main/lib/svg.js>
	if (value === true) return name;

	// `spaces` doesn’t accept a second argument, but it’s given here just to
	// keep the code cleaner.
	value =
		Array.isArray(value) ?
			(info.commaSeparated ? stringify$1 : stringify)(value, {
				padLeft: !state.settings.tightCommaSeparatedLists,
			})
		:	String(value);

	if (state.settings.collapseEmptyAttributes && !value) return name;

	// Check unquoted value.
	if (state.settings.preferUnquoted) {
		result = stringifyEntities(
			value,
			Object.assign({}, state.settings.characterReferences, {
				attribute: true,
				subset: constants.unquoted[x][y],
			}),
		);
	}

	// If we don’t want unquoted, or if `value` contains character references when
	// unquoted…
	if (result !== value) {
		// If the alternative is less common than `quote`, switch.
		if (state.settings.quoteSmart && ccount(value, quote) > ccount(value, state.alternative)) {
			quote = state.alternative;
		}

		result =
			quote +
			stringifyEntities(
				value,
				Object.assign({}, state.settings.characterReferences, {
					// Always encode without parse errors in non-HTML.
					subset: (quote === "'" ? constants.single : constants.double)[x][y],
					attribute: true,
				}),
			) +
			quote;
	}

	// Don’t add a `=` for unquoted empties.
	return name + (result ? "=" + result : result);
}

/**
 * @import {Parents, Text} from 'hast'
 * @import {Raw} from 'mdast-util-to-hast'
 * @import {State} from '../index.js'
 */

// Declare array as variable so it can be cached by `stringifyEntities`
const textEntitySubset = ["<", "&"];

/**
 * Serialize a text node.
 *
 * @param {Raw | Text} node
 *   Node to handle.
 * @param {number | undefined} _
 *   Index of `node` in `parent.
 * @param {Parents | undefined} parent
 *   Parent of `node`.
 * @param {State} state
 *   Info passed around about the current state.
 * @returns {string}
 *   Serialized node.
 */
function text(node, _, parent, state) {
	// Check if content of `node` should be escaped.
	return parent && parent.type === "element" && (parent.tagName === "script" || parent.tagName === "style") ?
			node.value
		:	stringifyEntities(
				node.value,
				Object.assign({}, state.settings.characterReferences, {
					subset: textEntitySubset,
				}),
			);
}

/**
 * @import {Parents} from 'hast'
 * @import {Raw} from 'mdast-util-to-hast'
 * @import {State} from '../index.js'
 */

/**
 * Serialize a raw node.
 *
 * @param {Raw} node
 *   Node to handle.
 * @param {number | undefined} index
 *   Index of `node` in `parent.
 * @param {Parents | undefined} parent
 *   Parent of `node`.
 * @param {State} state
 *   Info passed around about the current state.
 * @returns {string}
 *   Serialized node.
 */
function raw(node, index, parent, state) {
	return state.settings.allowDangerousHtml ? node.value : text(node, index, parent, state);
}

/**
 * @import {Parents, Root} from 'hast'
 * @import {State} from '../index.js'
 */

/**
 * Serialize a root.
 *
 * @param {Root} node
 *   Node to handle.
 * @param {number | undefined} _1
 *   Index of `node` in `parent.
 * @param {Parents | undefined} _2
 *   Parent of `node`.
 * @param {State} state
 *   Info passed around about the current state.
 * @returns {string}
 *   Serialized node.
 */
function root(node, _1, _2, state) {
	return state.all(node);
}

/**
 * @import {Nodes, Parents} from 'hast'
 * @import {State} from '../index.js'
 */

/**
 * @type {(node: Nodes, index: number | undefined, parent: Parents | undefined, state: State) => string}
 */
const handle = zwitch("type", {
	invalid,
	unknown,
	handlers: { comment, doctype, element, raw, root, text },
});

/**
 * Fail when a non-node is found in the tree.
 *
 * @param {unknown} node
 *   Unknown value.
 * @returns {never}
 *   Never.
 */
function invalid(node) {
	throw new Error("Expected node, not `" + node + "`");
}

/**
 * Fail when a node with an unknown type is found in the tree.
 *
 * @param {unknown} node_
 *  Unknown node.
 * @returns {never}
 *   Never.
 */
function unknown(node_) {
	// `type` is guaranteed by runtime JS.
	const node = /** @type {Nodes} */ (node_);
	throw new Error("Cannot compile unknown node `" + node.type + "`");
}

/**
 * @import {Nodes, Parents, RootContent} from 'hast'
 * @import {Schema} from 'property-information'
 * @import {Options as StringifyEntitiesOptions} from 'stringify-entities'
 */

/** @type {Options} */
const emptyOptions = {};

/** @type {CharacterReferences} */
const emptyCharacterReferences = {};

/** @type {Array<never>} */
const emptyChildren = [];

/**
 * Serialize hast as HTML.
 *
 * @param {Array<RootContent> | Nodes} tree
 *   Tree to serialize.
 * @param {Options | null | undefined} [options]
 *   Configuration (optional).
 * @returns {string}
 *   Serialized HTML.
 */
function toHtml(tree, options) {
	const options_ = options || emptyOptions;
	const quote = options_.quote || '"';
	const alternative = quote === '"' ? "'" : '"';

	if (quote !== '"' && quote !== "'") {
		throw new Error("Invalid quote `" + quote + "`, expected `'` or `\"`");
	}

	/** @type {State} */
	const state = {
		one,
		all,
		settings: {
			omitOptionalTags: options_.omitOptionalTags || false,
			allowParseErrors: options_.allowParseErrors || false,
			allowDangerousCharacters: options_.allowDangerousCharacters || false,
			quoteSmart: options_.quoteSmart || false,
			preferUnquoted: options_.preferUnquoted || false,
			tightAttributes: options_.tightAttributes || false,
			upperDoctype: options_.upperDoctype || false,
			tightDoctype: options_.tightDoctype || false,
			bogusComments: options_.bogusComments || false,
			tightCommaSeparatedLists: options_.tightCommaSeparatedLists || false,
			tightSelfClosing: options_.tightSelfClosing || false,
			collapseEmptyAttributes: options_.collapseEmptyAttributes || false,
			allowDangerousHtml: options_.allowDangerousHtml || false,
			voids: options_.voids || htmlVoidElements,
			characterReferences: options_.characterReferences || emptyCharacterReferences,
			closeSelfClosing: options_.closeSelfClosing || false,
			closeEmptyElements: options_.closeEmptyElements || false,
		},
		schema: options_.space === "svg" ? svg : html$4,
		quote,
		alternative,
	};

	return state.one(Array.isArray(tree) ? { type: "root", children: tree } : tree, undefined, undefined);
}

/**
 * Serialize a node.
 *
 * @this {State}
 *   Info passed around about the current state.
 * @param {Nodes} node
 *   Node to handle.
 * @param {number | undefined} index
 *   Index of `node` in `parent.
 * @param {Parents | undefined} parent
 *   Parent of `node`.
 * @returns {string}
 *   Serialized node.
 */
function one(node, index, parent) {
	return handle(node, index, parent, this);
}

/**
 * Serialize all children of `parent`.
 *
 * @this {State}
 *   Info passed around about the current state.
 * @param {Parents | undefined} parent
 *   Parent whose children to serialize.
 * @returns {string}
 */
function all(parent) {
	/** @type {Array<string>} */
	const results = [];
	const children = (parent && parent.children) || emptyChildren;
	let index = -1;

	while (++index < children.length) {
		results[index] = this.one(children[index], index, parent);
	}

	return results.join("");
}

/**
 * @import {Root} from 'hast'
 * @import {Options} from 'hast-util-to-html'
 * @import {Compiler, Processor} from 'unified'
 */

/**
 * Plugin to add support for serializing as HTML.
 *
 * @param {Options | null | undefined} [options]
 *   Configuration (optional).
 * @returns {undefined}
 *   Nothing.
 */
function rehypeStringify(options) {
	/** @type {Processor<undefined, undefined, undefined, Root, string>} */
	// @ts-expect-error: TS in JSDoc generates wrong types if `this` is typed regularly.
	const self = this;
	const settings = { ...self.data("settings"), ...options };

	self.compiler = compiler;

	/**
	 * @type {Compiler<Root, string>}
	 */
	function compiler(tree) {
		return toHtml(tree, settings);
	}
}

var Fe = Object.defineProperty;
var Ve = (o, e, t) => (e in o ? Fe(o, e, { enumerable: true, configurable: true, writable: true, value: t }) : (o[e] = t));
var k = (o, e, t) => Ve(o, typeof e != "symbol" ? e + "" : e, t);
function Zt(o, e, t, n = "before") {
	const s = typeof t == "string" ? t : t.id,
		r = ht(o),
		i = e.map((d) => bt$1(d, r)),
		l = Bt$1(s, o.doc);
	if (!l) throw new Error(`Block with ID ${s} not found`);
	let a = l.posBeforeNode;
	return (n === "after" && (a += l.node.nodeSize), o.step(new ReplaceStep(a, a, new Slice(Fragment.from(i), 0, 0))), i.map((d) => L$2(d, r)));
}
function Q(o) {
	if (!o || o.type.name !== "column") throw new Error("Invalid columnPos: does not point to column node.");
	const e = o.firstChild;
	if (!e) throw new Error("Invalid column: does not have child node.");
	const t = e.firstChild;
	if (!t) throw new Error("Invalid blockContainer: does not have child node.");
	return o.childCount === 1 && e.childCount === 1 && t.type.name === "paragraph" && t.content.content.length === 0;
}
function eo(o, e) {
	const t = o.doc.resolve(e),
		n = t.nodeAfter;
	if (!n || n.type.name !== "columnList") throw new Error("Invalid columnListPos: does not point to columnList node.");
	for (let s = n.childCount - 1; s >= 0; s--) {
		const r = o.doc.resolve(t.pos + 1).posAtIndex(s),
			l = o.doc.resolve(r).nodeAfter;
		if (!l || l.type.name !== "column") throw new Error("Invalid columnPos: does not point to column node.");
		Q(l) && o.delete(r, r + l.nodeSize);
	}
}
function H(o, e) {
	eo(o, e);
	const n = o.doc.resolve(e).nodeAfter;
	if (!n || n.type.name !== "columnList") throw new Error("Invalid columnListPos: does not point to columnList node.");
	if (n.childCount > 2) return;
	if (n.childCount < 2) throw new Error("Invalid columnList: contains fewer than two children.");
	const s = e + 1,
		i = o.doc.resolve(s).nodeAfter,
		l = e + n.nodeSize - 1,
		c = o.doc.resolve(l).nodeBefore;
	if (!i || !c) throw new Error("Invalid columnList: does not contain children.");
	const d = Q(i),
		u = Q(c);
	if (d && u) {
		o.delete(e, e + n.nodeSize);
		return;
	}
	if (d) {
		o.step(
			new ReplaceAroundStep(
				// Replaces `columnList`.
				e,
				e + n.nodeSize,
				// Replaces with content of last `column`.
				l - c.nodeSize + 1,
				l - 1,
				// Doesn't append anything.
				Slice.empty,
				0,
				false,
			),
		);
		return;
	}
	if (u) {
		o.step(
			new ReplaceAroundStep(
				// Replaces `columnList`.
				e,
				e + n.nodeSize,
				// Replaces with content of first `column`.
				s + 1,
				s + i.nodeSize - 1,
				// Doesn't append anything.
				Slice.empty,
				0,
				false,
			),
		);
		return;
	}
}
function ae(o, e, t) {
	const n = ht(o),
		s = t.map((u) => bt$1(u, n)),
		r = new Set(e.map((u) => (typeof u == "string" ? u : u.id))),
		i = [],
		l = /* @__PURE__ */ new Set(),
		a = typeof e[0] == "string" ? e[0] : e[0].id;
	let c = 0;
	if (
		(o.doc.descendants((u, p) => {
			if (r.size === 0) return false;
			if (!u.type.isInGroup("bnBlock") || !r.has(u.attrs.id)) return true;
			if ((i.push(L$2(u, n)), r.delete(u.attrs.id), t.length > 0 && u.attrs.id === a)) {
				const b = o.doc.nodeSize;
				o.insert(p, s);
				const g = o.doc.nodeSize;
				c += b - g;
			}
			const h = o.doc.nodeSize,
				f = o.doc.resolve(p - c);
			(f.node().type.name === "column" ? l.add(f.before(-1)) : f.node().type.name === "columnList" && l.add(f.before()),
				f.node().type.name === "blockGroup" && f.node(f.depth - 1).type.name !== "doc" && f.node().childCount === 1 ? o.delete(f.before(), f.after()) : o.delete(p - c, p - c + u.nodeSize));
			const m = o.doc.nodeSize;
			return ((c += h - m), false);
		}),
		r.size > 0)
	) {
		const u = [...r].join(`
`);
		throw Error("Blocks with the following IDs could not be found in the editor: " + u);
	}
	return (l.forEach((u) => H(o, u)), { insertedBlocks: s.map((u) => L$2(u, n)), removedBlocks: i });
}
function to(o, e, t, n, s) {
	let r;
	if (e)
		if (typeof e == "string") r = T$1([e], o.pmSchema, n);
		else if (Array.isArray(e)) r = T$1(e, o.pmSchema, n);
		else if (e.type === "tableContent") r = kt$1(e, o.pmSchema);
		else throw new O(e.type);
	else throw new Error("blockContent is required");
	const l = ((s == null ? void 0 : s.document) ?? document).createDocumentFragment();
	for (const a of r)
		if (a.type.name !== "text" && o.schema.inlineContentSchema[a.type.name]) {
			const c = o.schema.inlineContentSpecs[a.type.name].implementation;
			if (c) {
				const d = wt$1(a, o.schema.inlineContentSchema, o.schema.styleSchema),
					u = c.render.call(
						{
							renderType: "dom",
							props: void 0,
						},
						d,
						() => {},
						o,
					);
				if (u) {
					if ((l.appendChild(u.dom), u.contentDOM)) {
						const p = t.serializeFragment(a.content, s);
						((u.contentDOM.dataset.editable = ""), u.contentDOM.appendChild(p));
					}
					continue;
				}
			}
		} else if (a.type.name === "text") {
			let c = document.createTextNode(a.textContent);
			for (const d of a.marks.toReversed())
				if (d.type.name in o.schema.styleSpecs) {
					const u = o.schema.styleSpecs[d.type.name].implementation.render(d.attrs.stringValue, o);
					(u.contentDOM.appendChild(c), (c = u.dom));
				} else {
					const u = d.type.spec.toDOM(d, true),
						p = DOMSerializer.renderSpec(document, u);
					(p.contentDOM.appendChild(c), (c = p.dom));
				}
			l.appendChild(c);
		} else {
			const c = t.serializeFragment(Fragment.from([a]), s);
			l.appendChild(c);
		}
	return l;
}
function oo(o, e, t, n) {
	var u, p, h, f, m;
	const s = o.pmSchema.nodes.blockContainer,
		r = e.props || {};
	for (const [b, g] of Object.entries(o.schema.blockSchema[e.type].propSchema)) !(b in r) && g.default !== void 0 && (r[b] = g.default);
	const i = e.children || [],
		a = o.blockImplementations[e.type].implementation.render.call(
			{
				renderType: "dom",
				props: void 0,
			},
			{ ...e, props: r, children: i },
			o,
		);
	if (a.contentDOM && e.content) {
		const b = to(
			o,
			e.content,
			// TODO
			t,
			e.type,
			n,
		);
		a.contentDOM.appendChild(b);
	}
	if (o.pmSchema.nodes[e.type].isInGroup("bnBlock")) {
		if (e.children && e.children.length > 0) {
			const b = Se(o, e.children, t, n);
			(u = a.contentDOM) == null || u.append(b);
		}
		return a.dom;
	}
	const d =
		(h = (p = s.spec) == null ? void 0 : p.toDOM) == null ?
			void 0
		:	h.call(
				p,
				s.create({
					id: e.id,
					...r,
				}),
			);
	return ((f = d.contentDOM) == null || f.appendChild(a.dom), e.children && e.children.length > 0 && ((m = d.contentDOM) == null || m.appendChild(xe(o, e.children, t, n))), d.dom);
}
function Se(o, e, t, n) {
	const r = ((n == null ? void 0 : n.document) ?? document).createDocumentFragment();
	for (const i of e) {
		const l = oo(o, i, t, n);
		r.appendChild(l);
	}
	return r;
}
const xe = (o, e, t, n) => {
		var l;
		const s = o.pmSchema.nodes.blockGroup,
			r = s.spec.toDOM(s.create({})),
			i = Se(o, e, t, n);
		return ((l = r.contentDOM) == null || l.appendChild(i), r.dom);
	},
	no = (o) => (
		o.querySelectorAll('[data-content-type="numberedListItem"]').forEach((t) => {
			var s, r;
			const n = (r = (s = t.closest(".bn-block-outer")) == null ? void 0 : s.previousElementSibling) == null ? void 0 : r.querySelector('[data-content-type="numberedListItem"]');
			if (!n) t.setAttribute("data-index", t.getAttribute("data-start") || "1");
			else {
				const i = n.getAttribute("data-index");
				t.setAttribute("data-index", (parseInt(i || "0") + 1).toString());
			}
		}),
		o
	),
	so = (o) => (
		o.querySelectorAll('[data-content-type="checkListItem"] input').forEach((t) => {
			t.disabled = true;
		}),
		o
	),
	ro = (o) => (
		o.querySelectorAll('.bn-toggle-wrapper[data-show-children="false"]').forEach((t) => {
			t.setAttribute("data-show-children", "true");
		}),
		o
	),
	io = (o) => (
		o.querySelectorAll('[data-content-type="table"] table').forEach((t) => {
			(t.setAttribute("style", `--default-cell-min-width: ${Oe$2}px;`), t.setAttribute("data-show-children", "true"));
		}),
		o
	),
	ao = (o) => (
		o.querySelectorAll('[data-content-type="table"] table').forEach((t) => {
			var r;
			const n = document.createElement("div");
			n.className = "tableWrapper";
			const s = document.createElement("div");
			((s.className = "tableWrapper-inner"), n.appendChild(s), (r = t.parentElement) == null || r.appendChild(n), n.appendChild(t));
		}),
		o
	),
	co = (o) => (
		o.querySelectorAll(".bn-inline-content:empty").forEach((t) => {
			const n = document.createElement("span");
			((n.className = "ProseMirror-trailingBreak"), n.setAttribute("style", "display: inline-block;"), t.appendChild(n));
		}),
		o
	),
	lo = (o, e) => {
		const t = DOMSerializer.fromSchema(o),
			n = [no, so, ro, io, ao, co];
		return {
			serializeBlocks: (s, r) => {
				let i = xe(e, s, t, r);
				for (const l of n) i = l(i);
				return i.outerHTML;
			},
		};
	};
function uo(o) {
	return o.transact((e) => {
		const t = Y$1(e.doc, e.selection.anchor);
		if (e.selection instanceof CellSelection)
			return {
				type: "cell",
				anchorBlockId: t.node.attrs.id,
				anchorCellOffset: e.selection.$anchorCell.pos - t.posBeforeNode,
				headCellOffset: e.selection.$headCell.pos - t.posBeforeNode,
			};
		if (e.selection instanceof NodeSelection)
			return {
				type: "node",
				anchorBlockId: t.node.attrs.id,
			};
		{
			const n = Y$1(e.doc, e.selection.head);
			return {
				type: "text",
				anchorBlockId: t.node.attrs.id,
				headBlockId: n.node.attrs.id,
				anchorOffset: e.selection.anchor - t.posBeforeNode,
				headOffset: e.selection.head - n.posBeforeNode,
			};
		}
	});
}
function po(o, e) {
	var s, r;
	const t = (s = Bt$1(e.anchorBlockId, o.doc)) == null ? void 0 : s.posBeforeNode;
	if (t === void 0) throw new Error(`Could not find block with ID ${e.anchorBlockId} to update selection`);
	let n;
	if (e.type === "cell") n = CellSelection.create(o.doc, t + e.anchorCellOffset, t + e.headCellOffset);
	else if (e.type === "node") n = NodeSelection.create(o.doc, t + 1);
	else {
		const i = (r = Bt$1(e.headBlockId, o.doc)) == null ? void 0 : r.posBeforeNode;
		if (i === void 0) throw new Error(`Could not find block with ID ${e.headBlockId} to update selection`);
		n = TextSelection.create(o.doc, t + e.anchorOffset, i + e.headOffset);
	}
	o.setSelection(n);
}
function X(o) {
	return o
		.map((e) =>
			e.type === "columnList" ?
				e.children.map((t) => X(t.children)).flat()
			:	{
					...e,
					children: X(e.children),
				},
		)
		.flat();
}
function Ee(o, e, t) {
	o.transact((n) => {
		var i;
		const s = ((i = o.getSelection()) == null ? void 0 : i.blocks) || [o.getTextCursorPosition().block],
			r = uo(o);
		(o.removeBlocks(s), o.insertBlocks(X(s), e, t), po(n, r));
	});
}
function Pe(o) {
	return !o || o.type !== "columnList";
}
function Te(o, e, t) {
	let n, s;
	if (
		(e ?
			e.children.length > 0 ?
				((n = e.children[e.children.length - 1]), (s = "after"))
			:	((n = e), (s = "before"))
		:	t && ((n = t), (s = "before")),
		!n || !s)
	)
		return;
	const r = o.getParentBlock(n);
	return Pe(r) ? { referenceBlock: n, placement: s } : Te(o, s === "after" ? n : o.getPrevBlock(n), r);
}
function we(o, e, t) {
	let n, s;
	if (
		(e ?
			e.children.length > 0 ?
				((n = e.children[0]), (s = "before"))
			:	((n = e), (s = "after"))
		:	t && ((n = t), (s = "after")),
		!n || !s)
	)
		return;
	const r = o.getParentBlock(n);
	return Pe(r) ? { referenceBlock: n, placement: s } : we(o, s === "before" ? n : o.getNextBlock(n), r);
}
function fo(o) {
	o.transact(() => {
		const e = o.getSelection(),
			t = (e == null ? void 0 : e.blocks[0]) || o.getTextCursorPosition().block,
			n = Te(o, o.getPrevBlock(t), o.getParentBlock(t));
		n && Ee(o, n.referenceBlock, n.placement);
	});
}
function ho(o) {
	o.transact(() => {
		const e = o.getSelection(),
			t = (e == null ? void 0 : e.blocks[(e == null ? void 0 : e.blocks.length) - 1]) || o.getTextCursorPosition().block,
			n = we(o, o.getNextBlock(t), o.getParentBlock(t));
		n && Ee(o, n.referenceBlock, n.placement);
	});
}
function mo(o, e, t) {
	const { $from: n, $to: s } = o.selection,
		r = n.blockRange(
			s,
			(f) => f.childCount > 0 && (f.type.name === "blockGroup" || f.type.name === "column"),
			// change necessary to not look at first item child type
		);
	if (!r) return false;
	const i = r.startIndex;
	if (i === 0) return false;
	const a = r.parent.child(i - 1);
	if (a.type !== e) return false;
	const c = a.lastChild && a.lastChild.type === t,
		d = Fragment.from(c ? e.create() : null),
		u = new Slice(
			Fragment.from(
				e.create(null, Fragment.from(t.create(null, d))),
				// change necessary to create "groupType" instead of parent.type
			),
			c ? 3 : 1,
			0,
		),
		p = r.start,
		h = r.end;
	return (o.step(new ReplaceAroundStep(p - (c ? 3 : 1), h, p, h, u, 1, true)).scrollIntoView(), true);
}
function Me(o) {
	return o.transact((e) => mo(e, o.pmSchema.nodes.blockContainer, o.pmSchema.nodes.blockGroup));
}
function ko(o) {
	o._tiptapEditor.commands.liftListItem("blockContainer");
}
function bo(o) {
	return o.transact((e) => {
		const { bnBlock: t } = Ot$2(e);
		return e.doc.resolve(t.beforePos).nodeBefore !== null;
	});
}
function go(o) {
	return o.transact((e) => {
		const { bnBlock: t } = Ot$2(e);
		return e.doc.resolve(t.beforePos).depth > 1;
	});
}
function Bo(o, e) {
	const t = typeof e == "string" ? e : e.id,
		n = ht(o),
		s = Bt$1(t, o);
	if (s) return L$2(s.node, n);
}
function yo(o, e) {
	const t = typeof e == "string" ? e : e.id,
		n = Bt$1(t, o),
		s = ht(o);
	if (!n) return;
	const i = o.resolve(n.posBeforeNode).nodeBefore;
	if (i) return L$2(i, s);
}
function Co(o, e) {
	const t = typeof e == "string" ? e : e.id,
		n = Bt$1(t, o),
		s = ht(o);
	if (!n) return;
	const i = o.resolve(n.posBeforeNode + n.node.nodeSize).nodeAfter;
	if (i) return L$2(i, s);
}
function So(o, e) {
	const t = typeof e == "string" ? e : e.id,
		n = ht(o),
		s = Bt$1(t, o);
	if (!s) return;
	const r = o.resolve(s.posBeforeNode),
		i = r.node(),
		l = r.node(-1),
		a =
			l.type.name !== "doc" ?
				i.type.name === "blockGroup" ?
					l
				:	i
			:	void 0;
	if (a) return L$2(a, n);
}
class xo {
	constructor(e) {
		this.editor = e;
	}
	/**
	 * Gets a snapshot of all top-level (non-nested) blocks in the editor.
	 * @returns A snapshot of all top-level (non-nested) blocks in the editor.
	 */
	get document() {
		return this.editor.transact((e) => St$1(e.doc, this.editor.pmSchema));
	}
	/**
	 * Gets a snapshot of an existing block from the editor.
	 * @param blockIdentifier The identifier of an existing block that should be
	 * retrieved.
	 * @returns The block that matches the identifier, or `undefined` if no
	 * matching block was found.
	 */
	getBlock(e) {
		return this.editor.transact((t) => Bo(t.doc, e));
	}
	/**
	 * Gets a snapshot of the previous sibling of an existing block from the
	 * editor.
	 * @param blockIdentifier The identifier of an existing block for which the
	 * previous sibling should be retrieved.
	 * @returns The previous sibling of the block that matches the identifier.
	 * `undefined` if no matching block was found, or it's the first child/block
	 * in the document.
	 */
	getPrevBlock(e) {
		return this.editor.transact((t) => yo(t.doc, e));
	}
	/**
	 * Gets a snapshot of the next sibling of an existing block from the editor.
	 * @param blockIdentifier The identifier of an existing block for which the
	 * next sibling should be retrieved.
	 * @returns The next sibling of the block that matches the identifier.
	 * `undefined` if no matching block was found, or it's the last child/block in
	 * the document.
	 */
	getNextBlock(e) {
		return this.editor.transact((t) => Co(t.doc, e));
	}
	/**
	 * Gets a snapshot of the parent of an existing block from the editor.
	 * @param blockIdentifier The identifier of an existing block for which the
	 * parent should be retrieved.
	 * @returns The parent of the block that matches the identifier. `undefined`
	 * if no matching block was found, or the block isn't nested.
	 */
	getParentBlock(e) {
		return this.editor.transact((t) => So(t.doc, e));
	}
	/**
	 * Traverses all blocks in the editor depth-first, and executes a callback for each.
	 * @param callback The callback to execute for each block. Returning `false` stops the traversal.
	 * @param reverse Whether the blocks should be traversed in reverse order.
	 */
	forEachBlock(e, t = false) {
		const n = this.document.slice();
		t && n.reverse();
		function s(r) {
			for (const i of r) {
				if (e(i) === false) return false;
				const l = t ? i.children.slice().reverse() : i.children;
				if (!s(l)) return false;
			}
			return true;
		}
		s(n);
	}
	/**
	 * Inserts new blocks into the editor. If a block's `id` is undefined, BlockNote generates one automatically. Throws an
	 * error if the reference block could not be found.
	 * @param blocksToInsert An array of partial blocks that should be inserted.
	 * @param referenceBlock An identifier for an existing block, at which the new blocks should be inserted.
	 * @param placement Whether the blocks should be inserted just before, just after, or nested inside the
	 * `referenceBlock`.
	 */
	insertBlocks(e, t, n = "before") {
		return this.editor.transact((s) => Zt(s, e, t, n));
	}
	/**
	 * Updates an existing block in the editor. Since updatedBlock is a PartialBlock object, some fields might not be
	 * defined. These undefined fields are kept as-is from the existing block. Throws an error if the block to update could
	 * not be found.
	 * @param blockToUpdate The block that should be updated.
	 * @param update A partial block which defines how the existing block should be changed.
	 */
	updateBlock(e, t) {
		return this.editor.transact((n) => vo$1(n, e, t));
	}
	/**
	 * Removes existing blocks from the editor. Throws an error if any of the blocks could not be found.
	 * @param blocksToRemove An array of identifiers for existing blocks that should be removed.
	 */
	removeBlocks(e) {
		return this.editor.transact((t) => ae(t, e, []).removedBlocks);
	}
	/**
	 * Replaces existing blocks in the editor with new blocks. If the blocks that should be removed are not adjacent or
	 * are at different nesting levels, `blocksToInsert` will be inserted at the position of the first block in
	 * `blocksToRemove`. Throws an error if any of the blocks to remove could not be found.
	 * @param blocksToRemove An array of blocks that should be replaced.
	 * @param blocksToInsert An array of partial blocks to replace the old ones with.
	 */
	replaceBlocks(e, t) {
		return this.editor.transact((n) => ae(n, e, t));
	}
	/**
	 * Checks if the block containing the text cursor can be nested.
	 */
	canNestBlock() {
		return bo(this.editor);
	}
	/**
	 * Nests the block containing the text cursor into the block above it.
	 */
	nestBlock() {
		Me(this.editor);
	}
	/**
	 * Checks if the block containing the text cursor is nested.
	 */
	canUnnestBlock() {
		return go(this.editor);
	}
	/**
	 * Lifts the block containing the text cursor out of its parent.
	 */
	unnestBlock() {
		ko(this.editor);
	}
	/**
	 * Moves the selected blocks up. If the previous block has children, moves
	 * them to the end of its children. If there is no previous block, but the
	 * current blocks share a common parent, moves them out of & before it.
	 */
	moveBlocksUp() {
		return fo(this.editor);
	}
	/**
	 * Moves the selected blocks down. If the next block has children, moves
	 * them to the start of its children. If there is no next block, but the
	 * current blocks share a common parent, moves them out of & after it.
	 */
	moveBlocksDown() {
		return ho(this.editor);
	}
}
class Eo extends f {
	constructor(e) {
		(super(),
			(this.editor = e),
			e.on("create", () => {
				(e._tiptapEditor.on("update", ({ transaction: t, appendedTransactions: n }) => {
					this.emit("onChange", { editor: e, transaction: t, appendedTransactions: n });
				}),
					e._tiptapEditor.on("selectionUpdate", ({ transaction: t }) => {
						this.emit("onSelectionChange", { editor: e, transaction: t });
					}),
					e._tiptapEditor.on("mount", () => {
						this.emit("onMount", { editor: e });
					}),
					e._tiptapEditor.on("unmount", () => {
						this.emit("onUnmount", { editor: e });
					}));
			}));
	}
	/**
	 * Register a callback that will be called when the editor changes.
	 */
	onChange(e, t = true) {
		const n = ({ transaction: s, appendedTransactions: r }) => {
			(!t && ce(s)) ||
				e(this.editor, {
					getChanges() {
						return Mt(s, r);
					},
				});
		};
		return (
			this.on("onChange", n),
			() => {
				this.off("onChange", n);
			}
		);
	}
	/**
	 * Register a callback that will be called when the selection changes.
	 */
	onSelectionChange(e, t = false) {
		const n = (s) => {
			(!t && ce(s.transaction)) || e(this.editor);
		};
		return (
			this.on("onSelectionChange", n),
			() => {
				this.off("onSelectionChange", n);
			}
		);
	}
	/**
	 * Register a callback that will be called when the editor is mounted.
	 */
	onMount(e) {
		return (
			this.on("onMount", e),
			() => {
				this.off("onMount", e);
			}
		);
	}
	/**
	 * Register a callback that will be called when the editor is unmounted.
	 */
	onUnmount(e) {
		return (
			this.on("onUnmount", e),
			() => {
				this.off("onUnmount", e);
			}
		);
	}
}
function ce(o) {
	return !!o.getMeta("y-sync$");
}
function Po(o) {
	return Array.prototype.indexOf.call(o.parentElement.childNodes, o);
}
function To(o) {
	return o.nodeType === 3 && !/\S/.test(o.nodeValue || "");
}
function wo(o) {
	o.querySelectorAll("li > ul, li > ol").forEach((e) => {
		const t = Po(e),
			n = e.parentElement,
			s = Array.from(n.childNodes).slice(t + 1);
		(e.remove(),
			s.forEach((r) => {
				r.remove();
			}),
			n.insertAdjacentElement("afterend", e),
			s.reverse().forEach((r) => {
				if (To(r)) return;
				const i = document.createElement("li");
				(i.append(r), e.insertAdjacentElement("afterend", i));
			}),
			n.childNodes.length === 0 && n.remove());
	});
}
function Mo(o) {
	o.querySelectorAll("li + ul, li + ol").forEach((e) => {
		var r, i;
		const t = e.previousElementSibling,
			n = document.createElement("div");
		(t.insertAdjacentElement("afterend", n), n.append(t));
		const s = document.createElement("div");
		for (
			s.setAttribute("data-node-type", "blockGroup"), n.append(s);
			((r = n.nextElementSibling) == null ? void 0 : r.nodeName) === "UL" || ((i = n.nextElementSibling) == null ? void 0 : i.nodeName) === "OL";
		)
			s.append(n.nextElementSibling);
	});
}
let le = null;
function vo() {
	return le || (le = document.implementation.createHTMLDocument("title"));
}
function Io(o) {
	if (typeof o == "string") {
		const e = vo().createElement("div");
		((e.innerHTML = o), (o = e));
	}
	return (wo(o), Mo(o), o);
}
function Ao(o) {
	const e = o.ownerDocument.createTreeWalker(
		o,
		// NodeFilter.SHOW_COMMENT
		128,
	);
	let t;
	for (; (t = e.nextNode()); ) if (/^\s*notionvc:/.test(t.nodeValue || "")) return true;
	return false;
}
function No(o) {
	const e = /* @__PURE__ */ new Set(["PRE", "CODE"]),
		t = o.ownerDocument.createTreeWalker(
			o,
			// NodeFilter.SHOW_TEXT
			4,
			{
				acceptNode(r) {
					let i = r.parentElement;
					for (; i && i !== o; ) {
						if (e.has(i.tagName)) return 2;
						i = i.parentElement;
					}
					return 1;
				},
			},
		),
		n = [];
	let s;
	for (; (s = t.nextNode()); ) n.push(s);
	for (const r of n) r.nodeValue && /[\r\n]/.test(r.nodeValue) && (r.nodeValue = r.nodeValue.replace(/[ \t\r\n\f]+/g, " "));
}
function Lo(o) {
	Ao(o) || No(o);
}
function ve(o, e) {
	const t = Io(o);
	Lo(t);
	const s = DOMParser$1.fromSchema(e).parse(t, {
			topNode: e.nodes.blockGroup.create(),
		}),
		r = [];
	for (let i = 0; i < s.childCount; i++) r.push(L$2(s.child(i), e));
	return r;
}
function _o(o, e) {
	const t = e.value ? e.value : "",
		n = {};
	e.lang && (n["data-language"] = e.lang);
	let s = {
		type: "element",
		tagName: "code",
		properties: n,
		children: [{ type: "text", value: t }],
	};
	return (
		e.meta && (s.data = { meta: e.meta }),
		o.patch(e, s),
		(s = o.applyData(e, s)),
		(s = {
			type: "element",
			tagName: "pre",
			properties: {},
			children: [s],
		}),
		o.patch(e, s),
		s
	);
}
function Do(o, e) {
	var r;
	const t = String((e == null ? void 0 : e.url) || ""),
		n = e != null && e.title ? String(e.title) : void 0;
	let s = {
		type: "element",
		tagName: "video",
		properties: {
			"src": t,
			"data-name": n,
			"data-url": t,
			"controls": true,
		},
		children: [],
	};
	return ((r = o.patch) == null || r.call(o, e, s), (s = o.applyData ? o.applyData(e, s) : s), s);
}
function Ie(o) {
	return unified()
		.use(remarkParse)
		.use(remarkGfm)
		.use(remarkRehype, {
			handlers: {
				...handlers,
				image: (t, n) => {
					const s = String((n == null ? void 0 : n.url) || "");
					return mo$1(s) ? Do(t, n) : handlers.image(t, n);
				},
				code: _o,
				blockquote: (t, n) => {
					const s = {
						type: "element",
						tagName: "blockquote",
						properties: {},
						// The only difference from the original is that we don't wrap the children with line endings
						children: t.wrap(t.all(n), false),
					};
					return (t.patch(n, s), t.applyData(n, s));
				},
			},
		})
		.use(rehypeStringify)
		.processSync(o).value;
}
function Oo(o, e) {
	const t = Ie(o);
	return ve(t, e);
}
class $o {
	constructor(e) {
		this.editor = e;
	}
	/**
	 * Exports blocks into a simplified HTML string. To better conform to HTML standards, children of blocks which aren't list
	 * items are un-nested in the output HTML.
	 *
	 * @param blocks An array of blocks that should be serialized into HTML.
	 * @returns The blocks, serialized as an HTML string.
	 */
	blocksToHTMLLossy(e = this.editor.document) {
		return Be(this.editor.pmSchema, this.editor).exportBlocks(e, {});
	}
	/**
	 * Serializes blocks into an HTML string in the format that would normally be rendered by the editor.
	 *
	 * Use this method if you want to server-side render HTML (for example, a blog post that has been edited in BlockNote)
	 * and serve it to users without loading the editor on the client (i.e.: displaying the blog post)
	 *
	 * @param blocks An array of blocks that should be serialized into HTML.
	 * @returns The blocks, serialized as an HTML string.
	 */
	blocksToFullHTML(e = this.editor.document) {
		return lo(this.editor.pmSchema, this.editor).serializeBlocks(e, {});
	}
	/**
	 * Parses blocks from an HTML string. Tries to create `Block` objects out of any HTML block-level elements, and
	 * `InlineNode` objects from any HTML inline elements, though not all element types are recognized. If BlockNote
	 * doesn't recognize an HTML element's tag, it will parse it as a paragraph or plain text.
	 * @param html The HTML string to parse blocks from.
	 * @returns The blocks parsed from the HTML string.
	 */
	tryParseHTMLToBlocks(e) {
		return ve(e, this.editor.pmSchema);
	}
	/**
	 * Serializes blocks into a Markdown string. The output is simplified as Markdown does not support all features of
	 * BlockNote - children of blocks which aren't list items are un-nested and certain styles are removed.
	 * @param blocks An array of blocks that should be serialized into Markdown.
	 * @returns The blocks, serialized as a Markdown string.
	 */
	blocksToMarkdownLossy(e = this.editor.document) {
		return Uo$1(e, this.editor.pmSchema, this.editor, {});
	}
	/**
	 * Creates a list of blocks from a Markdown string. Tries to create `Block` and `InlineNode` objects based on
	 * Markdown syntax, though not all symbols are recognized. If BlockNote doesn't recognize a symbol, it will parse it
	 * as text.
	 * @param markdown The Markdown string to parse blocks from.
	 * @returns The blocks parsed from the Markdown string.
	 */
	tryParseMarkdownToBlocks(e) {
		return Oo(e, this.editor.pmSchema);
	}
	/**
	 * Paste HTML into the editor. Defaults to converting HTML to BlockNote HTML.
	 * @param html The HTML to paste.
	 * @param raw Whether to paste the HTML as is, or to convert it to BlockNote HTML.
	 */
	pasteHTML(e, t = false) {
		var s;
		let n = e;
		if (!t) {
			const r = this.tryParseHTMLToBlocks(e);
			n = this.blocksToFullHTML(r);
		}
		n && ((s = this.editor.prosemirrorView) == null || s.pasteHTML(n));
	}
	/**
	 * Paste text into the editor. Defaults to interpreting text as markdown.
	 * @param text The text to paste.
	 */
	pasteText(e) {
		var t;
		return (t = this.editor.prosemirrorView) == null ? void 0 : t.pasteText(e);
	}
	/**
	 * Paste markdown into the editor.
	 * @param markdown The markdown to paste.
	 */
	pasteMarkdown(e) {
		const t = Ie(e);
		return this.pasteHTML(t);
	}
}
const se = ["vscode-editor-data", "blocknote/html", "text/markdown", "text/html", "text/plain", "Files"];
function Ho(o, e) {
	if (!o.startsWith(".") || !e.startsWith(".")) throw new Error("The strings provided are not valid file extensions.");
	return o === e;
}
function Fo(o, e) {
	const t = o.split("/"),
		n = e.split("/");
	if (t.length !== 2) throw new Error(`The string ${o} is not a valid MIME type.`);
	if (n.length !== 2) throw new Error(`The string ${e} is not a valid MIME type.`);
	return t[1] === "*" || n[1] === "*" ? t[0] === n[0] : (t[0] === "*" || n[0] === "*" || t[0] === n[0]) && t[1] === n[1];
}
function de(o, e, t, n = "after") {
	let s;
	return (Array.isArray(e.content) && e.content.length === 0 ? (s = o.updateBlock(e, t).id) : (s = o.insertBlocks([t], e, n)[0].id), s);
}
async function Ae(o, e) {
	var r;
	if (!e.uploadFile) {
		console.warn("Attempted ot insert file, but uploadFile is not set in the BlockNote editor options");
		return;
	}
	const t = "dataTransfer" in o ? o.dataTransfer : o.clipboardData;
	if (t === null) return;
	let n = null;
	for (const i of se)
		if (t.types.includes(i)) {
			n = i;
			break;
		}
	if (n !== "Files") return;
	const s = t.items;
	if (s) {
		o.preventDefault();
		for (let i = 0; i < s.length; i++) {
			let l = "file";
			for (const c of Object.values(e.schema.blockSpecs))
				for (const d of ((r = c.implementation.meta) == null ? void 0 : r.fileBlockAccept) || []) {
					const u = d.startsWith("."),
						p = s[i].getAsFile();
					if (p && ((!u && p.type && Fo(s[i].type, d)) || (u && Ho("." + p.name.split(".").pop(), d)))) {
						l = c.config.type;
						break;
					}
				}
			const a = s[i].getAsFile();
			if (a) {
				const c = {
					type: l,
					props: {
						name: a.name,
					},
				};
				let d;
				if (o.type === "paste") {
					const h = e.getTextCursorPosition().block;
					d = de(e, h, c);
				} else if (o.type === "drop") {
					const h = {
							left: o.clientX,
							top: o.clientY,
						},
						f = e.prosemirrorView.posAtCoords(h);
					if (!f) return;
					d = e.transact((m) => {
						var y;
						const b = Y$1(m.doc, f.pos),
							g = (y = e.domElement) == null ? void 0 : y.querySelector(`[data-id="${b.node.attrs.id}"]`),
							x = g == null ? void 0 : g.getBoundingClientRect();
						return de(e, e.getBlock(b.node.attrs.id), c, x && (x.top + x.bottom) / 2 > h.top ? "before" : "after");
					});
				} else return;
				const u = await e.uploadFile(a, d),
					p =
						typeof u == "string" ?
							{
								props: {
									url: u,
								},
							}
						:	{ ...u };
				e.updateBlock(d, p);
			}
		}
	}
}
const Vo = (o) =>
		Extension.create({
			name: "dropFile",
			addProseMirrorPlugins() {
				return [
					new Plugin({
						props: {
							handleDOMEvents: {
								drop(e, t) {
									if (!o.isEditable) return;
									let n = null;
									for (const s of se)
										if (t.dataTransfer.types.includes(s)) {
											n = s;
											break;
										}
									return (
										n === null ? true
										: n === "Files" ? (Ae(t, o), true)
										: false
									);
								},
							},
						},
					}),
				];
			},
		}),
	Uo = /(^|\n) {0,3}#{1,6} {1,8}[^\n]{1,64}\r?\n\r?\n\s{0,32}\S/,
	zo = /(_|__|\*|\*\*|~~|==|\+\+)(?!\s)(?:[^\s](?:.{0,62}[^\s])?|\S)(?=\1)/,
	Ro = /\[[^\]]{1,128}\]\(https?:\/\/\S{1,999}\)/,
	Go = /(?:\s|^)`(?!\s)(?:[^\s`](?:[^`]{0,46}[^\s`])?|[^\s`])`([^\w]|$)/,
	Wo = /(?:^|\n)\s{0,5}-\s{1}[^\n]+\n\s{0,15}-\s/,
	jo = /(?:^|\n)\s{0,5}\d+\.\s{1}[^\n]+\n\s{0,15}\d+\.\s/,
	qo = /\n{2} {0,3}-{2,48}\n{2}/,
	Ko = /(?:\n|^)(```|~~~|\$\$)(?!`|~)[^\s]{0,64} {0,64}[^\n]{0,64}\n[\s\S]{0,9999}?\s*\1 {0,64}(?:\n+|$)/,
	Jo = /(?:\n|^)(?!\s)\w[^\n]{0,64}\r?\n(-|=)\1{0,64}\n\n\s{0,64}(\w|$)/,
	Yo = /(?:^|(\r?\n\r?\n))( {0,3}>[^\n]{1,333}\n){1,999}($|(\r?\n))/,
	Qo = /^\s*\|(.+\|)+\s*$/m,
	Xo = /^\s*\|(\s*[-:]+[-:]\s*\|)+\s*$/m,
	Zo = /^\s*\|(.+\|)+\s*$/m,
	en = (o) => Uo.test(o) || zo.test(o) || Ro.test(o) || Go.test(o) || Wo.test(o) || jo.test(o) || qo.test(o) || Ko.test(o) || Jo.test(o) || Yo.test(o) || Qo.test(o) || Xo.test(o) || Zo.test(o);
async function tn(o, e) {
	const { schema: t } = e.state;
	if (!o.clipboardData) return false;
	const n = o.clipboardData.getData("text/plain");
	if (!n) return false;
	if (!t.nodes.codeBlock) return (e.pasteText(n), true);
	const s = o.clipboardData.getData("vscode-editor-data"),
		r = s ? JSON.parse(s) : void 0,
		i = r == null ? void 0 : r.mode;
	return i ?
			(e.pasteHTML(
				`<pre><code class="language-${i}">${n.replace(
					/\r\n?/g,
					`
`,
				)}</code></pre>`,
			),
			true)
		:	false;
}
function on({ event: o, editor: e, prioritizeMarkdownOverHTML: t, plainTextAsMarkdown: n }) {
	var l;
	if (e.transact((a) => a.selection.$from.parent.type.spec.code && a.selection.$to.parent.type.spec.code)) {
		const a = (l = o.clipboardData) == null ? void 0 : l.getData("text/plain");
		if (a) return (e.pasteText(a), true);
	}
	let r;
	for (const a of se)
		if (o.clipboardData.types.includes(a)) {
			r = a;
			break;
		}
	if (!r) return true;
	if (r === "vscode-editor-data") return (tn(o, e.prosemirrorView), true);
	if (r === "Files") return (Ae(o, e), true);
	const i = o.clipboardData.getData(r);
	if (r === "blocknote/html") return (e.pasteHTML(i, true), true);
	if (r === "text/markdown") return (e.pasteMarkdown(i), true);
	if (t) {
		const a = o.clipboardData.getData("text/plain");
		if (en(a)) return (e.pasteMarkdown(a), true);
	}
	return (
		r === "text/html" ? (e.pasteHTML(i), true)
		: n ? (e.pasteMarkdown(i), true)
		: (e.pasteText(i), true)
	);
}
const nn = (o, e) =>
	Extension.create({
		name: "pasteFromClipboard",
		addProseMirrorPlugins() {
			return [
				new Plugin({
					props: {
						handleDOMEvents: {
							paste(t, n) {
								if ((n.preventDefault(), !!o.isEditable))
									return e({
										event: n,
										editor: o,
										defaultPasteHandler: ({ prioritizeMarkdownOverHTML: s = true, plainTextAsMarkdown: r = true } = {}) =>
											on({
												event: n,
												editor: o,
												prioritizeMarkdownOverHTML: s,
												plainTextAsMarkdown: r,
											}),
									});
							},
						},
					},
				}),
			];
		},
	});
function sn(o, e, t) {
	var l;
	let n = false;
	const s = o.state.selection instanceof CellSelection;
	if (!s) {
		const a = o.state.doc.slice(o.state.selection.from, o.state.selection.to, false).content,
			c = [];
		for (let d = 0; d < a.childCount; d++) c.push(a.child(d));
		((n = c.find((d) => d.type.isInGroup("bnBlock") || d.type.name === "blockGroup" || d.type.spec.group === "blockContent") === void 0), n && (e = a));
	}
	let r;
	const i = Be(o.state.schema, t);
	if (s) {
		((l = e.firstChild) == null ? void 0 : l.type.name) === "table" && (e = e.firstChild.content);
		const a = gt(e, t.schema.inlineContentSchema, t.schema.styleSchema);
		r = `<table>${i.exportInlineContent(a, {})}</table>`;
	} else if (n) {
		const a = F$2(e, t.schema.inlineContentSchema, t.schema.styleSchema);
		r = i.exportInlineContent(a, {});
	} else {
		const a = Wt(e);
		r = i.exportBlocks(a, {});
	}
	return r;
}
function Ne(o, e) {
	"node" in o.state.selection && o.state.selection.node.type.spec.group === "blockContent" && e.transact((i) => i.setSelection(new NodeSelection(i.doc.resolve(o.state.selection.from - 1))));
	const t = o.serializeForClipboard(o.state.selection.content()).dom.innerHTML,
		n = o.state.selection.content().content,
		s = sn(o, n, e),
		r = Oe$1(s);
	return { clipboardHTML: t, externalHTML: s, markdown: r };
}
const ue = () => {
		const o = window.getSelection();
		if (!o || o.isCollapsed) return true;
		let e = o.focusNode;
		for (; e; ) {
			if (e instanceof HTMLElement && e.getAttribute("contenteditable") === "false") return true;
			e = e.parentElement;
		}
		return false;
	},
	pe = (o, e, t) => {
		(t.preventDefault(), t.clipboardData.clearData());
		const { clipboardHTML: n, externalHTML: s, markdown: r } = Ne(e, o);
		(t.clipboardData.setData("blocknote/html", n), t.clipboardData.setData("text/html", s), t.clipboardData.setData("text/plain", r));
	},
	rn = (o) =>
		Extension.create({
			name: "copyToClipboard",
			addProseMirrorPlugins() {
				return [
					new Plugin({
						props: {
							handleDOMEvents: {
								copy(e, t) {
									return (ue() || pe(o, e, t), true);
								},
								cut(e, t) {
									return (ue() || (pe(o, e, t), e.editable && e.dispatch(e.state.tr.deleteSelection())), true);
								},
								// This is for the use-case in which only a block without content
								// is selected, e.g. an image block, and dragged (not using the
								// drag handle).
								dragstart(e, t) {
									if (!("node" in e.state.selection) || e.state.selection.node.type.spec.group !== "blockContent") return;
									(o.transact((i) => i.setSelection(new NodeSelection(i.doc.resolve(e.state.selection.from - 1)))), t.preventDefault(), t.dataTransfer.clearData());
									const { clipboardHTML: n, externalHTML: s, markdown: r } = Ne(e, o);
									return (t.dataTransfer.setData("blocknote/html", n), t.dataTransfer.setData("text/html", s), t.dataTransfer.setData("text/plain", r), true);
								},
							},
						},
					}),
				];
			},
		}),
	an = Extension.create({
		name: "blockBackgroundColor",
		addGlobalAttributes() {
			return [
				{
					types: ["tableCell", "tableHeader"],
					attributes: {
						backgroundColor: So$1(),
					},
				},
			];
		},
	}),
	cn = Node3.create({
		name: "hardBreak",
		inline: true,
		group: "inline",
		selectable: false,
		linebreakReplacement: true,
		priority: 10,
		parseHTML() {
			return [{ tag: "br" }];
		},
		renderHTML({ HTMLAttributes: o }) {
			return ["br", mergeAttributes(this.options.HTMLAttributes, o)];
		},
		renderText() {
			return `
`;
		},
	}),
	Le = (o, e) => {
		var l;
		const t = o.resolve(e),
			n = t.depth - 1,
			s = t.before(n),
			r = o.resolve(s).nodeAfter;
		return (
			r ?
				(l = r.type.spec.group) != null && l.includes("bnBlock") ?
					It$1(o.resolve(s))
				:	Le(o, s)
			:	void 0
		);
	},
	_ = (o, e) => {
		const t = o.resolve(e),
			n = t.index();
		if (n === 0) return;
		const s = t.posAtIndex(n - 1);
		return It$1(o.resolve(s));
	},
	L = (o, e) => {
		const t = o.resolve(e),
			n = t.index();
		if (n === t.node().childCount - 1) return;
		const s = t.posAtIndex(n + 1);
		return It$1(o.resolve(s));
	},
	_e = (o, e) => {
		for (; e.childContainer; ) {
			const t = e.childContainer.node,
				n = o.resolve(e.childContainer.beforePos + 1).posAtIndex(t.childCount - 1);
			e = It$1(o.resolve(n));
		}
		return e;
	},
	ln = (o, e) =>
		o.isBlockContainer && o.blockContent.node.type.spec.content === "inline*" && o.blockContent.node.childCount > 0 && e.isBlockContainer && e.blockContent.node.type.spec.content === "inline*",
	dn = (o, e, t, n) => {
		if (!n.isBlockContainer)
			throw new Error(`Attempted to merge block at position ${n.bnBlock.beforePos} into previous block at position ${t.bnBlock.beforePos}, but next block is not a block container`);
		if (n.childContainer) {
			const s = o.doc.resolve(n.childContainer.beforePos + 1),
				r = o.doc.resolve(n.childContainer.afterPos - 1),
				i = s.blockRange(r);
			if (e) {
				const l = o.doc.resolve(n.bnBlock.beforePos);
				o.tr.lift(i, l.depth);
			}
		}
		if (e) {
			if (!t.isBlockContainer)
				throw new Error(`Attempted to merge block at position ${n.bnBlock.beforePos} into previous block at position ${t.bnBlock.beforePos}, but previous block is not a block container`);
			e(o.tr.delete(t.blockContent.afterPos - 1, n.blockContent.beforePos + 1));
		}
		return true;
	},
	fe =
		(o) =>
		({ state: e, dispatch: t }) => {
			const n = e.doc.resolve(o),
				s = It$1(n),
				r = _(e.doc, s.bnBlock.beforePos);
			if (!r) return false;
			const i = _e(e.doc, r);
			return ln(i, s) ? dn(e, t, i, s) : false;
		},
	un = Extension.create({
		priority: 50,
		// TODO: The shortcuts need a refactor. Do we want to use a command priority
		//  design as there is now, or clump the logic into a single function?
		addKeyboardShortcuts() {
			const o = () =>
					this.editor.commands.first(({ chain: n, commands: s }) => [
						// Deletes the selection if it's not empty.
						() => s.deleteSelection(),
						// Undoes an input rule if one was triggered in the last editor state change.
						() => s.undoInputRule(),
						// Reverts block content type to a paragraph if the selection is at the start of the block.
						() =>
							s.command(({ state: r }) => {
								const i = Tt$2(r);
								if (!i.isBlockContainer) return false;
								const l = r.selection.from === i.blockContent.beforePos + 1,
									a = i.blockContent.node.type.name === "paragraph";
								return l && !a ?
										s.command(
											yo$1(i.bnBlock.beforePos, {
												type: "paragraph",
												props: {},
											}),
										)
									:	false;
							}),
						// Removes a level of nesting if the block is indented if the selection is at the start of the block.
						() =>
							s.command(({ state: r }) => {
								const i = Tt$2(r);
								if (!i.isBlockContainer) return false;
								const { blockContent: l } = i;
								return r.selection.from === l.beforePos + 1 ? s.liftListItem("blockContainer") : false;
							}),
						// Merges block with the previous one if it isn't indented, and the selection is at the start of the
						// block. The target block for merging must contain inline content.
						() =>
							s.command(({ state: r }) => {
								const i = Tt$2(r);
								if (!i.isBlockContainer) return false;
								const { bnBlock: l, blockContent: a } = i,
									c = _(r.doc, i.bnBlock.beforePos);
								if (!c || !c.isBlockContainer || c.blockContent.node.type.spec.content !== "inline*") return false;
								const d = r.selection.from === a.beforePos + 1,
									u = r.selection.empty,
									p = l.beforePos;
								return d && u ? n().command(fe(p)).scrollIntoView().run() : false;
							}),
						// If the previous block is a columnList, moves the current block to
						// the end of the last column in it.
						() =>
							s.command(({ state: r, tr: i, dispatch: l }) => {
								const a = Tt$2(r);
								if (!a.isBlockContainer) return false;
								const c = _(r.doc, a.bnBlock.beforePos);
								if (!c || c.isBlockContainer) return false;
								if (l) {
									const d = c.bnBlock.afterPos - 1,
										u = i.doc.resolve(d - 1);
									return (i.delete(a.bnBlock.beforePos, a.bnBlock.afterPos), i.insert(u.pos, a.bnBlock.node), i.setSelection(TextSelection.near(i.doc.resolve(u.pos + 1))), true);
								}
								return false;
							}),
						// If the block is the first in a column, moves it to the end of the
						// previous column. If there is no previous column, moves it above the
						// columnList.
						() =>
							s.command(({ state: r, tr: i, dispatch: l }) => {
								const a = Tt$2(r);
								if (!a.isBlockContainer || !(i.selection.from === a.blockContent.beforePos + 1)) return false;
								const d = i.doc.resolve(a.bnBlock.beforePos);
								if (d.nodeBefore || d.node().type.name !== "column") return false;
								const h = i.doc.resolve(a.bnBlock.beforePos),
									f = i.doc.resolve(h.before()),
									m = f.before();
								return (
									l &&
										(i.delete(a.bnBlock.beforePos, a.bnBlock.afterPos),
										H(i, m),
										f.pos === m + 1 ?
											(i.insert(m, a.bnBlock.node), i.setSelection(TextSelection.near(i.doc.resolve(m))))
										:	(i.insert(f.pos - 1, a.bnBlock.node), i.setSelection(TextSelection.near(i.doc.resolve(f.pos))))),
									true
								);
							}),
						// Deletes the current block if it's an empty block with inline content,
						// and moves the selection to the previous block.
						() =>
							s.command(({ state: r }) => {
								var a;
								const i = Tt$2(r);
								if (!i.isBlockContainer) return false;
								if (i.blockContent.node.childCount === 0 && i.blockContent.node.type.spec.content === "inline*") {
									const c = _(r.doc, i.bnBlock.beforePos);
									if (!c || !c.isBlockContainer) return false;
									let d = n();
									if (
										(i.childContainer && d.insertContentAt(i.bnBlock.afterPos, (a = i.childContainer) == null ? void 0 : a.node.content),
										c.blockContent.node.type.spec.content === "tableRow+")
									) {
										const m = i.bnBlock.beforePos - 1 - 1 - 1 - 1 - 1;
										d = d.setTextSelection(m);
									} else if (c.blockContent.node.type.spec.content === "") d = d.setNodeSelection(c.blockContent.beforePos);
									else {
										const u = c.blockContent.afterPos - 1;
										d = d.setTextSelection(u);
									}
									return d
										.deleteRange({
											from: i.bnBlock.beforePos,
											to: i.bnBlock.afterPos,
										})
										.scrollIntoView()
										.run();
								}
								return false;
							}),
						// Deletes previous block if it contains no content and isn't a table,
						// when the selection is empty and at the start of the block. Moves the
						// current block into the deleted block's place.
						() =>
							s.command(({ state: r }) => {
								const i = Tt$2(r);
								if (!i.isBlockContainer) return false;
								const l = r.selection.from === i.blockContent.beforePos + 1,
									a = r.selection.empty,
									c = _(r.doc, i.bnBlock.beforePos);
								if (c && l && a) {
									const d = _e(r.doc, c);
									if (!d.isBlockContainer) return false;
									if (d.blockContent.node.type.spec.content === "" || (d.blockContent.node.type.spec.content === "inline*" && d.blockContent.node.childCount === 0))
										return n()
											.cut(
												{
													from: i.bnBlock.beforePos,
													to: i.bnBlock.afterPos,
												},
												d.bnBlock.afterPos,
											)
											.deleteRange({
												from: d.bnBlock.beforePos,
												to: d.bnBlock.afterPos,
											})
											.run();
								}
								return false;
							}),
					]),
				e = () =>
					this.editor.commands.first(({ chain: n, commands: s }) => [
						// Deletes the selection if it's not empty.
						() => s.deleteSelection(),
						// Deletes the first child block and un-nests its children, if the
						// selection is empty and at the end of the current block. If both the
						// parent and child blocks have inline content, the child block's
						// content is appended to the parent's. The child block's own children
						// are unindented before it's deleted.
						() =>
							s.command(({ state: r }) => {
								var p;
								const i = Tt$2(r);
								if (!i.isBlockContainer || !i.childContainer) return false;
								const { blockContent: l, childContainer: a } = i,
									c = r.selection.from === l.afterPos - 1,
									d = r.selection.empty,
									u = It$1(r.doc.resolve(a.beforePos + 1));
								if (!u.isBlockContainer) return false;
								if (c && d) {
									const h = u.blockContent.node,
										f = h.type.spec.content === "inline*",
										m = l.node.type.spec.content === "inline*";
									return n()
										.insertContentAt(u.bnBlock.afterPos, ((p = u.childContainer) == null ? void 0 : p.node.content) || Fragment.empty)
										.deleteRange(
											// Deletes whole child container if there's only one child.
											a.node.childCount === 1 ?
												{
													from: a.beforePos,
													to: a.afterPos,
												}
											:	{
													from: u.bnBlock.beforePos,
													to: u.bnBlock.afterPos,
												},
										)
										.insertContentAt(r.selection.from, f && m ? h.content : null)
										.setTextSelection(r.selection.from)
										.scrollIntoView()
										.run();
								}
								return false;
							}),
						// Merges block with the next one (at the same nesting level or lower),
						// if one exists, the block has no children, and the selection is at the
						// end of the block.
						() =>
							s.command(({ state: r }) => {
								const i = Tt$2(r);
								if (!i.isBlockContainer) return false;
								const { bnBlock: l, blockContent: a } = i,
									c = L(r.doc, i.bnBlock.beforePos);
								if (!c || !c.isBlockContainer) return false;
								const d = r.selection.from === a.afterPos - 1,
									u = r.selection.empty,
									p = l.afterPos;
								return d && u ? n().command(fe(p)).scrollIntoView().run() : false;
							}),
						// If the previous block is a columnList, moves the current block to
						// the end of the last column in it.
						() =>
							s.command(({ state: r, tr: i, dispatch: l }) => {
								const a = Tt$2(r);
								if (!a.isBlockContainer) return false;
								const c = L(r.doc, a.bnBlock.beforePos);
								if (!c || c.isBlockContainer) return false;
								if (l) {
									const d = c.bnBlock.beforePos + 1,
										u = i.doc.resolve(d + 1);
									return (
										i.delete(u.pos, u.pos + u.nodeAfter.nodeSize),
										H(i, c.bnBlock.beforePos),
										i.insert(a.bnBlock.afterPos, u.nodeAfter),
										i.setSelection(TextSelection.near(i.doc.resolve(u.pos))),
										true
									);
								}
								return false;
							}),
						// If the block is the last in a column, moves it to the start of the
						// next column. If there is no next column, moves it below the
						// columnList.
						() =>
							s.command(({ state: r, tr: i, dispatch: l }) => {
								const a = Tt$2(r);
								if (!a.isBlockContainer || !(i.selection.from === a.blockContent.afterPos - 1)) return false;
								const d = i.doc.resolve(a.bnBlock.afterPos);
								if (d.nodeAfter || d.node().type.name !== "column") return false;
								const h = i.doc.resolve(a.bnBlock.afterPos),
									f = i.doc.resolve(h.after()),
									m = f.after();
								if (l) {
									const b = f.pos === m - 1 ? m : f.pos + 1,
										g = It$1(i.doc.resolve(b));
									(i.delete(g.bnBlock.beforePos, g.bnBlock.afterPos),
										H(i, m - f.node().nodeSize),
										i.insert(h.pos, g.bnBlock.node),
										i.setSelection(TextSelection.near(i.doc.resolve(b))));
								}
								return true;
							}),
						// Deletes the next block at either the same or lower nesting level, if
						// the selection is empty and at the end of the block. If both the
						// current and next blocks have inline content, the next block's
						// content is appended to the current block's. The next block's own
						// children are unindented before it's deleted.
						() =>
							s.command(({ state: r }) => {
								var d;
								const i = Tt$2(r);
								if (!i.isBlockContainer) return false;
								const { blockContent: l } = i,
									a = r.selection.from === l.afterPos - 1,
									c = r.selection.empty;
								if (a && c) {
									const u = (b, g) => {
											const x = L(b, g);
											if (x) return x;
											const y = Le(b, g);
											if (y) return u(b, y.bnBlock.beforePos);
										},
										p = u(r.doc, i.bnBlock.beforePos);
									if (!p || !p.isBlockContainer) return false;
									const h = p.blockContent.node,
										f = h.type.spec.content === "inline*",
										m = l.node.type.spec.content === "inline*";
									return n()
										.insertContentAt(p.bnBlock.afterPos, ((d = p.childContainer) == null ? void 0 : d.node.content) || Fragment.empty)
										.deleteRange({
											from: p.bnBlock.beforePos,
											to: p.bnBlock.afterPos,
										})
										.insertContentAt(r.selection.from, f && m ? h.content : null)
										.setTextSelection(r.selection.from)
										.scrollIntoView()
										.run();
								}
								return false;
							}),
						// Deletes the current block if it's an empty block with inline content,
						// and moves the selection to the next block.
						() =>
							s.command(({ state: r }) => {
								const i = Tt$2(r);
								if (!i.isBlockContainer) return false;
								if (i.blockContent.node.childCount === 0 && i.blockContent.node.type.spec.content === "inline*") {
									const a = L(r.doc, i.bnBlock.beforePos);
									if (!a || !a.isBlockContainer) return false;
									let c = n();
									if (a.blockContent.node.type.spec.content === "tableRow+") {
										const f = i.bnBlock.afterPos + 1 + 1 + 1 + 1 + 1;
										c = c.setTextSelection(f);
									} else a.blockContent.node.type.spec.content === "" ? (c = c.setNodeSelection(a.blockContent.beforePos)) : (c = c.setTextSelection(a.blockContent.beforePos + 1));
									return c
										.deleteRange({
											from: i.bnBlock.beforePos,
											to: i.bnBlock.afterPos,
										})
										.scrollIntoView()
										.run();
								}
								return false;
							}),
						// Deletes next block if it contains no content and isn't a table,
						// when the selection is empty and at the end of the block. Moves the
						// current block into the deleted block's place.
						() =>
							s.command(({ state: r }) => {
								const i = Tt$2(r);
								if (!i.isBlockContainer) return false;
								const l = r.selection.from === i.blockContent.afterPos - 1,
									a = r.selection.empty,
									c = L(r.doc, i.bnBlock.beforePos);
								if (!c || !c.isBlockContainer) return false;
								if (c && l && a && (c.blockContent.node.type.spec.content === "" || (c.blockContent.node.type.spec.content === "inline*" && c.blockContent.node.childCount === 0))) {
									const u = c.bnBlock.node.lastChild.content;
									return n()
										.deleteRange({
											from: c.bnBlock.beforePos,
											to: c.bnBlock.afterPos,
										})
										.insertContentAt(i.bnBlock.afterPos, c.bnBlock.node.childCount === 2 ? u : null)
										.run();
								}
								return false;
							}),
					]),
				t = (n = false) =>
					this.editor.commands.first(({ commands: s, tr: r }) => [
						// Removes a level of nesting if the block is empty & indented, while the selection is also empty & at the start
						// of the block.
						() =>
							s.command(({ state: i }) => {
								const l = Tt$2(i);
								if (!l.isBlockContainer) return false;
								const { bnBlock: a, blockContent: c } = l,
									{ depth: d } = i.doc.resolve(a.beforePos),
									u = i.selection.$anchor.parentOffset === 0,
									p = i.selection.anchor === i.selection.head,
									h = c.node.childCount === 0,
									f = d > 1;
								return u && p && h && f ? s.liftListItem("blockContainer") : false;
							}),
						// Creates a hard break if block is configured to do so.
						() =>
							s.command(({ state: i }) => {
								var c;
								const l = Tt$2(i),
									a = ((c = this.options.editor.schema.blockSchema[l.blockNoteType].meta) == null ? void 0 : c.hardBreakShortcut) ?? "shift+enter";
								if (a === "none") return false;
								if (
									// If shortcut is not configured, or is configured as "shift+enter",
									// create a hard break for shift+enter, but not for enter.
									(a === "shift+enter" && n) || // If shortcut is configured as "enter", create a hard break for
									// both enter and shift+enter.
									a === "enter"
								) {
									const d = r.storedMarks || r.selection.$head.marks().filter((u) => this.editor.extensionManager.splittableMarks.includes(u.type.name));
									return (r.insert(r.selection.head, r.doc.type.schema.nodes.hardBreak.create()).ensureMarks(d), true);
								}
								return false;
							}),
						// Creates a new block and moves the selection to it if the current one is empty, while the selection is also
						// empty & at the start of the block.
						() =>
							s.command(({ state: i, dispatch: l, tr: a }) => {
								var m;
								const c = Tt$2(i);
								if (!c.isBlockContainer) return false;
								const { bnBlock: d, blockContent: u } = c,
									p = i.selection.$anchor.parentOffset === 0,
									h = i.selection.anchor === i.selection.head,
									f = u.node.childCount === 0;
								if (p && h && f) {
									const b = d.afterPos,
										g = b + 2;
									if (l) {
										const x = i.schema.nodes.blockContainer.createAndFill(
											void 0,
											[i.schema.nodes.paragraph.createAndFill() || void 0, (m = c.childContainer) == null ? void 0 : m.node].filter((y) => y !== void 0),
										);
										(a
											.insert(b, x)
											.setSelection(new TextSelection(a.doc.resolve(g)))
											.scrollIntoView(),
											c.childContainer && a.delete(c.childContainer.beforePos, c.childContainer.afterPos));
									}
									return true;
								}
								return false;
							}),
						// Splits the current block, moving content inside that's after the cursor to a new text block below. Also
						// deletes the selection beforehand, if it's not empty.
						() =>
							s.command(({ state: i, chain: l }) => {
								const a = Tt$2(i);
								if (!a.isBlockContainer) return false;
								const { blockContent: c } = a,
									d = i.selection.$anchor.parentOffset === 0;
								return c.node.childCount === 0 ?
										false
									:	(l()
											.deleteSelection()
											.command(wo$1(i.selection.from, d, d))
											.run(),
										true);
							}),
					]);
			return {
				"Backspace": o,
				"Delete": e,
				"Enter": () => t(),
				"Shift-Enter": () => t(true),
				// Always returning true for tab key presses ensures they're not captured by the browser. Otherwise, they blur the
				// editor since the browser will try to use tab for keyboard navigation.
				"Tab": () => {
					var n, s;
					return (
							this.options.tabBehavior !== "prefer-indent" &&
								(((n = this.options.editor.getExtension(Ao$1)) != null && n.store.state) || ((s = this.options.editor.getExtension(H$1)) == null ? void 0 : s.store.state) !== void 0)
						) ?
							false
						:	Me(this.options.editor);
				},
				"Shift-Tab": () => {
					var n, s;
					return (
							this.options.tabBehavior !== "prefer-indent" &&
								(((n = this.options.editor.getExtension(Ao$1)) != null && n.store.state) || ((s = this.options.editor.getExtension(H$1)) == null ? void 0 : s.store.state) !== void 0)
						) ?
							false
						:	this.editor.commands.liftListItem("blockContainer");
				},
				"Shift-Mod-ArrowUp": () => (this.options.editor.moveBlocksUp(), true),
				"Shift-Mod-ArrowDown": () => (this.options.editor.moveBlocksDown(), true),
				"Mod-z": () => this.options.editor.undo(),
				"Mod-y": () => this.options.editor.redo(),
				"Shift-Mod-z": () => this.options.editor.redo(),
			};
		},
	}),
	pn = Mark.create({
		name: "insertion",
		inclusive: false,
		excludes: "deletion modification insertion",
		addAttributes() {
			return {
				id: { default: null, validate: "number" },
				// note: validate is supported in prosemirror but not in tiptap, so this doesn't actually work (considered not critical)
			};
		},
		extendMarkSchema(o) {
			return o.name !== "insertion" ?
					{}
				:	{
						blocknoteIgnore: true,
						inclusive: false,
						toDOM(e, t) {
							return [
								"ins",
								{
									"data-id": String(e.attrs.id),
									"data-inline": String(t),
									...(!t && { style: "display: contents" }),
									// changed to "contents" to make this work for table rows
								},
								0,
							];
						},
						parseDOM: [
							{
								tag: "ins",
								getAttrs(e) {
									return e.dataset.id ?
											{
												id: parseInt(e.dataset.id, 10),
											}
										:	false;
								},
							},
						],
					};
		},
	}),
	fn = Mark.create({
		name: "deletion",
		inclusive: false,
		excludes: "insertion modification deletion",
		addAttributes() {
			return {
				id: { default: null, validate: "number" },
				// note: validate is supported in prosemirror but not in tiptap
			};
		},
		extendMarkSchema(o) {
			return o.name !== "deletion" ?
					{}
				:	{
						blocknoteIgnore: true,
						inclusive: false,
						// attrs: {
						//   id: { validate: "number" },
						// },
						toDOM(e, t) {
							return [
								"del",
								{
									"data-id": String(e.attrs.id),
									"data-inline": String(t),
									...(!t && { style: "display: contents" }),
									// changed to "contents" to make this work for table rows
								},
								0,
							];
						},
						parseDOM: [
							{
								tag: "del",
								getAttrs(e) {
									return e.dataset.id ?
											{
												id: parseInt(e.dataset.id, 10),
											}
										:	false;
								},
							},
						],
					};
		},
	}),
	hn = Mark.create({
		name: "modification",
		inclusive: false,
		excludes: "deletion insertion",
		addAttributes() {
			return {
				id: { default: null, validate: "number" },
				type: { validate: "string" },
				attrName: { default: null, validate: "string|null" },
				previousValue: { default: null },
				newValue: { default: null },
			};
		},
		extendMarkSchema(o) {
			return o.name !== "modification" ?
					{}
				:	{
						blocknoteIgnore: true,
						inclusive: false,
						// attrs: {
						//   id: { validate: "number" },
						//   type: { validate: "string" },
						//   attrName: { default: null, validate: "string|null" },
						//   previousValue: { default: null },
						//   newValue: { default: null },
						// },
						toDOM(e, t) {
							return [
								t ? "span" : "div",
								{
									"data-type": "modification",
									"data-id": String(e.attrs.id),
									"data-mod-type": e.attrs.type,
									"data-mod-prev-val": JSON.stringify(e.attrs.previousValue),
									// TODO: Try to serialize marks with toJSON?
									"data-mod-new-val": JSON.stringify(e.attrs.newValue),
								},
								0,
							];
						},
						parseDOM: [
							{
								tag: "span[data-type='modification']",
								getAttrs(e) {
									return e.dataset.id ?
											{
												id: parseInt(e.dataset.id, 10),
												type: e.dataset.modType,
												previousValue: e.dataset.modPrevVal,
												newValue: e.dataset.modNewVal,
											}
										:	false;
								},
							},
							{
								tag: "div[data-type='modification']",
								getAttrs(e) {
									return e.dataset.id ?
											{
												id: parseInt(e.dataset.id, 10),
												type: e.dataset.modType,
												previousValue: e.dataset.modPrevVal,
											}
										:	false;
								},
							},
						],
					};
		},
	}),
	mn = Extension.create({
		name: "textAlignment",
		addGlobalAttributes() {
			return [
				{
					// Generally text alignment is handled through props using the custom
					// blocks API. Tables are the only blocks that are created as TipTap
					// nodes and ported to blocks, so we need to add text alignment in a
					// separate extension.
					types: ["tableCell", "tableHeader"],
					attributes: {
						textAlignment: {
							default: "left",
							parseHTML: (o) => o.getAttribute("data-text-alignment"),
							renderHTML: (o) =>
								o.textAlignment === "left" ?
									{}
								:	{
										"data-text-alignment": o.textAlignment,
									},
						},
					},
				},
			];
		},
	}),
	kn = Extension.create({
		name: "blockTextColor",
		addGlobalAttributes() {
			return [
				{
					types: ["table", "tableCell", "tableHeader"],
					attributes: {
						textColor: xo$1(),
					},
				},
			];
		},
	}),
	bn = {
		blockColor: "data-block-color",
		blockStyle: "data-block-style",
		id: "data-id",
		depth: "data-depth",
		depthChange: "data-depth-change",
	},
	gn = Node3.create({
		name: "blockContainer",
		group: "blockGroupChild bnBlock",
		// A block always contains content, and optionally a blockGroup which contains nested blocks
		content: "blockContent blockGroup?",
		// Ensures content-specific keyboard handlers trigger first.
		priority: 50,
		defining: true,
		marks: "insertion modification deletion",
		parseHTML() {
			return [
				{
					tag: "div[data-node-type=" + this.name + "]",
					getAttrs: (o) => {
						if (typeof o == "string") return false;
						const e = {};
						for (const [t, n] of Object.entries(bn)) o.getAttribute(n) && (e[t] = o.getAttribute(n));
						return e;
					},
				},
				// Ignore `blockOuter` divs, but parse the `blockContainer` divs inside them.
				{
					tag: 'div[data-node-type="blockOuter"]',
					skip: true,
				},
			];
		},
		renderHTML({ HTMLAttributes: o }) {
			var s;
			const e = document.createElement("div");
			((e.className = "bn-block-outer"), e.setAttribute("data-node-type", "blockOuter"));
			for (const [r, i] of Object.entries(o)) r !== "class" && e.setAttribute(r, i);
			const t = {
					...(((s = this.options.domAttributes) == null ? void 0 : s.block) || {}),
					...o,
				},
				n = document.createElement("div");
			((n.className = D$2("bn-block", t.class)), n.setAttribute("data-node-type", this.name));
			for (const [r, i] of Object.entries(t)) r !== "class" && n.setAttribute(r, i);
			return (
				e.appendChild(n),
				{
					dom: e,
					contentDOM: n,
				}
			);
		},
	}),
	Bn = Node3.create({
		name: "blockGroup",
		group: "childContainer",
		content: "blockGroupChild+",
		marks: "deletion insertion modification",
		parseHTML() {
			return [
				{
					tag: "div",
					getAttrs: (o) =>
						typeof o == "string" ? false
						: o.getAttribute("data-node-type") === "blockGroup" ? null
						: false,
				},
			];
		},
		renderHTML({ HTMLAttributes: o }) {
			var n;
			const e = {
					...(((n = this.options.domAttributes) == null ? void 0 : n.blockGroup) || {}),
					...o,
				},
				t = document.createElement("div");
			((t.className = D$2("bn-block-group", e.class)), t.setAttribute("data-node-type", "blockGroup"));
			for (const [s, r] of Object.entries(e)) s !== "class" && t.setAttribute(s, r);
			return {
				dom: t,
				contentDOM: t,
			};
		},
	}),
	yn = Node3.create({
		name: "doc",
		topNode: true,
		content: "blockGroup",
		marks: "insertion modification deletion",
	}),
	Cn = a$1(({ options: o }) => ({
		key: "collaboration",
		blockNoteExtensions: [Oo$1(o), ae$1(o), K(o), X$1(), Po$1(o)],
	}));
let he = false;
function Sn(o, e) {
	const t = [
		extensions_exports.ClipboardTextSerializer,
		extensions_exports.Commands,
		extensions_exports.Editable,
		extensions_exports.FocusEvents,
		extensions_exports.Tabindex,
		Gapcursor,
		Q$2.configure({
			// everything from bnBlock group (nodes that represent a BlockNote block should have an id)
			types: ["blockContainer", "columnList", "column"],
			setIdAttribute: e.setIdAttribute,
		}),
		cn,
		Text,
		// marks:
		pn,
		fn,
		hn,
		Link.extend({
			inclusive: false,
		}).configure({
			defaultProtocol: Vo$1,
			// only call this once if we have multiple editors installed. Or fix https://github.com/ueberdosis/tiptap/issues/5450
			protocols: he ? [] : Ro$1,
		}),
		...Object.values(o.schema.styleSpecs).map((n) =>
			n.implementation.mark.configure({
				editor: o,
			}),
		),
		kn,
		an,
		mn,
		// make sure escape blurs editor, so that we can tab to other elements in the host page (accessibility)
		Extension.create({
			name: "OverrideEscape",
			addKeyboardShortcuts: () => ({
				Escape: () => {
					var n;
					return (n = o.getExtension(Vn)) != null && n.shown() ? false : (o.blur(), true);
				},
			}),
		}),
		// nodes
		yn,
		gn.configure({
			editor: o,
			domAttributes: e.domAttributes,
		}),
		un.configure({
			editor: o,
			tabBehavior: e.tabBehavior,
		}),
		Bn.configure({
			domAttributes: e.domAttributes,
		}),
		...Object.values(o.schema.inlineContentSpecs)
			.filter((n) => n.config !== "link" && n.config !== "text")
			.map((n) =>
				n.implementation.node.configure({
					editor: o,
				}),
			),
		...Object.values(o.schema.blockSpecs).flatMap((n) => [
			// the node extension implementations
			...("node" in n.implementation ?
				[
					n.implementation.node.configure({
						editor: o,
						domAttributes: e.domAttributes,
					}),
				]
			:	[]),
		]),
		rn(o),
		nn(o, e.pasteHandler || ((n) => n.defaultPasteHandler())),
		Vo(o),
	];
	return ((he = true), t);
}
function xn(o, e) {
	const t = [Do$1(), Mo$1(e), H$1(e), Ao$1(e), No$1(e), Ho$1(), $o$1(e), b(e), _o$1(e), Vn(e), ...(e.trailingBlock !== false ? [jo$1()] : [])];
	return (e.collaboration ? t.push(Cn(e.collaboration)) : t.push(Lo$1()), "table" in o.schema.blockSpecs && t.push(Yo$1(e)), e.animations !== false && t.push(Fo$1()), t);
}
class En {
	constructor(e, t) {
		/**
		 * A set of extension keys which are disabled by the options
		 */
		k(this, "disabledExtensions", /* @__PURE__ */ new Set());
		/**
		 * A list of all the extensions that are registered to the editor
		 */
		k(this, "extensions", []);
		/**
		 * A map of all the abort controllers for each extension that has an init method defined
		 */
		k(this, "abortMap", /* @__PURE__ */ new Map());
		/**
		 * A map of all the extension factories that are registered to the editor
		 */
		k(this, "extensionFactories", /* @__PURE__ */ new Map());
		/**
		 * Because a single blocknote extension can both have it's own prosemirror plugins & additional generated ones (e.g. keymap & input rules plugins)
		 * We need to keep track of all the plugins for each extension, so that we can remove them when the extension is unregistered
		 */
		k(this, "extensionPlugins", /* @__PURE__ */ new Map());
		((this.editor = e),
			(this.options = t),
			e.onMount(() => {
				for (const n of this.extensions)
					if (n.mount) {
						const s = new window.AbortController(),
							r = n.mount({
								dom: e.prosemirrorView.dom,
								root: e.prosemirrorView.root,
								signal: s.signal,
							});
						(r &&
							s.signal.addEventListener("abort", () => {
								r();
							}),
							this.abortMap.set(n, s));
					}
			}),
			e.onUnmount(() => {
				for (const [n, s] of this.abortMap.entries()) (this.abortMap.delete(n), s.abort());
			}),
			(this.disabledExtensions = new Set(t.disableExtensions || [])));
		for (const n of xn(this.editor, this.options)) this.addExtension(n);
		for (const n of this.options.extensions ?? []) this.addExtension(n);
		for (const n of Object.values(this.editor.schema.blockSpecs)) for (const s of n.extensions ?? []) this.addExtension(s);
	}
	/**
	 * Register one or more extensions to the editor after the editor is initialized.
	 *
	 * This allows users to switch on & off extensions "at runtime".
	 */
	registerExtension(e) {
		var r;
		const t = [].concat(e).filter(Boolean);
		if (!t.length) {
			console.warn("No extensions found to register", e);
			return;
		}
		const n = t.map((i) => this.addExtension(i)).filter(Boolean),
			s = /* @__PURE__ */ new Set();
		for (const i of n)
			(i != null &&
				i.tiptapExtensions &&
				console.warn(
					`Extension ${i.key} has tiptap extensions, but these cannot be changed after initializing the editor. Please separate the extension into multiple extensions if you want to add them, or re-initialize the editor.`,
					i,
				),
				(r = i == null ? void 0 : i.inputRules) != null &&
					r.length &&
					console.warn(
						`Extension ${i.key} has input rules, but these cannot be changed after initializing the editor. Please separate the extension into multiple extensions if you want to add them, or re-initialize the editor.`,
						i,
					),
				this.getProsemirrorPluginsFromExtension(i).plugins.forEach((l) => {
					s.add(l);
				}));
		this.updatePlugins((i) => [...i, ...s]);
	}
	/**
	 * Register an extension to the editor
	 * @param extension - The extension to register
	 * @returns The extension instance
	 */
	addExtension(e) {
		let t;
		if ((typeof e == "function" ? (t = e({ editor: this.editor })) : (t = e), !(!t || this.disabledExtensions.has(t.key)))) {
			if (typeof e == "function") {
				const n = t[r$1];
				typeof n == "function" && this.extensionFactories.set(n, t);
			}
			if ((this.extensions.push(t), t.blockNoteExtensions)) for (const n of t.blockNoteExtensions) this.addExtension(n);
			return t;
		}
	}
	/**
	 * Resolve an extension or a list of extensions into a list of extension instances
	 * @param toResolve - The extension or list of extensions to resolve
	 * @returns A list of extension instances
	 */
	resolveExtensions(e) {
		const t = [];
		if (typeof e == "function") {
			const n = this.extensionFactories.get(e);
			n && t.push(n);
		} else if (Array.isArray(e)) for (const n of e) t.push(...this.resolveExtensions(n));
		else if (typeof e == "object" && "key" in e) t.push(e);
		else if (typeof e == "string") {
			const n = this.extensions.find((s) => s.key === e);
			n && t.push(n);
		}
		return t;
	}
	/**
	 * Unregister an extension from the editor
	 * @param toUnregister - The extension to unregister
	 * @returns void
	 */
	unregisterExtension(e) {
		var r;
		const t = this.resolveExtensions(e);
		if (!t.length) {
			console.warn("No extensions found to unregister", e);
			return;
		}
		let n = false;
		const s = /* @__PURE__ */ new Set();
		for (const i of t) {
			((this.extensions = this.extensions.filter((a) => a !== i)),
				this.extensionFactories.forEach((a, c) => {
					a === i && this.extensionFactories.delete(c);
				}),
				(r = this.abortMap.get(i)) == null || r.abort(),
				this.abortMap.delete(i));
			const l = this.extensionPlugins.get(i);
			(l == null ||
				l.forEach((a) => {
					s.add(a);
				}),
				this.extensionPlugins.delete(i),
				i.tiptapExtensions &&
					!n &&
					((n = true),
					console.warn(
						`Extension ${i.key} has tiptap extensions, but they will not be removed. Please separate the extension into multiple extensions if you want to remove them, or re-initialize the editor.`,
						e,
					)));
		}
		this.updatePlugins((i) => i.filter((l) => !s.has(l)));
	}
	/**
	 * Allows resetting the current prosemirror state's plugins
	 * @param update - A function that takes the current plugins and returns the new plugins
	 * @returns void
	 */
	updatePlugins(e) {
		const t = this.editor.prosemirrorState,
			n = t.reconfigure({
				plugins: e(t.plugins.slice()),
			});
		this.editor.prosemirrorView.updateState(n);
	}
	/**
	 * Get all the extensions that are registered to the editor
	 */
	getTiptapExtensions() {
		var s;
		const e = Sn(this.editor, this.options).filter((r) => !this.disabledExtensions.has(r.name)),
			t = P(this.extensions),
			n = /* @__PURE__ */ new Map();
		for (const r of this.extensions) {
			r.tiptapExtensions && e.push(...r.tiptapExtensions);
			const i = t(r.key),
				{ plugins: l, inputRules: a } = this.getProsemirrorPluginsFromExtension(r);
			(l.length &&
				e.push(
					Extension.create({
						name: r.key,
						priority: i,
						addProseMirrorPlugins: () => l,
					}),
				),
				a.length && (n.has(i) || n.set(i, []), n.get(i).push(...a)));
		}
		e.push(
			Extension.create({
				name: "blocknote-input-rules",
				addProseMirrorPlugins() {
					const r = [];
					return (
						Array.from(n.keys())
							.sort()
							.reverse()
							.forEach((i) => {
								r.push(...n.get(i));
							}),
						[inputRules({ rules: r })]
					);
				},
			}),
		);
		for (const r of ((s = this.options._tiptapOptions) == null ? void 0 : s.extensions) ?? []) e.push(r);
		return e;
	}
	/**
	 * This maps a blocknote extension into an array of Prosemirror plugins if it has any of the following:
	 * - plugins
	 * - keyboard shortcuts
	 * - input rules
	 */
	getProsemirrorPluginsFromExtension(e) {
		var s, r, i;
		const t = [...(e.prosemirrorPlugins ?? [])],
			n = [];
		return !((s = e.prosemirrorPlugins) != null && s.length) && !Object.keys(e.keyboardShortcuts || {}).length && !((r = e.inputRules) != null && r.length) ?
				{ plugins: t, inputRules: n }
			:	(this.extensionPlugins.set(e, t),
				(i = e.inputRules) != null &&
					i.length &&
					n.push(
						...e.inputRules.map(
							(l) =>
								new InputRule(l.find, (a, c, d, u) => {
									const p = l.replace({
										match: c,
										range: { from: d, to: u },
										editor: this.editor,
									});
									if (p) {
										const h = this.editor.getTextCursorPosition();
										if (this.editor.schema.blockSchema[h.block.type].content !== "inline") return null;
										const f = Ot$2(a.tr),
											m = a.tr.deleteRange(d, u);
										return (Q$1(m, f.bnBlock.beforePos, p), m);
									}
									return null;
								}),
						),
					),
				Object.keys(e.keyboardShortcuts || {}).length && t.push(keymap(Object.fromEntries(Object.entries(e.keyboardShortcuts).map(([l, a]) => [l, () => a({ editor: this.editor })])))),
				{ plugins: t, inputRules: n });
	}
	/**
	 * Get all extensions
	 */
	getExtensions() {
		return new Map(this.extensions.map((e) => [e.key, e]));
	}
	getExtension(e) {
		if (typeof e == "string") {
			const t = this.extensions.find((n) => n.key === e);
			return t || void 0;
		} else if (typeof e == "function") {
			const t = this.extensionFactories.get(e);
			return t || void 0;
		}
		throw new Error(`Invalid extension type: ${typeof e}`);
	}
	/**
	 * Check if an extension exists
	 */
	hasExtension(e) {
		return (
			typeof e == "string" ? this.extensions.some((t) => t.key === e)
			: typeof e == "object" && "key" in e ? this.extensions.some((t) => t.key === e.key)
			: typeof e == "function" ? this.extensionFactories.has(e)
			: false
		);
	}
}
function Pn(o, e) {
	let { $from: t, $to: n } = e;
	if (t.pos > t.start() && t.pos < o.content.size) {
		const s = o.textBetween(t.pos, t.pos + 1);
		if (/^[\w\p{P}]$/u.test(s)) {
			const i = o.textBetween(t.start(), t.pos).match(/[\w\p{P}]+$/u);
			i && (t = o.resolve(t.pos - i[0].length));
		}
	}
	if (n.pos < n.end() && n.pos > 0) {
		const s = o.textBetween(n.pos - 1, n.pos);
		if (/^[\w\p{P}]$/u.test(s)) {
			const i = o.textBetween(n.pos, n.end()).match(/^[\w\p{P}]+/u);
			i && (n = o.resolve(n.pos + i[0].length));
		}
	}
	return { $from: t, $to: n, from: t.pos, to: n.pos };
}
function Tn(o) {
	const e = ht(o);
	if (o.selection.empty || "node" in o.selection) return;
	const t = o.doc.resolve(Y$1(o.doc, o.selection.from).posBeforeNode),
		n = o.doc.resolve(Y$1(o.doc, o.selection.to).posBeforeNode),
		s = (c, d) => {
			const u = t.posAtIndex(c, d),
				p = o.doc.resolve(u).nodeAfter;
			if (!p) throw new Error(`Error getting selection - node not found at position ${u}`);
			return L$2(p, e);
		},
		r = [],
		i = t.sharedDepth(n.pos),
		l = t.index(i),
		a = n.index(i);
	if (t.depth > i) {
		r.push(L$2(t.nodeAfter, e));
		for (let c = t.depth; c > i; c--)
			if (t.node(c).type.isInGroup("childContainer")) {
				const u = t.index(c) + 1,
					p = t.node(c).childCount;
				for (let h = u; h < p; h++) r.push(s(h, c));
			}
	} else r.push(s(l, i));
	for (let c = l + 1; c <= a; c++) r.push(s(c, i));
	if (r.length === 0) throw new Error(`Error getting selection - selection doesn't span any blocks (${o.selection})`);
	return {
		blocks: r,
	};
}
function wn(o, e, t) {
	const n = typeof e == "string" ? e : e.id,
		s = typeof t == "string" ? t : t.id,
		r = ht(o),
		i = J$1(r);
	if (n === s) throw new Error(`Attempting to set selection with the same anchor and head blocks (id ${n})`);
	const l = Bt$1(n, o.doc);
	if (!l) throw new Error(`Block with ID ${n} not found`);
	const a = Bt$1(s, o.doc);
	if (!a) throw new Error(`Block with ID ${s} not found`);
	const c = Z(l),
		d = Z(a),
		u = i.blockSchema[c.blockNoteType],
		p = i.blockSchema[d.blockNoteType];
	if (!c.isBlockContainer || u.content === "none") throw new Error(`Attempting to set selection anchor in block without content (id ${n})`);
	if (!d.isBlockContainer || p.content === "none") throw new Error(`Attempting to set selection anchor in block without content (id ${s})`);
	let h, f;
	if (u.content === "table") {
		const m = TableMap.get(c.blockContent.node);
		h = c.blockContent.beforePos + m.positionAt(0, 0, c.blockContent.node) + 1 + 2;
	} else h = c.blockContent.beforePos + 1;
	if (p.content === "table") {
		const m = TableMap.get(d.blockContent.node),
			b = d.blockContent.beforePos + m.positionAt(m.height - 1, m.width - 1, d.blockContent.node) + 1,
			g = o.doc.resolve(b).nodeAfter.nodeSize;
		f = b + g - 2;
	} else f = d.blockContent.afterPos - 1;
	o.setSelection(TextSelection.create(o.doc, h, f));
}
function Mn(o, e = false) {
	const t = ht(o),
		n = e ? Pn(o.doc, o.selection) : o.selection;
	let s = n.$from,
		r = n.$to;
	for (; r.parentOffset >= r.parent.nodeSize - 2 && r.depth > 0; ) r = o.doc.resolve(r.pos + 1);
	for (; r.parentOffset === 0 && r.depth > 0; ) r = o.doc.resolve(r.pos - 1);
	for (; s.parentOffset === 0 && s.depth > 0; ) s = o.doc.resolve(s.pos - 1);
	for (; s.parentOffset >= s.parent.nodeSize - 2 && s.depth > 0; ) s = o.doc.resolve(s.pos + 1);
	const i = Dt$2(o.doc.slice(s.pos, r.pos, true), t);
	return {
		_meta: {
			startPos: s.pos,
			endPos: r.pos,
		},
		...i,
	};
}
function vn(o) {
	const { bnBlock: e } = Ot$2(o),
		t = ht(o.doc),
		n = o.doc.resolve(e.beforePos),
		s = n.nodeBefore,
		r = o.doc.resolve(e.afterPos).nodeAfter;
	let i;
	return (
		n.depth > 1 && ((i = n.node()), i.type.isInGroup("bnBlock") || (i = n.node(n.depth - 1))),
		{
			block: L$2(e.node, t),
			prevBlock: s === null ? void 0 : L$2(s, t),
			nextBlock: r === null ? void 0 : L$2(r, t),
			parentBlock: i === void 0 ? void 0 : L$2(i, t),
		}
	);
}
function De(o, e, t = "start") {
	const n = typeof e == "string" ? e : e.id,
		s = ht(o.doc),
		r = J$1(s),
		i = Bt$1(n, o.doc);
	if (!i) throw new Error(`Block with ID ${n} not found`);
	const l = Z(i),
		a = r.blockSchema[l.blockNoteType].content;
	if (l.isBlockContainer) {
		const c = l.blockContent;
		if (a === "none") {
			o.setSelection(NodeSelection.create(o.doc, c.beforePos));
			return;
		}
		if (a === "inline") t === "start" ? o.setSelection(TextSelection.create(o.doc, c.beforePos + 1)) : o.setSelection(TextSelection.create(o.doc, c.afterPos - 1));
		else if (a === "table") t === "start" ? o.setSelection(TextSelection.create(o.doc, c.beforePos + 4)) : o.setSelection(TextSelection.create(o.doc, c.afterPos - 4));
		else throw new O(a);
	} else {
		const c = t === "start" ? l.childContainer.node.firstChild : l.childContainer.node.lastChild;
		De(o, c.attrs.id, t);
	}
}
class In {
	constructor(e) {
		this.editor = e;
	}
	/**
	 * Gets a snapshot of the current selection. This contains all blocks (included nested blocks)
	 * that the selection spans across.
	 *
	 * If the selection starts / ends halfway through a block, the returned data will contain the entire block.
	 */
	getSelection() {
		return this.editor.transact((e) => Tn(e));
	}
	/**
	 * Gets a snapshot of the current selection. This contains all blocks (included nested blocks)
	 * that the selection spans across.
	 *
	 * If the selection starts / ends halfway through a block, the returned block will be
	 * only the part of the block that is included in the selection.
	 */
	getSelectionCutBlocks(e = false) {
		return this.editor.transact((t) => Mn(t, e));
	}
	/**
	 * Sets the selection to a range of blocks.
	 * @param startBlock The identifier of the block that should be the start of the selection.
	 * @param endBlock The identifier of the block that should be the end of the selection.
	 */
	setSelection(e, t) {
		return this.editor.transact((n) => wn(n, e, t));
	}
	/**
	 * Gets a snapshot of the current text cursor position.
	 * @returns A snapshot of the current text cursor position.
	 */
	getTextCursorPosition() {
		return this.editor.transact((e) => vn(e));
	}
	/**
	 * Sets the text cursor position to the start or end of an existing block. Throws an error if the target block could
	 * not be found.
	 * @param targetBlock The identifier of an existing block that the text cursor should be moved to.
	 * @param placement Whether the text cursor should be placed at the start or end of the block.
	 */
	setTextCursorPosition(e, t = "start") {
		return this.editor.transact((n) => De(n, e, t));
	}
	/**
	 * Gets the bounding box of the current selection.
	 */
	getSelectionBoundingBox() {
		if (!this.editor.prosemirrorView) return;
		const { selection: e } = this.editor.prosemirrorState,
			{ ranges: t } = e,
			n = Math.min(...t.map((r) => r.$from.pos)),
			s = Math.max(...t.map((r) => r.$to.pos));
		if (isNodeSelection(e)) {
			const r = this.editor.prosemirrorView.nodeDOM(n);
			if (r) return r.getBoundingClientRect();
		}
		return posToDOMRect(this.editor.prosemirrorView, n, s).toJSON();
	}
}
class An {
	constructor(e) {
		/**
		 * Stores the currently active transaction, which is the accumulated transaction from all {@link dispatch} calls during a {@link transact} calls
		 */
		k(this, "activeTransaction", null);
		// Flag to indicate if we're in a `can` call
		k(this, "isInCan", false);
		this.editor = e;
	}
	/**
	 * For any command that can be executed, you can check if it can be executed by calling `editor.can(command)`.
	 * @example
	 * ```ts
	 * if (editor.can(editor.undo)) {
	 *   // show button
	 * } else {
	 *   // hide button
	 * }
	 */
	can(e) {
		try {
			return ((this.isInCan = !0), e());
		} finally {
			this.isInCan = false;
		}
	}
	/**
	 * Execute a prosemirror command. This is mostly for backwards compatibility with older code.
	 *
	 * @note You should prefer the {@link transact} method when possible, as it will automatically handle the dispatching of the transaction and work across blocknote transactions.
	 *
	 * @example
	 * ```ts
	 * editor.exec((state, dispatch, view) => {
	 *   dispatch(state.tr.insertText("Hello, world!"));
	 * });
	 * ```
	 */
	exec(e) {
		if (this.activeTransaction) throw new Error("`exec` should not be called within a `transact` call, move the `exec` call outside of the `transact` call");
		if (this.isInCan) return this.canExec(e);
		const t = this.prosemirrorState,
			n = this.prosemirrorView;
		return e(t, (r) => this.prosemirrorView.dispatch(r), n);
	}
	/**
	 * Check if a command can be executed. A command should return `false` if it is not valid in the current state.
	 *
	 * @example
	 * ```ts
	 * if (editor.canExec(command)) {
	 *   // show button
	 * } else {
	 *   // hide button
	 * }
	 * ```
	 */
	canExec(e) {
		if (this.activeTransaction) throw new Error("`canExec` should not be called within a `transact` call, move the `canExec` call outside of the `transact` call");
		const t = this.prosemirrorState,
			n = this.prosemirrorView;
		return e(t, void 0, n);
	}
	/**
	 * Execute a function within a "blocknote transaction".
	 * All changes to the editor within the transaction will be grouped together, so that
	 * we can dispatch them as a single operation (thus creating only a single undo step)
	 *
	 * @note There is no need to dispatch the transaction, as it will be automatically dispatched when the callback is complete.
	 *
	 * @example
	 * ```ts
	 * // All changes to the editor will be grouped together
	 * editor.transact((tr) => {
	 *   tr.insertText("Hello, world!");
	 * // These two operations will be grouped together in a single undo step
	 *   editor.transact((tr) => {
	 *     tr.insertText("Hello, world!");
	 *   });
	 * });
	 * ```
	 */
	transact(e) {
		if (this.activeTransaction) return e(this.activeTransaction);
		try {
			this.activeTransaction = this.editor._tiptapEditor.state.tr;
			const t = e(this.activeTransaction),
				n = this.activeTransaction;
			return (
				(this.activeTransaction = null),
				n && // Only dispatch if the transaction was actually modified in some way
					(n.docChanged || n.selectionSet || n.scrolledIntoView || n.storedMarksSet || !n.isGeneric) &&
					this.prosemirrorView.dispatch(n),
				t
			);
		} finally {
			this.activeTransaction = null;
		}
	}
	/**
	 * Get the underlying prosemirror state
	 * @note Prefer using `editor.transact` to read the current editor state, as that will ensure the state is up to date
	 * @see https://prosemirror.net/docs/ref/#state.EditorState
	 */
	get prosemirrorState() {
		if (this.activeTransaction)
			throw new Error(
				"`prosemirrorState` should not be called within a `transact` call, move the `prosemirrorState` call outside of the `transact` call or use `editor.transact` to read the current editor state",
			);
		return this.editor._tiptapEditor.state;
	}
	/**
	 * Get the underlying prosemirror view
	 * @see https://prosemirror.net/docs/ref/#view.EditorView
	 */
	get prosemirrorView() {
		return this.editor._tiptapEditor.view;
	}
	isFocused() {
		var e;
		return ((e = this.prosemirrorView) == null ? void 0 : e.hasFocus()) || false;
	}
	focus() {
		var e;
		(e = this.prosemirrorView) == null || e.focus();
	}
	/**
	 * Checks if the editor is currently editable, or if it's locked.
	 * @returns True if the editor is editable, false otherwise.
	 */
	get isEditable() {
		if (!this.editor._tiptapEditor) {
			if (!this.editor.headless) throw new Error("no editor, but also not headless?");
			return false;
		}
		return this.editor._tiptapEditor.isEditable === void 0 ? true : this.editor._tiptapEditor.isEditable;
	}
	/**
	 * Makes the editor editable or locks it, depending on the argument passed.
	 * @param editable True to make the editor editable, or false to lock it.
	 */
	set isEditable(e) {
		if (!this.editor._tiptapEditor) {
			if (!this.editor.headless) throw new Error("no editor, but also not headless?");
			return;
		}
		this.editor._tiptapEditor.options.editable !== e && this.editor._tiptapEditor.setEditable(e);
	}
	/**
	 * Undo the last action.
	 */
	undo() {
		const e = this.editor.getExtension("yUndo");
		if (e) return this.exec(e.undoCommand);
		const t = this.editor.getExtension("history");
		if (t) return this.exec(t.undoCommand);
		throw new Error("No undo plugin found");
	}
	/**
	 * Redo the last action.
	 */
	redo() {
		const e = this.editor.getExtension("yUndo");
		if (e) return this.exec(e.redoCommand);
		const t = this.editor.getExtension("history");
		if (t) return this.exec(t.redoCommand);
		throw new Error("No redo plugin found");
	}
}
function Nn(o, e, t, n = { updateSelection: true }) {
	let { from: s, to: r } = typeof e == "number" ? { from: e, to: e } : { from: e.from, to: e.to },
		i = true,
		l = true,
		a = "";
	if (
		(t.forEach((c) => {
			(c.check(), i && c.isText && c.marks.length === 0 ? (a += c.text) : (i = false), (l = l ? c.isBlock : false));
		}),
		s === r && l)
	) {
		const { parent: c } = o.doc.resolve(s);
		c.isTextblock && !c.type.spec.code && !c.childCount && ((s -= 1), (r += 1));
	}
	return (i ? o.insertText(a, s, r) : o.replaceWith(s, r, t), n.updateSelection && selectionToInsertionEnd(o, o.steps.length - 1, -1), true);
}
class Ln {
	constructor(e) {
		this.editor = e;
	}
	/**
	 * Insert a piece of content at the current cursor position.
	 *
	 * @param content can be a string, or array of partial inline content elements
	 */
	insertInlineContent(e, { updateSelection: t = false } = {}) {
		const n = T$1(e, this.editor.pmSchema);
		this.editor.transact((s) => {
			Nn(
				s,
				{
					from: s.selection.from,
					to: s.selection.to,
				},
				n,
				{
					updateSelection: t,
				},
			);
		});
	}
	/**
	 * Gets the active text styles at the text cursor position or at the end of the current selection if it's active.
	 */
	getActiveStyles() {
		return this.editor.transact((e) => {
			const t = {},
				n = e.selection.$to.marks();
			for (const s of n) {
				const r = this.editor.schema.styleSchema[s.type.name];
				if (!r) {
					// Links are not considered styles in blocknote
					s.type.name !== "link" && // "blocknoteIgnore" tagged marks (such as comments) are also not considered BlockNote "styles"
						!s.type.spec.blocknoteIgnore &&
						console.warn("mark not found in styleschema", s.type.name);
					continue;
				}
				r.propSchema === "boolean" ? (t[r.type] = true) : (t[r.type] = s.attrs.stringValue);
			}
			return t;
		});
	}
	/**
	 * Adds styles to the currently selected content.
	 * @param styles The styles to add.
	 */
	addStyles(e) {
		for (const [t, n] of Object.entries(e)) {
			const s = this.editor.schema.styleSchema[t];
			if (!s) throw new Error(`style ${t} not found in styleSchema`);
			if (s.propSchema === "boolean") this.editor._tiptapEditor.commands.setMark(t);
			else if (s.propSchema === "string")
				this.editor._tiptapEditor.commands.setMark(t, {
					stringValue: n,
				});
			else throw new O(s.propSchema);
		}
	}
	/**
	 * Removes styles from the currently selected content.
	 * @param styles The styles to remove.
	 */
	removeStyles(e) {
		for (const t of Object.keys(e)) this.editor._tiptapEditor.commands.unsetMark(t);
	}
	/**
	 * Toggles styles on the currently selected content.
	 * @param styles The styles to toggle.
	 */
	toggleStyles(e) {
		for (const [t, n] of Object.entries(e)) {
			const s = this.editor.schema.styleSchema[t];
			if (!s) throw new Error(`style ${t} not found in styleSchema`);
			if (s.propSchema === "boolean") this.editor._tiptapEditor.commands.toggleMark(t);
			else if (s.propSchema === "string")
				this.editor._tiptapEditor.commands.toggleMark(t, {
					stringValue: n,
				});
			else throw new O(s.propSchema);
		}
	}
	/**
	 * Gets the currently selected text.
	 */
	getSelectedText() {
		return this.editor.transact((e) => e.doc.textBetween(e.selection.from, e.selection.to));
	}
	/**
	 * Gets the URL of the last link in the current selection, or `undefined` if there are no links in the selection.
	 */
	getSelectedLinkUrl() {
		return this.editor._tiptapEditor.getAttributes("link").href;
	}
	/**
	 * Creates a new link to replace the selected content.
	 * @param url The link URL.
	 * @param text The text to display the link with.
	 */
	createLink(e, t) {
		if (e === "") return;
		const n = this.editor.pmSchema.mark("link", { href: e });
		this.editor.transact((s) => {
			const { from: r, to: i } = s.selection;
			t ? s.insertText(t, r, i).addMark(r, r + t.length, n) : s.setSelection(TextSelection.create(s.doc, i)).addMark(r, i, n);
		});
	}
}
function _n(o) {
	return findParentNodeClosestToPos(o.state.selection.$from, (e) => e.type.name === "tableCell" || e.type.name === "tableHeader") !== void 0;
}
function Oe(o, e) {
	var s;
	const t = e.nodes.hardBreak;
	let n = Fragment.empty;
	return (
		o.forEach((r) => {
			r.isTextblock && r.childCount > 0 ? ((n = n.append(r.content)), (n = n.addToEnd(t.create())))
			: r.isText ? (n = n.addToEnd(r))
			: r.isBlock && r.childCount > 0 && ((n = n.append(Oe(r.content, e))), (n = n.addToEnd(t.create())));
		}),
		((s = n.lastChild) == null ? void 0 : s.type) === t && (n = n.cut(0, n.size - 1)),
		n
	);
}
function Dn(o, e) {
	const t = [];
	return (
		o.forEach((n, s, r) => {
			r !== e && t.push(n);
		}),
		Fragment.from(t)
	);
}
function On(o, e) {
	const t = [];
	for (let n = 0; n < o.childCount; n++)
		if (o.child(n).type.name === "tableRow")
			if (t.length > 0 && t[t.length - 1].type.name === "table") {
				const s = t[t.length - 1],
					r = s.copy(s.content.addToEnd(o.child(n)));
				t[t.length - 1] = r;
			} else {
				const s = e.nodes.table.createChecked(void 0, o.child(n));
				t.push(s);
			}
		else t.push(o.child(n));
	return ((o = Fragment.from(t)), o);
}
function $n(o, e) {
	let t = Fragment.from(o.content);
	if (((t = On(t, e.state.schema)), _n(e))) {
		let n = false;
		if (
			(t.descendants((s) => {
				s.type.isInGroup("tableContent") && (n = true);
			}),
			!n && // is the content valid for a table paragraph?
				!e.state.schema.nodes.tableParagraph.validContent(t))
		)
			return new Slice(Oe(t, e.state.schema), 0, 0);
	}
	if (!Hn(t, e)) return new Slice(t, o.openStart, o.openEnd);
	for (let n = 0; n < t.childCount; n++)
		if (t.child(n).type.spec.group === "blockContent") {
			const s = [t.child(n)];
			if (n + 1 < t.childCount && t.child(n + 1).type.name === "blockGroup") {
				const i = t
					.child(n + 1)
					.child(0)
					.child(0);
				(i.type.name === "bulletListItem" || i.type.name === "numberedListItem" || i.type.name === "checkListItem") && (s.push(t.child(n + 1)), (t = Dn(t, n + 1)));
			}
			const r = e.state.schema.nodes.blockContainer.createChecked(void 0, s);
			t = t.replaceChild(n, r);
		}
	return new Slice(t, o.openStart, o.openEnd);
}
function Hn(o, e) {
	var r, i;
	const t = o.childCount === 1,
		n = ((r = o.firstChild) == null ? void 0 : r.type.spec.content) === "inline*",
		s = ((i = o.firstChild) == null ? void 0 : i.type.spec.content) === "tableRow+";
	if (t) {
		if (n) return false;
		if (s) {
			const l = Tt$2(e.state);
			if (l.isBlockContainer) return !(l.blockContent.node.type.spec.content === "tableRow+");
		}
	}
	return true;
}
const Fn = {
	enableInputRules: true,
	enablePasteRules: true,
	enableCoreExtensions: false,
};
class $e extends f {
	constructor(t) {
		var c, d, u, p, h, f, m, b, g, x;
		super();
		/**
		 * The underlying prosemirror schema
		 */
		k(this, "pmSchema");
		k(this, "_tiptapEditor");
		/**
		 * Used by React to store a reference to an `ElementRenderer` helper utility to make sure we can render React elements
		 * in the correct context (used by `ReactRenderUtil`)
		 */
		k(this, "elementRenderer", null);
		/**
		 * Cache of all blocks. This makes sure we don't have to "recompute" blocks if underlying Prosemirror Nodes haven't changed.
		 * This is especially useful when we want to keep track of the same block across multiple operations,
		 * with this cache, blocks stay the same object reference (referential equality with ===).
		 */
		k(this, "blockCache", /* @__PURE__ */ new WeakMap());
		/**
		 * The dictionary contains translations for the editor.
		 */
		k(this, "dictionary");
		/**
		 * The schema of the editor. The schema defines which Blocks, InlineContent, and Styles are available in the editor.
		 */
		k(this, "schema");
		k(this, "blockImplementations");
		k(this, "inlineContentImplementations");
		k(this, "styleImplementations");
		/**
		 * The `uploadFile` method is what the editor uses when files need to be uploaded (for example when selecting an image to upload).
		 * This method should set when creating the editor as this is application-specific.
		 *
		 * `undefined` means the application doesn't support file uploads.
		 *
		 * @param file The file that should be uploaded.
		 * @returns The URL of the uploaded file OR an object containing props that should be set on the file block (such as an id)
		 */
		k(this, "uploadFile");
		k(this, "onUploadStartCallbacks", []);
		k(this, "onUploadEndCallbacks", []);
		k(this, "resolveFileUrl");
		/**
		 * Editor settings
		 */
		k(this, "settings");
		// Manager instances
		k(this, "_blockManager");
		k(this, "_eventManager");
		k(this, "_exportManager");
		k(this, "_extensionManager");
		k(this, "_selectionManager");
		k(this, "_stateManager");
		k(this, "_styleManager");
		/**
		 * Remove extension(s) from the editor
		 */
		k(this, "unregisterExtension", (...t) => this._extensionManager.unregisterExtension(...t));
		/**
		 * Register extension(s) to the editor
		 */
		k(this, "registerExtension", (...t) => this._extensionManager.registerExtension(...t));
		/**
		 * Get an extension from the editor
		 */
		k(this, "getExtension", (...t) => this._extensionManager.getExtension(...t));
		/**
		 * Mount the editor to a DOM element.
		 *
		 * @warning Not needed to call manually when using React, use BlockNoteView to take care of mounting
		 */
		k(this, "mount", (t) => {
			this._tiptapEditor.mount({ mount: t });
		});
		/**
		 * Unmount the editor from the DOM element it is bound to
		 */
		k(this, "unmount", () => {
			this._tiptapEditor.unmount();
		});
		((this.options = t),
			(this.dictionary = t.dictionary || i),
			(this.settings = {
				tables: {
					splitCells: ((c = t == null ? void 0 : t.tables) == null ? void 0 : c.splitCells) ?? false,
					cellBackgroundColor: ((d = t == null ? void 0 : t.tables) == null ? void 0 : d.cellBackgroundColor) ?? false,
					cellTextColor: ((u = t == null ? void 0 : t.tables) == null ? void 0 : u.cellTextColor) ?? false,
					headers: ((p = t == null ? void 0 : t.tables) == null ? void 0 : p.headers) ?? false,
				},
			}));
		const n = {
			defaultStyles: true,
			schema: t.schema || h$1.create(),
			...t,
			placeholders: {
				...this.dictionary.placeholders,
				...t.placeholders,
			},
		};
		if (
			((this.schema = n.schema),
			(this.blockImplementations = n.schema.blockSpecs),
			(this.inlineContentImplementations = n.schema.inlineContentSpecs),
			(this.styleImplementations = n.schema.styleSpecs),
			n.uploadFile)
		) {
			const y = n.uploadFile;
			this.uploadFile = async (I, w) => {
				this.onUploadStartCallbacks.forEach((A) => A.apply(this, [w]));
				try {
					return await y(I, w);
				} finally {
					this.onUploadEndCallbacks.forEach((A) => A.apply(this, [w]));
				}
			};
		}
		((this.resolveFileUrl = n.resolveFileUrl), (this._eventManager = new Eo(this)), (this._extensionManager = new En(this, n)));
		const s = this._extensionManager.getTiptapExtensions(),
			r = this._extensionManager.hasExtension("ySync") || this._extensionManager.hasExtension("liveblocksExtension");
		r && n.initialContent && console.warn("When using Collaboration, initialContent might cause conflicts, because changes should come from the collaboration provider");
		const i$1 = {
			...Fn,
			...n._tiptapOptions,
			element: null,
			autofocus: n.autofocus ?? false,
			extensions: s,
			editorProps: {
				...((h = n._tiptapOptions) == null ? void 0 : h.editorProps),
				attributes: {
					// As of TipTap v2.5.0 the tabIndex is removed when the editor is not
					// editable, so you can't focus it. We want to revert this as we have
					// UI behaviour that relies on it.
					tabIndex: "0",
					...((m = (f = n._tiptapOptions) == null ? void 0 : f.editorProps) == null ? void 0 : m.attributes),
					...((b = n.domAttributes) == null ? void 0 : b.editor),
					class: D$2("bn-editor", n.defaultStyles ? "bn-default-styles" : "", ((x = (g = n.domAttributes) == null ? void 0 : g.editor) == null ? void 0 : x.class) || ""),
				},
				transformPasted: $n,
			},
		};
		try {
			const y =
				n.initialContent ||
				(r ?
					[
						{
							type: "paragraph",
							id: "initialBlockId",
						},
					]
				:	[
						{
							type: "paragraph",
							id: Q$2.options.generateID(),
						},
					]);
			if (!Array.isArray(y) || y.length === 0) throw new Error("initialContent must be a non-empty array of blocks, received: " + y);
			const I = getSchema(i$1.extensions),
				w = y.map((He) => bt$1(He, I, this.schema.styleSchema).toJSON()),
				A = createDocument(
					{
						type: "doc",
						content: [
							{
								type: "blockGroup",
								content: w,
							},
						],
					},
					I,
					i$1.parseOptions,
				);
			((this._tiptapEditor = new Editor({
				...i$1,
				content: A.toJSON(),
			})),
				(this.pmSchema = this._tiptapEditor.schema));
		} catch (y) {
			throw new Error("Error creating document from blocks passed as `initialContent`", { cause: y });
		}
		let l;
		const a = this.pmSchema.nodes.doc.createAndFill;
		((this.pmSchema.nodes.doc.createAndFill = (...y) => {
			if (l) return l;
			const I = a.apply(this.pmSchema.nodes.doc, y),
				w = JSON.parse(JSON.stringify(I.toJSON()));
			return ((w.content[0].content[0].attrs.id = "initialBlockId"), (l = Node$1.fromJSON(this.pmSchema, w)), l);
		}),
			(this.pmSchema.cached.blockNoteEditor = this),
			(this._blockManager = new xo(this)),
			(this._exportManager = new $o(this)),
			(this._selectionManager = new In(this)),
			(this._stateManager = new An(this)),
			(this._styleManager = new Ln(this)),
			this.emit("create"));
	}
	static create(t) {
		return new $e(t ?? {});
	}
	/**
	 * BlockNote extensions that are added to the editor, keyed by the extension key
	 */
	get extensions() {
		return this._extensionManager.getExtensions();
	}
	/**
	 * Execute a prosemirror command. This is mostly for backwards compatibility with older code.
	 *
	 * @note You should prefer the {@link transact} method when possible, as it will automatically handle the dispatching of the transaction and work across blocknote transactions.
	 *
	 * @example
	 * ```ts
	 * editor.exec((state, dispatch, view) => {
	 *   dispatch(state.tr.insertText("Hello, world!"));
	 * });
	 * ```
	 */
	exec(t) {
		return this._stateManager.exec(t);
	}
	/**
	 * Check if a command can be executed. A command should return `false` if it is not valid in the current state.
	 *
	 * @example
	 * ```ts
	 * if (editor.canExec(command)) {
	 *   // show button
	 * } else {
	 *   // hide button
	 * }
	 * ```
	 */
	canExec(t) {
		return this._stateManager.canExec(t);
	}
	/**
	 * Execute a function within a "blocknote transaction".
	 * All changes to the editor within the transaction will be grouped together, so that
	 * we can dispatch them as a single operation (thus creating only a single undo step)
	 *
	 * @note There is no need to dispatch the transaction, as it will be automatically dispatched when the callback is complete.
	 *
	 * @example
	 * ```ts
	 * // All changes to the editor will be grouped together
	 * editor.transact((tr) => {
	 *   tr.insertText("Hello, world!");
	 * // These two operations will be grouped together in a single undo step
	 *   editor.transact((tr) => {
	 *     tr.insertText("Hello, world!");
	 *   });
	 * });
	 * ```
	 */
	transact(t) {
		return this._stateManager.transact(t);
	}
	/**
	 * Get the underlying prosemirror state
	 * @note Prefer using `editor.transact` to read the current editor state, as that will ensure the state is up to date
	 * @see https://prosemirror.net/docs/ref/#state.EditorState
	 */
	get prosemirrorState() {
		return this._stateManager.prosemirrorState;
	}
	/**
	 * Get the underlying prosemirror view
	 * @see https://prosemirror.net/docs/ref/#view.EditorView
	 */
	get prosemirrorView() {
		return this._stateManager.prosemirrorView;
	}
	get domElement() {
		var t;
		if (!this.headless) return (t = this.prosemirrorView) == null ? void 0 : t.dom;
	}
	isFocused() {
		var t;
		return this.headless ? false : ((t = this.prosemirrorView) == null ? void 0 : t.hasFocus()) || false;
	}
	get headless() {
		return !this._tiptapEditor.isInitialized;
	}
	/**
	 * Focus on the editor
	 */
	focus() {
		this.headless || this.prosemirrorView.focus();
	}
	/**
	 * Blur the editor
	 */
	blur() {
		var t;
		this.headless || (t = this.domElement) == null || t.blur();
	}
	// TODO move to extension
	onUploadStart(t) {
		return (
			this.onUploadStartCallbacks.push(t),
			() => {
				const n = this.onUploadStartCallbacks.indexOf(t);
				n > -1 && this.onUploadStartCallbacks.splice(n, 1);
			}
		);
	}
	onUploadEnd(t) {
		return (
			this.onUploadEndCallbacks.push(t),
			() => {
				const n = this.onUploadEndCallbacks.indexOf(t);
				n > -1 && this.onUploadEndCallbacks.splice(n, 1);
			}
		);
	}
	/**
	 * @deprecated, use `editor.document` instead
	 */
	get topLevelBlocks() {
		return this.document;
	}
	/**
	 * Gets a snapshot of all top-level (non-nested) blocks in the editor.
	 * @returns A snapshot of all top-level (non-nested) blocks in the editor.
	 */
	get document() {
		return this._blockManager.document;
	}
	/**
	 * Gets a snapshot of an existing block from the editor.
	 * @param blockIdentifier The identifier of an existing block that should be
	 * retrieved.
	 * @returns The block that matches the identifier, or `undefined` if no
	 * matching block was found.
	 */
	getBlock(t) {
		return this._blockManager.getBlock(t);
	}
	/**
	 * Gets a snapshot of the previous sibling of an existing block from the
	 * editor.
	 * @param blockIdentifier The identifier of an existing block for which the
	 * previous sibling should be retrieved.
	 * @returns The previous sibling of the block that matches the identifier.
	 * `undefined` if no matching block was found, or it's the first child/block
	 * in the document.
	 */
	getPrevBlock(t) {
		return this._blockManager.getPrevBlock(t);
	}
	/**
	 * Gets a snapshot of the next sibling of an existing block from the editor.
	 * @param blockIdentifier The identifier of an existing block for which the
	 * next sibling should be retrieved.
	 * @returns The next sibling of the block that matches the identifier.
	 * `undefined` if no matching block was found, or it's the last child/block in
	 * the document.
	 */
	getNextBlock(t) {
		return this._blockManager.getNextBlock(t);
	}
	/**
	 * Gets a snapshot of the parent of an existing block from the editor.
	 * @param blockIdentifier The identifier of an existing block for which the
	 * parent should be retrieved.
	 * @returns The parent of the block that matches the identifier. `undefined`
	 * if no matching block was found, or the block isn't nested.
	 */
	getParentBlock(t) {
		return this._blockManager.getParentBlock(t);
	}
	/**
	 * Traverses all blocks in the editor depth-first, and executes a callback for each.
	 * @param callback The callback to execute for each block. Returning `false` stops the traversal.
	 * @param reverse Whether the blocks should be traversed in reverse order.
	 */
	forEachBlock(t, n = false) {
		this._blockManager.forEachBlock(t, n);
	}
	/**
	 * Executes a callback whenever the editor's contents change.
	 * @param callback The callback to execute.
	 *
	 * @deprecated use {@link BlockNoteEditor.onChange} instead
	 */
	onEditorContentChange(t) {
		this._tiptapEditor.on("update", t);
	}
	/**
	 * Executes a callback whenever the editor's selection changes.
	 * @param callback The callback to execute.
	 *
	 * @deprecated use `onSelectionChange` instead
	 */
	onEditorSelectionChange(t) {
		this._tiptapEditor.on("selectionUpdate", t);
	}
	/**
	 * Executes a callback before any change is applied to the editor, allowing you to cancel the change.
	 * @param callback The callback to execute.
	 * @returns A function to remove the callback.
	 */
	onBeforeChange(t) {
		return this._extensionManager.getExtension(Do$1).subscribe(t);
	}
	/**
	 * Gets a snapshot of the current text cursor position.
	 * @returns A snapshot of the current text cursor position.
	 */
	getTextCursorPosition() {
		return this._selectionManager.getTextCursorPosition();
	}
	/**
	 * Sets the text cursor position to the start or end of an existing block. Throws an error if the target block could
	 * not be found.
	 * @param targetBlock The identifier of an existing block that the text cursor should be moved to.
	 * @param placement Whether the text cursor should be placed at the start or end of the block.
	 */
	setTextCursorPosition(t, n = "start") {
		return this._selectionManager.setTextCursorPosition(t, n);
	}
	/**
	 * Gets a snapshot of the current selection. This contains all blocks (included nested blocks)
	 * that the selection spans across.
	 *
	 * If the selection starts / ends halfway through a block, the returned data will contain the entire block.
	 */
	getSelection() {
		return this._selectionManager.getSelection();
	}
	/**
	 * Gets a snapshot of the current selection. This contains all blocks (included nested blocks)
	 * that the selection spans across.
	 *
	 * If the selection starts / ends halfway through a block, the returned block will be
	 * only the part of the block that is included in the selection.
	 */
	getSelectionCutBlocks(t = false) {
		return this._selectionManager.getSelectionCutBlocks(t);
	}
	/**
	 * Sets the selection to a range of blocks.
	 * @param startBlock The identifier of the block that should be the start of the selection.
	 * @param endBlock The identifier of the block that should be the end of the selection.
	 */
	setSelection(t, n) {
		return this._selectionManager.setSelection(t, n);
	}
	/**
	 * Checks if the editor is currently editable, or if it's locked.
	 * @returns True if the editor is editable, false otherwise.
	 */
	get isEditable() {
		return this._stateManager.isEditable;
	}
	/**
	 * Makes the editor editable or locks it, depending on the argument passed.
	 * @param editable True to make the editor editable, or false to lock it.
	 */
	set isEditable(t) {
		this._stateManager.isEditable = t;
	}
	/**
	 * Inserts new blocks into the editor. If a block's `id` is undefined, BlockNote generates one automatically. Throws an
	 * error if the reference block could not be found.
	 * @param blocksToInsert An array of partial blocks that should be inserted.
	 * @param referenceBlock An identifier for an existing block, at which the new blocks should be inserted.
	 * @param placement Whether the blocks should be inserted just before, just after, or nested inside the
	 * `referenceBlock`.
	 */
	insertBlocks(t, n, s = "before") {
		return this._blockManager.insertBlocks(t, n, s);
	}
	/**
	 * Updates an existing block in the editor. Since updatedBlock is a PartialBlock object, some fields might not be
	 * defined. These undefined fields are kept as-is from the existing block. Throws an error if the block to update could
	 * not be found.
	 * @param blockToUpdate The block that should be updated.
	 * @param update A partial block which defines how the existing block should be changed.
	 */
	updateBlock(t, n) {
		return this._blockManager.updateBlock(t, n);
	}
	/**
	 * Removes existing blocks from the editor. Throws an error if any of the blocks could not be found.
	 * @param blocksToRemove An array of identifiers for existing blocks that should be removed.
	 */
	removeBlocks(t) {
		return this._blockManager.removeBlocks(t);
	}
	/**
	 * Replaces existing blocks in the editor with new blocks. If the blocks that should be removed are not adjacent or
	 * are at different nesting levels, `blocksToInsert` will be inserted at the position of the first block in
	 * `blocksToRemove`. Throws an error if any of the blocks to remove could not be found.
	 * @param blocksToRemove An array of blocks that should be replaced.
	 * @param blocksToInsert An array of partial blocks to replace the old ones with.
	 */
	replaceBlocks(t, n) {
		return this._blockManager.replaceBlocks(t, n);
	}
	/**
	 * Undo the last action.
	 */
	undo() {
		return this._stateManager.undo();
	}
	/**
	 * Redo the last action.
	 */
	redo() {
		return this._stateManager.redo();
	}
	/**
	 * Insert a piece of content at the current cursor position.
	 *
	 * @param content can be a string, or array of partial inline content elements
	 */
	insertInlineContent(t, { updateSelection: n = false } = {}) {
		this._styleManager.insertInlineContent(t, { updateSelection: n });
	}
	/**
	 * Gets the active text styles at the text cursor position or at the end of the current selection if it's active.
	 */
	getActiveStyles() {
		return this._styleManager.getActiveStyles();
	}
	/**
	 * Adds styles to the currently selected content.
	 * @param styles The styles to add.
	 */
	addStyles(t) {
		this._styleManager.addStyles(t);
	}
	/**
	 * Removes styles from the currently selected content.
	 * @param styles The styles to remove.
	 */
	removeStyles(t) {
		this._styleManager.removeStyles(t);
	}
	/**
	 * Toggles styles on the currently selected content.
	 * @param styles The styles to toggle.
	 */
	toggleStyles(t) {
		this._styleManager.toggleStyles(t);
	}
	/**
	 * Gets the currently selected text.
	 */
	getSelectedText() {
		return this._styleManager.getSelectedText();
	}
	/**
	 * Gets the URL of the last link in the current selection, or `undefined` if there are no links in the selection.
	 */
	getSelectedLinkUrl() {
		return this._styleManager.getSelectedLinkUrl();
	}
	/**
	 * Creates a new link to replace the selected content.
	 * @param url The link URL.
	 * @param text The text to display the link with.
	 */
	createLink(t, n) {
		this._styleManager.createLink(t, n);
	}
	/**
	 * Checks if the block containing the text cursor can be nested.
	 */
	canNestBlock() {
		return this._blockManager.canNestBlock();
	}
	/**
	 * Nests the block containing the text cursor into the block above it.
	 */
	nestBlock() {
		this._blockManager.nestBlock();
	}
	/**
	 * Checks if the block containing the text cursor is nested.
	 */
	canUnnestBlock() {
		return this._blockManager.canUnnestBlock();
	}
	/**
	 * Lifts the block containing the text cursor out of its parent.
	 */
	unnestBlock() {
		this._blockManager.unnestBlock();
	}
	/**
	 * Moves the selected blocks up. If the previous block has children, moves
	 * them to the end of its children. If there is no previous block, but the
	 * current blocks share a common parent, moves them out of & before it.
	 */
	moveBlocksUp() {
		return this._blockManager.moveBlocksUp();
	}
	/**
	 * Moves the selected blocks down. If the next block has children, moves
	 * them to the start of its children. If there is no next block, but the
	 * current blocks share a common parent, moves them out of & after it.
	 */
	moveBlocksDown() {
		return this._blockManager.moveBlocksDown();
	}
	/**
	 * Exports blocks into a simplified HTML string. To better conform to HTML standards, children of blocks which aren't list
	 * items are un-nested in the output HTML.
	 *
	 * @param blocks An array of blocks that should be serialized into HTML.
	 * @returns The blocks, serialized as an HTML string.
	 */
	blocksToHTMLLossy(t = this.document) {
		return this._exportManager.blocksToHTMLLossy(t);
	}
	/**
	 * Serializes blocks into an HTML string in the format that would normally be rendered by the editor.
	 *
	 * Use this method if you want to server-side render HTML (for example, a blog post that has been edited in BlockNote)
	 * and serve it to users without loading the editor on the client (i.e.: displaying the blog post)
	 *
	 * @param blocks An array of blocks that should be serialized into HTML.
	 * @returns The blocks, serialized as an HTML string.
	 */
	blocksToFullHTML(t = this.document) {
		return this._exportManager.blocksToFullHTML(t);
	}
	/**
	 * Parses blocks from an HTML string. Tries to create `Block` objects out of any HTML block-level elements, and
	 * `InlineNode` objects from any HTML inline elements, though not all element types are recognized. If BlockNote
	 * doesn't recognize an HTML element's tag, it will parse it as a paragraph or plain text.
	 * @param html The HTML string to parse blocks from.
	 * @returns The blocks parsed from the HTML string.
	 */
	tryParseHTMLToBlocks(t) {
		return this._exportManager.tryParseHTMLToBlocks(t);
	}
	/**
	 * Serializes blocks into a Markdown string. The output is simplified as Markdown does not support all features of
	 * BlockNote - children of blocks which aren't list items are un-nested and certain styles are removed.
	 * @param blocks An array of blocks that should be serialized into Markdown.
	 * @returns The blocks, serialized as a Markdown string.
	 */
	blocksToMarkdownLossy(t = this.document) {
		return this._exportManager.blocksToMarkdownLossy(t);
	}
	/**
	 * Creates a list of blocks from a Markdown string. Tries to create `Block` and `InlineNode` objects based on
	 * Markdown syntax, though not all symbols are recognized. If BlockNote doesn't recognize a symbol, it will parse it
	 * as text.
	 * @param markdown The Markdown string to parse blocks from.
	 * @returns The blocks parsed from the Markdown string.
	 */
	tryParseMarkdownToBlocks(t) {
		return this._exportManager.tryParseMarkdownToBlocks(t);
	}
	/**
	 * A callback function that runs whenever the editor's contents change.
	 *
	 * @param callback The callback to execute.
	 * @returns A function to remove the callback.
	 */
	onChange(t, n) {
		return this._eventManager.onChange(t, n);
	}
	/**
	 * A callback function that runs whenever the text cursor position or selection changes.
	 *
	 * @param callback The callback to execute.
	 * @returns A function to remove the callback.
	 */
	onSelectionChange(t, n) {
		return this._eventManager.onSelectionChange(t, n);
	}
	/**
	 * A callback function that runs when the editor has been mounted.
	 *
	 * This can be useful for plugins to initialize themselves after the editor has been mounted.
	 *
	 * @param callback The callback to execute.
	 * @returns A function to remove the callback.
	 */
	onMount(t) {
		this._eventManager.onMount(t);
	}
	/**
	 * A callback function that runs when the editor has been unmounted.
	 *
	 * This can be useful for plugins to clean up themselves after the editor has been unmounted.
	 *
	 * @param callback The callback to execute.
	 * @returns A function to remove the callback.
	 */
	onUnmount(t) {
		this._eventManager.onUnmount(t);
	}
	/**
	 * Gets the bounding box of the current selection.
	 * @returns The bounding box of the current selection.
	 */
	getSelectionBoundingBox() {
		return this._selectionManager.getSelectionBoundingBox();
	}
	get isEmpty() {
		const t = this.document;
		return t.length === 0 || (t.length === 1 && t[0].type === "paragraph" && t[0].content.length === 0);
	}
	/**
	 * Paste HTML into the editor. Defaults to converting HTML to BlockNote HTML.
	 * @param html The HTML to paste.
	 * @param raw Whether to paste the HTML as is, or to convert it to BlockNote HTML.
	 */
	pasteHTML(t, n = false) {
		this._exportManager.pasteHTML(t, n);
	}
	/**
	 * Paste text into the editor. Defaults to interpreting text as markdown.
	 * @param text The text to paste.
	 */
	pasteText(t) {
		return this._exportManager.pasteText(t);
	}
	/**
	 * Paste markdown into the editor.
	 * @param markdown The markdown to paste.
	 */
	pasteMarkdown(t) {
		return this._exportManager.pasteMarkdown(t);
	}
}

// src/components/blocknote-editor/schemas.ts
var defaultSchema = h$1.create({
	blockSpecs: {
		...Po$2,
	},
	styleSpecs: {
		...$n$1,
	},
});
var { heading: _heading, table: _table, image: _image, video: _video, audio: _audio, file: _file, ...conservativeBlockSpecs } = Po$2;
var conservativeSchema = h$1.create({
	blockSpecs: conservativeBlockSpecs,
	styleSpecs: {
		...$n$1,
	},
});
var courseContentSchema = h$1.create({
	blockSpecs: {
		...Po$2,
	},
	styleSpecs: {
		...$n$1,
	},
});

function r(e) {
	var t,
		f,
		n = "";
	if ("string" == typeof e || "number" == typeof e) n += e;
	else if ("object" == typeof e)
		if (Array.isArray(e)) {
			var o = e.length;
			for (t = 0; t < o; t++) e[t] && (f = r(e[t])) && (n && (n += " "), (n += f));
		} else for (f in e) e[f] && (n && (n += " "), (n += f));
	return n;
}
function clsx() {
	for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r(e)) && (n && (n += " "), (n += t));
	return n;
}

const CLASS_PART_SEPARATOR = "-";
const createClassGroupUtils = (config) => {
	const classMap = createClassMap(config);
	const { conflictingClassGroups, conflictingClassGroupModifiers } = config;
	const getClassGroupId = (className) => {
		const classParts = className.split(CLASS_PART_SEPARATOR);
		// Classes like `-inset-1` produce an empty string as first classPart. We assume that classes for negative values are used correctly and remove it from classParts.
		if (classParts[0] === "" && classParts.length !== 1) {
			classParts.shift();
		}
		return getGroupRecursive(classParts, classMap) || getGroupIdForArbitraryProperty(className);
	};
	const getConflictingClassGroupIds = (classGroupId, hasPostfixModifier) => {
		const conflicts = conflictingClassGroups[classGroupId] || [];
		if (hasPostfixModifier && conflictingClassGroupModifiers[classGroupId]) {
			return [...conflicts, ...conflictingClassGroupModifiers[classGroupId]];
		}
		return conflicts;
	};
	return {
		getClassGroupId,
		getConflictingClassGroupIds,
	};
};
const getGroupRecursive = (classParts, classPartObject) => {
	if (classParts.length === 0) {
		return classPartObject.classGroupId;
	}
	const currentClassPart = classParts[0];
	const nextClassPartObject = classPartObject.nextPart.get(currentClassPart);
	const classGroupFromNextClassPart = nextClassPartObject ? getGroupRecursive(classParts.slice(1), nextClassPartObject) : undefined;
	if (classGroupFromNextClassPart) {
		return classGroupFromNextClassPart;
	}
	if (classPartObject.validators.length === 0) {
		return undefined;
	}
	const classRest = classParts.join(CLASS_PART_SEPARATOR);
	return classPartObject.validators.find(({ validator }) => validator(classRest))?.classGroupId;
};
const arbitraryPropertyRegex = /^\[(.+)\]$/;
const getGroupIdForArbitraryProperty = (className) => {
	if (arbitraryPropertyRegex.test(className)) {
		const arbitraryPropertyClassName = arbitraryPropertyRegex.exec(className)[1];
		const property = arbitraryPropertyClassName?.substring(0, arbitraryPropertyClassName.indexOf(":"));
		if (property) {
			// I use two dots here because one dot is used as prefix for class groups in plugins
			return "arbitrary.." + property;
		}
	}
};
/**
 * Exported for testing only
 */
const createClassMap = (config) => {
	const { theme, prefix } = config;
	const classMap = {
		nextPart: new Map(),
		validators: [],
	};
	const prefixedClassGroupEntries = getPrefixedClassGroupEntries(Object.entries(config.classGroups), prefix);
	prefixedClassGroupEntries.forEach(([classGroupId, classGroup]) => {
		processClassesRecursively(classGroup, classMap, classGroupId, theme);
	});
	return classMap;
};
const processClassesRecursively = (classGroup, classPartObject, classGroupId, theme) => {
	classGroup.forEach((classDefinition) => {
		if (typeof classDefinition === "string") {
			const classPartObjectToEdit = classDefinition === "" ? classPartObject : getPart(classPartObject, classDefinition);
			classPartObjectToEdit.classGroupId = classGroupId;
			return;
		}
		if (typeof classDefinition === "function") {
			if (isThemeGetter(classDefinition)) {
				processClassesRecursively(classDefinition(theme), classPartObject, classGroupId, theme);
				return;
			}
			classPartObject.validators.push({
				validator: classDefinition,
				classGroupId,
			});
			return;
		}
		Object.entries(classDefinition).forEach(([key, classGroup]) => {
			processClassesRecursively(classGroup, getPart(classPartObject, key), classGroupId, theme);
		});
	});
};
const getPart = (classPartObject, path) => {
	let currentClassPartObject = classPartObject;
	path.split(CLASS_PART_SEPARATOR).forEach((pathPart) => {
		if (!currentClassPartObject.nextPart.has(pathPart)) {
			currentClassPartObject.nextPart.set(pathPart, {
				nextPart: new Map(),
				validators: [],
			});
		}
		currentClassPartObject = currentClassPartObject.nextPart.get(pathPart);
	});
	return currentClassPartObject;
};
const isThemeGetter = (func) => func.isThemeGetter;
const getPrefixedClassGroupEntries = (classGroupEntries, prefix) => {
	if (!prefix) {
		return classGroupEntries;
	}
	return classGroupEntries.map(([classGroupId, classGroup]) => {
		const prefixedClassGroup = classGroup.map((classDefinition) => {
			if (typeof classDefinition === "string") {
				return prefix + classDefinition;
			}
			if (typeof classDefinition === "object") {
				return Object.fromEntries(Object.entries(classDefinition).map(([key, value]) => [prefix + key, value]));
			}
			return classDefinition;
		});
		return [classGroupId, prefixedClassGroup];
	});
};

// LRU cache inspired from hashlru (https://github.com/dominictarr/hashlru/blob/v1.0.4/index.js) but object replaced with Map to improve performance
const createLruCache = (maxCacheSize) => {
	if (maxCacheSize < 1) {
		return {
			get: () => undefined,
			set: () => {},
		};
	}
	let cacheSize = 0;
	let cache = new Map();
	let previousCache = new Map();
	const update = (key, value) => {
		cache.set(key, value);
		cacheSize++;
		if (cacheSize > maxCacheSize) {
			cacheSize = 0;
			previousCache = cache;
			cache = new Map();
		}
	};
	return {
		get(key) {
			let value = cache.get(key);
			if (value !== undefined) {
				return value;
			}
			if ((value = previousCache.get(key)) !== undefined) {
				update(key, value);
				return value;
			}
		},
		set(key, value) {
			if (cache.has(key)) {
				cache.set(key, value);
			} else {
				update(key, value);
			}
		},
	};
};
const IMPORTANT_MODIFIER = "!";
const createParseClassName = (config) => {
	const { separator, experimentalParseClassName } = config;
	const isSeparatorSingleCharacter = separator.length === 1;
	const firstSeparatorCharacter = separator[0];
	const separatorLength = separator.length;
	// parseClassName inspired by https://github.com/tailwindlabs/tailwindcss/blob/v3.2.2/src/util/splitAtTopLevelOnly.js
	const parseClassName = (className) => {
		const modifiers = [];
		let bracketDepth = 0;
		let modifierStart = 0;
		let postfixModifierPosition;
		for (let index = 0; index < className.length; index++) {
			let currentCharacter = className[index];
			if (bracketDepth === 0) {
				if (currentCharacter === firstSeparatorCharacter && (isSeparatorSingleCharacter || className.slice(index, index + separatorLength) === separator)) {
					modifiers.push(className.slice(modifierStart, index));
					modifierStart = index + separatorLength;
					continue;
				}
				if (currentCharacter === "/") {
					postfixModifierPosition = index;
					continue;
				}
			}
			if (currentCharacter === "[") {
				bracketDepth++;
			} else if (currentCharacter === "]") {
				bracketDepth--;
			}
		}
		const baseClassNameWithImportantModifier = modifiers.length === 0 ? className : className.substring(modifierStart);
		const hasImportantModifier = baseClassNameWithImportantModifier.startsWith(IMPORTANT_MODIFIER);
		const baseClassName = hasImportantModifier ? baseClassNameWithImportantModifier.substring(1) : baseClassNameWithImportantModifier;
		const maybePostfixModifierPosition = postfixModifierPosition && postfixModifierPosition > modifierStart ? postfixModifierPosition - modifierStart : undefined;
		return {
			modifiers,
			hasImportantModifier,
			baseClassName,
			maybePostfixModifierPosition,
		};
	};
	if (experimentalParseClassName) {
		return (className) =>
			experimentalParseClassName({
				className,
				parseClassName,
			});
	}
	return parseClassName;
};
/**
 * Sorts modifiers according to following schema:
 * - Predefined modifiers are sorted alphabetically
 * - When an arbitrary variant appears, it must be preserved which modifiers are before and after it
 */
const sortModifiers = (modifiers) => {
	if (modifiers.length <= 1) {
		return modifiers;
	}
	const sortedModifiers = [];
	let unsortedModifiers = [];
	modifiers.forEach((modifier) => {
		const isArbitraryVariant = modifier[0] === "[";
		if (isArbitraryVariant) {
			sortedModifiers.push(...unsortedModifiers.sort(), modifier);
			unsortedModifiers = [];
		} else {
			unsortedModifiers.push(modifier);
		}
	});
	sortedModifiers.push(...unsortedModifiers.sort());
	return sortedModifiers;
};
const createConfigUtils = (config) => ({
	cache: createLruCache(config.cacheSize),
	parseClassName: createParseClassName(config),
	...createClassGroupUtils(config),
});
const SPLIT_CLASSES_REGEX = /\s+/;
const mergeClassList = (classList, configUtils) => {
	const { parseClassName, getClassGroupId, getConflictingClassGroupIds } = configUtils;
	/**
	 * Set of classGroupIds in following format:
	 * `{importantModifier}{variantModifiers}{classGroupId}`
	 * @example 'float'
	 * @example 'hover:focus:bg-color'
	 * @example 'md:!pr'
	 */
	const classGroupsInConflict = [];
	const classNames = classList.trim().split(SPLIT_CLASSES_REGEX);
	let result = "";
	for (let index = classNames.length - 1; index >= 0; index -= 1) {
		const originalClassName = classNames[index];
		const { modifiers, hasImportantModifier, baseClassName, maybePostfixModifierPosition } = parseClassName(originalClassName);
		let hasPostfixModifier = Boolean(maybePostfixModifierPosition);
		let classGroupId = getClassGroupId(hasPostfixModifier ? baseClassName.substring(0, maybePostfixModifierPosition) : baseClassName);
		if (!classGroupId) {
			if (!hasPostfixModifier) {
				// Not a Tailwind class
				result = originalClassName + (result.length > 0 ? " " + result : result);
				continue;
			}
			classGroupId = getClassGroupId(baseClassName);
			if (!classGroupId) {
				// Not a Tailwind class
				result = originalClassName + (result.length > 0 ? " " + result : result);
				continue;
			}
			hasPostfixModifier = false;
		}
		const variantModifier = sortModifiers(modifiers).join(":");
		const modifierId = hasImportantModifier ? variantModifier + IMPORTANT_MODIFIER : variantModifier;
		const classId = modifierId + classGroupId;
		if (classGroupsInConflict.includes(classId)) {
			// Tailwind class omitted due to conflict
			continue;
		}
		classGroupsInConflict.push(classId);
		const conflictGroups = getConflictingClassGroupIds(classGroupId, hasPostfixModifier);
		for (let i = 0; i < conflictGroups.length; ++i) {
			const group = conflictGroups[i];
			classGroupsInConflict.push(modifierId + group);
		}
		// Tailwind class not in conflict
		result = originalClassName + (result.length > 0 ? " " + result : result);
	}
	return result;
};

/**
 * The code in this file is copied from https://github.com/lukeed/clsx and modified to suit the needs of tailwind-merge better.
 *
 * Specifically:
 * - Runtime code from https://github.com/lukeed/clsx/blob/v1.2.1/src/index.js
 * - TypeScript types from https://github.com/lukeed/clsx/blob/v1.2.1/clsx.d.ts
 *
 * Original code has MIT license: Copyright (c) Luke Edwards <luke.edwards05@gmail.com> (lukeed.com)
 */
function twJoin() {
	let index = 0;
	let argument;
	let resolvedValue;
	let string = "";
	while (index < arguments.length) {
		if ((argument = arguments[index++])) {
			if ((resolvedValue = toValue(argument))) {
				string && (string += " ");
				string += resolvedValue;
			}
		}
	}
	return string;
}
const toValue = (mix) => {
	if (typeof mix === "string") {
		return mix;
	}
	let resolvedValue;
	let string = "";
	for (let k = 0; k < mix.length; k++) {
		if (mix[k]) {
			if ((resolvedValue = toValue(mix[k]))) {
				string && (string += " ");
				string += resolvedValue;
			}
		}
	}
	return string;
};
function createTailwindMerge(createConfigFirst, ...createConfigRest) {
	let configUtils;
	let cacheGet;
	let cacheSet;
	let functionToCall = initTailwindMerge;
	function initTailwindMerge(classList) {
		const config = createConfigRest.reduce((previousConfig, createConfigCurrent) => createConfigCurrent(previousConfig), createConfigFirst());
		configUtils = createConfigUtils(config);
		cacheGet = configUtils.cache.get;
		cacheSet = configUtils.cache.set;
		functionToCall = tailwindMerge;
		return tailwindMerge(classList);
	}
	function tailwindMerge(classList) {
		const cachedResult = cacheGet(classList);
		if (cachedResult) {
			return cachedResult;
		}
		const result = mergeClassList(classList, configUtils);
		cacheSet(classList, result);
		return result;
	}
	return function callTailwindMerge() {
		return functionToCall(twJoin.apply(null, arguments));
	};
}
const fromTheme = (key) => {
	const themeGetter = (theme) => theme[key] || [];
	themeGetter.isThemeGetter = true;
	return themeGetter;
};
const arbitraryValueRegex = /^\[(?:([a-z-]+):)?(.+)\]$/i;
const fractionRegex = /^\d+\/\d+$/;
const stringLengths = /*#__PURE__*/ new Set(["px", "full", "screen"]);
const tshirtUnitRegex = /^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/;
const lengthUnitRegex = /\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\b(calc|min|max|clamp)\(.+\)|^0$/;
const colorFunctionRegex = /^(rgba?|hsla?|hwb|(ok)?(lab|lch)|color-mix)\(.+\)$/;
// Shadow always begins with x and y offset separated by underscore optionally prepended by inset
const shadowRegex = /^(inset_)?-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/;
const imageRegex = /^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\(.+\)$/;
const isLength = (value) => isNumber(value) || stringLengths.has(value) || fractionRegex.test(value);
const isArbitraryLength = (value) => getIsArbitraryValue(value, "length", isLengthOnly);
const isNumber = (value) => Boolean(value) && !Number.isNaN(Number(value));
const isArbitraryNumber = (value) => getIsArbitraryValue(value, "number", isNumber);
const isInteger = (value) => Boolean(value) && Number.isInteger(Number(value));
const isPercent = (value) => value.endsWith("%") && isNumber(value.slice(0, -1));
const isArbitraryValue = (value) => arbitraryValueRegex.test(value);
const isTshirtSize = (value) => tshirtUnitRegex.test(value);
const sizeLabels = /*#__PURE__*/ new Set(["length", "size", "percentage"]);
const isArbitrarySize = (value) => getIsArbitraryValue(value, sizeLabels, isNever);
const isArbitraryPosition = (value) => getIsArbitraryValue(value, "position", isNever);
const imageLabels = /*#__PURE__*/ new Set(["image", "url"]);
const isArbitraryImage = (value) => getIsArbitraryValue(value, imageLabels, isImage);
const isArbitraryShadow = (value) => getIsArbitraryValue(value, "", isShadow);
const isAny = () => true;
const getIsArbitraryValue = (value, label, testValue) => {
	const result = arbitraryValueRegex.exec(value);
	if (result) {
		if (result[1]) {
			return typeof label === "string" ? result[1] === label : label.has(result[1]);
		}
		return testValue(result[2]);
	}
	return false;
};
const isLengthOnly = (value) =>
	// `colorFunctionRegex` check is necessary because color functions can have percentages in them which which would be incorrectly classified as lengths.
	// For example, `hsl(0 0% 0%)` would be classified as a length without this check.
	// I could also use lookbehind assertion in `lengthUnitRegex` but that isn't supported widely enough.
	lengthUnitRegex.test(value) && !colorFunctionRegex.test(value);
const isNever = () => false;
const isShadow = (value) => shadowRegex.test(value);
const isImage = (value) => imageRegex.test(value);
const getDefaultConfig = () => {
	const colors = fromTheme("colors");
	const spacing = fromTheme("spacing");
	const blur = fromTheme("blur");
	const brightness = fromTheme("brightness");
	const borderColor = fromTheme("borderColor");
	const borderRadius = fromTheme("borderRadius");
	const borderSpacing = fromTheme("borderSpacing");
	const borderWidth = fromTheme("borderWidth");
	const contrast = fromTheme("contrast");
	const grayscale = fromTheme("grayscale");
	const hueRotate = fromTheme("hueRotate");
	const invert = fromTheme("invert");
	const gap = fromTheme("gap");
	const gradientColorStops = fromTheme("gradientColorStops");
	const gradientColorStopPositions = fromTheme("gradientColorStopPositions");
	const inset = fromTheme("inset");
	const margin = fromTheme("margin");
	const opacity = fromTheme("opacity");
	const padding = fromTheme("padding");
	const saturate = fromTheme("saturate");
	const scale = fromTheme("scale");
	const sepia = fromTheme("sepia");
	const skew = fromTheme("skew");
	const space = fromTheme("space");
	const translate = fromTheme("translate");
	const getOverscroll = () => ["auto", "contain", "none"];
	const getOverflow = () => ["auto", "hidden", "clip", "visible", "scroll"];
	const getSpacingWithAutoAndArbitrary = () => ["auto", isArbitraryValue, spacing];
	const getSpacingWithArbitrary = () => [isArbitraryValue, spacing];
	const getLengthWithEmptyAndArbitrary = () => ["", isLength, isArbitraryLength];
	const getNumberWithAutoAndArbitrary = () => ["auto", isNumber, isArbitraryValue];
	const getPositions = () => ["bottom", "center", "left", "left-bottom", "left-top", "right", "right-bottom", "right-top", "top"];
	const getLineStyles = () => ["solid", "dashed", "dotted", "double", "none"];
	const getBlendModes = () => [
		"normal",
		"multiply",
		"screen",
		"overlay",
		"darken",
		"lighten",
		"color-dodge",
		"color-burn",
		"hard-light",
		"soft-light",
		"difference",
		"exclusion",
		"hue",
		"saturation",
		"color",
		"luminosity",
	];
	const getAlign = () => ["start", "end", "center", "between", "around", "evenly", "stretch"];
	const getZeroAndEmpty = () => ["", "0", isArbitraryValue];
	const getBreaks = () => ["auto", "avoid", "all", "avoid-page", "page", "left", "right", "column"];
	const getNumberAndArbitrary = () => [isNumber, isArbitraryValue];
	return {
		cacheSize: 500,
		separator: ":",
		theme: {
			colors: [isAny],
			spacing: [isLength, isArbitraryLength],
			blur: ["none", "", isTshirtSize, isArbitraryValue],
			brightness: getNumberAndArbitrary(),
			borderColor: [colors],
			borderRadius: ["none", "", "full", isTshirtSize, isArbitraryValue],
			borderSpacing: getSpacingWithArbitrary(),
			borderWidth: getLengthWithEmptyAndArbitrary(),
			contrast: getNumberAndArbitrary(),
			grayscale: getZeroAndEmpty(),
			hueRotate: getNumberAndArbitrary(),
			invert: getZeroAndEmpty(),
			gap: getSpacingWithArbitrary(),
			gradientColorStops: [colors],
			gradientColorStopPositions: [isPercent, isArbitraryLength],
			inset: getSpacingWithAutoAndArbitrary(),
			margin: getSpacingWithAutoAndArbitrary(),
			opacity: getNumberAndArbitrary(),
			padding: getSpacingWithArbitrary(),
			saturate: getNumberAndArbitrary(),
			scale: getNumberAndArbitrary(),
			sepia: getZeroAndEmpty(),
			skew: getNumberAndArbitrary(),
			space: getSpacingWithArbitrary(),
			translate: getSpacingWithArbitrary(),
		},
		classGroups: {
			// Layout
			/**
			 * Aspect Ratio
			 * @see https://tailwindcss.com/docs/aspect-ratio
			 */
			"aspect": [
				{
					aspect: ["auto", "square", "video", isArbitraryValue],
				},
			],
			/**
			 * Container
			 * @see https://tailwindcss.com/docs/container
			 */
			"container": ["container"],
			/**
			 * Columns
			 * @see https://tailwindcss.com/docs/columns
			 */
			"columns": [
				{
					columns: [isTshirtSize],
				},
			],
			/**
			 * Break After
			 * @see https://tailwindcss.com/docs/break-after
			 */
			"break-after": [
				{
					"break-after": getBreaks(),
				},
			],
			/**
			 * Break Before
			 * @see https://tailwindcss.com/docs/break-before
			 */
			"break-before": [
				{
					"break-before": getBreaks(),
				},
			],
			/**
			 * Break Inside
			 * @see https://tailwindcss.com/docs/break-inside
			 */
			"break-inside": [
				{
					"break-inside": ["auto", "avoid", "avoid-page", "avoid-column"],
				},
			],
			/**
			 * Box Decoration Break
			 * @see https://tailwindcss.com/docs/box-decoration-break
			 */
			"box-decoration": [
				{
					"box-decoration": ["slice", "clone"],
				},
			],
			/**
			 * Box Sizing
			 * @see https://tailwindcss.com/docs/box-sizing
			 */
			"box": [
				{
					box: ["border", "content"],
				},
			],
			/**
			 * Display
			 * @see https://tailwindcss.com/docs/display
			 */
			"display": [
				"block",
				"inline-block",
				"inline",
				"flex",
				"inline-flex",
				"table",
				"inline-table",
				"table-caption",
				"table-cell",
				"table-column",
				"table-column-group",
				"table-footer-group",
				"table-header-group",
				"table-row-group",
				"table-row",
				"flow-root",
				"grid",
				"inline-grid",
				"contents",
				"list-item",
				"hidden",
			],
			/**
			 * Floats
			 * @see https://tailwindcss.com/docs/float
			 */
			"float": [
				{
					float: ["right", "left", "none", "start", "end"],
				},
			],
			/**
			 * Clear
			 * @see https://tailwindcss.com/docs/clear
			 */
			"clear": [
				{
					clear: ["left", "right", "both", "none", "start", "end"],
				},
			],
			/**
			 * Isolation
			 * @see https://tailwindcss.com/docs/isolation
			 */
			"isolation": ["isolate", "isolation-auto"],
			/**
			 * Object Fit
			 * @see https://tailwindcss.com/docs/object-fit
			 */
			"object-fit": [
				{
					object: ["contain", "cover", "fill", "none", "scale-down"],
				},
			],
			/**
			 * Object Position
			 * @see https://tailwindcss.com/docs/object-position
			 */
			"object-position": [
				{
					object: [...getPositions(), isArbitraryValue],
				},
			],
			/**
			 * Overflow
			 * @see https://tailwindcss.com/docs/overflow
			 */
			"overflow": [
				{
					overflow: getOverflow(),
				},
			],
			/**
			 * Overflow X
			 * @see https://tailwindcss.com/docs/overflow
			 */
			"overflow-x": [
				{
					"overflow-x": getOverflow(),
				},
			],
			/**
			 * Overflow Y
			 * @see https://tailwindcss.com/docs/overflow
			 */
			"overflow-y": [
				{
					"overflow-y": getOverflow(),
				},
			],
			/**
			 * Overscroll Behavior
			 * @see https://tailwindcss.com/docs/overscroll-behavior
			 */
			"overscroll": [
				{
					overscroll: getOverscroll(),
				},
			],
			/**
			 * Overscroll Behavior X
			 * @see https://tailwindcss.com/docs/overscroll-behavior
			 */
			"overscroll-x": [
				{
					"overscroll-x": getOverscroll(),
				},
			],
			/**
			 * Overscroll Behavior Y
			 * @see https://tailwindcss.com/docs/overscroll-behavior
			 */
			"overscroll-y": [
				{
					"overscroll-y": getOverscroll(),
				},
			],
			/**
			 * Position
			 * @see https://tailwindcss.com/docs/position
			 */
			"position": ["static", "fixed", "absolute", "relative", "sticky"],
			/**
			 * Top / Right / Bottom / Left
			 * @see https://tailwindcss.com/docs/top-right-bottom-left
			 */
			"inset": [
				{
					inset: [inset],
				},
			],
			/**
			 * Right / Left
			 * @see https://tailwindcss.com/docs/top-right-bottom-left
			 */
			"inset-x": [
				{
					"inset-x": [inset],
				},
			],
			/**
			 * Top / Bottom
			 * @see https://tailwindcss.com/docs/top-right-bottom-left
			 */
			"inset-y": [
				{
					"inset-y": [inset],
				},
			],
			/**
			 * Start
			 * @see https://tailwindcss.com/docs/top-right-bottom-left
			 */
			"start": [
				{
					start: [inset],
				},
			],
			/**
			 * End
			 * @see https://tailwindcss.com/docs/top-right-bottom-left
			 */
			"end": [
				{
					end: [inset],
				},
			],
			/**
			 * Top
			 * @see https://tailwindcss.com/docs/top-right-bottom-left
			 */
			"top": [
				{
					top: [inset],
				},
			],
			/**
			 * Right
			 * @see https://tailwindcss.com/docs/top-right-bottom-left
			 */
			"right": [
				{
					right: [inset],
				},
			],
			/**
			 * Bottom
			 * @see https://tailwindcss.com/docs/top-right-bottom-left
			 */
			"bottom": [
				{
					bottom: [inset],
				},
			],
			/**
			 * Left
			 * @see https://tailwindcss.com/docs/top-right-bottom-left
			 */
			"left": [
				{
					left: [inset],
				},
			],
			/**
			 * Visibility
			 * @see https://tailwindcss.com/docs/visibility
			 */
			"visibility": ["visible", "invisible", "collapse"],
			/**
			 * Z-Index
			 * @see https://tailwindcss.com/docs/z-index
			 */
			"z": [
				{
					z: ["auto", isInteger, isArbitraryValue],
				},
			],
			// Flexbox and Grid
			/**
			 * Flex Basis
			 * @see https://tailwindcss.com/docs/flex-basis
			 */
			"basis": [
				{
					basis: getSpacingWithAutoAndArbitrary(),
				},
			],
			/**
			 * Flex Direction
			 * @see https://tailwindcss.com/docs/flex-direction
			 */
			"flex-direction": [
				{
					flex: ["row", "row-reverse", "col", "col-reverse"],
				},
			],
			/**
			 * Flex Wrap
			 * @see https://tailwindcss.com/docs/flex-wrap
			 */
			"flex-wrap": [
				{
					flex: ["wrap", "wrap-reverse", "nowrap"],
				},
			],
			/**
			 * Flex
			 * @see https://tailwindcss.com/docs/flex
			 */
			"flex": [
				{
					flex: ["1", "auto", "initial", "none", isArbitraryValue],
				},
			],
			/**
			 * Flex Grow
			 * @see https://tailwindcss.com/docs/flex-grow
			 */
			"grow": [
				{
					grow: getZeroAndEmpty(),
				},
			],
			/**
			 * Flex Shrink
			 * @see https://tailwindcss.com/docs/flex-shrink
			 */
			"shrink": [
				{
					shrink: getZeroAndEmpty(),
				},
			],
			/**
			 * Order
			 * @see https://tailwindcss.com/docs/order
			 */
			"order": [
				{
					order: ["first", "last", "none", isInteger, isArbitraryValue],
				},
			],
			/**
			 * Grid Template Columns
			 * @see https://tailwindcss.com/docs/grid-template-columns
			 */
			"grid-cols": [
				{
					"grid-cols": [isAny],
				},
			],
			/**
			 * Grid Column Start / End
			 * @see https://tailwindcss.com/docs/grid-column
			 */
			"col-start-end": [
				{
					col: [
						"auto",
						{
							span: ["full", isInteger, isArbitraryValue],
						},
						isArbitraryValue,
					],
				},
			],
			/**
			 * Grid Column Start
			 * @see https://tailwindcss.com/docs/grid-column
			 */
			"col-start": [
				{
					"col-start": getNumberWithAutoAndArbitrary(),
				},
			],
			/**
			 * Grid Column End
			 * @see https://tailwindcss.com/docs/grid-column
			 */
			"col-end": [
				{
					"col-end": getNumberWithAutoAndArbitrary(),
				},
			],
			/**
			 * Grid Template Rows
			 * @see https://tailwindcss.com/docs/grid-template-rows
			 */
			"grid-rows": [
				{
					"grid-rows": [isAny],
				},
			],
			/**
			 * Grid Row Start / End
			 * @see https://tailwindcss.com/docs/grid-row
			 */
			"row-start-end": [
				{
					row: [
						"auto",
						{
							span: [isInteger, isArbitraryValue],
						},
						isArbitraryValue,
					],
				},
			],
			/**
			 * Grid Row Start
			 * @see https://tailwindcss.com/docs/grid-row
			 */
			"row-start": [
				{
					"row-start": getNumberWithAutoAndArbitrary(),
				},
			],
			/**
			 * Grid Row End
			 * @see https://tailwindcss.com/docs/grid-row
			 */
			"row-end": [
				{
					"row-end": getNumberWithAutoAndArbitrary(),
				},
			],
			/**
			 * Grid Auto Flow
			 * @see https://tailwindcss.com/docs/grid-auto-flow
			 */
			"grid-flow": [
				{
					"grid-flow": ["row", "col", "dense", "row-dense", "col-dense"],
				},
			],
			/**
			 * Grid Auto Columns
			 * @see https://tailwindcss.com/docs/grid-auto-columns
			 */
			"auto-cols": [
				{
					"auto-cols": ["auto", "min", "max", "fr", isArbitraryValue],
				},
			],
			/**
			 * Grid Auto Rows
			 * @see https://tailwindcss.com/docs/grid-auto-rows
			 */
			"auto-rows": [
				{
					"auto-rows": ["auto", "min", "max", "fr", isArbitraryValue],
				},
			],
			/**
			 * Gap
			 * @see https://tailwindcss.com/docs/gap
			 */
			"gap": [
				{
					gap: [gap],
				},
			],
			/**
			 * Gap X
			 * @see https://tailwindcss.com/docs/gap
			 */
			"gap-x": [
				{
					"gap-x": [gap],
				},
			],
			/**
			 * Gap Y
			 * @see https://tailwindcss.com/docs/gap
			 */
			"gap-y": [
				{
					"gap-y": [gap],
				},
			],
			/**
			 * Justify Content
			 * @see https://tailwindcss.com/docs/justify-content
			 */
			"justify-content": [
				{
					justify: ["normal", ...getAlign()],
				},
			],
			/**
			 * Justify Items
			 * @see https://tailwindcss.com/docs/justify-items
			 */
			"justify-items": [
				{
					"justify-items": ["start", "end", "center", "stretch"],
				},
			],
			/**
			 * Justify Self
			 * @see https://tailwindcss.com/docs/justify-self
			 */
			"justify-self": [
				{
					"justify-self": ["auto", "start", "end", "center", "stretch"],
				},
			],
			/**
			 * Align Content
			 * @see https://tailwindcss.com/docs/align-content
			 */
			"align-content": [
				{
					content: ["normal", ...getAlign(), "baseline"],
				},
			],
			/**
			 * Align Items
			 * @see https://tailwindcss.com/docs/align-items
			 */
			"align-items": [
				{
					items: ["start", "end", "center", "baseline", "stretch"],
				},
			],
			/**
			 * Align Self
			 * @see https://tailwindcss.com/docs/align-self
			 */
			"align-self": [
				{
					self: ["auto", "start", "end", "center", "stretch", "baseline"],
				},
			],
			/**
			 * Place Content
			 * @see https://tailwindcss.com/docs/place-content
			 */
			"place-content": [
				{
					"place-content": [...getAlign(), "baseline"],
				},
			],
			/**
			 * Place Items
			 * @see https://tailwindcss.com/docs/place-items
			 */
			"place-items": [
				{
					"place-items": ["start", "end", "center", "baseline", "stretch"],
				},
			],
			/**
			 * Place Self
			 * @see https://tailwindcss.com/docs/place-self
			 */
			"place-self": [
				{
					"place-self": ["auto", "start", "end", "center", "stretch"],
				},
			],
			// Spacing
			/**
			 * Padding
			 * @see https://tailwindcss.com/docs/padding
			 */
			"p": [
				{
					p: [padding],
				},
			],
			/**
			 * Padding X
			 * @see https://tailwindcss.com/docs/padding
			 */
			"px": [
				{
					px: [padding],
				},
			],
			/**
			 * Padding Y
			 * @see https://tailwindcss.com/docs/padding
			 */
			"py": [
				{
					py: [padding],
				},
			],
			/**
			 * Padding Start
			 * @see https://tailwindcss.com/docs/padding
			 */
			"ps": [
				{
					ps: [padding],
				},
			],
			/**
			 * Padding End
			 * @see https://tailwindcss.com/docs/padding
			 */
			"pe": [
				{
					pe: [padding],
				},
			],
			/**
			 * Padding Top
			 * @see https://tailwindcss.com/docs/padding
			 */
			"pt": [
				{
					pt: [padding],
				},
			],
			/**
			 * Padding Right
			 * @see https://tailwindcss.com/docs/padding
			 */
			"pr": [
				{
					pr: [padding],
				},
			],
			/**
			 * Padding Bottom
			 * @see https://tailwindcss.com/docs/padding
			 */
			"pb": [
				{
					pb: [padding],
				},
			],
			/**
			 * Padding Left
			 * @see https://tailwindcss.com/docs/padding
			 */
			"pl": [
				{
					pl: [padding],
				},
			],
			/**
			 * Margin
			 * @see https://tailwindcss.com/docs/margin
			 */
			"m": [
				{
					m: [margin],
				},
			],
			/**
			 * Margin X
			 * @see https://tailwindcss.com/docs/margin
			 */
			"mx": [
				{
					mx: [margin],
				},
			],
			/**
			 * Margin Y
			 * @see https://tailwindcss.com/docs/margin
			 */
			"my": [
				{
					my: [margin],
				},
			],
			/**
			 * Margin Start
			 * @see https://tailwindcss.com/docs/margin
			 */
			"ms": [
				{
					ms: [margin],
				},
			],
			/**
			 * Margin End
			 * @see https://tailwindcss.com/docs/margin
			 */
			"me": [
				{
					me: [margin],
				},
			],
			/**
			 * Margin Top
			 * @see https://tailwindcss.com/docs/margin
			 */
			"mt": [
				{
					mt: [margin],
				},
			],
			/**
			 * Margin Right
			 * @see https://tailwindcss.com/docs/margin
			 */
			"mr": [
				{
					mr: [margin],
				},
			],
			/**
			 * Margin Bottom
			 * @see https://tailwindcss.com/docs/margin
			 */
			"mb": [
				{
					mb: [margin],
				},
			],
			/**
			 * Margin Left
			 * @see https://tailwindcss.com/docs/margin
			 */
			"ml": [
				{
					ml: [margin],
				},
			],
			/**
			 * Space Between X
			 * @see https://tailwindcss.com/docs/space
			 */
			"space-x": [
				{
					"space-x": [space],
				},
			],
			/**
			 * Space Between X Reverse
			 * @see https://tailwindcss.com/docs/space
			 */
			"space-x-reverse": ["space-x-reverse"],
			/**
			 * Space Between Y
			 * @see https://tailwindcss.com/docs/space
			 */
			"space-y": [
				{
					"space-y": [space],
				},
			],
			/**
			 * Space Between Y Reverse
			 * @see https://tailwindcss.com/docs/space
			 */
			"space-y-reverse": ["space-y-reverse"],
			// Sizing
			/**
			 * Width
			 * @see https://tailwindcss.com/docs/width
			 */
			"w": [
				{
					w: ["auto", "min", "max", "fit", "svw", "lvw", "dvw", isArbitraryValue, spacing],
				},
			],
			/**
			 * Min-Width
			 * @see https://tailwindcss.com/docs/min-width
			 */
			"min-w": [
				{
					"min-w": [isArbitraryValue, spacing, "min", "max", "fit"],
				},
			],
			/**
			 * Max-Width
			 * @see https://tailwindcss.com/docs/max-width
			 */
			"max-w": [
				{
					"max-w": [
						isArbitraryValue,
						spacing,
						"none",
						"full",
						"min",
						"max",
						"fit",
						"prose",
						{
							screen: [isTshirtSize],
						},
						isTshirtSize,
					],
				},
			],
			/**
			 * Height
			 * @see https://tailwindcss.com/docs/height
			 */
			"h": [
				{
					h: [isArbitraryValue, spacing, "auto", "min", "max", "fit", "svh", "lvh", "dvh"],
				},
			],
			/**
			 * Min-Height
			 * @see https://tailwindcss.com/docs/min-height
			 */
			"min-h": [
				{
					"min-h": [isArbitraryValue, spacing, "min", "max", "fit", "svh", "lvh", "dvh"],
				},
			],
			/**
			 * Max-Height
			 * @see https://tailwindcss.com/docs/max-height
			 */
			"max-h": [
				{
					"max-h": [isArbitraryValue, spacing, "min", "max", "fit", "svh", "lvh", "dvh"],
				},
			],
			/**
			 * Size
			 * @see https://tailwindcss.com/docs/size
			 */
			"size": [
				{
					size: [isArbitraryValue, spacing, "auto", "min", "max", "fit"],
				},
			],
			// Typography
			/**
			 * Font Size
			 * @see https://tailwindcss.com/docs/font-size
			 */
			"font-size": [
				{
					text: ["base", isTshirtSize, isArbitraryLength],
				},
			],
			/**
			 * Font Smoothing
			 * @see https://tailwindcss.com/docs/font-smoothing
			 */
			"font-smoothing": ["antialiased", "subpixel-antialiased"],
			/**
			 * Font Style
			 * @see https://tailwindcss.com/docs/font-style
			 */
			"font-style": ["italic", "not-italic"],
			/**
			 * Font Weight
			 * @see https://tailwindcss.com/docs/font-weight
			 */
			"font-weight": [
				{
					font: ["thin", "extralight", "light", "normal", "medium", "semibold", "bold", "extrabold", "black", isArbitraryNumber],
				},
			],
			/**
			 * Font Family
			 * @see https://tailwindcss.com/docs/font-family
			 */
			"font-family": [
				{
					font: [isAny],
				},
			],
			/**
			 * Font Variant Numeric
			 * @see https://tailwindcss.com/docs/font-variant-numeric
			 */
			"fvn-normal": ["normal-nums"],
			/**
			 * Font Variant Numeric
			 * @see https://tailwindcss.com/docs/font-variant-numeric
			 */
			"fvn-ordinal": ["ordinal"],
			/**
			 * Font Variant Numeric
			 * @see https://tailwindcss.com/docs/font-variant-numeric
			 */
			"fvn-slashed-zero": ["slashed-zero"],
			/**
			 * Font Variant Numeric
			 * @see https://tailwindcss.com/docs/font-variant-numeric
			 */
			"fvn-figure": ["lining-nums", "oldstyle-nums"],
			/**
			 * Font Variant Numeric
			 * @see https://tailwindcss.com/docs/font-variant-numeric
			 */
			"fvn-spacing": ["proportional-nums", "tabular-nums"],
			/**
			 * Font Variant Numeric
			 * @see https://tailwindcss.com/docs/font-variant-numeric
			 */
			"fvn-fraction": ["diagonal-fractions", "stacked-fractions"],
			/**
			 * Letter Spacing
			 * @see https://tailwindcss.com/docs/letter-spacing
			 */
			"tracking": [
				{
					tracking: ["tighter", "tight", "normal", "wide", "wider", "widest", isArbitraryValue],
				},
			],
			/**
			 * Line Clamp
			 * @see https://tailwindcss.com/docs/line-clamp
			 */
			"line-clamp": [
				{
					"line-clamp": ["none", isNumber, isArbitraryNumber],
				},
			],
			/**
			 * Line Height
			 * @see https://tailwindcss.com/docs/line-height
			 */
			"leading": [
				{
					leading: ["none", "tight", "snug", "normal", "relaxed", "loose", isLength, isArbitraryValue],
				},
			],
			/**
			 * List Style Image
			 * @see https://tailwindcss.com/docs/list-style-image
			 */
			"list-image": [
				{
					"list-image": ["none", isArbitraryValue],
				},
			],
			/**
			 * List Style Type
			 * @see https://tailwindcss.com/docs/list-style-type
			 */
			"list-style-type": [
				{
					list: ["none", "disc", "decimal", isArbitraryValue],
				},
			],
			/**
			 * List Style Position
			 * @see https://tailwindcss.com/docs/list-style-position
			 */
			"list-style-position": [
				{
					list: ["inside", "outside"],
				},
			],
			/**
			 * Placeholder Color
			 * @deprecated since Tailwind CSS v3.0.0
			 * @see https://tailwindcss.com/docs/placeholder-color
			 */
			"placeholder-color": [
				{
					placeholder: [colors],
				},
			],
			/**
			 * Placeholder Opacity
			 * @see https://tailwindcss.com/docs/placeholder-opacity
			 */
			"placeholder-opacity": [
				{
					"placeholder-opacity": [opacity],
				},
			],
			/**
			 * Text Alignment
			 * @see https://tailwindcss.com/docs/text-align
			 */
			"text-alignment": [
				{
					text: ["left", "center", "right", "justify", "start", "end"],
				},
			],
			/**
			 * Text Color
			 * @see https://tailwindcss.com/docs/text-color
			 */
			"text-color": [
				{
					text: [colors],
				},
			],
			/**
			 * Text Opacity
			 * @see https://tailwindcss.com/docs/text-opacity
			 */
			"text-opacity": [
				{
					"text-opacity": [opacity],
				},
			],
			/**
			 * Text Decoration
			 * @see https://tailwindcss.com/docs/text-decoration
			 */
			"text-decoration": ["underline", "overline", "line-through", "no-underline"],
			/**
			 * Text Decoration Style
			 * @see https://tailwindcss.com/docs/text-decoration-style
			 */
			"text-decoration-style": [
				{
					decoration: [...getLineStyles(), "wavy"],
				},
			],
			/**
			 * Text Decoration Thickness
			 * @see https://tailwindcss.com/docs/text-decoration-thickness
			 */
			"text-decoration-thickness": [
				{
					decoration: ["auto", "from-font", isLength, isArbitraryLength],
				},
			],
			/**
			 * Text Underline Offset
			 * @see https://tailwindcss.com/docs/text-underline-offset
			 */
			"underline-offset": [
				{
					"underline-offset": ["auto", isLength, isArbitraryValue],
				},
			],
			/**
			 * Text Decoration Color
			 * @see https://tailwindcss.com/docs/text-decoration-color
			 */
			"text-decoration-color": [
				{
					decoration: [colors],
				},
			],
			/**
			 * Text Transform
			 * @see https://tailwindcss.com/docs/text-transform
			 */
			"text-transform": ["uppercase", "lowercase", "capitalize", "normal-case"],
			/**
			 * Text Overflow
			 * @see https://tailwindcss.com/docs/text-overflow
			 */
			"text-overflow": ["truncate", "text-ellipsis", "text-clip"],
			/**
			 * Text Wrap
			 * @see https://tailwindcss.com/docs/text-wrap
			 */
			"text-wrap": [
				{
					text: ["wrap", "nowrap", "balance", "pretty"],
				},
			],
			/**
			 * Text Indent
			 * @see https://tailwindcss.com/docs/text-indent
			 */
			"indent": [
				{
					indent: getSpacingWithArbitrary(),
				},
			],
			/**
			 * Vertical Alignment
			 * @see https://tailwindcss.com/docs/vertical-align
			 */
			"vertical-align": [
				{
					align: ["baseline", "top", "middle", "bottom", "text-top", "text-bottom", "sub", "super", isArbitraryValue],
				},
			],
			/**
			 * Whitespace
			 * @see https://tailwindcss.com/docs/whitespace
			 */
			"whitespace": [
				{
					whitespace: ["normal", "nowrap", "pre", "pre-line", "pre-wrap", "break-spaces"],
				},
			],
			/**
			 * Word Break
			 * @see https://tailwindcss.com/docs/word-break
			 */
			"break": [
				{
					break: ["normal", "words", "all", "keep"],
				},
			],
			/**
			 * Hyphens
			 * @see https://tailwindcss.com/docs/hyphens
			 */
			"hyphens": [
				{
					hyphens: ["none", "manual", "auto"],
				},
			],
			/**
			 * Content
			 * @see https://tailwindcss.com/docs/content
			 */
			"content": [
				{
					content: ["none", isArbitraryValue],
				},
			],
			// Backgrounds
			/**
			 * Background Attachment
			 * @see https://tailwindcss.com/docs/background-attachment
			 */
			"bg-attachment": [
				{
					bg: ["fixed", "local", "scroll"],
				},
			],
			/**
			 * Background Clip
			 * @see https://tailwindcss.com/docs/background-clip
			 */
			"bg-clip": [
				{
					"bg-clip": ["border", "padding", "content", "text"],
				},
			],
			/**
			 * Background Opacity
			 * @deprecated since Tailwind CSS v3.0.0
			 * @see https://tailwindcss.com/docs/background-opacity
			 */
			"bg-opacity": [
				{
					"bg-opacity": [opacity],
				},
			],
			/**
			 * Background Origin
			 * @see https://tailwindcss.com/docs/background-origin
			 */
			"bg-origin": [
				{
					"bg-origin": ["border", "padding", "content"],
				},
			],
			/**
			 * Background Position
			 * @see https://tailwindcss.com/docs/background-position
			 */
			"bg-position": [
				{
					bg: [...getPositions(), isArbitraryPosition],
				},
			],
			/**
			 * Background Repeat
			 * @see https://tailwindcss.com/docs/background-repeat
			 */
			"bg-repeat": [
				{
					bg: [
						"no-repeat",
						{
							repeat: ["", "x", "y", "round", "space"],
						},
					],
				},
			],
			/**
			 * Background Size
			 * @see https://tailwindcss.com/docs/background-size
			 */
			"bg-size": [
				{
					bg: ["auto", "cover", "contain", isArbitrarySize],
				},
			],
			/**
			 * Background Image
			 * @see https://tailwindcss.com/docs/background-image
			 */
			"bg-image": [
				{
					bg: [
						"none",
						{
							"gradient-to": ["t", "tr", "r", "br", "b", "bl", "l", "tl"],
						},
						isArbitraryImage,
					],
				},
			],
			/**
			 * Background Color
			 * @see https://tailwindcss.com/docs/background-color
			 */
			"bg-color": [
				{
					bg: [colors],
				},
			],
			/**
			 * Gradient Color Stops From Position
			 * @see https://tailwindcss.com/docs/gradient-color-stops
			 */
			"gradient-from-pos": [
				{
					from: [gradientColorStopPositions],
				},
			],
			/**
			 * Gradient Color Stops Via Position
			 * @see https://tailwindcss.com/docs/gradient-color-stops
			 */
			"gradient-via-pos": [
				{
					via: [gradientColorStopPositions],
				},
			],
			/**
			 * Gradient Color Stops To Position
			 * @see https://tailwindcss.com/docs/gradient-color-stops
			 */
			"gradient-to-pos": [
				{
					to: [gradientColorStopPositions],
				},
			],
			/**
			 * Gradient Color Stops From
			 * @see https://tailwindcss.com/docs/gradient-color-stops
			 */
			"gradient-from": [
				{
					from: [gradientColorStops],
				},
			],
			/**
			 * Gradient Color Stops Via
			 * @see https://tailwindcss.com/docs/gradient-color-stops
			 */
			"gradient-via": [
				{
					via: [gradientColorStops],
				},
			],
			/**
			 * Gradient Color Stops To
			 * @see https://tailwindcss.com/docs/gradient-color-stops
			 */
			"gradient-to": [
				{
					to: [gradientColorStops],
				},
			],
			// Borders
			/**
			 * Border Radius
			 * @see https://tailwindcss.com/docs/border-radius
			 */
			"rounded": [
				{
					rounded: [borderRadius],
				},
			],
			/**
			 * Border Radius Start
			 * @see https://tailwindcss.com/docs/border-radius
			 */
			"rounded-s": [
				{
					"rounded-s": [borderRadius],
				},
			],
			/**
			 * Border Radius End
			 * @see https://tailwindcss.com/docs/border-radius
			 */
			"rounded-e": [
				{
					"rounded-e": [borderRadius],
				},
			],
			/**
			 * Border Radius Top
			 * @see https://tailwindcss.com/docs/border-radius
			 */
			"rounded-t": [
				{
					"rounded-t": [borderRadius],
				},
			],
			/**
			 * Border Radius Right
			 * @see https://tailwindcss.com/docs/border-radius
			 */
			"rounded-r": [
				{
					"rounded-r": [borderRadius],
				},
			],
			/**
			 * Border Radius Bottom
			 * @see https://tailwindcss.com/docs/border-radius
			 */
			"rounded-b": [
				{
					"rounded-b": [borderRadius],
				},
			],
			/**
			 * Border Radius Left
			 * @see https://tailwindcss.com/docs/border-radius
			 */
			"rounded-l": [
				{
					"rounded-l": [borderRadius],
				},
			],
			/**
			 * Border Radius Start Start
			 * @see https://tailwindcss.com/docs/border-radius
			 */
			"rounded-ss": [
				{
					"rounded-ss": [borderRadius],
				},
			],
			/**
			 * Border Radius Start End
			 * @see https://tailwindcss.com/docs/border-radius
			 */
			"rounded-se": [
				{
					"rounded-se": [borderRadius],
				},
			],
			/**
			 * Border Radius End End
			 * @see https://tailwindcss.com/docs/border-radius
			 */
			"rounded-ee": [
				{
					"rounded-ee": [borderRadius],
				},
			],
			/**
			 * Border Radius End Start
			 * @see https://tailwindcss.com/docs/border-radius
			 */
			"rounded-es": [
				{
					"rounded-es": [borderRadius],
				},
			],
			/**
			 * Border Radius Top Left
			 * @see https://tailwindcss.com/docs/border-radius
			 */
			"rounded-tl": [
				{
					"rounded-tl": [borderRadius],
				},
			],
			/**
			 * Border Radius Top Right
			 * @see https://tailwindcss.com/docs/border-radius
			 */
			"rounded-tr": [
				{
					"rounded-tr": [borderRadius],
				},
			],
			/**
			 * Border Radius Bottom Right
			 * @see https://tailwindcss.com/docs/border-radius
			 */
			"rounded-br": [
				{
					"rounded-br": [borderRadius],
				},
			],
			/**
			 * Border Radius Bottom Left
			 * @see https://tailwindcss.com/docs/border-radius
			 */
			"rounded-bl": [
				{
					"rounded-bl": [borderRadius],
				},
			],
			/**
			 * Border Width
			 * @see https://tailwindcss.com/docs/border-width
			 */
			"border-w": [
				{
					border: [borderWidth],
				},
			],
			/**
			 * Border Width X
			 * @see https://tailwindcss.com/docs/border-width
			 */
			"border-w-x": [
				{
					"border-x": [borderWidth],
				},
			],
			/**
			 * Border Width Y
			 * @see https://tailwindcss.com/docs/border-width
			 */
			"border-w-y": [
				{
					"border-y": [borderWidth],
				},
			],
			/**
			 * Border Width Start
			 * @see https://tailwindcss.com/docs/border-width
			 */
			"border-w-s": [
				{
					"border-s": [borderWidth],
				},
			],
			/**
			 * Border Width End
			 * @see https://tailwindcss.com/docs/border-width
			 */
			"border-w-e": [
				{
					"border-e": [borderWidth],
				},
			],
			/**
			 * Border Width Top
			 * @see https://tailwindcss.com/docs/border-width
			 */
			"border-w-t": [
				{
					"border-t": [borderWidth],
				},
			],
			/**
			 * Border Width Right
			 * @see https://tailwindcss.com/docs/border-width
			 */
			"border-w-r": [
				{
					"border-r": [borderWidth],
				},
			],
			/**
			 * Border Width Bottom
			 * @see https://tailwindcss.com/docs/border-width
			 */
			"border-w-b": [
				{
					"border-b": [borderWidth],
				},
			],
			/**
			 * Border Width Left
			 * @see https://tailwindcss.com/docs/border-width
			 */
			"border-w-l": [
				{
					"border-l": [borderWidth],
				},
			],
			/**
			 * Border Opacity
			 * @see https://tailwindcss.com/docs/border-opacity
			 */
			"border-opacity": [
				{
					"border-opacity": [opacity],
				},
			],
			/**
			 * Border Style
			 * @see https://tailwindcss.com/docs/border-style
			 */
			"border-style": [
				{
					border: [...getLineStyles(), "hidden"],
				},
			],
			/**
			 * Divide Width X
			 * @see https://tailwindcss.com/docs/divide-width
			 */
			"divide-x": [
				{
					"divide-x": [borderWidth],
				},
			],
			/**
			 * Divide Width X Reverse
			 * @see https://tailwindcss.com/docs/divide-width
			 */
			"divide-x-reverse": ["divide-x-reverse"],
			/**
			 * Divide Width Y
			 * @see https://tailwindcss.com/docs/divide-width
			 */
			"divide-y": [
				{
					"divide-y": [borderWidth],
				},
			],
			/**
			 * Divide Width Y Reverse
			 * @see https://tailwindcss.com/docs/divide-width
			 */
			"divide-y-reverse": ["divide-y-reverse"],
			/**
			 * Divide Opacity
			 * @see https://tailwindcss.com/docs/divide-opacity
			 */
			"divide-opacity": [
				{
					"divide-opacity": [opacity],
				},
			],
			/**
			 * Divide Style
			 * @see https://tailwindcss.com/docs/divide-style
			 */
			"divide-style": [
				{
					divide: getLineStyles(),
				},
			],
			/**
			 * Border Color
			 * @see https://tailwindcss.com/docs/border-color
			 */
			"border-color": [
				{
					border: [borderColor],
				},
			],
			/**
			 * Border Color X
			 * @see https://tailwindcss.com/docs/border-color
			 */
			"border-color-x": [
				{
					"border-x": [borderColor],
				},
			],
			/**
			 * Border Color Y
			 * @see https://tailwindcss.com/docs/border-color
			 */
			"border-color-y": [
				{
					"border-y": [borderColor],
				},
			],
			/**
			 * Border Color S
			 * @see https://tailwindcss.com/docs/border-color
			 */
			"border-color-s": [
				{
					"border-s": [borderColor],
				},
			],
			/**
			 * Border Color E
			 * @see https://tailwindcss.com/docs/border-color
			 */
			"border-color-e": [
				{
					"border-e": [borderColor],
				},
			],
			/**
			 * Border Color Top
			 * @see https://tailwindcss.com/docs/border-color
			 */
			"border-color-t": [
				{
					"border-t": [borderColor],
				},
			],
			/**
			 * Border Color Right
			 * @see https://tailwindcss.com/docs/border-color
			 */
			"border-color-r": [
				{
					"border-r": [borderColor],
				},
			],
			/**
			 * Border Color Bottom
			 * @see https://tailwindcss.com/docs/border-color
			 */
			"border-color-b": [
				{
					"border-b": [borderColor],
				},
			],
			/**
			 * Border Color Left
			 * @see https://tailwindcss.com/docs/border-color
			 */
			"border-color-l": [
				{
					"border-l": [borderColor],
				},
			],
			/**
			 * Divide Color
			 * @see https://tailwindcss.com/docs/divide-color
			 */
			"divide-color": [
				{
					divide: [borderColor],
				},
			],
			/**
			 * Outline Style
			 * @see https://tailwindcss.com/docs/outline-style
			 */
			"outline-style": [
				{
					outline: ["", ...getLineStyles()],
				},
			],
			/**
			 * Outline Offset
			 * @see https://tailwindcss.com/docs/outline-offset
			 */
			"outline-offset": [
				{
					"outline-offset": [isLength, isArbitraryValue],
				},
			],
			/**
			 * Outline Width
			 * @see https://tailwindcss.com/docs/outline-width
			 */
			"outline-w": [
				{
					outline: [isLength, isArbitraryLength],
				},
			],
			/**
			 * Outline Color
			 * @see https://tailwindcss.com/docs/outline-color
			 */
			"outline-color": [
				{
					outline: [colors],
				},
			],
			/**
			 * Ring Width
			 * @see https://tailwindcss.com/docs/ring-width
			 */
			"ring-w": [
				{
					ring: getLengthWithEmptyAndArbitrary(),
				},
			],
			/**
			 * Ring Width Inset
			 * @see https://tailwindcss.com/docs/ring-width
			 */
			"ring-w-inset": ["ring-inset"],
			/**
			 * Ring Color
			 * @see https://tailwindcss.com/docs/ring-color
			 */
			"ring-color": [
				{
					ring: [colors],
				},
			],
			/**
			 * Ring Opacity
			 * @see https://tailwindcss.com/docs/ring-opacity
			 */
			"ring-opacity": [
				{
					"ring-opacity": [opacity],
				},
			],
			/**
			 * Ring Offset Width
			 * @see https://tailwindcss.com/docs/ring-offset-width
			 */
			"ring-offset-w": [
				{
					"ring-offset": [isLength, isArbitraryLength],
				},
			],
			/**
			 * Ring Offset Color
			 * @see https://tailwindcss.com/docs/ring-offset-color
			 */
			"ring-offset-color": [
				{
					"ring-offset": [colors],
				},
			],
			// Effects
			/**
			 * Box Shadow
			 * @see https://tailwindcss.com/docs/box-shadow
			 */
			"shadow": [
				{
					shadow: ["", "inner", "none", isTshirtSize, isArbitraryShadow],
				},
			],
			/**
			 * Box Shadow Color
			 * @see https://tailwindcss.com/docs/box-shadow-color
			 */
			"shadow-color": [
				{
					shadow: [isAny],
				},
			],
			/**
			 * Opacity
			 * @see https://tailwindcss.com/docs/opacity
			 */
			"opacity": [
				{
					opacity: [opacity],
				},
			],
			/**
			 * Mix Blend Mode
			 * @see https://tailwindcss.com/docs/mix-blend-mode
			 */
			"mix-blend": [
				{
					"mix-blend": [...getBlendModes(), "plus-lighter", "plus-darker"],
				},
			],
			/**
			 * Background Blend Mode
			 * @see https://tailwindcss.com/docs/background-blend-mode
			 */
			"bg-blend": [
				{
					"bg-blend": getBlendModes(),
				},
			],
			// Filters
			/**
			 * Filter
			 * @deprecated since Tailwind CSS v3.0.0
			 * @see https://tailwindcss.com/docs/filter
			 */
			"filter": [
				{
					filter: ["", "none"],
				},
			],
			/**
			 * Blur
			 * @see https://tailwindcss.com/docs/blur
			 */
			"blur": [
				{
					blur: [blur],
				},
			],
			/**
			 * Brightness
			 * @see https://tailwindcss.com/docs/brightness
			 */
			"brightness": [
				{
					brightness: [brightness],
				},
			],
			/**
			 * Contrast
			 * @see https://tailwindcss.com/docs/contrast
			 */
			"contrast": [
				{
					contrast: [contrast],
				},
			],
			/**
			 * Drop Shadow
			 * @see https://tailwindcss.com/docs/drop-shadow
			 */
			"drop-shadow": [
				{
					"drop-shadow": ["", "none", isTshirtSize, isArbitraryValue],
				},
			],
			/**
			 * Grayscale
			 * @see https://tailwindcss.com/docs/grayscale
			 */
			"grayscale": [
				{
					grayscale: [grayscale],
				},
			],
			/**
			 * Hue Rotate
			 * @see https://tailwindcss.com/docs/hue-rotate
			 */
			"hue-rotate": [
				{
					"hue-rotate": [hueRotate],
				},
			],
			/**
			 * Invert
			 * @see https://tailwindcss.com/docs/invert
			 */
			"invert": [
				{
					invert: [invert],
				},
			],
			/**
			 * Saturate
			 * @see https://tailwindcss.com/docs/saturate
			 */
			"saturate": [
				{
					saturate: [saturate],
				},
			],
			/**
			 * Sepia
			 * @see https://tailwindcss.com/docs/sepia
			 */
			"sepia": [
				{
					sepia: [sepia],
				},
			],
			/**
			 * Backdrop Filter
			 * @deprecated since Tailwind CSS v3.0.0
			 * @see https://tailwindcss.com/docs/backdrop-filter
			 */
			"backdrop-filter": [
				{
					"backdrop-filter": ["", "none"],
				},
			],
			/**
			 * Backdrop Blur
			 * @see https://tailwindcss.com/docs/backdrop-blur
			 */
			"backdrop-blur": [
				{
					"backdrop-blur": [blur],
				},
			],
			/**
			 * Backdrop Brightness
			 * @see https://tailwindcss.com/docs/backdrop-brightness
			 */
			"backdrop-brightness": [
				{
					"backdrop-brightness": [brightness],
				},
			],
			/**
			 * Backdrop Contrast
			 * @see https://tailwindcss.com/docs/backdrop-contrast
			 */
			"backdrop-contrast": [
				{
					"backdrop-contrast": [contrast],
				},
			],
			/**
			 * Backdrop Grayscale
			 * @see https://tailwindcss.com/docs/backdrop-grayscale
			 */
			"backdrop-grayscale": [
				{
					"backdrop-grayscale": [grayscale],
				},
			],
			/**
			 * Backdrop Hue Rotate
			 * @see https://tailwindcss.com/docs/backdrop-hue-rotate
			 */
			"backdrop-hue-rotate": [
				{
					"backdrop-hue-rotate": [hueRotate],
				},
			],
			/**
			 * Backdrop Invert
			 * @see https://tailwindcss.com/docs/backdrop-invert
			 */
			"backdrop-invert": [
				{
					"backdrop-invert": [invert],
				},
			],
			/**
			 * Backdrop Opacity
			 * @see https://tailwindcss.com/docs/backdrop-opacity
			 */
			"backdrop-opacity": [
				{
					"backdrop-opacity": [opacity],
				},
			],
			/**
			 * Backdrop Saturate
			 * @see https://tailwindcss.com/docs/backdrop-saturate
			 */
			"backdrop-saturate": [
				{
					"backdrop-saturate": [saturate],
				},
			],
			/**
			 * Backdrop Sepia
			 * @see https://tailwindcss.com/docs/backdrop-sepia
			 */
			"backdrop-sepia": [
				{
					"backdrop-sepia": [sepia],
				},
			],
			// Tables
			/**
			 * Border Collapse
			 * @see https://tailwindcss.com/docs/border-collapse
			 */
			"border-collapse": [
				{
					border: ["collapse", "separate"],
				},
			],
			/**
			 * Border Spacing
			 * @see https://tailwindcss.com/docs/border-spacing
			 */
			"border-spacing": [
				{
					"border-spacing": [borderSpacing],
				},
			],
			/**
			 * Border Spacing X
			 * @see https://tailwindcss.com/docs/border-spacing
			 */
			"border-spacing-x": [
				{
					"border-spacing-x": [borderSpacing],
				},
			],
			/**
			 * Border Spacing Y
			 * @see https://tailwindcss.com/docs/border-spacing
			 */
			"border-spacing-y": [
				{
					"border-spacing-y": [borderSpacing],
				},
			],
			/**
			 * Table Layout
			 * @see https://tailwindcss.com/docs/table-layout
			 */
			"table-layout": [
				{
					table: ["auto", "fixed"],
				},
			],
			/**
			 * Caption Side
			 * @see https://tailwindcss.com/docs/caption-side
			 */
			"caption": [
				{
					caption: ["top", "bottom"],
				},
			],
			// Transitions and Animation
			/**
			 * Tranisition Property
			 * @see https://tailwindcss.com/docs/transition-property
			 */
			"transition": [
				{
					transition: ["none", "all", "", "colors", "opacity", "shadow", "transform", isArbitraryValue],
				},
			],
			/**
			 * Transition Duration
			 * @see https://tailwindcss.com/docs/transition-duration
			 */
			"duration": [
				{
					duration: getNumberAndArbitrary(),
				},
			],
			/**
			 * Transition Timing Function
			 * @see https://tailwindcss.com/docs/transition-timing-function
			 */
			"ease": [
				{
					ease: ["linear", "in", "out", "in-out", isArbitraryValue],
				},
			],
			/**
			 * Transition Delay
			 * @see https://tailwindcss.com/docs/transition-delay
			 */
			"delay": [
				{
					delay: getNumberAndArbitrary(),
				},
			],
			/**
			 * Animation
			 * @see https://tailwindcss.com/docs/animation
			 */
			"animate": [
				{
					animate: ["none", "spin", "ping", "pulse", "bounce", isArbitraryValue],
				},
			],
			// Transforms
			/**
			 * Transform
			 * @see https://tailwindcss.com/docs/transform
			 */
			"transform": [
				{
					transform: ["", "gpu", "none"],
				},
			],
			/**
			 * Scale
			 * @see https://tailwindcss.com/docs/scale
			 */
			"scale": [
				{
					scale: [scale],
				},
			],
			/**
			 * Scale X
			 * @see https://tailwindcss.com/docs/scale
			 */
			"scale-x": [
				{
					"scale-x": [scale],
				},
			],
			/**
			 * Scale Y
			 * @see https://tailwindcss.com/docs/scale
			 */
			"scale-y": [
				{
					"scale-y": [scale],
				},
			],
			/**
			 * Rotate
			 * @see https://tailwindcss.com/docs/rotate
			 */
			"rotate": [
				{
					rotate: [isInteger, isArbitraryValue],
				},
			],
			/**
			 * Translate X
			 * @see https://tailwindcss.com/docs/translate
			 */
			"translate-x": [
				{
					"translate-x": [translate],
				},
			],
			/**
			 * Translate Y
			 * @see https://tailwindcss.com/docs/translate
			 */
			"translate-y": [
				{
					"translate-y": [translate],
				},
			],
			/**
			 * Skew X
			 * @see https://tailwindcss.com/docs/skew
			 */
			"skew-x": [
				{
					"skew-x": [skew],
				},
			],
			/**
			 * Skew Y
			 * @see https://tailwindcss.com/docs/skew
			 */
			"skew-y": [
				{
					"skew-y": [skew],
				},
			],
			/**
			 * Transform Origin
			 * @see https://tailwindcss.com/docs/transform-origin
			 */
			"transform-origin": [
				{
					origin: ["center", "top", "top-right", "right", "bottom-right", "bottom", "bottom-left", "left", "top-left", isArbitraryValue],
				},
			],
			// Interactivity
			/**
			 * Accent Color
			 * @see https://tailwindcss.com/docs/accent-color
			 */
			"accent": [
				{
					accent: ["auto", colors],
				},
			],
			/**
			 * Appearance
			 * @see https://tailwindcss.com/docs/appearance
			 */
			"appearance": [
				{
					appearance: ["none", "auto"],
				},
			],
			/**
			 * Cursor
			 * @see https://tailwindcss.com/docs/cursor
			 */
			"cursor": [
				{
					cursor: [
						"auto",
						"default",
						"pointer",
						"wait",
						"text",
						"move",
						"help",
						"not-allowed",
						"none",
						"context-menu",
						"progress",
						"cell",
						"crosshair",
						"vertical-text",
						"alias",
						"copy",
						"no-drop",
						"grab",
						"grabbing",
						"all-scroll",
						"col-resize",
						"row-resize",
						"n-resize",
						"e-resize",
						"s-resize",
						"w-resize",
						"ne-resize",
						"nw-resize",
						"se-resize",
						"sw-resize",
						"ew-resize",
						"ns-resize",
						"nesw-resize",
						"nwse-resize",
						"zoom-in",
						"zoom-out",
						isArbitraryValue,
					],
				},
			],
			/**
			 * Caret Color
			 * @see https://tailwindcss.com/docs/just-in-time-mode#caret-color-utilities
			 */
			"caret-color": [
				{
					caret: [colors],
				},
			],
			/**
			 * Pointer Events
			 * @see https://tailwindcss.com/docs/pointer-events
			 */
			"pointer-events": [
				{
					"pointer-events": ["none", "auto"],
				},
			],
			/**
			 * Resize
			 * @see https://tailwindcss.com/docs/resize
			 */
			"resize": [
				{
					resize: ["none", "y", "x", ""],
				},
			],
			/**
			 * Scroll Behavior
			 * @see https://tailwindcss.com/docs/scroll-behavior
			 */
			"scroll-behavior": [
				{
					scroll: ["auto", "smooth"],
				},
			],
			/**
			 * Scroll Margin
			 * @see https://tailwindcss.com/docs/scroll-margin
			 */
			"scroll-m": [
				{
					"scroll-m": getSpacingWithArbitrary(),
				},
			],
			/**
			 * Scroll Margin X
			 * @see https://tailwindcss.com/docs/scroll-margin
			 */
			"scroll-mx": [
				{
					"scroll-mx": getSpacingWithArbitrary(),
				},
			],
			/**
			 * Scroll Margin Y
			 * @see https://tailwindcss.com/docs/scroll-margin
			 */
			"scroll-my": [
				{
					"scroll-my": getSpacingWithArbitrary(),
				},
			],
			/**
			 * Scroll Margin Start
			 * @see https://tailwindcss.com/docs/scroll-margin
			 */
			"scroll-ms": [
				{
					"scroll-ms": getSpacingWithArbitrary(),
				},
			],
			/**
			 * Scroll Margin End
			 * @see https://tailwindcss.com/docs/scroll-margin
			 */
			"scroll-me": [
				{
					"scroll-me": getSpacingWithArbitrary(),
				},
			],
			/**
			 * Scroll Margin Top
			 * @see https://tailwindcss.com/docs/scroll-margin
			 */
			"scroll-mt": [
				{
					"scroll-mt": getSpacingWithArbitrary(),
				},
			],
			/**
			 * Scroll Margin Right
			 * @see https://tailwindcss.com/docs/scroll-margin
			 */
			"scroll-mr": [
				{
					"scroll-mr": getSpacingWithArbitrary(),
				},
			],
			/**
			 * Scroll Margin Bottom
			 * @see https://tailwindcss.com/docs/scroll-margin
			 */
			"scroll-mb": [
				{
					"scroll-mb": getSpacingWithArbitrary(),
				},
			],
			/**
			 * Scroll Margin Left
			 * @see https://tailwindcss.com/docs/scroll-margin
			 */
			"scroll-ml": [
				{
					"scroll-ml": getSpacingWithArbitrary(),
				},
			],
			/**
			 * Scroll Padding
			 * @see https://tailwindcss.com/docs/scroll-padding
			 */
			"scroll-p": [
				{
					"scroll-p": getSpacingWithArbitrary(),
				},
			],
			/**
			 * Scroll Padding X
			 * @see https://tailwindcss.com/docs/scroll-padding
			 */
			"scroll-px": [
				{
					"scroll-px": getSpacingWithArbitrary(),
				},
			],
			/**
			 * Scroll Padding Y
			 * @see https://tailwindcss.com/docs/scroll-padding
			 */
			"scroll-py": [
				{
					"scroll-py": getSpacingWithArbitrary(),
				},
			],
			/**
			 * Scroll Padding Start
			 * @see https://tailwindcss.com/docs/scroll-padding
			 */
			"scroll-ps": [
				{
					"scroll-ps": getSpacingWithArbitrary(),
				},
			],
			/**
			 * Scroll Padding End
			 * @see https://tailwindcss.com/docs/scroll-padding
			 */
			"scroll-pe": [
				{
					"scroll-pe": getSpacingWithArbitrary(),
				},
			],
			/**
			 * Scroll Padding Top
			 * @see https://tailwindcss.com/docs/scroll-padding
			 */
			"scroll-pt": [
				{
					"scroll-pt": getSpacingWithArbitrary(),
				},
			],
			/**
			 * Scroll Padding Right
			 * @see https://tailwindcss.com/docs/scroll-padding
			 */
			"scroll-pr": [
				{
					"scroll-pr": getSpacingWithArbitrary(),
				},
			],
			/**
			 * Scroll Padding Bottom
			 * @see https://tailwindcss.com/docs/scroll-padding
			 */
			"scroll-pb": [
				{
					"scroll-pb": getSpacingWithArbitrary(),
				},
			],
			/**
			 * Scroll Padding Left
			 * @see https://tailwindcss.com/docs/scroll-padding
			 */
			"scroll-pl": [
				{
					"scroll-pl": getSpacingWithArbitrary(),
				},
			],
			/**
			 * Scroll Snap Align
			 * @see https://tailwindcss.com/docs/scroll-snap-align
			 */
			"snap-align": [
				{
					snap: ["start", "end", "center", "align-none"],
				},
			],
			/**
			 * Scroll Snap Stop
			 * @see https://tailwindcss.com/docs/scroll-snap-stop
			 */
			"snap-stop": [
				{
					snap: ["normal", "always"],
				},
			],
			/**
			 * Scroll Snap Type
			 * @see https://tailwindcss.com/docs/scroll-snap-type
			 */
			"snap-type": [
				{
					snap: ["none", "x", "y", "both"],
				},
			],
			/**
			 * Scroll Snap Type Strictness
			 * @see https://tailwindcss.com/docs/scroll-snap-type
			 */
			"snap-strictness": [
				{
					snap: ["mandatory", "proximity"],
				},
			],
			/**
			 * Touch Action
			 * @see https://tailwindcss.com/docs/touch-action
			 */
			"touch": [
				{
					touch: ["auto", "none", "manipulation"],
				},
			],
			/**
			 * Touch Action X
			 * @see https://tailwindcss.com/docs/touch-action
			 */
			"touch-x": [
				{
					"touch-pan": ["x", "left", "right"],
				},
			],
			/**
			 * Touch Action Y
			 * @see https://tailwindcss.com/docs/touch-action
			 */
			"touch-y": [
				{
					"touch-pan": ["y", "up", "down"],
				},
			],
			/**
			 * Touch Action Pinch Zoom
			 * @see https://tailwindcss.com/docs/touch-action
			 */
			"touch-pz": ["touch-pinch-zoom"],
			/**
			 * User Select
			 * @see https://tailwindcss.com/docs/user-select
			 */
			"select": [
				{
					select: ["none", "text", "all", "auto"],
				},
			],
			/**
			 * Will Change
			 * @see https://tailwindcss.com/docs/will-change
			 */
			"will-change": [
				{
					"will-change": ["auto", "scroll", "contents", "transform", isArbitraryValue],
				},
			],
			// SVG
			/**
			 * Fill
			 * @see https://tailwindcss.com/docs/fill
			 */
			"fill": [
				{
					fill: [colors, "none"],
				},
			],
			/**
			 * Stroke Width
			 * @see https://tailwindcss.com/docs/stroke-width
			 */
			"stroke-w": [
				{
					stroke: [isLength, isArbitraryLength, isArbitraryNumber],
				},
			],
			/**
			 * Stroke
			 * @see https://tailwindcss.com/docs/stroke
			 */
			"stroke": [
				{
					stroke: [colors, "none"],
				},
			],
			// Accessibility
			/**
			 * Screen Readers
			 * @see https://tailwindcss.com/docs/screen-readers
			 */
			"sr": ["sr-only", "not-sr-only"],
			/**
			 * Forced Color Adjust
			 * @see https://tailwindcss.com/docs/forced-color-adjust
			 */
			"forced-color-adjust": [
				{
					"forced-color-adjust": ["auto", "none"],
				},
			],
		},
		conflictingClassGroups: {
			"overflow": ["overflow-x", "overflow-y"],
			"overscroll": ["overscroll-x", "overscroll-y"],
			"inset": ["inset-x", "inset-y", "start", "end", "top", "right", "bottom", "left"],
			"inset-x": ["right", "left"],
			"inset-y": ["top", "bottom"],
			"flex": ["basis", "grow", "shrink"],
			"gap": ["gap-x", "gap-y"],
			"p": ["px", "py", "ps", "pe", "pt", "pr", "pb", "pl"],
			"px": ["pr", "pl"],
			"py": ["pt", "pb"],
			"m": ["mx", "my", "ms", "me", "mt", "mr", "mb", "ml"],
			"mx": ["mr", "ml"],
			"my": ["mt", "mb"],
			"size": ["w", "h"],
			"font-size": ["leading"],
			"fvn-normal": ["fvn-ordinal", "fvn-slashed-zero", "fvn-figure", "fvn-spacing", "fvn-fraction"],
			"fvn-ordinal": ["fvn-normal"],
			"fvn-slashed-zero": ["fvn-normal"],
			"fvn-figure": ["fvn-normal"],
			"fvn-spacing": ["fvn-normal"],
			"fvn-fraction": ["fvn-normal"],
			"line-clamp": ["display", "overflow"],
			"rounded": [
				"rounded-s",
				"rounded-e",
				"rounded-t",
				"rounded-r",
				"rounded-b",
				"rounded-l",
				"rounded-ss",
				"rounded-se",
				"rounded-ee",
				"rounded-es",
				"rounded-tl",
				"rounded-tr",
				"rounded-br",
				"rounded-bl",
			],
			"rounded-s": ["rounded-ss", "rounded-es"],
			"rounded-e": ["rounded-se", "rounded-ee"],
			"rounded-t": ["rounded-tl", "rounded-tr"],
			"rounded-r": ["rounded-tr", "rounded-br"],
			"rounded-b": ["rounded-br", "rounded-bl"],
			"rounded-l": ["rounded-tl", "rounded-bl"],
			"border-spacing": ["border-spacing-x", "border-spacing-y"],
			"border-w": ["border-w-s", "border-w-e", "border-w-t", "border-w-r", "border-w-b", "border-w-l"],
			"border-w-x": ["border-w-r", "border-w-l"],
			"border-w-y": ["border-w-t", "border-w-b"],
			"border-color": ["border-color-s", "border-color-e", "border-color-t", "border-color-r", "border-color-b", "border-color-l"],
			"border-color-x": ["border-color-r", "border-color-l"],
			"border-color-y": ["border-color-t", "border-color-b"],
			"scroll-m": ["scroll-mx", "scroll-my", "scroll-ms", "scroll-me", "scroll-mt", "scroll-mr", "scroll-mb", "scroll-ml"],
			"scroll-mx": ["scroll-mr", "scroll-ml"],
			"scroll-my": ["scroll-mt", "scroll-mb"],
			"scroll-p": ["scroll-px", "scroll-py", "scroll-ps", "scroll-pe", "scroll-pt", "scroll-pr", "scroll-pb", "scroll-pl"],
			"scroll-px": ["scroll-pr", "scroll-pl"],
			"scroll-py": ["scroll-pt", "scroll-pb"],
			"touch": ["touch-x", "touch-y", "touch-pz"],
			"touch-x": ["touch"],
			"touch-y": ["touch"],
			"touch-pz": ["touch"],
		},
		conflictingClassGroupModifiers: {
			"font-size": ["leading"],
		},
	};
};
const twMerge = /*#__PURE__*/ createTailwindMerge(getDefaultConfig);

// src/primitive.tsx
function composeEventHandlers(originalEventHandler, ourEventHandler, { checkForDefaultPrevented = true } = {}) {
	return function handleEvent(event) {
		originalEventHandler?.(event);
		if (checkForDefaultPrevented === false || !event.defaultPrevented) {
			return ourEventHandler?.(event);
		}
	};
}

// src/create-context.tsx
const React$w = await importShared("react");
function createContextScope(scopeName, createContextScopeDeps = []) {
	let defaultContexts = [];
	function createContext3(rootComponentName, defaultContext) {
		const BaseContext = React$w.createContext(defaultContext);
		BaseContext.displayName = rootComponentName + "Context";
		const index = defaultContexts.length;
		defaultContexts = [...defaultContexts, defaultContext];
		const Provider = (props) => {
			const { scope, children, ...context } = props;
			const Context = scope?.[scopeName]?.[index] || BaseContext;
			const value = React$w.useMemo(() => context, Object.values(context));
			return /* @__PURE__ */ jsxRuntimeExports.jsx(Context.Provider, { value, children });
		};
		Provider.displayName = rootComponentName + "Provider";
		function useContext2(consumerName, scope) {
			const Context = scope?.[scopeName]?.[index] || BaseContext;
			const context = React$w.useContext(Context);
			if (context) return context;
			if (defaultContext !== void 0) return defaultContext;
			throw new Error(`\`${consumerName}\` must be used within \`${rootComponentName}\``);
		}
		return [Provider, useContext2];
	}
	const createScope = () => {
		const scopeContexts = defaultContexts.map((defaultContext) => {
			return React$w.createContext(defaultContext);
		});
		return function useScope(scope) {
			const contexts = scope?.[scopeName] || scopeContexts;
			return React$w.useMemo(() => ({ [`__scope${scopeName}`]: { ...scope, [scopeName]: contexts } }), [scope, contexts]);
		};
	};
	createScope.scopeName = scopeName;
	return [createContext3, composeContextScopes(createScope, ...createContextScopeDeps)];
}
function composeContextScopes(...scopes) {
	const baseScope = scopes[0];
	if (scopes.length === 1) return baseScope;
	const createScope = () => {
		const scopeHooks = scopes.map((createScope2) => ({
			useScope: createScope2(),
			scopeName: createScope2.scopeName,
		}));
		return function useComposedScopes(overrideScopes) {
			const nextScopes = scopeHooks.reduce((nextScopes2, { useScope, scopeName }) => {
				const scopeProps = useScope(overrideScopes);
				const currentScope = scopeProps[`__scope${scopeName}`];
				return { ...nextScopes2, ...currentScope };
			}, {});
			return React$w.useMemo(() => ({ [`__scope${baseScope.scopeName}`]: nextScopes }), [nextScopes]);
		};
	};
	createScope.scopeName = baseScope.scopeName;
	return createScope;
}

// src/compose-refs.tsx
const React$v = await importShared("react");

function setRef$1(ref, value) {
	if (typeof ref === "function") {
		return ref(value);
	} else if (ref !== null && ref !== void 0) {
		ref.current = value;
	}
}
function composeRefs(...refs) {
	return (node) => {
		let hasCleanup = false;
		const cleanups = refs.map((ref) => {
			const cleanup = setRef$1(ref, node);
			if (!hasCleanup && typeof cleanup == "function") {
				hasCleanup = true;
			}
			return cleanup;
		});
		if (hasCleanup) {
			return () => {
				for (let i = 0; i < cleanups.length; i++) {
					const cleanup = cleanups[i];
					if (typeof cleanup == "function") {
						cleanup();
					} else {
						setRef$1(refs[i], null);
					}
				}
			};
		}
	};
}
function useComposedRefs(...refs) {
	return React$v.useCallback(composeRefs(...refs), refs);
}

// src/slot.tsx
const React$u = await importShared("react");
// @__NO_SIDE_EFFECTS__
function createSlot(ownerName) {
	const Slot2 = React$u.forwardRef((props, forwardedRef) => {
		let { children, ...slotProps } = props;
		let slottableElement = null;
		let hasSlottable = false;
		const newChildren = [];
		if (isLazyComponent(children) && typeof use === "function") {
			children = use(children._payload);
		}
		React$u.Children.forEach(children, (maybeSlottable) => {
			if (isSlottable(maybeSlottable)) {
				hasSlottable = true;
				const slottable = maybeSlottable;
				let child = "child" in slottable.props ? slottable.props.child : slottable.props.children;
				if (isLazyComponent(child) && typeof use === "function") {
					child = use(child._payload);
				}
				slottableElement = getSlottableElementFromSlottable(slottable, child);
				newChildren.push(slottableElement?.props?.children);
			} else {
				newChildren.push(maybeSlottable);
			}
		});
		if (slottableElement) {
			slottableElement = React$u.cloneElement(slottableElement, void 0, newChildren);
		} else if (
			// A `Slottable` was found but it didn't resolve to a single element (e.g.
			// it wrapped multiple elements, text, or a render-prop `child` that
			// wasn't an element). Don't fall back to treating the `Slottable` wrapper
			// itself as the slot target — throw a descriptive error below instead.
			!hasSlottable &&
			React$u.Children.count(children) === 1 &&
			React$u.isValidElement(children)
		) {
			slottableElement = children;
		}
		const slottableElementRef = slottableElement ? getElementRef$1(slottableElement) : void 0;
		const composedRef = useComposedRefs(forwardedRef, slottableElementRef);
		if (!slottableElement) {
			if (children || children === 0) {
				throw new Error(hasSlottable ? createSlottableError(ownerName) : createSlotError(ownerName));
			}
			return children;
		}
		const mergedProps = mergeProps(slotProps, slottableElement.props ?? {});
		if (slottableElement.type !== React$u.Fragment) {
			mergedProps.ref = forwardedRef ? composedRef : slottableElementRef;
		}
		return React$u.cloneElement(slottableElement, mergedProps);
	});
	Slot2.displayName = `${ownerName}.Slot`;
	return Slot2;
}
var Slot$1 = /* @__PURE__ */ createSlot("Slot");
var SLOTTABLE_IDENTIFIER = Symbol.for("radix.slottable");
// @__NO_SIDE_EFFECTS__
function createSlottable(ownerName) {
	const Slottable2 = (props) => ("child" in props ? props.children(props.child) : props.children);
	Slottable2.displayName = `${ownerName}.Slottable`;
	Slottable2.__radixId = SLOTTABLE_IDENTIFIER;
	return Slottable2;
}
var getSlottableElementFromSlottable = (slottable, child) => {
	if ("child" in slottable.props) {
		const child2 = slottable.props.child;
		if (!React$u.isValidElement(child2)) return null;
		return React$u.cloneElement(child2, void 0, slottable.props.children(child2.props.children));
	}
	return React$u.isValidElement(child) ? child : null;
};
function mergeProps(slotProps, childProps) {
	const overrideProps = { ...childProps };
	for (const propName in childProps) {
		const slotPropValue = slotProps[propName];
		const childPropValue = childProps[propName];
		const isHandler = /^on[A-Z]/.test(propName);
		if (isHandler) {
			if (slotPropValue && childPropValue) {
				overrideProps[propName] = (...args) => {
					const result = childPropValue(...args);
					slotPropValue(...args);
					return result;
				};
			} else if (slotPropValue) {
				overrideProps[propName] = slotPropValue;
			}
		} else if (propName === "style") {
			overrideProps[propName] = { ...slotPropValue, ...childPropValue };
		} else if (propName === "className") {
			overrideProps[propName] = [slotPropValue, childPropValue].filter(Boolean).join(" ");
		}
	}
	return { ...slotProps, ...overrideProps };
}
function getElementRef$1(element) {
	let getter = Object.getOwnPropertyDescriptor(element.props, "ref")?.get;
	let mayWarn = getter && "isReactWarning" in getter && getter.isReactWarning;
	if (mayWarn) {
		return element.ref;
	}
	getter = Object.getOwnPropertyDescriptor(element, "ref")?.get;
	mayWarn = getter && "isReactWarning" in getter && getter.isReactWarning;
	if (mayWarn) {
		return element.props.ref;
	}
	return element.props.ref || element.ref;
}
function isSlottable(child) {
	return React$u.isValidElement(child) && typeof child.type === "function" && "__radixId" in child.type && child.type.__radixId === SLOTTABLE_IDENTIFIER;
}
var REACT_LAZY_TYPE = Symbol.for("react.lazy");
function isLazyComponent(element) {
	return element != null && typeof element === "object" && "$$typeof" in element && element.$$typeof === REACT_LAZY_TYPE && "_payload" in element && isPromiseLike(element._payload);
}
function isPromiseLike(value) {
	return typeof value === "object" && value !== null && "then" in value;
}
var createSlotError = (ownerName) => {
	return `${ownerName} failed to slot onto its children. Expected a single React element child or \`Slottable\`.`;
};
var createSlottableError = (ownerName) => {
	return `${ownerName} failed to slot onto its \`Slottable\`. Expected \`Slottable\` to receive a single React element child.`;
};
var use = React$u[" use ".trim().toString()];

// src/collection-legacy.tsx
const React$t = await importShared("react");
function createCollection(name) {
	const PROVIDER_NAME = name + "CollectionProvider";
	const [createCollectionContext, createCollectionScope] = createContextScope(PROVIDER_NAME);
	const [CollectionProviderImpl, useCollectionContext] = createCollectionContext(PROVIDER_NAME, { collectionRef: { current: null }, itemMap: /* @__PURE__ */ new Map() });
	const CollectionProvider = (props) => {
		const { scope, children } = props;
		const ref = React$t.useRef(null);
		const itemMap = React$t.useRef(/* @__PURE__ */ new Map()).current;
		return /* @__PURE__ */ jsxRuntimeExports.jsx(CollectionProviderImpl, { scope, itemMap, collectionRef: ref, children });
	};
	CollectionProvider.displayName = PROVIDER_NAME;
	const COLLECTION_SLOT_NAME = name + "CollectionSlot";
	const CollectionSlotImpl = createSlot(COLLECTION_SLOT_NAME);
	const CollectionSlot = React$t.forwardRef((props, forwardedRef) => {
		const { scope, children } = props;
		const context = useCollectionContext(COLLECTION_SLOT_NAME, scope);
		const composedRefs = useComposedRefs(forwardedRef, context.collectionRef);
		return /* @__PURE__ */ jsxRuntimeExports.jsx(CollectionSlotImpl, { ref: composedRefs, children });
	});
	CollectionSlot.displayName = COLLECTION_SLOT_NAME;
	const ITEM_SLOT_NAME = name + "CollectionItemSlot";
	const ITEM_DATA_ATTR = "data-radix-collection-item";
	const CollectionItemSlotImpl = createSlot(ITEM_SLOT_NAME);
	const CollectionItemSlot = React$t.forwardRef((props, forwardedRef) => {
		const { scope, children, ...itemData } = props;
		const ref = React$t.useRef(null);
		const composedRefs = useComposedRefs(forwardedRef, ref);
		const context = useCollectionContext(ITEM_SLOT_NAME, scope);
		React$t.useEffect(() => {
			context.itemMap.set(ref, { ref, ...itemData });
			return () => void context.itemMap.delete(ref);
		});
		return /* @__PURE__ */ jsxRuntimeExports.jsx(CollectionItemSlotImpl, { ...{ [ITEM_DATA_ATTR]: "" }, ref: composedRefs, children });
	});
	CollectionItemSlot.displayName = ITEM_SLOT_NAME;
	function useCollection(scope) {
		const context = useCollectionContext(name + "CollectionConsumer", scope);
		const getItems = React$t.useCallback(() => {
			const collectionNode = context.collectionRef.current;
			if (!collectionNode) return [];
			const orderedNodes = Array.from(collectionNode.querySelectorAll(`[${ITEM_DATA_ATTR}]`));
			const items = Array.from(context.itemMap.values());
			const orderedItems = items.sort((a, b) => orderedNodes.indexOf(a.ref.current) - orderedNodes.indexOf(b.ref.current));
			return orderedItems;
		}, [context.collectionRef, context.itemMap]);
		return getItems;
	}
	return [{ Provider: CollectionProvider, Slot: CollectionSlot, ItemSlot: CollectionItemSlot }, useCollection, createCollectionScope];
}

// src/collection.tsx
await importShared("react");

// src/use-layout-effect.tsx
const React$s = await importShared("react");

var useLayoutEffect2 = globalThis?.document ? React$s.useLayoutEffect : () => {};

// src/id.tsx
const React$r = await importShared("react");
var useReactId = React$r[" useId ".trim().toString()] || (() => void 0);
var count$1 = 0;
function useId(deterministicId) {
	const [id, setId] = React$r.useState(useReactId());
	useLayoutEffect2(() => {
		setId((reactId) => reactId ?? String(count$1++));
	}, [deterministicId]);
	return id ? `radix-${id}` : "";
}

// src/primitive.tsx
const React$q = await importShared("react");

const ReactDOM$3 = await importShared("react-dom");
var NODES = ["a", "button", "div", "form", "h2", "h3", "img", "input", "label", "li", "nav", "ol", "p", "select", "span", "svg", "ul"];
var Primitive = NODES.reduce((primitive, node) => {
	const Slot = createSlot(`Primitive.${node}`);
	const Node = React$q.forwardRef((props, forwardedRef) => {
		const { asChild, ...primitiveProps } = props;
		const Comp = asChild ? Slot : node;
		if (typeof window !== "undefined") {
			window[Symbol.for("radix-ui")] = true;
		}
		return /* @__PURE__ */ jsxRuntimeExports.jsx(Comp, { ...primitiveProps, ref: forwardedRef });
	});
	Node.displayName = `Primitive.${node}`;
	return { ...primitive, [node]: Node };
}, {});
function dispatchDiscreteCustomEvent(target, event) {
	if (target) ReactDOM$3.flushSync(() => target.dispatchEvent(event));
}

// src/use-callback-ref.tsx
const React$p = await importShared("react");

function useCallbackRef$1(callback) {
	const callbackRef = React$p.useRef(callback);
	React$p.useEffect(() => {
		callbackRef.current = callback;
	});
	return React$p.useMemo(
		() =>
			(...args) =>
				callbackRef.current?.(...args),
		[],
	);
}

const React$o = await importShared("react");

var useReactEffectEvent = React$o[" useEffectEvent ".trim().toString()];
var useReactInsertionEffect = React$o[" useInsertionEffect ".trim().toString()];
function useEffectEvent(callback) {
	if (typeof useReactEffectEvent === "function") {
		return useReactEffectEvent(callback);
	}
	const ref = React$o.useRef(() => {
		throw new Error("Cannot call an event handler while rendering.");
	});
	if (typeof useReactInsertionEffect === "function") {
		useReactInsertionEffect(() => {
			ref.current = callback;
		});
	} else {
		useLayoutEffect2(() => {
			ref.current = callback;
		});
	}
	return React$o.useMemo(
		() =>
			(...args) =>
				ref.current?.(...args),
		[],
	);
}

// src/use-controllable-state.tsx
const React$n = await importShared("react");
var useInsertionEffect = React$n[" useInsertionEffect ".trim().toString()] || useLayoutEffect2;
function useControllableState({ prop, defaultProp, onChange = () => {}, caller }) {
	const [uncontrolledProp, setUncontrolledProp, onChangeRef] = useUncontrolledState({
		defaultProp,
		onChange,
	});
	const isControlled = prop !== void 0;
	const value = isControlled ? prop : uncontrolledProp;
	{
		const isControlledRef = React$n.useRef(prop !== void 0);
		React$n.useEffect(() => {
			const wasControlled = isControlledRef.current;
			if (wasControlled !== isControlled) {
				const from = wasControlled ? "controlled" : "uncontrolled";
				const to = isControlled ? "controlled" : "uncontrolled";
				console.warn(
					`${caller} is changing from ${from} to ${to}. Components should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled value for the lifetime of the component.`,
				);
			}
			isControlledRef.current = isControlled;
		}, [isControlled, caller]);
	}
	const setValue = React$n.useCallback(
		(nextValue) => {
			if (isControlled) {
				const value2 = isFunction$1(nextValue) ? nextValue(prop) : nextValue;
				if (value2 !== prop) {
					onChangeRef.current?.(value2);
				}
			} else {
				setUncontrolledProp(nextValue);
			}
		},
		[isControlled, prop, setUncontrolledProp, onChangeRef],
	);
	return [value, setValue];
}
function useUncontrolledState({ defaultProp, onChange }) {
	const [value, setValue] = React$n.useState(defaultProp);
	const prevValueRef = React$n.useRef(value);
	const onChangeRef = React$n.useRef(onChange);
	useInsertionEffect(() => {
		onChangeRef.current = onChange;
	}, [onChange]);
	React$n.useEffect(() => {
		if (prevValueRef.current !== value) {
			onChangeRef.current?.(value);
			prevValueRef.current = value;
		}
	}, [value, prevValueRef]);
	return [value, setValue, onChangeRef];
}
function isFunction$1(value) {
	return typeof value === "function";
}

// src/use-controllable-state-reducer.tsx
await importShared("react");

// src/direction.tsx
const React$m = await importShared("react");
var DirectionContext = React$m.createContext(void 0);
function useDirection(localDir) {
	const globalDir = React$m.useContext(DirectionContext);
	return localDir || globalDir || "ltr";
}

// src/roving-focus-group.tsx
const React$l = await importShared("react");
var ENTRY_FOCUS = "rovingFocusGroup.onEntryFocus";
var EVENT_OPTIONS$1 = { bubbles: false, cancelable: true };
var GROUP_NAME$1 = "RovingFocusGroup";
var [Collection$1, useCollection$1, createCollectionScope$1] = createCollection(GROUP_NAME$1);
var [createRovingFocusGroupContext, createRovingFocusGroupScope] = createContextScope(GROUP_NAME$1, [createCollectionScope$1]);
var [RovingFocusProvider, useRovingFocusContext] = createRovingFocusGroupContext(GROUP_NAME$1);
var RovingFocusGroup = React$l.forwardRef((props, forwardedRef) => {
	return /* @__PURE__ */ jsxRuntimeExports.jsx(Collection$1.Provider, {
		scope: props.__scopeRovingFocusGroup,
		children: /* @__PURE__ */ jsxRuntimeExports.jsx(Collection$1.Slot, {
			scope: props.__scopeRovingFocusGroup,
			children: /* @__PURE__ */ jsxRuntimeExports.jsx(RovingFocusGroupImpl, { ...props, ref: forwardedRef }),
		}),
	});
});
RovingFocusGroup.displayName = GROUP_NAME$1;
var RovingFocusGroupImpl = React$l.forwardRef((props, forwardedRef) => {
	const {
		__scopeRovingFocusGroup,
		orientation,
		loop = false,
		dir,
		currentTabStopId: currentTabStopIdProp,
		defaultCurrentTabStopId,
		onCurrentTabStopIdChange,
		onEntryFocus,
		preventScrollOnEntryFocus = false,
		...groupProps
	} = props;
	const ref = React$l.useRef(null);
	const composedRefs = useComposedRefs(forwardedRef, ref);
	const direction = useDirection(dir);
	const [currentTabStopId, setCurrentTabStopId] = useControllableState({
		prop: currentTabStopIdProp,
		defaultProp: defaultCurrentTabStopId ?? null,
		onChange: onCurrentTabStopIdChange,
		caller: GROUP_NAME$1,
	});
	const [isTabbingBackOut, setIsTabbingBackOut] = React$l.useState(false);
	const handleEntryFocus = useCallbackRef$1(onEntryFocus);
	const getItems = useCollection$1(__scopeRovingFocusGroup);
	const isClickFocusRef = React$l.useRef(false);
	const [focusableItemsCount, setFocusableItemsCount] = React$l.useState(0);
	React$l.useEffect(() => {
		const node = ref.current;
		if (node) {
			node.addEventListener(ENTRY_FOCUS, handleEntryFocus);
			return () => node.removeEventListener(ENTRY_FOCUS, handleEntryFocus);
		}
	}, [handleEntryFocus]);
	return /* @__PURE__ */ jsxRuntimeExports.jsx(RovingFocusProvider, {
		scope: __scopeRovingFocusGroup,
		orientation,
		dir: direction,
		loop,
		currentTabStopId,
		onItemFocus: React$l.useCallback((tabStopId) => setCurrentTabStopId(tabStopId), [setCurrentTabStopId]),
		onItemShiftTab: React$l.useCallback(() => setIsTabbingBackOut(true), []),
		onFocusableItemAdd: React$l.useCallback(() => setFocusableItemsCount((prevCount) => prevCount + 1), []),
		onFocusableItemRemove: React$l.useCallback(() => setFocusableItemsCount((prevCount) => prevCount - 1), []),
		children: /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.div, {
			"tabIndex": isTabbingBackOut || focusableItemsCount === 0 ? -1 : 0,
			"data-orientation": orientation,
			...groupProps,
			"ref": composedRefs,
			"style": { outline: "none", ...props.style },
			"onMouseDown": composeEventHandlers(props.onMouseDown, () => {
				isClickFocusRef.current = true;
			}),
			"onFocus": composeEventHandlers(props.onFocus, (event) => {
				const isKeyboardFocus = !isClickFocusRef.current;
				if (event.target === event.currentTarget && isKeyboardFocus && !isTabbingBackOut) {
					const entryFocusEvent = new CustomEvent(ENTRY_FOCUS, EVENT_OPTIONS$1);
					event.currentTarget.dispatchEvent(entryFocusEvent);
					if (!entryFocusEvent.defaultPrevented) {
						const items = getItems().filter((item) => item.focusable);
						const activeItem = items.find((item) => item.active);
						const currentItem = items.find((item) => item.id === currentTabStopId);
						const candidateItems = [activeItem, currentItem, ...items].filter(Boolean);
						const candidateNodes = candidateItems.map((item) => item.ref.current);
						focusFirst$1(candidateNodes, preventScrollOnEntryFocus);
					}
				}
				isClickFocusRef.current = false;
			}),
			"onBlur": composeEventHandlers(props.onBlur, () => setIsTabbingBackOut(false)),
		}),
	});
});
var ITEM_NAME$1 = "RovingFocusGroupItem";
var RovingFocusGroupItem = React$l.forwardRef((props, forwardedRef) => {
	const { __scopeRovingFocusGroup, focusable = true, active = false, tabStopId, children, ...itemProps } = props;
	const autoId = useId();
	const id = tabStopId || autoId;
	const context = useRovingFocusContext(ITEM_NAME$1, __scopeRovingFocusGroup);
	const isCurrentTabStop = context.currentTabStopId === id;
	const getItems = useCollection$1(__scopeRovingFocusGroup);
	const { onFocusableItemAdd, onFocusableItemRemove, currentTabStopId } = context;
	React$l.useEffect(() => {
		if (focusable) {
			onFocusableItemAdd();
			return () => onFocusableItemRemove();
		}
	}, [focusable, onFocusableItemAdd, onFocusableItemRemove]);
	return /* @__PURE__ */ jsxRuntimeExports.jsx(Collection$1.ItemSlot, {
		scope: __scopeRovingFocusGroup,
		id,
		focusable,
		active,
		children: /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.span, {
			"tabIndex": isCurrentTabStop ? 0 : -1,
			"data-orientation": context.orientation,
			...itemProps,
			"ref": forwardedRef,
			"onMouseDown": composeEventHandlers(props.onMouseDown, (event) => {
				if (!focusable) event.preventDefault();
				else context.onItemFocus(id);
			}),
			"onFocus": composeEventHandlers(props.onFocus, () => context.onItemFocus(id)),
			"onKeyDown": composeEventHandlers(props.onKeyDown, (event) => {
				if (event.key === "Tab" && event.shiftKey) {
					context.onItemShiftTab();
					return;
				}
				if (event.target !== event.currentTarget) return;
				const focusIntent = getFocusIntent(event, context.orientation, context.dir);
				if (focusIntent !== void 0) {
					if (event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) return;
					event.preventDefault();
					const items = getItems().filter((item) => item.focusable);
					let candidateNodes = items.map((item) => item.ref.current);
					if (focusIntent === "last") candidateNodes.reverse();
					else if (focusIntent === "prev" || focusIntent === "next") {
						if (focusIntent === "prev") candidateNodes.reverse();
						const currentIndex = candidateNodes.indexOf(event.currentTarget);
						candidateNodes = context.loop ? wrapArray$1(candidateNodes, currentIndex + 1) : candidateNodes.slice(currentIndex + 1);
					}
					setTimeout(() => focusFirst$1(candidateNodes));
				}
			}),
			"children": typeof children === "function" ? children({ isCurrentTabStop, hasTabStop: currentTabStopId != null }) : children,
		}),
	});
});
RovingFocusGroupItem.displayName = ITEM_NAME$1;
var MAP_KEY_TO_FOCUS_INTENT = {
	ArrowLeft: "prev",
	ArrowUp: "prev",
	ArrowRight: "next",
	ArrowDown: "next",
	PageUp: "first",
	Home: "first",
	PageDown: "last",
	End: "last",
};
function getDirectionAwareKey(key, dir) {
	if (dir !== "rtl") return key;
	return (
		key === "ArrowLeft" ? "ArrowRight"
		: key === "ArrowRight" ? "ArrowLeft"
		: key
	);
}
function getFocusIntent(event, orientation, dir) {
	const key = getDirectionAwareKey(event.key, dir);
	if (orientation === "vertical" && ["ArrowLeft", "ArrowRight"].includes(key)) return void 0;
	if (orientation === "horizontal" && ["ArrowUp", "ArrowDown"].includes(key)) return void 0;
	return MAP_KEY_TO_FOCUS_INTENT[key];
}
function focusFirst$1(candidates, preventScroll = false) {
	const PREVIOUSLY_FOCUSED_ELEMENT = document.activeElement;
	for (const candidate of candidates) {
		if (candidate === PREVIOUSLY_FOCUSED_ELEMENT) return;
		candidate.focus({ preventScroll });
		if (document.activeElement !== PREVIOUSLY_FOCUSED_ELEMENT) return;
	}
}
function wrapArray$1(array, startIndex) {
	return array.map((_, index) => array[(startIndex + index) % array.length]);
}
var Root$3 = RovingFocusGroup;
var Item = RovingFocusGroupItem;

// src/presence.tsx
const React2 = await importShared("react");

// src/use-state-machine.tsx
const React$k = await importShared("react");

function useStateMachine(initialState, machine) {
	return React$k.useReducer((state, event) => {
		const nextState = machine[state][event];
		return nextState ?? state;
	}, initialState);
}

// src/presence.tsx
var Presence = (props) => {
	const { present, children } = props;
	const presence = usePresence(present);
	const child = typeof children === "function" ? children({ present: presence.isPresent }) : React2.Children.only(children);
	const ref = useStableComposedRefs(presence.ref, getElementRef(child));
	const forceMount = typeof children === "function";
	return forceMount || presence.isPresent ? React2.cloneElement(child, { ref }) : null;
};
Presence.displayName = "Presence";
function usePresence(present) {
	const [node, setNode] = React2.useState();
	const stylesRef = React2.useRef(null);
	const prevPresentRef = React2.useRef(present);
	const prevAnimationNameRef = React2.useRef("none");
	const initialState = present ? "mounted" : "unmounted";
	const [state, send] = useStateMachine(initialState, {
		mounted: {
			UNMOUNT: "unmounted",
			ANIMATION_OUT: "unmountSuspended",
		},
		unmountSuspended: {
			MOUNT: "mounted",
			ANIMATION_END: "unmounted",
		},
		unmounted: {
			MOUNT: "mounted",
		},
	});
	React2.useEffect(() => {
		const currentAnimationName = getAnimationName(stylesRef.current);
		prevAnimationNameRef.current = state === "mounted" ? currentAnimationName : "none";
	}, [state]);
	useLayoutEffect2(() => {
		const styles = stylesRef.current;
		const wasPresent = prevPresentRef.current;
		const hasPresentChanged = wasPresent !== present;
		if (hasPresentChanged) {
			const prevAnimationName = prevAnimationNameRef.current;
			const currentAnimationName = getAnimationName(styles);
			if (present) {
				send("MOUNT");
			} else if (currentAnimationName === "none" || styles?.display === "none") {
				send("UNMOUNT");
			} else {
				const isAnimating = prevAnimationName !== currentAnimationName;
				if (wasPresent && isAnimating) {
					send("ANIMATION_OUT");
				} else {
					send("UNMOUNT");
				}
			}
			prevPresentRef.current = present;
		}
	}, [present, send]);
	useLayoutEffect2(() => {
		if (node) {
			let timeoutId;
			const ownerWindow = node.ownerDocument.defaultView ?? window;
			const handleAnimationEnd = (event) => {
				const currentAnimationName = getAnimationName(stylesRef.current);
				const isCurrentAnimation = currentAnimationName.includes(CSS.escape(event.animationName));
				if (event.target === node && isCurrentAnimation) {
					send("ANIMATION_END");
					if (!prevPresentRef.current) {
						const currentFillMode = node.style.animationFillMode;
						node.style.animationFillMode = "forwards";
						timeoutId = ownerWindow.setTimeout(() => {
							if (node.style.animationFillMode === "forwards") {
								node.style.animationFillMode = currentFillMode;
							}
						});
					}
				}
			};
			const handleAnimationStart = (event) => {
				if (event.target === node) {
					prevAnimationNameRef.current = getAnimationName(stylesRef.current);
				}
			};
			node.addEventListener("animationstart", handleAnimationStart);
			node.addEventListener("animationcancel", handleAnimationEnd);
			node.addEventListener("animationend", handleAnimationEnd);
			return () => {
				ownerWindow.clearTimeout(timeoutId);
				node.removeEventListener("animationstart", handleAnimationStart);
				node.removeEventListener("animationcancel", handleAnimationEnd);
				node.removeEventListener("animationend", handleAnimationEnd);
			};
		} else {
			send("ANIMATION_END");
		}
	}, [node, send]);
	return {
		isPresent: ["mounted", "unmountSuspended"].includes(state),
		ref: React2.useCallback((node2) => {
			stylesRef.current = node2 ? getComputedStyle(node2) : null;
			setNode(node2);
		}, []),
	};
}
function setRef(ref, value) {
	if (typeof ref === "function") {
		return ref(value);
	} else if (ref !== null && ref !== void 0) {
		ref.current = value;
	}
}
function useStableComposedRefs(...refs) {
	const refsRef = React2.useRef(refs);
	refsRef.current = refs;
	return React2.useCallback((node) => {
		const currentRefs = refsRef.current;
		let hasCleanup = false;
		const cleanups = currentRefs.map((ref) => {
			const cleanup = setRef(ref, node);
			if (!hasCleanup && typeof cleanup === "function") {
				hasCleanup = true;
			}
			return cleanup;
		});
		if (hasCleanup) {
			return () => {
				for (let i = 0; i < cleanups.length; i++) {
					const cleanup = cleanups[i];
					if (typeof cleanup === "function") {
						cleanup();
					} else {
						setRef(currentRefs[i], null);
					}
				}
			};
		}
	}, []);
}
function getAnimationName(styles) {
	return styles?.animationName || "none";
}
function getElementRef(element) {
	let getter = Object.getOwnPropertyDescriptor(element.props, "ref")?.get;
	let mayWarn = getter && "isReactWarning" in getter && getter.isReactWarning;
	if (mayWarn) {
		return element.ref;
	}
	getter = Object.getOwnPropertyDescriptor(element, "ref")?.get;
	mayWarn = getter && "isReactWarning" in getter && getter.isReactWarning;
	if (mayWarn) {
		return element.props.ref;
	}
	return element.props.ref || element.ref;
}

// src/tabs.tsx
const React$j = await importShared("react");
var TABS_NAME = "Tabs";
var [createTabsContext] = createContextScope(TABS_NAME, [createRovingFocusGroupScope]);
var useRovingFocusGroupScope = createRovingFocusGroupScope();
var [TabsProvider, useTabsContext] = createTabsContext(TABS_NAME);
var Tabs = React$j.forwardRef((props, forwardedRef) => {
	const { __scopeTabs, value: valueProp, onValueChange, defaultValue, orientation = "horizontal", dir, activationMode = "automatic", ...tabsProps } = props;
	const direction = useDirection(dir);
	const [value, setValue] = useControllableState({
		prop: valueProp,
		onChange: onValueChange,
		defaultProp: defaultValue ?? "",
		caller: TABS_NAME,
	});
	return /* @__PURE__ */ jsxRuntimeExports.jsx(TabsProvider, {
		scope: __scopeTabs,
		baseId: useId(),
		value,
		onValueChange: setValue,
		orientation,
		dir: direction,
		activationMode,
		children: /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.div, {
			"dir": direction,
			"data-orientation": orientation,
			...tabsProps,
			"ref": forwardedRef,
		}),
	});
});
Tabs.displayName = TABS_NAME;
var TAB_LIST_NAME = "TabsList";
var TabsList = React$j.forwardRef((props, forwardedRef) => {
	const { __scopeTabs, loop = true, ...listProps } = props;
	const context = useTabsContext(TAB_LIST_NAME, __scopeTabs);
	const rovingFocusGroupScope = useRovingFocusGroupScope(__scopeTabs);
	return /* @__PURE__ */ jsxRuntimeExports.jsx(Root$3, {
		asChild: true,
		...rovingFocusGroupScope,
		orientation: context.orientation,
		dir: context.dir,
		loop,
		children: /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.div, {
			"role": "tablist",
			"aria-orientation": context.orientation,
			...listProps,
			"ref": forwardedRef,
		}),
	});
});
TabsList.displayName = TAB_LIST_NAME;
var TRIGGER_NAME$1 = "TabsTrigger";
var TabsTrigger = React$j.forwardRef((props, forwardedRef) => {
	const { __scopeTabs, value, disabled = false, ...triggerProps } = props;
	const context = useTabsContext(TRIGGER_NAME$1, __scopeTabs);
	const rovingFocusGroupScope = useRovingFocusGroupScope(__scopeTabs);
	const triggerId = makeTriggerId(context.baseId, value);
	const contentId = makeContentId(context.baseId, value);
	const isSelected = value === context.value;
	return /* @__PURE__ */ jsxRuntimeExports.jsx(Item, {
		asChild: true,
		...rovingFocusGroupScope,
		focusable: !disabled,
		active: isSelected,
		children: /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.button, {
			"type": "button",
			"role": "tab",
			"aria-selected": isSelected,
			"aria-controls": contentId,
			"data-state": isSelected ? "active" : "inactive",
			"data-disabled": disabled ? "" : void 0,
			disabled,
			"id": triggerId,
			...triggerProps,
			"ref": forwardedRef,
			"onMouseDown": composeEventHandlers(props.onMouseDown, (event) => {
				if (!disabled && event.button === 0 && event.ctrlKey === false) {
					context.onValueChange(value);
				} else {
					event.preventDefault();
				}
			}),
			"onKeyDown": composeEventHandlers(props.onKeyDown, (event) => {
				if ([" ", "Enter"].includes(event.key)) context.onValueChange(value);
			}),
			"onFocus": composeEventHandlers(props.onFocus, () => {
				const isAutomaticActivation = context.activationMode !== "manual";
				if (!isSelected && !disabled && isAutomaticActivation) {
					context.onValueChange(value);
				}
			}),
		}),
	});
});
TabsTrigger.displayName = TRIGGER_NAME$1;
var CONTENT_NAME$2 = "TabsContent";
var TabsContent = React$j.forwardRef((props, forwardedRef) => {
	const { __scopeTabs, value, forceMount, children, ...contentProps } = props;
	const context = useTabsContext(CONTENT_NAME$2, __scopeTabs);
	const triggerId = makeTriggerId(context.baseId, value);
	const contentId = makeContentId(context.baseId, value);
	const isSelected = value === context.value;
	const isMountAnimationPreventedRef = React$j.useRef(isSelected);
	React$j.useEffect(() => {
		const rAF = requestAnimationFrame(() => (isMountAnimationPreventedRef.current = false));
		return () => cancelAnimationFrame(rAF);
	}, []);
	return /* @__PURE__ */ jsxRuntimeExports.jsx(Presence, {
		present: forceMount || isSelected,
		children: ({ present }) =>
			/* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.div, {
				"data-state": isSelected ? "active" : "inactive",
				"data-orientation": context.orientation,
				"role": "tabpanel",
				"aria-labelledby": triggerId,
				"hidden": !present,
				"id": contentId,
				"tabIndex": 0,
				...contentProps,
				"ref": forwardedRef,
				"style": {
					...props.style,
					animationDuration: isMountAnimationPreventedRef.current ? "0s" : void 0,
				},
				"children": present && children,
			}),
	});
});
TabsContent.displayName = CONTENT_NAME$2;
function makeTriggerId(baseId, value) {
	return `${baseId}-trigger-${value}`;
}
function makeContentId(baseId, value) {
	return `${baseId}-content-${value}`;
}
var Root2$1 = Tabs;
var List = TabsList;
var Trigger = TabsTrigger;
var Content$1 = TabsContent;

// src/number.ts
function clamp$1(value, [min, max]) {
	return Math.min(max, Math.max(min, value));
}

// src/dismissable-layer.tsx
const React$i = await importShared("react");
var DISMISSABLE_LAYER_NAME = "DismissableLayer";
var CONTEXT_UPDATE = "dismissableLayer.update";
var POINTER_DOWN_OUTSIDE = "dismissableLayer.pointerDownOutside";
var FOCUS_OUTSIDE = "dismissableLayer.focusOutside";
var originalBodyPointerEvents;
var DismissableLayerContext = React$i.createContext({
	layers: /* @__PURE__ */ new Set(),
	layersWithOutsidePointerEventsDisabled: /* @__PURE__ */ new Set(),
	branches: /* @__PURE__ */ new Set(),
	// Outside elements that belong to a layer's own dismiss affordance (eg, a
	// dialog overlay). Pressing them should dismiss the layer regardless of
	// whether or not they stop propagation.
	//
	// See https://github.com/radix-ui/primitives/issues/3346
	dismissableSurfaces: /* @__PURE__ */ new Set(),
});
var DismissableLayer = React$i.forwardRef((props, forwardedRef) => {
	const { disableOutsidePointerEvents = false, deferPointerDownOutside = false, onEscapeKeyDown, onPointerDownOutside, onFocusOutside, onInteractOutside, onDismiss, ...layerProps } = props;
	const context = React$i.useContext(DismissableLayerContext);
	const [node, setNode] = React$i.useState(null);
	const ownerDocument = node?.ownerDocument ?? globalThis?.document;
	const [, force] = React$i.useState({});
	const composedRefs = useComposedRefs(forwardedRef, setNode);
	const layers = Array.from(context.layers);
	const [highestLayerWithOutsidePointerEventsDisabled] = [...context.layersWithOutsidePointerEventsDisabled].slice(-1);
	const highestLayerWithOutsidePointerEventsDisabledIndex = layers.indexOf(highestLayerWithOutsidePointerEventsDisabled);
	const index = node ? layers.indexOf(node) : -1;
	const isBodyPointerEventsDisabled = context.layersWithOutsidePointerEventsDisabled.size > 0;
	const isPointerEventsEnabled = index >= highestLayerWithOutsidePointerEventsDisabledIndex;
	const isDeferredPointerDownOutsideRef = React$i.useRef(false);
	const pointerDownOutside = usePointerDownOutside(
		(event) => {
			const target = event.target;
			if (!(target instanceof Node)) {
				return;
			}
			const isPointerDownOnBranch = [...context.branches].some((branch) => branch.contains(target));
			if (!isPointerEventsEnabled || isPointerDownOnBranch) return;
			onPointerDownOutside?.(event);
			onInteractOutside?.(event);
			if (!event.defaultPrevented) onDismiss?.();
		},
		{
			ownerDocument,
			deferPointerDownOutside,
			isDeferredPointerDownOutsideRef,
			dismissableSurfaces: context.dismissableSurfaces,
		},
	);
	const focusOutside = useFocusOutside((event) => {
		if (deferPointerDownOutside && isDeferredPointerDownOutsideRef.current) {
			return;
		}
		const target = event.target;
		const isFocusInBranch = [...context.branches].some((branch) => branch.contains(target));
		if (isFocusInBranch) return;
		onFocusOutside?.(event);
		onInteractOutside?.(event);
		if (!event.defaultPrevented) onDismiss?.();
	}, ownerDocument);
	const isHighestLayer = node ? index === layers.length - 1 : false;
	const handleKeyDown = useEffectEvent((event) => {
		if (event.key !== "Escape") {
			return;
		}
		onEscapeKeyDown?.(event);
		if (!event.defaultPrevented && onDismiss) {
			event.preventDefault();
			onDismiss();
		}
	});
	React$i.useEffect(() => {
		if (!isHighestLayer) {
			return;
		}
		ownerDocument.addEventListener("keydown", handleKeyDown, { capture: true });
		return () => ownerDocument.removeEventListener("keydown", handleKeyDown, { capture: true });
	}, [ownerDocument, isHighestLayer]);
	React$i.useEffect(() => {
		if (!node) return;
		if (disableOutsidePointerEvents) {
			if (context.layersWithOutsidePointerEventsDisabled.size === 0) {
				originalBodyPointerEvents = ownerDocument.body.style.pointerEvents;
				ownerDocument.body.style.pointerEvents = "none";
			}
			context.layersWithOutsidePointerEventsDisabled.add(node);
		}
		context.layers.add(node);
		dispatchUpdate();
		return () => {
			if (disableOutsidePointerEvents) {
				context.layersWithOutsidePointerEventsDisabled.delete(node);
				if (context.layersWithOutsidePointerEventsDisabled.size === 0) {
					ownerDocument.body.style.pointerEvents = originalBodyPointerEvents;
				}
			}
		};
	}, [node, ownerDocument, disableOutsidePointerEvents, context]);
	React$i.useEffect(() => {
		return () => {
			if (!node) return;
			context.layers.delete(node);
			context.layersWithOutsidePointerEventsDisabled.delete(node);
			dispatchUpdate();
		};
	}, [node, context]);
	React$i.useEffect(() => {
		const handleUpdate = () => force({});
		document.addEventListener(CONTEXT_UPDATE, handleUpdate);
		return () => document.removeEventListener(CONTEXT_UPDATE, handleUpdate);
	}, []);
	return /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.div, {
		...layerProps,
		ref: composedRefs,
		style: {
			pointerEvents:
				isBodyPointerEventsDisabled ?
					isPointerEventsEnabled ? "auto"
					:	"none"
				:	void 0,
			...props.style,
		},
		onFocusCapture: composeEventHandlers(props.onFocusCapture, focusOutside.onFocusCapture),
		onBlurCapture: composeEventHandlers(props.onBlurCapture, focusOutside.onBlurCapture),
		onPointerDownCapture: composeEventHandlers(props.onPointerDownCapture, pointerDownOutside.onPointerDownCapture),
	});
});
DismissableLayer.displayName = DISMISSABLE_LAYER_NAME;
var BRANCH_NAME = "DismissableLayerBranch";
var DismissableLayerBranch = React$i.forwardRef((props, forwardedRef) => {
	const context = React$i.useContext(DismissableLayerContext);
	const ref = React$i.useRef(null);
	const composedRefs = useComposedRefs(forwardedRef, ref);
	React$i.useEffect(() => {
		const node = ref.current;
		if (node) {
			context.branches.add(node);
			return () => {
				context.branches.delete(node);
			};
		}
	}, [context.branches]);
	return /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.div, { ...props, ref: composedRefs });
});
DismissableLayerBranch.displayName = BRANCH_NAME;
function useDismissableLayerSurface() {
	const context = React$i.useContext(DismissableLayerContext);
	const [node, setNode] = React$i.useState(null);
	React$i.useEffect(() => {
		if (!node) {
			return;
		}
		context.dismissableSurfaces.add(node);
		return () => {
			context.dismissableSurfaces.delete(node);
		};
	}, [node, context.dismissableSurfaces]);
	return setNode;
}
function usePointerDownOutside(onPointerDownOutside, args) {
	const { ownerDocument = globalThis?.document, deferPointerDownOutside = false, isDeferredPointerDownOutsideRef, dismissableSurfaces } = args;
	const handlePointerDownOutside = useCallbackRef$1(onPointerDownOutside);
	const isPointerInsideReactTreeRef = React$i.useRef(false);
	const isPointerDownOutsideRef = React$i.useRef(false);
	const interceptedOutsideInteractionEventsRef = React$i.useRef(/* @__PURE__ */ new Map());
	const handleClickRef = React$i.useRef(() => {});
	React$i.useEffect(() => {
		function resetOutsideInteraction() {
			isPointerDownOutsideRef.current = false;
			isDeferredPointerDownOutsideRef.current = false;
			interceptedOutsideInteractionEventsRef.current.clear();
		}
		function isOutsideInteractionIntercepted() {
			return Array.from(interceptedOutsideInteractionEventsRef.current.values()).some(Boolean);
		}
		function handleInteractionCapture(event) {
			if (!isPointerDownOutsideRef.current) {
				return;
			}
			const target = event.target;
			const isDismissableSurface = target instanceof Node && [...dismissableSurfaces].some((surface) => surface.contains(target));
			if (!isDismissableSurface) {
				interceptedOutsideInteractionEventsRef.current.set(event.type, true);
			}
			if (event.type === "click") {
				window.setTimeout(() => {
					if (isPointerDownOutsideRef.current) {
						handleClickRef.current();
					}
				}, 0);
			}
		}
		function handleInteractionBubble(event) {
			if (isPointerDownOutsideRef.current) {
				interceptedOutsideInteractionEventsRef.current.set(event.type, false);
			}
		}
		const handlePointerDown = (event) => {
			if (event.target && !isPointerInsideReactTreeRef.current) {
				let handleAndDispatchPointerDownOutsideEvent2 = function () {
					ownerDocument.removeEventListener("click", handleClickRef.current);
					const wasOutsideInteractionIntercepted = isOutsideInteractionIntercepted();
					resetOutsideInteraction();
					if (!wasOutsideInteractionIntercepted) {
						handleAndDispatchCustomEvent(POINTER_DOWN_OUTSIDE, handlePointerDownOutside, eventDetail, { discrete: true });
					}
				};
				const eventDetail = { originalEvent: event };
				isPointerDownOutsideRef.current = true;
				isDeferredPointerDownOutsideRef.current = deferPointerDownOutside && event.button === 0;
				interceptedOutsideInteractionEventsRef.current.clear();
				if (!deferPointerDownOutside || event.button !== 0) {
					handleAndDispatchPointerDownOutsideEvent2();
				} else {
					ownerDocument.removeEventListener("click", handleClickRef.current);
					handleClickRef.current = handleAndDispatchPointerDownOutsideEvent2;
					ownerDocument.addEventListener("click", handleClickRef.current, { once: true });
				}
			} else {
				ownerDocument.removeEventListener("click", handleClickRef.current);
				resetOutsideInteraction();
			}
			isPointerInsideReactTreeRef.current = false;
		};
		const outsideInteractionEvents = ["pointerup", "mousedown", "mouseup", "touchstart", "touchend", "click"];
		for (const eventName of outsideInteractionEvents) {
			ownerDocument.addEventListener(eventName, handleInteractionCapture, true);
			ownerDocument.addEventListener(eventName, handleInteractionBubble);
		}
		const timerId = window.setTimeout(() => {
			ownerDocument.addEventListener("pointerdown", handlePointerDown);
		}, 0);
		return () => {
			window.clearTimeout(timerId);
			ownerDocument.removeEventListener("pointerdown", handlePointerDown);
			ownerDocument.removeEventListener("click", handleClickRef.current);
			for (const eventName of outsideInteractionEvents) {
				ownerDocument.removeEventListener(eventName, handleInteractionCapture, true);
				ownerDocument.removeEventListener(eventName, handleInteractionBubble);
			}
		};
	}, [ownerDocument, handlePointerDownOutside, deferPointerDownOutside, isDeferredPointerDownOutsideRef, dismissableSurfaces]);
	return {
		// ensures we check React component tree (not just DOM tree)
		onPointerDownCapture: () => (isPointerInsideReactTreeRef.current = true),
	};
}
function useFocusOutside(onFocusOutside, ownerDocument = globalThis?.document) {
	const handleFocusOutside = useCallbackRef$1(onFocusOutside);
	const isFocusInsideReactTreeRef = React$i.useRef(false);
	React$i.useEffect(() => {
		const handleFocus = (event) => {
			if (event.target && !isFocusInsideReactTreeRef.current) {
				const eventDetail = { originalEvent: event };
				handleAndDispatchCustomEvent(FOCUS_OUTSIDE, handleFocusOutside, eventDetail, {
					discrete: false,
				});
			}
		};
		ownerDocument.addEventListener("focusin", handleFocus);
		return () => ownerDocument.removeEventListener("focusin", handleFocus);
	}, [ownerDocument, handleFocusOutside]);
	return {
		onFocusCapture: () => (isFocusInsideReactTreeRef.current = true),
		onBlurCapture: () => (isFocusInsideReactTreeRef.current = false),
	};
}
function dispatchUpdate() {
	const event = new CustomEvent(CONTEXT_UPDATE);
	document.dispatchEvent(event);
}
function handleAndDispatchCustomEvent(name, handler, detail, { discrete }) {
	const target = detail.originalEvent.target;
	const event = new CustomEvent(name, { bubbles: false, cancelable: true, detail });
	if (handler) target.addEventListener(name, handler, { once: true });
	if (discrete) {
		dispatchDiscreteCustomEvent(target, event);
	} else {
		target.dispatchEvent(event);
	}
}

// src/focus-scope.tsx
const React$h = await importShared("react");
var AUTOFOCUS_ON_MOUNT = "focusScope.autoFocusOnMount";
var AUTOFOCUS_ON_UNMOUNT = "focusScope.autoFocusOnUnmount";
var EVENT_OPTIONS = { bubbles: false, cancelable: true };
var FOCUS_SCOPE_NAME = "FocusScope";
var FocusScope = React$h.forwardRef((props, forwardedRef) => {
	const { loop = false, trapped = false, onMountAutoFocus: onMountAutoFocusProp, onUnmountAutoFocus: onUnmountAutoFocusProp, ...scopeProps } = props;
	const [container, setContainer] = React$h.useState(null);
	const onMountAutoFocus = useCallbackRef$1(onMountAutoFocusProp);
	const onUnmountAutoFocus = useCallbackRef$1(onUnmountAutoFocusProp);
	const lastFocusedElementRef = React$h.useRef(null);
	const composedRefs = useComposedRefs(forwardedRef, setContainer);
	const focusScope = React$h.useRef({
		paused: false,
		pause() {
			this.paused = true;
		},
		resume() {
			this.paused = false;
		},
	}).current;
	React$h.useEffect(() => {
		if (trapped) {
			let handleFocusIn2 = function (event) {
					if (focusScope.paused || !container) return;
					const target = event.target;
					if (container.contains(target)) {
						lastFocusedElementRef.current = target;
					} else {
						focus(lastFocusedElementRef.current, { select: true });
					}
				},
				handleFocusOut2 = function (event) {
					if (focusScope.paused || !container) return;
					const relatedTarget = event.relatedTarget;
					if (relatedTarget === null) return;
					if (!container.contains(relatedTarget)) {
						focus(lastFocusedElementRef.current, { select: true });
					}
				},
				handleMutations2 = function (mutations) {
					const focusedElement = document.activeElement;
					if (focusedElement !== document.body) return;
					for (const mutation of mutations) {
						if (mutation.removedNodes.length > 0) focus(container);
					}
				};
			document.addEventListener("focusin", handleFocusIn2);
			document.addEventListener("focusout", handleFocusOut2);
			const mutationObserver = new MutationObserver(handleMutations2);
			if (container) mutationObserver.observe(container, { childList: true, subtree: true });
			return () => {
				document.removeEventListener("focusin", handleFocusIn2);
				document.removeEventListener("focusout", handleFocusOut2);
				mutationObserver.disconnect();
			};
		}
	}, [trapped, container, focusScope.paused]);
	React$h.useEffect(() => {
		if (container) {
			focusScopesStack.add(focusScope);
			const previouslyFocusedElement = document.activeElement;
			const hasFocusedCandidate = container.contains(previouslyFocusedElement);
			if (!hasFocusedCandidate) {
				const mountEvent = new CustomEvent(AUTOFOCUS_ON_MOUNT, EVENT_OPTIONS);
				container.addEventListener(AUTOFOCUS_ON_MOUNT, onMountAutoFocus);
				container.dispatchEvent(mountEvent);
				if (!mountEvent.defaultPrevented) {
					focusFirst(removeLinks(getTabbableCandidates(container)), { select: true });
					if (document.activeElement === previouslyFocusedElement) {
						focus(container);
					}
				}
			}
			return () => {
				container.removeEventListener(AUTOFOCUS_ON_MOUNT, onMountAutoFocus);
				setTimeout(() => {
					const unmountEvent = new CustomEvent(AUTOFOCUS_ON_UNMOUNT, EVENT_OPTIONS);
					container.addEventListener(AUTOFOCUS_ON_UNMOUNT, onUnmountAutoFocus);
					container.dispatchEvent(unmountEvent);
					if (!unmountEvent.defaultPrevented) {
						focus(previouslyFocusedElement ?? document.body, { select: true });
					}
					container.removeEventListener(AUTOFOCUS_ON_UNMOUNT, onUnmountAutoFocus);
					focusScopesStack.remove(focusScope);
				}, 0);
			};
		}
	}, [container, onMountAutoFocus, onUnmountAutoFocus, focusScope]);
	const handleKeyDown = React$h.useCallback(
		(event) => {
			if (!loop && !trapped) return;
			if (focusScope.paused) return;
			const isTabKey = event.key === "Tab" && !event.altKey && !event.ctrlKey && !event.metaKey;
			const focusedElement = document.activeElement;
			if (isTabKey && focusedElement) {
				const container2 = event.currentTarget;
				const [first, last] = getTabbableEdges(container2);
				const hasTabbableElementsInside = first && last;
				if (!hasTabbableElementsInside) {
					if (focusedElement === container2) event.preventDefault();
				} else {
					if (!event.shiftKey && focusedElement === last) {
						event.preventDefault();
						if (loop) focus(first, { select: true });
					} else if (event.shiftKey && focusedElement === first) {
						event.preventDefault();
						if (loop) focus(last, { select: true });
					}
				}
			}
		},
		[loop, trapped, focusScope.paused],
	);
	return /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.div, { tabIndex: -1, ...scopeProps, ref: composedRefs, onKeyDown: handleKeyDown });
});
FocusScope.displayName = FOCUS_SCOPE_NAME;
function focusFirst(candidates, { select = false } = {}) {
	const previouslyFocusedElement = document.activeElement;
	for (const candidate of candidates) {
		focus(candidate, { select });
		if (document.activeElement !== previouslyFocusedElement) return;
	}
}
function getTabbableEdges(container) {
	const candidates = getTabbableCandidates(container);
	const first = findVisible(candidates, container);
	const last = findVisible(candidates.reverse(), container);
	return [first, last];
}
function getTabbableCandidates(container) {
	const nodes = [];
	const walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, {
		acceptNode: (node) => {
			const isHiddenInput = node.tagName === "INPUT" && node.type === "hidden";
			if (node.disabled || node.hidden || isHiddenInput) return NodeFilter.FILTER_SKIP;
			return node.tabIndex >= 0 ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
		},
	});
	while (walker.nextNode()) nodes.push(walker.currentNode);
	return nodes;
}
function findVisible(elements, container) {
	for (const element of elements) {
		if (!isHidden(element, { upTo: container })) return element;
	}
}
function isHidden(node, { upTo }) {
	if (getComputedStyle(node).visibility === "hidden") return true;
	while (node) {
		if (upTo !== void 0 && node === upTo) return false;
		if (getComputedStyle(node).display === "none") return true;
		node = node.parentElement;
	}
	return false;
}
function isSelectableInput(element) {
	return element instanceof HTMLInputElement && "select" in element;
}
function focus(element, { select = false } = {}) {
	if (element && element.focus) {
		const previouslyFocusedElement = document.activeElement;
		element.focus({ preventScroll: true });
		if (element !== previouslyFocusedElement && isSelectableInput(element) && select) element.select();
	}
}
var focusScopesStack = createFocusScopesStack();
function createFocusScopesStack() {
	let stack = [];
	return {
		add(focusScope) {
			const activeFocusScope = stack[0];
			if (focusScope !== activeFocusScope) {
				activeFocusScope?.pause();
			}
			stack = arrayRemove(stack, focusScope);
			stack.unshift(focusScope);
		},
		remove(focusScope) {
			stack = arrayRemove(stack, focusScope);
			stack[0]?.resume();
		},
	};
}
function arrayRemove(array, item) {
	const updatedArray = [...array];
	const index = updatedArray.indexOf(item);
	if (index !== -1) {
		updatedArray.splice(index, 1);
	}
	return updatedArray;
}
function removeLinks(items) {
	return items.filter((item) => item.tagName !== "A");
}

// src/portal.tsx
const React$g = await importShared("react");

const ReactDOM$2 = await importShared("react-dom");
var PORTAL_NAME$1 = "Portal";
var Portal = React$g.forwardRef((props, forwardedRef) => {
	const { container: containerProp, ...portalProps } = props;
	const [mounted, setMounted] = React$g.useState(false);
	useLayoutEffect2(() => setMounted(true), []);
	const container = containerProp || (mounted && globalThis?.document?.body);
	return container ? ReactDOM$2.createPortal(/* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.div, { ...portalProps, ref: forwardedRef }), container) : null;
});
Portal.displayName = PORTAL_NAME$1;

// src/focus-guards.tsx
const React$f = await importShared("react");

var count = 0;
var guards = null;
function useFocusGuards() {
	React$f.useEffect(() => {
		if (!guards) {
			guards = { start: createFocusGuard(), end: createFocusGuard() };
		}
		const { start, end } = guards;
		if (document.body.firstElementChild !== start) {
			document.body.insertAdjacentElement("afterbegin", start);
		}
		if (document.body.lastElementChild !== end) {
			document.body.insertAdjacentElement("beforeend", end);
		}
		count++;
		return () => {
			if (count === 1) {
				guards?.start.remove();
				guards?.end.remove();
				guards = null;
			}
			count = Math.max(0, count - 1);
		};
	}, []);
}
function createFocusGuard() {
	const element = document.createElement("span");
	element.setAttribute("data-radix-focus-guard", "");
	element.tabIndex = 0;
	element.style.outline = "none";
	element.style.opacity = "0";
	element.style.position = "fixed";
	element.style.pointerEvents = "none";
	return element;
}

/******************************************************************************
Copyright (c) Microsoft Corporation.

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */

var __assign = function () {
	__assign =
		Object.assign ||
		function __assign(t) {
			for (var s, i = 1, n = arguments.length; i < n; i++) {
				s = arguments[i];
				for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
			}
			return t;
		};
	return __assign.apply(this, arguments);
};

function __rest(s, e) {
	var t = {};
	for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p];
	if (s != null && typeof Object.getOwnPropertySymbols === "function")
		for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
			if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]];
		}
	return t;
}

function __spreadArray(to, from, pack) {
	if (pack || arguments.length === 2)
		for (var i = 0, l = from.length, ar; i < l; i++) {
			if (ar || !(i in from)) {
				if (!ar) ar = Array.prototype.slice.call(from, 0, i);
				ar[i] = from[i];
			}
		}
	return to.concat(ar || Array.prototype.slice.call(from));
}

typeof SuppressedError === "function" ? SuppressedError : (
	function (error, suppressed, message) {
		var e = new Error(message);
		return ((e.name = "SuppressedError"), (e.error = error), (e.suppressed = suppressed), e);
	}
);

var zeroRightClassName = "right-scroll-bar-position";
var fullWidthClassName = "width-before-scroll-bar";
var noScrollbarsClassName = "with-scroll-bars-hidden";
/**
 * Name of a CSS variable containing the amount of "hidden" scrollbar
 * ! might be undefined ! use will fallback!
 */
var removedBarSizeVariable = "--removed-body-scroll-bar-size";

/**
 * Assigns a value for a given ref, no matter of the ref format
 * @param {RefObject} ref - a callback function or ref object
 * @param value - a new value
 *
 * @see https://github.com/theKashey/use-callback-ref#assignref
 * @example
 * const refObject = useRef();
 * const refFn = (ref) => {....}
 *
 * assignRef(refObject, "refValue");
 * assignRef(refFn, "refValue");
 */
function assignRef(ref, value) {
	if (typeof ref === "function") {
		ref(value);
	} else if (ref) {
		ref.current = value;
	}
	return ref;
}

const { useState } = await importShared("react");

/**
 * creates a MutableRef with ref change callback
 * @param initialValue - initial ref value
 * @param {Function} callback - a callback to run when value changes
 *
 * @example
 * const ref = useCallbackRef(0, (newValue, oldValue) => console.log(oldValue, '->', newValue);
 * ref.current = 1;
 * // prints 0 -> 1
 *
 * @see https://reactjs.org/docs/hooks-reference.html#useref
 * @see https://github.com/theKashey/use-callback-ref#usecallbackref---to-replace-reactuseref
 * @returns {MutableRefObject}
 */
function useCallbackRef(initialValue, callback) {
	var ref = useState(function () {
		return {
			// value
			value: initialValue,
			// last callback
			callback: callback,
			// "memoized" public interface
			facade: {
				get current() {
					return ref.value;
				},
				set current(value) {
					var last = ref.value;
					if (last !== value) {
						ref.value = value;
						ref.callback(value, last);
					}
				},
			},
		};
	})[0];
	// update callback
	ref.callback = callback;
	return ref.facade;
}

const React$e = await importShared("react");
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? React$e.useLayoutEffect : React$e.useEffect;
var currentValues = new WeakMap();
/**
 * Merges two or more refs together providing a single interface to set their value
 * @param {RefObject|Ref} refs
 * @returns {MutableRefObject} - a new ref, which translates all changes to {refs}
 *
 * @see {@link mergeRefs} a version without buit-in memoization
 * @see https://github.com/theKashey/use-callback-ref#usemergerefs
 * @example
 * const Component = React.forwardRef((props, ref) => {
 *   const ownRef = useRef();
 *   const domRef = useMergeRefs([ref, ownRef]); // 👈 merge together
 *   return <div ref={domRef}>...</div>
 * }
 */
function useMergeRefs(refs, defaultValue) {
	var callbackRef = useCallbackRef(null, function (newValue) {
		return refs.forEach(function (ref) {
			return assignRef(ref, newValue);
		});
	});
	// handle refs changes - added or removed
	useIsomorphicLayoutEffect(
		function () {
			var oldValue = currentValues.get(callbackRef);
			if (oldValue) {
				var prevRefs_1 = new Set(oldValue);
				var nextRefs_1 = new Set(refs);
				var current_1 = callbackRef.current;
				prevRefs_1.forEach(function (ref) {
					if (!nextRefs_1.has(ref)) {
						assignRef(ref, null);
					}
				});
				nextRefs_1.forEach(function (ref) {
					if (!prevRefs_1.has(ref)) {
						assignRef(ref, current_1);
					}
				});
			}
			currentValues.set(callbackRef, refs);
		},
		[refs],
	);
	return callbackRef;
}

function ItoI(a) {
	return a;
}
function innerCreateMedium(defaults, middleware) {
	if (middleware === void 0) {
		middleware = ItoI;
	}
	var buffer = [];
	var assigned = false;
	var medium = {
		read: function () {
			if (assigned) {
				throw new Error("Sidecar: could not `read` from an `assigned` medium. `read` could be used only with `useMedium`.");
			}
			if (buffer.length) {
				return buffer[buffer.length - 1];
			}
			return defaults;
		},
		useMedium: function (data) {
			var item = middleware(data, assigned);
			buffer.push(item);
			return function () {
				buffer = buffer.filter(function (x) {
					return x !== item;
				});
			};
		},
		assignSyncMedium: function (cb) {
			assigned = true;
			while (buffer.length) {
				var cbs = buffer;
				buffer = [];
				cbs.forEach(cb);
			}
			buffer = {
				push: function (x) {
					return cb(x);
				},
				filter: function () {
					return buffer;
				},
			};
		},
		assignMedium: function (cb) {
			assigned = true;
			var pendingQueue = [];
			if (buffer.length) {
				var cbs = buffer;
				buffer = [];
				cbs.forEach(cb);
				pendingQueue = buffer;
			}
			var executeQueue = function () {
				var cbs = pendingQueue;
				pendingQueue = [];
				cbs.forEach(cb);
			};
			var cycle = function () {
				return Promise.resolve().then(executeQueue);
			};
			cycle();
			buffer = {
				push: function (x) {
					pendingQueue.push(x);
					cycle();
				},
				filter: function (filter) {
					pendingQueue = pendingQueue.filter(filter);
					return buffer;
				},
			};
		},
	};
	return medium;
}
// eslint-disable-next-line @typescript-eslint/ban-types
function createSidecarMedium(options) {
	if (options === void 0) {
		options = {};
	}
	var medium = innerCreateMedium(null);
	medium.options = __assign({ async: true, ssr: false }, options);
	return medium;
}

const React$d = await importShared("react");

var SideCar$1 = function (_a) {
	var sideCar = _a.sideCar,
		rest = __rest(_a, ["sideCar"]);
	if (!sideCar) {
		throw new Error("Sidecar: please provide `sideCar` property to import the right car");
	}
	var Target = sideCar.read();
	if (!Target) {
		throw new Error("Sidecar medium not found");
	}
	return React$d.createElement(Target, __assign({}, rest));
};
SideCar$1.isSideCarExport = true;
function exportSidecar(medium, exported) {
	medium.useMedium(exported);
	return SideCar$1;
}

var effectCar = createSidecarMedium();

const React$c = await importShared("react");
var nothing = function () {
	return;
};
/**
 * Removes scrollbar from the page and contain the scroll within the Lock
 */
var RemoveScroll = React$c.forwardRef(function (props, parentRef) {
	var ref = React$c.useRef(null);
	var _a = React$c.useState({
			onScrollCapture: nothing,
			onWheelCapture: nothing,
			onTouchMoveCapture: nothing,
		}),
		callbacks = _a[0],
		setCallbacks = _a[1];
	var forwardProps = props.forwardProps,
		children = props.children,
		className = props.className,
		removeScrollBar = props.removeScrollBar,
		enabled = props.enabled,
		shards = props.shards,
		sideCar = props.sideCar,
		noRelative = props.noRelative,
		noIsolation = props.noIsolation,
		inert = props.inert,
		allowPinchZoom = props.allowPinchZoom,
		_b = props.as,
		Container = _b === void 0 ? "div" : _b,
		gapMode = props.gapMode,
		rest = __rest(props, ["forwardProps", "children", "className", "removeScrollBar", "enabled", "shards", "sideCar", "noRelative", "noIsolation", "inert", "allowPinchZoom", "as", "gapMode"]);
	var SideCar = sideCar;
	var containerRef = useMergeRefs([ref, parentRef]);
	var containerProps = __assign(__assign({}, rest), callbacks);
	return React$c.createElement(
		React$c.Fragment,
		null,
		enabled &&
			React$c.createElement(SideCar, {
				sideCar: effectCar,
				removeScrollBar: removeScrollBar,
				shards: shards,
				noRelative: noRelative,
				noIsolation: noIsolation,
				inert: inert,
				setCallbacks: setCallbacks,
				allowPinchZoom: !!allowPinchZoom,
				lockRef: ref,
				gapMode: gapMode,
			}),
		forwardProps ?
			React$c.cloneElement(React$c.Children.only(children), __assign(__assign({}, containerProps), { ref: containerRef }))
		:	React$c.createElement(Container, __assign({}, containerProps, { className: className, ref: containerRef }), children),
	);
});
RemoveScroll.defaultProps = {
	enabled: true,
	removeScrollBar: true,
	inert: false,
};
RemoveScroll.classNames = {
	fullWidth: fullWidthClassName,
	zeroRight: zeroRightClassName,
};

var getNonce = function () {
	if (typeof __webpack_nonce__ !== "undefined") {
		return __webpack_nonce__;
	}
	return undefined;
};

function makeStyleTag() {
	if (!document) return null;
	var tag = document.createElement("style");
	tag.type = "text/css";
	var nonce = getNonce();
	if (nonce) {
		tag.setAttribute("nonce", nonce);
	}
	return tag;
}
function injectStyles(tag, css) {
	// @ts-ignore
	if (tag.styleSheet) {
		// @ts-ignore
		tag.styleSheet.cssText = css;
	} else {
		tag.appendChild(document.createTextNode(css));
	}
}
function insertStyleTag(tag) {
	var head = document.head || document.getElementsByTagName("head")[0];
	head.appendChild(tag);
}
var stylesheetSingleton = function () {
	var counter = 0;
	var stylesheet = null;
	return {
		add: function (style) {
			if (counter == 0) {
				if ((stylesheet = makeStyleTag())) {
					injectStyles(stylesheet, style);
					insertStyleTag(stylesheet);
				}
			}
			counter++;
		},
		remove: function () {
			counter--;
			if (!counter && stylesheet) {
				stylesheet.parentNode && stylesheet.parentNode.removeChild(stylesheet);
				stylesheet = null;
			}
		},
	};
};

const React$b = await importShared("react");
/**
 * creates a hook to control style singleton
 * @see {@link styleSingleton} for a safer component version
 * @example
 * ```tsx
 * const useStyle = styleHookSingleton();
 * ///
 * useStyle('body { overflow: hidden}');
 */
var styleHookSingleton = function () {
	var sheet = stylesheetSingleton();
	return function (styles, isDynamic) {
		React$b.useEffect(
			function () {
				sheet.add(styles);
				return function () {
					sheet.remove();
				};
			},
			[styles && isDynamic],
		);
	};
};

/**
 * create a Component to add styles on demand
 * - styles are added when first instance is mounted
 * - styles are removed when the last instance is unmounted
 * - changing styles in runtime does nothing unless dynamic is set. But with multiple components that can lead to the undefined behavior
 */
var styleSingleton = function () {
	var useStyle = styleHookSingleton();
	var Sheet = function (_a) {
		var styles = _a.styles,
			dynamic = _a.dynamic;
		useStyle(styles, dynamic);
		return null;
	};
	return Sheet;
};

var zeroGap = {
	left: 0,
	top: 0,
	right: 0,
	gap: 0,
};
var parse = function (x) {
	return parseInt(x || "", 10) || 0;
};
var getOffset = function (gapMode) {
	var cs = window.getComputedStyle(document.body);
	var left = cs[gapMode === "padding" ? "paddingLeft" : "marginLeft"];
	var top = cs[gapMode === "padding" ? "paddingTop" : "marginTop"];
	var right = cs[gapMode === "padding" ? "paddingRight" : "marginRight"];
	return [parse(left), parse(top), parse(right)];
};
var getGapWidth = function (gapMode) {
	if (gapMode === void 0) {
		gapMode = "margin";
	}
	if (typeof window === "undefined") {
		return zeroGap;
	}
	var offsets = getOffset(gapMode);
	var documentWidth = document.documentElement.clientWidth;
	var windowWidth = window.innerWidth;
	return {
		left: offsets[0],
		top: offsets[1],
		right: offsets[2],
		gap: Math.max(0, windowWidth - documentWidth + offsets[2] - offsets[0]),
	};
};

const React$a = await importShared("react");
var Style = styleSingleton();
var lockAttribute = "data-scroll-locked";
// important tip - once we measure scrollBar width and remove them
// we could not repeat this operation
// thus we are using style-singleton - only the first "yet correct" style will be applied.
var getStyles = function (_a, allowRelative, gapMode, important) {
	var left = _a.left,
		top = _a.top,
		right = _a.right,
		gap = _a.gap;
	if (gapMode === void 0) {
		gapMode = "margin";
	}
	return "\n  ."
		.concat(noScrollbarsClassName, " {\n   overflow: hidden ")
		.concat(important, ";\n   padding-right: ")
		.concat(gap, "px ")
		.concat(important, ";\n  }\n  body[")
		.concat(lockAttribute, "] {\n    overflow: hidden ")
		.concat(important, ";\n    overscroll-behavior: contain;\n    ")
		.concat(
			[
				allowRelative && "position: relative ".concat(important, ";"),
				gapMode === "margin" &&
					"\n    padding-left: "
						.concat(left, "px;\n    padding-top: ")
						.concat(top, "px;\n    padding-right: ")
						.concat(right, "px;\n    margin-left:0;\n    margin-top:0;\n    margin-right: ")
						.concat(gap, "px ")
						.concat(important, ";\n    "),
				gapMode === "padding" && "padding-right: ".concat(gap, "px ").concat(important, ";"),
			]
				.filter(Boolean)
				.join(""),
			"\n  }\n  \n  .",
		)
		.concat(zeroRightClassName, " {\n    right: ")
		.concat(gap, "px ")
		.concat(important, ";\n  }\n  \n  .")
		.concat(fullWidthClassName, " {\n    margin-right: ")
		.concat(gap, "px ")
		.concat(important, ";\n  }\n  \n  .")
		.concat(zeroRightClassName, " .")
		.concat(zeroRightClassName, " {\n    right: 0 ")
		.concat(important, ";\n  }\n  \n  .")
		.concat(fullWidthClassName, " .")
		.concat(fullWidthClassName, " {\n    margin-right: 0 ")
		.concat(important, ";\n  }\n  \n  body[")
		.concat(lockAttribute, "] {\n    ")
		.concat(removedBarSizeVariable, ": ")
		.concat(gap, "px;\n  }\n");
};
var getCurrentUseCounter = function () {
	var counter = parseInt(document.body.getAttribute(lockAttribute) || "0", 10);
	return isFinite(counter) ? counter : 0;
};
var useLockAttribute = function () {
	React$a.useEffect(function () {
		document.body.setAttribute(lockAttribute, (getCurrentUseCounter() + 1).toString());
		return function () {
			var newCounter = getCurrentUseCounter() - 1;
			if (newCounter <= 0) {
				document.body.removeAttribute(lockAttribute);
			} else {
				document.body.setAttribute(lockAttribute, newCounter.toString());
			}
		};
	}, []);
};
/**
 * Removes page scrollbar and blocks page scroll when mounted
 */
var RemoveScrollBar = function (_a) {
	var noRelative = _a.noRelative,
		noImportant = _a.noImportant,
		_b = _a.gapMode,
		gapMode = _b === void 0 ? "margin" : _b;
	useLockAttribute();
	/*
     gap will be measured on every component mount
     however it will be used only by the "first" invocation
     due to singleton nature of <Style
     */
	var gap = React$a.useMemo(
		function () {
			return getGapWidth(gapMode);
		},
		[gapMode],
	);
	return React$a.createElement(Style, { styles: getStyles(gap, !noRelative, gapMode, !noImportant ? "!important" : "") });
};

var passiveSupported = false;
if (typeof window !== "undefined") {
	try {
		var options = Object.defineProperty({}, "passive", {
			get: function () {
				passiveSupported = true;
				return true;
			},
		});
		// @ts-ignore
		window.addEventListener("test", options, options);
		// @ts-ignore
		window.removeEventListener("test", options, options);
	} catch (err) {
		passiveSupported = false;
	}
}
var nonPassive = passiveSupported ? { passive: false } : false;

var alwaysContainsScroll = function (node) {
	// textarea will always _contain_ scroll inside self. It only can be hidden
	return node.tagName === "TEXTAREA";
};
var elementCanBeScrolled = function (node, overflow) {
	if (!(node instanceof Element)) {
		return false;
	}
	var styles = window.getComputedStyle(node);
	return (
		// not-not-scrollable
		styles[overflow] !== "hidden" &&
		// contains scroll inside self
		!(styles.overflowY === styles.overflowX && !alwaysContainsScroll(node) && styles[overflow] === "visible")
	);
};
var elementCouldBeVScrolled = function (node) {
	return elementCanBeScrolled(node, "overflowY");
};
var elementCouldBeHScrolled = function (node) {
	return elementCanBeScrolled(node, "overflowX");
};
var locationCouldBeScrolled = function (axis, node) {
	var ownerDocument = node.ownerDocument;
	var current = node;
	do {
		// Skip over shadow root
		if (typeof ShadowRoot !== "undefined" && current instanceof ShadowRoot) {
			current = current.host;
		}
		var isScrollable = elementCouldBeScrolled(axis, current);
		if (isScrollable) {
			var _a = getScrollVariables(axis, current),
				scrollHeight = _a[1],
				clientHeight = _a[2];
			if (scrollHeight > clientHeight) {
				return true;
			}
		}
		current = current.parentNode;
	} while (current && current !== ownerDocument.body);
	return false;
};
var getVScrollVariables = function (_a) {
	var scrollTop = _a.scrollTop,
		scrollHeight = _a.scrollHeight,
		clientHeight = _a.clientHeight;
	return [scrollTop, scrollHeight, clientHeight];
};
var getHScrollVariables = function (_a) {
	var scrollLeft = _a.scrollLeft,
		scrollWidth = _a.scrollWidth,
		clientWidth = _a.clientWidth;
	return [scrollLeft, scrollWidth, clientWidth];
};
var elementCouldBeScrolled = function (axis, node) {
	return axis === "v" ? elementCouldBeVScrolled(node) : elementCouldBeHScrolled(node);
};
var getScrollVariables = function (axis, node) {
	return axis === "v" ? getVScrollVariables(node) : getHScrollVariables(node);
};
var getDirectionFactor = function (axis, direction) {
	/**
	 * If the element's direction is rtl (right-to-left), then scrollLeft is 0 when the scrollbar is at its rightmost position,
	 * and then increasingly negative as you scroll towards the end of the content.
	 * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollLeft
	 */
	return axis === "h" && direction === "rtl" ? -1 : 1;
};
var handleScroll = function (axis, endTarget, event, sourceDelta, noOverscroll) {
	var directionFactor = getDirectionFactor(axis, window.getComputedStyle(endTarget).direction);
	var delta = directionFactor * sourceDelta;
	// find scrollable target
	var target = event.target;
	var targetInLock = endTarget.contains(target);
	var shouldCancelScroll = false;
	var isDeltaPositive = delta > 0;
	var availableScroll = 0;
	var availableScrollTop = 0;
	do {
		if (!target) {
			break;
		}
		var _a = getScrollVariables(axis, target),
			position = _a[0],
			scroll_1 = _a[1],
			capacity = _a[2];
		var elementScroll = scroll_1 - capacity - directionFactor * position;
		if (position || elementScroll) {
			if (elementCouldBeScrolled(axis, target)) {
				availableScroll += elementScroll;
				availableScrollTop += position;
			}
		}
		var parent_1 = target.parentNode;
		// we will "bubble" from ShadowDom in case we are, or just to the parent in normal case
		// this is the same logic used in focus-lock
		target = parent_1 && parent_1.nodeType === Node.DOCUMENT_FRAGMENT_NODE ? parent_1.host : parent_1;
	} while (
		// portaled content
		(!targetInLock && target !== document.body) ||
		// self content
		(targetInLock && (endTarget.contains(target) || endTarget === target))
	);
	// handle epsilon around 0 (non standard zoom levels)
	if (isDeltaPositive && (Math.abs(availableScroll) < 1 || false)) {
		shouldCancelScroll = true;
	} else if (!isDeltaPositive && (Math.abs(availableScrollTop) < 1 || false)) {
		shouldCancelScroll = true;
	}
	return shouldCancelScroll;
};

const React$9 = await importShared("react");
var getTouchXY = function (event) {
	return "changedTouches" in event ? [event.changedTouches[0].clientX, event.changedTouches[0].clientY] : [0, 0];
};
var getDeltaXY = function (event) {
	return [event.deltaX, event.deltaY];
};
var extractRef = function (ref) {
	return ref && "current" in ref ? ref.current : ref;
};
var deltaCompare = function (x, y) {
	return x[0] === y[0] && x[1] === y[1];
};
var generateStyle = function (id) {
	return "\n  .block-interactivity-".concat(id, " {pointer-events: none;}\n  .allow-interactivity-").concat(id, " {pointer-events: all;}\n");
};
var idCounter = 0;
var lockStack = [];
function RemoveScrollSideCar(props) {
	var shouldPreventQueue = React$9.useRef([]);
	var touchStartRef = React$9.useRef([0, 0]);
	var activeAxis = React$9.useRef();
	var id = React$9.useState(idCounter++)[0];
	var Style = React$9.useState(styleSingleton)[0];
	var lastProps = React$9.useRef(props);
	React$9.useEffect(
		function () {
			lastProps.current = props;
		},
		[props],
	);
	React$9.useEffect(
		function () {
			if (props.inert) {
				document.body.classList.add("block-interactivity-".concat(id));
				var allow_1 = __spreadArray([props.lockRef.current], (props.shards || []).map(extractRef), true).filter(Boolean);
				allow_1.forEach(function (el) {
					return el.classList.add("allow-interactivity-".concat(id));
				});
				return function () {
					document.body.classList.remove("block-interactivity-".concat(id));
					allow_1.forEach(function (el) {
						return el.classList.remove("allow-interactivity-".concat(id));
					});
				};
			}
			return;
		},
		[props.inert, props.lockRef.current, props.shards],
	);
	var shouldCancelEvent = React$9.useCallback(function (event, parent) {
		if (("touches" in event && event.touches.length === 2) || (event.type === "wheel" && event.ctrlKey)) {
			return !lastProps.current.allowPinchZoom;
		}
		var touch = getTouchXY(event);
		var touchStart = touchStartRef.current;
		var deltaX = "deltaX" in event ? event.deltaX : touchStart[0] - touch[0];
		var deltaY = "deltaY" in event ? event.deltaY : touchStart[1] - touch[1];
		var currentAxis;
		var target = event.target;
		var moveDirection = Math.abs(deltaX) > Math.abs(deltaY) ? "h" : "v";
		// allow horizontal touch move on Range inputs. They will not cause any scroll
		if ("touches" in event && moveDirection === "h" && target.type === "range") {
			return false;
		}
		// allow drag selection (iOS); check if selection's anchorNode is the same as target or contains target
		var selection = window.getSelection();
		var anchorNode = selection && selection.anchorNode;
		var isTouchingSelection = anchorNode ? anchorNode === target || anchorNode.contains(target) : false;
		if (isTouchingSelection) {
			return false;
		}
		var canBeScrolledInMainDirection = locationCouldBeScrolled(moveDirection, target);
		if (!canBeScrolledInMainDirection) {
			return true;
		}
		if (canBeScrolledInMainDirection) {
			currentAxis = moveDirection;
		} else {
			currentAxis = moveDirection === "v" ? "h" : "v";
			canBeScrolledInMainDirection = locationCouldBeScrolled(moveDirection, target);
			// other axis might be not scrollable
		}
		if (!canBeScrolledInMainDirection) {
			return false;
		}
		if (!activeAxis.current && "changedTouches" in event && (deltaX || deltaY)) {
			activeAxis.current = currentAxis;
		}
		if (!currentAxis) {
			return true;
		}
		var cancelingAxis = activeAxis.current || currentAxis;
		return handleScroll(cancelingAxis, parent, event, cancelingAxis === "h" ? deltaX : deltaY);
	}, []);
	var shouldPrevent = React$9.useCallback(function (_event) {
		var event = _event;
		if (!lockStack.length || lockStack[lockStack.length - 1] !== Style) {
			// not the last active
			return;
		}
		var delta = "deltaY" in event ? getDeltaXY(event) : getTouchXY(event);
		var sourceEvent = shouldPreventQueue.current.filter(function (e) {
			return e.name === event.type && (e.target === event.target || event.target === e.shadowParent) && deltaCompare(e.delta, delta);
		})[0];
		// self event, and should be canceled
		if (sourceEvent && sourceEvent.should) {
			if (event.cancelable) {
				event.preventDefault();
			}
			return;
		}
		// outside or shard event
		if (!sourceEvent) {
			var shardNodes = (lastProps.current.shards || [])
				.map(extractRef)
				.filter(Boolean)
				.filter(function (node) {
					return node.contains(event.target);
				});
			var shouldStop = shardNodes.length > 0 ? shouldCancelEvent(event, shardNodes[0]) : !lastProps.current.noIsolation;
			if (shouldStop) {
				if (event.cancelable) {
					event.preventDefault();
				}
			}
		}
	}, []);
	var shouldCancel = React$9.useCallback(function (name, delta, target, should) {
		var event = { name: name, delta: delta, target: target, should: should, shadowParent: getOutermostShadowParent(target) };
		shouldPreventQueue.current.push(event);
		setTimeout(function () {
			shouldPreventQueue.current = shouldPreventQueue.current.filter(function (e) {
				return e !== event;
			});
		}, 1);
	}, []);
	var scrollTouchStart = React$9.useCallback(function (event) {
		touchStartRef.current = getTouchXY(event);
		activeAxis.current = undefined;
	}, []);
	var scrollWheel = React$9.useCallback(function (event) {
		shouldCancel(event.type, getDeltaXY(event), event.target, shouldCancelEvent(event, props.lockRef.current));
	}, []);
	var scrollTouchMove = React$9.useCallback(function (event) {
		shouldCancel(event.type, getTouchXY(event), event.target, shouldCancelEvent(event, props.lockRef.current));
	}, []);
	React$9.useEffect(function () {
		lockStack.push(Style);
		props.setCallbacks({
			onScrollCapture: scrollWheel,
			onWheelCapture: scrollWheel,
			onTouchMoveCapture: scrollTouchMove,
		});
		document.addEventListener("wheel", shouldPrevent, nonPassive);
		document.addEventListener("touchmove", shouldPrevent, nonPassive);
		document.addEventListener("touchstart", scrollTouchStart, nonPassive);
		return function () {
			lockStack = lockStack.filter(function (inst) {
				return inst !== Style;
			});
			document.removeEventListener("wheel", shouldPrevent, nonPassive);
			document.removeEventListener("touchmove", shouldPrevent, nonPassive);
			document.removeEventListener("touchstart", scrollTouchStart, nonPassive);
		};
	}, []);
	var removeScrollBar = props.removeScrollBar,
		inert = props.inert;
	return React$9.createElement(
		React$9.Fragment,
		null,
		inert ? React$9.createElement(Style, { styles: generateStyle(id) }) : null,
		removeScrollBar ? React$9.createElement(RemoveScrollBar, { noRelative: props.noRelative, gapMode: props.gapMode }) : null,
	);
}
function getOutermostShadowParent(node) {
	var shadowParent = null;
	while (node !== null) {
		if (node instanceof ShadowRoot) {
			shadowParent = node.host;
			node = node.host;
		}
		node = node.parentNode;
	}
	return shadowParent;
}

const SideCar = exportSidecar(effectCar, RemoveScrollSideCar);

const React$8 = await importShared("react");
var ReactRemoveScroll = React$8.forwardRef(function (props, ref) {
	return React$8.createElement(RemoveScroll, __assign({}, props, { ref: ref, sideCar: SideCar }));
});
ReactRemoveScroll.classNames = RemoveScroll.classNames;

var getDefaultParent = function (originalTarget) {
	if (typeof document === "undefined") {
		return null;
	}
	var sampleTarget = Array.isArray(originalTarget) ? originalTarget[0] : originalTarget;
	return sampleTarget.ownerDocument.body;
};
var counterMap = new WeakMap();
var uncontrolledNodes = new WeakMap();
var markerMap = {};
var lockCount = 0;
var unwrapHost = function (node) {
	return node && (node.host || unwrapHost(node.parentNode));
};
var correctTargets = function (parent, targets) {
	return targets
		.map(function (target) {
			if (parent.contains(target)) {
				return target;
			}
			var correctedTarget = unwrapHost(target);
			if (correctedTarget && parent.contains(correctedTarget)) {
				return correctedTarget;
			}
			console.error("aria-hidden", target, "in not contained inside", parent, ". Doing nothing");
			return null;
		})
		.filter(function (x) {
			return Boolean(x);
		});
};
/**
 * Marks everything except given node(or nodes) as aria-hidden
 * @param {Element | Element[]} originalTarget - elements to keep on the page
 * @param [parentNode] - top element, defaults to document.body
 * @param {String} [markerName] - a special attribute to mark every node
 * @param {String} [controlAttribute] - html Attribute to control
 * @return {Undo} undo command
 */
var applyAttributeToOthers = function (originalTarget, parentNode, markerName, controlAttribute) {
	var targets = correctTargets(parentNode, Array.isArray(originalTarget) ? originalTarget : [originalTarget]);
	if (!markerMap[markerName]) {
		markerMap[markerName] = new WeakMap();
	}
	var markerCounter = markerMap[markerName];
	var hiddenNodes = [];
	var elementsToKeep = new Set();
	var elementsToStop = new Set(targets);
	var keep = function (el) {
		if (!el || elementsToKeep.has(el)) {
			return;
		}
		elementsToKeep.add(el);
		keep(el.parentNode);
	};
	targets.forEach(keep);
	var deep = function (parent) {
		if (!parent || elementsToStop.has(parent)) {
			return;
		}
		Array.prototype.forEach.call(parent.children, function (node) {
			if (elementsToKeep.has(node)) {
				deep(node);
			} else {
				try {
					var attr = node.getAttribute(controlAttribute);
					var alreadyHidden = attr !== null && attr !== "false";
					var counterValue = (counterMap.get(node) || 0) + 1;
					var markerValue = (markerCounter.get(node) || 0) + 1;
					counterMap.set(node, counterValue);
					markerCounter.set(node, markerValue);
					hiddenNodes.push(node);
					if (counterValue === 1 && alreadyHidden) {
						uncontrolledNodes.set(node, true);
					}
					if (markerValue === 1) {
						node.setAttribute(markerName, "true");
					}
					if (!alreadyHidden) {
						node.setAttribute(controlAttribute, "true");
					}
				} catch (e) {
					console.error("aria-hidden: cannot operate on ", node, e);
				}
			}
		});
	};
	deep(parentNode);
	elementsToKeep.clear();
	lockCount++;
	return function () {
		hiddenNodes.forEach(function (node) {
			var counterValue = counterMap.get(node) - 1;
			var markerValue = markerCounter.get(node) - 1;
			counterMap.set(node, counterValue);
			markerCounter.set(node, markerValue);
			if (!counterValue) {
				if (!uncontrolledNodes.has(node)) {
					node.removeAttribute(controlAttribute);
				}
				uncontrolledNodes.delete(node);
			}
			if (!markerValue) {
				node.removeAttribute(markerName);
			}
		});
		lockCount--;
		if (!lockCount) {
			// clear
			counterMap = new WeakMap();
			counterMap = new WeakMap();
			uncontrolledNodes = new WeakMap();
			markerMap = {};
		}
	};
};
/**
 * Marks everything except given node(or nodes) as aria-hidden
 * @param {Element | Element[]} originalTarget - elements to keep on the page
 * @param [parentNode] - top element, defaults to document.body
 * @param {String} [markerName] - a special attribute to mark every node
 * @return {Undo} undo command
 */
var hideOthers = function (originalTarget, parentNode, markerName) {
	if (markerName === void 0) {
		markerName = "data-aria-hidden";
	}
	var targets = Array.from(Array.isArray(originalTarget) ? originalTarget : [originalTarget]);
	var activeParentNode = getDefaultParent(originalTarget);
	if (!activeParentNode) {
		return function () {
			return null;
		};
	}
	// we should not hide aria-live elements - https://github.com/theKashey/aria-hidden/issues/10
	// and script elements, as they have no impact on accessibility.
	targets.push.apply(targets, Array.from(activeParentNode.querySelectorAll("[aria-live], script")));
	return applyAttributeToOthers(targets, activeParentNode, markerName, "aria-hidden");
};

const falsyToString = (value) =>
	typeof value === "boolean" ? `${value}`
	: value === 0 ? "0"
	: value;
const cx = clsx;
const cva = (base, config) => (props) => {
	var _config_compoundVariants;
	if ((config === null || config === void 0 ? void 0 : config.variants) == null)
		return cx(base, props === null || props === void 0 ? void 0 : props.class, props === null || props === void 0 ? void 0 : props.className);
	const { variants, defaultVariants } = config;
	const getVariantClassNames = Object.keys(variants).map((variant) => {
		const variantProp = props === null || props === void 0 ? void 0 : props[variant];
		const defaultVariantProp = defaultVariants === null || defaultVariants === void 0 ? void 0 : defaultVariants[variant];
		if (variantProp === null) return null;
		const variantKey = falsyToString(variantProp) || falsyToString(defaultVariantProp);
		return variants[variant][variantKey];
	});
	const propsWithoutUndefined =
		props &&
		Object.entries(props).reduce((acc, param) => {
			let [key, value] = param;
			if (value === undefined) {
				return acc;
			}
			acc[key] = value;
			return acc;
		}, {});
	const getCompoundVariantClassNames =
		config === null || config === void 0 ? void 0
		: (_config_compoundVariants = config.compoundVariants) === null || _config_compoundVariants === void 0 ? void 0
		: _config_compoundVariants.reduce((acc, param) => {
				let { class: cvClass, className: cvClassName, ...compoundVariantOptions } = param;
				return (
						Object.entries(compoundVariantOptions).every((param) => {
							let [key, value] = param;
							return Array.isArray(value) ?
									value.includes(
										{
											...defaultVariants,
											...propsWithoutUndefined,
										}[key],
									)
								:	{
										...defaultVariants,
										...propsWithoutUndefined,
									}[key] === value;
						})
					) ?
						[...acc, cvClass, cvClassName]
					:	acc;
			}, []);
	return cx(base, getVariantClassNames, getCompoundVariantClassNames, props === null || props === void 0 ? void 0 : props.class, props === null || props === void 0 ? void 0 : props.className);
};

// src/label.tsx
const React$7 = await importShared("react");
var NAME$2 = "Label";
var Label = React$7.forwardRef((props, forwardedRef) => {
	return /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.label, {
		...props,
		ref: forwardedRef,
		onMouseDown: (event) => {
			const target = event.target;
			if (target.closest("button, input, select, textarea")) return;
			props.onMouseDown?.(event);
			if (!event.defaultPrevented && event.detail > 1) event.preventDefault();
		},
	});
});
Label.displayName = NAME$2;
var Root$2 = Label;

// src/use-previous.tsx
const React$6 = await importShared("react");

function usePrevious(value) {
	const ref = React$6.useRef({ value, previous: value });
	return React$6.useMemo(() => {
		if (ref.current.value !== value) {
			ref.current.previous = ref.current.value;
			ref.current.value = value;
		}
		return ref.current.previous;
	}, [value]);
}

// src/use-size.tsx
const React$5 = await importShared("react");
function useSize(element) {
	const [size, setSize] = React$5.useState(void 0);
	useLayoutEffect2(() => {
		if (element) {
			setSize({ width: element.offsetWidth, height: element.offsetHeight });
			const resizeObserver = new ResizeObserver((entries) => {
				if (!Array.isArray(entries)) {
					return;
				}
				if (!entries.length) {
					return;
				}
				const entry = entries[0];
				let width;
				let height;
				if ("borderBoxSize" in entry) {
					const borderSizeEntry = entry["borderBoxSize"];
					const borderSize = Array.isArray(borderSizeEntry) ? borderSizeEntry[0] : borderSizeEntry;
					width = borderSize["inlineSize"];
					height = borderSize["blockSize"];
				} else {
					width = element.offsetWidth;
					height = element.offsetHeight;
				}
				setSize({ width, height });
			});
			resizeObserver.observe(element, { box: "border-box" });
			return () => resizeObserver.unobserve(element);
		} else {
			setSize(void 0);
		}
	}, [element]);
	return size;
}

/**
 * Custom positioning reference element.
 * @see https://floating-ui.com/docs/virtual-elements
 */

const sides = ["top", "right", "bottom", "left"];
const alignments = ["start", "end"];
const placements = /*#__PURE__*/ sides.reduce((acc, side) => acc.concat(side, side + "-" + alignments[0], side + "-" + alignments[1]), []);
const min = Math.min;
const max = Math.max;
const round = Math.round;
const floor = Math.floor;
const createCoords = (v) => ({
	x: v,
	y: v,
});
const oppositeSideMap = {
	left: "right",
	right: "left",
	bottom: "top",
	top: "bottom",
};
function clamp(start, value, end) {
	return max(start, min(value, end));
}
function evaluate(value, param) {
	return typeof value === "function" ? value(param) : value;
}
function getSide(placement) {
	return placement.split("-")[0];
}
function getAlignment(placement) {
	return placement.split("-")[1];
}
function getOppositeAxis(axis) {
	return axis === "x" ? "y" : "x";
}
function getAxisLength(axis) {
	return axis === "y" ? "height" : "width";
}
function getSideAxis(placement) {
	const firstChar = placement[0];
	return firstChar === "t" || firstChar === "b" ? "y" : "x";
}
function getAlignmentAxis(placement) {
	return getOppositeAxis(getSideAxis(placement));
}
function getAlignmentSides(placement, rects, rtl) {
	if (rtl === void 0) {
		rtl = false;
	}
	const alignment = getAlignment(placement);
	const alignmentAxis = getAlignmentAxis(placement);
	const length = getAxisLength(alignmentAxis);
	let mainAlignmentSide =
		alignmentAxis === "x" ?
			alignment === (rtl ? "end" : "start") ?
				"right"
			:	"left"
		: alignment === "start" ? "bottom"
		: "top";
	if (rects.reference[length] > rects.floating[length]) {
		mainAlignmentSide = getOppositePlacement(mainAlignmentSide);
	}
	return [mainAlignmentSide, getOppositePlacement(mainAlignmentSide)];
}
function getExpandedPlacements(placement) {
	const oppositePlacement = getOppositePlacement(placement);
	return [getOppositeAlignmentPlacement(placement), oppositePlacement, getOppositeAlignmentPlacement(oppositePlacement)];
}
function getOppositeAlignmentPlacement(placement) {
	return placement.includes("start") ? placement.replace("start", "end") : placement.replace("end", "start");
}
const lrPlacement = ["left", "right"];
const rlPlacement = ["right", "left"];
const tbPlacement = ["top", "bottom"];
const btPlacement = ["bottom", "top"];
function getSideList(side, isStart, rtl) {
	switch (side) {
		case "top":
		case "bottom":
			if (rtl) return isStart ? rlPlacement : lrPlacement;
			return isStart ? lrPlacement : rlPlacement;
		case "left":
		case "right":
			return isStart ? tbPlacement : btPlacement;
		default:
			return [];
	}
}
function getOppositeAxisPlacements(placement, flipAlignment, direction, rtl) {
	const alignment = getAlignment(placement);
	let list = getSideList(getSide(placement), direction === "start", rtl);
	if (alignment) {
		list = list.map((side) => side + "-" + alignment);
		if (flipAlignment) {
			list = list.concat(list.map(getOppositeAlignmentPlacement));
		}
	}
	return list;
}
function getOppositePlacement(placement) {
	const side = getSide(placement);
	return oppositeSideMap[side] + placement.slice(side.length);
}
function expandPaddingObject(padding) {
	return {
		top: 0,
		right: 0,
		bottom: 0,
		left: 0,
		...padding,
	};
}
function getPaddingObject(padding) {
	return typeof padding !== "number" ?
			expandPaddingObject(padding)
		:	{
				top: padding,
				right: padding,
				bottom: padding,
				left: padding,
			};
}
function rectToClientRect(rect) {
	const { x, y, width, height } = rect;
	return {
		width,
		height,
		top: y,
		left: x,
		right: x + width,
		bottom: y + height,
		x,
		y,
	};
}

function computeCoordsFromPlacement(_ref, placement, rtl) {
	let { reference, floating } = _ref;
	const sideAxis = getSideAxis(placement);
	const alignmentAxis = getAlignmentAxis(placement);
	const alignLength = getAxisLength(alignmentAxis);
	const side = getSide(placement);
	const isVertical = sideAxis === "y";
	const commonX = reference.x + reference.width / 2 - floating.width / 2;
	const commonY = reference.y + reference.height / 2 - floating.height / 2;
	const commonAlign = reference[alignLength] / 2 - floating[alignLength] / 2;
	let coords;
	switch (side) {
		case "top":
			coords = {
				x: commonX,
				y: reference.y - floating.height,
			};
			break;
		case "bottom":
			coords = {
				x: commonX,
				y: reference.y + reference.height,
			};
			break;
		case "right":
			coords = {
				x: reference.x + reference.width,
				y: commonY,
			};
			break;
		case "left":
			coords = {
				x: reference.x - floating.width,
				y: commonY,
			};
			break;
		default:
			coords = {
				x: reference.x,
				y: reference.y,
			};
	}
	switch (getAlignment(placement)) {
		case "start":
			coords[alignmentAxis] -= commonAlign * (rtl && isVertical ? -1 : 1);
			break;
		case "end":
			coords[alignmentAxis] += commonAlign * (rtl && isVertical ? -1 : 1);
			break;
	}
	return coords;
}

/**
 * Resolves with an object of overflow side offsets that determine how much the
 * element is overflowing a given clipping boundary on each side.
 * - positive = overflowing the boundary by that number of pixels
 * - negative = how many pixels left before it will overflow
 * - 0 = lies flush with the boundary
 * @see https://floating-ui.com/docs/detectOverflow
 */
async function detectOverflow(state, options) {
	var _await$platform$isEle;
	if (options === void 0) {
		options = {};
	}
	const { x, y, platform, rects, elements, strategy } = state;
	const { boundary = "clippingAncestors", rootBoundary = "viewport", elementContext = "floating", altBoundary = false, padding = 0 } = evaluate(options, state);
	const paddingObject = getPaddingObject(padding);
	const altContext = elementContext === "floating" ? "reference" : "floating";
	const element = elements[altBoundary ? altContext : elementContext];
	const clippingClientRect = rectToClientRect(
		await platform.getClippingRect({
			element:
				(
					(_await$platform$isEle = await (platform.isElement == null ? void 0 : platform.isElement(element))) != null ? _await$platform$isEle : true
				) ?
					element
				:	element.contextElement || (await (platform.getDocumentElement == null ? void 0 : platform.getDocumentElement(elements.floating))),
			boundary,
			rootBoundary,
			strategy,
		}),
	);
	const rect =
		elementContext === "floating" ?
			{
				x,
				y,
				width: rects.floating.width,
				height: rects.floating.height,
			}
		:	rects.reference;
	const offsetParent = await (platform.getOffsetParent == null ? void 0 : platform.getOffsetParent(elements.floating));
	const offsetScale =
		(await (platform.isElement == null ? void 0 : platform.isElement(offsetParent))) ?
			(await (platform.getScale == null ? void 0 : platform.getScale(offsetParent))) || {
				x: 1,
				y: 1,
			}
		:	{
				x: 1,
				y: 1,
			};
	const elementClientRect = rectToClientRect(
		platform.convertOffsetParentRelativeRectToViewportRelativeRect ?
			await platform.convertOffsetParentRelativeRectToViewportRelativeRect({
				elements,
				rect,
				offsetParent,
				strategy,
			})
		:	rect,
	);
	return {
		top: (clippingClientRect.top - elementClientRect.top + paddingObject.top) / offsetScale.y,
		bottom: (elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom) / offsetScale.y,
		left: (clippingClientRect.left - elementClientRect.left + paddingObject.left) / offsetScale.x,
		right: (elementClientRect.right - clippingClientRect.right + paddingObject.right) / offsetScale.x,
	};
}

// Maximum number of resets that can occur before bailing to avoid infinite reset loops.
const MAX_RESET_COUNT = 50;

/**
 * Computes the `x` and `y` coordinates that will place the floating element
 * next to a given reference element.
 *
 * This export does not have any `platform` interface logic. You will need to
 * write one for the platform you are using Floating UI with.
 */
const computePosition$1 = async (reference, floating, config) => {
	const { placement = "bottom", strategy = "absolute", middleware = [], platform } = config;
	const platformWithDetectOverflow =
		platform.detectOverflow ? platform : (
			{
				...platform,
				detectOverflow,
			}
		);
	const rtl = await (platform.isRTL == null ? void 0 : platform.isRTL(floating));
	let rects = await platform.getElementRects({
		reference,
		floating,
		strategy,
	});
	let { x, y } = computeCoordsFromPlacement(rects, placement, rtl);
	let statefulPlacement = placement;
	let resetCount = 0;
	const middlewareData = {};
	for (let i = 0; i < middleware.length; i++) {
		const currentMiddleware = middleware[i];
		if (!currentMiddleware) {
			continue;
		}
		const { name, fn } = currentMiddleware;
		const {
			x: nextX,
			y: nextY,
			data,
			reset,
		} = await fn({
			x,
			y,
			initialPlacement: placement,
			placement: statefulPlacement,
			strategy,
			middlewareData,
			rects,
			platform: platformWithDetectOverflow,
			elements: {
				reference,
				floating,
			},
		});
		x = nextX != null ? nextX : x;
		y = nextY != null ? nextY : y;
		middlewareData[name] = {
			...middlewareData[name],
			...data,
		};
		if (reset && resetCount < MAX_RESET_COUNT) {
			resetCount++;
			if (typeof reset === "object") {
				if (reset.placement) {
					statefulPlacement = reset.placement;
				}
				if (reset.rects) {
					rects =
						reset.rects === true ?
							await platform.getElementRects({
								reference,
								floating,
								strategy,
							})
						:	reset.rects;
				}
				({ x, y } = computeCoordsFromPlacement(rects, statefulPlacement, rtl));
			}
			i = -1;
		}
	}
	return {
		x,
		y,
		placement: statefulPlacement,
		strategy,
		middlewareData,
	};
};

/**
 * Provides data to position an inner element of the floating element so that it
 * appears centered to the reference element.
 * @see https://floating-ui.com/docs/arrow
 */
const arrow$3 = (options) => ({
	name: "arrow",
	options,
	async fn(state) {
		const { x, y, placement, rects, platform, elements, middlewareData } = state;
		// Since `element` is required, we don't Partial<> the type.
		const { element, padding = 0 } = evaluate(options, state) || {};
		if (element == null) {
			return {};
		}
		const paddingObject = getPaddingObject(padding);
		const coords = {
			x,
			y,
		};
		const axis = getAlignmentAxis(placement);
		const length = getAxisLength(axis);
		const arrowDimensions = await platform.getDimensions(element);
		const isYAxis = axis === "y";
		const minProp = isYAxis ? "top" : "left";
		const maxProp = isYAxis ? "bottom" : "right";
		const clientProp = isYAxis ? "clientHeight" : "clientWidth";
		const endDiff = rects.reference[length] + rects.reference[axis] - coords[axis] - rects.floating[length];
		const startDiff = coords[axis] - rects.reference[axis];
		const arrowOffsetParent = await (platform.getOffsetParent == null ? void 0 : platform.getOffsetParent(element));
		let clientSize = arrowOffsetParent ? arrowOffsetParent[clientProp] : 0;

		// DOM platform can return `window` as the `offsetParent`.
		if (!clientSize || !(await (platform.isElement == null ? void 0 : platform.isElement(arrowOffsetParent)))) {
			clientSize = elements.floating[clientProp] || rects.floating[length];
		}
		const centerToReference = endDiff / 2 - startDiff / 2;

		// If the padding is large enough that it causes the arrow to no longer be
		// centered, modify the padding so that it is centered.
		const largestPossiblePadding = clientSize / 2 - arrowDimensions[length] / 2 - 1;
		const minPadding = min(paddingObject[minProp], largestPossiblePadding);
		const maxPadding = min(paddingObject[maxProp], largestPossiblePadding);

		// Make sure the arrow doesn't overflow the floating element if the center
		// point is outside the floating element's bounds.
		const min$1 = minPadding;
		const max = clientSize - arrowDimensions[length] - maxPadding;
		const center = clientSize / 2 - arrowDimensions[length] / 2 + centerToReference;
		const offset = clamp(min$1, center, max);

		// If the reference is small enough that the arrow's padding causes it to
		// to point to nothing for an aligned placement, adjust the offset of the
		// floating element itself. To ensure `shift()` continues to take action,
		// a single reset is performed when this is true.
		const shouldAddOffset =
			!middlewareData.arrow && getAlignment(placement) != null && center !== offset && rects.reference[length] / 2 - (center < min$1 ? minPadding : maxPadding) - arrowDimensions[length] / 2 < 0;
		const alignmentOffset =
			shouldAddOffset ?
				center < min$1 ?
					center - min$1
				:	center - max
			:	0;
		return {
			[axis]: coords[axis] + alignmentOffset,
			data: {
				[axis]: offset,
				centerOffset: center - offset - alignmentOffset,
				...(shouldAddOffset && {
					alignmentOffset,
				}),
			},
			reset: shouldAddOffset,
		};
	},
});

function getPlacementList(alignment, autoAlignment, allowedPlacements) {
	const allowedPlacementsSortedByAlignment =
		alignment ?
			[...allowedPlacements.filter((placement) => getAlignment(placement) === alignment), ...allowedPlacements.filter((placement) => getAlignment(placement) !== alignment)]
		:	allowedPlacements.filter((placement) => getSide(placement) === placement);
	return allowedPlacementsSortedByAlignment.filter((placement) => {
		if (alignment) {
			return getAlignment(placement) === alignment || (autoAlignment ? getOppositeAlignmentPlacement(placement) !== placement : false);
		}
		return true;
	});
}
/**
 * Optimizes the visibility of the floating element by choosing the placement
 * that has the most space available automatically, without needing to specify a
 * preferred placement. Alternative to `flip`.
 * @see https://floating-ui.com/docs/autoPlacement
 */
const autoPlacement$2 = function (options) {
	if (options === void 0) {
		options = {};
	}
	return {
		name: "autoPlacement",
		options,
		async fn(state) {
			var _middlewareData$autoP, _middlewareData$autoP2, _placementsThatFitOnE;
			const { rects, middlewareData, placement, platform, elements } = state;
			const { crossAxis = false, alignment, allowedPlacements = placements, autoAlignment = true, ...detectOverflowOptions } = evaluate(options, state);
			const placements$1 = alignment !== undefined || allowedPlacements === placements ? getPlacementList(alignment || null, autoAlignment, allowedPlacements) : allowedPlacements;
			const overflow = await platform.detectOverflow(state, detectOverflowOptions);
			const currentIndex = ((_middlewareData$autoP = middlewareData.autoPlacement) == null ? void 0 : _middlewareData$autoP.index) || 0;
			const currentPlacement = placements$1[currentIndex];
			if (currentPlacement == null) {
				return {};
			}
			const alignmentSides = getAlignmentSides(currentPlacement, rects, await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating)));

			// Make `computeCoords` start from the right place.
			if (placement !== currentPlacement) {
				return {
					reset: {
						placement: placements$1[0],
					},
				};
			}
			const currentOverflows = [overflow[getSide(currentPlacement)], overflow[alignmentSides[0]], overflow[alignmentSides[1]]];
			const allOverflows = [
				...(((_middlewareData$autoP2 = middlewareData.autoPlacement) == null ? void 0 : _middlewareData$autoP2.overflows) || []),
				{
					placement: currentPlacement,
					overflows: currentOverflows,
				},
			];
			const nextPlacement = placements$1[currentIndex + 1];

			// There are more placements to check.
			if (nextPlacement) {
				return {
					data: {
						index: currentIndex + 1,
						overflows: allOverflows,
					},
					reset: {
						placement: nextPlacement,
					},
				};
			}
			const placementsSortedByMostSpace = allOverflows
				.map((d) => {
					const alignment = getAlignment(d.placement);
					return [
						d.placement,
						alignment && crossAxis ?
							// Check along the mainAxis and main crossAxis side.
							d.overflows.slice(0, 2).reduce((acc, v) => acc + v, 0)
							// Check only the mainAxis.
						:	d.overflows[0],
						d.overflows,
					];
				})
				.sort((a, b) => a[1] - b[1]);
			const placementsThatFitOnEachSide = placementsSortedByMostSpace.filter((d) =>
				d[2]
					.slice(
						0,
						// Aligned placements should not check their opposite crossAxis
						// side.
						getAlignment(d[0]) ? 2 : 3,
					)
					.every((v) => v <= 0),
			);
			const resetPlacement = ((_placementsThatFitOnE = placementsThatFitOnEachSide[0]) == null ? void 0 : _placementsThatFitOnE[0]) || placementsSortedByMostSpace[0][0];
			if (resetPlacement !== placement) {
				return {
					data: {
						index: currentIndex + 1,
						overflows: allOverflows,
					},
					reset: {
						placement: resetPlacement,
					},
				};
			}
			return {};
		},
	};
};

/**
 * Optimizes the visibility of the floating element by flipping the `placement`
 * in order to keep it in view when the preferred placement(s) will overflow the
 * clipping boundary. Alternative to `autoPlacement`.
 * @see https://floating-ui.com/docs/flip
 */
const flip$2 = function (options) {
	if (options === void 0) {
		options = {};
	}
	return {
		name: "flip",
		options,
		async fn(state) {
			var _middlewareData$arrow, _middlewareData$flip;
			const { placement, middlewareData, rects, initialPlacement, platform, elements } = state;
			const {
				mainAxis: checkMainAxis = true,
				crossAxis: checkCrossAxis = true,
				fallbackPlacements: specifiedFallbackPlacements,
				fallbackStrategy = "bestFit",
				fallbackAxisSideDirection = "none",
				flipAlignment = true,
				...detectOverflowOptions
			} = evaluate(options, state);

			// If a reset by the arrow was caused due to an alignment offset being
			// added, we should skip any logic now since `flip()` has already done its
			// work.
			// https://github.com/floating-ui/floating-ui/issues/2549#issuecomment-1719601643
			if ((_middlewareData$arrow = middlewareData.arrow) != null && _middlewareData$arrow.alignmentOffset) {
				return {};
			}
			const side = getSide(placement);
			const initialSideAxis = getSideAxis(initialPlacement);
			const isBasePlacement = getSide(initialPlacement) === initialPlacement;
			const rtl = await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating));
			const fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipAlignment ? [getOppositePlacement(initialPlacement)] : getExpandedPlacements(initialPlacement));
			const hasFallbackAxisSideDirection = fallbackAxisSideDirection !== "none";
			if (!specifiedFallbackPlacements && hasFallbackAxisSideDirection) {
				fallbackPlacements.push(...getOppositeAxisPlacements(initialPlacement, flipAlignment, fallbackAxisSideDirection, rtl));
			}
			const placements = [initialPlacement, ...fallbackPlacements];
			const overflow = await platform.detectOverflow(state, detectOverflowOptions);
			const overflows = [];
			let overflowsData = ((_middlewareData$flip = middlewareData.flip) == null ? void 0 : _middlewareData$flip.overflows) || [];
			if (checkMainAxis) {
				overflows.push(overflow[side]);
			}
			if (checkCrossAxis) {
				const sides = getAlignmentSides(placement, rects, rtl);
				overflows.push(overflow[sides[0]], overflow[sides[1]]);
			}
			overflowsData = [
				...overflowsData,
				{
					placement,
					overflows,
				},
			];

			// One or more sides is overflowing.
			if (!overflows.every((side) => side <= 0)) {
				var _middlewareData$flip2, _overflowsData$filter;
				const nextIndex = (((_middlewareData$flip2 = middlewareData.flip) == null ? void 0 : _middlewareData$flip2.index) || 0) + 1;
				const nextPlacement = placements[nextIndex];
				if (nextPlacement) {
					const ignoreCrossAxisOverflow = checkCrossAxis === "alignment" ? initialSideAxis !== getSideAxis(nextPlacement) : false;
					if (
						!ignoreCrossAxisOverflow ||
						// We leave the current main axis only if every placement on that axis
						// overflows the main axis.
						overflowsData.every((d) => (getSideAxis(d.placement) === initialSideAxis ? d.overflows[0] > 0 : true))
					) {
						// Try next placement and re-run the lifecycle.
						return {
							data: {
								index: nextIndex,
								overflows: overflowsData,
							},
							reset: {
								placement: nextPlacement,
							},
						};
					}
				}

				// First, find the candidates that fit on the mainAxis side of overflow,
				// then find the placement that fits the best on the main crossAxis side.
				let resetPlacement =
					(_overflowsData$filter = overflowsData.filter((d) => d.overflows[0] <= 0).sort((a, b) => a.overflows[1] - b.overflows[1])[0]) == null ? void 0 : _overflowsData$filter.placement;

				// Otherwise fallback.
				if (!resetPlacement) {
					switch (fallbackStrategy) {
						case "bestFit": {
							var _overflowsData$filter2;
							const placement =
								(
									(_overflowsData$filter2 = overflowsData
										.filter((d) => {
											if (hasFallbackAxisSideDirection) {
												const currentSideAxis = getSideAxis(d.placement);
												return (
													currentSideAxis === initialSideAxis ||
													// Create a bias to the `y` side axis due to horizontal
													// reading directions favoring greater width.
													currentSideAxis === "y"
												);
											}
											return true;
										})
										.map((d) => [d.placement, d.overflows.filter((overflow) => overflow > 0).reduce((acc, overflow) => acc + overflow, 0)])
										.sort((a, b) => a[1] - b[1])[0]) == null
								) ?
									void 0
								:	_overflowsData$filter2[0];
							if (placement) {
								resetPlacement = placement;
							}
							break;
						}
						case "initialPlacement":
							resetPlacement = initialPlacement;
							break;
					}
				}
				if (placement !== resetPlacement) {
					return {
						reset: {
							placement: resetPlacement,
						},
					};
				}
			}
			return {};
		},
	};
};

function getSideOffsets(overflow, rect) {
	return {
		top: overflow.top - rect.height,
		right: overflow.right - rect.width,
		bottom: overflow.bottom - rect.height,
		left: overflow.left - rect.width,
	};
}
function isAnySideFullyClipped(overflow) {
	return sides.some((side) => overflow[side] >= 0);
}
/**
 * Provides data to hide the floating element in applicable situations, such as
 * when it is not in the same clipping context as the reference element.
 * @see https://floating-ui.com/docs/hide
 */
const hide$2 = function (options) {
	if (options === void 0) {
		options = {};
	}
	return {
		name: "hide",
		options,
		async fn(state) {
			const { rects, platform } = state;
			const { strategy = "referenceHidden", ...detectOverflowOptions } = evaluate(options, state);
			switch (strategy) {
				case "referenceHidden": {
					const overflow = await platform.detectOverflow(state, {
						...detectOverflowOptions,
						elementContext: "reference",
					});
					const offsets = getSideOffsets(overflow, rects.reference);
					return {
						data: {
							referenceHiddenOffsets: offsets,
							referenceHidden: isAnySideFullyClipped(offsets),
						},
					};
				}
				case "escaped": {
					const overflow = await platform.detectOverflow(state, {
						...detectOverflowOptions,
						altBoundary: true,
					});
					const offsets = getSideOffsets(overflow, rects.floating);
					return {
						data: {
							escapedOffsets: offsets,
							escaped: isAnySideFullyClipped(offsets),
						},
					};
				}
				default: {
					return {};
				}
			}
		},
	};
};

const originSides = /*#__PURE__*/ new Set(["left", "top"]);

// For type backwards-compatibility, the `OffsetOptions` type was also
// Derivable.

async function convertValueToCoords(state, options) {
	const { placement, platform, elements } = state;
	const rtl = await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating));
	const side = getSide(placement);
	const alignment = getAlignment(placement);
	const isVertical = getSideAxis(placement) === "y";
	const mainAxisMulti = originSides.has(side) ? -1 : 1;
	const crossAxisMulti = rtl && isVertical ? -1 : 1;
	const rawValue = evaluate(options, state);

	// eslint-disable-next-line prefer-const
	let { mainAxis, crossAxis, alignmentAxis } =
		typeof rawValue === "number" ?
			{
				mainAxis: rawValue,
				crossAxis: 0,
				alignmentAxis: null,
			}
		:	{
				mainAxis: rawValue.mainAxis || 0,
				crossAxis: rawValue.crossAxis || 0,
				alignmentAxis: rawValue.alignmentAxis,
			};
	if (alignment && typeof alignmentAxis === "number") {
		crossAxis = alignment === "end" ? alignmentAxis * -1 : alignmentAxis;
	}
	return isVertical ?
			{
				x: crossAxis * crossAxisMulti,
				y: mainAxis * mainAxisMulti,
			}
		:	{
				x: mainAxis * mainAxisMulti,
				y: crossAxis * crossAxisMulti,
			};
}

/**
 * Modifies the placement by translating the floating element along the
 * specified axes.
 * A number (shorthand for `mainAxis` or distance), or an axes configuration
 * object may be passed.
 * @see https://floating-ui.com/docs/offset
 */
const offset$2 = function (options) {
	if (options === void 0) {
		options = 0;
	}
	return {
		name: "offset",
		options,
		async fn(state) {
			var _middlewareData$offse, _middlewareData$arrow;
			const { x, y, placement, middlewareData } = state;
			const diffCoords = await convertValueToCoords(state, options);

			// If the placement is the same and the arrow caused an alignment offset
			// then we don't need to change the positioning coordinates.
			if (
				placement === ((_middlewareData$offse = middlewareData.offset) == null ? void 0 : _middlewareData$offse.placement) &&
				(_middlewareData$arrow = middlewareData.arrow) != null &&
				_middlewareData$arrow.alignmentOffset
			) {
				return {};
			}
			return {
				x: x + diffCoords.x,
				y: y + diffCoords.y,
				data: {
					...diffCoords,
					placement,
				},
			};
		},
	};
};

/**
 * Optimizes the visibility of the floating element by shifting it in order to
 * keep it in view when it will overflow the clipping boundary.
 * @see https://floating-ui.com/docs/shift
 */
const shift$2 = function (options) {
	if (options === void 0) {
		options = {};
	}
	return {
		name: "shift",
		options,
		async fn(state) {
			const { x, y, placement, platform } = state;
			const {
				mainAxis: checkMainAxis = true,
				crossAxis: checkCrossAxis = false,
				limiter = {
					fn: (_ref) => {
						let { x, y } = _ref;
						return {
							x,
							y,
						};
					},
				},
				...detectOverflowOptions
			} = evaluate(options, state);
			const coords = {
				x,
				y,
			};
			const overflow = await platform.detectOverflow(state, detectOverflowOptions);
			const crossAxis = getSideAxis(getSide(placement));
			const mainAxis = getOppositeAxis(crossAxis);
			let mainAxisCoord = coords[mainAxis];
			let crossAxisCoord = coords[crossAxis];
			if (checkMainAxis) {
				const minSide = mainAxis === "y" ? "top" : "left";
				const maxSide = mainAxis === "y" ? "bottom" : "right";
				const min = mainAxisCoord + overflow[minSide];
				const max = mainAxisCoord - overflow[maxSide];
				mainAxisCoord = clamp(min, mainAxisCoord, max);
			}
			if (checkCrossAxis) {
				const minSide = crossAxis === "y" ? "top" : "left";
				const maxSide = crossAxis === "y" ? "bottom" : "right";
				const min = crossAxisCoord + overflow[minSide];
				const max = crossAxisCoord - overflow[maxSide];
				crossAxisCoord = clamp(min, crossAxisCoord, max);
			}
			const limitedCoords = limiter.fn({
				...state,
				[mainAxis]: mainAxisCoord,
				[crossAxis]: crossAxisCoord,
			});
			return {
				...limitedCoords,
				data: {
					x: limitedCoords.x - x,
					y: limitedCoords.y - y,
					enabled: {
						[mainAxis]: checkMainAxis,
						[crossAxis]: checkCrossAxis,
					},
				},
			};
		},
	};
};
/**
 * Built-in `limiter` that will stop `shift()` at a certain point.
 */
const limitShift$2 = function (options) {
	if (options === void 0) {
		options = {};
	}
	return {
		options,
		fn(state) {
			const { x, y, placement, rects, middlewareData } = state;
			const { offset = 0, mainAxis: checkMainAxis = true, crossAxis: checkCrossAxis = true } = evaluate(options, state);
			const coords = {
				x,
				y,
			};
			const crossAxis = getSideAxis(placement);
			const mainAxis = getOppositeAxis(crossAxis);
			let mainAxisCoord = coords[mainAxis];
			let crossAxisCoord = coords[crossAxis];
			const rawOffset = evaluate(offset, state);
			const computedOffset =
				typeof rawOffset === "number" ?
					{
						mainAxis: rawOffset,
						crossAxis: 0,
					}
				:	{
						mainAxis: 0,
						crossAxis: 0,
						...rawOffset,
					};
			if (checkMainAxis) {
				const len = mainAxis === "y" ? "height" : "width";
				const limitMin = rects.reference[mainAxis] - rects.floating[len] + computedOffset.mainAxis;
				const limitMax = rects.reference[mainAxis] + rects.reference[len] - computedOffset.mainAxis;
				if (mainAxisCoord < limitMin) {
					mainAxisCoord = limitMin;
				} else if (mainAxisCoord > limitMax) {
					mainAxisCoord = limitMax;
				}
			}
			if (checkCrossAxis) {
				var _middlewareData$offse, _middlewareData$offse2;
				const len = mainAxis === "y" ? "width" : "height";
				const isOriginSide = originSides.has(getSide(placement));
				const limitMin =
					rects.reference[crossAxis] -
					rects.floating[len] +
					(isOriginSide ? ((_middlewareData$offse = middlewareData.offset) == null ? void 0 : _middlewareData$offse[crossAxis]) || 0 : 0) +
					(isOriginSide ? 0 : computedOffset.crossAxis);
				const limitMax =
					rects.reference[crossAxis] +
					rects.reference[len] +
					(isOriginSide ? 0 : ((_middlewareData$offse2 = middlewareData.offset) == null ? void 0 : _middlewareData$offse2[crossAxis]) || 0) -
					(isOriginSide ? computedOffset.crossAxis : 0);
				if (crossAxisCoord < limitMin) {
					crossAxisCoord = limitMin;
				} else if (crossAxisCoord > limitMax) {
					crossAxisCoord = limitMax;
				}
			}
			return {
				[mainAxis]: mainAxisCoord,
				[crossAxis]: crossAxisCoord,
			};
		},
	};
};

/**
 * Provides data that allows you to change the size of the floating element —
 * for instance, prevent it from overflowing the clipping boundary or match the
 * width of the reference element.
 * @see https://floating-ui.com/docs/size
 */
const size$2 = function (options) {
	if (options === void 0) {
		options = {};
	}
	return {
		name: "size",
		options,
		async fn(state) {
			var _state$middlewareData, _state$middlewareData2;
			const { placement, rects, platform, elements } = state;
			const { apply = () => {}, ...detectOverflowOptions } = evaluate(options, state);
			const overflow = await platform.detectOverflow(state, detectOverflowOptions);
			const side = getSide(placement);
			const alignment = getAlignment(placement);
			const isYAxis = getSideAxis(placement) === "y";
			const { width, height } = rects.floating;
			let heightSide;
			let widthSide;
			if (side === "top" || side === "bottom") {
				heightSide = side;
				widthSide = alignment === ((await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating))) ? "start" : "end") ? "left" : "right";
			} else {
				widthSide = side;
				heightSide = alignment === "end" ? "top" : "bottom";
			}
			const maximumClippingHeight = height - overflow.top - overflow.bottom;
			const maximumClippingWidth = width - overflow.left - overflow.right;
			const overflowAvailableHeight = min(height - overflow[heightSide], maximumClippingHeight);
			const overflowAvailableWidth = min(width - overflow[widthSide], maximumClippingWidth);
			const noShift = !state.middlewareData.shift;
			let availableHeight = overflowAvailableHeight;
			let availableWidth = overflowAvailableWidth;
			if ((_state$middlewareData = state.middlewareData.shift) != null && _state$middlewareData.enabled.x) {
				availableWidth = maximumClippingWidth;
			}
			if ((_state$middlewareData2 = state.middlewareData.shift) != null && _state$middlewareData2.enabled.y) {
				availableHeight = maximumClippingHeight;
			}
			if (noShift && !alignment) {
				const xMin = max(overflow.left, 0);
				const xMax = max(overflow.right, 0);
				const yMin = max(overflow.top, 0);
				const yMax = max(overflow.bottom, 0);
				if (isYAxis) {
					availableWidth = width - 2 * (xMin !== 0 || xMax !== 0 ? xMin + xMax : max(overflow.left, overflow.right));
				} else {
					availableHeight = height - 2 * (yMin !== 0 || yMax !== 0 ? yMin + yMax : max(overflow.top, overflow.bottom));
				}
			}
			await apply({
				...state,
				availableWidth,
				availableHeight,
			});
			const nextDimensions = await platform.getDimensions(elements.floating);
			if (width !== nextDimensions.width || height !== nextDimensions.height) {
				return {
					reset: {
						rects: true,
					},
				};
			}
			return {};
		},
	};
};

function hasWindow() {
	return typeof window !== "undefined";
}
function getNodeName(node) {
	if (isNode(node)) {
		return (node.nodeName || "").toLowerCase();
	}
	// Mocked nodes in testing environments may not be instances of Node. By
	// returning `#document` an infinite loop won't occur.
	// https://github.com/floating-ui/floating-ui/issues/2317
	return "#document";
}
function getWindow(node) {
	var _node$ownerDocument;
	return (node == null || (_node$ownerDocument = node.ownerDocument) == null ? void 0 : _node$ownerDocument.defaultView) || window;
}
function getDocumentElement(node) {
	var _ref;
	return (_ref = (isNode(node) ? node.ownerDocument : node.document) || window.document) == null ? void 0 : _ref.documentElement;
}
function isNode(value) {
	if (!hasWindow()) {
		return false;
	}
	return value instanceof Node || value instanceof getWindow(value).Node;
}
function isElement(value) {
	if (!hasWindow()) {
		return false;
	}
	return value instanceof Element || value instanceof getWindow(value).Element;
}
function isHTMLElement(value) {
	if (!hasWindow()) {
		return false;
	}
	return value instanceof HTMLElement || value instanceof getWindow(value).HTMLElement;
}
function isShadowRoot(value) {
	if (!hasWindow() || typeof ShadowRoot === "undefined") {
		return false;
	}
	return value instanceof ShadowRoot || value instanceof getWindow(value).ShadowRoot;
}
function isOverflowElement(element) {
	const { overflow, overflowX, overflowY, display } = getComputedStyle$1(element);
	return /auto|scroll|overlay|hidden|clip/.test(overflow + overflowY + overflowX) && display !== "inline" && display !== "contents";
}
function isTableElement(element) {
	return /^(table|td|th)$/.test(getNodeName(element));
}
function isTopLayer(element) {
	try {
		if (element.matches(":popover-open")) {
			return true;
		}
	} catch (_e) {
		// no-op
	}
	try {
		return element.matches(":modal");
	} catch (_e) {
		return false;
	}
}
const willChangeRe = /transform|translate|scale|rotate|perspective|filter/;
const containRe = /paint|layout|strict|content/;
const isNotNone = (value) => !!value && value !== "none";
let isWebKitValue;
function isContainingBlock(elementOrCss) {
	const css = isElement(elementOrCss) ? getComputedStyle$1(elementOrCss) : elementOrCss;

	// https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block
	// https://drafts.csswg.org/css-transforms-2/#individual-transforms
	return (
		isNotNone(css.transform) ||
		isNotNone(css.translate) ||
		isNotNone(css.scale) ||
		isNotNone(css.rotate) ||
		isNotNone(css.perspective) ||
		(!isWebKit() && (isNotNone(css.backdropFilter) || isNotNone(css.filter))) ||
		willChangeRe.test(css.willChange || "") ||
		containRe.test(css.contain || "")
	);
}
function getContainingBlock(element) {
	let currentNode = getParentNode(element);
	while (isHTMLElement(currentNode) && !isLastTraversableNode(currentNode)) {
		if (isContainingBlock(currentNode)) {
			return currentNode;
		} else if (isTopLayer(currentNode)) {
			return null;
		}
		currentNode = getParentNode(currentNode);
	}
	return null;
}
function isWebKit() {
	if (isWebKitValue == null) {
		isWebKitValue = typeof CSS !== "undefined" && CSS.supports && CSS.supports("-webkit-backdrop-filter", "none");
	}
	return isWebKitValue;
}
function isLastTraversableNode(node) {
	return /^(html|body|#document)$/.test(getNodeName(node));
}
function getComputedStyle$1(element) {
	return getWindow(element).getComputedStyle(element);
}
function getNodeScroll(element) {
	if (isElement(element)) {
		return {
			scrollLeft: element.scrollLeft,
			scrollTop: element.scrollTop,
		};
	}
	return {
		scrollLeft: element.scrollX,
		scrollTop: element.scrollY,
	};
}
function getParentNode(node) {
	if (getNodeName(node) === "html") {
		return node;
	}
	const result =
		// Step into the shadow DOM of the parent of a slotted node.
		node.assignedSlot ||
		// DOM Element detected.
		node.parentNode ||
		// ShadowRoot detected.
		(isShadowRoot(node) && node.host) ||
		// Fallback.
		getDocumentElement(node);
	return isShadowRoot(result) ? result.host : result;
}
function getNearestOverflowAncestor(node) {
	const parentNode = getParentNode(node);
	if (isLastTraversableNode(parentNode)) {
		return node.ownerDocument ? node.ownerDocument.body : node.body;
	}
	if (isHTMLElement(parentNode) && isOverflowElement(parentNode)) {
		return parentNode;
	}
	return getNearestOverflowAncestor(parentNode);
}
function getOverflowAncestors(node, list, traverseIframes) {
	var _node$ownerDocument2;
	if (list === void 0) {
		list = [];
	}
	if (traverseIframes === void 0) {
		traverseIframes = true;
	}
	const scrollableAncestor = getNearestOverflowAncestor(node);
	const isBody = scrollableAncestor === ((_node$ownerDocument2 = node.ownerDocument) == null ? void 0 : _node$ownerDocument2.body);
	const win = getWindow(scrollableAncestor);
	if (isBody) {
		const frameElement = getFrameElement(win);
		return list.concat(win, win.visualViewport || [], isOverflowElement(scrollableAncestor) ? scrollableAncestor : [], frameElement && traverseIframes ? getOverflowAncestors(frameElement) : []);
	} else {
		return list.concat(scrollableAncestor, getOverflowAncestors(scrollableAncestor, [], traverseIframes));
	}
}
function getFrameElement(win) {
	return win.parent && Object.getPrototypeOf(win.parent) ? win.frameElement : null;
}

function getCssDimensions(element) {
	const css = getComputedStyle$1(element);
	// In testing environments, the `width` and `height` properties are empty
	// strings for SVG elements, returning NaN. Fallback to `0` in this case.
	let width = parseFloat(css.width) || 0;
	let height = parseFloat(css.height) || 0;
	const hasOffset = isHTMLElement(element);
	const offsetWidth = hasOffset ? element.offsetWidth : width;
	const offsetHeight = hasOffset ? element.offsetHeight : height;
	const shouldFallback = round(width) !== offsetWidth || round(height) !== offsetHeight;
	if (shouldFallback) {
		width = offsetWidth;
		height = offsetHeight;
	}
	return {
		width,
		height,
		$: shouldFallback,
	};
}

function unwrapElement(element) {
	return !isElement(element) ? element.contextElement : element;
}

function getScale(element) {
	const domElement = unwrapElement(element);
	if (!isHTMLElement(domElement)) {
		return createCoords(1);
	}
	const rect = domElement.getBoundingClientRect();
	const { width, height, $ } = getCssDimensions(domElement);
	let x = ($ ? round(rect.width) : rect.width) / width;
	let y = ($ ? round(rect.height) : rect.height) / height;

	// 0, NaN, or Infinity should always fallback to 1.

	if (!x || !Number.isFinite(x)) {
		x = 1;
	}
	if (!y || !Number.isFinite(y)) {
		y = 1;
	}
	return {
		x,
		y,
	};
}

const noOffsets = /*#__PURE__*/ createCoords(0);
function getVisualOffsets(element) {
	const win = getWindow(element);
	if (!isWebKit() || !win.visualViewport) {
		return noOffsets;
	}
	return {
		x: win.visualViewport.offsetLeft,
		y: win.visualViewport.offsetTop,
	};
}
function shouldAddVisualOffsets(element, isFixed, floatingOffsetParent) {
	if (isFixed === void 0) {
		isFixed = false;
	}
	if (!floatingOffsetParent || (isFixed && floatingOffsetParent !== getWindow(element))) {
		return false;
	}
	return isFixed;
}

function getBoundingClientRect(element, includeScale, isFixedStrategy, offsetParent) {
	if (includeScale === void 0) {
		includeScale = false;
	}
	if (isFixedStrategy === void 0) {
		isFixedStrategy = false;
	}
	const clientRect = element.getBoundingClientRect();
	const domElement = unwrapElement(element);
	let scale = createCoords(1);
	if (includeScale) {
		if (offsetParent) {
			if (isElement(offsetParent)) {
				scale = getScale(offsetParent);
			}
		} else {
			scale = getScale(element);
		}
	}
	const visualOffsets = shouldAddVisualOffsets(domElement, isFixedStrategy, offsetParent) ? getVisualOffsets(domElement) : createCoords(0);
	let x = (clientRect.left + visualOffsets.x) / scale.x;
	let y = (clientRect.top + visualOffsets.y) / scale.y;
	let width = clientRect.width / scale.x;
	let height = clientRect.height / scale.y;
	if (domElement) {
		const win = getWindow(domElement);
		const offsetWin = offsetParent && isElement(offsetParent) ? getWindow(offsetParent) : offsetParent;
		let currentWin = win;
		let currentIFrame = getFrameElement(currentWin);
		while (currentIFrame && offsetParent && offsetWin !== currentWin) {
			const iframeScale = getScale(currentIFrame);
			const iframeRect = currentIFrame.getBoundingClientRect();
			const css = getComputedStyle$1(currentIFrame);
			const left = iframeRect.left + (currentIFrame.clientLeft + parseFloat(css.paddingLeft)) * iframeScale.x;
			const top = iframeRect.top + (currentIFrame.clientTop + parseFloat(css.paddingTop)) * iframeScale.y;
			x *= iframeScale.x;
			y *= iframeScale.y;
			width *= iframeScale.x;
			height *= iframeScale.y;
			x += left;
			y += top;
			currentWin = getWindow(currentIFrame);
			currentIFrame = getFrameElement(currentWin);
		}
	}
	return rectToClientRect({
		width,
		height,
		x,
		y,
	});
}

// If <html> has a CSS width greater than the viewport, then this will be
// incorrect for RTL.
function getWindowScrollBarX(element, rect) {
	const leftScroll = getNodeScroll(element).scrollLeft;
	if (!rect) {
		return getBoundingClientRect(getDocumentElement(element)).left + leftScroll;
	}
	return rect.left + leftScroll;
}

function getHTMLOffset(documentElement, scroll) {
	const htmlRect = documentElement.getBoundingClientRect();
	const x = htmlRect.left + scroll.scrollLeft - getWindowScrollBarX(documentElement, htmlRect);
	const y = htmlRect.top + scroll.scrollTop;
	return {
		x,
		y,
	};
}

function convertOffsetParentRelativeRectToViewportRelativeRect(_ref) {
	let { elements, rect, offsetParent, strategy } = _ref;
	const isFixed = strategy === "fixed";
	const documentElement = getDocumentElement(offsetParent);
	const topLayer = elements ? isTopLayer(elements.floating) : false;
	if (offsetParent === documentElement || (topLayer && isFixed)) {
		return rect;
	}
	let scroll = {
		scrollLeft: 0,
		scrollTop: 0,
	};
	let scale = createCoords(1);
	const offsets = createCoords(0);
	const isOffsetParentAnElement = isHTMLElement(offsetParent);
	if (isOffsetParentAnElement || (!isOffsetParentAnElement && !isFixed)) {
		if (getNodeName(offsetParent) !== "body" || isOverflowElement(documentElement)) {
			scroll = getNodeScroll(offsetParent);
		}
		if (isOffsetParentAnElement) {
			const offsetRect = getBoundingClientRect(offsetParent);
			scale = getScale(offsetParent);
			offsets.x = offsetRect.x + offsetParent.clientLeft;
			offsets.y = offsetRect.y + offsetParent.clientTop;
		}
	}
	const htmlOffset = documentElement && !isOffsetParentAnElement && !isFixed ? getHTMLOffset(documentElement, scroll) : createCoords(0);
	return {
		width: rect.width * scale.x,
		height: rect.height * scale.y,
		x: rect.x * scale.x - scroll.scrollLeft * scale.x + offsets.x + htmlOffset.x,
		y: rect.y * scale.y - scroll.scrollTop * scale.y + offsets.y + htmlOffset.y,
	};
}

function getClientRects(element) {
	return Array.from(element.getClientRects());
}

// Gets the entire size of the scrollable document area, even extending outside
// of the `<html>` and `<body>` rect bounds if horizontally scrollable.
function getDocumentRect(element) {
	const html = getDocumentElement(element);
	const scroll = getNodeScroll(element);
	const body = element.ownerDocument.body;
	const width = max(html.scrollWidth, html.clientWidth, body.scrollWidth, body.clientWidth);
	const height = max(html.scrollHeight, html.clientHeight, body.scrollHeight, body.clientHeight);
	let x = -scroll.scrollLeft + getWindowScrollBarX(element);
	const y = -scroll.scrollTop;
	if (getComputedStyle$1(body).direction === "rtl") {
		x += max(html.clientWidth, body.clientWidth) - width;
	}
	return {
		width,
		height,
		x,
		y,
	};
}

// Safety check: ensure the scrollbar space is reasonable in case this
// calculation is affected by unusual styles.
// Most scrollbars leave 15-18px of space.
const SCROLLBAR_MAX = 25;
function getViewportRect(element, strategy) {
	const win = getWindow(element);
	const html = getDocumentElement(element);
	const visualViewport = win.visualViewport;
	let width = html.clientWidth;
	let height = html.clientHeight;
	let x = 0;
	let y = 0;
	if (visualViewport) {
		width = visualViewport.width;
		height = visualViewport.height;
		const visualViewportBased = isWebKit();
		if (!visualViewportBased || (visualViewportBased && strategy === "fixed")) {
			x = visualViewport.offsetLeft;
			y = visualViewport.offsetTop;
		}
	}
	const windowScrollbarX = getWindowScrollBarX(html);
	// <html> `overflow: hidden` + `scrollbar-gutter: stable` reduces the
	// visual width of the <html> but this is not considered in the size
	// of `html.clientWidth`.
	if (windowScrollbarX <= 0) {
		const doc = html.ownerDocument;
		const body = doc.body;
		const bodyStyles = getComputedStyle(body);
		const bodyMarginInline = doc.compatMode === "CSS1Compat" ? parseFloat(bodyStyles.marginLeft) + parseFloat(bodyStyles.marginRight) || 0 : 0;
		const clippingStableScrollbarWidth = Math.abs(html.clientWidth - body.clientWidth - bodyMarginInline);
		if (clippingStableScrollbarWidth <= SCROLLBAR_MAX) {
			width -= clippingStableScrollbarWidth;
		}
	} else if (windowScrollbarX <= SCROLLBAR_MAX) {
		// If the <body> scrollbar is on the left, the width needs to be extended
		// by the scrollbar amount so there isn't extra space on the right.
		width += windowScrollbarX;
	}
	return {
		width,
		height,
		x,
		y,
	};
}

// Returns the inner client rect, subtracting scrollbars if present.
function getInnerBoundingClientRect(element, strategy) {
	const clientRect = getBoundingClientRect(element, true, strategy === "fixed");
	const top = clientRect.top + element.clientTop;
	const left = clientRect.left + element.clientLeft;
	const scale = isHTMLElement(element) ? getScale(element) : createCoords(1);
	const width = element.clientWidth * scale.x;
	const height = element.clientHeight * scale.y;
	const x = left * scale.x;
	const y = top * scale.y;
	return {
		width,
		height,
		x,
		y,
	};
}
function getClientRectFromClippingAncestor(element, clippingAncestor, strategy) {
	let rect;
	if (clippingAncestor === "viewport") {
		rect = getViewportRect(element, strategy);
	} else if (clippingAncestor === "document") {
		rect = getDocumentRect(getDocumentElement(element));
	} else if (isElement(clippingAncestor)) {
		rect = getInnerBoundingClientRect(clippingAncestor, strategy);
	} else {
		const visualOffsets = getVisualOffsets(element);
		rect = {
			x: clippingAncestor.x - visualOffsets.x,
			y: clippingAncestor.y - visualOffsets.y,
			width: clippingAncestor.width,
			height: clippingAncestor.height,
		};
	}
	return rectToClientRect(rect);
}
function hasFixedPositionAncestor(element, stopNode) {
	const parentNode = getParentNode(element);
	if (parentNode === stopNode || !isElement(parentNode) || isLastTraversableNode(parentNode)) {
		return false;
	}
	return getComputedStyle$1(parentNode).position === "fixed" || hasFixedPositionAncestor(parentNode, stopNode);
}

// A "clipping ancestor" is an `overflow` element with the characteristic of
// clipping (or hiding) child elements. This returns all clipping ancestors
// of the given element up the tree.
function getClippingElementAncestors(element, cache) {
	const cachedResult = cache.get(element);
	if (cachedResult) {
		return cachedResult;
	}
	let result = getOverflowAncestors(element, [], false).filter((el) => isElement(el) && getNodeName(el) !== "body");
	let currentContainingBlockComputedStyle = null;
	const elementIsFixed = getComputedStyle$1(element).position === "fixed";
	let currentNode = elementIsFixed ? getParentNode(element) : element;

	// https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block
	while (isElement(currentNode) && !isLastTraversableNode(currentNode)) {
		const computedStyle = getComputedStyle$1(currentNode);
		const currentNodeIsContaining = isContainingBlock(currentNode);
		if (!currentNodeIsContaining && computedStyle.position === "fixed") {
			currentContainingBlockComputedStyle = null;
		}
		const shouldDropCurrentNode =
			elementIsFixed ?
				!currentNodeIsContaining && !currentContainingBlockComputedStyle
			:	(!currentNodeIsContaining &&
					computedStyle.position === "static" &&
					!!currentContainingBlockComputedStyle &&
					(currentContainingBlockComputedStyle.position === "absolute" || currentContainingBlockComputedStyle.position === "fixed")) ||
				(isOverflowElement(currentNode) && !currentNodeIsContaining && hasFixedPositionAncestor(element, currentNode));
		if (shouldDropCurrentNode) {
			// Drop non-containing blocks.
			result = result.filter((ancestor) => ancestor !== currentNode);
		} else {
			// Record last containing block for next iteration.
			currentContainingBlockComputedStyle = computedStyle;
		}
		currentNode = getParentNode(currentNode);
	}
	cache.set(element, result);
	return result;
}

// Gets the maximum area that the element is visible in due to any number of
// clipping ancestors.
function getClippingRect(_ref) {
	let { element, boundary, rootBoundary, strategy } = _ref;
	const elementClippingAncestors =
		boundary === "clippingAncestors" ?
			isTopLayer(element) ? []
			:	getClippingElementAncestors(element, this._c)
		:	[].concat(boundary);
	const clippingAncestors = [...elementClippingAncestors, rootBoundary];
	const firstRect = getClientRectFromClippingAncestor(element, clippingAncestors[0], strategy);
	let top = firstRect.top;
	let right = firstRect.right;
	let bottom = firstRect.bottom;
	let left = firstRect.left;
	for (let i = 1; i < clippingAncestors.length; i++) {
		const rect = getClientRectFromClippingAncestor(element, clippingAncestors[i], strategy);
		top = max(rect.top, top);
		right = min(rect.right, right);
		bottom = min(rect.bottom, bottom);
		left = max(rect.left, left);
	}
	return {
		width: right - left,
		height: bottom - top,
		x: left,
		y: top,
	};
}

function getDimensions(element) {
	const { width, height } = getCssDimensions(element);
	return {
		width,
		height,
	};
}

function getRectRelativeToOffsetParent(element, offsetParent, strategy) {
	const isOffsetParentAnElement = isHTMLElement(offsetParent);
	const documentElement = getDocumentElement(offsetParent);
	const isFixed = strategy === "fixed";
	const rect = getBoundingClientRect(element, true, isFixed, offsetParent);
	let scroll = {
		scrollLeft: 0,
		scrollTop: 0,
	};
	const offsets = createCoords(0);

	// If the <body> scrollbar appears on the left (e.g. RTL systems). Use
	// Firefox with layout.scrollbar.side = 3 in about:config to test this.
	function setLeftRTLScrollbarOffset() {
		offsets.x = getWindowScrollBarX(documentElement);
	}
	if (isOffsetParentAnElement || (!isOffsetParentAnElement && !isFixed)) {
		if (getNodeName(offsetParent) !== "body" || isOverflowElement(documentElement)) {
			scroll = getNodeScroll(offsetParent);
		}
		if (isOffsetParentAnElement) {
			const offsetRect = getBoundingClientRect(offsetParent, true, isFixed, offsetParent);
			offsets.x = offsetRect.x + offsetParent.clientLeft;
			offsets.y = offsetRect.y + offsetParent.clientTop;
		} else if (documentElement) {
			setLeftRTLScrollbarOffset();
		}
	}
	if (isFixed && !isOffsetParentAnElement && documentElement) {
		setLeftRTLScrollbarOffset();
	}
	const htmlOffset = documentElement && !isOffsetParentAnElement && !isFixed ? getHTMLOffset(documentElement, scroll) : createCoords(0);
	const x = rect.left + scroll.scrollLeft - offsets.x - htmlOffset.x;
	const y = rect.top + scroll.scrollTop - offsets.y - htmlOffset.y;
	return {
		x,
		y,
		width: rect.width,
		height: rect.height,
	};
}

function isStaticPositioned(element) {
	return getComputedStyle$1(element).position === "static";
}

function getTrueOffsetParent(element, polyfill) {
	if (!isHTMLElement(element) || getComputedStyle$1(element).position === "fixed") {
		return null;
	}
	if (polyfill) {
		return polyfill(element);
	}
	let rawOffsetParent = element.offsetParent;

	// Firefox returns the <html> element as the offsetParent if it's non-static,
	// while Chrome and Safari return the <body> element. The <body> element must
	// be used to perform the correct calculations even if the <html> element is
	// non-static.
	if (getDocumentElement(element) === rawOffsetParent) {
		rawOffsetParent = rawOffsetParent.ownerDocument.body;
	}
	return rawOffsetParent;
}

// Gets the closest ancestor positioned element. Handles some edge cases,
// such as table ancestors and cross browser bugs.
function getOffsetParent(element, polyfill) {
	const win = getWindow(element);
	if (isTopLayer(element)) {
		return win;
	}
	if (!isHTMLElement(element)) {
		let svgOffsetParent = getParentNode(element);
		while (svgOffsetParent && !isLastTraversableNode(svgOffsetParent)) {
			if (isElement(svgOffsetParent) && !isStaticPositioned(svgOffsetParent)) {
				return svgOffsetParent;
			}
			svgOffsetParent = getParentNode(svgOffsetParent);
		}
		return win;
	}
	let offsetParent = getTrueOffsetParent(element, polyfill);
	while (offsetParent && isTableElement(offsetParent) && isStaticPositioned(offsetParent)) {
		offsetParent = getTrueOffsetParent(offsetParent, polyfill);
	}
	if (offsetParent && isLastTraversableNode(offsetParent) && isStaticPositioned(offsetParent) && !isContainingBlock(offsetParent)) {
		return win;
	}
	return offsetParent || getContainingBlock(element) || win;
}

const getElementRects = async function (data) {
	const getOffsetParentFn = this.getOffsetParent || getOffsetParent;
	const getDimensionsFn = this.getDimensions;
	const floatingDimensions = await getDimensionsFn(data.floating);
	return {
		reference: getRectRelativeToOffsetParent(data.reference, await getOffsetParentFn(data.floating), data.strategy),
		floating: {
			x: 0,
			y: 0,
			width: floatingDimensions.width,
			height: floatingDimensions.height,
		},
	};
};

function isRTL(element) {
	return getComputedStyle$1(element).direction === "rtl";
}

const platform = {
	convertOffsetParentRelativeRectToViewportRelativeRect,
	getDocumentElement,
	getClippingRect,
	getOffsetParent,
	getElementRects,
	getClientRects,
	getDimensions,
	getScale,
	isElement,
	isRTL,
};

function rectsAreEqual(a, b) {
	return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;
}

// https://samthor.au/2021/observing-dom/
function observeMove(element, onMove) {
	let io = null;
	let timeoutId;
	const root = getDocumentElement(element);
	function cleanup() {
		var _io;
		clearTimeout(timeoutId);
		(_io = io) == null || _io.disconnect();
		io = null;
	}
	function refresh(skip, threshold) {
		if (skip === void 0) {
			skip = false;
		}
		if (threshold === void 0) {
			threshold = 1;
		}
		cleanup();
		const elementRectForRootMargin = element.getBoundingClientRect();
		const { left, top, width, height } = elementRectForRootMargin;
		if (!skip) {
			onMove();
		}
		if (!width || !height) {
			return;
		}
		const insetTop = floor(top);
		const insetRight = floor(root.clientWidth - (left + width));
		const insetBottom = floor(root.clientHeight - (top + height));
		const insetLeft = floor(left);
		const rootMargin = -insetTop + "px " + -insetRight + "px " + -insetBottom + "px " + -insetLeft + "px";
		const options = {
			rootMargin,
			threshold: max(0, min(1, threshold)) || 1,
		};
		let isFirstUpdate = true;
		function handleObserve(entries) {
			const ratio = entries[0].intersectionRatio;
			if (ratio !== threshold) {
				if (!isFirstUpdate) {
					return refresh();
				}
				if (!ratio) {
					// If the reference is clipped, the ratio is 0. Throttle the refresh
					// to prevent an infinite loop of updates.
					timeoutId = setTimeout(() => {
						refresh(false, 1e-7);
					}, 1000);
				} else {
					refresh(false, ratio);
				}
			}
			if (ratio === 1 && !rectsAreEqual(elementRectForRootMargin, element.getBoundingClientRect())) {
				// It's possible that even though the ratio is reported as 1, the
				// element is not actually fully within the IntersectionObserver's root
				// area anymore. This can happen under performance constraints. This may
				// be a bug in the browser's IntersectionObserver implementation. To
				// work around this, we compare the element's bounding rect now with
				// what it was at the time we created the IntersectionObserver. If they
				// are not equal then the element moved, so we refresh.
				refresh();
			}
			isFirstUpdate = false;
		}

		// Older browsers don't support a `document` as the root and will throw an
		// error.
		try {
			io = new IntersectionObserver(handleObserve, {
				...options,
				// Handle <iframe>s
				root: root.ownerDocument,
			});
		} catch (_e) {
			io = new IntersectionObserver(handleObserve, options);
		}
		io.observe(element);
	}
	refresh(true);
	return cleanup;
}

/**
 * Automatically updates the position of the floating element when necessary.
 * Should only be called when the floating element is mounted on the DOM or
 * visible on the screen.
 * @returns cleanup function that should be invoked when the floating element is
 * removed from the DOM or hidden from the screen.
 * @see https://floating-ui.com/docs/autoUpdate
 */
function autoUpdate(reference, floating, update, options) {
	if (options === void 0) {
		options = {};
	}
	const {
		ancestorScroll = true,
		ancestorResize = true,
		elementResize = typeof ResizeObserver === "function",
		layoutShift = typeof IntersectionObserver === "function",
		animationFrame = false,
	} = options;
	const referenceEl = unwrapElement(reference);
	const ancestors = ancestorScroll || ancestorResize ? [...(referenceEl ? getOverflowAncestors(referenceEl) : []), ...(floating ? getOverflowAncestors(floating) : [])] : [];
	ancestors.forEach((ancestor) => {
		ancestorScroll &&
			ancestor.addEventListener("scroll", update, {
				passive: true,
			});
		ancestorResize && ancestor.addEventListener("resize", update);
	});
	const cleanupIo = referenceEl && layoutShift ? observeMove(referenceEl, update) : null;
	let reobserveFrame = -1;
	let resizeObserver = null;
	if (elementResize) {
		resizeObserver = new ResizeObserver((_ref) => {
			let [firstEntry] = _ref;
			if (firstEntry && firstEntry.target === referenceEl && resizeObserver && floating) {
				// Prevent update loops when using the `size` middleware.
				// https://github.com/floating-ui/floating-ui/issues/1740
				resizeObserver.unobserve(floating);
				cancelAnimationFrame(reobserveFrame);
				reobserveFrame = requestAnimationFrame(() => {
					var _resizeObserver;
					(_resizeObserver = resizeObserver) == null || _resizeObserver.observe(floating);
				});
			}
			update();
		});
		if (referenceEl && !animationFrame) {
			resizeObserver.observe(referenceEl);
		}
		if (floating) {
			resizeObserver.observe(floating);
		}
	}
	let frameId;
	let prevRefRect = animationFrame ? getBoundingClientRect(reference) : null;
	if (animationFrame) {
		frameLoop();
	}
	function frameLoop() {
		const nextRefRect = getBoundingClientRect(reference);
		if (prevRefRect && !rectsAreEqual(prevRefRect, nextRefRect)) {
			update();
		}
		prevRefRect = nextRefRect;
		frameId = requestAnimationFrame(frameLoop);
	}
	update();
	return () => {
		var _resizeObserver2;
		ancestors.forEach((ancestor) => {
			ancestorScroll && ancestor.removeEventListener("scroll", update);
			ancestorResize && ancestor.removeEventListener("resize", update);
		});
		cleanupIo == null || cleanupIo();
		(_resizeObserver2 = resizeObserver) == null || _resizeObserver2.disconnect();
		resizeObserver = null;
		if (animationFrame) {
			cancelAnimationFrame(frameId);
		}
	};
}

/**
 * Modifies the placement by translating the floating element along the
 * specified axes.
 * A number (shorthand for `mainAxis` or distance), or an axes configuration
 * object may be passed.
 * @see https://floating-ui.com/docs/offset
 */
const offset$1 = offset$2;

/**
 * Optimizes the visibility of the floating element by choosing the placement
 * that has the most space available automatically, without needing to specify a
 * preferred placement. Alternative to `flip`.
 * @see https://floating-ui.com/docs/autoPlacement
 */
const autoPlacement$1 = autoPlacement$2;

/**
 * Optimizes the visibility of the floating element by shifting it in order to
 * keep it in view when it will overflow the clipping boundary.
 * @see https://floating-ui.com/docs/shift
 */
const shift$1 = shift$2;

/**
 * Optimizes the visibility of the floating element by flipping the `placement`
 * in order to keep it in view when the preferred placement(s) will overflow the
 * clipping boundary. Alternative to `autoPlacement`.
 * @see https://floating-ui.com/docs/flip
 */
const flip$1 = flip$2;

/**
 * Provides data that allows you to change the size of the floating element —
 * for instance, prevent it from overflowing the clipping boundary or match the
 * width of the reference element.
 * @see https://floating-ui.com/docs/size
 */
const size$1 = size$2;

/**
 * Provides data to hide the floating element in applicable situations, such as
 * when it is not in the same clipping context as the reference element.
 * @see https://floating-ui.com/docs/hide
 */
const hide$1 = hide$2;

/**
 * Provides data to position an inner element of the floating element so that it
 * appears centered to the reference element.
 * @see https://floating-ui.com/docs/arrow
 */
const arrow$2 = arrow$3;

/**
 * Built-in `limiter` that will stop `shift()` at a certain point.
 */
const limitShift$1 = limitShift$2;

/**
 * Computes the `x` and `y` coordinates that will place the floating element
 * next to a given reference element.
 */
const computePosition = (reference, floating, options) => {
	// This caches the expensive `getClippingElementAncestors` function so that
	// multiple lifecycle resets re-use the same result. It only lives for a
	// single call. If other functions become expensive, we can add them as well.
	const cache = new Map();
	const mergedOptions = {
		platform,
		...options,
	};
	const platformWithCache = {
		...mergedOptions.platform,
		_c: cache,
	};
	return computePosition$1(reference, floating, {
		...mergedOptions,
		platform: platformWithCache,
	});
};

const React$4 = await importShared("react");

const { useLayoutEffect } = await importShared("react");

const ReactDOM$1 = await importShared("react-dom");

var isClient = typeof document !== "undefined";

var noop = function noop() {};
var index = isClient ? useLayoutEffect : noop;

// Fork of `fast-deep-equal` that only does the comparisons we need and compares
// functions
function deepEqual(a, b) {
	if (a === b) {
		return true;
	}
	if (typeof a !== typeof b) {
		return false;
	}
	if (typeof a === "function" && a.toString() === b.toString()) {
		return true;
	}
	let length;
	let i;
	let keys;
	if (a && b && typeof a === "object") {
		if (Array.isArray(a)) {
			length = a.length;
			if (length !== b.length) return false;
			for (i = length; i-- !== 0; ) {
				if (!deepEqual(a[i], b[i])) {
					return false;
				}
			}
			return true;
		}
		keys = Object.keys(a);
		length = keys.length;
		if (length !== Object.keys(b).length) {
			return false;
		}
		for (i = length; i-- !== 0; ) {
			if (!{}.hasOwnProperty.call(b, keys[i])) {
				return false;
			}
		}
		for (i = length; i-- !== 0; ) {
			const key = keys[i];
			if (key === "_owner" && a.$$typeof) {
				continue;
			}
			if (!deepEqual(a[key], b[key])) {
				return false;
			}
		}
		return true;
	}
	return a !== a && b !== b;
}

function getDPR(element) {
	if (typeof window === "undefined") {
		return 1;
	}
	const win = element.ownerDocument.defaultView || window;
	return win.devicePixelRatio || 1;
}

function roundByDPR(element, value) {
	const dpr = getDPR(element);
	return Math.round(value * dpr) / dpr;
}

function useLatestRef(value) {
	const ref = React$4.useRef(value);
	index(() => {
		ref.current = value;
	});
	return ref;
}

/**
 * Provides data to position a floating element.
 * @see https://floating-ui.com/docs/useFloating
 */
function useFloating(options) {
	if (options === void 0) {
		options = {};
	}
	const {
		placement = "bottom",
		strategy = "absolute",
		middleware = [],
		platform,
		elements: { reference: externalReference, floating: externalFloating } = {},
		transform = true,
		whileElementsMounted,
		open,
	} = options;
	const [data, setData] = React$4.useState({
		x: 0,
		y: 0,
		strategy,
		placement,
		middlewareData: {},
		isPositioned: false,
	});
	const [latestMiddleware, setLatestMiddleware] = React$4.useState(middleware);
	if (!deepEqual(latestMiddleware, middleware)) {
		setLatestMiddleware(middleware);
	}
	const [_reference, _setReference] = React$4.useState(null);
	const [_floating, _setFloating] = React$4.useState(null);
	const setReference = React$4.useCallback((node) => {
		if (node !== referenceRef.current) {
			referenceRef.current = node;
			_setReference(node);
		}
	}, []);
	const setFloating = React$4.useCallback((node) => {
		if (node !== floatingRef.current) {
			floatingRef.current = node;
			_setFloating(node);
		}
	}, []);
	const referenceEl = externalReference || _reference;
	const floatingEl = externalFloating || _floating;
	const referenceRef = React$4.useRef(null);
	const floatingRef = React$4.useRef(null);
	const dataRef = React$4.useRef(data);
	const hasWhileElementsMounted = whileElementsMounted != null;
	const whileElementsMountedRef = useLatestRef(whileElementsMounted);
	const platformRef = useLatestRef(platform);
	const openRef = useLatestRef(open);
	const update = React$4.useCallback(() => {
		if (!referenceRef.current || !floatingRef.current) {
			return;
		}
		const config = {
			placement,
			strategy,
			middleware: latestMiddleware,
		};
		if (platformRef.current) {
			config.platform = platformRef.current;
		}
		computePosition(referenceRef.current, floatingRef.current, config).then((data) => {
			const fullData = {
				...data,
				// The floating element's position may be recomputed while it's closed
				// but still mounted (such as when transitioning out). To ensure
				// `isPositioned` will be `false` initially on the next open, avoid
				// setting it to `true` when `open === false` (must be specified).
				isPositioned: openRef.current !== false,
			};
			if (isMountedRef.current && !deepEqual(dataRef.current, fullData)) {
				dataRef.current = fullData;
				ReactDOM$1.flushSync(() => {
					setData(fullData);
				});
			}
		});
	}, [latestMiddleware, placement, strategy, platformRef, openRef]);
	index(() => {
		if (open === false && dataRef.current.isPositioned) {
			dataRef.current.isPositioned = false;
			setData((data) => ({
				...data,
				isPositioned: false,
			}));
		}
	}, [open]);
	const isMountedRef = React$4.useRef(false);
	index(() => {
		isMountedRef.current = true;
		return () => {
			isMountedRef.current = false;
		};
	}, []);
	index(() => {
		if (referenceEl) referenceRef.current = referenceEl;
		if (floatingEl) floatingRef.current = floatingEl;
		if (referenceEl && floatingEl) {
			if (whileElementsMountedRef.current) {
				return whileElementsMountedRef.current(referenceEl, floatingEl, update);
			}
			update();
		}
	}, [referenceEl, floatingEl, update, whileElementsMountedRef, hasWhileElementsMounted]);
	const refs = React$4.useMemo(
		() => ({
			reference: referenceRef,
			floating: floatingRef,
			setReference,
			setFloating,
		}),
		[setReference, setFloating],
	);
	const elements = React$4.useMemo(
		() => ({
			reference: referenceEl,
			floating: floatingEl,
		}),
		[referenceEl, floatingEl],
	);
	const floatingStyles = React$4.useMemo(() => {
		const initialStyles = {
			position: strategy,
			left: 0,
			top: 0,
		};
		if (!elements.floating) {
			return initialStyles;
		}
		const x = roundByDPR(elements.floating, data.x);
		const y = roundByDPR(elements.floating, data.y);
		if (transform) {
			return {
				...initialStyles,
				transform: "translate(" + x + "px, " + y + "px)",
				...(getDPR(elements.floating) >= 1.5 && {
					willChange: "transform",
				}),
			};
		}
		return {
			position: strategy,
			left: x,
			top: y,
		};
	}, [strategy, transform, elements.floating, data.x, data.y]);
	return React$4.useMemo(
		() => ({
			...data,
			update,
			refs,
			elements,
			floatingStyles,
		}),
		[data, update, refs, elements, floatingStyles],
	);
}

/**
 * Provides data to position an inner element of the floating element so that it
 * appears centered to the reference element.
 * This wraps the core `arrow` middleware to allow React refs as the element.
 * @see https://floating-ui.com/docs/arrow
 */
const arrow$1 = (options) => {
	function isRef(value) {
		return {}.hasOwnProperty.call(value, "current");
	}
	return {
		name: "arrow",
		options,
		fn(state) {
			const { element, padding } = typeof options === "function" ? options(state) : options;
			if (element && isRef(element)) {
				if (element.current != null) {
					return arrow$2({
						element: element.current,
						padding,
					}).fn(state);
				}
				return {};
			}
			if (element) {
				return arrow$2({
					element,
					padding,
				}).fn(state);
			}
			return {};
		},
	};
};

/**
 * Modifies the placement by translating the floating element along the
 * specified axes.
 * A number (shorthand for `mainAxis` or distance), or an axes configuration
 * object may be passed.
 * @see https://floating-ui.com/docs/offset
 */
const offset = (options, deps) => {
	const result = offset$1(options);
	return {
		name: result.name,
		fn: result.fn,
		options: [options, deps],
	};
};

/**
 * Optimizes the visibility of the floating element by shifting it in order to
 * keep it in view when it will overflow the clipping boundary.
 * @see https://floating-ui.com/docs/shift
 */
const shift = (options, deps) => {
	const result = shift$1(options);
	return {
		name: result.name,
		fn: result.fn,
		options: [options, deps],
	};
};

/**
 * Built-in `limiter` that will stop `shift()` at a certain point.
 */
const limitShift = (options, deps) => {
	const result = limitShift$1(options);
	return {
		fn: result.fn,
		options: [options, deps],
	};
};

/**
 * Optimizes the visibility of the floating element by flipping the `placement`
 * in order to keep it in view when the preferred placement(s) will overflow the
 * clipping boundary. Alternative to `autoPlacement`.
 * @see https://floating-ui.com/docs/flip
 */
const flip = (options, deps) => {
	const result = flip$1(options);
	return {
		name: result.name,
		fn: result.fn,
		options: [options, deps],
	};
};

/**
 * Provides data that allows you to change the size of the floating element —
 * for instance, prevent it from overflowing the clipping boundary or match the
 * width of the reference element.
 * @see https://floating-ui.com/docs/size
 */
const size = (options, deps) => {
	const result = size$1(options);
	return {
		name: result.name,
		fn: result.fn,
		options: [options, deps],
	};
};

/**
 * Optimizes the visibility of the floating element by choosing the placement
 * that has the most space available automatically, without needing to specify a
 * preferred placement. Alternative to `flip`.
 * @see https://floating-ui.com/docs/autoPlacement
 */
const autoPlacement = (options, deps) => {
	const result = autoPlacement$1(options);
	return {
		name: result.name,
		fn: result.fn,
		options: [options, deps],
	};
};

/**
 * Provides data to hide the floating element in applicable situations, such as
 * when it is not in the same clipping context as the reference element.
 * @see https://floating-ui.com/docs/hide
 */
const hide = (options, deps) => {
	const result = hide$1(options);
	return {
		name: result.name,
		fn: result.fn,
		options: [options, deps],
	};
};

/**
 * Provides data to position an inner element of the floating element so that it
 * appears centered to the reference element.
 * This wraps the core `arrow` middleware to allow React refs as the element.
 * @see https://floating-ui.com/docs/arrow
 */
const arrow = (options, deps) => {
	const result = arrow$1(options);
	return {
		name: result.name,
		fn: result.fn,
		options: [options, deps],
	};
};

// src/arrow.tsx
const React$3 = await importShared("react");
var NAME$1 = "Arrow";
var Arrow$1 = React$3.forwardRef((props, forwardedRef) => {
	const { children, width = 10, height = 5, ...arrowProps } = props;
	return /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.svg, {
		...arrowProps,
		ref: forwardedRef,
		width,
		height,
		viewBox: "0 0 30 10",
		preserveAspectRatio: "none",
		children: props.asChild ? children : /* @__PURE__ */ jsxRuntimeExports.jsx("polygon", { points: "0,0 30,0 15,10" }),
	});
});
Arrow$1.displayName = NAME$1;
var Root$1 = Arrow$1;

// src/popper.tsx
const React$2 = await importShared("react");
var POPPER_NAME = "Popper";
var [createPopperContext, createPopperScope] = createContextScope(POPPER_NAME);
var [PopperProvider, usePopperContext] = createPopperContext(POPPER_NAME);
var Popper = (props) => {
	const { __scopePopper, children } = props;
	const [anchor, setAnchor] = React$2.useState(null);
	const [placementState, setPlacementState] = React$2.useState(void 0);
	return /* @__PURE__ */ jsxRuntimeExports.jsx(PopperProvider, {
		scope: __scopePopper,
		anchor,
		onAnchorChange: setAnchor,
		placementState,
		setPlacementState,
		children,
	});
};
Popper.displayName = POPPER_NAME;
var ANCHOR_NAME = "PopperAnchor";
var PopperAnchor = React$2.forwardRef((props, forwardedRef) => {
	const { __scopePopper, virtualRef, ...anchorProps } = props;
	const context = usePopperContext(ANCHOR_NAME, __scopePopper);
	const ref = React$2.useRef(null);
	const onAnchorChange = context.onAnchorChange;
	const callbackRef = React$2.useCallback(
		(node) => {
			ref.current = node;
			if (node) {
				onAnchorChange(node);
			}
		},
		[onAnchorChange],
	);
	const composedRefs = useComposedRefs(forwardedRef, callbackRef);
	const anchorRef = React$2.useRef(null);
	React$2.useEffect(() => {
		if (!virtualRef) {
			return;
		}
		const previousAnchor = anchorRef.current;
		anchorRef.current = virtualRef.current;
		if (previousAnchor !== anchorRef.current) {
			onAnchorChange(anchorRef.current);
		}
	});
	const sideAndAlign = context.placementState && getSideAndAlignFromPlacement(context.placementState);
	const placedSide = sideAndAlign?.[0];
	const placedAlign = sideAndAlign?.[1];
	return virtualRef ? null : (
			/* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.div, {
				"data-radix-popper-side": placedSide,
				"data-radix-popper-align": placedAlign,
				...anchorProps,
				"ref": composedRefs,
			})
		);
});
PopperAnchor.displayName = ANCHOR_NAME;
var CONTENT_NAME$1 = "PopperContent";
var [PopperContentProvider, useContentContext] = createPopperContext(CONTENT_NAME$1);
var PopperContent = React$2.forwardRef((props, forwardedRef) => {
	const {
		__scopePopper,
		side = "bottom",
		sideOffset = 0,
		align = "center",
		alignOffset = 0,
		arrowPadding = 0,
		avoidCollisions = true,
		collisionBoundary = [],
		collisionPadding: collisionPaddingProp = 0,
		sticky = "partial",
		hideWhenDetached = false,
		updatePositionStrategy = "optimized",
		onPlaced,
		...contentProps
	} = props;
	const context = usePopperContext(CONTENT_NAME$1, __scopePopper);
	const [content, setContent] = React$2.useState(null);
	const composedRefs = useComposedRefs(forwardedRef, setContent);
	const [arrow$1, setArrow] = React$2.useState(null);
	const arrowSize = useSize(arrow$1);
	const arrowWidth = arrowSize?.width ?? 0;
	const arrowHeight = arrowSize?.height ?? 0;
	const desiredPlacement = side + (align !== "center" ? "-" + align : "");
	const collisionPadding = typeof collisionPaddingProp === "number" ? collisionPaddingProp : { top: 0, right: 0, bottom: 0, left: 0, ...collisionPaddingProp };
	const boundary = Array.isArray(collisionBoundary) ? collisionBoundary : [collisionBoundary];
	const hasExplicitBoundaries = boundary.length > 0;
	const detectOverflowOptions = {
		padding: collisionPadding,
		boundary: boundary.filter(isNotNull),
		// with `strategy: 'fixed'`, this is the only way to get it to respect boundaries
		altBoundary: hasExplicitBoundaries,
	};
	const { refs, floatingStyles, placement, isPositioned, middlewareData } = useFloating({
		// default to `fixed` strategy so users don't have to pick and we also avoid focus scroll issues
		strategy: "fixed",
		placement: desiredPlacement,
		whileElementsMounted: (...args) => {
			const cleanup = autoUpdate(...args, {
				animationFrame: updatePositionStrategy === "always",
			});
			return cleanup;
		},
		elements: {
			reference: context.anchor,
		},
		middleware: [
			offset({ mainAxis: sideOffset + arrowHeight, alignmentAxis: alignOffset }),
			avoidCollisions &&
				shift({
					mainAxis: true,
					crossAxis: false,
					limiter: sticky === "partial" ? limitShift() : void 0,
					...detectOverflowOptions,
				}),
			avoidCollisions && flip({ ...detectOverflowOptions }),
			size({
				...detectOverflowOptions,
				apply: ({ elements, rects, availableWidth, availableHeight }) => {
					const { width: anchorWidth, height: anchorHeight } = rects.reference;
					const contentStyle = elements.floating.style;
					contentStyle.setProperty("--radix-popper-available-width", `${availableWidth}px`);
					contentStyle.setProperty("--radix-popper-available-height", `${availableHeight}px`);
					contentStyle.setProperty("--radix-popper-anchor-width", `${anchorWidth}px`);
					contentStyle.setProperty("--radix-popper-anchor-height", `${anchorHeight}px`);
				},
			}),
			arrow$1 && arrow({ element: arrow$1, padding: arrowPadding }),
			transformOrigin({ arrowWidth, arrowHeight }),
			hideWhenDetached &&
				hide({
					strategy: "referenceHidden",
					...detectOverflowOptions,
					// `hide` detects whether the anchor (reference) is clipped, so when
					// no explicit `collisionBoundary` is set we fall back to Floating
					// UI's default clipping ancestors (e.g. a scrollable menu). This
					// lets an occluded submenu hide once its anchor scrolls out of view
					// (#3237). The collision/size middlewares deliberately keep the
					// viewport-based default to avoid clamping content rendered inside
					// transformed or overflow-clipping portal containers.
					boundary: hasExplicitBoundaries ? detectOverflowOptions.boundary : void 0,
				}),
		],
	});
	const setPlacementState = context.setPlacementState;
	useLayoutEffect2(() => {
		setPlacementState(placement);
		return () => {
			setPlacementState(void 0);
		};
	}, [placement, setPlacementState]);
	const [placedSide, placedAlign] = getSideAndAlignFromPlacement(placement);
	const handlePlaced = useCallbackRef$1(onPlaced);
	useLayoutEffect2(() => {
		if (isPositioned) {
			handlePlaced?.();
		}
	}, [isPositioned, handlePlaced]);
	const arrowX = middlewareData.arrow?.x;
	const arrowY = middlewareData.arrow?.y;
	const cannotCenterArrow = middlewareData.arrow?.centerOffset !== 0;
	const [contentZIndex, setContentZIndex] = React$2.useState();
	useLayoutEffect2(() => {
		if (content) setContentZIndex(window.getComputedStyle(content).zIndex);
	}, [content]);
	return /* @__PURE__ */ jsxRuntimeExports.jsx("div", {
		"ref": refs.setFloating,
		"data-radix-popper-content-wrapper": "",
		"style": {
			...floatingStyles,
			"transform": isPositioned ? floatingStyles.transform : "translate(0, -200%)",
			// keep off the page when measuring
			"minWidth": "max-content",
			"zIndex": contentZIndex,
			"--radix-popper-transform-origin": [middlewareData.transformOrigin?.x, middlewareData.transformOrigin?.y].join(" "),
			// hide the content if using the hide middleware and should be hidden
			// set visibility to hidden and disable pointer events so the UI behaves
			// as if the PopperContent isn't there at all
			...(middlewareData.hide?.referenceHidden && {
				visibility: "hidden",
				pointerEvents: "none",
			}),
		},
		"dir": props.dir,
		"children": /* @__PURE__ */ jsxRuntimeExports.jsx(PopperContentProvider, {
			scope: __scopePopper,
			placedSide,
			placedAlign,
			onArrowChange: setArrow,
			arrowX,
			arrowY,
			shouldHideArrow: cannotCenterArrow,
			children: /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.div, {
				"data-side": placedSide,
				"data-align": placedAlign,
				...contentProps,
				"ref": composedRefs,
				"style": {
					...contentProps.style,
					// if the PopperContent hasn't been placed yet (not all measurements done)
					// we prevent animations so that users's animation don't kick in too early referring wrong sides
					animation: !isPositioned ? "none" : void 0,
				},
			}),
		}),
	});
});
PopperContent.displayName = CONTENT_NAME$1;
var ARROW_NAME$1 = "PopperArrow";
var OPPOSITE_SIDE = {
	top: "bottom",
	right: "left",
	bottom: "top",
	left: "right",
};
var PopperArrow = React$2.forwardRef(function PopperArrow2(props, forwardedRef) {
	const { __scopePopper, ...arrowProps } = props;
	const contentContext = useContentContext(ARROW_NAME$1, __scopePopper);
	const baseSide = OPPOSITE_SIDE[contentContext.placedSide];
	return (
		// we have to use an extra wrapper because `ResizeObserver` (used by `useSize`)
		// doesn't report size as we'd expect on SVG elements.
		// it reports their bounding box which is effectively the largest path inside the SVG.
		/* @__PURE__ */ jsxRuntimeExports.jsx("span", {
			ref: contentContext.onArrowChange,
			style: {
				position: "absolute",
				left: contentContext.arrowX,
				top: contentContext.arrowY,
				[baseSide]: 0,
				transformOrigin: {
					top: "",
					right: "0 0",
					bottom: "center 0",
					left: "100% 0",
				}[contentContext.placedSide],
				transform: {
					top: "translateY(100%)",
					right: "translateY(50%) rotate(90deg) translateX(-50%)",
					bottom: `rotate(180deg)`,
					left: "translateY(50%) rotate(-90deg) translateX(50%)",
				}[contentContext.placedSide],
				visibility: contentContext.shouldHideArrow ? "hidden" : void 0,
			},
			children: /* @__PURE__ */ jsxRuntimeExports.jsx(Root$1, {
				...arrowProps,
				ref: forwardedRef,
				style: {
					...arrowProps.style,
					// ensures the element can be measured correctly (mostly for if SVG)
					display: "block",
				},
			}),
		})
	);
});
PopperArrow.displayName = ARROW_NAME$1;
function isNotNull(value) {
	return value !== null;
}
var transformOrigin = (options) => ({
	name: "transformOrigin",
	options,
	fn(data) {
		const { placement, rects, middlewareData } = data;
		const cannotCenterArrow = middlewareData.arrow?.centerOffset !== 0;
		const isArrowHidden = cannotCenterArrow;
		const arrowWidth = isArrowHidden ? 0 : options.arrowWidth;
		const arrowHeight = isArrowHidden ? 0 : options.arrowHeight;
		const [placedSide, placedAlign] = getSideAndAlignFromPlacement(placement);
		const noArrowAlign = { start: "0%", center: "50%", end: "100%" }[placedAlign];
		const arrowXCenter = (middlewareData.arrow?.x ?? 0) + arrowWidth / 2;
		const arrowYCenter = (middlewareData.arrow?.y ?? 0) + arrowHeight / 2;
		let x = "";
		let y = "";
		if (placedSide === "bottom") {
			x = isArrowHidden ? noArrowAlign : `${arrowXCenter}px`;
			y = `${-arrowHeight}px`;
		} else if (placedSide === "top") {
			x = isArrowHidden ? noArrowAlign : `${arrowXCenter}px`;
			y = `${rects.floating.height + arrowHeight}px`;
		} else if (placedSide === "right") {
			x = `${-arrowHeight}px`;
			y = isArrowHidden ? noArrowAlign : `${arrowYCenter}px`;
		} else if (placedSide === "left") {
			x = `${rects.floating.width + arrowHeight}px`;
			y = isArrowHidden ? noArrowAlign : `${arrowYCenter}px`;
		}
		return { data: { x, y } };
	},
});
function getSideAndAlignFromPlacement(placement) {
	const [side, align = "center"] = placement.split("-");
	return [side, align];
}
var Root2 = Popper;
var Anchor = PopperAnchor;
var Content = PopperContent;
var Arrow = PopperArrow;

// src/visually-hidden.tsx
const React$1 = await importShared("react");
var VISUALLY_HIDDEN_STYLES = Object.freeze({
	// See: https://github.com/twbs/bootstrap/blob/main/scss/mixins/_visually-hidden.scss
	position: "absolute",
	border: 0,
	width: 1,
	height: 1,
	padding: 0,
	margin: -1,
	overflow: "hidden",
	clip: "rect(0, 0, 0, 0)",
	whiteSpace: "nowrap",
	wordWrap: "normal",
});
var NAME = "VisuallyHidden";
var VisuallyHidden = React$1.forwardRef((props, forwardedRef) => {
	return /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.span, {
		...props,
		ref: forwardedRef,
		style: { ...VISUALLY_HIDDEN_STYLES, ...props.style },
	});
});
VisuallyHidden.displayName = NAME;
var Root = VisuallyHidden;

// src/select.tsx
const React = await importShared("react");

const ReactDOM = await importShared("react-dom");
var OPEN_KEYS = [" ", "Enter", "ArrowUp", "ArrowDown"];
var SELECTION_KEYS = [" ", "Enter"];
var SELECT_NAME = "Select";
var [Collection, useCollection, createCollectionScope] = createCollection(SELECT_NAME);
var [createSelectContext] = createContextScope(SELECT_NAME, [createCollectionScope, createPopperScope]);
var usePopperScope = createPopperScope();
var [SelectProviderImpl, useSelectContext] = createSelectContext(SELECT_NAME);
var [SelectNativeOptionsProvider, useSelectNativeOptionsContext] = createSelectContext(SELECT_NAME);
var PROVIDER_NAME = "SelectProvider";
function SelectProvider(props) {
	const {
		__scopeSelect,
		children,
		open: openProp,
		defaultOpen,
		onOpenChange,
		value: valueProp,
		defaultValue,
		onValueChange,
		dir,
		name,
		autoComplete,
		disabled,
		required,
		form,
		// @ts-expect-error internal render prop used by `Select` to compose its default parts
		internal_do_not_use_render,
	} = props;
	const popperScope = usePopperScope(__scopeSelect);
	const [trigger, setTrigger] = React.useState(null);
	const [valueNode, setValueNode] = React.useState(null);
	const [valueNodeHasChildren, setValueNodeHasChildren] = React.useState(false);
	const direction = useDirection(dir);
	const [open, setOpen] = useControllableState({
		prop: openProp,
		defaultProp: defaultOpen ?? false,
		onChange: onOpenChange,
		caller: SELECT_NAME,
	});
	const [value, setValue] = useControllableState({
		prop: valueProp,
		defaultProp: defaultValue,
		onChange: onValueChange,
		caller: SELECT_NAME,
	});
	const triggerPointerDownPosRef = React.useRef(null);
	const isFormControl = trigger ? !!form || !!trigger.closest("form") : true;
	const [nativeOptionsSet, setNativeOptionsSet] = React.useState(/* @__PURE__ */ new Set());
	const contentId = useId();
	const nativeSelectKey = Array.from(nativeOptionsSet)
		.map((option) => option.props.value)
		.join(";");
	const handleNativeOptionAdd = React.useCallback((option) => {
		setNativeOptionsSet((prev) => new Set(prev).add(option));
	}, []);
	const handleNativeOptionRemove = React.useCallback((option) => {
		setNativeOptionsSet((prev) => {
			const optionsSet = new Set(prev);
			optionsSet.delete(option);
			return optionsSet;
		});
	}, []);
	const context = {
		required,
		trigger,
		onTriggerChange: setTrigger,
		valueNode,
		onValueNodeChange: setValueNode,
		valueNodeHasChildren,
		onValueNodeHasChildrenChange: setValueNodeHasChildren,
		contentId,
		value,
		onValueChange: setValue,
		open,
		onOpenChange: setOpen,
		dir: direction,
		triggerPointerDownPosRef,
		disabled,
		name,
		autoComplete,
		form,
		nativeOptions: nativeOptionsSet,
		nativeSelectKey,
		isFormControl,
	};
	return /* @__PURE__ */ jsxRuntimeExports.jsx(Root2, {
		...popperScope,
		children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectProviderImpl, {
			scope: __scopeSelect,
			...context,
			children: /* @__PURE__ */ jsxRuntimeExports.jsx(Collection.Provider, {
				scope: __scopeSelect,
				children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectNativeOptionsProvider, {
					scope: __scopeSelect,
					onNativeOptionAdd: handleNativeOptionAdd,
					onNativeOptionRemove: handleNativeOptionRemove,
					children: isFunction(internal_do_not_use_render) ? internal_do_not_use_render(context) : children,
				}),
			}),
		}),
	});
}
SelectProvider.displayName = PROVIDER_NAME;
var Select = (props) => {
	const { __scopeSelect, children, ...providerProps } = props;
	return /* @__PURE__ */ jsxRuntimeExports.jsx(SelectProvider, {
		__scopeSelect,
		...providerProps,
		internal_do_not_use_render: ({ isFormControl }) =>
			/* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, {
				children: [
					children,
					isFormControl ?
						/* @__PURE__ */ jsxRuntimeExports.jsx(SelectBubbleInput, {
							__scopeSelect,
						})
					:	null,
				],
			}),
	});
};
Select.displayName = SELECT_NAME;
var TRIGGER_NAME = "SelectTrigger";
var SelectTrigger = React.forwardRef((props, forwardedRef) => {
	const { __scopeSelect, disabled = false, ...triggerProps } = props;
	const popperScope = usePopperScope(__scopeSelect);
	const context = useSelectContext(TRIGGER_NAME, __scopeSelect);
	const isDisabled = context.disabled || disabled;
	const composedRefs = useComposedRefs(forwardedRef, context.onTriggerChange);
	const getItems = useCollection(__scopeSelect);
	const pointerTypeRef = React.useRef("touch");
	const [searchRef, handleTypeaheadSearch, resetTypeahead] = useTypeaheadSearch((search) => {
		const enabledItems = getItems().filter((item) => !item.disabled);
		const currentItem = enabledItems.find((item) => item.value === context.value);
		const nextItem = findNextItem(enabledItems, search, currentItem);
		if (nextItem !== void 0) {
			context.onValueChange(nextItem.value);
		}
	});
	const handleOpen = (pointerEvent) => {
		if (!isDisabled) {
			context.onOpenChange(true);
			resetTypeahead();
		}
		if (pointerEvent) {
			context.triggerPointerDownPosRef.current = {
				x: Math.round(pointerEvent.pageX),
				y: Math.round(pointerEvent.pageY),
			};
		}
	};
	return /* @__PURE__ */ jsxRuntimeExports.jsx(Anchor, {
		asChild: true,
		...popperScope,
		children: /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.button, {
			"type": "button",
			"role": "combobox",
			"aria-controls": context.open ? context.contentId : void 0,
			"aria-expanded": context.open,
			"aria-required": context.required,
			"aria-autocomplete": "none",
			"dir": context.dir,
			"data-state": context.open ? "open" : "closed",
			"disabled": isDisabled,
			"data-disabled": isDisabled ? "" : void 0,
			"data-placeholder": shouldShowPlaceholder(context.value) ? "" : void 0,
			...triggerProps,
			"ref": composedRefs,
			"onClick": composeEventHandlers(triggerProps.onClick, (event) => {
				event.currentTarget.focus();
				if (pointerTypeRef.current !== "mouse") {
					handleOpen(event);
				}
			}),
			"onPointerDown": composeEventHandlers(triggerProps.onPointerDown, (event) => {
				pointerTypeRef.current = event.pointerType;
				const target = event.target;
				if (target.hasPointerCapture(event.pointerId)) {
					target.releasePointerCapture(event.pointerId);
				}
				if (event.button === 0 && event.ctrlKey === false && event.pointerType === "mouse") {
					handleOpen(event);
					event.preventDefault();
				}
			}),
			"onKeyDown": composeEventHandlers(triggerProps.onKeyDown, (event) => {
				const isTypingAhead = searchRef.current !== "";
				const isModifierKey = event.ctrlKey || event.altKey || event.metaKey;
				if (!isModifierKey && event.key.length === 1) handleTypeaheadSearch(event.key);
				if (isTypingAhead && event.key === " ") return;
				if (OPEN_KEYS.includes(event.key)) {
					handleOpen();
					event.preventDefault();
				}
			}),
		}),
	});
});
SelectTrigger.displayName = TRIGGER_NAME;
var VALUE_NAME = "SelectValue";
var SelectValue = React.forwardRef((props, forwardedRef) => {
	const { __scopeSelect, className, style, children, placeholder = "", ...valueProps } = props;
	const context = useSelectContext(VALUE_NAME, __scopeSelect);
	const { onValueNodeHasChildrenChange } = context;
	const hasChildren = children !== void 0;
	const composedRefs = useComposedRefs(forwardedRef, context.onValueNodeChange);
	useLayoutEffect2(() => {
		onValueNodeHasChildrenChange(hasChildren);
	}, [onValueNodeHasChildrenChange, hasChildren]);
	const showPlaceholder = shouldShowPlaceholder(context.value);
	return /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.span, {
		...valueProps,
		asChild: showPlaceholder ? false : valueProps.asChild,
		ref: composedRefs,
		style: { pointerEvents: "none" },
		children: /* @__PURE__ */ jsxRuntimeExports.jsx(React.Fragment, { children: showPlaceholder ? placeholder : children }, showPlaceholder ? "placeholder" : "value"),
	});
});
SelectValue.displayName = VALUE_NAME;
var ICON_NAME = "SelectIcon";
var SelectIcon = React.forwardRef((props, forwardedRef) => {
	const { __scopeSelect, children, ...iconProps } = props;
	return /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.span, { "aria-hidden": true, ...iconProps, "ref": forwardedRef, "children": children || "\u25BC" });
});
SelectIcon.displayName = ICON_NAME;
var PORTAL_NAME = "SelectPortal";
var [PortalProvider, usePortalContext] = createSelectContext(PORTAL_NAME, {
	forceMount: void 0,
});
var SelectPortal = (props) => {
	const { __scopeSelect, forceMount, ...portalProps } = props;
	return /* @__PURE__ */ jsxRuntimeExports.jsx(PortalProvider, {
		scope: props.__scopeSelect,
		forceMount,
		children: /* @__PURE__ */ jsxRuntimeExports.jsx(Portal, { asChild: true, ...portalProps }),
	});
};
SelectPortal.displayName = PORTAL_NAME;
var CONTENT_NAME = "SelectContent";
var SelectContent = React.forwardRef((props, forwardedRef) => {
	const portalContext = usePortalContext(CONTENT_NAME, props.__scopeSelect);
	const { forceMount = portalContext.forceMount, ...contentProps } = props;
	const context = useSelectContext(CONTENT_NAME, props.__scopeSelect);
	const [fragment, setFragment] = React.useState();
	useLayoutEffect2(() => {
		setFragment(new DocumentFragment());
	}, []);
	return /* @__PURE__ */ jsxRuntimeExports.jsx(Presence, {
		present: forceMount || context.open,
		children: ({ present }) =>
			present ?
				/* @__PURE__ */ jsxRuntimeExports.jsx(SelectContentImpl, { ...contentProps, ref: forwardedRef })
			:	/* @__PURE__ */ jsxRuntimeExports.jsx(SelectContentFragment, { ...contentProps, fragment }),
	});
});
SelectContent.displayName = CONTENT_NAME;
var SelectContentFragment = React.forwardRef((props, forwardedRef) => {
	const { __scopeSelect, children, fragment } = props;
	if (!fragment) return null;
	return ReactDOM.createPortal(
		/* @__PURE__ */ jsxRuntimeExports.jsx(SelectContentProvider, {
			scope: __scopeSelect,
			children: /* @__PURE__ */ jsxRuntimeExports.jsx(Collection.Slot, { scope: __scopeSelect, children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref: forwardedRef, children }) }),
		}),
		fragment,
	);
});
SelectContentFragment.displayName = "SelectContentFragment";
var CONTENT_MARGIN = 10;
var [SelectContentProvider, useSelectContentContext] = createSelectContext(CONTENT_NAME);
var CONTENT_IMPL_NAME = "SelectContentImpl";
var Slot = createSlot("SelectContent.RemoveScroll");
var SelectContentImpl = React.forwardRef((props, forwardedRef) => {
	const { __scopeSelect } = props;
	const {
		position = "item-aligned",
		onCloseAutoFocus,
		onEscapeKeyDown,
		onPointerDownOutside,
		//
		// PopperContent props
		side,
		sideOffset,
		align,
		alignOffset,
		arrowPadding,
		collisionBoundary,
		collisionPadding,
		sticky,
		hideWhenDetached,
		avoidCollisions,
		//
		...contentProps
	} = props;
	const context = useSelectContext(CONTENT_NAME, __scopeSelect);
	const [content, setContent] = React.useState(null);
	const [viewport, setViewport] = React.useState(null);
	const composedRefs = useComposedRefs(forwardedRef, setContent);
	const [selectedItem, setSelectedItem] = React.useState(null);
	const [selectedItemText, setSelectedItemText] = React.useState(null);
	const getItems = useCollection(__scopeSelect);
	const [isPositioned, setIsPositioned] = React.useState(false);
	const firstValidItemFoundRef = React.useRef(false);
	React.useEffect(() => {
		if (content) return hideOthers(content);
	}, [content]);
	useFocusGuards();
	const focusFirst = React.useCallback(
		(candidates) => {
			const [firstItem, ...restItems] = getItems().map((item) => item.ref.current);
			const [lastItem] = restItems.slice(-1);
			const PREVIOUSLY_FOCUSED_ELEMENT = document.activeElement;
			for (const candidate of candidates) {
				if (candidate === PREVIOUSLY_FOCUSED_ELEMENT) return;
				candidate?.scrollIntoView({ block: "nearest" });
				if (candidate === firstItem && viewport) viewport.scrollTop = 0;
				if (candidate === lastItem && viewport) viewport.scrollTop = viewport.scrollHeight;
				candidate?.focus();
				if (document.activeElement !== PREVIOUSLY_FOCUSED_ELEMENT) return;
			}
		},
		[getItems, viewport],
	);
	const focusSelectedItem = React.useCallback(() => focusFirst([selectedItem, content]), [focusFirst, selectedItem, content]);
	React.useEffect(() => {
		if (isPositioned) {
			focusSelectedItem();
		}
	}, [isPositioned, focusSelectedItem]);
	const { onOpenChange, triggerPointerDownPosRef } = context;
	React.useEffect(() => {
		if (content) {
			let pointerMoveDelta = { x: 0, y: 0 };
			const handlePointerMove = (event) => {
				pointerMoveDelta = {
					x: Math.abs(Math.round(event.pageX) - (triggerPointerDownPosRef.current?.x ?? 0)),
					y: Math.abs(Math.round(event.pageY) - (triggerPointerDownPosRef.current?.y ?? 0)),
				};
			};
			const handlePointerUp = (event) => {
				if (pointerMoveDelta.x <= 10 && pointerMoveDelta.y <= 10) {
					event.preventDefault();
				} else {
					if (!event.composedPath().includes(content)) {
						onOpenChange(false);
					}
				}
				document.removeEventListener("pointermove", handlePointerMove);
				triggerPointerDownPosRef.current = null;
			};
			if (triggerPointerDownPosRef.current !== null) {
				document.addEventListener("pointermove", handlePointerMove);
				document.addEventListener("pointerup", handlePointerUp, { capture: true, once: true });
			}
			return () => {
				document.removeEventListener("pointermove", handlePointerMove);
				document.removeEventListener("pointerup", handlePointerUp, { capture: true });
			};
		}
	}, [content, onOpenChange, triggerPointerDownPosRef]);
	React.useEffect(() => {
		const close = () => onOpenChange(false);
		window.addEventListener("blur", close);
		window.addEventListener("resize", close);
		return () => {
			window.removeEventListener("blur", close);
			window.removeEventListener("resize", close);
		};
	}, [onOpenChange]);
	const [searchRef, handleTypeaheadSearch] = useTypeaheadSearch((search) => {
		const enabledItems = getItems().filter((item) => !item.disabled);
		const currentItem = enabledItems.find((item) => item.ref.current === document.activeElement);
		const nextItem = findNextItem(enabledItems, search, currentItem);
		if (nextItem) {
			setTimeout(() => nextItem.ref.current?.focus());
		}
	});
	const itemRefCallback = React.useCallback(
		(node, value, disabled) => {
			const isFirstValidItem = !firstValidItemFoundRef.current && !disabled;
			const isSelectedItem = context.value !== void 0 && context.value === value;
			if (isSelectedItem || isFirstValidItem) {
				setSelectedItem(node);
				if (isFirstValidItem) firstValidItemFoundRef.current = true;
			}
		},
		[context.value],
	);
	const handleItemLeave = React.useCallback(() => content?.focus(), [content]);
	const itemTextRefCallback = React.useCallback(
		(node, value, disabled) => {
			const isFirstValidItem = !firstValidItemFoundRef.current && !disabled;
			const isSelectedItem = context.value !== void 0 && context.value === value;
			if (isSelectedItem || isFirstValidItem) {
				setSelectedItemText(node);
			}
		},
		[context.value],
	);
	const SelectPosition = position === "popper" ? SelectPopperPosition : SelectItemAlignedPosition;
	const popperContentProps =
		SelectPosition === SelectPopperPosition ?
			{
				side,
				sideOffset,
				align,
				alignOffset,
				arrowPadding,
				collisionBoundary,
				collisionPadding,
				sticky,
				hideWhenDetached,
				avoidCollisions,
			}
		:	{};
	return /* @__PURE__ */ jsxRuntimeExports.jsx(SelectContentProvider, {
		scope: __scopeSelect,
		content,
		viewport,
		onViewportChange: setViewport,
		itemRefCallback,
		selectedItem,
		onItemLeave: handleItemLeave,
		itemTextRefCallback,
		focusSelectedItem,
		selectedItemText,
		position,
		isPositioned,
		searchRef,
		children: /* @__PURE__ */ jsxRuntimeExports.jsx(ReactRemoveScroll, {
			as: Slot,
			allowPinchZoom: true,
			children: /* @__PURE__ */ jsxRuntimeExports.jsx(FocusScope, {
				asChild: true,
				trapped: context.open,
				onMountAutoFocus: (event) => {
					event.preventDefault();
				},
				onUnmountAutoFocus: composeEventHandlers(onCloseAutoFocus, (event) => {
					context.trigger?.focus({ preventScroll: true });
					event.preventDefault();
				}),
				children: /* @__PURE__ */ jsxRuntimeExports.jsx(DismissableLayer, {
					asChild: true,
					disableOutsidePointerEvents: true,
					onEscapeKeyDown,
					onPointerDownOutside,
					onFocusOutside: (event) => event.preventDefault(),
					onDismiss: () => context.onOpenChange(false),
					children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectPosition, {
						"role": "listbox",
						"id": context.contentId,
						"data-state": context.open ? "open" : "closed",
						"dir": context.dir,
						"onContextMenu": (event) => event.preventDefault(),
						...contentProps,
						...popperContentProps,
						"onPlaced": () => setIsPositioned(true),
						"ref": composedRefs,
						"style": {
							// flex layout so we can place the scroll buttons properly
							display: "flex",
							flexDirection: "column",
							// reset the outline by default as the content MAY get focused
							outline: "none",
							...contentProps.style,
						},
						"onKeyDown": composeEventHandlers(contentProps.onKeyDown, (event) => {
							const isModifierKey = event.ctrlKey || event.altKey || event.metaKey;
							if (event.key === "Tab") event.preventDefault();
							if (!isModifierKey && event.key.length === 1) handleTypeaheadSearch(event.key);
							if (["ArrowUp", "ArrowDown", "Home", "End"].includes(event.key)) {
								const items = getItems().filter((item) => !item.disabled);
								let candidateNodes = items.map((item) => item.ref.current);
								if (["ArrowUp", "End"].includes(event.key)) {
									candidateNodes = candidateNodes.slice().reverse();
								}
								if (["ArrowUp", "ArrowDown"].includes(event.key)) {
									const currentElement = event.target;
									const currentIndex = candidateNodes.indexOf(currentElement);
									candidateNodes = candidateNodes.slice(currentIndex + 1);
								}
								setTimeout(() => focusFirst(candidateNodes));
								event.preventDefault();
							}
						}),
					}),
				}),
			}),
		}),
	});
});
SelectContentImpl.displayName = CONTENT_IMPL_NAME;
var ITEM_ALIGNED_POSITION_NAME = "SelectItemAlignedPosition";
var SelectItemAlignedPosition = React.forwardRef((props, forwardedRef) => {
	const { __scopeSelect, onPlaced, ...popperProps } = props;
	const context = useSelectContext(CONTENT_NAME, __scopeSelect);
	const contentContext = useSelectContentContext(CONTENT_NAME, __scopeSelect);
	const [contentWrapper, setContentWrapper] = React.useState(null);
	const [content, setContent] = React.useState(null);
	const composedRefs = useComposedRefs(forwardedRef, setContent);
	const getItems = useCollection(__scopeSelect);
	const shouldExpandOnScrollRef = React.useRef(false);
	const shouldRepositionRef = React.useRef(true);
	const { viewport, selectedItem, selectedItemText, focusSelectedItem } = contentContext;
	const position = React.useCallback(() => {
		if (context.trigger && context.valueNode && contentWrapper && content && viewport && selectedItem && selectedItemText) {
			const triggerRect = context.trigger.getBoundingClientRect();
			const contentRect = content.getBoundingClientRect();
			const valueNodeRect = context.valueNode.getBoundingClientRect();
			const itemTextRect = selectedItemText.getBoundingClientRect();
			if (context.dir !== "rtl") {
				const itemTextOffset = itemTextRect.left - contentRect.left;
				const left = valueNodeRect.left - itemTextOffset;
				const leftDelta = triggerRect.left - left;
				const minContentWidth = triggerRect.width + leftDelta;
				const contentWidth = Math.max(minContentWidth, contentRect.width);
				const rightEdge = window.innerWidth - CONTENT_MARGIN;
				const clampedLeft = clamp$1(left, [
					CONTENT_MARGIN,
					// Prevents the content from going off the starting edge of the
					// viewport. It may still go off the ending edge, but this can be
					// controlled by the user since they may want to manage overflow in a
					// specific way.
					// https://github.com/radix-ui/primitives/issues/2049
					Math.max(CONTENT_MARGIN, rightEdge - contentWidth),
				]);
				contentWrapper.style.minWidth = minContentWidth + "px";
				contentWrapper.style.left = clampedLeft + "px";
			} else {
				const itemTextOffset = contentRect.right - itemTextRect.right;
				const right = window.innerWidth - valueNodeRect.right - itemTextOffset;
				const rightDelta = window.innerWidth - triggerRect.right - right;
				const minContentWidth = triggerRect.width + rightDelta;
				const contentWidth = Math.max(minContentWidth, contentRect.width);
				const leftEdge = window.innerWidth - CONTENT_MARGIN;
				const clampedRight = clamp$1(right, [CONTENT_MARGIN, Math.max(CONTENT_MARGIN, leftEdge - contentWidth)]);
				contentWrapper.style.minWidth = minContentWidth + "px";
				contentWrapper.style.right = clampedRight + "px";
			}
			const items = getItems();
			const availableHeight = window.innerHeight - CONTENT_MARGIN * 2;
			const itemsHeight = viewport.scrollHeight;
			const contentStyles = window.getComputedStyle(content);
			const contentBorderTopWidth = parseInt(contentStyles.borderTopWidth, 10);
			const contentPaddingTop = parseInt(contentStyles.paddingTop, 10);
			const contentBorderBottomWidth = parseInt(contentStyles.borderBottomWidth, 10);
			const contentPaddingBottom = parseInt(contentStyles.paddingBottom, 10);
			const fullContentHeight = contentBorderTopWidth + contentPaddingTop + itemsHeight + contentPaddingBottom + contentBorderBottomWidth;
			const minContentHeight = Math.min(selectedItem.offsetHeight * 5, fullContentHeight);
			const viewportStyles = window.getComputedStyle(viewport);
			const viewportPaddingTop = parseInt(viewportStyles.paddingTop, 10);
			const viewportPaddingBottom = parseInt(viewportStyles.paddingBottom, 10);
			const topEdgeToTriggerMiddle = triggerRect.top + triggerRect.height / 2 - CONTENT_MARGIN;
			const triggerMiddleToBottomEdge = availableHeight - topEdgeToTriggerMiddle;
			const selectedItemHalfHeight = selectedItem.offsetHeight / 2;
			const itemOffsetMiddle = selectedItem.offsetTop + selectedItemHalfHeight;
			const contentTopToItemMiddle = contentBorderTopWidth + contentPaddingTop + itemOffsetMiddle;
			const itemMiddleToContentBottom = fullContentHeight - contentTopToItemMiddle;
			const willAlignWithoutTopOverflow = contentTopToItemMiddle <= topEdgeToTriggerMiddle;
			if (willAlignWithoutTopOverflow) {
				const isLastItem = items.length > 0 && selectedItem === items[items.length - 1].ref.current;
				contentWrapper.style.bottom = "0px";
				const viewportOffsetBottom = content.clientHeight - viewport.offsetTop - viewport.offsetHeight;
				const clampedTriggerMiddleToBottomEdge = Math.max(
					triggerMiddleToBottomEdge,
					selectedItemHalfHeight + // viewport might have padding bottom, include it to avoid a scrollable viewport
						(isLastItem ? viewportPaddingBottom : 0) +
						viewportOffsetBottom +
						contentBorderBottomWidth,
				);
				const height = contentTopToItemMiddle + clampedTriggerMiddleToBottomEdge;
				contentWrapper.style.height = height + "px";
			} else {
				const isFirstItem = items.length > 0 && selectedItem === items[0].ref.current;
				contentWrapper.style.top = "0px";
				const clampedTopEdgeToTriggerMiddle = Math.max(
					topEdgeToTriggerMiddle,
					contentBorderTopWidth +
						viewport.offsetTop + // viewport might have padding top, include it to avoid a scrollable viewport
						(isFirstItem ? viewportPaddingTop : 0) +
						selectedItemHalfHeight,
				);
				const height = clampedTopEdgeToTriggerMiddle + itemMiddleToContentBottom;
				contentWrapper.style.height = height + "px";
				viewport.scrollTop = contentTopToItemMiddle - topEdgeToTriggerMiddle + viewport.offsetTop;
			}
			contentWrapper.style.margin = `${CONTENT_MARGIN}px 0`;
			contentWrapper.style.minHeight = minContentHeight + "px";
			contentWrapper.style.maxHeight = availableHeight + "px";
			onPlaced?.();
			requestAnimationFrame(() => (shouldExpandOnScrollRef.current = true));
		}
	}, [getItems, context.trigger, context.valueNode, contentWrapper, content, viewport, selectedItem, selectedItemText, context.dir, onPlaced]);
	useLayoutEffect2(() => position(), [position]);
	const [contentZIndex, setContentZIndex] = React.useState();
	useLayoutEffect2(() => {
		if (content) setContentZIndex(window.getComputedStyle(content).zIndex);
	}, [content]);
	const handleScrollButtonChange = React.useCallback(
		(node) => {
			if (node && shouldRepositionRef.current === true) {
				position();
				focusSelectedItem?.();
				shouldRepositionRef.current = false;
			}
		},
		[position, focusSelectedItem],
	);
	return /* @__PURE__ */ jsxRuntimeExports.jsx(SelectViewportProvider, {
		scope: __scopeSelect,
		contentWrapper,
		shouldExpandOnScrollRef,
		onScrollButtonChange: handleScrollButtonChange,
		children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", {
			ref: setContentWrapper,
			style: {
				display: "flex",
				flexDirection: "column",
				position: "fixed",
				zIndex: contentZIndex,
			},
			children: /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.div, {
				...popperProps,
				ref: composedRefs,
				style: {
					// When we get the height of the content, it includes borders. If we were to set
					// the height without having `boxSizing: 'border-box'` it would be too big.
					boxSizing: "border-box",
					// We need to ensure the content doesn't get taller than the wrapper
					maxHeight: "100%",
					...popperProps.style,
				},
			}),
		}),
	});
});
SelectItemAlignedPosition.displayName = ITEM_ALIGNED_POSITION_NAME;
var POPPER_POSITION_NAME = "SelectPopperPosition";
var SelectPopperPosition = React.forwardRef((props, forwardedRef) => {
	const { __scopeSelect, align = "start", collisionPadding = CONTENT_MARGIN, ...popperProps } = props;
	const popperScope = usePopperScope(__scopeSelect);
	return /* @__PURE__ */ jsxRuntimeExports.jsx(Content, {
		...popperScope,
		...popperProps,
		ref: forwardedRef,
		align,
		collisionPadding,
		style: {
			// Ensure border-box for floating-ui calculations
			boxSizing: "border-box",
			...popperProps.style,
			// re-namespace exposed content custom properties
			...{
				"--radix-select-content-transform-origin": "var(--radix-popper-transform-origin)",
				"--radix-select-content-available-width": "var(--radix-popper-available-width)",
				"--radix-select-content-available-height": "var(--radix-popper-available-height)",
				"--radix-select-trigger-width": "var(--radix-popper-anchor-width)",
				"--radix-select-trigger-height": "var(--radix-popper-anchor-height)",
			},
		},
	});
});
SelectPopperPosition.displayName = POPPER_POSITION_NAME;
var [SelectViewportProvider, useSelectViewportContext] = createSelectContext(CONTENT_NAME, {});
var VIEWPORT_NAME = "SelectViewport";
var SelectViewport = React.forwardRef((props, forwardedRef) => {
	const { __scopeSelect, nonce, ...viewportProps } = props;
	const contentContext = useSelectContentContext(VIEWPORT_NAME, __scopeSelect);
	const viewportContext = useSelectViewportContext(VIEWPORT_NAME, __scopeSelect);
	const composedRefs = useComposedRefs(forwardedRef, contentContext.onViewportChange);
	const prevScrollTopRef = React.useRef(0);
	return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, {
		children: [
			/* @__PURE__ */ jsxRuntimeExports.jsx("style", {
				dangerouslySetInnerHTML: {
					__html: `[data-radix-select-viewport]{scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;}[data-radix-select-viewport]::-webkit-scrollbar{display:none}`,
				},
				nonce,
			}),
			/* @__PURE__ */ jsxRuntimeExports.jsx(Collection.Slot, {
				scope: __scopeSelect,
				children: /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.div, {
					"data-radix-select-viewport": "",
					"role": "presentation",
					...viewportProps,
					"ref": composedRefs,
					"style": {
						// we use position: 'relative' here on the `viewport` so that when we call
						// `selectedItem.offsetTop` in calculations, the offset is relative to the viewport
						// (independent of the scrollUpButton).
						position: "relative",
						flex: 1,
						// Viewport should only be scrollable in the vertical direction.
						// This won't work in vertical writing modes, so we'll need to
						// revisit this if/when that is supported
						// https://developer.chrome.com/blog/vertical-form-controls
						overflow: "hidden auto",
						...viewportProps.style,
					},
					"onScroll": composeEventHandlers(viewportProps.onScroll, (event) => {
						const viewport = event.currentTarget;
						const { contentWrapper, shouldExpandOnScrollRef } = viewportContext;
						if (shouldExpandOnScrollRef?.current && contentWrapper) {
							const scrolledBy = Math.abs(prevScrollTopRef.current - viewport.scrollTop);
							if (scrolledBy > 0) {
								const availableHeight = window.innerHeight - CONTENT_MARGIN * 2;
								const cssMinHeight = parseFloat(contentWrapper.style.minHeight);
								const cssHeight = parseFloat(contentWrapper.style.height);
								const prevHeight = Math.max(cssMinHeight, cssHeight);
								if (prevHeight < availableHeight) {
									const nextHeight = prevHeight + scrolledBy;
									const clampedNextHeight = Math.min(availableHeight, nextHeight);
									const heightDiff = nextHeight - clampedNextHeight;
									contentWrapper.style.height = clampedNextHeight + "px";
									if (contentWrapper.style.bottom === "0px") {
										viewport.scrollTop = heightDiff > 0 ? heightDiff : 0;
										contentWrapper.style.justifyContent = "flex-end";
									}
								}
							}
						}
						prevScrollTopRef.current = viewport.scrollTop;
					}),
				}),
			}),
		],
	});
});
SelectViewport.displayName = VIEWPORT_NAME;
var GROUP_NAME = "SelectGroup";
var [SelectGroupContextProvider, useSelectGroupContext] = createSelectContext(GROUP_NAME);
var SelectGroup = React.forwardRef((props, forwardedRef) => {
	const { __scopeSelect, ...groupProps } = props;
	const groupId = useId();
	return /* @__PURE__ */ jsxRuntimeExports.jsx(SelectGroupContextProvider, {
		scope: __scopeSelect,
		id: groupId,
		children: /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.div, { "role": "group", "aria-labelledby": groupId, ...groupProps, "ref": forwardedRef }),
	});
});
SelectGroup.displayName = GROUP_NAME;
var LABEL_NAME = "SelectLabel";
var SelectLabel = React.forwardRef((props, forwardedRef) => {
	const { __scopeSelect, ...labelProps } = props;
	const groupContext = useSelectGroupContext(LABEL_NAME, __scopeSelect);
	return /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.div, { id: groupContext.id, ...labelProps, ref: forwardedRef });
});
SelectLabel.displayName = LABEL_NAME;
var ITEM_NAME = "SelectItem";
var [SelectItemContextProvider, useSelectItemContext] = createSelectContext(ITEM_NAME);
var SelectItem = React.forwardRef((props, forwardedRef) => {
	const { __scopeSelect, value, disabled = false, textValue: textValueProp, ...itemProps } = props;
	const context = useSelectContext(ITEM_NAME, __scopeSelect);
	const contentContext = useSelectContentContext(ITEM_NAME, __scopeSelect);
	const isSelected = context.value === value;
	const [textValue, setTextValue] = React.useState(textValueProp ?? "");
	const [isFocused, setIsFocused] = React.useState(false);
	const handleItemRefCallback = useCallbackRef$1((node) => contentContext.itemRefCallback?.(node, value, disabled));
	const composedRefs = useComposedRefs(forwardedRef, handleItemRefCallback);
	const textId = useId();
	const pointerTypeRef = React.useRef("touch");
	const handleSelect = () => {
		if (!disabled) {
			context.onValueChange(value);
			context.onOpenChange(false);
		}
	};
	return /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItemContextProvider, {
		scope: __scopeSelect,
		value,
		disabled,
		textId,
		isSelected,
		onItemTextChange: React.useCallback((node) => {
			setTextValue((prevTextValue) => prevTextValue || (node?.textContent ?? "").trim());
		}, []),
		children: /* @__PURE__ */ jsxRuntimeExports.jsx(Collection.ItemSlot, {
			scope: __scopeSelect,
			value,
			disabled,
			textValue,
			children: /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.div, {
				"role": "option",
				"aria-labelledby": textId,
				"data-highlighted": isFocused ? "" : void 0,
				"aria-selected": isSelected && isFocused,
				"data-state": isSelected ? "checked" : "unchecked",
				"aria-disabled": disabled || void 0,
				"data-disabled": disabled ? "" : void 0,
				"tabIndex": disabled ? void 0 : -1,
				...itemProps,
				"ref": composedRefs,
				"onFocus": composeEventHandlers(itemProps.onFocus, () => setIsFocused(true)),
				"onBlur": composeEventHandlers(itemProps.onBlur, () => setIsFocused(false)),
				"onClick": composeEventHandlers(itemProps.onClick, () => {
					if (pointerTypeRef.current !== "mouse") handleSelect();
				}),
				"onPointerUp": composeEventHandlers(itemProps.onPointerUp, () => {
					if (pointerTypeRef.current === "mouse") handleSelect();
				}),
				"onPointerDown": composeEventHandlers(itemProps.onPointerDown, (event) => {
					pointerTypeRef.current = event.pointerType;
				}),
				"onPointerMove": composeEventHandlers(itemProps.onPointerMove, (event) => {
					pointerTypeRef.current = event.pointerType;
					if (disabled) {
						contentContext.onItemLeave?.();
					} else if (pointerTypeRef.current === "mouse") {
						event.currentTarget.focus({ preventScroll: true });
					}
				}),
				"onPointerLeave": composeEventHandlers(itemProps.onPointerLeave, (event) => {
					if (event.currentTarget === document.activeElement) {
						contentContext.onItemLeave?.();
					}
				}),
				"onKeyDown": composeEventHandlers(itemProps.onKeyDown, (event) => {
					const isTypingAhead = contentContext.searchRef?.current !== "";
					if (isTypingAhead && event.key === " ") return;
					if (SELECTION_KEYS.includes(event.key)) handleSelect();
					if (event.key === " ") event.preventDefault();
				}),
			}),
		}),
	});
});
SelectItem.displayName = ITEM_NAME;
var ITEM_TEXT_NAME = "SelectItemText";
var SelectItemText = React.forwardRef((props, forwardedRef) => {
	const { __scopeSelect, className, style, ...itemTextProps } = props;
	const context = useSelectContext(ITEM_TEXT_NAME, __scopeSelect);
	const contentContext = useSelectContentContext(ITEM_TEXT_NAME, __scopeSelect);
	const itemContext = useSelectItemContext(ITEM_TEXT_NAME, __scopeSelect);
	const nativeOptionsContext = useSelectNativeOptionsContext(ITEM_TEXT_NAME, __scopeSelect);
	const [itemTextNode, setItemTextNode] = React.useState(null);
	const handleItemTextRefCallback = useCallbackRef$1((node) => contentContext.itemTextRefCallback?.(node, itemContext.value, itemContext.disabled));
	const composedRefs = useComposedRefs(forwardedRef, setItemTextNode, itemContext.onItemTextChange, handleItemTextRefCallback);
	const textContent = itemTextNode?.textContent;
	const nativeOption = React.useMemo(
		() => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: itemContext.value, disabled: itemContext.disabled, children: textContent }, itemContext.value),
		[itemContext.disabled, itemContext.value, textContent],
	);
	const { onNativeOptionAdd, onNativeOptionRemove } = nativeOptionsContext;
	useLayoutEffect2(() => {
		onNativeOptionAdd(nativeOption);
		return () => onNativeOptionRemove(nativeOption);
	}, [onNativeOptionAdd, onNativeOptionRemove, nativeOption]);
	return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, {
		children: [
			/* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.span, { id: itemContext.textId, ...itemTextProps, ref: composedRefs }),
			itemContext.isSelected && context.valueNode && !context.valueNodeHasChildren && !shouldShowPlaceholder(context.value) ?
				ReactDOM.createPortal(itemTextProps.children, context.valueNode)
			:	null,
		],
	});
});
SelectItemText.displayName = ITEM_TEXT_NAME;
var ITEM_INDICATOR_NAME = "SelectItemIndicator";
var SelectItemIndicator = React.forwardRef((props, forwardedRef) => {
	const { __scopeSelect, ...itemIndicatorProps } = props;
	const itemContext = useSelectItemContext(ITEM_INDICATOR_NAME, __scopeSelect);
	return itemContext.isSelected ? /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.span, { "aria-hidden": true, ...itemIndicatorProps, "ref": forwardedRef }) : null;
});
SelectItemIndicator.displayName = ITEM_INDICATOR_NAME;
var SCROLL_UP_BUTTON_NAME = "SelectScrollUpButton";
var SelectScrollUpButton = React.forwardRef((props, forwardedRef) => {
	const contentContext = useSelectContentContext(SCROLL_UP_BUTTON_NAME, props.__scopeSelect);
	const viewportContext = useSelectViewportContext(SCROLL_UP_BUTTON_NAME, props.__scopeSelect);
	const [canScrollUp, setCanScrollUp] = React.useState(false);
	const composedRefs = useComposedRefs(forwardedRef, viewportContext.onScrollButtonChange);
	useLayoutEffect2(() => {
		if (contentContext.viewport && contentContext.isPositioned) {
			let handleScroll2 = function () {
				const canScrollUp2 = viewport.scrollTop > 0;
				setCanScrollUp(canScrollUp2);
			};
			const viewport = contentContext.viewport;
			handleScroll2();
			viewport.addEventListener("scroll", handleScroll2);
			return () => viewport.removeEventListener("scroll", handleScroll2);
		}
	}, [contentContext.viewport, contentContext.isPositioned]);
	return canScrollUp ?
			/* @__PURE__ */ jsxRuntimeExports.jsx(SelectScrollButtonImpl, {
				...props,
				ref: composedRefs,
				onAutoScroll: () => {
					const { viewport, selectedItem } = contentContext;
					if (viewport && selectedItem) {
						viewport.scrollTop = viewport.scrollTop - selectedItem.offsetHeight;
					}
				},
			})
		:	null;
});
SelectScrollUpButton.displayName = SCROLL_UP_BUTTON_NAME;
var SCROLL_DOWN_BUTTON_NAME = "SelectScrollDownButton";
var SelectScrollDownButton = React.forwardRef((props, forwardedRef) => {
	const contentContext = useSelectContentContext(SCROLL_DOWN_BUTTON_NAME, props.__scopeSelect);
	const viewportContext = useSelectViewportContext(SCROLL_DOWN_BUTTON_NAME, props.__scopeSelect);
	const [canScrollDown, setCanScrollDown] = React.useState(false);
	const composedRefs = useComposedRefs(forwardedRef, viewportContext.onScrollButtonChange);
	useLayoutEffect2(() => {
		if (contentContext.viewport && contentContext.isPositioned) {
			let handleScroll2 = function () {
				const maxScroll = viewport.scrollHeight - viewport.clientHeight;
				const canScrollDown2 = Math.ceil(viewport.scrollTop) < maxScroll;
				setCanScrollDown(canScrollDown2);
			};
			const viewport = contentContext.viewport;
			handleScroll2();
			viewport.addEventListener("scroll", handleScroll2);
			return () => viewport.removeEventListener("scroll", handleScroll2);
		}
	}, [contentContext.viewport, contentContext.isPositioned]);
	return canScrollDown ?
			/* @__PURE__ */ jsxRuntimeExports.jsx(SelectScrollButtonImpl, {
				...props,
				ref: composedRefs,
				onAutoScroll: () => {
					const { viewport, selectedItem } = contentContext;
					if (viewport && selectedItem) {
						viewport.scrollTop = viewport.scrollTop + selectedItem.offsetHeight;
					}
				},
			})
		:	null;
});
SelectScrollDownButton.displayName = SCROLL_DOWN_BUTTON_NAME;
var SelectScrollButtonImpl = React.forwardRef((props, forwardedRef) => {
	const { __scopeSelect, onAutoScroll, ...scrollIndicatorProps } = props;
	const contentContext = useSelectContentContext("SelectScrollButton", __scopeSelect);
	const autoScrollTimerRef = React.useRef(null);
	const getItems = useCollection(__scopeSelect);
	const clearAutoScrollTimer = React.useCallback(() => {
		if (autoScrollTimerRef.current !== null) {
			window.clearInterval(autoScrollTimerRef.current);
			autoScrollTimerRef.current = null;
		}
	}, []);
	React.useEffect(() => {
		return () => clearAutoScrollTimer();
	}, [clearAutoScrollTimer]);
	useLayoutEffect2(() => {
		const activeItem = getItems().find((item) => item.ref.current === document.activeElement);
		activeItem?.ref.current?.scrollIntoView({ block: "nearest" });
	}, [getItems]);
	return /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.div, {
		"aria-hidden": true,
		...scrollIndicatorProps,
		"ref": forwardedRef,
		"style": { flexShrink: 0, ...scrollIndicatorProps.style },
		"onPointerDown": composeEventHandlers(scrollIndicatorProps.onPointerDown, () => {
			if (autoScrollTimerRef.current === null) {
				autoScrollTimerRef.current = window.setInterval(onAutoScroll, 50);
			}
		}),
		"onPointerMove": composeEventHandlers(scrollIndicatorProps.onPointerMove, () => {
			contentContext.onItemLeave?.();
			if (autoScrollTimerRef.current === null) {
				autoScrollTimerRef.current = window.setInterval(onAutoScroll, 50);
			}
		}),
		"onPointerLeave": composeEventHandlers(scrollIndicatorProps.onPointerLeave, () => {
			clearAutoScrollTimer();
		}),
	});
});
var SEPARATOR_NAME = "SelectSeparator";
var SelectSeparator = React.forwardRef((props, forwardedRef) => {
	const { __scopeSelect, ...separatorProps } = props;
	return /* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.div, { "aria-hidden": true, ...separatorProps, "ref": forwardedRef });
});
SelectSeparator.displayName = SEPARATOR_NAME;
var ARROW_NAME = "SelectArrow";
var SelectArrow = React.forwardRef((props, forwardedRef) => {
	const { __scopeSelect, ...arrowProps } = props;
	const popperScope = usePopperScope(__scopeSelect);
	const contentContext = useSelectContentContext(ARROW_NAME, __scopeSelect);
	return contentContext.position === "popper" ? /* @__PURE__ */ jsxRuntimeExports.jsx(Arrow, { ...popperScope, ...arrowProps, ref: forwardedRef }) : null;
});
SelectArrow.displayName = ARROW_NAME;
var BUBBLE_INPUT_NAME = "SelectBubbleInput";
var SelectBubbleInput = React.forwardRef(({ __scopeSelect, ...props }, forwardedRef) => {
	const context = useSelectContext(BUBBLE_INPUT_NAME, __scopeSelect);
	const { value, onValueChange, required, disabled, name, autoComplete, form } = context;
	const { nativeOptions, nativeSelectKey } = context;
	const ref = React.useRef(null);
	const composedRefs = useComposedRefs(forwardedRef, ref);
	const selectValue = value ?? "";
	const prevValue = usePrevious(selectValue);
	const hasEmptyValueOption = Array.from(nativeOptions).some((option) => (option.props.value ?? "") === "");
	React.useEffect(() => {
		const select = ref.current;
		if (!select) return;
		const selectProto = window.HTMLSelectElement.prototype;
		const descriptor = Object.getOwnPropertyDescriptor(selectProto, "value");
		const setValue = descriptor.set;
		if (prevValue !== selectValue && setValue) {
			const event = new Event("change", { bubbles: true });
			setValue.call(select, selectValue);
			select.dispatchEvent(event);
		}
	}, [prevValue, selectValue]);
	return /* @__PURE__ */ jsxRuntimeExports.jsxs(
		Primitive.select,
		{
			"aria-hidden": true,
			required,
			"tabIndex": -1,
			name,
			autoComplete,
			disabled,
			form,
			"onChange": (event) => onValueChange(event.target.value),
			...props,
			"style": { ...VISUALLY_HIDDEN_STYLES, ...props.style },
			"ref": composedRefs,
			"defaultValue": selectValue,
			"children": [shouldShowPlaceholder(value) && !hasEmptyValueOption ? /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "" }) : null, Array.from(nativeOptions)],
		},
		nativeSelectKey,
	);
});
SelectBubbleInput.displayName = BUBBLE_INPUT_NAME;
function isFunction(value) {
	return typeof value === "function";
}
function shouldShowPlaceholder(value) {
	return value === "" || value === void 0;
}
function useTypeaheadSearch(onSearchChange) {
	const handleSearchChange = useCallbackRef$1(onSearchChange);
	const searchRef = React.useRef("");
	const timerRef = React.useRef(0);
	const handleTypeaheadSearch = React.useCallback(
		(key) => {
			const search = searchRef.current + key;
			handleSearchChange(search);
			(function updateSearch(value) {
				searchRef.current = value;
				window.clearTimeout(timerRef.current);
				if (value !== "") timerRef.current = window.setTimeout(() => updateSearch(""), 1e3);
			})(search);
		},
		[handleSearchChange],
	);
	const resetTypeahead = React.useCallback(() => {
		searchRef.current = "";
		window.clearTimeout(timerRef.current);
	}, []);
	React.useEffect(() => {
		return () => window.clearTimeout(timerRef.current);
	}, []);
	return [searchRef, handleTypeaheadSearch, resetTypeahead];
}
function findNextItem(items, search, currentItem) {
	const isRepeated = search.length > 1 && Array.from(search).every((char) => char === search[0]);
	const normalizedSearch = isRepeated ? search[0] : search;
	const currentItemIndex = currentItem ? items.indexOf(currentItem) : -1;
	let wrappedItems = wrapArray(items, Math.max(currentItemIndex, 0));
	const excludeCurrentItem = normalizedSearch.length === 1;
	if (excludeCurrentItem) wrappedItems = wrappedItems.filter((v) => v !== currentItem);
	const nextItem = wrappedItems.find((item) => item.textValue.toLowerCase().startsWith(normalizedSearch.toLowerCase()));
	return nextItem !== currentItem ? nextItem : void 0;
}
function wrapArray(array, startIndex) {
	return array.map((_, index) => array[(startIndex + index) % array.length]);
}

export {
	__vitePreload as $,
	remarkParse as A,
	remarkRehype as B,
	VFile as C,
	DismissableLayer as D,
	unreachable as E,
	FocusScope as F,
	visit as G,
	Content$1 as H,
	Root$2 as I,
	SelectTrigger as J,
	SelectIcon as K,
	List as L,
	SelectScrollUpButton as M,
	SelectScrollDownButton as N,
	SelectPortal as O,
	Primitive as P,
	SelectContent as Q,
	ReactRemoveScroll as R,
	Slot$1 as S,
	Trigger as T,
	SelectViewport as U,
	VFileMessage as V,
	SelectLabel as W,
	SelectItem as X,
	SelectItemIndicator as Y,
	SelectItemText as Z,
	SelectSeparator as _,
	useDirection as a,
	createPopperScope as a$,
	twMerge as a0,
	clsx as a1,
	cva as a2,
	Select as a3,
	SelectGroup as a4,
	SelectValue as a5,
	Root2$1 as a6,
	conservativeSchema as a7,
	courseContentSchema as a8,
	defaultSchema as a9,
	L$1 as aA,
	S$1 as aB,
	Bo$1 as aC,
	autoUpdate as aD,
	No$1 as aE,
	Vn as aF,
	_o$1 as aG,
	H$1 as aH,
	fo$1 as aI,
	Ro$1 as aJ,
	Vo$1 as aK,
	No$2 as aL,
	autoPlacement as aM,
	size as aN,
	Bt$1 as aO,
	Ao$2 as aP,
	zo$1 as aQ,
	A$3 as aR,
	D$3 as aS,
	Oe$2 as aT,
	Lo$2 as aU,
	R$1 as aV,
	h$1 as aW,
	$n$1 as aX,
	Cn$1 as aY,
	Anchor as aZ,
	Root2 as a_,
	a$1 as aa,
	f$2 as ab,
	ySyncPluginKey as ac,
	getRelativeSelection as ad,
	b as ae,
	f as af,
	isShadowRoot as ag,
	isHTMLElement as ah,
	useFloating as ai,
	isElement as aj,
	getOverflowAncestors as ak,
	isWebKit as al,
	isLastTraversableNode as am,
	getParentNode as an,
	getComputedStyle$1 as ao,
	getNodeName as ap,
	$e as aq,
	offset as ar,
	shift as as,
	flip as at,
	To$1 as au,
	m$1 as av,
	Ao$1 as aw,
	D$2 as ax,
	b$2 as ay,
	Yo$1 as az,
	Presence as b,
	createRovingFocusGroupScope as b0,
	Item as b1,
	createCollection as b2,
	Root$3 as b3,
	Content as b4,
	dispatchDiscreteCustomEvent as b5,
	Arrow as b6,
	Root as b7,
	createSlottable as b8,
	At$2 as b9,
	composeEventHandlers as c,
	createContextScope as d,
	useCallbackRef$1 as e,
	useLayoutEffect2 as f,
	clamp$1 as g,
	Portal as h,
	useDismissableLayerSurface as i,
	hideOthers as j,
	createSlot as k,
	useFocusGuards as l,
	useControllableState as m,
	useId as n,
	usePrevious as o,
	useSize as p,
	html$4 as q,
	ok$1 as r,
	svg as s,
	find as t,
	useComposedRefs as u,
	stringify$1 as v,
	whitespace as w,
	stringify as x,
	pointStart as y,
	unified as z,
};
