@notectl/core 2.3.1 → 2.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/THIRD-PARTY-NOTICES.md +129 -0
- package/dist/chunks/{AlignmentPlugin--xAFlRqE.mjs → AlignmentPlugin-C9Rr_R-g.mjs} +2 -2
- package/dist/chunks/{AlignmentPlugin--xAFlRqE.mjs.map → AlignmentPlugin-C9Rr_R-g.mjs.map} +1 -1
- package/dist/chunks/{AttributedMarkCommands-bagbH8Qt.mjs → AttributedMarkCommands-BCvmqeFZ.mjs} +2 -2
- package/dist/chunks/{AttributedMarkCommands-bagbH8Qt.mjs.map → AttributedMarkCommands-BCvmqeFZ.mjs.map} +1 -1
- package/dist/chunks/{BidiIsolationPlugin-DBXyXpgm.mjs → BidiIsolationPlugin-Cw2KMk-H.mjs} +2 -2
- package/dist/chunks/{BidiIsolationPlugin-DBXyXpgm.mjs.map → BidiIsolationPlugin-Cw2KMk-H.mjs.map} +1 -1
- package/dist/chunks/{BlockInsertion-CJusvrDL.mjs → BlockInsertion-CYH2gRkY.mjs} +2 -2
- package/dist/chunks/{BlockInsertion-CJusvrDL.mjs.map → BlockInsertion-CYH2gRkY.mjs.map} +1 -1
- package/dist/chunks/{BlockquotePlugin-Bz2glHwX.mjs → BlockquotePlugin-0bZSbkyo.mjs} +2 -2
- package/dist/chunks/{BlockquotePlugin-Bz2glHwX.mjs.map → BlockquotePlugin-0bZSbkyo.mjs.map} +1 -1
- package/dist/chunks/CaretNavigationPlugin-BPZ9b6XR.mjs +2 -0
- package/dist/chunks/CaretNavigationPlugin-BPZ9b6XR.mjs.map +1 -0
- package/dist/chunks/CodeBlockPlugin-BCJ3faXx.mjs +2 -0
- package/dist/chunks/{CodeBlockPlugin-C1hrdbSZ.mjs.map → CodeBlockPlugin-BCJ3faXx.mjs.map} +1 -1
- package/dist/chunks/{ColorGrid-C9RQoEWF.mjs → ColorGrid-DQEkJ-0y.mjs} +2 -2
- package/dist/chunks/{ColorGrid-C9RQoEWF.mjs.map → ColorGrid-DQEkJ-0y.mjs.map} +1 -1
- package/dist/chunks/ColorPickerPopup-CyF26zIU.mjs +2 -0
- package/dist/chunks/{ColorPickerPopup-DiA68Pzz.mjs.map → ColorPickerPopup-CyF26zIU.mjs.map} +1 -1
- package/dist/chunks/Commands-D5ND50PQ.mjs +2 -0
- package/dist/chunks/{Commands-1dFFldi7.mjs.map → Commands-D5ND50PQ.mjs.map} +1 -1
- package/dist/chunks/{Decoration-BpoEOANl.mjs → Decoration-BOP1TAUd.mjs} +2 -2
- package/dist/chunks/{Decoration-BpoEOANl.mjs.map → Decoration-BOP1TAUd.mjs.map} +1 -1
- package/dist/chunks/{FontPlugin-By-TGrQo.mjs → FontPlugin-D041tiLe.mjs} +2 -2
- package/dist/chunks/{FontPlugin-By-TGrQo.mjs.map → FontPlugin-D041tiLe.mjs.map} +1 -1
- package/dist/chunks/{FontSizePlugin-Dln-tYXV.mjs → FontSizePlugin-8CAvj1RD.mjs} +2 -2
- package/dist/chunks/{FontSizePlugin-Dln-tYXV.mjs.map → FontSizePlugin-8CAvj1RD.mjs.map} +1 -1
- package/dist/chunks/{FormulaPlugin-By1befrR.mjs → FormulaPlugin-DJ39BtqV.mjs} +2 -2
- package/dist/chunks/FormulaPlugin-DJ39BtqV.mjs.map +1 -0
- package/dist/chunks/GapCursorNavigation-DCWOmopl.mjs +2 -0
- package/dist/chunks/{GapCursorNavigation-BuXngVu-.mjs.map → GapCursorNavigation-DCWOmopl.mjs.map} +1 -1
- package/dist/chunks/GapCursorPlugin-2iR6506y.mjs +2 -0
- package/dist/chunks/{GapCursorPlugin-DAxVzXRs.mjs.map → GapCursorPlugin-2iR6506y.mjs.map} +1 -1
- package/dist/chunks/{HardBreakPlugin-DlbSlrdq.mjs → HardBreakPlugin-vniSvTgq.mjs} +2 -2
- package/dist/chunks/{HardBreakPlugin-DlbSlrdq.mjs.map → HardBreakPlugin-vniSvTgq.mjs.map} +1 -1
- package/dist/chunks/{HeadingPlugin-uMAjPOk5.mjs → HeadingPlugin-DUKsLuMd.mjs} +2 -2
- package/dist/chunks/{HeadingPlugin-uMAjPOk5.mjs.map → HeadingPlugin-DUKsLuMd.mjs.map} +1 -1
- package/dist/chunks/{HighlightPlugin-Bj7FPkIQ.mjs → HighlightPlugin-DRTAE1N5.mjs} +2 -2
- package/dist/chunks/{HighlightPlugin-Bj7FPkIQ.mjs.map → HighlightPlugin-DRTAE1N5.mjs.map} +1 -1
- package/dist/chunks/HorizontalRulePlugin-B9OvwrKB.mjs +2 -0
- package/dist/chunks/{HorizontalRulePlugin-DUVmTQpX.mjs.map → HorizontalRulePlugin-B9OvwrKB.mjs.map} +1 -1
- package/dist/chunks/{ImagePlugin-FToCOjd5.mjs → ImagePlugin-CwxTRr6d.mjs} +2 -2
- package/dist/chunks/{ImagePlugin-FToCOjd5.mjs.map → ImagePlugin-CwxTRr6d.mjs.map} +1 -1
- package/dist/chunks/{InlineCodePlugin-pISIoGQE.mjs → InlineCodePlugin-uxgMUX_E.mjs} +2 -2
- package/dist/chunks/{InlineCodePlugin-pISIoGQE.mjs.map → InlineCodePlugin-uxgMUX_E.mjs.map} +1 -1
- package/dist/chunks/{LinkPlugin-Ct5Z180S.mjs → LinkPlugin-CvWHbSkb.mjs} +2 -2
- package/dist/chunks/{LinkPlugin-Ct5Z180S.mjs.map → LinkPlugin-CvWHbSkb.mjs.map} +1 -1
- package/dist/chunks/{ListPlugin-DAY5ezNs.mjs → ListPlugin-BchSFWVn.mjs} +2 -2
- package/dist/chunks/{ListPlugin-DAY5ezNs.mjs.map → ListPlugin-BchSFWVn.mjs.map} +1 -1
- package/dist/chunks/{MarkdownHTMLRegistry-CKEx7LkR.mjs → MarkdownHTMLRegistry-Q7l0d6Ma.mjs} +2 -2
- package/dist/chunks/{MarkdownHTMLRegistry-CKEx7LkR.mjs.map → MarkdownHTMLRegistry-Q7l0d6Ma.mjs.map} +1 -1
- package/dist/chunks/{MarkdownParser-CWt3M6jR.mjs → MarkdownParser-Dp62mfUq.mjs} +2 -2
- package/dist/chunks/{MarkdownParser-CWt3M6jR.mjs.map → MarkdownParser-Dp62mfUq.mjs.map} +1 -1
- package/dist/chunks/{MarkdownSerializer-J-i28out.mjs → MarkdownSerializer-DBELFAeo.mjs} +2 -2
- package/dist/chunks/{MarkdownSerializer-J-i28out.mjs.map → MarkdownSerializer-DBELFAeo.mjs.map} +1 -1
- package/dist/chunks/MathFont-BS8IwYh2.mjs +2 -0
- package/dist/chunks/MathFont-BS8IwYh2.mjs.map +1 -0
- package/dist/chunks/NodeSelectionCommands-WH5snxr_.mjs +2 -0
- package/dist/chunks/{NodeSelectionCommands-1PjwUDTJ.mjs.map → NodeSelectionCommands-WH5snxr_.mjs.map} +1 -1
- package/dist/chunks/PluginHelpers-KDuTlmXm.mjs +2 -0
- package/dist/chunks/{PluginHelpers-Dx_0hfDe.mjs.map → PluginHelpers-KDuTlmXm.mjs.map} +1 -1
- package/dist/chunks/{PopupManager-CT-fMZJn.mjs → PopupManager-Cj86uDEB.mjs} +2 -2
- package/dist/chunks/{PopupManager-CT-fMZJn.mjs.map → PopupManager-Cj86uDEB.mjs.map} +1 -1
- package/dist/chunks/PrintPlugin-D2javl_d.mjs +2 -0
- package/dist/chunks/{PrintPlugin-7zz9mhhU.mjs.map → PrintPlugin-D2javl_d.mjs.map} +1 -1
- package/dist/chunks/SimpleMark-gTQaSJcO.mjs +2 -0
- package/dist/chunks/{SimpleMark-7CQcA4RK.mjs.map → SimpleMark-gTQaSJcO.mjs.map} +1 -1
- package/dist/chunks/SmartPastePlugin-BvJQ-fOz.mjs +2 -0
- package/dist/chunks/SmartPastePlugin-BvJQ-fOz.mjs.map +1 -0
- package/dist/chunks/StarterFonts-BDQPg-cu.mjs +2 -0
- package/dist/chunks/StarterFonts-BDQPg-cu.mjs.map +1 -0
- package/dist/chunks/{StrikethroughPlugin-C2oWvGc3.mjs → StrikethroughPlugin-BdrG1TTY.mjs} +2 -2
- package/dist/chunks/{StrikethroughPlugin-C2oWvGc3.mjs.map → StrikethroughPlugin-BdrG1TTY.mjs.map} +1 -1
- package/dist/chunks/{SuperSubPlugin-D1xaKrSu.mjs → SuperSubPlugin-DTZB0Y3D.mjs} +2 -2
- package/dist/chunks/{SuperSubPlugin-D1xaKrSu.mjs.map → SuperSubPlugin-DTZB0Y3D.mjs.map} +1 -1
- package/dist/chunks/{TablePlugin-PJe3zwr-.mjs → TablePlugin-CPvg9YZS.mjs} +2 -2
- package/dist/chunks/{TablePlugin-PJe3zwr-.mjs.map → TablePlugin-CPvg9YZS.mjs.map} +1 -1
- package/dist/chunks/{TextColorPlugin-CTd9tfeG.mjs → TextColorPlugin-Ckg3KWvb.mjs} +2 -2
- package/dist/chunks/{TextColorPlugin-CTd9tfeG.mjs.map → TextColorPlugin-Ckg3KWvb.mjs.map} +1 -1
- package/dist/chunks/{TextDirectionAutoPlugin-CPnKVVuI.mjs → TextDirectionAutoPlugin-DRGbgTlx.mjs} +2 -2
- package/dist/chunks/{TextDirectionAutoPlugin-CPnKVVuI.mjs.map → TextDirectionAutoPlugin-DRGbgTlx.mjs.map} +1 -1
- package/dist/chunks/TextDirectionPlugin-BCeYyJHo.mjs +2 -0
- package/dist/chunks/{TextDirectionPlugin-Bns_03vL.mjs.map → TextDirectionPlugin-BCeYyJHo.mjs.map} +1 -1
- package/dist/chunks/TextDirectionService-OFOU6r_s.mjs +2 -0
- package/dist/chunks/{TextDirectionService-BcX07mw2.mjs.map → TextDirectionService-OFOU6r_s.mjs.map} +1 -1
- package/dist/chunks/TextFormattingPlugin-BhRxSC8q.mjs +2 -0
- package/dist/chunks/{TextFormattingPlugin-DBVlePqj.mjs.map → TextFormattingPlugin-BhRxSC8q.mjs.map} +1 -1
- package/dist/chunks/ThemeEngine-BUVo_zaT.mjs +3 -0
- package/dist/chunks/ThemeEngine-BUVo_zaT.mjs.map +1 -0
- package/dist/chunks/ToolbarPlugin-dmTj1jIz.mjs +2 -0
- package/dist/chunks/{ToolbarPlugin-DtfGTO_V.mjs.map → ToolbarPlugin-dmTj1jIz.mjs.map} +1 -1
- package/dist/chunks/{VideoPlugin-BrR8g6Rz.mjs → VideoPlugin-BugkI7-m.mjs} +2 -2
- package/dist/chunks/{VideoPlugin-BrR8g6Rz.mjs.map → VideoPlugin-BugkI7-m.mjs.map} +1 -1
- package/dist/chunks/XmlSupport-DIXH5on9.mjs +2 -0
- package/dist/chunks/XmlSupport-DIXH5on9.mjs.map +1 -0
- package/dist/chunks/full-JM0-SwWy.mjs +2 -0
- package/dist/chunks/{full-DSWM4cKQ.mjs.map → full-JM0-SwWy.mjs.map} +1 -1
- package/dist/chunks/minimal-DSlR2A27.mjs +2 -0
- package/dist/chunks/{minimal-CFkkPXef.mjs.map → minimal-DSlR2A27.mjs.map} +1 -1
- package/dist/chunks/register-BjsdJ3Aq.mjs +2 -0
- package/dist/chunks/register-BjsdJ3Aq.mjs.map +1 -0
- package/dist/chunks/src-5GKFQ2mQ.mjs +2 -0
- package/dist/chunks/src-5GKFQ2mQ.mjs.map +1 -0
- package/dist/chunks/xml-BsV44TrJ.mjs +2 -0
- package/dist/chunks/xml-BsV44TrJ.mjs.map +1 -0
- package/dist/editor/NotectlEditor.d.ts.map +1 -1
- package/dist/fonts/math.d.ts +3 -0
- package/dist/fonts/math.d.ts.map +1 -0
- package/dist/fonts/math.mjs +1 -0
- package/dist/fonts/starter.d.ts +3 -0
- package/dist/fonts/starter.d.ts.map +1 -0
- package/dist/fonts/starter.mjs +1 -0
- package/dist/fonts.d.ts +4 -4
- package/dist/fonts.mjs +1 -1
- package/dist/full.d.ts +1 -1
- package/dist/full.mjs +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/markdown.mjs +1 -1
- package/dist/notectl-core.mjs +1 -1
- package/dist/notectl-core.umd.js +1 -1
- package/dist/notectl-core.umd.js.map +1 -1
- package/dist/plugins/alignment.mjs +1 -1
- package/dist/plugins/bidi-isolation.mjs +1 -1
- package/dist/plugins/blockquote.mjs +1 -1
- package/dist/plugins/caret-navigation.mjs +1 -1
- package/dist/plugins/code-block/CodeBlockHighlighting.d.ts.map +1 -1
- package/dist/plugins/code-block.mjs +1 -1
- package/dist/plugins/font-size.mjs +1 -1
- package/dist/plugins/font.mjs +1 -1
- package/dist/plugins/formula/FormulaTypes.d.ts +1 -1
- package/dist/plugins/formula.mjs +1 -1
- package/dist/plugins/gap-cursor.mjs +1 -1
- package/dist/plugins/hard-break.mjs +1 -1
- package/dist/plugins/heading.mjs +1 -1
- package/dist/plugins/highlight.mjs +1 -1
- package/dist/plugins/horizontal-rule.mjs +1 -1
- package/dist/plugins/image.mjs +1 -1
- package/dist/plugins/inline-code.mjs +1 -1
- package/dist/plugins/language.mjs +1 -1
- package/dist/plugins/link.mjs +1 -1
- package/dist/plugins/list.mjs +1 -1
- package/dist/plugins/print.mjs +1 -1
- package/dist/plugins/shared.mjs +1 -1
- package/dist/plugins/smart-paste/SmartPastePlugin.d.ts.map +1 -1
- package/dist/plugins/smart-paste.mjs +1 -1
- package/dist/plugins/strikethrough.mjs +1 -1
- package/dist/plugins/super-sub.mjs +1 -1
- package/dist/plugins/table.mjs +1 -1
- package/dist/plugins/text-color.mjs +1 -1
- package/dist/plugins/text-direction-auto.mjs +1 -1
- package/dist/plugins/text-direction.mjs +1 -1
- package/dist/plugins/text-formatting.mjs +1 -1
- package/dist/plugins/toolbar.mjs +1 -1
- package/dist/plugins/video.mjs +1 -1
- package/dist/presets/full.d.ts +1 -1
- package/dist/presets/full.mjs +1 -1
- package/dist/presets/minimal.d.ts +1 -1
- package/dist/presets/minimal.mjs +1 -1
- package/dist/presets.d.ts +1 -1
- package/dist/presets.mjs +1 -1
- package/dist/register.d.ts +3 -0
- package/dist/register.d.ts.map +1 -0
- package/dist/register.mjs +1 -0
- package/package.json +24 -223
- package/dist/chunks/CaretNavigationPlugin-CNsT0eb4.mjs +0 -2
- package/dist/chunks/CaretNavigationPlugin-CNsT0eb4.mjs.map +0 -1
- package/dist/chunks/CodeBlockPlugin-C1hrdbSZ.mjs +0 -2
- package/dist/chunks/ColorPickerPopup-DiA68Pzz.mjs +0 -2
- package/dist/chunks/Commands-1dFFldi7.mjs +0 -2
- package/dist/chunks/FormulaPlugin-By1befrR.mjs.map +0 -1
- package/dist/chunks/GapCursorNavigation-BuXngVu-.mjs +0 -2
- package/dist/chunks/GapCursorPlugin-DAxVzXRs.mjs +0 -2
- package/dist/chunks/HorizontalRulePlugin-DUVmTQpX.mjs +0 -2
- package/dist/chunks/NodeSelectionCommands-1PjwUDTJ.mjs +0 -2
- package/dist/chunks/PluginHelpers-Dx_0hfDe.mjs +0 -2
- package/dist/chunks/PrintPlugin-7zz9mhhU.mjs +0 -2
- package/dist/chunks/PrintTypes-CxU69qu3.mjs +0 -3
- package/dist/chunks/PrintTypes-CxU69qu3.mjs.map +0 -1
- package/dist/chunks/SimpleMark-7CQcA4RK.mjs +0 -2
- package/dist/chunks/SmartPastePlugin-CYa_nD9v.mjs +0 -2
- package/dist/chunks/SmartPastePlugin-CYa_nD9v.mjs.map +0 -1
- package/dist/chunks/TextDirectionPlugin-Bns_03vL.mjs +0 -2
- package/dist/chunks/TextDirectionService-BcX07mw2.mjs +0 -2
- package/dist/chunks/TextFormattingPlugin-DBVlePqj.mjs +0 -2
- package/dist/chunks/ToolbarPlugin-DtfGTO_V.mjs +0 -2
- package/dist/chunks/XmlDetector-D7TQpw-e.mjs +0 -2
- package/dist/chunks/XmlDetector-D7TQpw-e.mjs.map +0 -1
- package/dist/chunks/XmlSupport-BYiRwlkK.mjs +0 -2
- package/dist/chunks/XmlSupport-BYiRwlkK.mjs.map +0 -1
- package/dist/chunks/fonts-DQC6Uj4r.mjs +0 -2
- package/dist/chunks/fonts-DQC6Uj4r.mjs.map +0 -1
- package/dist/chunks/full-DSWM4cKQ.mjs +0 -2
- package/dist/chunks/minimal-CFkkPXef.mjs +0 -2
- package/dist/chunks/src-DiAOfuOT.mjs +0 -2
- package/dist/chunks/src-DiAOfuOT.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MarkdownParser-CWt3M6jR.mjs","names":[],"sources":["../../src/serialization/markdown/InlineAst.ts","../../src/serialization/markdown/GfmAutolink.ts","../../src/serialization/markdown/LinkReferenceExtractor.ts","../../src/serialization/markdown/InlineTokenizer.ts","../../src/serialization/markdown/MarkdownParseContext.ts","../../src/serialization/MarkdownParser.ts"],"sourcesContent":["/**\n * Inline AST node model + linked-list helpers shared by the inline parser.\n *\n * A doubly-linked list of nodes is used during parsing so the emphasis\n * delimiter-stack algorithm runs in linear time (no array splicing), which is\n * the ReDoS-safety guarantee for untrusted pasted Markdown (D1).\n */\n\n/** Inline AST node kind. */\nexport type InlineKind =\n\t| 'text'\n\t| 'code'\n\t| 'softbreak'\n\t| 'hardbreak'\n\t| 'emph'\n\t| 'strong'\n\t| 'strike'\n\t| 'highlight'\n\t| 'sup'\n\t| 'sub'\n\t| 'link'\n\t| 'image'\n\t| 'inline_node'\n\t| 'html_inline';\n\n/** A single inline AST node (mutable; linked into a sibling list). */\nexport interface InlineAstNode {\n\ttype: InlineKind;\n\t/** Literal text for `text` / `code` / `html_inline`. */\n\tliteral: string;\n\t/** Container children (`emph`, `strong`, `link`, …). */\n\tchildren: InlineAstNode[];\n\t/** Link/image destination + title. */\n\thref: string;\n\ttitle: string;\n\t/** Image alt text (plain). */\n\talt: string;\n\t/** Generic inline-node passthrough (plugin extensions, e.g. math). */\n\tinlineType: string;\n\tattrs: Record<string, string | number | boolean>;\n\tprev: InlineAstNode | null;\n\tnext: InlineAstNode | null;\n}\n\n/** Creates a fresh inline AST node of the given kind. */\nexport function makeNode(type: InlineKind, literal = ''): InlineAstNode {\n\treturn {\n\t\ttype,\n\t\tliteral,\n\t\tchildren: [],\n\t\thref: '',\n\t\ttitle: '',\n\t\talt: '',\n\t\tinlineType: '',\n\t\tattrs: {},\n\t\tprev: null,\n\t\tnext: null,\n\t};\n}\n\n/** Appends `node` after `target` in the sibling linked list. */\nexport function insertAfter(target: InlineAstNode, node: InlineAstNode): void {\n\tnode.prev = target;\n\tnode.next = target.next;\n\tif (target.next) target.next.prev = node;\n\ttarget.next = node;\n}\n\n/** Removes `node` from its sibling linked list. */\nexport function unlink(node: InlineAstNode): void {\n\tif (node.prev) node.prev.next = node.next;\n\tif (node.next) node.next.prev = node.prev;\n\tnode.prev = null;\n\tnode.next = null;\n}\n","/**\n * GFM extended autolinks: `www.`, `http(s)://`, `mailto:`/`xmpp:`, bare emails.\n *\n * Implements the GFM \"autolinks (extension)\" rules rather than a naive URL\n * regex: valid-domain checks (at least one period, no underscore in the last\n * two segments), trailing-punctuation trimming, closing-paren balancing, and\n * trailing entity-reference stripping. Two integration points:\n *\n * - `matchGfmAutolinkAt` runs inside the inline parser's dispatch and scans the\n * raw source. Matching before emphasis processing keeps URLs with `_` / `~`\n * in their path intact instead of fragmenting them into delimiter runs.\n * - `applyGfmEmailAutolinks` is a post-pass over the resolved inline AST for\n * bare `user@host.tld` emails: the local part may contain delimiter\n * characters, so the surrounding text is only whole again after emphasis\n * resolution has turned unused delimiters back into plain text.\n *\n * Every scan here is a bounded forward/backward walk (linear time, D1).\n */\n\nimport { type InlineAstNode, makeNode } from './InlineAst.js';\n\n/** A matched GFM autolink: destination, display text, and source length. */\nexport interface GfmAutolinkMatch {\n\treadonly href: string;\n\treadonly display: string;\n\treadonly length: number;\n}\n\n/** Trailing characters GFM never counts as part of an autolink. */\nconst TRIMMABLE_PUNCTUATION = '?!.,:*_~';\n\n/** Characters that may precede an autolink (besides start-of-text). */\nconst BOUNDARY = /[\\s*_~(]/;\n\n/** One domain segment: alphanumerics, underscores, hyphens. */\nconst DOMAIN_SEGMENT = /^[a-zA-Z0-9_-]+$/;\n\n/** Bare email form (sticky, for in-source scans after a `mailto:`/`xmpp:` scheme). */\nconst EMAIL_AT = /[a-zA-Z0-9.+_-]+@[a-zA-Z0-9_-]+(?:\\.[a-zA-Z0-9_-]+)+/y;\n\nconst EMAIL_LOCAL_CHAR = /[a-zA-Z0-9.+_-]/;\nconst EMAIL_DOMAIN_CHAR = /[a-zA-Z0-9_-]/;\n\n/** Cheap pre-check: whether `pos` could start a GFM autolink (boundary + prefix). */\nexport function isGfmAutolinkCandidate(text: string, pos: number): boolean {\n\tconst ch: string = text[pos] ?? '';\n\tif (ch !== 'h' && ch !== 'w' && ch !== 'm' && ch !== 'x' && ch !== 'f') return false;\n\tif (pos > 0 && !BOUNDARY.test(text[pos - 1] ?? '')) return false;\n\treturn (\n\t\ttext.startsWith('http://', pos) ||\n\t\ttext.startsWith('https://', pos) ||\n\t\ttext.startsWith('ftp://', pos) ||\n\t\ttext.startsWith('www.', pos) ||\n\t\ttext.startsWith('mailto:', pos) ||\n\t\ttext.startsWith('xmpp:', pos)\n\t);\n}\n\n/** Matches a GFM autolink at `pos` (boundary included in the check), or null. */\nexport function matchGfmAutolinkAt(text: string, pos: number): GfmAutolinkMatch | null {\n\tif (!isGfmAutolinkCandidate(text, pos)) return null;\n\tif (\n\t\ttext.startsWith('http://', pos) ||\n\t\ttext.startsWith('https://', pos) ||\n\t\ttext.startsWith('ftp://', pos)\n\t) {\n\t\treturn matchUrl(text, pos);\n\t}\n\tif (text.startsWith('www.', pos)) return matchWww(text, pos);\n\treturn matchProtocolEmail(text, pos);\n}\n\n/** The raw autolink candidate at `pos`: the trimmed run up to whitespace or `<`. */\nfunction linkCandidate(text: string, pos: number): string {\n\tlet end: number = pos;\n\twhile (end < text.length) {\n\t\tconst ch: string = text[end] ?? '';\n\t\tif (ch === '<' || /\\s/.test(ch)) break;\n\t\tend++;\n\t}\n\treturn trimTrailing(text.slice(pos, end));\n}\n\nfunction matchUrl(text: string, pos: number): GfmAutolinkMatch | null {\n\tconst candidate: string = linkCandidate(text, pos);\n\tconst schemeLength: number = text.startsWith('https://', pos)\n\t\t? 8\n\t\t: text.startsWith('http://', pos)\n\t\t\t? 7\n\t\t\t: 6;\n\tif (!isValidDomain(hostOf(candidate.slice(schemeLength)))) return null;\n\treturn { href: candidate, display: candidate, length: candidate.length };\n}\n\nfunction matchWww(text: string, pos: number): GfmAutolinkMatch | null {\n\tconst candidate: string = linkCandidate(text, pos);\n\tif (!isValidDomain(hostOf(candidate))) return null;\n\treturn { href: `http://${candidate}`, display: candidate, length: candidate.length };\n}\n\n/** `mailto:` / `xmpp:` + email; the scheme is part of both href and display. */\nfunction matchProtocolEmail(text: string, pos: number): GfmAutolinkMatch | null {\n\tconst schemeLength: number = text.startsWith('mailto:', pos) ? 7 : 5;\n\tEMAIL_AT.lastIndex = pos + schemeLength;\n\tconst match: RegExpExecArray | null = EMAIL_AT.exec(text);\n\tconst email: string | undefined = match?.[0];\n\tif (!email || !isValidEmailEnd(email)) return null;\n\tconst full: string = text.slice(pos, pos + schemeLength + email.length);\n\treturn { href: full, display: full, length: full.length };\n}\n\n/** The host part of a candidate (up to the first `/ ? # :`). */\nfunction hostOf(rest: string): string {\n\tconst end: number = rest.search(/[/?#:]/);\n\treturn end === -1 ? rest : rest.slice(0, end);\n}\n\n/** GFM valid domain: >= 2 segments, no underscore in the last two segments. */\nfunction isValidDomain(domain: string): boolean {\n\tconst segments: string[] = domain.split('.');\n\tif (segments.length < 2) return false;\n\tif (!segments.every((segment) => DOMAIN_SEGMENT.test(segment))) return false;\n\treturn !segments.slice(-2).some((segment) => segment.includes('_'));\n}\n\n/** GFM email rule: the last character must not be `-` or `_`. */\nfunction isValidEmailEnd(email: string): boolean {\n\treturn !email.endsWith('-') && !email.endsWith('_');\n}\n\n/**\n * GFM trailing trims, applied until stable: trailing punctuation, an excess\n * closing paren (only while the candidate holds more `)` than `(`), and a\n * trailing entity reference (`©`). Each pass removes at least one\n * character via a bounded backward scan, so the whole trim is linear.\n */\nfunction trimTrailing(candidate: string): string {\n\tlet open = 0;\n\tlet close = 0;\n\tfor (const ch of candidate) {\n\t\tif (ch === '(') open++;\n\t\telse if (ch === ')') close++;\n\t}\n\tlet end: number = candidate.length;\n\twhile (end > 0) {\n\t\tconst ch: string = candidate[end - 1] ?? '';\n\t\tif (TRIMMABLE_PUNCTUATION.includes(ch)) {\n\t\t\tend--;\n\t\t\tcontinue;\n\t\t}\n\t\tif (ch === ')' && close > open) {\n\t\t\tend--;\n\t\t\tclose--;\n\t\t\tcontinue;\n\t\t}\n\t\tif (ch === ';') {\n\t\t\tlet j: number = end - 2;\n\t\t\twhile (j > 0 && /[a-zA-Z0-9]/.test(candidate[j] ?? '')) j--;\n\t\t\tif (candidate[j] === '&' && j < end - 2) {\n\t\t\t\tend = j;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t}\n\treturn candidate.slice(0, end);\n}\n\n/**\n * Post-pass over the resolved inline AST: turns bare `user@host.tld` emails in\n * plain text into `mailto:` links. Adjacent text nodes (including leftover\n * delimiter runs) are merged first so a local part like `a_b` is scanned as one\n * string. Subtrees that must not be linkified (links, images, code, raw HTML,\n * plugin inline nodes) are skipped.\n */\nexport function applyGfmEmailAutolinks(nodes: InlineAstNode[]): void {\n\tmergeAdjacentAstText(nodes);\n\tfor (let i = 0; i < nodes.length; i++) {\n\t\tconst node: InlineAstNode | undefined = nodes[i];\n\t\tif (!node || skipsEmailScan(node.type)) continue;\n\t\tif (node.type === 'text') {\n\t\t\tconst split: InlineAstNode[] | null = splitEmails(node.literal);\n\t\t\tif (split) {\n\t\t\t\tnodes.splice(i, 1, ...split);\n\t\t\t\ti += split.length - 1;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (node.children.length > 0) applyGfmEmailAutolinks(node.children);\n\t}\n}\n\nfunction skipsEmailScan(type: InlineAstNode['type']): boolean {\n\treturn (\n\t\ttype === 'link' ||\n\t\ttype === 'image' ||\n\t\ttype === 'code' ||\n\t\ttype === 'html_inline' ||\n\t\ttype === 'inline_node'\n\t);\n}\n\n/**\n * Merges adjacent text siblings in place (delimiter leftovers are text too).\n * Single compaction pass — no per-merge splice, which would go quadratic on a\n * long run of unmatched delimiters (D1).\n */\nfunction mergeAdjacentAstText(nodes: InlineAstNode[]): void {\n\tlet write = 0;\n\tfor (const node of nodes) {\n\t\tconst prev: InlineAstNode | undefined = write > 0 ? nodes[write - 1] : undefined;\n\t\tif (prev && prev.type === 'text' && node.type === 'text') {\n\t\t\tprev.literal += node.literal;\n\t\t\tcontinue;\n\t\t}\n\t\tnodes[write] = node;\n\t\twrite++;\n\t}\n\tnodes.length = write;\n}\n\n/**\n * Splits a literal into text/link nodes around bare emails, or null if none.\n * Anchored on each `@` with bounded back/forward scans instead of a global\n * regex: a `X+@` pattern backtracks quadratically over long runs of local-part\n * characters (the ReDoS fuzz suite catches exactly that, D1).\n */\nfunction splitEmails(literal: string): InlineAstNode[] | null {\n\tconst out: InlineAstNode[] = [];\n\tlet cursor = 0;\n\tlet at: number = literal.indexOf('@');\n\twhile (at !== -1) {\n\t\tlet start: number = at;\n\t\twhile (start > cursor && EMAIL_LOCAL_CHAR.test(literal[start - 1] ?? '')) start--;\n\t\tlet end: number = at + 1;\n\t\twhile (\n\t\t\tend < literal.length &&\n\t\t\t(EMAIL_DOMAIN_CHAR.test(literal[end] ?? '') || literal[end] === '.')\n\t\t) {\n\t\t\tend++;\n\t\t}\n\t\t// Trailing periods are punctuation, not part of the address.\n\t\twhile (end > at + 1 && literal[end - 1] === '.') end--;\n\t\tconst email: string = literal.slice(start, end);\n\t\tif (start < at && isValidBareEmail(email, end - at - 1)) {\n\t\t\tif (start > cursor) out.push(makeNode('text', literal.slice(cursor, start)));\n\t\t\tconst link: InlineAstNode = makeNode('link');\n\t\t\tlink.href = `mailto:${email}`;\n\t\t\tlink.children = [makeNode('text', email)];\n\t\t\tout.push(link);\n\t\t\tcursor = end;\n\t\t\tat = literal.indexOf('@', end);\n\t\t\tcontinue;\n\t\t}\n\t\tat = literal.indexOf('@', at + 1);\n\t}\n\tif (out.length === 0) return null;\n\tif (cursor < literal.length) out.push(makeNode('text', literal.slice(cursor)));\n\treturn out;\n}\n\n/** Validates a scanned bare email: >= 2 domain segments, valid end character. */\nfunction isValidBareEmail(email: string, domainLength: number): boolean {\n\tif (domainLength <= 0) return false;\n\tconst domain: string = email.slice(email.indexOf('@') + 1);\n\tconst segments: string[] = domain.split('.');\n\tif (segments.length < 2) return false;\n\tif (segments.some((segment) => segment === '')) return false;\n\treturn isValidEmailEnd(email);\n}\n","/**\n * Link reference definition extraction (`[label]: dest \"title\"`).\n *\n * Runs as a pre-pass over the source before block tokenization, mirroring how\n * commonmark.js resolves references before inline parsing. CommonMark rules\n * honored here:\n *\n * - A definition never interrupts an open paragraph (a def-shaped line that\n * lazily continues a paragraph stays paragraph text).\n * - Def-shaped lines inside fenced code are code and survive verbatim.\n * - The destination may sit on the line after the label, and a title may sit\n * on its own following line (multi-line labels and multi-line titles are not\n * supported; such lines stay paragraph text, which never drops content).\n * - Titles honor backslash escapes; destinations and titles decode escapes and\n * entity references; labels match case-folded (Unicode, `ẞ` ↔ `SS`).\n * - A definition inside a blockquote is recognized too; the quote marker line\n * is kept so the (now empty) container survives.\n *\n * First definition of a label wins, as per spec.\n */\n\nimport { ATX_HEADING, THEMATIC_BREAK, matchFenceOpen } from './BlockTokenizer.js';\nimport { decodeEscapesAndEntities } from './MarkdownEntities.js';\n\n/** A link reference definition target. */\nexport interface LinkRef {\n\treadonly href: string;\n\treadonly title?: string;\n}\n\n/** `[label]:` at up to 3 spaces indent; the label may hold escaped brackets. */\nconst LABEL_LINE = /^ {0,3}\\[((?:\\\\.|[^\\\\[\\]])+)\\]:(.*)$/;\n\n/** One or more blockquote markers prefixing a line. */\nconst BLOCKQUOTE_PREFIX = /^( {0,3}(?:> ?)+)(.*)$/;\n\n/** A line holding nothing but a link title (for the title-on-next-line form). */\nconst TITLE_LINE =\n\t/^[ \\t]*(?:\"((?:\\\\.|[^\"\\\\])*)\"|'((?:\\\\.|[^'\\\\])*)'|\\(((?:\\\\.|[^()\\\\])*)\\))[ \\t]*$/;\n\nconst FENCE_CLOSE = /^ {0,3}(`{3,}|~{3,})[ \\t]*$/;\n\n/**\n * Case-folds a reference label for matching. `toUpperCase().toLowerCase()`\n * approximates Unicode case folding (it maps `ẞ`/`ß` and `SS` to the same\n * string), which plain `toLowerCase()` does not.\n */\nexport function foldReferenceLabel(label: string): string {\n\treturn label\n\t\t.trim()\n\t\t.replace(/[ \\t\\n]+/g, ' ')\n\t\t.toLowerCase()\n\t\t.toUpperCase()\n\t\t.toLowerCase();\n}\n\n/** Removes link reference definitions from the source and returns them as a map. */\nexport function extractLinkReferences(source: string): {\n\tsource: string;\n\tlinkRefs: Map<string, LinkRef>;\n} {\n\tconst linkRefs = new Map<string, LinkRef>();\n\tconst lines: string[] = source.split('\\n');\n\tconst kept: string[] = [];\n\tlet fence: { char: string; len: number } | null = null;\n\t// Whether a paragraph is open above the next line: a definition is only\n\t// recognized at a block boundary (document start, after a blank line, a\n\t// heading, a thematic break, a fence, or another definition).\n\tlet openParagraph = false;\n\tlet i = 0;\n\n\twhile (i < lines.length) {\n\t\tconst line: string = lines[i] ?? '';\n\n\t\tif (fence) {\n\t\t\tkept.push(line);\n\t\t\tconst close: RegExpMatchArray | null = line.match(FENCE_CLOSE);\n\t\t\tconst run: string | undefined = close?.[1];\n\t\t\tif (run && run[0] === fence.char && run.length >= fence.len) fence = null;\n\t\t\topenParagraph = false;\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\t\tconst open: RegExpMatchArray | null = matchFenceOpen(line);\n\t\tif (open?.[1]) {\n\t\t\tfence = { char: open[1][0] ?? '`', len: open[1].length };\n\t\t\tkept.push(line);\n\t\t\topenParagraph = false;\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst consumed: number = openParagraph ? 0 : tryDefinition(lines, i, '', linkRefs, kept);\n\t\tif (consumed > 0) {\n\t\t\ti += consumed;\n\t\t\tcontinue;\n\t\t}\n\n\t\tkept.push(line);\n\t\t// A blank line or a complete block (heading, thematic break) closes any open\n\t\t// paragraph; every other non-blank line keeps one open. Deliberate keep-bias:\n\t\t// a def-shaped line after, e.g., a table row or setext underline is preserved\n\t\t// as text rather than registered, which never drops content (D3).\n\t\topenParagraph = line.trim() !== '' && !ATX_HEADING.test(line) && !THEMATIC_BREAK.test(line);\n\t\ti++;\n\t}\n\treturn { source: kept.join('\\n'), linkRefs };\n}\n\n/**\n * Tries to consume a definition starting at `lines[start]`, registering it and\n * pushing any kept container-prefix line. Returns the number of source lines\n * consumed (0 = no definition here).\n */\nfunction tryDefinition(\n\tlines: readonly string[],\n\tstart: number,\n\tprefix: string,\n\tlinkRefs: Map<string, LinkRef>,\n\tkept: string[],\n): number {\n\tconst line: string = lines[start] ?? '';\n\n\tconst label: RegExpMatchArray | null = line.match(LABEL_LINE);\n\tif (!label) {\n\t\t// A definition nested in a blockquote: strip the marker(s) and retry once.\n\t\tif (prefix === '') {\n\t\t\tconst quoted: RegExpMatchArray | null = line.match(BLOCKQUOTE_PREFIX);\n\t\t\tif (quoted?.[1] && LABEL_LINE.test(quoted[2] ?? '')) {\n\t\t\t\tconst inner: string[] = [];\n\t\t\t\tconst consumed: number = tryDefinition([quoted[2] ?? ''], 0, quoted[1], linkRefs, inner);\n\t\t\t\tif (consumed > 0) {\n\t\t\t\t\t// Keep the bare marker so the blockquote container survives.\n\t\t\t\t\tkept.push(quoted[1].trimEnd());\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\n\tconst labelText: string = label[1] ?? '';\n\tlet rest: string = (label[2] ?? '').trim();\n\tlet consumed = 1;\n\n\t// Destination on the next line (only supported outside containers).\n\tif (rest === '' && prefix === '') {\n\t\tconst next: string = lines[start + consumed] ?? '';\n\t\tif (next.trim() === '') return 0;\n\t\trest = next.trim();\n\t\tconsumed++;\n\t}\n\tif (rest === '') return 0;\n\n\tconst dest = scanDestination(rest);\n\tif (!dest) return 0;\n\tconst afterDest: string = rest.slice(dest.end).trim();\n\n\tlet title: string | undefined;\n\tif (afterDest !== '') {\n\t\tconst inline: RegExpMatchArray | null = afterDest.match(TITLE_LINE);\n\t\tif (!inline) return 0; // Junk after the destination: not a definition.\n\t\ttitle = titleFrom(inline);\n\t} else if (prefix === '') {\n\t\t// Optional title on its own next line.\n\t\tconst next: string = lines[start + consumed] ?? '';\n\t\tconst nextTitle: RegExpMatchArray | null = next.match(TITLE_LINE);\n\t\tif (nextTitle) {\n\t\t\ttitle = titleFrom(nextTitle);\n\t\t\tconsumed++;\n\t\t}\n\t}\n\n\tconst key: string = foldReferenceLabel(labelText);\n\tif (key !== '' && !linkRefs.has(key)) {\n\t\tlinkRefs.set(key, title !== undefined ? { href: dest.href, title } : { href: dest.href });\n\t}\n\treturn consumed;\n}\n\nfunction titleFrom(match: RegExpMatchArray): string {\n\treturn decodeEscapesAndEntities(match[1] ?? match[2] ?? match[3] ?? '');\n}\n\n/** Scans a definition destination: `<angle>` or a bare paren-balanced run. */\nfunction scanDestination(rest: string): { href: string; end: number } | null {\n\tif (rest[0] === '<') {\n\t\tlet i = 1;\n\t\twhile (i < rest.length) {\n\t\t\tconst ch: string = rest[i] ?? '';\n\t\t\tif (ch === '\\\\') {\n\t\t\t\ti += 2;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (ch === '>') {\n\t\t\t\treturn { href: decodeEscapesAndEntities(rest.slice(1, i)), end: i + 1 };\n\t\t\t}\n\t\t\tif (ch === '<') return null;\n\t\t\ti++;\n\t\t}\n\t\treturn null;\n\t}\n\tlet depth = 0;\n\tlet i = 0;\n\twhile (i < rest.length) {\n\t\tconst ch: string = rest[i] ?? '';\n\t\tif (ch === ' ' || ch === '\\t') break;\n\t\tif (ch === '\\\\') {\n\t\t\ti += 2;\n\t\t\tcontinue;\n\t\t}\n\t\tif (ch === '(') depth++;\n\t\tif (ch === ')') {\n\t\t\tif (depth === 0) break;\n\t\t\tdepth--;\n\t\t}\n\t\ti++;\n\t}\n\tif (i === 0) return null;\n\treturn { href: decodeEscapesAndEntities(rest.slice(0, i)), end: i };\n}\n","/**\n * Inline Markdown parser: a linear-time, delimiter-stack tokenizer (the\n * CommonMark reference approach), producing an inline AST that is then flattened\n * into notectl's `(TextNode | InlineNode)[]` mark-set model.\n *\n * Linear-time and backtracking-free by construction: emphasis runs through a\n * doubly-linked delimiter stack, code spans / autolinks / inline HTML are\n * forward scans, and there is no catastrophic-backtracking regex. This is the\n * ReDoS-safety guarantee for untrusted pasted Markdown (D1).\n *\n * Marks with no Markdown form round-trip through inline HTML produced by the\n * serializer's fallback: a balanced `<u>…</u>` / `<span style>…</span>` run is\n * captured and re-parsed via the HTML parser, recovering the original mark (D3).\n */\n\nimport type { InlineNode, Mark, TextNode } from '../../model/Document.js';\nimport { createInlineNode, createTextNode } from '../../model/Document.js';\nimport { type InlineTypeName, inlineType, markType } from '../../model/TypeBrands.js';\nimport { parseHTMLToDocument } from '../DocumentParser.js';\nimport {\n\ttype GfmAutolinkMatch,\n\tapplyGfmEmailAutolinks,\n\tisGfmAutolinkCandidate,\n\tmatchGfmAutolinkAt,\n} from './GfmAutolink.js';\nimport { type InlineAstNode, type InlineKind, insertAfter, makeNode, unlink } from './InlineAst.js';\nimport { foldReferenceLabel } from './LinkReferenceExtractor.js';\nimport {\n\tdecodeEntitiesOnly,\n\tdecodeEntity,\n\tdecodeEscapesAndEntities,\n\tmatchEntityAt,\n} from './MarkdownEntities.js';\nimport { resolveMarkdownHTMLRegistry } from './MarkdownHTMLRegistry.js';\nimport type { ParseContext } from './MarkdownParseContext.js';\n\n// --- Delimiter / bracket stacks ---\n\ninterface Delimiter {\n\tcc: string;\n\tnumdelims: number;\n\torigdelims: number;\n\tnode: InlineAstNode;\n\tcanOpen: boolean;\n\tcanClose: boolean;\n\tprev: Delimiter | null;\n\tnext: Delimiter | null;\n}\n\ninterface Bracket {\n\tnode: InlineAstNode;\n\tprev: Bracket | null;\n\tprevDelim: Delimiter | null;\n\tindex: number;\n\timage: boolean;\n\tactive: boolean;\n}\n\n/** Upper bound on an inline link destination scan (ReDoS guard; real URLs are short). */\nconst MAX_DESTINATION_LENGTH = 8192;\n\n/** Unicode punctuation + symbols, per CommonMark 0.30+ flanking rules. */\nconst UNICODE_PUNCTUATION = /[\\p{P}\\p{S}]/u;\nconst ASCII_PUNCTUATION = /[!-/:-@[-`{-~]/;\nconst ESCAPABLE = /[!-/:-@[-`{-~]/;\nconst INLINE_HTML_TAGS: ReadonlySet<string> = new Set([\n\t'u',\n\t'sup',\n\t'sub',\n\t'span',\n\t'mark',\n\t'b',\n\t'i',\n\t'em',\n\t'strong',\n\t'code',\n\t's',\n\t'del',\n\t'small',\n\t'abbr',\n\t'kbd',\n\t'a',\n]);\n\nfunction isWhitespace(ch: string): boolean {\n\treturn ch === '' || /\\s/.test(ch);\n}\n\n/** Flanking punctuation test; ASCII fast path, Unicode classes beyond it. */\nfunction isPunct(ch: string): boolean {\n\tif (ch === '') return false;\n\tif (ch.charCodeAt(0) < 128) return ASCII_PUNCTUATION.test(ch);\n\treturn UNICODE_PUNCTUATION.test(ch);\n}\n\n/** The inline parser. One instance per inline string. */\nclass InlineParser {\n\tprivate pos = 0;\n\tprivate readonly text: string;\n\tprivate readonly len: number;\n\tprivate readonly ctx: ParseContext;\n\tprivate delimiters: Delimiter | null = null;\n\tprivate brackets: Bracket | null = null;\n\t/** Tail of the output sibling list, so self-linking tokens can extend it. */\n\tprivate tail: InlineAstNode | null = null;\n\n\tconstructor(text: string, ctx: ParseContext) {\n\t\tthis.text = text;\n\t\tthis.len = text.length;\n\t\tthis.ctx = ctx;\n\t}\n\n\t/** Parses the inline string into a list of top-level AST nodes. */\n\tparse(): InlineAstNode[] {\n\t\tconst root: InlineAstNode = makeNode('text');\n\t\tthis.tail = root;\n\n\t\twhile (this.pos < this.len) {\n\t\t\tconst node: InlineAstNode | null = this.parseToken();\n\t\t\tif (node) this.appendNode(node);\n\t\t}\n\n\t\tthis.processEmphasis(null);\n\t\treturn collectSiblings(root);\n\t}\n\n\t/** Links a freshly produced token at the tail of the sibling list. */\n\tprivate appendNode(node: InlineAstNode): void {\n\t\tif (this.tail) insertAfter(this.tail, node);\n\t\tthis.tail = node;\n\t}\n\n\t/** Dispatches on the current character, returning the produced node (already linked for delims). */\n\tprivate parseToken(): InlineAstNode | null {\n\t\tconst ch: string = this.text[this.pos] ?? '';\n\t\tswitch (ch) {\n\t\t\tcase '\\n':\n\t\t\t\treturn this.parseNewline();\n\t\t\tcase '\\\\':\n\t\t\t\treturn this.parseBackslash();\n\t\t\tcase '`':\n\t\t\t\treturn this.parseBackticks();\n\t\t\tcase '*':\n\t\t\tcase '_':\n\t\t\tcase '~':\n\t\t\tcase '=':\n\t\t\tcase '^':\n\t\t\t\treturn this.parseDelimiterRun(ch);\n\t\t\tcase '[':\n\t\t\t\treturn this.parseOpenBracket(false);\n\t\t\tcase '!':\n\t\t\t\tif (this.text[this.pos + 1] === '[') return this.parseOpenBracket(true);\n\t\t\t\tthis.pos++;\n\t\t\t\treturn makeNode('text', '!');\n\t\t\tcase ']':\n\t\t\t\treturn this.parseCloseBracket();\n\t\t\tcase '<':\n\t\t\t\treturn this.parseLessThan();\n\t\t\tcase '&':\n\t\t\t\treturn this.parseEntity();\n\t\t\tdefault: {\n\t\t\t\tconst auto: InlineAstNode | null = this.parseGfmAutolink();\n\t\t\t\tif (auto) return auto;\n\t\t\t\tconst ext: InlineAstNode | null = this.parseExtension();\n\t\t\t\tif (ext) return ext;\n\t\t\t\treturn this.parseString();\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Plain text up to the next significant character. */\n\tprivate parseString(): InlineAstNode {\n\t\tconst start: number = this.pos;\n\t\tthis.pos++;\n\t\twhile (this.pos < this.len) {\n\t\t\tconst ch: string = this.text[this.pos] ?? '';\n\t\t\tif ('\\n\\\\`*_~=^[]!<&'.includes(ch)) break;\n\t\t\tif (this.matchesGfmAutolinkStart()) break;\n\t\t\tif (this.matchesExtensionStart()) break;\n\t\t\tthis.pos++;\n\t\t}\n\t\treturn makeNode('text', this.text.slice(start, this.pos));\n\t}\n\n\t/**\n\t * GFM extended autolink (`www.`, `http(s)://`, `mailto:`/`xmpp:`) at the\n\t * current position, scanned on the raw source (#199). Skipped inside an open\n\t * bracket: explicit `[text](url)` syntax wins there, and a link must never\n\t * nest inside another link. Bare emails are handled in a post-pass instead\n\t * (see `applyGfmEmailAutolinks`).\n\t */\n\tprivate parseGfmAutolink(): InlineAstNode | null {\n\t\tif (!this.ctx.opts.gfm || this.brackets) return null;\n\t\tconst match: GfmAutolinkMatch | null = matchGfmAutolinkAt(this.text, this.pos);\n\t\tif (!match) return null;\n\t\tthis.pos += match.length;\n\t\tconst node: InlineAstNode = makeNode('link');\n\t\tnode.href = match.href;\n\t\tnode.children = [makeNode('text', match.display)];\n\t\treturn node;\n\t}\n\n\tprivate matchesGfmAutolinkStart(): boolean {\n\t\tif (!this.ctx.opts.gfm || this.brackets) return false;\n\t\treturn isGfmAutolinkCandidate(this.text, this.pos);\n\t}\n\n\t/** Plugin syntax extensions (e.g. formula `$...$`). */\n\tprivate parseExtension(): InlineAstNode | null {\n\t\tfor (const ext of this.ctx.opts.syntaxExtensions) {\n\t\t\tconst match = ext.matchInline?.(this.text, this.pos);\n\t\t\tif (match) {\n\t\t\t\tthis.pos += match.length;\n\t\t\t\tconst node: InlineAstNode = makeNode('inline_node');\n\t\t\t\tnode.inlineType = match.type;\n\t\t\t\tnode.attrs = match.attrs;\n\t\t\t\treturn node;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate matchesExtensionStart(): boolean {\n\t\tfor (const ext of this.ctx.opts.syntaxExtensions) {\n\t\t\tif (ext.matchInline?.(this.text, this.pos)) return true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * A newline produces a soft break, unless the line above ends in two or more\n\t * spaces: that is CommonMark's second hard-break form (#193). The trailing\n\t * whitespace lives in the previously emitted text node (the block tokenizer\n\t * preserves interior trailing spaces for exactly this check) and is stripped\n\t * either way — line-end whitespace is never content. The backslash form is\n\t * handled in `parseBackslash`.\n\t */\n\tprivate parseNewline(): InlineAstNode {\n\t\tthis.pos++;\n\t\t// Skip leading spaces of the next line.\n\t\twhile (this.text[this.pos] === ' ') this.pos++;\n\t\tconst prev: InlineAstNode | null = this.tail;\n\t\tif (prev && prev.type === 'text' && prev.literal.endsWith(' ')) {\n\t\t\tconst hard: boolean = prev.literal.endsWith(' ');\n\t\t\tprev.literal = prev.literal.replace(/[ \\t]+$/, '');\n\t\t\treturn makeNode(hard ? 'hardbreak' : 'softbreak');\n\t\t}\n\t\treturn makeNode('softbreak');\n\t}\n\n\tprivate parseBackslash(): InlineAstNode {\n\t\tthis.pos++;\n\t\tconst next: string = this.text[this.pos] ?? '';\n\t\tif (next === '\\n') {\n\t\t\tthis.pos++;\n\t\t\twhile (this.text[this.pos] === ' ') this.pos++;\n\t\t\treturn makeNode('hardbreak');\n\t\t}\n\t\tif (ESCAPABLE.test(next)) {\n\t\t\tthis.pos++;\n\t\t\treturn makeNode('text', next);\n\t\t}\n\t\treturn makeNode('text', '\\\\');\n\t}\n\n\t/** A backtick code span; falls back to literal backticks if unclosed. */\n\tprivate parseBackticks(): InlineAstNode {\n\t\tconst start: number = this.pos;\n\t\tlet count = 0;\n\t\twhile (this.text[this.pos] === '`') {\n\t\t\tcount++;\n\t\t\tthis.pos++;\n\t\t}\n\t\tconst fence: string = '`'.repeat(count);\n\t\tconst after: number = this.pos;\n\t\tlet search: number = after;\n\t\twhile (search < this.len) {\n\t\t\tconst idx: number = this.text.indexOf(fence, search);\n\t\t\tif (idx === -1) break;\n\t\t\t// Require an exact-length run (not part of a longer run).\n\t\t\tif (this.text[idx - 1] !== '`' && this.text[idx + count] !== '`') {\n\t\t\t\tlet content: string = this.text.slice(after, idx).replace(/\\n/g, ' ');\n\t\t\t\tif (\n\t\t\t\t\tcontent.length > 2 &&\n\t\t\t\t\tcontent.startsWith(' ') &&\n\t\t\t\t\tcontent.endsWith(' ') &&\n\t\t\t\t\tcontent.trim() !== ''\n\t\t\t\t) {\n\t\t\t\t\tcontent = content.slice(1, -1);\n\t\t\t\t}\n\t\t\t\tthis.pos = idx + count;\n\t\t\t\treturn makeNode('code', content);\n\t\t\t}\n\t\t\tsearch = idx + 1;\n\t\t}\n\t\t// No closer: treat the opening run as literal text.\n\t\tthis.pos = after;\n\t\treturn makeNode('text', this.text.slice(start, after));\n\t}\n\n\t/** A run of emphasis/extension delimiters; records a stack entry. */\n\tprivate parseDelimiterRun(cc: string): InlineAstNode {\n\t\tconst { numdelims, canOpen, canClose } = this.scanDelims(cc);\n\t\tconst node: InlineAstNode = makeNode('text', cc.repeat(numdelims));\n\t\tconst delim: Delimiter = {\n\t\t\tcc,\n\t\t\tnumdelims,\n\t\t\torigdelims: numdelims,\n\t\t\tnode,\n\t\t\tcanOpen,\n\t\t\tcanClose,\n\t\t\tprev: this.delimiters,\n\t\t\tnext: null,\n\t\t};\n\t\tif (this.delimiters) this.delimiters.next = delim;\n\t\tthis.delimiters = delim;\n\t\treturn node;\n\t}\n\n\t/** Counts a delimiter run and computes its flanking flags. */\n\tprivate scanDelims(cc: string): { numdelims: number; canOpen: boolean; canClose: boolean } {\n\t\tconst start: number = this.pos;\n\t\twhile (this.text[this.pos] === cc) this.pos++;\n\t\tconst numdelims: number = this.pos - start;\n\n\t\tconst before: string = start === 0 ? '' : (this.text[start - 1] ?? '');\n\t\tconst after: string = this.text[this.pos] ?? '';\n\t\tconst beforeWs: boolean = isWhitespace(before);\n\t\tconst afterWs: boolean = isWhitespace(after);\n\t\tconst beforePunct: boolean = isPunct(before);\n\t\tconst afterPunct: boolean = isPunct(after);\n\n\t\tconst leftFlanking: boolean = !afterWs && (!afterPunct || beforeWs || beforePunct);\n\t\tconst rightFlanking: boolean = !beforeWs && (!beforePunct || afterWs || afterPunct);\n\n\t\tlet canOpen: boolean;\n\t\tlet canClose: boolean;\n\t\tif (cc === '_') {\n\t\t\tcanOpen = leftFlanking && (!rightFlanking || beforePunct);\n\t\t\tcanClose = rightFlanking && (!leftFlanking || afterPunct);\n\t\t} else {\n\t\t\tcanOpen = leftFlanking;\n\t\t\tcanClose = rightFlanking;\n\t\t}\n\t\treturn { numdelims, canOpen, canClose };\n\t}\n\n\t/** `[` or `![`: push a bracket and emit its literal text node. */\n\tprivate parseOpenBracket(image: boolean): InlineAstNode {\n\t\tconst literal: string = image ? '![' : '[';\n\t\tthis.pos += literal.length;\n\t\tconst node: InlineAstNode = makeNode('text', literal);\n\t\tthis.brackets = {\n\t\t\tnode,\n\t\t\tprev: this.brackets,\n\t\t\tprevDelim: this.delimiters,\n\t\t\tindex: this.pos,\n\t\t\timage,\n\t\t\tactive: true,\n\t\t};\n\t\treturn node;\n\t}\n\n\t/** `]`: resolve a link/image against the open bracket, or emit literal `]`. */\n\tprivate parseCloseBracket(): InlineAstNode | null {\n\t\tthis.pos++;\n\t\tconst opener: Bracket | null = this.brackets;\n\t\tif (!opener || !opener.active) {\n\t\t\tif (opener) this.brackets = opener.prev;\n\t\t\treturn makeNode('text', ']');\n\t\t}\n\n\t\tconst dest: { href: string; title: string } | null = this.parseLinkTarget(opener);\n\t\tif (!dest) {\n\t\t\tthis.brackets = opener.prev;\n\t\t\treturn makeNode('text', ']');\n\t\t}\n\n\t\tconst wrapper: InlineAstNode = makeNode(opener.image ? 'image' : 'link');\n\t\twrapper.href = dest.href;\n\t\twrapper.title = dest.title;\n\n\t\t// Resolve emphasis inside the bracketed range first. The emphasis algorithm\n\t\t// walks the sibling linked list, so it must run while the bracket contents\n\t\t// are still linked, before they are detached into the wrapper's children.\n\t\tthis.processEmphasis(opener.prevDelim);\n\n\t\t// Move the (now emphasis-resolved) nodes between the opener and now into the wrapper.\n\t\tlet node: InlineAstNode | null = opener.node.next;\n\t\twhile (node) {\n\t\t\tconst next: InlineAstNode | null = node.next;\n\t\t\tunlink(node);\n\t\t\twrapper.children.push(node);\n\t\t\tnode = next;\n\t\t}\n\t\tif (opener.image) {\n\t\t\twrapper.alt = astPlainText(wrapper.children);\n\t\t\twrapper.children = [];\n\t\t}\n\n\t\t// The wrapper takes the opener's place and becomes the new tail. It is linked\n\t\t// here, so the parse loop must not append it again (returning null prevents the\n\t\t// double-link that would otherwise corrupt the wrapper's sibling pointers).\n\t\tinsertAfter(opener.node, wrapper);\n\t\tunlink(opener.node);\n\t\tthis.tail = wrapper;\n\n\t\t// Links cannot contain other links.\n\t\tif (!opener.image) {\n\t\t\tlet b: Bracket | null = opener.prev;\n\t\t\twhile (b) {\n\t\t\t\tif (!b.image) b.active = false;\n\t\t\t\tb = b.prev;\n\t\t\t}\n\t\t}\n\t\tthis.brackets = opener.prev;\n\t\treturn null;\n\t}\n\n\t/** Parses an inline `(dest \"title\")` or a reference `[label]` target. */\n\tprivate parseLinkTarget(opener: Bracket): { href: string; title: string } | null {\n\t\tif (this.text[this.pos] === '(') {\n\t\t\tconst inline = this.parseInlineDestination();\n\t\t\tif (inline) return inline;\n\t\t}\n\t\t// Reference link: [label][ref], [label][], or shortcut [label].\n\t\tconst label: string = this.text.slice(opener.index, this.pos - 1);\n\t\tlet ref: string = label;\n\t\tlet explicitRef = false;\n\t\t// Position to advance to *only* when the reference resolves. A failed\n\t\t// lookup must leave `this.pos` at the `]` so the trailing `[ref]` re-parses\n\t\t// as literal text — otherwise `[text][missing]` silently drops `[missing]`.\n\t\tlet resolvedEnd: number = this.pos;\n\t\tif (this.text[this.pos] === '[') {\n\t\t\tconst close: number = this.text.indexOf(']', this.pos + 1);\n\t\t\tif (close !== -1) {\n\t\t\t\tconst explicit: string = this.text.slice(this.pos + 1, close);\n\t\t\t\tif (explicit.trim() !== '') {\n\t\t\t\t\tref = explicit;\n\t\t\t\t\texplicitRef = true;\n\t\t\t\t}\n\t\t\t\tresolvedEnd = close + 1;\n\t\t\t}\n\t\t}\n\t\t// CommonMark forbids unescaped `[` inside a reference label. For shortcut\n\t\t// and collapsed forms the bracket text is the label; a full reference\n\t\t// (`[text][ref]`) may hold anything in its text part.\n\t\tif (!explicitRef && ref.includes('[')) return null;\n\t\tconst found = this.ctx.linkRefs.get(foldReferenceLabel(ref));\n\t\tif (found) {\n\t\t\tthis.pos = resolvedEnd;\n\t\t\treturn { href: found.href, title: found.title ?? '' };\n\t\t}\n\t\treturn null;\n\t}\n\n\t/** Parses `(url \"title\")` starting at the current `(`; returns null if malformed. */\n\tprivate parseInlineDestination(): { href: string; title: string } | null {\n\t\tlet i: number = this.pos + 1;\n\t\twhile (this.text[i] === ' ' || this.text[i] === '\\n') i++;\n\n\t\tlet href = '';\n\t\tif (this.text[i] === '<') {\n\t\t\t// Angle destination: ends at the first unescaped `>`; `<` and newlines\n\t\t\t// are not allowed inside. Escapes and entities decode afterwards.\n\t\t\tlet end: number = i + 1;\n\t\t\tconst angleLimit: number = Math.min(this.len, i + 1 + MAX_DESTINATION_LENGTH);\n\t\t\twhile (end < angleLimit) {\n\t\t\t\tconst ch: string = this.text[end] ?? '';\n\t\t\t\tif (ch === '\\\\' && ESCAPABLE.test(this.text[end + 1] ?? '')) {\n\t\t\t\t\tend += 2;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (ch === '>' || ch === '<' || ch === '\\n') break;\n\t\t\t\tend++;\n\t\t\t}\n\t\t\tif (this.text[end] !== '>') return null;\n\t\t\thref = decodeEscapesAndEntities(this.text.slice(i + 1, end));\n\t\t\ti = end + 1;\n\t\t} else {\n\t\t\tlet depth = 0;\n\t\t\t// Bound the destination scan: real URLs are short, and an unbounded scan\n\t\t\t// over adversarial input (`](](]( …` with no closing paren) is quadratic.\n\t\t\tconst limit: number = Math.min(this.len, i + MAX_DESTINATION_LENGTH);\n\t\t\twhile (i < limit) {\n\t\t\t\tconst ch: string = this.text[i] ?? '';\n\t\t\t\tif (ch === '\\\\' && ESCAPABLE.test(this.text[i + 1] ?? '')) {\n\t\t\t\t\thref += this.text[i + 1];\n\t\t\t\t\ti += 2;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (ch === '(') depth++;\n\t\t\t\telse if (ch === ')') {\n\t\t\t\t\tif (depth === 0) break;\n\t\t\t\t\tdepth--;\n\t\t\t\t} else if (ch === ' ' || ch === '\\t' || ch === '\\n' || ch === '[' || ch === ']') {\n\t\t\t\t\t// Only ASCII whitespace ends a bare destination (a non-breaking\n\t\t\t\t\t// space is part of it, per CommonMark); `[`/`]` are not valid there\n\t\t\t\t\t// (real URLs percent-encode them) and bailing here keeps degenerate\n\t\t\t\t\t// `](](]( …` input linear instead of quadratic (D1 ReDoS guard).\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\thref += ch;\n\t\t\t\ti++;\n\t\t\t}\n\t\t\t// Scan hit the length cap without resolving a closing paren: not a link.\n\t\t\tif (i >= limit && this.text[i] !== ')') return null;\n\t\t\thref = decodeEntitiesOnly(href);\n\t\t}\n\n\t\twhile (this.text[i] === ' ' || this.text[i] === '\\n') i++;\n\t\tlet title = '';\n\t\tconst open: string = this.text[i] ?? '';\n\t\tif (open === '\"' || open === \"'\" || open === '(') {\n\t\t\tconst parsed: { title: string; next: number } | null = this.parseLinkTitle(i);\n\t\t\tif (!parsed) return null;\n\t\t\ttitle = parsed.title;\n\t\t\ti = parsed.next;\n\t\t}\n\t\twhile (this.text[i] === ' ' || this.text[i] === '\\n') i++;\n\t\tif (this.text[i] !== ')') return null;\n\t\tthis.pos = i + 1;\n\t\treturn { href, title };\n\t}\n\n\t/**\n\t * Parses a link/image title that starts at its opening delimiter (`\"`, `'`,\n\t * or `(`), honoring backslash escapes so an escaped closing delimiter stays\n\t * part of the title (e.g. `\"a\\\"b\"` is the title `a\"b`). Mirrors the serializer\n\t * which escapes the delimiter on output, so titles round-trip losslessly.\n\t * Returns the unescaped title and the index just past the closing delimiter,\n\t * or null if it is never closed within the scan bound (D1 ReDoS guard).\n\t */\n\tprivate parseLinkTitle(start: number): { title: string; next: number } | null {\n\t\tconst open: string = this.text[start] ?? '';\n\t\tconst close: string = open === '(' ? ')' : open;\n\t\tlet title = '';\n\t\tlet i: number = start + 1;\n\t\tconst limit: number = Math.min(this.len, start + 1 + MAX_DESTINATION_LENGTH);\n\t\twhile (i < limit) {\n\t\t\tconst ch: string = this.text[i] ?? '';\n\t\t\tif (ch === '\\\\' && ESCAPABLE.test(this.text[i + 1] ?? '')) {\n\t\t\t\ttitle += this.text[i + 1];\n\t\t\t\ti += 2;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (ch === close) return { title: decodeEntitiesOnly(title), next: i + 1 };\n\t\t\ttitle += ch;\n\t\t\ti++;\n\t\t}\n\t\treturn null;\n\t}\n\n\t/** `&`: decodes an HTML entity reference, or emits a literal `&`. */\n\tprivate parseEntity(): InlineAstNode {\n\t\tconst match: RegExpExecArray | null = matchEntityAt(this.text, this.pos);\n\t\tif (!match?.[1]) {\n\t\t\tthis.pos++;\n\t\t\treturn makeNode('text', '&');\n\t\t}\n\t\tthis.pos += match[0].length;\n\t\treturn makeNode('text', decodeEntity(match[1]));\n\t}\n\n\t/** `<`: autolink, inline HTML (balanced fallback run), or literal. */\n\tprivate parseLessThan(): InlineAstNode {\n\t\tconst auto: InlineAstNode | null = this.parseAutolink();\n\t\tif (auto) return auto;\n\t\tconst html: InlineAstNode | null = this.parseInlineHtml();\n\t\tif (html) return html;\n\t\tthis.pos++;\n\t\treturn makeNode('text', '<');\n\t}\n\n\tprivate parseAutolink(): InlineAstNode | null {\n\t\tconst rest: string = this.text.slice(this.pos);\n\t\tconst uri: RegExpMatchArray | null = rest.match(/^<([a-zA-Z][a-zA-Z0-9+.-]{1,31}:[^<>\\s]*)>/);\n\t\tif (uri?.[1]) {\n\t\t\tthis.pos += uri[0].length;\n\t\t\tconst node: InlineAstNode = makeNode('link');\n\t\t\tnode.href = uri[1];\n\t\t\tnode.children = [makeNode('text', uri[1])];\n\t\t\treturn node;\n\t\t}\n\t\tconst email: RegExpMatchArray | null = rest.match(\n\t\t\t/^<([a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)>/,\n\t\t);\n\t\tif (email?.[1]) {\n\t\t\tthis.pos += email[0].length;\n\t\t\tconst node: InlineAstNode = makeNode('link');\n\t\t\tnode.href = `mailto:${email[1]}`;\n\t\t\tnode.children = [makeNode('text', email[1])];\n\t\t\treturn node;\n\t\t}\n\t\treturn null;\n\t}\n\n\t/** Captures a balanced inline-HTML fallback run (`<u>…</u>`) or a single tag. */\n\tprivate parseInlineHtml(): InlineAstNode | null {\n\t\tconst rest: string = this.text.slice(this.pos);\n\t\tconst open: RegExpMatchArray | null = rest.match(/^<([a-zA-Z][a-zA-Z0-9-]*)(?:\\s[^<>]*)?>/);\n\t\tif (!open?.[1]) {\n\t\t\tconst comment: RegExpMatchArray | null = rest.match(/^<!--[\\s\\S]*?-->/);\n\t\t\tif (comment) {\n\t\t\t\tthis.pos += comment[0].length;\n\t\t\t\treturn makeNode('html_inline', normalizeHtmlLiteral(comment[0]));\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\tconst tag: string = open[1].toLowerCase();\n\t\tif (INLINE_HTML_TAGS.has(tag)) {\n\t\t\tconst closeTag = `</${tag}>`;\n\t\t\tconst closeIdx: number = this.text.toLowerCase().indexOf(closeTag, this.pos + open[0].length);\n\t\t\tif (closeIdx !== -1) {\n\t\t\t\tconst end: number = closeIdx + closeTag.length;\n\t\t\t\tconst html: string = this.text.slice(this.pos, end);\n\t\t\t\tthis.pos = end;\n\t\t\t\treturn makeNode('html_inline', normalizeHtmlLiteral(html));\n\t\t\t}\n\t\t}\n\t\tthis.pos += open[0].length;\n\t\treturn makeNode('html_inline', normalizeHtmlLiteral(open[0]));\n\t}\n\n\t/**\n\t * The CommonMark emphasis algorithm: walk closers, match against openers,\n\t * wrap the enclosed nodes, honoring the rule-of-three for `*` / `_`.\n\t *\n\t * `openersBottom` is the CommonMark \"lower bound for opener searches\" cache.\n\t * It must be keyed by `(delimiter char, canOpen, origdelims % 3)` — exactly\n\t * the bucketing used by the reference implementation — because the rule-of-\n\t * three test depends on both flags. Keying by the character alone lets a\n\t * `canOpen=false` closer that fails lower the bound for a later `canOpen=true`\n\t * closer of a different length class, silently dropping valid emphasis (e.g.\n\t * `**a*b*c**` would lose all of its bold/italic).\n\t */\n\tprivate processEmphasis(stackBottom: Delimiter | null): void {\n\t\tconst openersBottom = new Map<string, Delimiter | null>();\n\t\tconst bottomFor = (d: Delimiter): Delimiter | null => {\n\t\t\tconst key: string = emphBottomKey(d);\n\t\t\treturn openersBottom.has(key) ? (openersBottom.get(key) ?? null) : stackBottom;\n\t\t};\n\n\t\tlet closer: Delimiter | null = stackBottom ? stackBottom.next : this.firstDelimiter();\n\t\twhile (closer) {\n\t\t\tif (!closer.canClose) {\n\t\t\t\tcloser = closer.next;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst cc: string = closer.cc;\n\t\t\tlet opener: Delimiter | null = closer.prev;\n\t\t\tlet openerFound = false;\n\t\t\tconst bottom: Delimiter | null = bottomFor(closer);\n\t\t\twhile (opener && opener !== stackBottom && opener !== bottom) {\n\t\t\t\tif (opener.cc === cc && opener.canOpen && this.emphMatches(opener, closer)) {\n\t\t\t\t\topenerFound = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\topener = opener.prev;\n\t\t\t}\n\n\t\t\tif (!openerFound) {\n\t\t\t\topenersBottom.set(emphBottomKey(closer), closer.prev);\n\t\t\t\tif (!closer.canOpen) this.removeDelimiter(closer);\n\t\t\t\tcloser = closer.next;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst matchedOpener: Delimiter = opener as Delimiter;\n\t\t\tconst use: number = this.wrapCount(cc, matchedOpener, closer);\n\t\t\tif (use === 0) {\n\t\t\t\tcloser = closer.next;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tthis.wrapEmphasis(cc, matchedOpener, closer, use);\n\n\t\t\tmatchedOpener.numdelims -= use;\n\t\t\tcloser.numdelims -= use;\n\t\t\tmatchedOpener.node.literal = matchedOpener.node.literal.slice(use);\n\t\t\tcloser.node.literal = closer.node.literal.slice(use);\n\n\t\t\tthis.removeDelimitersBetween(matchedOpener, closer);\n\t\t\tif (matchedOpener.numdelims === 0) this.removeDelimiter(matchedOpener);\n\t\t\tif (closer.numdelims === 0) {\n\t\t\t\tconst next: Delimiter | null = closer.next;\n\t\t\t\tthis.removeDelimiter(closer);\n\t\t\t\tcloser = next;\n\t\t\t}\n\t\t}\n\n\t\t// Remove all delimiters above stackBottom.\n\t\twhile (this.delimiters && this.delimiters !== stackBottom) {\n\t\t\tthis.removeDelimiter(this.delimiters);\n\t\t}\n\t}\n\n\tprivate firstDelimiter(): Delimiter | null {\n\t\tlet d: Delimiter | null = this.delimiters;\n\t\twhile (d?.prev) d = d.prev;\n\t\treturn d;\n\t}\n\n\t/** Rule-of-three test for `*` / `_`; other delimiters always match. */\n\tprivate emphMatches(opener: Delimiter, closer: Delimiter): boolean {\n\t\tif (opener.cc !== '*' && opener.cc !== '_') return true;\n\t\tconst oddMatch: boolean =\n\t\t\t(closer.canOpen || opener.canClose) &&\n\t\t\tcloser.origdelims % 3 !== 0 &&\n\t\t\t(opener.origdelims + closer.origdelims) % 3 === 0;\n\t\treturn !oddMatch;\n\t}\n\n\t/** How many delimiters this match consumes (and implicitly which wrapper kind). */\n\tprivate wrapCount(cc: string, opener: Delimiter, closer: Delimiter): number {\n\t\tconst both2: boolean = opener.numdelims >= 2 && closer.numdelims >= 2;\n\t\tif (cc === '*' || cc === '_') return both2 ? 2 : 1;\n\t\tif (cc === '~') {\n\t\t\tif (this.ctx.opts.gfm && both2) return 2;\n\t\t\treturn this.ctx.opts.extendedInlineSyntax ? 1 : 0;\n\t\t}\n\t\tif (cc === '=') return this.ctx.opts.extendedInlineSyntax && both2 ? 2 : 0;\n\t\tif (cc === '^') return this.ctx.opts.extendedInlineSyntax ? 1 : 0;\n\t\treturn 0;\n\t}\n\n\tprivate wrapKind(cc: string, use: number): InlineKind {\n\t\tif (cc === '*' || cc === '_') return use === 2 ? 'strong' : 'emph';\n\t\tif (cc === '~') return use === 2 ? 'strike' : 'sub';\n\t\tif (cc === '=') return 'highlight';\n\t\treturn 'sup';\n\t}\n\n\t/** Moves nodes between opener and closer into a new wrapper node. */\n\tprivate wrapEmphasis(cc: string, opener: Delimiter, closer: Delimiter, use: number): void {\n\t\tconst wrapper: InlineAstNode = makeNode(this.wrapKind(cc, use));\n\t\tlet node: InlineAstNode | null = opener.node.next;\n\t\twhile (node && node !== closer.node) {\n\t\t\tconst next: InlineAstNode | null = node.next;\n\t\t\tunlink(node);\n\t\t\twrapper.children.push(node);\n\t\t\tnode = next;\n\t\t}\n\t\tinsertAfter(opener.node, wrapper);\n\t}\n\n\tprivate removeDelimitersBetween(opener: Delimiter, closer: Delimiter): void {\n\t\tlet d: Delimiter | null = closer.prev;\n\t\twhile (d && d !== opener) {\n\t\t\tconst prev: Delimiter | null = d.prev;\n\t\t\tthis.removeDelimiter(d);\n\t\t\td = prev;\n\t\t}\n\t}\n\n\tprivate removeDelimiter(delim: Delimiter): void {\n\t\tif (delim.prev) delim.prev.next = delim.next;\n\t\tif (delim.next) delim.next.prev = delim.prev;\n\t\tif (!delim.next) this.delimiters = delim.prev;\n\t}\n}\n\n/**\n * The `openersBottom` bucket key for a closer: `(char, canOpen, origdelims % 3)`,\n * mirroring the CommonMark reference implementation's index (`base + (canOpen ?\n * 3 : 0) + (origdelims % 3)`). `origdelims` is used, not the live `numdelims`, so\n * the key is stable across the delimiter's consumption.\n */\nfunction emphBottomKey(d: Delimiter): string {\n\treturn `${d.cc}${d.canOpen ? 1 : 0}${d.origdelims % 3}`;\n}\n\n/**\n * Normalizes newlines inside a captured raw-HTML run to single spaces. The\n * literal is opaque to the engine; on re-import the newlines would become soft\n * breaks (spaces) anyway, so normalizing here keeps `md → doc → md` a fixpoint.\n */\nfunction normalizeHtmlLiteral(html: string): string {\n\treturn html.replace(/\\n[ \\t]*/g, ' ');\n}\n\n/** Collects the sibling list after the synthetic root into an array. */\nfunction collectSiblings(root: InlineAstNode): InlineAstNode[] {\n\tconst out: InlineAstNode[] = [];\n\tlet node: InlineAstNode | null = root.next;\n\twhile (node) {\n\t\tout.push(node);\n\t\tnode = node.next;\n\t}\n\treturn out;\n}\n\n/** Extracts the plain-text content of an AST subtree (for image alt text). */\nfunction astPlainText(nodes: readonly InlineAstNode[]): string {\n\tlet out = '';\n\tfor (const node of nodes) {\n\t\tif (node.type === 'text' || node.type === 'code') out += node.literal;\n\t\telse if (node.type === 'softbreak' || node.type === 'hardbreak') out += ' ';\n\t\telse if (node.children.length) out += astPlainText(node.children);\n\t\telse if (node.alt) out += node.alt;\n\t}\n\treturn out;\n}\n\n// --- Flatten AST → notectl inline content ---\n\nconst KIND_TO_MARK: Partial<Record<InlineKind, string>> = {\n\temph: 'italic',\n\tstrong: 'bold',\n\tstrike: 'strikethrough',\n\thighlight: 'highlight',\n\tsup: 'superscript',\n\tsub: 'subscript',\n};\n\n/** Whether a mark type is usable (no registry means tests; allow all). */\nfunction markAllowed(ctx: ParseContext, type: string): boolean {\n\treturn !ctx.registry || ctx.registry.getMarkSpec(type) !== undefined;\n}\n\nfunction flatten(\n\tnodes: readonly InlineAstNode[],\n\tmarks: readonly Mark[],\n\tctx: ParseContext,\n\tout: (TextNode | InlineNode)[],\n): void {\n\tfor (const node of nodes) {\n\t\tflattenNode(node, marks, ctx, out);\n\t}\n}\n\nfunction flattenNode(\n\tnode: InlineAstNode,\n\tmarks: readonly Mark[],\n\tctx: ParseContext,\n\tout: (TextNode | InlineNode)[],\n): void {\n\tswitch (node.type) {\n\t\tcase 'text':\n\t\t\tif (node.literal) out.push(createTextNode(node.literal, [...marks]));\n\t\t\treturn;\n\t\tcase 'softbreak':\n\t\t\tout.push(createTextNode(' ', [...marks]));\n\t\t\treturn;\n\t\tcase 'hardbreak':\n\t\t\tout.push(createInlineNode(inlineType('hard_break'), {}));\n\t\t\treturn;\n\t\tcase 'code':\n\t\t\tout.push(createTextNode(node.literal, withMark(marks, ctx, 'code')));\n\t\t\treturn;\n\t\tcase 'link': {\n\t\t\tconst attrs: Record<string, string> = { href: node.href };\n\t\t\tif (node.title) attrs.title = node.title;\n\t\t\tflatten(node.children, withMark(marks, ctx, 'link', attrs), ctx, out);\n\t\t\treturn;\n\t\t}\n\t\tcase 'image': {\n\t\t\tpushImage(node, marks, ctx, out);\n\t\t\treturn;\n\t\t}\n\t\tcase 'inline_node':\n\t\t\tout.push(\n\t\t\t\tcreateInlineNode(inlineType(node.inlineType) as InlineTypeName, node.attrs, [...marks]),\n\t\t\t);\n\t\t\treturn;\n\t\tcase 'html_inline':\n\t\t\tpushInlineHtml(node.literal, marks, ctx, out);\n\t\t\treturn;\n\t\tdefault: {\n\t\t\tconst markName: string | undefined = KIND_TO_MARK[node.type];\n\t\t\tif (markName) {\n\t\t\t\tflatten(node.children, withMark(marks, ctx, markName), ctx, out);\n\t\t\t} else {\n\t\t\t\tflatten(node.children, marks, ctx, out);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** Returns `marks` plus a new mark of `type` (with attrs), if the schema allows it. */\nfunction withMark(\n\tmarks: readonly Mark[],\n\tctx: ParseContext,\n\ttype: string,\n\tattrs?: Record<string, string | number | boolean>,\n): Mark[] {\n\tif (!markAllowed(ctx, type)) return [...marks];\n\tconst mark: Mark = attrs ? { type: markType(type), attrs } : { type: markType(type) };\n\treturn [...marks, mark];\n}\n\n/** Emits an inline image node, or degrades to alt text when unavailable. */\nfunction pushImage(\n\tnode: InlineAstNode,\n\tmarks: readonly Mark[],\n\tctx: ParseContext,\n\tout: (TextNode | InlineNode)[],\n): void {\n\tconst hasInlineImage: boolean =\n\t\t!ctx.registry || ctx.registry.getInlineNodeSpec('image_inline') !== undefined;\n\tif (hasInlineImage) {\n\t\tconst attrs: Record<string, string> = { src: node.href, alt: node.alt };\n\t\tif (node.title) attrs.title = node.title;\n\t\t// Carry the surrounding marks (e.g. a `link` from `[](url)`) so a\n\t\t// linked inline image keeps its link instead of silently dropping it.\n\t\tout.push(createInlineNode(inlineType('image_inline'), attrs, [...marks]));\n\t\treturn;\n\t}\n\tif (node.alt) out.push(createTextNode(node.alt, [...marks]));\n}\n\n/** Re-parses an inline-HTML fallback run via the HTML parser to recover marks. */\nfunction pushInlineHtml(\n\thtml: string,\n\tmarks: readonly Mark[],\n\tctx: ParseContext,\n\tout: (TextNode | InlineNode)[],\n): void {\n\t// The HTML parser treats a lone <br> in a paragraph as an empty-block\n\t// placeholder. Here the paragraph is synthetic, so it is a real hard break.\n\tconst hardBreakAllowed: boolean =\n\t\t!ctx.registry || ctx.registry.getInlineNodeSpec('hard_break') !== undefined;\n\tif (hardBreakAllowed && /^<br\\s*\\/?>$/i.test(html.trim())) {\n\t\tout.push(createInlineNode(inlineType('hard_break'), {}, [...marks]));\n\t\treturn;\n\t}\n\tconst doc = parseHTMLToDocument(`<p>${html}</p>`, resolveMarkdownHTMLRegistry(ctx.registry));\n\tconst block = doc.children[0];\n\tif (!block) return;\n\tlet emitted = false;\n\tfor (const child of block.children) {\n\t\tif ('text' in child && child.text !== '') {\n\t\t\tout.push(createTextNode(child.text, mergeMarks(marks, child.marks)));\n\t\t\temitted = true;\n\t\t} else if ('inlineType' in child) {\n\t\t\tout.push(createInlineNode(child.inlineType, child.attrs, mergeMarks(marks, child.marks)));\n\t\t\temitted = true;\n\t\t}\n\t}\n\t// An unknown/empty HTML element has no baseline model representation. Keep\n\t// it literal on registry-free calls instead of silently dropping input.\n\tif (!emitted && !ctx.registry) out.push(createTextNode(html, [...marks]));\n}\n\n/** Merges two mark lists, de-duplicating by type. */\nfunction mergeMarks(outer: readonly Mark[], inner: readonly Mark[]): Mark[] {\n\tconst result: Mark[] = [...outer];\n\tfor (const mark of inner) {\n\t\tif (!result.some((m) => m.type === mark.type)) result.push(mark);\n\t}\n\treturn result;\n}\n\n/** Merges adjacent text nodes that share an identical mark set. */\nfunction mergeAdjacentText(nodes: readonly (TextNode | InlineNode)[]): (TextNode | InlineNode)[] {\n\tconst result: (TextNode | InlineNode)[] = [];\n\tfor (const node of nodes) {\n\t\tconst prev = result[result.length - 1];\n\t\tif ('text' in node && prev && 'text' in prev && sameMarkTypes(prev.marks, node.marks)) {\n\t\t\tresult[result.length - 1] = { type: 'text', text: prev.text + node.text, marks: prev.marks };\n\t\t} else {\n\t\t\tresult.push(node);\n\t\t}\n\t}\n\treturn result;\n}\n\n/** Structural equality of two mark lists (type + attrs, order-independent). */\nfunction sameMarkTypes(a: readonly Mark[], b: readonly Mark[]): boolean {\n\tif (a.length !== b.length) return false;\n\tconst key = (m: Mark): string => `${m.type}:${JSON.stringify(m.attrs ?? {})}`;\n\tconst setB = new Set(b.map(key));\n\treturn a.every((m) => setB.has(key(m)));\n}\n\n/** Parses inline Markdown into text/inline nodes (linear-time, ReDoS-safe). */\nexport function parseInline(text: string, ctx: ParseContext): (TextNode | InlineNode)[] {\n\tconst ast: InlineAstNode[] = new InlineParser(text, ctx).parse();\n\tif (ctx.opts.gfm) applyGfmEmailAutolinks(ast);\n\tconst out: (TextNode | InlineNode)[] = [];\n\tflatten(ast, [], ctx, out);\n\tconst merged: (TextNode | InlineNode)[] = mergeAdjacentText(out);\n\treturn merged.length > 0 ? merged : [createTextNode('')];\n}\n","/**\n * Resolved parse options and the context threaded through the Markdown parser.\n * Mirrors `MarkdownContext.ts` on the serialize side.\n */\n\nimport type { SchemaRegistry } from '../../model/SchemaRegistry.js';\nimport type { MarkdownParseOptions, MarkdownSyntaxExtension } from '../MarkdownTypes.js';\n\n/** Fully-resolved parse options (no `undefined`). */\nexport interface ResolvedParseOptions {\n\treadonly flavor: 'commonmark' | 'gfm';\n\treadonly gfm: boolean;\n\treadonly extendedInlineSyntax: boolean;\n\treadonly htmlFallback: boolean;\n\treadonly syntaxExtensions: readonly MarkdownSyntaxExtension[];\n}\n\n/** Internal context shared by the block and inline parsers. */\nexport interface ParseContext {\n\treadonly registry?: SchemaRegistry;\n\treadonly opts: ResolvedParseOptions;\n\t/** Link reference definitions collected from the source (`[ref]: url \"title\"`). */\n\treadonly linkRefs: ReadonlyMap<string, { readonly href: string; readonly title?: string }>;\n}\n\n/** Resolves user-facing parse options into a complete settings object with defaults. */\nexport function resolveParseOptions(options?: MarkdownParseOptions): ResolvedParseOptions {\n\tconst flavor: 'commonmark' | 'gfm' = options?.flavor ?? 'gfm';\n\treturn {\n\t\tflavor,\n\t\tgfm: flavor === 'gfm',\n\t\textendedInlineSyntax: options?.extendedInlineSyntax ?? false,\n\t\thtmlFallback: options?.htmlFallback !== false,\n\t\tsyntaxExtensions: options?.syntaxExtensions ?? [],\n\t};\n}\n","/**\n * MarkdownParser: converts a Markdown string into an immutable Document.\n *\n * Mirrors `DocumentParser.ts` (HTML) but is reached **only** via dynamic\n * `import()` so it stays code-split out of the core bundle (D13). The grammar is\n * centralized (CommonMark is a global, context-sensitive grammar that cannot be\n * decomposed into per-spec rules, D4); tokens map to schema node types by name\n * through the registry. Raw HTML blocks delegate to `parseHTMLToDocument` so the\n * superset round-trip stays lossless (D3). Plugin syntax (`$...$`) is contributed\n * via `options.syntaxExtensions`, never hard-coded here.\n */\n\nimport { hoistDisallowedBlocks } from '../model/ContentModel.js';\nimport type { BlockNode, Document, InlineNode, TextNode } from '../model/Document.js';\nimport {\n\tcreateBlockNode,\n\tcreateDocument,\n\tcreateTextNode,\n\tisInlineNode,\n} from '../model/Document.js';\nimport type { SchemaRegistry } from '../model/SchemaRegistry.js';\nimport { type NodeTypeName, nodeType } from '../model/TypeBrands.js';\nimport { parseHTMLToDocument } from './DocumentParser.js';\nimport type { MarkdownParseOptions } from './MarkdownTypes.js';\nimport type { BlockToken } from './markdown/BlockTokenizer.js';\nimport { tokenizeBlocks } from './markdown/BlockTokenizer.js';\nimport type { ColumnAlign } from './markdown/GfmTableParser.js';\nimport { parseInline } from './markdown/InlineTokenizer.js';\nimport { extractLinkReferences } from './markdown/LinkReferenceExtractor.js';\nimport { resolveMarkdownHTMLRegistry } from './markdown/MarkdownHTMLRegistry.js';\nimport { type ParseContext, resolveParseOptions } from './markdown/MarkdownParseContext.js';\n\n/** Parses a Markdown string into a Document. */\nexport function parseMarkdownToDocument(\n\tmarkdown: string,\n\tregistry?: SchemaRegistry,\n\toptions?: MarkdownParseOptions,\n): Document {\n\tconst normalized: string = markdown.replace(/\\r\\n?/g, '\\n');\n\tconst { source, linkRefs } = extractLinkReferences(normalized);\n\n\tconst ctx: ParseContext = {\n\t\tregistry,\n\t\topts: resolveParseOptions(options),\n\t\tlinkRefs,\n\t};\n\n\tconst tokens: BlockToken[] = tokenizeBlocks(source, ctx.opts.gfm, ctx.opts.syntaxExtensions);\n\tconst blocks: BlockNode[] = tokensToBlocks(tokens, ctx);\n\tif (blocks.length === 0) return createDocument();\n\tconst doc: Document = createDocument(blocks);\n\t// A container item can only hold the block types its schema allows; an\n\t// importer that nested (say) a table under an item is repaired by hoisting the\n\t// disallowed block up to a valid ancestor (#194).\n\treturn registry ? hoistDisallowedBlocks(doc, registry) : doc;\n}\n\n/** Converts a list of block tokens into Document block nodes. */\nfunction tokensToBlocks(tokens: readonly BlockToken[], ctx: ParseContext): BlockNode[] {\n\tconst blocks: BlockNode[] = [];\n\tfor (const token of tokens) {\n\t\tappendToken(token, blocks, ctx);\n\t}\n\treturn blocks;\n}\n\n/** Appends the block node(s) produced by a single token. */\nfunction appendToken(token: BlockToken, blocks: BlockNode[], ctx: ParseContext): void {\n\tswitch (token.type) {\n\t\tcase 'heading':\n\t\t\tblocks.push(makeBlock('heading', parseInline(token.text, ctx), ctx, { level: token.level }));\n\t\t\treturn;\n\t\tcase 'paragraph': {\n\t\t\tconst inline: (TextNode | InlineNode)[] = parseInline(token.text, ctx);\n\t\t\tconst image: BlockNode | null = promoteStandaloneImage(inline, ctx);\n\t\t\tblocks.push(image ?? makeBlock('paragraph', inline, ctx));\n\t\t\treturn;\n\t\t}\n\t\tcase 'table':\n\t\t\tblocks.push(buildTable(token.aligns, token.header, token.rows, ctx));\n\t\t\treturn;\n\t\tcase 'extension_block':\n\t\t\tblocks.push(makeBlock(token.nodeType, [createTextNode('')], ctx, token.attrs));\n\t\t\treturn;\n\t\tcase 'code_block':\n\t\t\tblocks.push(\n\t\t\t\tmakeBlock('code_block', [createTextNode(token.code)], ctx, { language: token.language }),\n\t\t\t);\n\t\t\treturn;\n\t\tcase 'hr':\n\t\t\tblocks.push(makeBlock('horizontal_rule', [createTextNode('')], ctx));\n\t\t\treturn;\n\t\tcase 'list_item': {\n\t\t\tconst attrs: Record<string, string | number | boolean> = {\n\t\t\t\tlistType: token.listType,\n\t\t\t\tindent: token.indent,\n\t\t\t\tchecked: token.checked,\n\t\t\t};\n\t\t\tif (token.children && token.children.length > 0) {\n\t\t\t\t// Multi-block item (#194): a container whose children are blocks.\n\t\t\t\tconst children: BlockNode[] = tokensToBlocks(token.children, ctx);\n\t\t\t\tif (ctx.registry && !ctx.registry.getNodeSpec('list_item')) {\n\t\t\t\t\t// Type unavailable: degrade to the children as top-level blocks.\n\t\t\t\t\tblocks.push(...children);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tblocks.push(createBlockNode(nodeType('list_item'), children, undefined, attrs));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tblocks.push(makeBlock('list_item', parseInline(token.text, ctx), ctx, attrs));\n\t\t\treturn;\n\t\t}\n\t\tcase 'blockquote': {\n\t\t\tconst children: BlockNode[] = tokensToBlocks(token.children, ctx);\n\t\t\tif (children.length === 0) {\n\t\t\t\tchildren.push(createBlockNode(nodeType('paragraph'), [createTextNode('')]));\n\t\t\t}\n\t\t\tblocks.push(makeContainer('blockquote', children, ctx));\n\t\t\treturn;\n\t\t}\n\t\tcase 'html':\n\t\t\t// A comment-only block produces no content (and must not leave a stray\n\t\t\t// empty paragraph between blocks, e.g. two lists split by `<!-- -->`).\n\t\t\tif (isCommentOnly(token.html)) return;\n\t\t\tfor (const block of parseHTMLToDocument(token.html, resolveMarkdownHTMLRegistry(ctx.registry))\n\t\t\t\t.children) {\n\t\t\t\tblocks.push(block);\n\t\t\t}\n\t\t\treturn;\n\t}\n}\n\n/** Whether an HTML block consists solely of comments (linear manual scan). */\nfunction isCommentOnly(html: string): boolean {\n\tlet rest: string = html.trim();\n\twhile (rest.startsWith('<!--')) {\n\t\tconst end: number = rest.indexOf('-->');\n\t\tif (end === -1) return false;\n\t\trest = rest.slice(end + 3).trim();\n\t}\n\treturn rest === '';\n}\n\n/** Builds a leaf block, falling back to a paragraph if the type is unavailable. */\nfunction makeBlock(\n\ttype: string,\n\tchildren: readonly (TextNode | InlineNode)[],\n\tctx: ParseContext,\n\tattrs?: Record<string, string | number | boolean>,\n): BlockNode {\n\tconst resolved: NodeTypeName = resolveType(type, ctx);\n\tconst childList: readonly (TextNode | InlineNode)[] =\n\t\tchildren.length > 0 ? children : [createTextNode('')];\n\treturn createBlockNode(\n\t\tresolved,\n\t\tchildList,\n\t\tundefined,\n\t\tattrs && resolved === nodeType(type) ? attrs : undefined,\n\t);\n}\n\n/** Builds a container block (block children), falling back to a paragraph if unavailable. */\nfunction makeContainer(type: string, children: readonly BlockNode[], ctx: ParseContext): BlockNode {\n\tconst resolved: NodeTypeName = resolveType(type, ctx);\n\tif (resolved !== nodeType(type)) {\n\t\t// Type unavailable: degrade to the container's children inline (best effort).\n\t\treturn children[0] ?? createBlockNode(nodeType('paragraph'), [createTextNode('')]);\n\t}\n\treturn createBlockNode(resolved, children);\n}\n\n/** Resolves a desired node type against the registry, falling back to paragraph. */\nfunction resolveType(type: string, ctx: ParseContext): NodeTypeName {\n\tif (ctx.registry && !ctx.registry.getNodeSpec(type)) return nodeType('paragraph');\n\treturn nodeType(type);\n}\n\n/**\n * Promotes a paragraph holding only a single inline image to a block `image`\n * node, matching the serializer (a standalone-image line becomes a block image).\n * Returns null when the paragraph is not a lone image or `image` is unavailable.\n * A marked image (e.g. a linked `[](url)`) is never promoted: the\n * block `image` node cannot hold a mark, so promotion would drop the link.\n */\nfunction promoteStandaloneImage(\n\tinline: readonly (TextNode | InlineNode)[],\n\tctx: ParseContext,\n): BlockNode | null {\n\tif (inline.length !== 1) return null;\n\tconst only = inline[0];\n\tif (!only || !isInlineNode(only) || only.inlineType !== 'image_inline') return null;\n\tif (only.marks.length > 0) return null;\n\tif (ctx.registry && !ctx.registry.getNodeSpec('image')) return null;\n\n\tconst attrs: Record<string, string | number | boolean> = {\n\t\tsrc: String(only.attrs.src ?? ''),\n\t\talt: String(only.attrs.alt ?? ''),\n\t\talign: 'center',\n\t};\n\tif (only.attrs.title) attrs.title = String(only.attrs.title);\n\treturn createBlockNode(nodeType('image'), [createTextNode('')], undefined, attrs);\n}\n\n/** Builds a GFM table node (table → table_row → table_cell → paragraph). */\nfunction buildTable(\n\taligns: readonly ColumnAlign[],\n\theader: readonly string[],\n\trows: readonly (readonly string[])[],\n\tctx: ParseContext,\n): BlockNode {\n\tif (ctx.registry && !ctx.registry.getNodeSpec('table')) {\n\t\t// No table support: degrade to a paragraph per row so content is never lost.\n\t\tconst lines: string = [header, ...rows].map((r) => r.join(' | ')).join('\\n');\n\t\treturn makeBlock('paragraph', parseInline(lines, ctx), ctx);\n\t}\n\n\tconst buildRow = (cells: readonly string[]): BlockNode => {\n\t\tconst tableCells: BlockNode[] = cells.map((cell, col) => {\n\t\t\tconst align: ColumnAlign = aligns[col] ?? null;\n\t\t\tconst paragraph: BlockNode = createBlockNode(\n\t\t\t\tnodeType('paragraph'),\n\t\t\t\tparseInline(cell, ctx),\n\t\t\t\tundefined,\n\t\t\t\talign ? { align } : undefined,\n\t\t\t);\n\t\t\treturn createBlockNode(nodeType('table_cell'), [paragraph]);\n\t\t});\n\t\treturn createBlockNode(nodeType('table_row'), tableCells);\n\t};\n\n\t// GFM normalizes every body row to the header's column count: overlong rows\n\t// drop their extra cells, short rows are padded with empty cells. Keeping rows\n\t// ragged would break the per-column model invariant the table tools rely on\n\t// (column insert/delete and cell navigation index by a fixed colIndex per row).\n\tconst columnCount: number = header.length;\n\tconst normalizeRow = (cells: readonly string[]): readonly string[] => {\n\t\tif (cells.length === columnCount) return cells;\n\t\tconst normalized: string[] = cells.slice(0, columnCount);\n\t\twhile (normalized.length < columnCount) normalized.push('');\n\t\treturn normalized;\n\t};\n\n\tconst tableRows: BlockNode[] = [\n\t\tbuildRow(header),\n\t\t...rows.map((row) => buildRow(normalizeRow(row))),\n\t];\n\treturn createBlockNode(nodeType('table'), tableRows);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MarkdownParser-Dp62mfUq.mjs","names":[],"sources":["../../src/serialization/markdown/InlineAst.ts","../../src/serialization/markdown/GfmAutolink.ts","../../src/serialization/markdown/LinkReferenceExtractor.ts","../../src/serialization/markdown/InlineTokenizer.ts","../../src/serialization/markdown/MarkdownParseContext.ts","../../src/serialization/MarkdownParser.ts"],"sourcesContent":["/**\n * Inline AST node model + linked-list helpers shared by the inline parser.\n *\n * A doubly-linked list of nodes is used during parsing so the emphasis\n * delimiter-stack algorithm runs in linear time (no array splicing), which is\n * the ReDoS-safety guarantee for untrusted pasted Markdown (D1).\n */\n\n/** Inline AST node kind. */\nexport type InlineKind =\n\t| 'text'\n\t| 'code'\n\t| 'softbreak'\n\t| 'hardbreak'\n\t| 'emph'\n\t| 'strong'\n\t| 'strike'\n\t| 'highlight'\n\t| 'sup'\n\t| 'sub'\n\t| 'link'\n\t| 'image'\n\t| 'inline_node'\n\t| 'html_inline';\n\n/** A single inline AST node (mutable; linked into a sibling list). */\nexport interface InlineAstNode {\n\ttype: InlineKind;\n\t/** Literal text for `text` / `code` / `html_inline`. */\n\tliteral: string;\n\t/** Container children (`emph`, `strong`, `link`, …). */\n\tchildren: InlineAstNode[];\n\t/** Link/image destination + title. */\n\thref: string;\n\ttitle: string;\n\t/** Image alt text (plain). */\n\talt: string;\n\t/** Generic inline-node passthrough (plugin extensions, e.g. math). */\n\tinlineType: string;\n\tattrs: Record<string, string | number | boolean>;\n\tprev: InlineAstNode | null;\n\tnext: InlineAstNode | null;\n}\n\n/** Creates a fresh inline AST node of the given kind. */\nexport function makeNode(type: InlineKind, literal = ''): InlineAstNode {\n\treturn {\n\t\ttype,\n\t\tliteral,\n\t\tchildren: [],\n\t\thref: '',\n\t\ttitle: '',\n\t\talt: '',\n\t\tinlineType: '',\n\t\tattrs: {},\n\t\tprev: null,\n\t\tnext: null,\n\t};\n}\n\n/** Appends `node` after `target` in the sibling linked list. */\nexport function insertAfter(target: InlineAstNode, node: InlineAstNode): void {\n\tnode.prev = target;\n\tnode.next = target.next;\n\tif (target.next) target.next.prev = node;\n\ttarget.next = node;\n}\n\n/** Removes `node` from its sibling linked list. */\nexport function unlink(node: InlineAstNode): void {\n\tif (node.prev) node.prev.next = node.next;\n\tif (node.next) node.next.prev = node.prev;\n\tnode.prev = null;\n\tnode.next = null;\n}\n","/**\n * GFM extended autolinks: `www.`, `http(s)://`, `mailto:`/`xmpp:`, bare emails.\n *\n * Implements the GFM \"autolinks (extension)\" rules rather than a naive URL\n * regex: valid-domain checks (at least one period, no underscore in the last\n * two segments), trailing-punctuation trimming, closing-paren balancing, and\n * trailing entity-reference stripping. Two integration points:\n *\n * - `matchGfmAutolinkAt` runs inside the inline parser's dispatch and scans the\n * raw source. Matching before emphasis processing keeps URLs with `_` / `~`\n * in their path intact instead of fragmenting them into delimiter runs.\n * - `applyGfmEmailAutolinks` is a post-pass over the resolved inline AST for\n * bare `user@host.tld` emails: the local part may contain delimiter\n * characters, so the surrounding text is only whole again after emphasis\n * resolution has turned unused delimiters back into plain text.\n *\n * Every scan here is a bounded forward/backward walk (linear time, D1).\n */\n\nimport { type InlineAstNode, makeNode } from './InlineAst.js';\n\n/** A matched GFM autolink: destination, display text, and source length. */\nexport interface GfmAutolinkMatch {\n\treadonly href: string;\n\treadonly display: string;\n\treadonly length: number;\n}\n\n/** Trailing characters GFM never counts as part of an autolink. */\nconst TRIMMABLE_PUNCTUATION = '?!.,:*_~';\n\n/** Characters that may precede an autolink (besides start-of-text). */\nconst BOUNDARY = /[\\s*_~(]/;\n\n/** One domain segment: alphanumerics, underscores, hyphens. */\nconst DOMAIN_SEGMENT = /^[a-zA-Z0-9_-]+$/;\n\n/** Bare email form (sticky, for in-source scans after a `mailto:`/`xmpp:` scheme). */\nconst EMAIL_AT = /[a-zA-Z0-9.+_-]+@[a-zA-Z0-9_-]+(?:\\.[a-zA-Z0-9_-]+)+/y;\n\nconst EMAIL_LOCAL_CHAR = /[a-zA-Z0-9.+_-]/;\nconst EMAIL_DOMAIN_CHAR = /[a-zA-Z0-9_-]/;\n\n/** Cheap pre-check: whether `pos` could start a GFM autolink (boundary + prefix). */\nexport function isGfmAutolinkCandidate(text: string, pos: number): boolean {\n\tconst ch: string = text[pos] ?? '';\n\tif (ch !== 'h' && ch !== 'w' && ch !== 'm' && ch !== 'x' && ch !== 'f') return false;\n\tif (pos > 0 && !BOUNDARY.test(text[pos - 1] ?? '')) return false;\n\treturn (\n\t\ttext.startsWith('http://', pos) ||\n\t\ttext.startsWith('https://', pos) ||\n\t\ttext.startsWith('ftp://', pos) ||\n\t\ttext.startsWith('www.', pos) ||\n\t\ttext.startsWith('mailto:', pos) ||\n\t\ttext.startsWith('xmpp:', pos)\n\t);\n}\n\n/** Matches a GFM autolink at `pos` (boundary included in the check), or null. */\nexport function matchGfmAutolinkAt(text: string, pos: number): GfmAutolinkMatch | null {\n\tif (!isGfmAutolinkCandidate(text, pos)) return null;\n\tif (\n\t\ttext.startsWith('http://', pos) ||\n\t\ttext.startsWith('https://', pos) ||\n\t\ttext.startsWith('ftp://', pos)\n\t) {\n\t\treturn matchUrl(text, pos);\n\t}\n\tif (text.startsWith('www.', pos)) return matchWww(text, pos);\n\treturn matchProtocolEmail(text, pos);\n}\n\n/** The raw autolink candidate at `pos`: the trimmed run up to whitespace or `<`. */\nfunction linkCandidate(text: string, pos: number): string {\n\tlet end: number = pos;\n\twhile (end < text.length) {\n\t\tconst ch: string = text[end] ?? '';\n\t\tif (ch === '<' || /\\s/.test(ch)) break;\n\t\tend++;\n\t}\n\treturn trimTrailing(text.slice(pos, end));\n}\n\nfunction matchUrl(text: string, pos: number): GfmAutolinkMatch | null {\n\tconst candidate: string = linkCandidate(text, pos);\n\tconst schemeLength: number = text.startsWith('https://', pos)\n\t\t? 8\n\t\t: text.startsWith('http://', pos)\n\t\t\t? 7\n\t\t\t: 6;\n\tif (!isValidDomain(hostOf(candidate.slice(schemeLength)))) return null;\n\treturn { href: candidate, display: candidate, length: candidate.length };\n}\n\nfunction matchWww(text: string, pos: number): GfmAutolinkMatch | null {\n\tconst candidate: string = linkCandidate(text, pos);\n\tif (!isValidDomain(hostOf(candidate))) return null;\n\treturn { href: `http://${candidate}`, display: candidate, length: candidate.length };\n}\n\n/** `mailto:` / `xmpp:` + email; the scheme is part of both href and display. */\nfunction matchProtocolEmail(text: string, pos: number): GfmAutolinkMatch | null {\n\tconst schemeLength: number = text.startsWith('mailto:', pos) ? 7 : 5;\n\tEMAIL_AT.lastIndex = pos + schemeLength;\n\tconst match: RegExpExecArray | null = EMAIL_AT.exec(text);\n\tconst email: string | undefined = match?.[0];\n\tif (!email || !isValidEmailEnd(email)) return null;\n\tconst full: string = text.slice(pos, pos + schemeLength + email.length);\n\treturn { href: full, display: full, length: full.length };\n}\n\n/** The host part of a candidate (up to the first `/ ? # :`). */\nfunction hostOf(rest: string): string {\n\tconst end: number = rest.search(/[/?#:]/);\n\treturn end === -1 ? rest : rest.slice(0, end);\n}\n\n/** GFM valid domain: >= 2 segments, no underscore in the last two segments. */\nfunction isValidDomain(domain: string): boolean {\n\tconst segments: string[] = domain.split('.');\n\tif (segments.length < 2) return false;\n\tif (!segments.every((segment) => DOMAIN_SEGMENT.test(segment))) return false;\n\treturn !segments.slice(-2).some((segment) => segment.includes('_'));\n}\n\n/** GFM email rule: the last character must not be `-` or `_`. */\nfunction isValidEmailEnd(email: string): boolean {\n\treturn !email.endsWith('-') && !email.endsWith('_');\n}\n\n/**\n * GFM trailing trims, applied until stable: trailing punctuation, an excess\n * closing paren (only while the candidate holds more `)` than `(`), and a\n * trailing entity reference (`©`). Each pass removes at least one\n * character via a bounded backward scan, so the whole trim is linear.\n */\nfunction trimTrailing(candidate: string): string {\n\tlet open = 0;\n\tlet close = 0;\n\tfor (const ch of candidate) {\n\t\tif (ch === '(') open++;\n\t\telse if (ch === ')') close++;\n\t}\n\tlet end: number = candidate.length;\n\twhile (end > 0) {\n\t\tconst ch: string = candidate[end - 1] ?? '';\n\t\tif (TRIMMABLE_PUNCTUATION.includes(ch)) {\n\t\t\tend--;\n\t\t\tcontinue;\n\t\t}\n\t\tif (ch === ')' && close > open) {\n\t\t\tend--;\n\t\t\tclose--;\n\t\t\tcontinue;\n\t\t}\n\t\tif (ch === ';') {\n\t\t\tlet j: number = end - 2;\n\t\t\twhile (j > 0 && /[a-zA-Z0-9]/.test(candidate[j] ?? '')) j--;\n\t\t\tif (candidate[j] === '&' && j < end - 2) {\n\t\t\t\tend = j;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t}\n\treturn candidate.slice(0, end);\n}\n\n/**\n * Post-pass over the resolved inline AST: turns bare `user@host.tld` emails in\n * plain text into `mailto:` links. Adjacent text nodes (including leftover\n * delimiter runs) are merged first so a local part like `a_b` is scanned as one\n * string. Subtrees that must not be linkified (links, images, code, raw HTML,\n * plugin inline nodes) are skipped.\n */\nexport function applyGfmEmailAutolinks(nodes: InlineAstNode[]): void {\n\tmergeAdjacentAstText(nodes);\n\tfor (let i = 0; i < nodes.length; i++) {\n\t\tconst node: InlineAstNode | undefined = nodes[i];\n\t\tif (!node || skipsEmailScan(node.type)) continue;\n\t\tif (node.type === 'text') {\n\t\t\tconst split: InlineAstNode[] | null = splitEmails(node.literal);\n\t\t\tif (split) {\n\t\t\t\tnodes.splice(i, 1, ...split);\n\t\t\t\ti += split.length - 1;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (node.children.length > 0) applyGfmEmailAutolinks(node.children);\n\t}\n}\n\nfunction skipsEmailScan(type: InlineAstNode['type']): boolean {\n\treturn (\n\t\ttype === 'link' ||\n\t\ttype === 'image' ||\n\t\ttype === 'code' ||\n\t\ttype === 'html_inline' ||\n\t\ttype === 'inline_node'\n\t);\n}\n\n/**\n * Merges adjacent text siblings in place (delimiter leftovers are text too).\n * Single compaction pass — no per-merge splice, which would go quadratic on a\n * long run of unmatched delimiters (D1).\n */\nfunction mergeAdjacentAstText(nodes: InlineAstNode[]): void {\n\tlet write = 0;\n\tfor (const node of nodes) {\n\t\tconst prev: InlineAstNode | undefined = write > 0 ? nodes[write - 1] : undefined;\n\t\tif (prev && prev.type === 'text' && node.type === 'text') {\n\t\t\tprev.literal += node.literal;\n\t\t\tcontinue;\n\t\t}\n\t\tnodes[write] = node;\n\t\twrite++;\n\t}\n\tnodes.length = write;\n}\n\n/**\n * Splits a literal into text/link nodes around bare emails, or null if none.\n * Anchored on each `@` with bounded back/forward scans instead of a global\n * regex: a `X+@` pattern backtracks quadratically over long runs of local-part\n * characters (the ReDoS fuzz suite catches exactly that, D1).\n */\nfunction splitEmails(literal: string): InlineAstNode[] | null {\n\tconst out: InlineAstNode[] = [];\n\tlet cursor = 0;\n\tlet at: number = literal.indexOf('@');\n\twhile (at !== -1) {\n\t\tlet start: number = at;\n\t\twhile (start > cursor && EMAIL_LOCAL_CHAR.test(literal[start - 1] ?? '')) start--;\n\t\tlet end: number = at + 1;\n\t\twhile (\n\t\t\tend < literal.length &&\n\t\t\t(EMAIL_DOMAIN_CHAR.test(literal[end] ?? '') || literal[end] === '.')\n\t\t) {\n\t\t\tend++;\n\t\t}\n\t\t// Trailing periods are punctuation, not part of the address.\n\t\twhile (end > at + 1 && literal[end - 1] === '.') end--;\n\t\tconst email: string = literal.slice(start, end);\n\t\tif (start < at && isValidBareEmail(email, end - at - 1)) {\n\t\t\tif (start > cursor) out.push(makeNode('text', literal.slice(cursor, start)));\n\t\t\tconst link: InlineAstNode = makeNode('link');\n\t\t\tlink.href = `mailto:${email}`;\n\t\t\tlink.children = [makeNode('text', email)];\n\t\t\tout.push(link);\n\t\t\tcursor = end;\n\t\t\tat = literal.indexOf('@', end);\n\t\t\tcontinue;\n\t\t}\n\t\tat = literal.indexOf('@', at + 1);\n\t}\n\tif (out.length === 0) return null;\n\tif (cursor < literal.length) out.push(makeNode('text', literal.slice(cursor)));\n\treturn out;\n}\n\n/** Validates a scanned bare email: >= 2 domain segments, valid end character. */\nfunction isValidBareEmail(email: string, domainLength: number): boolean {\n\tif (domainLength <= 0) return false;\n\tconst domain: string = email.slice(email.indexOf('@') + 1);\n\tconst segments: string[] = domain.split('.');\n\tif (segments.length < 2) return false;\n\tif (segments.some((segment) => segment === '')) return false;\n\treturn isValidEmailEnd(email);\n}\n","/**\n * Link reference definition extraction (`[label]: dest \"title\"`).\n *\n * Runs as a pre-pass over the source before block tokenization, mirroring how\n * commonmark.js resolves references before inline parsing. CommonMark rules\n * honored here:\n *\n * - A definition never interrupts an open paragraph (a def-shaped line that\n * lazily continues a paragraph stays paragraph text).\n * - Def-shaped lines inside fenced code are code and survive verbatim.\n * - The destination may sit on the line after the label, and a title may sit\n * on its own following line (multi-line labels and multi-line titles are not\n * supported; such lines stay paragraph text, which never drops content).\n * - Titles honor backslash escapes; destinations and titles decode escapes and\n * entity references; labels match case-folded (Unicode, `ẞ` ↔ `SS`).\n * - A definition inside a blockquote is recognized too; the quote marker line\n * is kept so the (now empty) container survives.\n *\n * First definition of a label wins, as per spec.\n */\n\nimport { ATX_HEADING, THEMATIC_BREAK, matchFenceOpen } from './BlockTokenizer.js';\nimport { decodeEscapesAndEntities } from './MarkdownEntities.js';\n\n/** A link reference definition target. */\nexport interface LinkRef {\n\treadonly href: string;\n\treadonly title?: string;\n}\n\n/** `[label]:` at up to 3 spaces indent; the label may hold escaped brackets. */\nconst LABEL_LINE = /^ {0,3}\\[((?:\\\\.|[^\\\\[\\]])+)\\]:(.*)$/;\n\n/** One or more blockquote markers prefixing a line. */\nconst BLOCKQUOTE_PREFIX = /^( {0,3}(?:> ?)+)(.*)$/;\n\n/** A line holding nothing but a link title (for the title-on-next-line form). */\nconst TITLE_LINE =\n\t/^[ \\t]*(?:\"((?:\\\\.|[^\"\\\\])*)\"|'((?:\\\\.|[^'\\\\])*)'|\\(((?:\\\\.|[^()\\\\])*)\\))[ \\t]*$/;\n\nconst FENCE_CLOSE = /^ {0,3}(`{3,}|~{3,})[ \\t]*$/;\n\n/**\n * Case-folds a reference label for matching. `toUpperCase().toLowerCase()`\n * approximates Unicode case folding (it maps `ẞ`/`ß` and `SS` to the same\n * string), which plain `toLowerCase()` does not.\n */\nexport function foldReferenceLabel(label: string): string {\n\treturn label\n\t\t.trim()\n\t\t.replace(/[ \\t\\n]+/g, ' ')\n\t\t.toLowerCase()\n\t\t.toUpperCase()\n\t\t.toLowerCase();\n}\n\n/** Removes link reference definitions from the source and returns them as a map. */\nexport function extractLinkReferences(source: string): {\n\tsource: string;\n\tlinkRefs: Map<string, LinkRef>;\n} {\n\tconst linkRefs = new Map<string, LinkRef>();\n\tconst lines: string[] = source.split('\\n');\n\tconst kept: string[] = [];\n\tlet fence: { char: string; len: number } | null = null;\n\t// Whether a paragraph is open above the next line: a definition is only\n\t// recognized at a block boundary (document start, after a blank line, a\n\t// heading, a thematic break, a fence, or another definition).\n\tlet openParagraph = false;\n\tlet i = 0;\n\n\twhile (i < lines.length) {\n\t\tconst line: string = lines[i] ?? '';\n\n\t\tif (fence) {\n\t\t\tkept.push(line);\n\t\t\tconst close: RegExpMatchArray | null = line.match(FENCE_CLOSE);\n\t\t\tconst run: string | undefined = close?.[1];\n\t\t\tif (run && run[0] === fence.char && run.length >= fence.len) fence = null;\n\t\t\topenParagraph = false;\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\t\tconst open: RegExpMatchArray | null = matchFenceOpen(line);\n\t\tif (open?.[1]) {\n\t\t\tfence = { char: open[1][0] ?? '`', len: open[1].length };\n\t\t\tkept.push(line);\n\t\t\topenParagraph = false;\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst consumed: number = openParagraph ? 0 : tryDefinition(lines, i, '', linkRefs, kept);\n\t\tif (consumed > 0) {\n\t\t\ti += consumed;\n\t\t\tcontinue;\n\t\t}\n\n\t\tkept.push(line);\n\t\t// A blank line or a complete block (heading, thematic break) closes any open\n\t\t// paragraph; every other non-blank line keeps one open. Deliberate keep-bias:\n\t\t// a def-shaped line after, e.g., a table row or setext underline is preserved\n\t\t// as text rather than registered, which never drops content (D3).\n\t\topenParagraph = line.trim() !== '' && !ATX_HEADING.test(line) && !THEMATIC_BREAK.test(line);\n\t\ti++;\n\t}\n\treturn { source: kept.join('\\n'), linkRefs };\n}\n\n/**\n * Tries to consume a definition starting at `lines[start]`, registering it and\n * pushing any kept container-prefix line. Returns the number of source lines\n * consumed (0 = no definition here).\n */\nfunction tryDefinition(\n\tlines: readonly string[],\n\tstart: number,\n\tprefix: string,\n\tlinkRefs: Map<string, LinkRef>,\n\tkept: string[],\n): number {\n\tconst line: string = lines[start] ?? '';\n\n\tconst label: RegExpMatchArray | null = line.match(LABEL_LINE);\n\tif (!label) {\n\t\t// A definition nested in a blockquote: strip the marker(s) and retry once.\n\t\tif (prefix === '') {\n\t\t\tconst quoted: RegExpMatchArray | null = line.match(BLOCKQUOTE_PREFIX);\n\t\t\tif (quoted?.[1] && LABEL_LINE.test(quoted[2] ?? '')) {\n\t\t\t\tconst inner: string[] = [];\n\t\t\t\tconst consumed: number = tryDefinition([quoted[2] ?? ''], 0, quoted[1], linkRefs, inner);\n\t\t\t\tif (consumed > 0) {\n\t\t\t\t\t// Keep the bare marker so the blockquote container survives.\n\t\t\t\t\tkept.push(quoted[1].trimEnd());\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\n\tconst labelText: string = label[1] ?? '';\n\tlet rest: string = (label[2] ?? '').trim();\n\tlet consumed = 1;\n\n\t// Destination on the next line (only supported outside containers).\n\tif (rest === '' && prefix === '') {\n\t\tconst next: string = lines[start + consumed] ?? '';\n\t\tif (next.trim() === '') return 0;\n\t\trest = next.trim();\n\t\tconsumed++;\n\t}\n\tif (rest === '') return 0;\n\n\tconst dest = scanDestination(rest);\n\tif (!dest) return 0;\n\tconst afterDest: string = rest.slice(dest.end).trim();\n\n\tlet title: string | undefined;\n\tif (afterDest !== '') {\n\t\tconst inline: RegExpMatchArray | null = afterDest.match(TITLE_LINE);\n\t\tif (!inline) return 0; // Junk after the destination: not a definition.\n\t\ttitle = titleFrom(inline);\n\t} else if (prefix === '') {\n\t\t// Optional title on its own next line.\n\t\tconst next: string = lines[start + consumed] ?? '';\n\t\tconst nextTitle: RegExpMatchArray | null = next.match(TITLE_LINE);\n\t\tif (nextTitle) {\n\t\t\ttitle = titleFrom(nextTitle);\n\t\t\tconsumed++;\n\t\t}\n\t}\n\n\tconst key: string = foldReferenceLabel(labelText);\n\tif (key !== '' && !linkRefs.has(key)) {\n\t\tlinkRefs.set(key, title !== undefined ? { href: dest.href, title } : { href: dest.href });\n\t}\n\treturn consumed;\n}\n\nfunction titleFrom(match: RegExpMatchArray): string {\n\treturn decodeEscapesAndEntities(match[1] ?? match[2] ?? match[3] ?? '');\n}\n\n/** Scans a definition destination: `<angle>` or a bare paren-balanced run. */\nfunction scanDestination(rest: string): { href: string; end: number } | null {\n\tif (rest[0] === '<') {\n\t\tlet i = 1;\n\t\twhile (i < rest.length) {\n\t\t\tconst ch: string = rest[i] ?? '';\n\t\t\tif (ch === '\\\\') {\n\t\t\t\ti += 2;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (ch === '>') {\n\t\t\t\treturn { href: decodeEscapesAndEntities(rest.slice(1, i)), end: i + 1 };\n\t\t\t}\n\t\t\tif (ch === '<') return null;\n\t\t\ti++;\n\t\t}\n\t\treturn null;\n\t}\n\tlet depth = 0;\n\tlet i = 0;\n\twhile (i < rest.length) {\n\t\tconst ch: string = rest[i] ?? '';\n\t\tif (ch === ' ' || ch === '\\t') break;\n\t\tif (ch === '\\\\') {\n\t\t\ti += 2;\n\t\t\tcontinue;\n\t\t}\n\t\tif (ch === '(') depth++;\n\t\tif (ch === ')') {\n\t\t\tif (depth === 0) break;\n\t\t\tdepth--;\n\t\t}\n\t\ti++;\n\t}\n\tif (i === 0) return null;\n\treturn { href: decodeEscapesAndEntities(rest.slice(0, i)), end: i };\n}\n","/**\n * Inline Markdown parser: a linear-time, delimiter-stack tokenizer (the\n * CommonMark reference approach), producing an inline AST that is then flattened\n * into notectl's `(TextNode | InlineNode)[]` mark-set model.\n *\n * Linear-time and backtracking-free by construction: emphasis runs through a\n * doubly-linked delimiter stack, code spans / autolinks / inline HTML are\n * forward scans, and there is no catastrophic-backtracking regex. This is the\n * ReDoS-safety guarantee for untrusted pasted Markdown (D1).\n *\n * Marks with no Markdown form round-trip through inline HTML produced by the\n * serializer's fallback: a balanced `<u>…</u>` / `<span style>…</span>` run is\n * captured and re-parsed via the HTML parser, recovering the original mark (D3).\n */\n\nimport type { InlineNode, Mark, TextNode } from '../../model/Document.js';\nimport { createInlineNode, createTextNode } from '../../model/Document.js';\nimport { type InlineTypeName, inlineType, markType } from '../../model/TypeBrands.js';\nimport { parseHTMLToDocument } from '../DocumentParser.js';\nimport {\n\ttype GfmAutolinkMatch,\n\tapplyGfmEmailAutolinks,\n\tisGfmAutolinkCandidate,\n\tmatchGfmAutolinkAt,\n} from './GfmAutolink.js';\nimport { type InlineAstNode, type InlineKind, insertAfter, makeNode, unlink } from './InlineAst.js';\nimport { foldReferenceLabel } from './LinkReferenceExtractor.js';\nimport {\n\tdecodeEntitiesOnly,\n\tdecodeEntity,\n\tdecodeEscapesAndEntities,\n\tmatchEntityAt,\n} from './MarkdownEntities.js';\nimport { resolveMarkdownHTMLRegistry } from './MarkdownHTMLRegistry.js';\nimport type { ParseContext } from './MarkdownParseContext.js';\n\n// --- Delimiter / bracket stacks ---\n\ninterface Delimiter {\n\tcc: string;\n\tnumdelims: number;\n\torigdelims: number;\n\tnode: InlineAstNode;\n\tcanOpen: boolean;\n\tcanClose: boolean;\n\tprev: Delimiter | null;\n\tnext: Delimiter | null;\n}\n\ninterface Bracket {\n\tnode: InlineAstNode;\n\tprev: Bracket | null;\n\tprevDelim: Delimiter | null;\n\tindex: number;\n\timage: boolean;\n\tactive: boolean;\n}\n\n/** Upper bound on an inline link destination scan (ReDoS guard; real URLs are short). */\nconst MAX_DESTINATION_LENGTH = 8192;\n\n/** Unicode punctuation + symbols, per CommonMark 0.30+ flanking rules. */\nconst UNICODE_PUNCTUATION = /[\\p{P}\\p{S}]/u;\nconst ASCII_PUNCTUATION = /[!-/:-@[-`{-~]/;\nconst ESCAPABLE = /[!-/:-@[-`{-~]/;\nconst INLINE_HTML_TAGS: ReadonlySet<string> = new Set([\n\t'u',\n\t'sup',\n\t'sub',\n\t'span',\n\t'mark',\n\t'b',\n\t'i',\n\t'em',\n\t'strong',\n\t'code',\n\t's',\n\t'del',\n\t'small',\n\t'abbr',\n\t'kbd',\n\t'a',\n]);\n\nfunction isWhitespace(ch: string): boolean {\n\treturn ch === '' || /\\s/.test(ch);\n}\n\n/** Flanking punctuation test; ASCII fast path, Unicode classes beyond it. */\nfunction isPunct(ch: string): boolean {\n\tif (ch === '') return false;\n\tif (ch.charCodeAt(0) < 128) return ASCII_PUNCTUATION.test(ch);\n\treturn UNICODE_PUNCTUATION.test(ch);\n}\n\n/** The inline parser. One instance per inline string. */\nclass InlineParser {\n\tprivate pos = 0;\n\tprivate readonly text: string;\n\tprivate readonly len: number;\n\tprivate readonly ctx: ParseContext;\n\tprivate delimiters: Delimiter | null = null;\n\tprivate brackets: Bracket | null = null;\n\t/** Tail of the output sibling list, so self-linking tokens can extend it. */\n\tprivate tail: InlineAstNode | null = null;\n\n\tconstructor(text: string, ctx: ParseContext) {\n\t\tthis.text = text;\n\t\tthis.len = text.length;\n\t\tthis.ctx = ctx;\n\t}\n\n\t/** Parses the inline string into a list of top-level AST nodes. */\n\tparse(): InlineAstNode[] {\n\t\tconst root: InlineAstNode = makeNode('text');\n\t\tthis.tail = root;\n\n\t\twhile (this.pos < this.len) {\n\t\t\tconst node: InlineAstNode | null = this.parseToken();\n\t\t\tif (node) this.appendNode(node);\n\t\t}\n\n\t\tthis.processEmphasis(null);\n\t\treturn collectSiblings(root);\n\t}\n\n\t/** Links a freshly produced token at the tail of the sibling list. */\n\tprivate appendNode(node: InlineAstNode): void {\n\t\tif (this.tail) insertAfter(this.tail, node);\n\t\tthis.tail = node;\n\t}\n\n\t/** Dispatches on the current character, returning the produced node (already linked for delims). */\n\tprivate parseToken(): InlineAstNode | null {\n\t\tconst ch: string = this.text[this.pos] ?? '';\n\t\tswitch (ch) {\n\t\t\tcase '\\n':\n\t\t\t\treturn this.parseNewline();\n\t\t\tcase '\\\\':\n\t\t\t\treturn this.parseBackslash();\n\t\t\tcase '`':\n\t\t\t\treturn this.parseBackticks();\n\t\t\tcase '*':\n\t\t\tcase '_':\n\t\t\tcase '~':\n\t\t\tcase '=':\n\t\t\tcase '^':\n\t\t\t\treturn this.parseDelimiterRun(ch);\n\t\t\tcase '[':\n\t\t\t\treturn this.parseOpenBracket(false);\n\t\t\tcase '!':\n\t\t\t\tif (this.text[this.pos + 1] === '[') return this.parseOpenBracket(true);\n\t\t\t\tthis.pos++;\n\t\t\t\treturn makeNode('text', '!');\n\t\t\tcase ']':\n\t\t\t\treturn this.parseCloseBracket();\n\t\t\tcase '<':\n\t\t\t\treturn this.parseLessThan();\n\t\t\tcase '&':\n\t\t\t\treturn this.parseEntity();\n\t\t\tdefault: {\n\t\t\t\tconst auto: InlineAstNode | null = this.parseGfmAutolink();\n\t\t\t\tif (auto) return auto;\n\t\t\t\tconst ext: InlineAstNode | null = this.parseExtension();\n\t\t\t\tif (ext) return ext;\n\t\t\t\treturn this.parseString();\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Plain text up to the next significant character. */\n\tprivate parseString(): InlineAstNode {\n\t\tconst start: number = this.pos;\n\t\tthis.pos++;\n\t\twhile (this.pos < this.len) {\n\t\t\tconst ch: string = this.text[this.pos] ?? '';\n\t\t\tif ('\\n\\\\`*_~=^[]!<&'.includes(ch)) break;\n\t\t\tif (this.matchesGfmAutolinkStart()) break;\n\t\t\tif (this.matchesExtensionStart()) break;\n\t\t\tthis.pos++;\n\t\t}\n\t\treturn makeNode('text', this.text.slice(start, this.pos));\n\t}\n\n\t/**\n\t * GFM extended autolink (`www.`, `http(s)://`, `mailto:`/`xmpp:`) at the\n\t * current position, scanned on the raw source (#199). Skipped inside an open\n\t * bracket: explicit `[text](url)` syntax wins there, and a link must never\n\t * nest inside another link. Bare emails are handled in a post-pass instead\n\t * (see `applyGfmEmailAutolinks`).\n\t */\n\tprivate parseGfmAutolink(): InlineAstNode | null {\n\t\tif (!this.ctx.opts.gfm || this.brackets) return null;\n\t\tconst match: GfmAutolinkMatch | null = matchGfmAutolinkAt(this.text, this.pos);\n\t\tif (!match) return null;\n\t\tthis.pos += match.length;\n\t\tconst node: InlineAstNode = makeNode('link');\n\t\tnode.href = match.href;\n\t\tnode.children = [makeNode('text', match.display)];\n\t\treturn node;\n\t}\n\n\tprivate matchesGfmAutolinkStart(): boolean {\n\t\tif (!this.ctx.opts.gfm || this.brackets) return false;\n\t\treturn isGfmAutolinkCandidate(this.text, this.pos);\n\t}\n\n\t/** Plugin syntax extensions (e.g. formula `$...$`). */\n\tprivate parseExtension(): InlineAstNode | null {\n\t\tfor (const ext of this.ctx.opts.syntaxExtensions) {\n\t\t\tconst match = ext.matchInline?.(this.text, this.pos);\n\t\t\tif (match) {\n\t\t\t\tthis.pos += match.length;\n\t\t\t\tconst node: InlineAstNode = makeNode('inline_node');\n\t\t\t\tnode.inlineType = match.type;\n\t\t\t\tnode.attrs = match.attrs;\n\t\t\t\treturn node;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate matchesExtensionStart(): boolean {\n\t\tfor (const ext of this.ctx.opts.syntaxExtensions) {\n\t\t\tif (ext.matchInline?.(this.text, this.pos)) return true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * A newline produces a soft break, unless the line above ends in two or more\n\t * spaces: that is CommonMark's second hard-break form (#193). The trailing\n\t * whitespace lives in the previously emitted text node (the block tokenizer\n\t * preserves interior trailing spaces for exactly this check) and is stripped\n\t * either way — line-end whitespace is never content. The backslash form is\n\t * handled in `parseBackslash`.\n\t */\n\tprivate parseNewline(): InlineAstNode {\n\t\tthis.pos++;\n\t\t// Skip leading spaces of the next line.\n\t\twhile (this.text[this.pos] === ' ') this.pos++;\n\t\tconst prev: InlineAstNode | null = this.tail;\n\t\tif (prev && prev.type === 'text' && prev.literal.endsWith(' ')) {\n\t\t\tconst hard: boolean = prev.literal.endsWith(' ');\n\t\t\tprev.literal = prev.literal.replace(/[ \\t]+$/, '');\n\t\t\treturn makeNode(hard ? 'hardbreak' : 'softbreak');\n\t\t}\n\t\treturn makeNode('softbreak');\n\t}\n\n\tprivate parseBackslash(): InlineAstNode {\n\t\tthis.pos++;\n\t\tconst next: string = this.text[this.pos] ?? '';\n\t\tif (next === '\\n') {\n\t\t\tthis.pos++;\n\t\t\twhile (this.text[this.pos] === ' ') this.pos++;\n\t\t\treturn makeNode('hardbreak');\n\t\t}\n\t\tif (ESCAPABLE.test(next)) {\n\t\t\tthis.pos++;\n\t\t\treturn makeNode('text', next);\n\t\t}\n\t\treturn makeNode('text', '\\\\');\n\t}\n\n\t/** A backtick code span; falls back to literal backticks if unclosed. */\n\tprivate parseBackticks(): InlineAstNode {\n\t\tconst start: number = this.pos;\n\t\tlet count = 0;\n\t\twhile (this.text[this.pos] === '`') {\n\t\t\tcount++;\n\t\t\tthis.pos++;\n\t\t}\n\t\tconst fence: string = '`'.repeat(count);\n\t\tconst after: number = this.pos;\n\t\tlet search: number = after;\n\t\twhile (search < this.len) {\n\t\t\tconst idx: number = this.text.indexOf(fence, search);\n\t\t\tif (idx === -1) break;\n\t\t\t// Require an exact-length run (not part of a longer run).\n\t\t\tif (this.text[idx - 1] !== '`' && this.text[idx + count] !== '`') {\n\t\t\t\tlet content: string = this.text.slice(after, idx).replace(/\\n/g, ' ');\n\t\t\t\tif (\n\t\t\t\t\tcontent.length > 2 &&\n\t\t\t\t\tcontent.startsWith(' ') &&\n\t\t\t\t\tcontent.endsWith(' ') &&\n\t\t\t\t\tcontent.trim() !== ''\n\t\t\t\t) {\n\t\t\t\t\tcontent = content.slice(1, -1);\n\t\t\t\t}\n\t\t\t\tthis.pos = idx + count;\n\t\t\t\treturn makeNode('code', content);\n\t\t\t}\n\t\t\tsearch = idx + 1;\n\t\t}\n\t\t// No closer: treat the opening run as literal text.\n\t\tthis.pos = after;\n\t\treturn makeNode('text', this.text.slice(start, after));\n\t}\n\n\t/** A run of emphasis/extension delimiters; records a stack entry. */\n\tprivate parseDelimiterRun(cc: string): InlineAstNode {\n\t\tconst { numdelims, canOpen, canClose } = this.scanDelims(cc);\n\t\tconst node: InlineAstNode = makeNode('text', cc.repeat(numdelims));\n\t\tconst delim: Delimiter = {\n\t\t\tcc,\n\t\t\tnumdelims,\n\t\t\torigdelims: numdelims,\n\t\t\tnode,\n\t\t\tcanOpen,\n\t\t\tcanClose,\n\t\t\tprev: this.delimiters,\n\t\t\tnext: null,\n\t\t};\n\t\tif (this.delimiters) this.delimiters.next = delim;\n\t\tthis.delimiters = delim;\n\t\treturn node;\n\t}\n\n\t/** Counts a delimiter run and computes its flanking flags. */\n\tprivate scanDelims(cc: string): { numdelims: number; canOpen: boolean; canClose: boolean } {\n\t\tconst start: number = this.pos;\n\t\twhile (this.text[this.pos] === cc) this.pos++;\n\t\tconst numdelims: number = this.pos - start;\n\n\t\tconst before: string = start === 0 ? '' : (this.text[start - 1] ?? '');\n\t\tconst after: string = this.text[this.pos] ?? '';\n\t\tconst beforeWs: boolean = isWhitespace(before);\n\t\tconst afterWs: boolean = isWhitespace(after);\n\t\tconst beforePunct: boolean = isPunct(before);\n\t\tconst afterPunct: boolean = isPunct(after);\n\n\t\tconst leftFlanking: boolean = !afterWs && (!afterPunct || beforeWs || beforePunct);\n\t\tconst rightFlanking: boolean = !beforeWs && (!beforePunct || afterWs || afterPunct);\n\n\t\tlet canOpen: boolean;\n\t\tlet canClose: boolean;\n\t\tif (cc === '_') {\n\t\t\tcanOpen = leftFlanking && (!rightFlanking || beforePunct);\n\t\t\tcanClose = rightFlanking && (!leftFlanking || afterPunct);\n\t\t} else {\n\t\t\tcanOpen = leftFlanking;\n\t\t\tcanClose = rightFlanking;\n\t\t}\n\t\treturn { numdelims, canOpen, canClose };\n\t}\n\n\t/** `[` or `![`: push a bracket and emit its literal text node. */\n\tprivate parseOpenBracket(image: boolean): InlineAstNode {\n\t\tconst literal: string = image ? '![' : '[';\n\t\tthis.pos += literal.length;\n\t\tconst node: InlineAstNode = makeNode('text', literal);\n\t\tthis.brackets = {\n\t\t\tnode,\n\t\t\tprev: this.brackets,\n\t\t\tprevDelim: this.delimiters,\n\t\t\tindex: this.pos,\n\t\t\timage,\n\t\t\tactive: true,\n\t\t};\n\t\treturn node;\n\t}\n\n\t/** `]`: resolve a link/image against the open bracket, or emit literal `]`. */\n\tprivate parseCloseBracket(): InlineAstNode | null {\n\t\tthis.pos++;\n\t\tconst opener: Bracket | null = this.brackets;\n\t\tif (!opener || !opener.active) {\n\t\t\tif (opener) this.brackets = opener.prev;\n\t\t\treturn makeNode('text', ']');\n\t\t}\n\n\t\tconst dest: { href: string; title: string } | null = this.parseLinkTarget(opener);\n\t\tif (!dest) {\n\t\t\tthis.brackets = opener.prev;\n\t\t\treturn makeNode('text', ']');\n\t\t}\n\n\t\tconst wrapper: InlineAstNode = makeNode(opener.image ? 'image' : 'link');\n\t\twrapper.href = dest.href;\n\t\twrapper.title = dest.title;\n\n\t\t// Resolve emphasis inside the bracketed range first. The emphasis algorithm\n\t\t// walks the sibling linked list, so it must run while the bracket contents\n\t\t// are still linked, before they are detached into the wrapper's children.\n\t\tthis.processEmphasis(opener.prevDelim);\n\n\t\t// Move the (now emphasis-resolved) nodes between the opener and now into the wrapper.\n\t\tlet node: InlineAstNode | null = opener.node.next;\n\t\twhile (node) {\n\t\t\tconst next: InlineAstNode | null = node.next;\n\t\t\tunlink(node);\n\t\t\twrapper.children.push(node);\n\t\t\tnode = next;\n\t\t}\n\t\tif (opener.image) {\n\t\t\twrapper.alt = astPlainText(wrapper.children);\n\t\t\twrapper.children = [];\n\t\t}\n\n\t\t// The wrapper takes the opener's place and becomes the new tail. It is linked\n\t\t// here, so the parse loop must not append it again (returning null prevents the\n\t\t// double-link that would otherwise corrupt the wrapper's sibling pointers).\n\t\tinsertAfter(opener.node, wrapper);\n\t\tunlink(opener.node);\n\t\tthis.tail = wrapper;\n\n\t\t// Links cannot contain other links.\n\t\tif (!opener.image) {\n\t\t\tlet b: Bracket | null = opener.prev;\n\t\t\twhile (b) {\n\t\t\t\tif (!b.image) b.active = false;\n\t\t\t\tb = b.prev;\n\t\t\t}\n\t\t}\n\t\tthis.brackets = opener.prev;\n\t\treturn null;\n\t}\n\n\t/** Parses an inline `(dest \"title\")` or a reference `[label]` target. */\n\tprivate parseLinkTarget(opener: Bracket): { href: string; title: string } | null {\n\t\tif (this.text[this.pos] === '(') {\n\t\t\tconst inline = this.parseInlineDestination();\n\t\t\tif (inline) return inline;\n\t\t}\n\t\t// Reference link: [label][ref], [label][], or shortcut [label].\n\t\tconst label: string = this.text.slice(opener.index, this.pos - 1);\n\t\tlet ref: string = label;\n\t\tlet explicitRef = false;\n\t\t// Position to advance to *only* when the reference resolves. A failed\n\t\t// lookup must leave `this.pos` at the `]` so the trailing `[ref]` re-parses\n\t\t// as literal text — otherwise `[text][missing]` silently drops `[missing]`.\n\t\tlet resolvedEnd: number = this.pos;\n\t\tif (this.text[this.pos] === '[') {\n\t\t\tconst close: number = this.text.indexOf(']', this.pos + 1);\n\t\t\tif (close !== -1) {\n\t\t\t\tconst explicit: string = this.text.slice(this.pos + 1, close);\n\t\t\t\tif (explicit.trim() !== '') {\n\t\t\t\t\tref = explicit;\n\t\t\t\t\texplicitRef = true;\n\t\t\t\t}\n\t\t\t\tresolvedEnd = close + 1;\n\t\t\t}\n\t\t}\n\t\t// CommonMark forbids unescaped `[` inside a reference label. For shortcut\n\t\t// and collapsed forms the bracket text is the label; a full reference\n\t\t// (`[text][ref]`) may hold anything in its text part.\n\t\tif (!explicitRef && ref.includes('[')) return null;\n\t\tconst found = this.ctx.linkRefs.get(foldReferenceLabel(ref));\n\t\tif (found) {\n\t\t\tthis.pos = resolvedEnd;\n\t\t\treturn { href: found.href, title: found.title ?? '' };\n\t\t}\n\t\treturn null;\n\t}\n\n\t/** Parses `(url \"title\")` starting at the current `(`; returns null if malformed. */\n\tprivate parseInlineDestination(): { href: string; title: string } | null {\n\t\tlet i: number = this.pos + 1;\n\t\twhile (this.text[i] === ' ' || this.text[i] === '\\n') i++;\n\n\t\tlet href = '';\n\t\tif (this.text[i] === '<') {\n\t\t\t// Angle destination: ends at the first unescaped `>`; `<` and newlines\n\t\t\t// are not allowed inside. Escapes and entities decode afterwards.\n\t\t\tlet end: number = i + 1;\n\t\t\tconst angleLimit: number = Math.min(this.len, i + 1 + MAX_DESTINATION_LENGTH);\n\t\t\twhile (end < angleLimit) {\n\t\t\t\tconst ch: string = this.text[end] ?? '';\n\t\t\t\tif (ch === '\\\\' && ESCAPABLE.test(this.text[end + 1] ?? '')) {\n\t\t\t\t\tend += 2;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (ch === '>' || ch === '<' || ch === '\\n') break;\n\t\t\t\tend++;\n\t\t\t}\n\t\t\tif (this.text[end] !== '>') return null;\n\t\t\thref = decodeEscapesAndEntities(this.text.slice(i + 1, end));\n\t\t\ti = end + 1;\n\t\t} else {\n\t\t\tlet depth = 0;\n\t\t\t// Bound the destination scan: real URLs are short, and an unbounded scan\n\t\t\t// over adversarial input (`](](]( …` with no closing paren) is quadratic.\n\t\t\tconst limit: number = Math.min(this.len, i + MAX_DESTINATION_LENGTH);\n\t\t\twhile (i < limit) {\n\t\t\t\tconst ch: string = this.text[i] ?? '';\n\t\t\t\tif (ch === '\\\\' && ESCAPABLE.test(this.text[i + 1] ?? '')) {\n\t\t\t\t\thref += this.text[i + 1];\n\t\t\t\t\ti += 2;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (ch === '(') depth++;\n\t\t\t\telse if (ch === ')') {\n\t\t\t\t\tif (depth === 0) break;\n\t\t\t\t\tdepth--;\n\t\t\t\t} else if (ch === ' ' || ch === '\\t' || ch === '\\n' || ch === '[' || ch === ']') {\n\t\t\t\t\t// Only ASCII whitespace ends a bare destination (a non-breaking\n\t\t\t\t\t// space is part of it, per CommonMark); `[`/`]` are not valid there\n\t\t\t\t\t// (real URLs percent-encode them) and bailing here keeps degenerate\n\t\t\t\t\t// `](](]( …` input linear instead of quadratic (D1 ReDoS guard).\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\thref += ch;\n\t\t\t\ti++;\n\t\t\t}\n\t\t\t// Scan hit the length cap without resolving a closing paren: not a link.\n\t\t\tif (i >= limit && this.text[i] !== ')') return null;\n\t\t\thref = decodeEntitiesOnly(href);\n\t\t}\n\n\t\twhile (this.text[i] === ' ' || this.text[i] === '\\n') i++;\n\t\tlet title = '';\n\t\tconst open: string = this.text[i] ?? '';\n\t\tif (open === '\"' || open === \"'\" || open === '(') {\n\t\t\tconst parsed: { title: string; next: number } | null = this.parseLinkTitle(i);\n\t\t\tif (!parsed) return null;\n\t\t\ttitle = parsed.title;\n\t\t\ti = parsed.next;\n\t\t}\n\t\twhile (this.text[i] === ' ' || this.text[i] === '\\n') i++;\n\t\tif (this.text[i] !== ')') return null;\n\t\tthis.pos = i + 1;\n\t\treturn { href, title };\n\t}\n\n\t/**\n\t * Parses a link/image title that starts at its opening delimiter (`\"`, `'`,\n\t * or `(`), honoring backslash escapes so an escaped closing delimiter stays\n\t * part of the title (e.g. `\"a\\\"b\"` is the title `a\"b`). Mirrors the serializer\n\t * which escapes the delimiter on output, so titles round-trip losslessly.\n\t * Returns the unescaped title and the index just past the closing delimiter,\n\t * or null if it is never closed within the scan bound (D1 ReDoS guard).\n\t */\n\tprivate parseLinkTitle(start: number): { title: string; next: number } | null {\n\t\tconst open: string = this.text[start] ?? '';\n\t\tconst close: string = open === '(' ? ')' : open;\n\t\tlet title = '';\n\t\tlet i: number = start + 1;\n\t\tconst limit: number = Math.min(this.len, start + 1 + MAX_DESTINATION_LENGTH);\n\t\twhile (i < limit) {\n\t\t\tconst ch: string = this.text[i] ?? '';\n\t\t\tif (ch === '\\\\' && ESCAPABLE.test(this.text[i + 1] ?? '')) {\n\t\t\t\ttitle += this.text[i + 1];\n\t\t\t\ti += 2;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (ch === close) return { title: decodeEntitiesOnly(title), next: i + 1 };\n\t\t\ttitle += ch;\n\t\t\ti++;\n\t\t}\n\t\treturn null;\n\t}\n\n\t/** `&`: decodes an HTML entity reference, or emits a literal `&`. */\n\tprivate parseEntity(): InlineAstNode {\n\t\tconst match: RegExpExecArray | null = matchEntityAt(this.text, this.pos);\n\t\tif (!match?.[1]) {\n\t\t\tthis.pos++;\n\t\t\treturn makeNode('text', '&');\n\t\t}\n\t\tthis.pos += match[0].length;\n\t\treturn makeNode('text', decodeEntity(match[1]));\n\t}\n\n\t/** `<`: autolink, inline HTML (balanced fallback run), or literal. */\n\tprivate parseLessThan(): InlineAstNode {\n\t\tconst auto: InlineAstNode | null = this.parseAutolink();\n\t\tif (auto) return auto;\n\t\tconst html: InlineAstNode | null = this.parseInlineHtml();\n\t\tif (html) return html;\n\t\tthis.pos++;\n\t\treturn makeNode('text', '<');\n\t}\n\n\tprivate parseAutolink(): InlineAstNode | null {\n\t\tconst rest: string = this.text.slice(this.pos);\n\t\tconst uri: RegExpMatchArray | null = rest.match(/^<([a-zA-Z][a-zA-Z0-9+.-]{1,31}:[^<>\\s]*)>/);\n\t\tif (uri?.[1]) {\n\t\t\tthis.pos += uri[0].length;\n\t\t\tconst node: InlineAstNode = makeNode('link');\n\t\t\tnode.href = uri[1];\n\t\t\tnode.children = [makeNode('text', uri[1])];\n\t\t\treturn node;\n\t\t}\n\t\tconst email: RegExpMatchArray | null = rest.match(\n\t\t\t/^<([a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)>/,\n\t\t);\n\t\tif (email?.[1]) {\n\t\t\tthis.pos += email[0].length;\n\t\t\tconst node: InlineAstNode = makeNode('link');\n\t\t\tnode.href = `mailto:${email[1]}`;\n\t\t\tnode.children = [makeNode('text', email[1])];\n\t\t\treturn node;\n\t\t}\n\t\treturn null;\n\t}\n\n\t/** Captures a balanced inline-HTML fallback run (`<u>…</u>`) or a single tag. */\n\tprivate parseInlineHtml(): InlineAstNode | null {\n\t\tconst rest: string = this.text.slice(this.pos);\n\t\tconst open: RegExpMatchArray | null = rest.match(/^<([a-zA-Z][a-zA-Z0-9-]*)(?:\\s[^<>]*)?>/);\n\t\tif (!open?.[1]) {\n\t\t\tconst comment: RegExpMatchArray | null = rest.match(/^<!--[\\s\\S]*?-->/);\n\t\t\tif (comment) {\n\t\t\t\tthis.pos += comment[0].length;\n\t\t\t\treturn makeNode('html_inline', normalizeHtmlLiteral(comment[0]));\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\tconst tag: string = open[1].toLowerCase();\n\t\tif (INLINE_HTML_TAGS.has(tag)) {\n\t\t\tconst closeTag = `</${tag}>`;\n\t\t\tconst closeIdx: number = this.text.toLowerCase().indexOf(closeTag, this.pos + open[0].length);\n\t\t\tif (closeIdx !== -1) {\n\t\t\t\tconst end: number = closeIdx + closeTag.length;\n\t\t\t\tconst html: string = this.text.slice(this.pos, end);\n\t\t\t\tthis.pos = end;\n\t\t\t\treturn makeNode('html_inline', normalizeHtmlLiteral(html));\n\t\t\t}\n\t\t}\n\t\tthis.pos += open[0].length;\n\t\treturn makeNode('html_inline', normalizeHtmlLiteral(open[0]));\n\t}\n\n\t/**\n\t * The CommonMark emphasis algorithm: walk closers, match against openers,\n\t * wrap the enclosed nodes, honoring the rule-of-three for `*` / `_`.\n\t *\n\t * `openersBottom` is the CommonMark \"lower bound for opener searches\" cache.\n\t * It must be keyed by `(delimiter char, canOpen, origdelims % 3)` — exactly\n\t * the bucketing used by the reference implementation — because the rule-of-\n\t * three test depends on both flags. Keying by the character alone lets a\n\t * `canOpen=false` closer that fails lower the bound for a later `canOpen=true`\n\t * closer of a different length class, silently dropping valid emphasis (e.g.\n\t * `**a*b*c**` would lose all of its bold/italic).\n\t */\n\tprivate processEmphasis(stackBottom: Delimiter | null): void {\n\t\tconst openersBottom = new Map<string, Delimiter | null>();\n\t\tconst bottomFor = (d: Delimiter): Delimiter | null => {\n\t\t\tconst key: string = emphBottomKey(d);\n\t\t\treturn openersBottom.has(key) ? (openersBottom.get(key) ?? null) : stackBottom;\n\t\t};\n\n\t\tlet closer: Delimiter | null = stackBottom ? stackBottom.next : this.firstDelimiter();\n\t\twhile (closer) {\n\t\t\tif (!closer.canClose) {\n\t\t\t\tcloser = closer.next;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst cc: string = closer.cc;\n\t\t\tlet opener: Delimiter | null = closer.prev;\n\t\t\tlet openerFound = false;\n\t\t\tconst bottom: Delimiter | null = bottomFor(closer);\n\t\t\twhile (opener && opener !== stackBottom && opener !== bottom) {\n\t\t\t\tif (opener.cc === cc && opener.canOpen && this.emphMatches(opener, closer)) {\n\t\t\t\t\topenerFound = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\topener = opener.prev;\n\t\t\t}\n\n\t\t\tif (!openerFound) {\n\t\t\t\topenersBottom.set(emphBottomKey(closer), closer.prev);\n\t\t\t\tif (!closer.canOpen) this.removeDelimiter(closer);\n\t\t\t\tcloser = closer.next;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst matchedOpener: Delimiter = opener as Delimiter;\n\t\t\tconst use: number = this.wrapCount(cc, matchedOpener, closer);\n\t\t\tif (use === 0) {\n\t\t\t\tcloser = closer.next;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tthis.wrapEmphasis(cc, matchedOpener, closer, use);\n\n\t\t\tmatchedOpener.numdelims -= use;\n\t\t\tcloser.numdelims -= use;\n\t\t\tmatchedOpener.node.literal = matchedOpener.node.literal.slice(use);\n\t\t\tcloser.node.literal = closer.node.literal.slice(use);\n\n\t\t\tthis.removeDelimitersBetween(matchedOpener, closer);\n\t\t\tif (matchedOpener.numdelims === 0) this.removeDelimiter(matchedOpener);\n\t\t\tif (closer.numdelims === 0) {\n\t\t\t\tconst next: Delimiter | null = closer.next;\n\t\t\t\tthis.removeDelimiter(closer);\n\t\t\t\tcloser = next;\n\t\t\t}\n\t\t}\n\n\t\t// Remove all delimiters above stackBottom.\n\t\twhile (this.delimiters && this.delimiters !== stackBottom) {\n\t\t\tthis.removeDelimiter(this.delimiters);\n\t\t}\n\t}\n\n\tprivate firstDelimiter(): Delimiter | null {\n\t\tlet d: Delimiter | null = this.delimiters;\n\t\twhile (d?.prev) d = d.prev;\n\t\treturn d;\n\t}\n\n\t/** Rule-of-three test for `*` / `_`; other delimiters always match. */\n\tprivate emphMatches(opener: Delimiter, closer: Delimiter): boolean {\n\t\tif (opener.cc !== '*' && opener.cc !== '_') return true;\n\t\tconst oddMatch: boolean =\n\t\t\t(closer.canOpen || opener.canClose) &&\n\t\t\tcloser.origdelims % 3 !== 0 &&\n\t\t\t(opener.origdelims + closer.origdelims) % 3 === 0;\n\t\treturn !oddMatch;\n\t}\n\n\t/** How many delimiters this match consumes (and implicitly which wrapper kind). */\n\tprivate wrapCount(cc: string, opener: Delimiter, closer: Delimiter): number {\n\t\tconst both2: boolean = opener.numdelims >= 2 && closer.numdelims >= 2;\n\t\tif (cc === '*' || cc === '_') return both2 ? 2 : 1;\n\t\tif (cc === '~') {\n\t\t\tif (this.ctx.opts.gfm && both2) return 2;\n\t\t\treturn this.ctx.opts.extendedInlineSyntax ? 1 : 0;\n\t\t}\n\t\tif (cc === '=') return this.ctx.opts.extendedInlineSyntax && both2 ? 2 : 0;\n\t\tif (cc === '^') return this.ctx.opts.extendedInlineSyntax ? 1 : 0;\n\t\treturn 0;\n\t}\n\n\tprivate wrapKind(cc: string, use: number): InlineKind {\n\t\tif (cc === '*' || cc === '_') return use === 2 ? 'strong' : 'emph';\n\t\tif (cc === '~') return use === 2 ? 'strike' : 'sub';\n\t\tif (cc === '=') return 'highlight';\n\t\treturn 'sup';\n\t}\n\n\t/** Moves nodes between opener and closer into a new wrapper node. */\n\tprivate wrapEmphasis(cc: string, opener: Delimiter, closer: Delimiter, use: number): void {\n\t\tconst wrapper: InlineAstNode = makeNode(this.wrapKind(cc, use));\n\t\tlet node: InlineAstNode | null = opener.node.next;\n\t\twhile (node && node !== closer.node) {\n\t\t\tconst next: InlineAstNode | null = node.next;\n\t\t\tunlink(node);\n\t\t\twrapper.children.push(node);\n\t\t\tnode = next;\n\t\t}\n\t\tinsertAfter(opener.node, wrapper);\n\t}\n\n\tprivate removeDelimitersBetween(opener: Delimiter, closer: Delimiter): void {\n\t\tlet d: Delimiter | null = closer.prev;\n\t\twhile (d && d !== opener) {\n\t\t\tconst prev: Delimiter | null = d.prev;\n\t\t\tthis.removeDelimiter(d);\n\t\t\td = prev;\n\t\t}\n\t}\n\n\tprivate removeDelimiter(delim: Delimiter): void {\n\t\tif (delim.prev) delim.prev.next = delim.next;\n\t\tif (delim.next) delim.next.prev = delim.prev;\n\t\tif (!delim.next) this.delimiters = delim.prev;\n\t}\n}\n\n/**\n * The `openersBottom` bucket key for a closer: `(char, canOpen, origdelims % 3)`,\n * mirroring the CommonMark reference implementation's index (`base + (canOpen ?\n * 3 : 0) + (origdelims % 3)`). `origdelims` is used, not the live `numdelims`, so\n * the key is stable across the delimiter's consumption.\n */\nfunction emphBottomKey(d: Delimiter): string {\n\treturn `${d.cc}${d.canOpen ? 1 : 0}${d.origdelims % 3}`;\n}\n\n/**\n * Normalizes newlines inside a captured raw-HTML run to single spaces. The\n * literal is opaque to the engine; on re-import the newlines would become soft\n * breaks (spaces) anyway, so normalizing here keeps `md → doc → md` a fixpoint.\n */\nfunction normalizeHtmlLiteral(html: string): string {\n\treturn html.replace(/\\n[ \\t]*/g, ' ');\n}\n\n/** Collects the sibling list after the synthetic root into an array. */\nfunction collectSiblings(root: InlineAstNode): InlineAstNode[] {\n\tconst out: InlineAstNode[] = [];\n\tlet node: InlineAstNode | null = root.next;\n\twhile (node) {\n\t\tout.push(node);\n\t\tnode = node.next;\n\t}\n\treturn out;\n}\n\n/** Extracts the plain-text content of an AST subtree (for image alt text). */\nfunction astPlainText(nodes: readonly InlineAstNode[]): string {\n\tlet out = '';\n\tfor (const node of nodes) {\n\t\tif (node.type === 'text' || node.type === 'code') out += node.literal;\n\t\telse if (node.type === 'softbreak' || node.type === 'hardbreak') out += ' ';\n\t\telse if (node.children.length) out += astPlainText(node.children);\n\t\telse if (node.alt) out += node.alt;\n\t}\n\treturn out;\n}\n\n// --- Flatten AST → notectl inline content ---\n\nconst KIND_TO_MARK: Partial<Record<InlineKind, string>> = {\n\temph: 'italic',\n\tstrong: 'bold',\n\tstrike: 'strikethrough',\n\thighlight: 'highlight',\n\tsup: 'superscript',\n\tsub: 'subscript',\n};\n\n/** Whether a mark type is usable (no registry means tests; allow all). */\nfunction markAllowed(ctx: ParseContext, type: string): boolean {\n\treturn !ctx.registry || ctx.registry.getMarkSpec(type) !== undefined;\n}\n\nfunction flatten(\n\tnodes: readonly InlineAstNode[],\n\tmarks: readonly Mark[],\n\tctx: ParseContext,\n\tout: (TextNode | InlineNode)[],\n): void {\n\tfor (const node of nodes) {\n\t\tflattenNode(node, marks, ctx, out);\n\t}\n}\n\nfunction flattenNode(\n\tnode: InlineAstNode,\n\tmarks: readonly Mark[],\n\tctx: ParseContext,\n\tout: (TextNode | InlineNode)[],\n): void {\n\tswitch (node.type) {\n\t\tcase 'text':\n\t\t\tif (node.literal) out.push(createTextNode(node.literal, [...marks]));\n\t\t\treturn;\n\t\tcase 'softbreak':\n\t\t\tout.push(createTextNode(' ', [...marks]));\n\t\t\treturn;\n\t\tcase 'hardbreak':\n\t\t\tout.push(createInlineNode(inlineType('hard_break'), {}));\n\t\t\treturn;\n\t\tcase 'code':\n\t\t\tout.push(createTextNode(node.literal, withMark(marks, ctx, 'code')));\n\t\t\treturn;\n\t\tcase 'link': {\n\t\t\tconst attrs: Record<string, string> = { href: node.href };\n\t\t\tif (node.title) attrs.title = node.title;\n\t\t\tflatten(node.children, withMark(marks, ctx, 'link', attrs), ctx, out);\n\t\t\treturn;\n\t\t}\n\t\tcase 'image': {\n\t\t\tpushImage(node, marks, ctx, out);\n\t\t\treturn;\n\t\t}\n\t\tcase 'inline_node':\n\t\t\tout.push(\n\t\t\t\tcreateInlineNode(inlineType(node.inlineType) as InlineTypeName, node.attrs, [...marks]),\n\t\t\t);\n\t\t\treturn;\n\t\tcase 'html_inline':\n\t\t\tpushInlineHtml(node.literal, marks, ctx, out);\n\t\t\treturn;\n\t\tdefault: {\n\t\t\tconst markName: string | undefined = KIND_TO_MARK[node.type];\n\t\t\tif (markName) {\n\t\t\t\tflatten(node.children, withMark(marks, ctx, markName), ctx, out);\n\t\t\t} else {\n\t\t\t\tflatten(node.children, marks, ctx, out);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** Returns `marks` plus a new mark of `type` (with attrs), if the schema allows it. */\nfunction withMark(\n\tmarks: readonly Mark[],\n\tctx: ParseContext,\n\ttype: string,\n\tattrs?: Record<string, string | number | boolean>,\n): Mark[] {\n\tif (!markAllowed(ctx, type)) return [...marks];\n\tconst mark: Mark = attrs ? { type: markType(type), attrs } : { type: markType(type) };\n\treturn [...marks, mark];\n}\n\n/** Emits an inline image node, or degrades to alt text when unavailable. */\nfunction pushImage(\n\tnode: InlineAstNode,\n\tmarks: readonly Mark[],\n\tctx: ParseContext,\n\tout: (TextNode | InlineNode)[],\n): void {\n\tconst hasInlineImage: boolean =\n\t\t!ctx.registry || ctx.registry.getInlineNodeSpec('image_inline') !== undefined;\n\tif (hasInlineImage) {\n\t\tconst attrs: Record<string, string> = { src: node.href, alt: node.alt };\n\t\tif (node.title) attrs.title = node.title;\n\t\t// Carry the surrounding marks (e.g. a `link` from `[](url)`) so a\n\t\t// linked inline image keeps its link instead of silently dropping it.\n\t\tout.push(createInlineNode(inlineType('image_inline'), attrs, [...marks]));\n\t\treturn;\n\t}\n\tif (node.alt) out.push(createTextNode(node.alt, [...marks]));\n}\n\n/** Re-parses an inline-HTML fallback run via the HTML parser to recover marks. */\nfunction pushInlineHtml(\n\thtml: string,\n\tmarks: readonly Mark[],\n\tctx: ParseContext,\n\tout: (TextNode | InlineNode)[],\n): void {\n\t// The HTML parser treats a lone <br> in a paragraph as an empty-block\n\t// placeholder. Here the paragraph is synthetic, so it is a real hard break.\n\tconst hardBreakAllowed: boolean =\n\t\t!ctx.registry || ctx.registry.getInlineNodeSpec('hard_break') !== undefined;\n\tif (hardBreakAllowed && /^<br\\s*\\/?>$/i.test(html.trim())) {\n\t\tout.push(createInlineNode(inlineType('hard_break'), {}, [...marks]));\n\t\treturn;\n\t}\n\tconst doc = parseHTMLToDocument(`<p>${html}</p>`, resolveMarkdownHTMLRegistry(ctx.registry));\n\tconst block = doc.children[0];\n\tif (!block) return;\n\tlet emitted = false;\n\tfor (const child of block.children) {\n\t\tif ('text' in child && child.text !== '') {\n\t\t\tout.push(createTextNode(child.text, mergeMarks(marks, child.marks)));\n\t\t\temitted = true;\n\t\t} else if ('inlineType' in child) {\n\t\t\tout.push(createInlineNode(child.inlineType, child.attrs, mergeMarks(marks, child.marks)));\n\t\t\temitted = true;\n\t\t}\n\t}\n\t// An unknown/empty HTML element has no baseline model representation. Keep\n\t// it literal on registry-free calls instead of silently dropping input.\n\tif (!emitted && !ctx.registry) out.push(createTextNode(html, [...marks]));\n}\n\n/** Merges two mark lists, de-duplicating by type. */\nfunction mergeMarks(outer: readonly Mark[], inner: readonly Mark[]): Mark[] {\n\tconst result: Mark[] = [...outer];\n\tfor (const mark of inner) {\n\t\tif (!result.some((m) => m.type === mark.type)) result.push(mark);\n\t}\n\treturn result;\n}\n\n/** Merges adjacent text nodes that share an identical mark set. */\nfunction mergeAdjacentText(nodes: readonly (TextNode | InlineNode)[]): (TextNode | InlineNode)[] {\n\tconst result: (TextNode | InlineNode)[] = [];\n\tfor (const node of nodes) {\n\t\tconst prev = result[result.length - 1];\n\t\tif ('text' in node && prev && 'text' in prev && sameMarkTypes(prev.marks, node.marks)) {\n\t\t\tresult[result.length - 1] = { type: 'text', text: prev.text + node.text, marks: prev.marks };\n\t\t} else {\n\t\t\tresult.push(node);\n\t\t}\n\t}\n\treturn result;\n}\n\n/** Structural equality of two mark lists (type + attrs, order-independent). */\nfunction sameMarkTypes(a: readonly Mark[], b: readonly Mark[]): boolean {\n\tif (a.length !== b.length) return false;\n\tconst key = (m: Mark): string => `${m.type}:${JSON.stringify(m.attrs ?? {})}`;\n\tconst setB = new Set(b.map(key));\n\treturn a.every((m) => setB.has(key(m)));\n}\n\n/** Parses inline Markdown into text/inline nodes (linear-time, ReDoS-safe). */\nexport function parseInline(text: string, ctx: ParseContext): (TextNode | InlineNode)[] {\n\tconst ast: InlineAstNode[] = new InlineParser(text, ctx).parse();\n\tif (ctx.opts.gfm) applyGfmEmailAutolinks(ast);\n\tconst out: (TextNode | InlineNode)[] = [];\n\tflatten(ast, [], ctx, out);\n\tconst merged: (TextNode | InlineNode)[] = mergeAdjacentText(out);\n\treturn merged.length > 0 ? merged : [createTextNode('')];\n}\n","/**\n * Resolved parse options and the context threaded through the Markdown parser.\n * Mirrors `MarkdownContext.ts` on the serialize side.\n */\n\nimport type { SchemaRegistry } from '../../model/SchemaRegistry.js';\nimport type { MarkdownParseOptions, MarkdownSyntaxExtension } from '../MarkdownTypes.js';\n\n/** Fully-resolved parse options (no `undefined`). */\nexport interface ResolvedParseOptions {\n\treadonly flavor: 'commonmark' | 'gfm';\n\treadonly gfm: boolean;\n\treadonly extendedInlineSyntax: boolean;\n\treadonly htmlFallback: boolean;\n\treadonly syntaxExtensions: readonly MarkdownSyntaxExtension[];\n}\n\n/** Internal context shared by the block and inline parsers. */\nexport interface ParseContext {\n\treadonly registry?: SchemaRegistry;\n\treadonly opts: ResolvedParseOptions;\n\t/** Link reference definitions collected from the source (`[ref]: url \"title\"`). */\n\treadonly linkRefs: ReadonlyMap<string, { readonly href: string; readonly title?: string }>;\n}\n\n/** Resolves user-facing parse options into a complete settings object with defaults. */\nexport function resolveParseOptions(options?: MarkdownParseOptions): ResolvedParseOptions {\n\tconst flavor: 'commonmark' | 'gfm' = options?.flavor ?? 'gfm';\n\treturn {\n\t\tflavor,\n\t\tgfm: flavor === 'gfm',\n\t\textendedInlineSyntax: options?.extendedInlineSyntax ?? false,\n\t\thtmlFallback: options?.htmlFallback !== false,\n\t\tsyntaxExtensions: options?.syntaxExtensions ?? [],\n\t};\n}\n","/**\n * MarkdownParser: converts a Markdown string into an immutable Document.\n *\n * Mirrors `DocumentParser.ts` (HTML) but is reached **only** via dynamic\n * `import()` so it stays code-split out of the core bundle (D13). The grammar is\n * centralized (CommonMark is a global, context-sensitive grammar that cannot be\n * decomposed into per-spec rules, D4); tokens map to schema node types by name\n * through the registry. Raw HTML blocks delegate to `parseHTMLToDocument` so the\n * superset round-trip stays lossless (D3). Plugin syntax (`$...$`) is contributed\n * via `options.syntaxExtensions`, never hard-coded here.\n */\n\nimport { hoistDisallowedBlocks } from '../model/ContentModel.js';\nimport type { BlockNode, Document, InlineNode, TextNode } from '../model/Document.js';\nimport {\n\tcreateBlockNode,\n\tcreateDocument,\n\tcreateTextNode,\n\tisInlineNode,\n} from '../model/Document.js';\nimport type { SchemaRegistry } from '../model/SchemaRegistry.js';\nimport { type NodeTypeName, nodeType } from '../model/TypeBrands.js';\nimport { parseHTMLToDocument } from './DocumentParser.js';\nimport type { MarkdownParseOptions } from './MarkdownTypes.js';\nimport type { BlockToken } from './markdown/BlockTokenizer.js';\nimport { tokenizeBlocks } from './markdown/BlockTokenizer.js';\nimport type { ColumnAlign } from './markdown/GfmTableParser.js';\nimport { parseInline } from './markdown/InlineTokenizer.js';\nimport { extractLinkReferences } from './markdown/LinkReferenceExtractor.js';\nimport { resolveMarkdownHTMLRegistry } from './markdown/MarkdownHTMLRegistry.js';\nimport { type ParseContext, resolveParseOptions } from './markdown/MarkdownParseContext.js';\n\n/** Parses a Markdown string into a Document. */\nexport function parseMarkdownToDocument(\n\tmarkdown: string,\n\tregistry?: SchemaRegistry,\n\toptions?: MarkdownParseOptions,\n): Document {\n\tconst normalized: string = markdown.replace(/\\r\\n?/g, '\\n');\n\tconst { source, linkRefs } = extractLinkReferences(normalized);\n\n\tconst ctx: ParseContext = {\n\t\tregistry,\n\t\topts: resolveParseOptions(options),\n\t\tlinkRefs,\n\t};\n\n\tconst tokens: BlockToken[] = tokenizeBlocks(source, ctx.opts.gfm, ctx.opts.syntaxExtensions);\n\tconst blocks: BlockNode[] = tokensToBlocks(tokens, ctx);\n\tif (blocks.length === 0) return createDocument();\n\tconst doc: Document = createDocument(blocks);\n\t// A container item can only hold the block types its schema allows; an\n\t// importer that nested (say) a table under an item is repaired by hoisting the\n\t// disallowed block up to a valid ancestor (#194).\n\treturn registry ? hoistDisallowedBlocks(doc, registry) : doc;\n}\n\n/** Converts a list of block tokens into Document block nodes. */\nfunction tokensToBlocks(tokens: readonly BlockToken[], ctx: ParseContext): BlockNode[] {\n\tconst blocks: BlockNode[] = [];\n\tfor (const token of tokens) {\n\t\tappendToken(token, blocks, ctx);\n\t}\n\treturn blocks;\n}\n\n/** Appends the block node(s) produced by a single token. */\nfunction appendToken(token: BlockToken, blocks: BlockNode[], ctx: ParseContext): void {\n\tswitch (token.type) {\n\t\tcase 'heading':\n\t\t\tblocks.push(makeBlock('heading', parseInline(token.text, ctx), ctx, { level: token.level }));\n\t\t\treturn;\n\t\tcase 'paragraph': {\n\t\t\tconst inline: (TextNode | InlineNode)[] = parseInline(token.text, ctx);\n\t\t\tconst image: BlockNode | null = promoteStandaloneImage(inline, ctx);\n\t\t\tblocks.push(image ?? makeBlock('paragraph', inline, ctx));\n\t\t\treturn;\n\t\t}\n\t\tcase 'table':\n\t\t\tblocks.push(buildTable(token.aligns, token.header, token.rows, ctx));\n\t\t\treturn;\n\t\tcase 'extension_block':\n\t\t\tblocks.push(makeBlock(token.nodeType, [createTextNode('')], ctx, token.attrs));\n\t\t\treturn;\n\t\tcase 'code_block':\n\t\t\tblocks.push(\n\t\t\t\tmakeBlock('code_block', [createTextNode(token.code)], ctx, { language: token.language }),\n\t\t\t);\n\t\t\treturn;\n\t\tcase 'hr':\n\t\t\tblocks.push(makeBlock('horizontal_rule', [createTextNode('')], ctx));\n\t\t\treturn;\n\t\tcase 'list_item': {\n\t\t\tconst attrs: Record<string, string | number | boolean> = {\n\t\t\t\tlistType: token.listType,\n\t\t\t\tindent: token.indent,\n\t\t\t\tchecked: token.checked,\n\t\t\t};\n\t\t\tif (token.children && token.children.length > 0) {\n\t\t\t\t// Multi-block item (#194): a container whose children are blocks.\n\t\t\t\tconst children: BlockNode[] = tokensToBlocks(token.children, ctx);\n\t\t\t\tif (ctx.registry && !ctx.registry.getNodeSpec('list_item')) {\n\t\t\t\t\t// Type unavailable: degrade to the children as top-level blocks.\n\t\t\t\t\tblocks.push(...children);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tblocks.push(createBlockNode(nodeType('list_item'), children, undefined, attrs));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tblocks.push(makeBlock('list_item', parseInline(token.text, ctx), ctx, attrs));\n\t\t\treturn;\n\t\t}\n\t\tcase 'blockquote': {\n\t\t\tconst children: BlockNode[] = tokensToBlocks(token.children, ctx);\n\t\t\tif (children.length === 0) {\n\t\t\t\tchildren.push(createBlockNode(nodeType('paragraph'), [createTextNode('')]));\n\t\t\t}\n\t\t\tblocks.push(makeContainer('blockquote', children, ctx));\n\t\t\treturn;\n\t\t}\n\t\tcase 'html':\n\t\t\t// A comment-only block produces no content (and must not leave a stray\n\t\t\t// empty paragraph between blocks, e.g. two lists split by `<!-- -->`).\n\t\t\tif (isCommentOnly(token.html)) return;\n\t\t\tfor (const block of parseHTMLToDocument(token.html, resolveMarkdownHTMLRegistry(ctx.registry))\n\t\t\t\t.children) {\n\t\t\t\tblocks.push(block);\n\t\t\t}\n\t\t\treturn;\n\t}\n}\n\n/** Whether an HTML block consists solely of comments (linear manual scan). */\nfunction isCommentOnly(html: string): boolean {\n\tlet rest: string = html.trim();\n\twhile (rest.startsWith('<!--')) {\n\t\tconst end: number = rest.indexOf('-->');\n\t\tif (end === -1) return false;\n\t\trest = rest.slice(end + 3).trim();\n\t}\n\treturn rest === '';\n}\n\n/** Builds a leaf block, falling back to a paragraph if the type is unavailable. */\nfunction makeBlock(\n\ttype: string,\n\tchildren: readonly (TextNode | InlineNode)[],\n\tctx: ParseContext,\n\tattrs?: Record<string, string | number | boolean>,\n): BlockNode {\n\tconst resolved: NodeTypeName = resolveType(type, ctx);\n\tconst childList: readonly (TextNode | InlineNode)[] =\n\t\tchildren.length > 0 ? children : [createTextNode('')];\n\treturn createBlockNode(\n\t\tresolved,\n\t\tchildList,\n\t\tundefined,\n\t\tattrs && resolved === nodeType(type) ? attrs : undefined,\n\t);\n}\n\n/** Builds a container block (block children), falling back to a paragraph if unavailable. */\nfunction makeContainer(type: string, children: readonly BlockNode[], ctx: ParseContext): BlockNode {\n\tconst resolved: NodeTypeName = resolveType(type, ctx);\n\tif (resolved !== nodeType(type)) {\n\t\t// Type unavailable: degrade to the container's children inline (best effort).\n\t\treturn children[0] ?? createBlockNode(nodeType('paragraph'), [createTextNode('')]);\n\t}\n\treturn createBlockNode(resolved, children);\n}\n\n/** Resolves a desired node type against the registry, falling back to paragraph. */\nfunction resolveType(type: string, ctx: ParseContext): NodeTypeName {\n\tif (ctx.registry && !ctx.registry.getNodeSpec(type)) return nodeType('paragraph');\n\treturn nodeType(type);\n}\n\n/**\n * Promotes a paragraph holding only a single inline image to a block `image`\n * node, matching the serializer (a standalone-image line becomes a block image).\n * Returns null when the paragraph is not a lone image or `image` is unavailable.\n * A marked image (e.g. a linked `[](url)`) is never promoted: the\n * block `image` node cannot hold a mark, so promotion would drop the link.\n */\nfunction promoteStandaloneImage(\n\tinline: readonly (TextNode | InlineNode)[],\n\tctx: ParseContext,\n): BlockNode | null {\n\tif (inline.length !== 1) return null;\n\tconst only = inline[0];\n\tif (!only || !isInlineNode(only) || only.inlineType !== 'image_inline') return null;\n\tif (only.marks.length > 0) return null;\n\tif (ctx.registry && !ctx.registry.getNodeSpec('image')) return null;\n\n\tconst attrs: Record<string, string | number | boolean> = {\n\t\tsrc: String(only.attrs.src ?? ''),\n\t\talt: String(only.attrs.alt ?? ''),\n\t\talign: 'center',\n\t};\n\tif (only.attrs.title) attrs.title = String(only.attrs.title);\n\treturn createBlockNode(nodeType('image'), [createTextNode('')], undefined, attrs);\n}\n\n/** Builds a GFM table node (table → table_row → table_cell → paragraph). */\nfunction buildTable(\n\taligns: readonly ColumnAlign[],\n\theader: readonly string[],\n\trows: readonly (readonly string[])[],\n\tctx: ParseContext,\n): BlockNode {\n\tif (ctx.registry && !ctx.registry.getNodeSpec('table')) {\n\t\t// No table support: degrade to a paragraph per row so content is never lost.\n\t\tconst lines: string = [header, ...rows].map((r) => r.join(' | ')).join('\\n');\n\t\treturn makeBlock('paragraph', parseInline(lines, ctx), ctx);\n\t}\n\n\tconst buildRow = (cells: readonly string[]): BlockNode => {\n\t\tconst tableCells: BlockNode[] = cells.map((cell, col) => {\n\t\t\tconst align: ColumnAlign = aligns[col] ?? null;\n\t\t\tconst paragraph: BlockNode = createBlockNode(\n\t\t\t\tnodeType('paragraph'),\n\t\t\t\tparseInline(cell, ctx),\n\t\t\t\tundefined,\n\t\t\t\talign ? { align } : undefined,\n\t\t\t);\n\t\t\treturn createBlockNode(nodeType('table_cell'), [paragraph]);\n\t\t});\n\t\treturn createBlockNode(nodeType('table_row'), tableCells);\n\t};\n\n\t// GFM normalizes every body row to the header's column count: overlong rows\n\t// drop their extra cells, short rows are padded with empty cells. Keeping rows\n\t// ragged would break the per-column model invariant the table tools rely on\n\t// (column insert/delete and cell navigation index by a fixed colIndex per row).\n\tconst columnCount: number = header.length;\n\tconst normalizeRow = (cells: readonly string[]): readonly string[] => {\n\t\tif (cells.length === columnCount) return cells;\n\t\tconst normalized: string[] = cells.slice(0, columnCount);\n\t\twhile (normalized.length < columnCount) normalized.push('');\n\t\treturn normalized;\n\t};\n\n\tconst tableRows: BlockNode[] = [\n\t\tbuildRow(header),\n\t\t...rows.map((row) => buildRow(normalizeRow(row))),\n\t];\n\treturn createBlockNode(nodeType('table'), tableRows);\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{t}from"./rolldown-runtime-H_PjY6_i.mjs";import{o as n}from"./HTMLUtils-Bhg3_P8B.mjs";import{E as e,O as r,T as o,_ as s,b as i,p as l,u as c}from"./Document-827l3P80.mjs";import{r as a}from"./AttrRegistry-B3zB02z9.mjs";import{_ as u,a as f,h as p,i as h,v as m}from"./TableDimensions-BBhjHNjQ.mjs";import{a as g,i as $,o as d,r as k,t as y}from"./MarkdownHTMLRegistry-
|
|
2
|
-
//# sourceMappingURL=MarkdownSerializer-
|
|
1
|
+
import{t}from"./rolldown-runtime-H_PjY6_i.mjs";import{o as n}from"./HTMLUtils-Bhg3_P8B.mjs";import{E as e,O as r,T as o,_ as s,b as i,p as l,u as c}from"./Document-827l3P80.mjs";import{r as a}from"./AttrRegistry-B3zB02z9.mjs";import{_ as u,a as f,h as p,i as h,v as m}from"./TableDimensions-BBhjHNjQ.mjs";import{a as g,i as $,o as d,r as k,t as y}from"./MarkdownHTMLRegistry-Q7l0d6Ma.mjs";function b(t){const n=t?.flavor??"gfm";return{flavor:n,gfm:"gfm"===n,htmlFallback:!1!==t?.htmlFallback,headingStyle:t?.headingStyle??"atx",bullet:t?.bullet??"-",emphasis:t?.emphasis??"*",codeFence:t?.codeFence??"```",listIndent:t?.listIndent??2}}function S(t){return{flavor:t.flavor,htmlFallback:t.htmlFallback}}var x=new Set(["\\","`","*","[","]","<",">"]);function v(t,n){const e=t[n-1],r=t[n+1];return void 0!==e&&void 0!==r&&/\w/.test(e)&&/\w/.test(r)}function M(t,n){let e="";for(let r=0;r<t.length;r++){const o=t[r]??"";x.has(o)?e+=`\\${o}`:"_"!==o||v(t,r)?e+="~"===o&&n?"\\~":"&"!==o?o:"&":e+="\\_"}return e}var F=/^(\s*)([#>])/,T=/^(\s*)([-*+])(\s)/,j=/^(\s*)(\d{1,9})([.)])(\s)/;function w(t){return F.test(t)?t.replace(F,(t,n,e)=>`${n}\\${e}`):T.test(t)?t.replace(T,(t,n,e,r)=>`${n}\\${e}${r}`):j.test(t)?t.replace(j,(t,n,e,r,o)=>`${n}${e}\\${r}${o}`):function(t){return d.test(t)||$.test(t)||g.test(t)||k.test(t)}(t)?t.replace(/\S/,t=>`\\${t}`):t}function L(t){return/[\s()]/.test(t)?`<${t.replace(/([<>])/g,"\\$1")}>`:t}function _(t){return t.replace(/(["\\])/g,"\\$1")}var H={styleAttr:t=>t?` style="${t}"`:""};function I(t,n){const e=t.type;return"link"===e||"bold"===e||"italic"===e||!("strikethrough"!==e||!n)}function N(t,n){const e=n.opts.emphasis;switch(t.type){case"bold":return{open:e.repeat(2),close:e.repeat(2)};case"italic":return{open:e,close:e};case"strikethrough":return{open:"~~",close:"~~"};default:{const n=String(t.attrs?.href??""),e=String(t.attrs?.title??"");return{open:"[",close:`](${L(n)}${e?` "${_(e)}"`:""})`}}}}function O(t){return"link"!==t.type}function A(t,n){return t.type===n.type&&JSON.stringify(t.attrs??{})===JSON.stringify(n.attrs??{})}function D(t,n){const e=[],r=[];for(const o of t.marks)I(o,n)&&("link"===o.type?e.push(o):r.push(o));return[...e,...r]}function W(t,n){const e=[],r=[...t];for(let o=r.length-1;o>=0;o--){const t=r[o];t&&!O(t)&&(e.push(t),r.splice(o,1))}for(const o of n){if(!O(o))continue;const t=r.findIndex(t=>A(t,o));if(-1!==t){const n=r[t];n&&e.push(n),r.splice(t,1)}}for(const o of r)e.push(o);return e}function z(t,n){if(""===t.text)return"";if(t.marks.some(t=>"code"===t.type))return function(t){const n=function(t){let n=0,e=0;for(const r of t)"`"===r?(e++,n=Math.max(n,e)):e=0;return"`".repeat(n+1)}(t);return`${n}${t.startsWith("`")||t.endsWith("`")||t.length>0&&0===t.trim().length?` ${t} `:t}${n}`}(t.text);const e=n.opts.gfm,r=t.marks.filter(t=>!I(t,e)&&"code"!==t.type);return r.length>0&&n.opts.htmlFallback?m(t.text,r,y(n.registry),n.markOrder,H):M(t.text,e)}function J(t,n){if("hard_break"===t.inlineType)return"\\\n";if("image_inline"===t.inlineType)return function(t,n){const e=String(t.attrs.src??""),r=String(t.attrs.alt??""),o=String(t.attrs.title??""),s=o?` "${_(o)}"`:"";return`}${s})`}(t,n);const e=n.registry?.getInlineNodeSpec(t.inlineType),r=e?.toMarkdown?.(t,S(n.opts));return null!=r?r:n.opts.htmlFallback&&e?.toHTMLString?e.toHTMLString(t):""}function P(t){const n=[];for(const e of t){const t=n[n.length-1];!o(e)&&t&&!o(t)&&r(t.marks,e.marks)?n[n.length-1]={type:"text",text:t.text+e.text,marks:t.marks}:n.push(e)}return n}function R(t,n){return!(!t||o(t))&&t.marks.some(t=>A(t,n))}function q(t,n){return 0===n.length?[...t]:t.filter(t=>!n.some(n=>A(n,t)))}function B(t,n,e,r,o){const s=t.marks.filter(t=>function(t,n){return I(t,n)&&"link"!==t.type}(t,r));if(0===s.length)return void o.push(t);const i=t.text,l=function(t){let n=0;for(;n<t.length&&/\s/.test(t[n]);)n++;return n}(i);if(l===i.length){const r=s.filter(t=>!(R(n,t)&&R(e,t)));return void o.push(c(i,q(t.marks,r)))}const a=function(t){let n=t.length;for(;n>0&&/\s/.test(t[n-1]);)n--;return t.length-n}(i),u=i.slice(0,l),f=i.slice(l,i.length-a),p=i.slice(i.length-a);if(u){const e=s.filter(t=>!R(n,t));o.push(c(u,q(t.marks,e)))}if(o.push(c(f,t.marks)),p){const n=s.filter(t=>!R(e,t));o.push(c(p,q(t.marks,n)))}}function E(t,n){const e=n.opts.gfm,r=function(t,n){const e=[];for(let r=0;r<t.length;r++){const s=t[r];s&&(o(s)?e.push(s):B(s,t[r-1],t[r+1],n,e))}return P(e)}(P(t),e);let s="",i=[];const l=t=>{for(let e=i.length-1;e>=t;e--){const t=i[e];t&&(s+=N(t,n).close)}i=i.slice(0,t)};for(const c of r){const t=W(D(c,e),i);let r=0;for(;r<i.length&&r<t.length&&A(i[r],t[r]);)r++;l(r);for(let e=r;e<t.length;e++){const r=t[e];r&&(s+=N(r,n).open,i.push(r))}s+=o(c)?J(c,n):z(c,n)}return l(0),s}function U(t,n){const e=[];let r=0;for(;r<t.length;){const o=t[r];if(o)if(a(o,"list_item")){const o=[];for(;r<t.length;){const n=t[r];if(!n||!a(n,"list_item"))break;o.push(n),r++}e.push(n.opts.htmlFallback&&o.some(G)?K(o,n):V(o,n))}else e.push(C(o,n)),r++;else r++}return e.filter(t=>t.length>0).join("\n\n")}function C(t,n){if(n.opts.htmlFallback&&G(t))return K([t],n);const r=t.type;return"paragraph"===r?function(t,n){const e=E(i(t),n);return""===e.trim()?"":e.split("\n").map(w).join("\n")}(t,n):"heading"===r?function(t,n){const e=function(t){return t<1?1:t>6?6:Math.floor(t)}(t.attrs?.level??1),r=E(i(t),n).replace(/\n/g," ");if("setext"===n.opts.headingStyle&&e<=2&&""!==r)return`${r}\n${(1===e?"=":"-").repeat(Math.max(3,r.length))}`;const o="#".repeat(e);return r?`${o} ${r}`:o}(t,n):"code_block"===r?function(t,n){const e=String(t.attrs?.language??""),r=s(t),o=n.opts.codeFence[0]??"`",i=o.repeat(Math.max(3,function(t,n){let e=0,r=0;for(const o of t)o===n?(r++,e=Math.max(e,r)):r=0;return e}(r,o)+1));return`${i}${e}\n${r}\n${i}`}(t,n):"horizontal_rule"===r?n.inListItem?"***":"---":"image"===r?function(t,n){const e=String(t.attrs?.src??""),r=String(t.attrs?.alt??""),o=String(t.attrs?.title??""),s=t.attrs?.width,i=t.attrs?.height,l=t.attrs?.align;if((void 0!==s||void 0!==i||void 0!==l&&"center"!==l)&&n.opts.htmlFallback){const e=n.registry?.getNodeSpec("image")?.toHTML?.(t,"");if(e)return e}const c=o?` "${_(o)}"`:"";return`}${c})`}(t,n):"blockquote"===r?function(t,n){return U(l(t),n).split("\n").map(t=>""===t?">":`> ${t}`).join("\n")}(t,n):"table"===r?function(t,n){const e=l(t);if(0===e.length)return"";if(!n.opts.gfm&&n.opts.htmlFallback)return K([t],n);if(n.opts.htmlFallback&&(function(t){for(const n of t)for(const t of l(n)){const n=t.attrs?.colspan,e=t.attrs?.rowspan;if((n??1)>1||(e??1)>1)return!0}return!1}(e)||function(t,n){return!!h(t.attrs?.columnWidthsPx)||n.some(t=>void 0!==f(t.attrs?.minHeightPx))}(t,e)))return K([t],n);const r=e.map(t=>l(t).map(t=>function(t,n){return l(t).map(t=>E(i(t),n)).join(" ").replace(/\n/g," ").replace(/\|/g,"\\|").trim()}(t,n))),o=r.reduce((t,n)=>Math.max(t,n.length),0);if(0===o)return"";const s=t=>{const n=[...t];for(;n.length<o;)n.push("");return`| ${n.join(" | ")} |`},c=e[0],a=Array.from({length:o},(t,n)=>{return e=function(t,n){if(!t)return;const e=l(t)[n];return e?l(e)[0]?.attrs?.align:void 0}(c,n),"start"===e?":---":"center"===e?":---:":"end"===e?"---:":"---";var e}),u=[];u.push(s(r[0]??[])),u.push(`| ${a.join(" | ")} |`);for(let i=1;i<r.length;i++)u.push(s(r[i]??[]));return u.join("\n")}(t,n):"title"===r?Q(t,n,1):"subtitle"===r?Q(t,n,2):function(t,n){const r=n.registry?.getNodeSpec(t.type),o=e(t)?E(i(t),n):U(l(t),n),s=r?.toMarkdown?.(t,o,S(n.opts));return null!=s?s:n.opts.htmlFallback&&r?.toHTML?r.toHTML(t,o):o}(t,n)}function G(t){return!!n(t.htmlId)||l(t).some(G)}function K(t,n){return p({children:t},y(n.registry),{includeBlockIds:!1})}function Q(t,n,e){if(n.opts.htmlFallback){const e=E(i(t),n),r=n.registry?.getNodeSpec(t.type)?.toHTML?.(t,e);if(r)return r}const r=E(i(t),n).replace(/\n/g," "),o="#".repeat(e);return r?`${o} ${r}`:o}function V(t,n){const r=[],o=[],s=[];let c=-1;for(const a of t){const t=String(a.attrs?.listType??"bullet"),u=Math.max(0,a.attrs?.indent??0),f=Math.min(u,c+1);c=f;const p=0===f?0:s[f-1]??0,h=" ".repeat(p);let m;if(o.length=f+1,s.length=f+1,"ordered"===t)o[f]=(o[f]??0)+1,m=`${o[f]}.`;else if("checklist"===t){const t=!0===a.attrs?.checked;m=`${n.opts.bullet} [${t?"x":" "}]`}else m=n.opts.bullet;const g="checklist"===t?n.opts.bullet.length+1:m.length+1;if(s[f]=p+g,e(a)){const t=w(E(i(a),n).replace(/\n/g," "));r.push(t?`${h}${m} ${t}`:`${h}${m}`);continue}const $=U(l(a),{...n,inListItem:!0});if(""===$){r.push(`${h}${m}`);continue}const d=" ".repeat(s[f]??p+g),k=$.split("\n");r.push(`${h}${m} ${k[0]??""}`);for(let n=1;n<k.length;n++){const t=k[n]??"";r.push(""===t?"":`${d}${t}`)}}return r.join("\n")}var X=/* @__PURE__ */t({serializeDocumentToMarkdown:()=>Y});function Y(t,n,e){const r={registry:n,opts:b(e),markOrder:n?u(n):void 0};return function(t){const n=t.trim();return n.length>0?`${n}\n`:""}(U(t.children,r))}export{Y as n,X as t};
|
|
2
|
+
//# sourceMappingURL=MarkdownSerializer-DBELFAeo.mjs.map
|