@portabletext/markdown 2.1.0 → 2.2.1
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 +145 -3
- package/dist/index.d.ts +415 -186
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +936 -43
- package/dist/index.js.map +1 -1
- package/package.json +5 -6
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/key-generator.ts","../src/from-portable-text/build-list-index-map.ts","../src/from-portable-text/escape-plain-text.ts","../src/from-portable-text/list-item-first-block.ts","../src/from-portable-text/render-node.ts","../src/from-portable-text/renderers/block-spacing.ts","../src/from-portable-text/renderers/hard-break.ts","../src/from-portable-text/renderers/list-item.ts","../src/escape.ts","../src/from-portable-text/renderers/marks.ts","../src/from-portable-text/renderers/style.ts","../src/from-portable-text/renderers/type.ts","../src/from-portable-text/portable-text-to-markdown.ts","../src/default-schema.ts","../src/to-portable-text/degradation-messages.ts","../src/to-portable-text/matchers.ts","../src/to-portable-text/markdown-to-portable-text.ts"],"sourcesContent":["export function defaultKeyGenerator() {\n return randomKey(12)\n}\n\nconst getByteHexTable = (() => {\n let table: any[]\n return () => {\n if (table) {\n return table\n }\n\n table = []\n for (let i = 0; i < 256; ++i) {\n table[i] = (i + 0x100).toString(16).slice(1)\n }\n return table\n }\n})()\n\n// WHATWG crypto RNG - https://w3c.github.io/webcrypto/Overview.html\nfunction whatwgRNG(length = 16) {\n const rnds8 = new Uint8Array(length)\n crypto.getRandomValues(rnds8)\n return rnds8\n}\n\nfunction randomKey(length?: number): string {\n const table = getByteHexTable()\n return whatwgRNG(length)\n .reduce((str, n) => str + table[n], '')\n .slice(0, length)\n}\n","import {\n compileSchema,\n defineSchema,\n isTextBlock,\n type PortableTextBlock,\n} from '@portabletext/schema'\nimport type {ArbitraryTypedObject, TypedObject} from '@portabletext/types'\nimport {defaultKeyGenerator} from '../key-generator'\n\nconst schema = compileSchema(defineSchema({}))\n\n/**\n * Builds a map of list item `_key`s to their index, and a map of list item\n * `_key`s to the depth they should be rendered at.\n *\n * The depth is not the same as the block's `level`. A list can start at a level\n * deeper than 1, and can skip levels, but Markdown has no way to express either:\n * indentation is relative to the list item above, and indenting a first item by\n * four spaces or more makes it a code block rather than a list. So each jump to a\n * deeper level counts as a single step of nesting, however many levels it spans.\n *\n * Mutates the blocks in place by adding a `_key` if necessary.\n */\nexport function buildListIndexMap<\n Block extends TypedObject = PortableTextBlock | ArbitraryTypedObject,\n>(\n blocks: Array<Block>,\n): {listIndexMap: Map<string, number>; listDepthMap: Map<string, number>} {\n const levelIndexMaps = new Map<string, Map<number, number>>()\n const listIndexMap = new Map<string, number>()\n const listDepthMap = new Map<string, number>()\n\n // Levels of the list items this one is nested inside, shallowest first\n let levelStack: Array<number> = []\n\n function depthOf(level: number): number {\n let deepest = levelStack.at(-1)\n\n while (deepest !== undefined && deepest > level) {\n levelStack.pop()\n deepest = levelStack.at(-1)\n }\n\n if (deepest !== level) {\n levelStack.push(level)\n }\n\n return levelStack.length - 1\n }\n\n let previousListItem:\n | {\n listItem: string\n depth: number\n }\n | undefined\n\n for (let blockIndex = 0; blockIndex < blocks.length; blockIndex++) {\n const block = blocks.at(blockIndex)\n\n if (block === undefined) {\n continue\n }\n\n if (!block._key) {\n block._key = defaultKeyGenerator()\n }\n\n // Clear the state if we encounter a non-text block\n if (!isTextBlock({schema}, block)) {\n levelIndexMaps.clear()\n previousListItem = undefined\n levelStack = []\n\n continue\n }\n\n // Clear the state if we encounter a non-list text block\n if (block.listItem === undefined || block.level === undefined) {\n levelIndexMaps.clear()\n previousListItem = undefined\n levelStack = []\n\n continue\n }\n\n const depth = depthOf(block.level)\n listDepthMap.set(block._key, depth)\n\n // If we encounter a new list item, we set the initial index to 1 for the\n // list type on that level.\n if (!previousListItem) {\n const listIndex = 1\n const levelIndexMap =\n levelIndexMaps.get(block.listItem) ?? new Map<number, number>()\n levelIndexMap.set(depth, listIndex)\n levelIndexMaps.set(block.listItem, levelIndexMap)\n\n listIndexMap.set(block._key, listIndex)\n\n previousListItem = {\n listItem: block.listItem,\n depth,\n }\n\n continue\n }\n\n // If the previous list item is of the same type but on a lower level, we\n // need to reset the level index map for that type.\n if (\n previousListItem.listItem === block.listItem &&\n previousListItem.depth < depth\n ) {\n const listIndex = 1\n const levelIndexMap =\n levelIndexMaps.get(block.listItem) ?? new Map<number, number>()\n levelIndexMap.set(depth, listIndex)\n levelIndexMaps.set(block.listItem, levelIndexMap)\n\n listIndexMap.set(block._key, listIndex)\n\n previousListItem = {\n listItem: block.listItem,\n depth,\n }\n\n continue\n }\n\n // Reset other list types at current depth and deeper\n levelIndexMaps.forEach((levelIndexMap, listItem) => {\n if (listItem === block.listItem) {\n return\n }\n\n // Reset all levels that are >= current level\n const depthsToDelete: number[] = []\n\n levelIndexMap.forEach((_, existingDepth) => {\n if (existingDepth >= depth) {\n depthsToDelete.push(existingDepth)\n }\n })\n\n depthsToDelete.forEach((depthToDelete) => {\n levelIndexMap.delete(depthToDelete)\n })\n })\n\n const levelIndexMap =\n levelIndexMaps.get(block.listItem) ?? new Map<number, number>()\n const levelCounter = levelIndexMap.get(depth) ?? 0\n levelIndexMap.set(depth, levelCounter + 1)\n levelIndexMaps.set(block.listItem, levelIndexMap)\n\n listIndexMap.set(block._key, levelCounter + 1)\n\n previousListItem = {\n listItem: block.listItem,\n depth,\n }\n }\n\n return {listIndexMap, listDepthMap}\n}\n","import {isPortableTextSpan} from '@portabletext/toolkit'\nimport type {\n ArbitraryTypedObject,\n PortableTextMarkDefinition,\n PortableTextSpan,\n} from '@portabletext/types'\nimport LinkifyIt from 'linkify-it'\n\n/**\n * The CommonMark ASCII punctuation set. Only these characters can be\n * backslash-escaped into a literal without changing the parsed text.\n */\nconst ASCII_PUNCTUATION = /[!-/:-@[-`{-~]/\n\nconst ENTITY_REFERENCE = /&(?:[a-zA-Z][a-zA-Z0-9]*|#[0-9]+|#[xX][0-9a-fA-F]+);/g\nconst BACKSLASH_BEFORE_PUNCTUATION = new RegExp(\n `\\\\\\\\(?=${ASCII_PUNCTUATION.source})`,\n 'g',\n)\nconst TILDE_RUN = /~{2,}/g\nconst HTML_LIKE_ANGLE_BRACKET = /<(?=[a-zA-Z/!?])/g\nconst BRACKET_BEFORE_LINK_OPEN = /\\](?=[([])/g\n\n/**\n * markdown-it's own emphasis-flanking rule classifies a delimiter's\n * neighbor as punctuation using Unicode's `P` (Punctuation) and `S`\n * (Symbol) categories, not just ASCII: an emoji, an em dash, or a CJK\n * character flanks a `*`/`_` run exactly like an ASCII punctuation\n * character does, so testing ASCII alone would fabricate emphasis next to\n * non-ASCII text the real reparse wouldn't create. Matched against one\n * full code point at a time (a surrogate pair for an astral character\n * like an emoji), never a lone UTF-16 code unit, which the category\n * classes never match.\n */\nconst UNICODE_PUNCTUATION_OR_SYMBOL = /^(?:\\p{P}|\\p{S})$/u\n\n// Configured with no options, exactly like markdown-it constructs its own\n// `md.linkify` instance: same default schemas (http/https/ftp/'//'/mailto)\n// and the same built-in TLD list, so a range this reports as a link is a\n// range the real reparse will claim too.\nconst linkify = new LinkifyIt()\n\ntype LeafPiece =\n | {kind: 'text'; raw: string; isLinkLabel: boolean; markSignature: string}\n | {kind: 'hardBreak'}\n | {kind: 'opaque'}\n\n/**\n * Stands in for one opaque child (an inline object, or a leaf a custom\n * renderer will replace) in a joined line's text. It can never match a\n * hazard's trigger character, so it safely walls off an in-progress\n * construct (an ordered-list marker, a ref-def label) without needing a\n * dedicated flag, while still counting as real, non-whitespace content for\n * emphasis-flanking purposes.\n */\nconst OPAQUE_CHAR = '\\0'\n\n/** Which leaf (by index into the flat `pieces` list) a line character came\n * from, and at what offset into that leaf's *prepared* text (raw text, with\n * a link-label leaf's brackets/backslashes already doubled). `null` marks\n * an opaque character: it has no leaf-local home, so no edit can target it. */\ninterface LineChar {\n pieceIndex: number\n offset: number\n}\n\n/** A single-position splice against a joined line's raw text: remove\n * `deleteCount` characters at `at` and put `insert` in their place. A pure\n * insertion (a backslash escape) has `deleteCount: 0`. */\ninterface Edit {\n at: number\n deleteCount: number\n insert: string\n /**\n * Set for an entity-reference or backtick escape: both change what\n * markdown-it's inline parser hands to its linkify pass (an entity\n * decodes first, a backtick can open a code span first), so the linkify\n * mask - built from this line's raw, undecoded text - can never be\n * trusted to have already accounted for them. Kept regardless of any\n * linkify claim overlapping it.\n */\n bypassLinkifyMask?: boolean\n}\n\n/**\n * Plans the escaped replacement for every plain-text leaf a block's children\n * will produce, in the exact left-to-right order `renderText` visits them\n * (mirroring `buildMarksTree`'s own `text.split('\\n')` leaf splitting).\n *\n * Escaping runs ahead of rendering, over the flat span sequence: some\n * hazards only exist across a leaf boundary (an ordered-list marker, a\n * ref-def label, an emphasis run) because an annotation or decorator mark\n * that introduces no markup of its own splices its children in seamlessly.\n * The plan works line by line (a block's children joined into text, split\n * at hard breaks) rather than leaf by leaf: each line's leaves are joined\n * into one string first, opaque children (inline objects, or leaves a\n * custom renderer will replace) masked with a sentinel that can't match any\n * hazard, and every hazard - inline and line-start alike - is detected once\n * against that real, complete line, with true left/right context on both\n * sides. Detected hazards become position-tracked edits against the line's\n * raw text, which are then split back into each contributing leaf's own\n * escaped text; only that composition step is leaf-scoped.\n *\n * A joined line's text that markdown-it's own linkify pass (bundled as\n * `linkify-it`) would claim as a bare URL or email is masked from most\n * edits: the linkify carve-out promises that substring round-trips\n * byte-identical, gaining only a link mark, so escaping inside it would\n * corrupt text linkify is about to claim as a link's visible text. An\n * entity-reference or backtick escape is never masked (see `computeLinkifyMask`\n * for why), and a claim spliced across a decorator boundary is never masked\n * in the first place.\n *\n * `isHeading` is set for ATX headings: only the first joined line sits\n * inside the `# ` prefix an ATX heading can never be reparsed as a block\n * construct within, so line-leading hazards are skipped there; a hard\n * break's later lines are ordinary markdown lines and get the full\n * line-start battery. That first line carries a line-*end* hazard of its\n * own instead: a trailing `#`-run reads back as the heading's own optional\n * closing sequence.\n *\n * `isListItem` is set when the block renders as list-item content: a\n * `[ ] `/`[x] `/`[X] ` at the very start of the first joined line reads\n * back as a GFM task-list checkbox, regardless of the list's own item type.\n *\n * `hardBreakOutputHasNewline` says whether the renderer's actual hard-break\n * output contains a newline. A custom `hardBreak` can render to something\n * with no newline of its own (eg `() => '<br />'`), in which case the\n * leaves on either side of it land on the same rendered line, not two: a\n * hard break like that can't be planned as a line boundary, so it's walled\n * off as an opaque segment instead, the same protection an inline object's\n * unknown rendered text already gets.\n */\nexport function planLeafEscaping(\n children: ReadonlyArray<PortableTextSpan | ArbitraryTypedObject>,\n markDefs: ReadonlyArray<PortableTextMarkDefinition>,\n options: {\n isHeading: boolean\n isListItem: boolean\n hardBreakOutputHasNewline: boolean\n },\n): Array<string> {\n const linkMarkKeys = new Set(\n markDefs.filter((def) => def._type === 'link').map((def) => def._key),\n )\n // Every markDef key a span's marks can reference (any annotation, not\n // just link): what's left after removing those from a span's `marks` is\n // its decorator set, the only marks `buildMarksTree` reliably renders as\n // delimiters by default (an unregistered annotation type falls back to\n // passing its children through unchanged, same as an unknown decorator).\n const markDefKeys = new Set(markDefs.map((def) => def._key))\n const pieces: Array<LeafPiece> = []\n\n for (const child of children) {\n if (isPortableTextSpan(child)) {\n const isLinkLabel = (child.marks ?? []).some((mark) =>\n linkMarkKeys.has(mark),\n )\n // Sorted so two spans carrying the same decorators in a different\n // order still compare equal: `buildMarksTree` nests by decorator\n // identity, not by a span's own array order, so it produces the same\n // markup boundaries either way.\n const markSignature = (child.marks ?? [])\n .filter((mark) => !markDefKeys.has(mark))\n .sort()\n .join(',')\n const lines = child.text.split('\\n')\n lines.forEach((line, index) => {\n if (index > 0) {\n pieces.push(\n options.hardBreakOutputHasNewline\n ? {kind: 'hardBreak'}\n : {kind: 'opaque'},\n )\n }\n pieces.push({kind: 'text', raw: line, isLinkLabel, markSignature})\n })\n } else {\n pieces.push({kind: 'opaque'})\n }\n }\n\n const pieceOutputs: Array<string> = pieces.map(() => '')\n\n let lineIndex = 0\n let lineText = ''\n let lineChars: Array<LineChar | null> = []\n let lineIsLinkLabelChar: Array<boolean> = []\n let lineMarkSignature: Array<string> = []\n\n const flushLine = () => {\n processLine({\n text: lineText,\n chars: lineChars,\n isLinkLabelChar: lineIsLinkLabelChar,\n markSignature: lineMarkSignature,\n lineIndex,\n isHeading: options.isHeading,\n isListItem: options.isListItem,\n pieceOutputs,\n })\n lineIndex++\n lineText = ''\n lineChars = []\n lineIsLinkLabelChar = []\n lineMarkSignature = []\n }\n\n for (let pieceIndex = 0; pieceIndex < pieces.length; pieceIndex++) {\n const piece = pieces[pieceIndex]\n\n if (!piece || piece.kind === 'hardBreak') {\n flushLine()\n continue\n }\n\n if (piece.kind === 'opaque') {\n lineText += OPAQUE_CHAR\n lineChars.push(null)\n lineIsLinkLabelChar.push(false)\n lineMarkSignature.push('')\n continue\n }\n\n // A link label's brackets and backslashes are escaped unconditionally,\n // up front: a label must stay bracket-balanced regardless of context,\n // so this doesn't depend on anything the line-level hazard scan below\n // discovers.\n const prepared = piece.isLinkLabel\n ? escapeLinkLabelBrackets(piece.raw)\n : piece.raw\n\n for (let offset = 0; offset < prepared.length; offset++) {\n lineText += prepared[offset]\n lineChars.push({pieceIndex, offset})\n lineIsLinkLabelChar.push(piece.isLinkLabel)\n lineMarkSignature.push(piece.markSignature)\n }\n }\n flushLine()\n\n const escaped: Array<string> = []\n pieces.forEach((piece, index) => {\n if (piece.kind === 'text') {\n escaped.push(pieceOutputs[index] ?? '')\n }\n })\n return escaped\n}\n\nfunction processLine(args: {\n text: string\n chars: Array<LineChar | null>\n isLinkLabelChar: Array<boolean>\n markSignature: Array<string>\n lineIndex: number\n isHeading: boolean\n isListItem: boolean\n pieceOutputs: Array<string>\n}): void {\n const {text, chars, isLinkLabelChar, markSignature, pieceOutputs} = args\n\n const linkifyMask = computeLinkifyMask(\n text,\n chars,\n isLinkLabelChar,\n markSignature,\n )\n const edits = [\n ...collectInlineEdits(text, isLinkLabelChar),\n ...collectLineStartEdits(text, args),\n ].filter((edit) => edit.bypassLinkifyMask || !isMasked(edit, linkifyMask))\n\n applyEdits(text, chars, edits, pieceOutputs)\n}\n\n/**\n * Marks every character of this line that markdown-it's linkify pass would\n * claim as part of a bare URL or email. Link-label and opaque characters\n * are blanked out first: a link label's visible text sits inside `[...]`\n * markup real linkify never reconsiders, and an opaque child's rendered\n * text is unknown at plan time, so neither should join or seed a match.\n *\n * The probe only sees this line's raw, undecoded text, one hazard pass\n * ahead of markdown-it's own pipeline: it runs linkify against inline\n * tokenization and entity decoding, not before them. A claim survives only\n * if it lies entirely inside one run of identical decorator marks: a\n * decorator boundary crossing it splices that decorator's delimiters\n * (`**`, `` ` ``, ...) into the middle of the range real linkify would see,\n * which breaks the very claim being trusted. An annotation-only boundary\n * (a link's own label text is already excluded above; any other\n * annotation type falls back to rendering with no delimiters at all,\n * same as an unregistered decorator) never splices, so it can't invalidate\n * a claim either.\n */\nfunction computeLinkifyMask(\n text: string,\n chars: Array<LineChar | null>,\n isLinkLabelChar: Array<boolean>,\n markSignature: Array<string>,\n): Array<boolean> {\n const mask = new Array<boolean>(text.length).fill(false)\n\n // Every schema `linkify-it`'s default config recognizes - `http(s):`,\n // `ftp:`, `//`, `www.`, and a `user@host` email - needs a `.`, `:`, or\n // `@` somewhere in the line; skipping the match call on a line with\n // none of those is the cheap majority-case exit, not a heuristic that\n // could miss a real claim.\n if (!/[.:@]/.test(text)) {\n return mask\n }\n\n let probe = ''\n for (let index = 0; index < text.length; index++) {\n probe += chars[index] === null || isLinkLabelChar[index] ? ' ' : text[index]\n }\n\n const matches = linkify.match(probe) ?? []\n for (const match of matches) {\n if (match.schema === '') {\n // A fuzzy claim (`www.`, bare domain): markdown-it's emphasis pass\n // beats these on reparse, so paired `*`/`_`/`~~` inside one is\n // consumed as markup and the characters vanish. Only explicit-scheme\n // claims (`http:`, `mailto:`, ...) win whole against inline\n // constructs; fuzzy forms take normal escaping instead, trading\n // their link mark for text fidelity.\n continue\n }\n const signature = markSignature[match.index]\n let staysWithinOneMarkRun = true\n for (let index = match.index; index < match.lastIndex; index++) {\n if (markSignature[index] !== signature) {\n staysWithinOneMarkRun = false\n break\n }\n }\n if (!staysWithinOneMarkRun) {\n continue\n }\n for (let index = match.index; index < match.lastIndex; index++) {\n mask[index] = true\n }\n }\n return mask\n}\n\nfunction isMasked(edit: Edit, mask: ReadonlyArray<boolean>): boolean {\n const end = edit.at + Math.max(edit.deleteCount, 1)\n for (let index = edit.at; index < end; index++) {\n if (mask[index]) {\n return true\n }\n }\n return false\n}\n\n/** Rewrites a line's raw text into each contributing leaf's escaped text by\n * walking it once, left to right, applying at most one edit per position.\n * Every hazard is keyed off its own trigger character - a backslash, a\n * tilde, a backtick, an `&`, a `<`, a `*`/`_`, a `]`, or (line-start only,\n * one hazard per line) a `#`, `>`, `[`, `-`/`+`/`*`, the `.`/`)` after an\n * ordered-list marker's digits, `=`, 4 spaces, or a tab - and no two of\n * those characters coincide at one position, so two edits can never target\n * the same position. */\nfunction applyEdits(\n text: string,\n chars: ReadonlyArray<LineChar | null>,\n edits: ReadonlyArray<Edit>,\n pieceOutputs: Array<string>,\n): void {\n const editsByPosition = new Map<number, Edit>()\n for (const edit of edits) {\n if (editsByPosition.has(edit.at)) {\n throw new Error(\n `Two hazard edits targeted the same position (${edit.at}); ` +\n 'hazard trigger characters are assumed disjoint by construction.',\n )\n }\n editsByPosition.set(edit.at, edit)\n }\n\n let index = 0\n while (index < text.length) {\n const edit = editsByPosition.get(index)\n const owner = chars[index]\n\n if (edit) {\n if (owner) {\n pieceOutputs[owner.pieceIndex] =\n (pieceOutputs[owner.pieceIndex] ?? '') + edit.insert\n }\n if (edit.deleteCount > 0) {\n index += edit.deleteCount\n continue\n }\n }\n\n if (owner) {\n pieceOutputs[owner.pieceIndex] =\n (pieceOutputs[owner.pieceIndex] ?? '') + (text[index] ?? '')\n }\n index++\n }\n}\n\n/**\n * Hazards that can appear anywhere on a line: emphasis/strikethrough runs,\n * a backtick, an entity reference, an HTML/autolink-shaped `<`, a literal\n * backslash before punctuation, and a `]` immediately before `(`/`[`\n * (which would otherwise read back as a link/image open).\n */\nfunction collectInlineEdits(\n text: string,\n isLinkLabelChar: ReadonlyArray<boolean>,\n): Array<Edit> {\n const edits: Array<Edit> = []\n\n for (const match of text.matchAll(BACKSLASH_BEFORE_PUNCTUATION)) {\n const at = match.index ?? 0\n // A link label's backslashes were already doubled unconditionally\n // while preparing its text; doubling them again here would flip their\n // parity back to unescaped.\n if (!isLinkLabelChar[at]) {\n edits.push({at, deleteCount: 0, insert: '\\\\'})\n }\n }\n\n for (const match of text.matchAll(TILDE_RUN)) {\n const start = match.index ?? 0\n for (let index = start; index < start + match[0].length; index++) {\n edits.push({at: index, deleteCount: 0, insert: '\\\\'})\n }\n }\n\n for (let index = 0; index < text.length; index++) {\n if (text[index] === '`') {\n // Every backtick is escaped outright: even a lone one can pair with\n // another lone backtick elsewhere to open a code span, which the\n // linkify mask can't see coming - a code span forms during inline\n // tokenization, before linkify ever runs - so this bypasses it.\n edits.push({\n at: index,\n deleteCount: 0,\n insert: '\\\\',\n bypassLinkifyMask: true,\n })\n }\n }\n\n for (const match of text.matchAll(ENTITY_REFERENCE)) {\n // An entity reference decodes before linkify runs, so a masked range\n // built from this line's raw text can't already account for it.\n edits.push({\n at: match.index ?? 0,\n deleteCount: 0,\n insert: '\\\\',\n bypassLinkifyMask: true,\n })\n }\n\n for (const match of text.matchAll(HTML_LIKE_ANGLE_BRACKET)) {\n edits.push({at: match.index ?? 0, deleteCount: 0, insert: '\\\\'})\n }\n\n edits.push(...collectEmphasisEdits(text))\n\n for (const match of text.matchAll(BRACKET_BEFORE_LINK_OPEN)) {\n const at = match.index ?? 0\n // A link label's `]` was already escaped unconditionally while\n // preparing its text (`escapeLinkLabelBrackets`); escaping it again\n // here would double the backslash and reopen the label early on\n // reparse, same reasoning as the backslash rule above.\n if (!isLinkLabelChar[at]) {\n edits.push({at, deleteCount: 0, insert: '\\\\'})\n }\n }\n\n return edits\n}\n\nfunction isWhitespace(char: string | undefined): boolean {\n return char === undefined || /\\s/.test(char)\n}\n\nfunction isPunctuation(char: string | undefined): boolean {\n return char !== undefined && UNICODE_PUNCTUATION_OR_SYMBOL.test(char)\n}\n\n/**\n * The full code point sitting immediately before `index`: two UTF-16 code\n * units for an astral character (eg an emoji) whose low surrogate lands at\n * `index - 1`, one otherwise.\n */\nfunction codePointBefore(text: string, index: number): string | undefined {\n if (index <= 0) {\n return undefined\n }\n if (\n index >= 2 &&\n isLowSurrogate(text[index - 1]) &&\n isHighSurrogate(text[index - 2])\n ) {\n return text.slice(index - 2, index)\n }\n return text[index - 1]\n}\n\n/**\n * The full code point sitting immediately at `index`: two UTF-16 code units\n * for an astral character whose high surrogate lands at `index`, one\n * otherwise.\n */\nfunction codePointAt(text: string, index: number): string | undefined {\n if (index >= text.length) {\n return undefined\n }\n if (isHighSurrogate(text[index]) && isLowSurrogate(text[index + 1])) {\n return text.slice(index, index + 2)\n }\n return text[index]\n}\n\nfunction isHighSurrogate(char: string | undefined): boolean {\n if (char === undefined) {\n return false\n }\n const code = char.charCodeAt(0)\n return code >= 0xd800 && code <= 0xdbff\n}\n\nfunction isLowSurrogate(char: string | undefined): boolean {\n if (char === undefined) {\n return false\n }\n const code = char.charCodeAt(0)\n return code >= 0xdc00 && code <= 0xdfff\n}\n\nfunction isLeftFlanking(\n before: string | undefined,\n after: string | undefined,\n): boolean {\n if (isWhitespace(after)) {\n return false\n }\n return !isPunctuation(after) || isWhitespace(before) || isPunctuation(before)\n}\n\nfunction isRightFlanking(\n before: string | undefined,\n after: string | undefined,\n): boolean {\n if (isWhitespace(before)) {\n return false\n }\n return !isPunctuation(before) || isWhitespace(after) || isPunctuation(after)\n}\n\n/**\n * Finds `*`/`_` runs CommonMark would treat as flanking delimiters, using\n * each run's true neighbors on the joined line (the start/end of the line\n * itself counts as whitespace, matching the spec's treatment of line\n * boundaries).\n */\nfunction collectEmphasisEdits(text: string): Array<Edit> {\n const edits: Array<Edit> = []\n let index = 0\n\n while (index < text.length) {\n const char = text[index]\n\n if (char !== '*' && char !== '_') {\n index++\n continue\n }\n\n let end = index\n while (end < text.length && text[end] === char) {\n end++\n }\n\n const before = codePointBefore(text, index)\n const after = codePointAt(text, end)\n const leftFlanking = isLeftFlanking(before, after)\n const rightFlanking = isRightFlanking(before, after)\n\n const canOpen =\n char === '_'\n ? leftFlanking && (!rightFlanking || isPunctuation(before))\n : leftFlanking\n const canClose =\n char === '_'\n ? rightFlanking && (!leftFlanking || isPunctuation(after))\n : rightFlanking\n\n if (canOpen || canClose) {\n for (let position = index; position < end; position++) {\n edits.push({at: position, deleteCount: 0, insert: '\\\\'})\n }\n }\n\n index = end\n }\n\n return edits\n}\n\n/**\n * Hazards that only matter at the start (or, for a handful of whole-line\n * constructs, the start *and* end) of a line: headings, blockquotes, list\n * markers, ref-defs, setext underlines, thematic breaks, indented code, and\n * a list item's own GFM task-checkbox prefix. A fence needs no branch of\n * its own here: the inline backtick/tilde escaping every line already\n * neutralizes the run a fence needs, so it can never open one on reparse.\n * The remaining branches are mutually exclusive by construction\n * (each targets a disjoint leading character) and return as soon as one\n * matches, mirroring how CommonMark itself commits to one block-start\n * interpretation per line; the checkbox branch above is the one exception,\n * since a list item's checkbox prefix and, say, its heading marker are two\n * independent hazards that can both apply to the same first line.\n */\nfunction collectLineStartEdits(\n text: string,\n context: {isHeading: boolean; isListItem: boolean; lineIndex: number},\n): Array<Edit> {\n const edits: Array<Edit> = []\n const isFirstLine = context.lineIndex === 0\n\n // CommonMark allows up to 3 leading spaces before a block marker without\n // affecting how it's parsed, so every hazard below (including the GFM\n // task-checkbox the parser's own pre-pass looks for, after its own\n // leading-whitespace trim) checks what follows them; the escape itself\n // still has to land right before the marker, not at the front of those\n // spaces (a backslash-space isn't an escape).\n const leadingSpaces = /^ {0,3}/.exec(text)?.[0].length ?? 0\n const rest = text.slice(leadingSpaces)\n\n if (context.isListItem && isFirstLine && /^\\[[ xX]\\] /.test(rest)) {\n edits.push({at: leadingSpaces, deleteCount: 0, insert: '\\\\'})\n }\n\n if (context.isHeading && isFirstLine) {\n // A closing sequence must be preceded by a space, unless it's *all*\n // the heading has: then the space ATX headings require after their\n // opening `#`s stands in for it.\n const closingSequence = /^(?:(.*[ \\t]))?(#+[ \\t]*)$/.exec(text)\n if (closingSequence) {\n edits.push({\n at: closingSequence[1]?.length ?? 0,\n deleteCount: 0,\n insert: '\\\\',\n })\n }\n return edits\n }\n\n const orderedListMarker = /^ {0,3}(\\d{1,9})([.)])(?=[ \\t]|$)/.exec(text)\n if (orderedListMarker) {\n edits.push({\n at: orderedListMarker[0].length - 1,\n deleteCount: 0,\n insert: '\\\\',\n })\n return edits\n }\n\n if (/^#{1,6}(?:[ \\t]|$)/.test(rest)) {\n edits.push({at: leadingSpaces, deleteCount: 0, insert: '\\\\'})\n return edits\n }\n\n if (rest.startsWith('>')) {\n edits.push({at: leadingSpaces, deleteCount: 0, insert: '\\\\'})\n return edits\n }\n\n if (/^\\[[^\\]\\n]*\\]:/.test(rest)) {\n edits.push({at: leadingSpaces, deleteCount: 0, insert: '\\\\'})\n return edits\n }\n\n if (/^[-+*](?:[ \\t]|$)/.test(rest)) {\n edits.push({at: leadingSpaces, deleteCount: 0, insert: '\\\\'})\n return edits\n }\n\n // CommonMark 4.1 allows interior spaces/tabs between a thematic break's\n // delimiter characters.\n if (/^ {0,3}([-*_])(?:[ \\t]*\\1){2,}[ \\t]*$/.test(text)) {\n edits.push({at: leadingSpaces, deleteCount: 0, insert: '\\\\'})\n return edits\n }\n\n if (/^ {0,3}=+[ \\t]*$/.test(text)) {\n edits.push({at: leadingSpaces, deleteCount: 0, insert: '\\\\'})\n return edits\n }\n\n if (/^ {0,3}-+[ \\t]*$/.test(text)) {\n edits.push({at: leadingSpaces, deleteCount: 0, insert: '\\\\'})\n return edits\n }\n\n if (/^ {4}/.test(text)) {\n // A numeric character reference decodes to the same literal character\n // during inline parsing, after block structure (and its indented-code\n // -block rule, 4 columns of leading whitespace) has already been\n // decided.\n edits.push({at: 0, deleteCount: 1, insert: ' '})\n return edits\n }\n\n const tabIndent = /^ {0,3}\\t/.exec(text)\n if (tabIndent) {\n // A tab advances to the next multiple of 4 columns, so even 0-3\n // leading spaces before one reaches the indented-code-block\n // threshold; encoding the tab itself (not the spaces before it) is\n // enough to break that count.\n edits.push({at: tabIndent[0].length - 1, deleteCount: 1, insert: '	'})\n return edits\n }\n\n return edits\n}\n\n/**\n * Text rendered inside a link label needs every `[`, `]` and `\\` escaped\n * unconditionally, on top of the general-purpose hazard escaping every\n * line gets: a link label must stay bracket-balanced, and any literal\n * backslash in it needs protecting regardless of what follows (unlike\n * plain text, where only a backslash immediately before punctuation is a\n * hazard).\n */\nfunction escapeLinkLabelBrackets(text: string): string {\n return text.replace(/[[\\]\\\\]/g, (char) => `\\\\${char}`)\n}\n","import type {PortableTextBlock} from '@portabletext/types'\n\n/**\n * Blocks currently known to be a list item's first content block: it shares\n * its first line with the list marker (and, for a task item, its GFM\n * checkbox), which changes how `renderBlock` plans line-start hazard\n * escaping. Internal to this package so the signal never reaches the\n * public `Serializable`/`RenderNode` types a custom renderer's `.d.ts`\n * would otherwise expose it through.\n *\n * A block is marked right before rendering it; the `renderNode` call that\n * dispatches to `renderBlock` consumes the membership on the way past so a\n * later, unrelated render of the same object (still possible - `renderNode`\n * accepts any `TypedObject`) doesn't inherit a stale claim.\n */\nconst listItemFirstBlocks = new WeakSet<PortableTextBlock>()\n\nexport function markListItemFirstBlock(block: PortableTextBlock): void {\n listItemFirstBlocks.add(block)\n}\n\nexport function consumeListItemFirstBlock(block: PortableTextBlock): boolean {\n const isListItemFirstBlock = listItemFirstBlocks.has(block)\n listItemFirstBlocks.delete(block)\n return isListItemFirstBlock\n}\n","import {\n buildMarksTree,\n isPortableTextBlock,\n isPortableTextListItemBlock,\n isPortableTextToolkitSpan,\n isPortableTextToolkitTextNode,\n spanToPlainText,\n type ToolkitNestedPortableTextSpan,\n type ToolkitTextNode,\n} from '@portabletext/toolkit'\nimport type {\n PortableTextBlock,\n PortableTextListItemBlock,\n PortableTextMarkDefinition,\n PortableTextSpan,\n TypedObject,\n} from '@portabletext/types'\nimport {planLeafEscaping} from './escape-plain-text'\nimport {\n consumeListItemFirstBlock,\n markListItemFirstBlock,\n} from './list-item-first-block'\nimport type {PortableTextRenderers, RenderNode, Serializable} from './types'\n\n/**\n * ATX headings are single-line, inline-only leaf blocks: an ATX heading's\n * first line sits inside its `# ` prefix and can never be reparsed as a\n * block construct, so line-leading hazards never apply there. A hard\n * break's later lines are ordinary markdown lines outside that prefix and\n * get the full line-start battery, same as any other block's continuation.\n */\nconst HEADING_STYLES = new Set(['h1', 'h2', 'h3', 'h4', 'h5', 'h6'])\n\nexport const createRenderNode = (\n renderers: PortableTextRenderers,\n listIndexMap: Map<string, number>,\n listDepthMap: Map<string, number>,\n): RenderNode => {\n // Keyed by the actual `@text` node objects `buildMarksTree` produces, not\n // by render order: a custom type/mark renderer can call `renderNode` with\n // a synthetic node of its own mid-block (eg to render a placeholder), and\n // that call must not shift which escaped string a later real leaf gets.\n // A synthetic node was never planned, so it's absent from the map and\n // renders its own raw text.\n const escapedTextByNode = new WeakMap<ToolkitTextNode, string>()\n\n // Computed once per document render: a custom `hardBreak` can render to\n // something with no newline of its own (eg `() => '<br />'`), in which\n // case a `\\n` inside a span's text is not a real line boundary in the\n // rendered output, and `planLeafEscaping` needs to know that up front.\n const hardBreakOutputHasNewline = renderers.hardBreak().includes('\\n')\n\n // Shared by `renderBlock` and `renderListItem`, whatever the block's\n // style: both flatten `node.children` into the same text/mark tree and\n // need it escaped and keyed before rendering it.\n function renderBlockChildren(\n node: PortableTextBlock | PortableTextListItemBlock,\n isHeading: boolean,\n isListItem = false,\n ): string {\n const chunks = planLeafEscaping(node.children ?? [], node.markDefs ?? [], {\n isHeading,\n isListItem,\n hardBreakOutputHasNewline,\n })\n const tree = buildMarksTree(node)\n assignEscapedText(tree, chunks)\n\n return tree\n .map((child, i) =>\n renderNode({node: child, isInline: true, index: i, renderNode}),\n )\n .join('')\n }\n\n // Walks the tree in the same left-to-right, opaque-object-skipping order\n // `planLeafEscaping` used to produce `chunks`, so each `@text` leaf gets\n // keyed to the chunk planned for it.\n function assignEscapedText(\n nodes: ReadonlyArray<\n ToolkitNestedPortableTextSpan | ToolkitTextNode | TypedObject\n >,\n chunks: ReadonlyArray<string>,\n ): void {\n let pointer = 0\n\n const visit = (\n node: ToolkitNestedPortableTextSpan | ToolkitTextNode | TypedObject,\n ) => {\n if (isPortableTextToolkitTextNode(node)) {\n if (node.text !== '\\n') {\n const escaped = chunks[pointer]\n if (escaped !== undefined) {\n escapedTextByNode.set(node, escaped)\n }\n pointer++\n }\n return\n }\n\n if (isPortableTextToolkitSpan(node)) {\n node.children.forEach(visit)\n }\n }\n\n nodes.forEach(visit)\n }\n\n function renderNode<N extends TypedObject>(options: Serializable<N>): string {\n const {node, index, isInline} = options\n\n if (isPortableTextListItemBlock(node)) {\n return renderListItem(node, index)\n }\n\n if (isPortableTextToolkitSpan(node)) {\n return renderSpan(node)\n }\n\n if (isPortableTextBlock(node)) {\n return renderBlock(node, index, isInline, consumeListItemFirstBlock(node))\n }\n\n if (isPortableTextToolkitTextNode(node)) {\n return renderText(node)\n }\n\n return renderCustomBlock(node, index, isInline)\n }\n\n function renderListItem(\n node: PortableTextListItemBlock<\n PortableTextMarkDefinition,\n PortableTextSpan\n >,\n index: number,\n ): string {\n const renderer = renderers.listItem\n const handler =\n typeof renderer === 'function' ? renderer : renderer[node.listItem]\n const itemHandler = handler || renderers.unknownListItem\n\n let children: string\n\n if (node.style && node.style !== 'normal') {\n // Wrap any other style in whatever the block component says to use.\n // `renderNode` would recurse straight back into `renderListItem` if\n // `blockNode` still carried `listItem`, so it's stripped from the\n // copy; `markListItemFirstBlock` restores the list-item context\n // (line-start hazard escaping, the GFM checkbox prefix) onto that\n // same copy so `renderBlock` picks it up via `consumeListItemFirstBlock`.\n const {listItem: _listItem, ...blockNode} = node\n markListItemFirstBlock(blockNode)\n children = renderNode({\n node: blockNode,\n index,\n isInline: false,\n renderNode,\n })\n // Strip trailing newlines from block styles - list item component handles spacing\n children = children.replace(/\\n+$/, '')\n } else {\n children = renderBlockChildren(node, false, true)\n }\n\n return itemHandler({\n value: node,\n index,\n listIndex: node._key ? listIndexMap.get(node._key) : undefined,\n listDepth: node._key ? listDepthMap.get(node._key) : undefined,\n isInline: false,\n renderNode,\n children,\n })\n }\n\n function renderSpan(node: ToolkitNestedPortableTextSpan): string {\n const {markDef, markType, markKey} = node\n const span = renderers.marks[markType] || renderers.unknownMark\n const children = node.children.map((child, childIndex) =>\n renderNode({node: child, index: childIndex, isInline: true, renderNode}),\n )\n\n return span({\n text: spanToPlainText(node),\n value: markDef,\n markType,\n markKey,\n renderNode,\n children: children.join(''),\n })\n }\n\n function renderBlock(\n node: PortableTextBlock,\n index: number,\n isInline: boolean,\n isListItem: boolean,\n ): string {\n const style = node.style || 'normal'\n const children = renderBlockChildren(\n node,\n HEADING_STYLES.has(style),\n isListItem,\n )\n const handler =\n typeof renderers.block === 'function'\n ? renderers.block\n : renderers.block[style]\n const block = handler || renderers.unknownBlockStyle\n\n return block({index, isInline, children, value: node, renderNode})\n }\n\n function renderText(node: ToolkitTextNode): string {\n if (node.text === '\\n') {\n return renderers.hardBreak()\n }\n\n return escapedTextByNode.get(node) ?? node.text\n }\n\n function renderCustomBlock(\n value: TypedObject,\n index: number,\n isInline: boolean,\n ): string {\n const component = renderers.types[value._type] ?? renderers.unknownType\n\n return component({\n value,\n isInline,\n index,\n renderNode,\n })\n }\n\n return renderNode\n}\n","import {\n isPortableTextBlock,\n isPortableTextListItemBlock,\n} from '@portabletext/toolkit'\nimport type {TypedObject} from '@portabletext/types'\n\n/**\n * @public\n */\nexport type BlockSpacingRenderer = (options: {\n current: TypedObject\n next: TypedObject\n}) => string | undefined\n\n/**\n * @public\n */\nexport const DefaultBlockSpacingRenderer: BlockSpacingRenderer = ({\n current,\n next,\n}) => {\n if (\n isPortableTextListItemBlock(current) &&\n isPortableTextListItemBlock(next)\n ) {\n return '\\n'\n }\n\n if (\n isPortableTextBlock(current) &&\n isPortableTextBlock(next) &&\n current.style === 'blockquote' &&\n next.style === 'blockquote'\n ) {\n return '\\n>\\n'\n }\n\n return '\\n\\n'\n}\n","/**\n * @public\n */\nexport const DefaultHardBreakRenderer = (): string => ' \\n'\n","import type {PortableTextListItemRenderer} from '../types'\n\n/**\n * @public\n */\nexport const DefaultListItemRenderer: PortableTextListItemRenderer = ({\n children,\n value,\n listIndex,\n listDepth,\n}) => {\n const listStyle = value.listItem || 'bullet'\n const depth = listDepth ?? (value.level || 1) - 1\n const indent = ' '.repeat(depth)\n\n if (listStyle === 'number') {\n return `${indent}${listIndex ?? 1}. ${children}`\n }\n\n if (listStyle === 'task') {\n const checked =\n 'checked' in value && typeof value.checked === 'boolean'\n ? value.checked\n : false\n const marker = checked ? '[x]' : '[ ]'\n return `${indent}- ${marker} ${children}`\n }\n\n return `${indent}- ${children}`\n}\n\n/**\n * @public\n */\nexport const DefaultUnknownListItemRenderer: PortableTextListItemRenderer = ({\n children,\n}) => {\n return `- ${children}\\n`\n}\n","/**\n * Escapes special characters in image alt texts and link texts.\n */\nexport function escapeImageAndLinkText(text: string): string {\n return text.replace(/([[\\]\\\\])/g, '\\\\$1')\n}\n\n/**\n * Unescapes special characters in image alt texts and link texts.\n */\nexport function unescapeImageAndLinkText(text: string): string {\n return text.replace(/\\\\([!\"#$%&'()*+,\\-./:;<=>?@[\\\\\\]^_`{|}~])/g, '$1')\n}\n\n/**\n * Escapes special characters in image/link titles (the part inside quotes).\n */\nexport function escapeImageAndLinkTitle(text: string): string {\n return text.replace(/([\\\\\"])/g, '\\\\$1')\n}\n\n/**\n * Escapes characters that have special meaning at the row level of a GFM\n * table cell.\n *\n * A literal `|` ends the cell, so a pipe preceded by an even number of\n * backslashes (including zero) gets one more: paired backslashes cancel\n * out to a literal backslash and leave the pipe live, so parity, not mere\n * presence, decides whether it is already escaped. Newlines end the row,\n * so they are replaced with `<br>` to keep the visible line break inside\n * the cell.\n *\n * Backslashes themselves are left alone here; only the parity check reads\n * them, so escapes already in the rendered cell (such as `\\[` and `\\]` in\n * link text) survive the pass untouched.\n */\nexport function escapeTableCell(text: string): string {\n return text\n .replace(/(\\\\*)\\|/g, (match, backslashes: string) =>\n backslashes.length % 2 === 0 ? `${backslashes}\\\\|` : match,\n )\n .replace(/\\n/g, '<br>')\n}\n","import type {TypedObject} from '@portabletext/types'\nimport {escapeImageAndLinkTitle} from '../../escape'\nimport type {PortableTextMarkRenderer} from '../types'\n\n/**\n * @public\n */\nexport const DefaultEmRenderer: PortableTextMarkRenderer = ({children}) =>\n `_${children}_`\n\n/**\n * @public\n */\nexport const DefaultStrongRenderer: PortableTextMarkRenderer = ({children}) =>\n `**${children}**`\n\n/**\n * Renders a `code` decorator from the raw span text, bypassing the escaped\n * `children`: code content is verbatim, never markdown syntax. The\n * backtick fence is widened past the longest run of backticks already in\n * the text (CommonMark: the fence must be longer than any run it encloses).\n *\n * A space is padded on each side when the content starts or ends with a\n * backtick, so the fence and the content's own backtick never merge into\n * one run, and also when the content starts and ends with a space and\n * isn't all whitespace (`text.trim() !== ''`; an all-space code span has\n * nothing else for CommonMark's strip rule to leave behind, so padding it\n * would only add visible spaces), because CommonMark itself would\n * otherwise strip one such space per side on reparse; the padding\n * pre-compensates for that strip.\n *\n * @public\n */\nexport const DefaultCodeRenderer: PortableTextMarkRenderer = ({text}) =>\n wrapInCodeSpan(text)\n\nexport function wrapInCodeSpan(text: string): string {\n const fence = '`'.repeat(longestBacktickRun(text) + 1)\n const touchesBacktick = text.startsWith('`') || text.endsWith('`')\n const wouldBeStripped =\n text.startsWith(' ') && text.endsWith(' ') && text.trim() !== ''\n const padding = touchesBacktick || wouldBeStripped ? ' ' : ''\n return `${fence}${padding}${text}${padding}${fence}`\n}\n\nfunction longestBacktickRun(text: string): number {\n let longest = 0\n for (const run of text.match(/`+/g) ?? []) {\n longest = Math.max(longest, run.length)\n }\n return longest\n}\n\n/**\n * @public\n */\nexport const DefaultUnderlineRenderer: PortableTextMarkRenderer = ({\n children,\n}) => `<u>${children}</u>`\n\n/**\n * @public\n */\nexport const DefaultStrikeThroughRenderer: PortableTextMarkRenderer = ({\n children,\n}) => `~~${children}~~`\n\ninterface DefaultLink extends TypedObject {\n _type: 'link'\n href: string\n title: string | undefined\n}\n\n/**\n * @public\n */\nexport const DefaultLinkRenderer: PortableTextMarkRenderer<DefaultLink> = ({\n children,\n value,\n}) => {\n const href = value?.href || ''\n const title = value?.title || ''\n const looksSafe = uriLooksSafe(href)\n\n if (looksSafe) {\n // Check if the URL looks like an HTML injection attempt\n // If it has quotes AND angle brackets, or other suspicious patterns, encode more aggressively\n const looksLikeInjection = /[\"'][^\"']*[<>]|[<>][^<>]*[\"']/.test(href)\n\n if (looksLikeInjection) {\n // Encode all special characters that could be used for injection\n const encodedHref = href.replace(/[\"<>() ]/g, (char) => {\n return `%${char.charCodeAt(0).toString(16).toUpperCase()}`\n })\n return `[${children}](${encodedHref})`\n }\n\n // For normal URLs, don't encode parentheses - Markdown handles balanced parens fine\n return `[${children}](${href}${title ? ` \"${escapeImageAndLinkTitle(title)}\"` : ''})`\n }\n\n // Return children without link when URL is unsafe\n return children\n}\n\nfunction uriLooksSafe(uri: string): boolean {\n const url = (uri || '').trim()\n const first = url.charAt(0)\n\n if (first === '#' || first === '/') {\n return true\n }\n\n const colonIndex = url.indexOf(':')\n if (colonIndex === -1) {\n return true\n }\n\n const allowedProtocols = ['http', 'https', 'mailto', 'tel']\n const proto = url.slice(0, colonIndex).toLowerCase()\n if (allowedProtocols.indexOf(proto) !== -1) {\n return true\n }\n\n const queryIndex = url.indexOf('?')\n if (queryIndex !== -1 && colonIndex > queryIndex) {\n return true\n }\n\n const hashIndex = url.indexOf('#')\n if (hashIndex !== -1 && colonIndex > hashIndex) {\n return true\n }\n\n return false\n}\n\n/**\n * @public\n */\nexport const DefaultUnknownMarkRenderer: PortableTextMarkRenderer = ({\n children,\n}) => {\n return children\n}\n","import type {PortableTextBlock} from '@portabletext/types'\nimport type {PortableTextRenderer} from '../types'\n\ntype PortableTextBlockRenderer = PortableTextRenderer<PortableTextBlock>\n\n/**\n * @public\n */\nexport const DefaultNormalRenderer: PortableTextBlockRenderer = ({\n children,\n}) => {\n // Empty blocks should not add extra spacing\n if (!children || children.trim() === '') {\n return ''\n }\n\n return children\n}\n\n/**\n * @public\n */\nexport const DefaultBlockquoteRenderer: PortableTextBlockRenderer = ({\n children,\n}) => {\n // Prefix each line with \"> \" for proper blockquote formatting\n // This handles multi-line content and preserves empty lines\n if (!children) {\n return '>'\n }\n\n return children\n .split('\\n')\n .map((line) => `> ${line}`)\n .join('\\n')\n}\n\n/**\n * @public\n */\nexport const DefaultH1Renderer: PortableTextBlockRenderer = ({children}) =>\n `# ${children}`\n\n/**\n * @public\n */\nexport const DefaultH2Renderer: PortableTextBlockRenderer = ({children}) =>\n `## ${children}`\n\n/**\n * @public\n */\nexport const DefaultH3Renderer: PortableTextBlockRenderer = ({children}) =>\n `### ${children}`\n\n/**\n * @public\n */\nexport const DefaultH4Renderer: PortableTextBlockRenderer = ({children}) =>\n `#### ${children}`\n\n/**\n * @public\n */\nexport const DefaultH5Renderer: PortableTextBlockRenderer = ({children}) =>\n `##### ${children}`\n\n/**\n * @public\n */\nexport const DefaultH6Renderer: PortableTextBlockRenderer = ({children}) =>\n `###### ${children}`\n\n/**\n * @public\n */\nexport const DefaultUnknownStyleRenderer: PortableTextBlockRenderer = ({\n children,\n}) => {\n return children ?? ''\n}\n","import {isTypedObject} from '@portabletext/schema'\nimport {isPortableTextBlock} from '@portabletext/toolkit'\nimport type {PortableTextBlock, TypedObject} from '@portabletext/types'\nimport {\n escapeImageAndLinkText,\n escapeImageAndLinkTitle,\n escapeTableCell,\n} from '../../escape'\nimport {markListItemFirstBlock} from '../list-item-first-block'\nimport type {PortableTextTypeRenderer} from '../types'\nimport {wrapInCodeSpan} from './marks'\n\n/**\n * @public\n */\nexport const DefaultCodeBlockRenderer: PortableTextTypeRenderer<{\n _type: 'code'\n code: string\n language: string | undefined\n}> = (options) => {\n if (!isCodeShaped(options.value)) {\n return DefaultUnknownTypeRenderer(options)\n }\n return `\\`\\`\\`${normalizeLanguage(options.value.language)}\\n${options.value.code}\\n\\`\\`\\``\n}\n\nfunction isCodeShaped(value: unknown): value is {code: string} {\n return typeof (value as {code?: unknown} | null)?.code === 'string'\n}\n\n/**\n * A fence info string is everything after the opening fence on the same\n * line, so a real `language` can never contain a newline, and the parser\n * only ever produces a string. Junk in this optional field should not send\n * an otherwise valid code block to the fenced-JSON path, so it is treated\n * as absent instead of guarded.\n */\nfunction normalizeLanguage(language: unknown): string {\n if (typeof language !== 'string' || language.includes('\\n')) {\n return ''\n }\n if (language === 'json:object') {\n // `json:object` is reserved as the object carrier: a code block\n // emitting it as its info string would re-parse as the embedded\n // object whenever its content happens to be typed JSON, destroying\n // the code block. The language degrades to absent instead.\n return ''\n }\n return language\n}\n\n/**\n * @public\n */\nexport const DefaultHorizontalRuleRenderer: PortableTextTypeRenderer = () => {\n return '---'\n}\n\n/**\n * @public\n */\nexport const DefaultHtmlRenderer: PortableTextTypeRenderer<{\n _type: 'html'\n html: string\n}> = (options) => {\n if (!isHtmlShaped(options.value)) {\n return DefaultUnknownTypeRenderer(options)\n }\n return options.value.html\n}\n\nfunction isHtmlShaped(value: unknown): value is {html: string} {\n return typeof (value as {html?: unknown} | null)?.html === 'string'\n}\n\n/**\n * @public\n */\nexport const DefaultImageRenderer: PortableTextTypeRenderer<{\n _type: 'image'\n src: string\n alt: string | undefined\n title: string | undefined\n}> = (options) => {\n if (!isImageShaped(options.value)) {\n return DefaultUnknownTypeRenderer(options)\n }\n const alt = escapeImageAndLinkText(options.value.alt ?? '')\n const title = options.value.title\n ? ` \"${escapeImageAndLinkTitle(options.value.title)}\"`\n : ''\n return ``\n}\n\nfunction isImageShaped(value: unknown): value is {\n src: string\n alt: string | null | undefined\n title: string | null | undefined\n} {\n const image = value as {src?: unknown; alt?: unknown; title?: unknown} | null\n return (\n typeof image?.src === 'string' &&\n // CMS payloads commonly store cleared optional strings as `null`; the\n // render body treats `null` like absent, so the guard must too\n (image.alt == null || typeof image.alt === 'string') &&\n (image.title == null || typeof image.title === 'string')\n )\n}\n\n/**\n * A table is table-shaped when everything the renderer dereferences is\n * there: `rows` an array of typed objects with a `cells` array, every cell\n * a typed object whose `value` array holds typed objects (`renderNode`'s\n * input contract). The predicate narrows to exactly what `renderTable`\n * consumes, so the renderer needs no casts. A malformed `table` value\n * (e.g. a consumer's differently-shaped `table` type) falls back to the\n * fenced-JSON path instead of throwing.\n */\nfunction isTableShaped(value: unknown): value is TableShaped {\n const rows = (value as {rows?: unknown} | null)?.rows\n return (\n Array.isArray(rows) &&\n rows.every(\n (row) =>\n isTypedObject(row) &&\n Array.isArray(row['cells']) &&\n row['cells'].every(\n (cell) =>\n isTypedObject(cell) &&\n Array.isArray(cell['value']) &&\n cell['value'].every(isTypedObject),\n ),\n )\n )\n}\n\ntype TableShaped = {\n headerRows?: unknown\n alignment?: unknown\n rows: Array<{cells: Array<{value: Array<TypedObject>}>}>\n}\n\n/**\n * Renders a Portable Text table block-object back to Markdown.\n *\n * The PT `headerRows` field decides the header. Missing `headerRows` and\n * `headerRows === 0` both render headerless: GFM has no headerless form, so\n * an empty header row is emitted and every row goes in the body (that empty\n * header reads back as `headerRows: 0` via `markdownToPortableText`).\n * `headerRows >= 1` promotes `rows[0]` to the header. GFM allows exactly one\n * header row, so header rows beyond the first flatten into the body, lossy,\n * but the extra rows stay on the Portable Text side.\n *\n * Asymmetric tables (rows of varying cell counts) are widened to match\n * the row with the most cells. Narrower rows are padded with empty cells\n * so a GFM parser doesn't silently drop the extra cells in wider rows.\n *\n * @public\n */\nexport const DefaultTableRenderer: PortableTextTypeRenderer<{\n _type: 'table'\n headerRows: number | undefined\n alignment: Array<'left' | 'center' | 'right' | null> | undefined\n rows: Array<{\n _key: string\n cells: Array<{\n _key: string\n value: Array<PortableTextBlock>\n }>\n }>\n}> = (options) => {\n const {value, renderNode} = options\n\n if (!isTableShaped(value)) {\n return DefaultUnknownTypeRenderer(options)\n }\n\n return renderTable(value, renderNode)\n}\n\nfunction renderTable(\n value: TableShaped,\n renderNode: Parameters<PortableTextTypeRenderer>[0]['renderNode'],\n): string {\n const rows = value.rows\n // `alignment` is an extension field, not part of the table shape: junk\n // here should not send an otherwise valid table to the fenced-JSON path,\n // so it is normalized away instead of guarded (`{}.at` would throw below).\n const alignment: ReadonlyArray<unknown> | undefined = Array.isArray(\n value.alignment,\n )\n ? value.alignment\n : undefined\n\n const headerRow = rows.at(0)\n\n if (!headerRow) {\n return ''\n }\n\n // Helper to extract text from cell blocks\n const getCellText = (cellBlocks: Array<TypedObject>): string => {\n return cellBlocks\n .map((block, index) =>\n renderNode({\n node: block,\n index,\n isInline: false,\n renderNode,\n }),\n )\n .join(' ')\n .trim()\n }\n\n const lines: string[] = []\n\n // GFM requires every row to have the same number of cells as the header row\n // and the delimiter row. Parsers silently drop excess cells from body rows\n // that are wider than the header, so we widen the table to the widest row\n // and pad narrower rows with empty cells to keep all data visible.\n const columnCount = rows.reduce(\n (max, row) => Math.max(max, row.cells.length),\n 0,\n )\n\n const renderCells = (texts: Array<string>): string => {\n const padded = [...texts]\n while (padded.length < columnCount) {\n padded.push('')\n }\n return `| ${padded.join(' | ')} |`\n }\n\n const renderRow = (cells: typeof headerRow.cells): string =>\n renderCells(cells.map((cell) => escapeTableCell(getCellText(cell.value))))\n\n // Delimiter row, sized to the column count. Each cell's colons encode the\n // column's alignment as defined by `value.alignment[columnIndex]`.\n const separators = Array.from({length: columnCount}, (_, index) => {\n const align = alignment?.at(index)\n if (align === 'left') {\n return ' :--- '\n }\n if (align === 'center') {\n return ' :---: '\n }\n if (align === 'right') {\n return ' ---: '\n }\n return ' --- '\n })\n const delimiter = `|${separators.join('|')}|`\n\n const hasHeader = (Number(value.headerRows) || 0) >= 1\n\n if (!hasHeader) {\n // Headerless table: emit an empty header row and keep every row in the\n // body.\n lines.push(renderCells([]))\n lines.push(delimiter)\n for (const row of rows) {\n lines.push(renderRow(row.cells))\n }\n } else {\n // `rows[0]` is the header. Header rows beyond the first flatten into the\n // body (GFM has a single header row).\n lines.push(renderRow(headerRow.cells))\n lines.push(delimiter)\n for (let i = 1; i < rows.length; i++) {\n const row = rows.at(i)\n if (row) {\n lines.push(renderRow(row.cells))\n }\n }\n }\n\n return lines.join('\\n')\n}\n\n/**\n * @public\n */\nexport const DefaultCalloutRenderer: PortableTextTypeRenderer<{\n _type: 'callout'\n tone: string\n content: Array<PortableTextBlock>\n}> = (options) => {\n if (!isCalloutShaped(options.value)) {\n return DefaultUnknownTypeRenderer(options)\n }\n const {renderNode} = options\n const renderedContent = options.value.content\n .map((block, index) =>\n renderNode({\n node: block._type === 'block' ? {...block, style: 'normal'} : block,\n index,\n isInline: false,\n renderNode,\n }),\n )\n .join('\\n\\n')\n\n const prefixed = renderedContent\n .split('\\n')\n .map((line) => (line === '' ? '>' : `> ${line}`))\n .join('\\n')\n\n return `> [!${options.value.tone.toUpperCase()}]\\n${prefixed}`\n}\n\nfunction isCalloutShaped(\n value: unknown,\n): value is {tone: string; content: Array<TypedObject>} {\n const callout = value as {tone?: unknown; content?: unknown} | null\n return (\n typeof callout?.tone === 'string' &&\n Array.isArray(callout.content) &&\n callout.content.every(isTypedObject)\n )\n}\n\n/**\n * Renders a structural blockquote block-object (the `types.blockquote` shape\n * produced by `markdownToPortableText` when a `types.blockquote` matcher is\n * provided) back to Markdown. Each content block is rendered via the\n * recursive renderer pipeline, joined with blank lines, and every line is\n * prefixed with `> ` to form a Markdown blockquote.\n *\n * Distinct from `DefaultBlockquoteRenderer`, which renders flat-path text\n * blocks with `style: 'blockquote'`.\n *\n * @public\n */\nexport const DefaultBlockquoteObjectRenderer: PortableTextTypeRenderer<{\n _type: 'blockquote'\n content: Array<PortableTextBlock>\n}> = ({value, renderNode}) => {\n const renderedContent = value.content\n .map((block, index) =>\n renderNode({\n node: block._type === 'block' ? {...block, style: 'normal'} : block,\n index,\n isInline: false,\n renderNode,\n }),\n )\n .join('\\n\\n')\n\n return renderedContent\n .split('\\n')\n .map((line) => (line === '' ? '>' : `> ${line}`))\n .join('\\n')\n}\n\n/**\n * Renders a structural list block-object (the `types.list` shape produced by\n * `markdownToPortableText` when a `types.list` matcher is provided) back to\n * Markdown. Items render as `- ` for `kind: 'bullet'`, `1. `/`2. ` for `'number'`,\n * and `- [x] ` / `- [ ] ` for `'task'`. Items can hold any blocks - text blocks,\n * code blocks, callouts, images, and nested lists - and content other than the\n * leading text block is indented to keep it inside the item.\n *\n * @public\n */\nexport const DefaultListRenderer: PortableTextTypeRenderer<{\n _type: 'list'\n kind: 'bullet' | 'number' | 'task'\n items: Array<{\n _type: 'list-item'\n _key: string\n checked?: boolean\n content: Array<PortableTextBlock | TypedObject>\n }>\n}> = ({value, renderNode}) => {\n // A list is \"loose\" when any item carries multiple non-list-block\n // content entries (a continuation paragraph, a code block, etc).\n // CommonMark uses blank lines between items in loose lists; tight lists\n // pack items together with single newlines. A nested list as a second\n // child of an item does NOT make the list loose, so we ignore those when\n // counting.\n const isLoose = value.items.some((item) => {\n const nonNestedBlocks = item.content.filter(\n (block) => (block as TypedObject)._type !== 'list',\n )\n return nonNestedBlocks.length > 1\n })\n const itemSeparator = isLoose ? '\\n\\n' : '\\n'\n\n const lines = value.items.map((item, itemIndex) => {\n const marker = getListMarker(value.kind, itemIndex, item.checked)\n // Continuation indent matches the marker's width so that subsequent\n // blocks attach to this item under CommonMark's lazy-continuation rule.\n // Bullet `- ` indents to 2; ordered `1. ` indents to 3, `10. ` to 4.\n // Task `- [x] ` is conceptually `- ` + a `[x] ` content prefix at the\n // markdown-it level, so its continuation indent stays at 2.\n const indentWidth = value.kind === 'task' ? 2 : marker.length\n const indent = ' '.repeat(indentWidth)\n\n const renderedBlocks = item.content.map((block, blockIndex) => {\n // Only the first block shares its first line with the marker (and,\n // for a task item, its GFM checkbox); later blocks render on their\n // own indented lines.\n if (blockIndex === 0 && isPortableTextBlock(block)) {\n markListItemFirstBlock(block)\n }\n return {\n isNestedList: (block as TypedObject)._type === 'list',\n text: renderNode({\n node: block as TypedObject,\n index: blockIndex,\n isInline: false,\n renderNode,\n }),\n }\n })\n\n const [first, ...rest] = renderedBlocks\n // Trim trailing whitespace from empty items so `- ` becomes `-`.\n const head = `${marker}${first?.text ?? ''}`.trimEnd()\n if (rest.length === 0) {\n return head\n }\n\n const tail = rest\n .map((rendered) => {\n const indented = rendered.text\n .split('\\n')\n .map((line) => (line === '' ? '' : `${indent}${line}`))\n .join('\\n')\n // Nested lists hug the previous block (tight list); other content\n // gets a blank line separator (paragraph break).\n return rendered.isNestedList ? `\\n${indented}` : `\\n\\n${indented}`\n })\n .join('')\n\n return `${head}${tail}`\n })\n\n return lines.join(itemSeparator)\n}\n\nfunction getListMarker(\n kind: 'bullet' | 'number' | 'task',\n itemIndex: number,\n checked: boolean | undefined,\n): string {\n if (kind === 'number') {\n return `${itemIndex + 1}. `\n }\n if (kind === 'task') {\n return checked ? '- [x] ' : '- [ ] '\n }\n return '- '\n}\n\n/**\n * @public\n */\nexport const DefaultUnknownTypeRenderer: PortableTextTypeRenderer = ({\n value,\n isInline,\n}) => {\n if (isInline) {\n // Single-line JSON: code spans turn newlines into spaces on\n // reparse, so a pretty-printed payload would still reconstruct but\n // would not survive byte-identically, breaking the fixpoint.\n return `json:object${wrapInCodeSpan(JSON.stringify(value))}`\n }\n return `\\`\\`\\`json:object\\n${JSON.stringify(value, null, 2)}\\n\\`\\`\\``\n}\n","import type {\n ArbitraryTypedObject,\n PortableTextBlock,\n TypedObject,\n} from '@portabletext/types'\nimport {buildListIndexMap} from './build-list-index-map'\nimport {createRenderNode} from './render-node'\nimport {\n DefaultBlockSpacingRenderer,\n type BlockSpacingRenderer,\n} from './renderers/block-spacing'\nimport {DefaultHardBreakRenderer} from './renderers/hard-break'\nimport {\n DefaultListItemRenderer,\n DefaultUnknownListItemRenderer,\n} from './renderers/list-item'\nimport {\n DefaultCodeRenderer,\n DefaultEmRenderer,\n DefaultLinkRenderer,\n DefaultStrikeThroughRenderer,\n DefaultStrongRenderer,\n DefaultUnderlineRenderer,\n DefaultUnknownMarkRenderer,\n} from './renderers/marks'\nimport {\n DefaultBlockquoteRenderer,\n DefaultH1Renderer,\n DefaultH2Renderer,\n DefaultH3Renderer,\n DefaultH4Renderer,\n DefaultH5Renderer,\n DefaultH6Renderer,\n DefaultNormalRenderer,\n DefaultUnknownStyleRenderer,\n} from './renderers/style'\nimport {\n DefaultCalloutRenderer,\n DefaultCodeBlockRenderer,\n DefaultHorizontalRuleRenderer,\n DefaultHtmlRenderer,\n DefaultImageRenderer,\n DefaultTableRenderer,\n DefaultUnknownTypeRenderer,\n} from './renderers/type'\nimport type {PortableTextRenderers} from './types'\n\nconst defaultRenderers: PortableTextRenderers = {\n types: {\n 'callout': DefaultCalloutRenderer,\n 'code': DefaultCodeBlockRenderer,\n 'horizontal-rule': DefaultHorizontalRuleRenderer,\n 'html': DefaultHtmlRenderer,\n 'image': DefaultImageRenderer,\n 'table': DefaultTableRenderer,\n },\n\n block: {\n normal: DefaultNormalRenderer,\n blockquote: DefaultBlockquoteRenderer,\n h1: DefaultH1Renderer,\n h2: DefaultH2Renderer,\n h3: DefaultH3Renderer,\n h4: DefaultH4Renderer,\n h5: DefaultH5Renderer,\n h6: DefaultH6Renderer,\n },\n marks: {\n 'em': DefaultEmRenderer,\n 'strong': DefaultStrongRenderer,\n 'code': DefaultCodeRenderer,\n 'underline': DefaultUnderlineRenderer,\n 'strike-through': DefaultStrikeThroughRenderer,\n 'link': DefaultLinkRenderer,\n },\n listItem: DefaultListItemRenderer,\n hardBreak: DefaultHardBreakRenderer,\n\n unknownType: DefaultUnknownTypeRenderer,\n unknownMark: DefaultUnknownMarkRenderer,\n unknownListItem: DefaultUnknownListItemRenderer,\n unknownBlockStyle: DefaultUnknownStyleRenderer,\n}\n\ntype Options = Partial<PortableTextRenderers> & {\n blockSpacing?: BlockSpacingRenderer\n}\n\n/**\n * @public\n */\nexport function portableTextToMarkdown<\n Block extends TypedObject = PortableTextBlock | ArbitraryTypedObject,\n>(blocks: Array<Block>, options: Options = {}): string {\n const renderers = {\n block: {\n ...defaultRenderers.block,\n ...options.block,\n },\n listItem: options.listItem ?? defaultRenderers.listItem,\n marks: {\n ...defaultRenderers.marks,\n ...options.marks,\n },\n types: {\n ...defaultRenderers.types,\n ...options.types,\n },\n hardBreak: options.hardBreak ?? defaultRenderers.hardBreak,\n unknownType: options.unknownType ?? defaultRenderers.unknownType,\n unknownBlockStyle:\n options.unknownBlockStyle ?? defaultRenderers.unknownBlockStyle,\n unknownListItem:\n options.unknownListItem ?? defaultRenderers.unknownListItem,\n unknownMark: options.unknownMark ?? defaultRenderers.unknownMark,\n }\n const renderBlockSpacing = options.blockSpacing ?? DefaultBlockSpacingRenderer\n\n const {listIndexMap, listDepthMap} = buildListIndexMap(blocks)\n const renderNode = createRenderNode(renderers, listIndexMap, listDepthMap)\n\n return blocks\n .map((node, index) => {\n const renderedNode = renderNode({\n node,\n index,\n isInline: false,\n renderNode,\n })\n\n if (index === blocks.length - 1) {\n return renderedNode\n }\n\n const nextNode = blocks.at(index + 1)\n\n if (!nextNode) {\n return renderedNode\n }\n\n const blockSpacing =\n renderBlockSpacing({\n current: node,\n next: nextNode,\n }) ?? '\\n\\n'\n\n return `${renderedNode}${blockSpacing}`\n })\n .join('')\n}\n","import {\n compileSchema,\n defineSchema,\n type AnnotationDefinition,\n type BlockObjectDefinition,\n type DecoratorDefinition,\n type ListDefinition,\n type StyleDefinition,\n} from '@portabletext/schema'\n\n/********************\n * Default style definitions\n ********************/\n\nexport const normalStyleDefinition = {\n name: 'normal',\n} as const satisfies StyleDefinition\n\nexport const h1StyleDefinition = {\n name: 'h1',\n} as const satisfies StyleDefinition\n\nexport const h2StyleDefinition = {\n name: 'h2',\n} as const satisfies StyleDefinition\n\nexport const h3StyleDefinition = {\n name: 'h3',\n} as const satisfies StyleDefinition\n\nexport const h4StyleDefinition = {\n name: 'h4',\n} as const satisfies StyleDefinition\n\nexport const h5StyleDefinition = {\n name: 'h5',\n} as const satisfies StyleDefinition\n\nexport const h6StyleDefinition = {\n name: 'h6',\n} as const satisfies StyleDefinition\n\nexport const blockquoteStyleDefinition = {\n name: 'blockquote',\n} as const satisfies StyleDefinition\n\n/********************\n * Default list definitions\n ********************/\n\nexport const defaultOrderedListItemDefinition = {\n name: 'number',\n} as const satisfies ListDefinition\n\nexport const defaultUnorderedListItemDefinition = {\n name: 'bullet',\n} as const satisfies ListDefinition\n\nexport const defaultTaskListItemDefinition = {\n name: 'task',\n} as const satisfies ListDefinition\n\n/********************\n * Default decorator definitions\n ********************/\n\nexport const defaultStrongDecoratorDefinition = {\n name: 'strong',\n} as const satisfies DecoratorDefinition\n\nexport const defaultEmDecoratorDefinition = {\n name: 'em',\n} as const satisfies DecoratorDefinition\n\nexport const defaultCodeDecoratorDefinition = {\n name: 'code',\n} as const satisfies DecoratorDefinition\n\nexport const defaultStrikeThroughDecoratorDefinition = {\n name: 'strike-through',\n} as const satisfies DecoratorDefinition\n\n/********************\n * Default annotation definitions\n ********************/\n\nexport const defaultLinkObjectDefinition = {\n name: 'link',\n fields: [\n {name: 'href', type: 'string'},\n {name: 'title', type: 'string'},\n ],\n} as const satisfies AnnotationDefinition\n\n/********************\n * Default object definitions\n ********************/\n\nexport const defaultCodeObjectDefinition = {\n name: 'code',\n fields: [\n {name: 'language', type: 'string'},\n {name: 'code', type: 'string'},\n ],\n} as const satisfies BlockObjectDefinition\n\nexport const defaultImageObjectDefinition = {\n name: 'image',\n fields: [\n {name: 'src', type: 'string'},\n {name: 'alt', type: 'string'},\n {name: 'title', type: 'string'},\n ],\n} as const satisfies BlockObjectDefinition\n\nexport const defaultHorizontalRuleObjectDefinition = {\n name: 'horizontal-rule',\n} as const satisfies BlockObjectDefinition\n\nexport const defaultHtmlObjectDefinition = {\n name: 'html',\n fields: [{name: 'html', type: 'string'}],\n} as const satisfies BlockObjectDefinition\n\n/**\n * Mirrors the canonical shape `@portabletext/plugin-table` expects: `table`\n * (`headerRows`, `rows`), `row` (`cells`), `cell` (`value`, text blocks or\n * standalone `image` objects). `alignment` is a `@portabletext/markdown`\n * extension field; `@portabletext/plugin-table` ignores it. Keep this in\n * sync with `packages/plugin-table/src/table-config.ts`'s\n * `defaultTableConfig`.\n */\nexport const defaultTableObjectDefinition = {\n name: 'table',\n fields: [\n {name: 'headerRows', type: 'number'},\n {name: 'alignment', type: 'array'},\n {\n name: 'rows',\n type: 'array',\n of: [\n {\n type: 'object',\n name: 'row',\n fields: [\n {\n name: 'cells',\n type: 'array',\n of: [\n {\n type: 'object',\n name: 'cell',\n fields: [\n {\n name: 'value',\n type: 'array',\n of: [{type: 'block'}, {type: 'image'}],\n },\n ],\n },\n ],\n },\n ],\n },\n ],\n },\n ],\n} as const satisfies BlockObjectDefinition\n\nexport const defaultCalloutObjectDefinition = {\n name: 'callout',\n fields: [\n {name: 'tone', type: 'string'},\n {name: 'content', type: 'array'},\n ],\n} as const satisfies BlockObjectDefinition\n\n/**\n * The default schema for converting markdown to Portable Text.\n *\n * @public\n */\nexport const defaultSchema = compileSchema(\n defineSchema({\n block: {\n fields: [{name: 'checked', type: 'boolean'}],\n },\n styles: [\n normalStyleDefinition,\n h1StyleDefinition,\n h2StyleDefinition,\n h3StyleDefinition,\n h4StyleDefinition,\n h5StyleDefinition,\n h6StyleDefinition,\n blockquoteStyleDefinition,\n ],\n lists: [\n defaultOrderedListItemDefinition,\n defaultUnorderedListItemDefinition,\n defaultTaskListItemDefinition,\n ],\n decorators: [\n defaultStrongDecoratorDefinition,\n defaultEmDecoratorDefinition,\n defaultCodeDecoratorDefinition,\n defaultStrikeThroughDecoratorDefinition,\n ],\n annotations: [defaultLinkObjectDefinition],\n blockObjects: [\n defaultCalloutObjectDefinition,\n defaultCodeObjectDefinition,\n defaultHorizontalRuleObjectDefinition,\n defaultHtmlObjectDefinition,\n defaultImageObjectDefinition,\n defaultTableObjectDefinition,\n ],\n inlineObjects: [defaultImageObjectDefinition],\n }),\n)\n","import type {DegradationType} from './markdown-to-portable-text'\n\n/**\n * The full catalog of `Degradation['message']` prose, one entry per\n * `DegradationType`, grouped and ordered to match the union in\n * `markdown-to-portable-text.ts`. Kept apart from the conversion's\n * formatting mechanism (`buildDegradationMessage` and friends, in\n * `markdown-to-portable-text.ts`) so the catalog reads as one list instead\n * of being scattered across the walk that triggers it.\n *\n * `satisfies Record<DegradationType, ...>` makes the catalog exhaustive: a\n * new `DegradationType` member without a matching entry here is a type\n * error, caught at compile time instead of surfacing as an `undefined`\n * message at runtime.\n *\n * Style rule for every message here: a declarative statement of what\n * happened, never a verb that can parse as an imperative. A verb whose past\n * and imperative forms coincide (\"split\", \"set\", \"cut\", ...) never leads a\n * message, since the reader can't tell \"X was split\" from an instruction to\n * split X. State the effect first, the cause second, naming the schema\n * declaration that's missing.\n *\n * Not exported from the package entry: `message` is unstable by contract\n * (see `Degradation['message']`'s doc comment), so the catalog producing it\n * stays internal too.\n */\nexport const degradationMessage = {\n 'decorator-dropped': (\n decorator: 'code' | 'strong' | 'em' | 'strikeThrough',\n ): string => {\n switch (decorator) {\n case 'code':\n return 'Removed inline-code formatting, kept the text: the schema has no `code` decorator'\n case 'strong':\n return 'Removed bold formatting, kept the text: the schema has no `strong` decorator'\n case 'em':\n return 'Removed italic formatting, kept the text: the schema has no `em` decorator'\n case 'strikeThrough':\n return 'Removed strikethrough formatting, kept the text: the schema has no `strike-through` decorator'\n }\n },\n\n 'annotation-dropped': (cause: 'missing-url' | 'no-annotation'): string =>\n cause === 'missing-url'\n ? 'Removed a link that has no URL, kept its text'\n : 'Removed the link, kept its text: the schema has no `link` annotation',\n\n 'style-fallback': (name: string): string => {\n if (/^h[1-6]$/.test(name)) {\n const hashes = '#'.repeat(Number(name.slice(1)))\n return `\\`${hashes}\\` heading became a normal paragraph: the schema has no \\`${name}\\` style`\n }\n\n if (name === 'blockquote') {\n return 'Blockquote became normal paragraphs: the schema has no `blockquote` style'\n }\n\n return `Fell back to \\`normal\\` style: \\`${name}\\` not in schema`\n },\n\n 'list-flattened': (kind: 'bullet' | 'number'): string => {\n const label = kind === 'number' ? 'Numbered' : 'Bullet'\n return `${label} list became plain paragraphs: the schema has no \\`${kind}\\` list`\n },\n\n 'task-checkbox-stripped': (checked: boolean): string => {\n const checkbox = checked ? '[x]' : '[ ]'\n return `Removed the \\`${checkbox}\\` checkbox, kept a plain list item: the schema has no \\`task\\` list`\n },\n\n 'table-flattened':\n 'Table became plain text blocks, rows and columns lost: the schema has no `table` block object',\n\n 'code-block-to-text': (language: string | undefined): string =>\n language\n ? `\\`${language}\\` code block became plain text: the schema has no \\`code\\` block object`\n : `Code block became plain text: the schema has no \\`code\\` block object`,\n\n 'horizontal-rule-to-text':\n 'Horizontal rule became the text `---`: the schema has no `horizontal-rule` block object',\n\n 'html-block-to-text':\n 'HTML block became plain text: the schema has no `html` block object',\n\n 'inline-html-dropped':\n 'Removed inline HTML tags, kept nothing: `html.inline` is `skip` (the default)',\n\n 'image-block-to-inline': (cause: 'table-cell' | 'no-block-image'): string =>\n cause === 'table-cell'\n ? \"The image became inline: a table cell can't hold a block-level `image`\"\n : 'The image became inline: the schema has no block-level `image`',\n\n 'image-inline-to-block':\n 'The image became its own block, splitting the paragraph: the schema has no inline `image`',\n\n 'image-to-text':\n 'Image became its markdown source as plain text: the schema has no `image` object',\n\n 'callout-fallback': (calloutType: string, style: string): string =>\n `\\`[!${calloutType.toUpperCase()}]\\` callout became ${style}-styled text: the schema has no \\`callout\\` block object`,\n\n 'fields-dropped': (names: string, construct: string): string =>\n `Dropped ${names} from \\`${construct}\\`: not in the schema's \\`${construct}\\` fields`,\n\n 'object-carrier-invalid': (\n kind: 'fence' | 'code-span',\n payload: string,\n ): string =>\n kind === 'fence'\n ? `\\`json:object\\` fence fell back to a code block: ${describeObjectCarrierFailure(payload)}`\n : `\\`json:object\\`-tagged code span fell back to a plain code span: ${describeObjectCarrierFailure(payload)}`,\n} satisfies Record<DegradationType, string | ((...args: never[]) => string)>\n\n/**\n * Names why a `json:object` payload failed to parse as an object carrier:\n * a payload that isn't a JSON object at all reads differently from one\n * that is but has no usable `_type`.\n */\nfunction describeObjectCarrierFailure(payload: string): string {\n let parsed: unknown\n\n try {\n parsed = JSON.parse(payload)\n } catch {\n return 'the payload is not valid JSON'\n }\n\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n return 'the payload is not a JSON object'\n }\n\n return 'the payload has no string `_type`'\n}\n","import type {\n PortableTextObject,\n Schema,\n SchemaDefinition,\n} from '@portabletext/schema'\n\n/**\n * Fields a default object/annotation matcher (`buildObjectMatcher`,\n * `buildAnnotationMatcher`) silently dropped while filtering a converted\n * value down to the schema's declared fields: the keys the conversion\n * supplied that the schema's field list doesn't declare, so they never made\n * it into the built object. Tagged onto the matcher's return value; read it\n * back with `readDroppedFields`. A consumer-supplied matcher's return value\n * never carries this: its own filtering, if any, is not this module's\n * business.\n */\nexport type DroppedFields = {\n construct: string\n keys: Array<string>\n}\n\nconst droppedFieldsTag = Symbol('droppedFields')\n\nexport function readDroppedFields(\n object: PortableTextObject | undefined,\n): DroppedFields | undefined {\n if (!object) {\n return undefined\n }\n return (object as unknown as Record<symbol, unknown>)[droppedFieldsTag] as\n | DroppedFields\n | undefined\n}\n\nfunction buildFilteredObject(\n schemaDefinition: {name: string; fields: ReadonlyArray<{name: string}>},\n value: Record<string, unknown>,\n keyGenerator: () => string,\n): PortableTextObject {\n const filteredValue = schemaDefinition.fields.reduce<Record<string, unknown>>(\n (filteredValue, field) => {\n const fieldValue = value[field.name]\n\n if (fieldValue !== undefined) {\n filteredValue[field.name] = fieldValue\n }\n\n return filteredValue\n },\n {},\n )\n\n const object = {\n _key: keyGenerator(),\n _type: schemaDefinition.name,\n ...filteredValue,\n }\n\n const suppliedKeys = Object.entries(value)\n .filter(([, fieldValue]) => fieldValue !== undefined)\n .map(([key]) => key)\n const droppedKeys = suppliedKeys.filter((key) => !(key in filteredValue))\n\n if (droppedKeys.length > 0) {\n Object.defineProperty(object, droppedFieldsTag, {\n value: {construct: schemaDefinition.name, keys: droppedKeys},\n enumerable: false,\n })\n }\n\n return object\n}\n\n/**\n * Matcher function for mapping markdown elements to Portable Text block styles.\n *\n * @public\n */\nexport type StyleMatcher = ({\n context,\n}: {\n context: {schema: Schema}\n}) => string | undefined\n\nexport function buildStyleMatcher<TDefinition extends {name: string}>(\n definition: TDefinition,\n): StyleMatcher {\n return ({context}) => {\n const schemaDefinition = context.schema.styles.find(\n (item) => item.name === definition.name,\n )\n\n if (!schemaDefinition) {\n return undefined\n }\n\n return schemaDefinition.name\n }\n}\n\n/**\n * Matcher function for mapping markdown list items to Portable Text list types.\n *\n * @public\n */\nexport type ListItemMatcher = ({\n context,\n}: {\n context: {schema: Schema}\n}) => string | undefined\n\nexport function buildListItemMatcher<TDefinition extends {name: string}>(\n definition: TDefinition,\n): ListItemMatcher {\n return ({context}) => {\n const schemaDefinition = context.schema.lists.find(\n (item) => item.name === definition.name,\n )\n\n if (!schemaDefinition) {\n return undefined\n }\n\n return schemaDefinition.name\n }\n}\n\n/**\n * Matcher function for mapping markdown inline formatting to Portable Text decorators.\n *\n * @public\n */\nexport type DecoratorMatcher = ({\n context,\n}: {\n context: {schema: Schema}\n}) => string | undefined\n\nexport function buildDecoratorMatcher<TDefinition extends {name: string}>(\n definition: TDefinition,\n): DecoratorMatcher {\n return ({context}) => {\n const schemaDefinition = context.schema.decorators.find(\n (item) => item.name === definition.name,\n )\n\n if (!schemaDefinition) {\n return undefined\n }\n\n return schemaDefinition.name\n }\n}\n\n/**\n * Matcher function for mapping markdown links to Portable Text annotations.\n *\n * @public\n */\nexport type AnnotationMatcher<\n TValue extends Record<string, unknown> = Record<string, never>,\n> = ({\n context,\n value,\n}: {\n context: {schema: Schema; keyGenerator: () => string}\n value: TValue\n}) => PortableTextObject | undefined\n\nexport function buildAnnotationMatcher<TDefinition extends {name: string}>(\n definition: TDefinition,\n): AnnotationMatcher<ExtractValue<TDefinition>> {\n return ({context, value}) => {\n const schemaDefinition = context.schema.annotations.find(\n (item) => item.name === definition.name,\n )\n\n if (!schemaDefinition) {\n return undefined\n }\n\n return buildFilteredObject(schemaDefinition, value, context.keyGenerator)\n }\n}\n\n/**\n * Matcher function for mapping markdown objects to Portable Text block or inline objects.\n *\n * @public\n */\nexport type ObjectMatcher<\n TValue extends Record<string, unknown> = Record<string, never>,\n> = ({\n context,\n value,\n isInline,\n}: {\n context: {schema: Schema; keyGenerator: () => string}\n value: TValue\n isInline: boolean\n}) => PortableTextObject | undefined\n\nexport function buildObjectMatcher<TDefinition extends {name: string}>(\n definition: TDefinition,\n): ObjectMatcher<ExtractValue<TDefinition>> {\n return ({context, value, isInline}) => {\n const schemaCollection = isInline\n ? context.schema.inlineObjects\n : context.schema.blockObjects\n\n const schemaDefinition = schemaCollection.find(\n (item) => item.name === definition.name,\n )\n\n if (!schemaDefinition) {\n return undefined\n }\n\n return buildFilteredObject(schemaDefinition, value, context.keyGenerator)\n }\n}\n\nexport type ExtractValue<\n TDefinition extends NonNullable<SchemaDefinition['blockObjects']>[0],\n> = TDefinition extends {fields: ReadonlyArray<{name: infer TNames}>}\n ? Record<TNames & string, unknown>\n : Record<string, never>\n","import {alert} from '@mdit/plugin-alert'\nimport {\n isSpan,\n isTextBlock,\n type PortableTextBlock,\n type PortableTextObject,\n type PortableTextTextBlock,\n type Schema,\n} from '@portabletext/schema'\nimport markdownit from 'markdown-it'\nimport {\n blockquoteStyleDefinition,\n defaultCalloutObjectDefinition,\n defaultCodeDecoratorDefinition,\n defaultCodeObjectDefinition,\n defaultEmDecoratorDefinition,\n defaultHorizontalRuleObjectDefinition,\n defaultHtmlObjectDefinition,\n defaultImageObjectDefinition,\n defaultLinkObjectDefinition,\n defaultOrderedListItemDefinition,\n defaultSchema,\n defaultStrikeThroughDecoratorDefinition,\n defaultStrongDecoratorDefinition,\n defaultTableObjectDefinition,\n defaultTaskListItemDefinition,\n defaultUnorderedListItemDefinition,\n h1StyleDefinition,\n h2StyleDefinition,\n h3StyleDefinition,\n h4StyleDefinition,\n h5StyleDefinition,\n h6StyleDefinition,\n normalStyleDefinition,\n} from '../default-schema'\nimport {unescapeImageAndLinkText} from '../escape'\nimport {defaultKeyGenerator} from '../key-generator'\nimport {degradationMessage} from './degradation-messages'\nimport {\n buildAnnotationMatcher,\n buildDecoratorMatcher,\n buildListItemMatcher,\n buildObjectMatcher,\n buildStyleMatcher,\n readDroppedFields,\n type AnnotationMatcher,\n type DecoratorMatcher,\n type ExtractValue,\n type ListItemMatcher,\n type ObjectMatcher,\n type StyleMatcher,\n} from './matchers'\n\n/**\n * The classification of a lossy conversion encountered while converting\n * markdown to Portable Text: a markdown construct the conversion couldn't\n * carry through losslessly, whether because the target schema (or the\n * active matchers) doesn't represent it, or because the surrounding\n * structure (a table cell, for instance) can't hold the shape markdown\n * expressed, so the conversion fell back to a lossier representation\n * instead of failing.\n *\n * @public\n */\nexport type DegradationType =\n | 'decorator-dropped'\n | 'annotation-dropped'\n | 'style-fallback'\n | 'list-flattened'\n | 'task-checkbox-stripped'\n | 'table-flattened'\n | 'code-block-to-text'\n | 'horizontal-rule-to-text'\n | 'html-block-to-text'\n | 'inline-html-dropped'\n | 'image-block-to-inline'\n | 'image-inline-to-block'\n | 'image-to-text'\n | 'callout-fallback'\n | 'fields-dropped'\n | 'object-carrier-invalid'\n\n/**\n * Reports a single lossy conversion, in encounter order: as the conversion\n * walks the markdown, that's the order nested constructs close in, not\n * necessarily top-to-bottom document order. `line` is the 1-based markdown\n * source line: usually the line of the token that degraded, but for a\n * token without its own line (an inline construct inside a table cell, for\n * instance) the enclosing construct's line instead; absent when no token\n * carries a usable line at all. `snippet` is the offending construct's\n * text, truncated to 40 characters with an ellipsis, present whenever the\n * degradation has a specific piece of source text to quote (a dropped\n * decorator's span, an unsupported image's alt text) and absent when the\n * construct has nothing to quote (a table with no `table` block object, a\n * callout whose type isn't in the schema). `message` is human-readable and\n * may change between releases; match on `type`, not `message`. The set of\n * `type` values grows in minor releases as new degradation sites report, so\n * compare against the values you handle rather than switching exhaustively.\n *\n * @public\n */\nexport type Degradation = {\n type: DegradationType\n message: string\n line?: number\n snippet?: string\n}\n\ntype Options = {\n schema?: Schema\n keyGenerator?: () => string\n /**\n * Called at most once, after the conversion has walked the whole\n * document, only when at least one construct degraded. Left unset, the\n * conversion stays silent and returns the lossiest representation it can\n * build. Passed a function, observe every degradation via\n * `report.degradations`, in encounter order. Enforce against lossy output by\n * throwing your own error from inside the callback, using\n * `report.message` (the canonical grouped text) as its message; the\n * throw propagates out of `markdownToPortableText`.\n *\n * ```ts\n * markdownToPortableText(markdown, {onDegradation: ({message}) => { throw new Error(message) }})\n * ```\n *\n * `report` is a single object, not positional parameters, so it can gain\n * fields later without a breaking change.\n */\n onDegradation?: (report: {\n degradations: Array<Degradation>\n message: string\n }) => void\n marks?: {\n strong?: DecoratorMatcher\n em?: DecoratorMatcher\n code?: DecoratorMatcher\n strikeThrough?: DecoratorMatcher\n link?: AnnotationMatcher<{href: string; title: string | undefined}>\n }\n block?: {\n normal?: StyleMatcher\n blockquote?: StyleMatcher\n h1?: StyleMatcher\n h2?: StyleMatcher\n h3?: StyleMatcher\n h4?: StyleMatcher\n h5?: StyleMatcher\n h6?: StyleMatcher\n }\n listItem?: {\n number?: ListItemMatcher\n bullet?: ListItemMatcher\n task?: ListItemMatcher\n }\n types?: {\n code?: ObjectMatcher<{language: string | undefined; code: string}>\n horizontalRule?: ObjectMatcher\n html?: ObjectMatcher<{html: string}>\n table?: ObjectMatcher<{\n headerRows: number | undefined\n alignment: Array<'left' | 'center' | 'right' | null> | undefined\n rows: Array<{\n _key: string\n _type: 'row'\n cells: Array<{\n _type: 'cell'\n _key: string\n value: Array<PortableTextBlock>\n }>\n }>\n }>\n image?: ObjectMatcher<{src: string; alt: string; title: string | undefined}>\n callout?: ObjectMatcher<{tone: string; content: Array<PortableTextBlock>}>\n blockquote?: ObjectMatcher<{content: Array<PortableTextBlock>}>\n list?: ObjectMatcher<{\n kind: 'bullet' | 'number' | 'task'\n items: Array<{\n _type: 'list-item'\n _key: string\n checked?: boolean\n content: Array<PortableTextBlock | PortableTextObject>\n }>\n }>\n }\n html?: {\n /**\n * How to handle inline HTML.\n * - 'skip': Ignore inline HTML (default)\n * - 'text': Convert inline HTML to plain text\n *\n * @defaultValue 'skip'\n */\n inline?: 'skip' | 'text'\n }\n}\n\nconst codeBlockMatcher: ObjectMatcher<\n ExtractValue<typeof defaultCodeObjectDefinition>\n> = ({context, value, isInline}) => {\n const defaultMatcher = buildObjectMatcher(defaultCodeObjectDefinition)\n const codeObject = defaultMatcher({context, value, isInline})\n\n if (!codeObject) {\n return undefined\n }\n\n if (!('code' in codeObject)) {\n return undefined\n }\n\n return codeObject\n}\n\nconst imageBlockMatcher: ObjectMatcher<\n ExtractValue<typeof defaultImageObjectDefinition>\n> = ({context, value, isInline}) => {\n const defaultMatcher = buildObjectMatcher(defaultImageObjectDefinition)\n const imageObject = defaultMatcher({context, value, isInline})\n\n if (!imageObject) {\n return undefined\n }\n\n if (!('src' in imageObject)) {\n return undefined\n }\n\n return imageObject\n}\n\nconst tableBlockMatcher: ObjectMatcher<\n ExtractValue<typeof defaultTableObjectDefinition>\n> = ({context, value, isInline}) => {\n const defaultMatcher = buildObjectMatcher(defaultTableObjectDefinition)\n const tableObject = defaultMatcher({context, value, isInline})\n\n if (!tableObject) {\n return undefined\n }\n\n if (!('rows' in tableObject)) {\n return undefined\n }\n\n return tableObject\n}\n\nconst defaultOptions = {\n schema: defaultSchema,\n keyGenerator: defaultKeyGenerator,\n html: {\n inline: 'skip',\n },\n block: {\n normal: buildStyleMatcher(normalStyleDefinition),\n blockquote: buildStyleMatcher(blockquoteStyleDefinition),\n h1: buildStyleMatcher(h1StyleDefinition),\n h2: buildStyleMatcher(h2StyleDefinition),\n h3: buildStyleMatcher(h3StyleDefinition),\n h4: buildStyleMatcher(h4StyleDefinition),\n h5: buildStyleMatcher(h5StyleDefinition),\n h6: buildStyleMatcher(h6StyleDefinition),\n },\n listItem: {\n number: buildListItemMatcher(defaultOrderedListItemDefinition),\n bullet: buildListItemMatcher(defaultUnorderedListItemDefinition),\n task: buildListItemMatcher(defaultTaskListItemDefinition),\n },\n marks: {\n strong: buildDecoratorMatcher(defaultStrongDecoratorDefinition),\n em: buildDecoratorMatcher(defaultEmDecoratorDefinition),\n code: buildDecoratorMatcher(defaultCodeDecoratorDefinition),\n strikeThrough: buildDecoratorMatcher(\n defaultStrikeThroughDecoratorDefinition,\n ),\n link: buildAnnotationMatcher(defaultLinkObjectDefinition),\n },\n types: {\n code: codeBlockMatcher,\n horizontalRule: buildObjectMatcher(defaultHorizontalRuleObjectDefinition),\n html: buildObjectMatcher(defaultHtmlObjectDefinition),\n image: imageBlockMatcher,\n callout: buildObjectMatcher(defaultCalloutObjectDefinition),\n table: tableBlockMatcher,\n },\n} as const satisfies Options\n\n/**\n * Reads GFM column alignment from a markdown-it cell token's `style`\n * attribute. Tolerates other CSS declarations sharing the value.\n */\nexport function extractAlignmentFromStyleAttr(\n styleAttr: string | null,\n): 'left' | 'center' | 'right' | null {\n if (!styleAttr) {\n return null\n }\n const match = styleAttr.match(/text-align\\s*:\\s*(left|center|right)/)\n if (!match) {\n return null\n }\n return match[1] as 'left' | 'center' | 'right'\n}\n\n/**\n * A table row is empty when every cell holds only blank spans, no non-empty\n * text, no inline objects, no non-text blocks. Used to detect a headerless\n * GFM table: `portableTextToMarkdown` emits an empty header row for\n * `headerRows: 0`, and an empty header must round-trip back to\n * `headerRows: 0` rather than a phantom header row.\n */\nfunction isEmptyTableRow(\n cells: Array<{value: Array<PortableTextBlock>}>,\n context: {schema: Schema},\n): boolean {\n return cells.every((cell) =>\n cell.value.every(\n (block) =>\n isTextBlock(context, block) &&\n block.children.every(\n (child) => isSpan(context, child) && (child.text ?? '').trim() === '',\n ),\n ),\n )\n}\n\n/**\n * Flattens a table structure by lifting all blocks from all cells.\n */\nfunction flattenTable(\n table: {\n rows: Array<{\n _key: string\n _type: 'row'\n cells: Array<{\n _type: 'cell'\n _key: string\n value: Array<PortableTextBlock>\n }>\n }>\n headerRows: number\n },\n portableText: Array<PortableTextBlock>,\n): void {\n // Flatten the table by lifting all blocks from all cells\n for (const row of table.rows) {\n for (const cell of row.cells) {\n for (const block of cell.value) {\n portableText.push(block)\n }\n }\n }\n}\n\n/**\n * Truncates a degradation message's snippet to keep the thrown/reported\n * message readable. Truncates on character count, not word boundaries: the\n * snippet is a diagnostic pointer back to the source, not prose. Undefined\n * for empty input, so a construct with nothing to quote (an empty link's\n * text, say) omits `snippet` entirely instead of reporting `\"\"`. Backs the\n * cut off by one unit when it would land on a lead surrogate, so a snippet\n * ending mid-emoji doesn't produce an unpaired surrogate. A literal newline\n * surviving into the snippet is escaped to `\\n`, since the reported message\n * is one line per finding.\n */\nfunction truncateSnippet(text: string, maxLength = 40): string | undefined {\n if (text.length === 0) {\n return undefined\n }\n\n if (text.length <= maxLength) {\n return text.replace(/\\n/g, '\\\\n')\n }\n\n let cut = maxLength\n const codeUnit = text.charCodeAt(cut - 1)\n if (codeUnit >= 0xd800 && codeUnit <= 0xdbff) {\n cut -= 1\n }\n\n return `${text.slice(0, cut).replace(/\\n/g, '\\\\n')}...`\n}\n\n/**\n * Concatenates the plain text between an inline open token (`strong_open`,\n * `em_open`, `s_open`, `link_open`) and its matching close, for use as a\n * degradation message snippet. Tracks nesting depth so a same-type token\n * nested inside itself doesn't stop the scan at the wrong close.\n */\nfunction collectInlineText(\n children: ReadonlyArray<{type: string; content: string}>,\n openIndex: number,\n openType: string,\n closeType: string,\n): string {\n let depth = 1\n let text = ''\n for (let i = openIndex + 1; i < children.length; i++) {\n const child = children[i]\n if (!child) {\n continue\n }\n if (child.type === openType) {\n depth++\n } else if (child.type === closeType) {\n depth--\n if (depth === 0) {\n break\n }\n } else if (child.type === 'text') {\n text += child.content\n } else if (child.type === 'softbreak') {\n text += ' '\n } else if (child.type === 'hardbreak') {\n text += '\\n'\n }\n }\n return text\n}\n\n// A per-group snippet/line list longer than this is truncated with an\n// `and N more` tail: the grouped message is a one-line-per-finding summary,\n// not a full dump of every occurrence (that's what `degradations` is for).\nconst MAX_LISTED_PER_GROUP = 5\n\nfunction capList(values: ReadonlyArray<string>): string {\n if (values.length <= MAX_LISTED_PER_GROUP) {\n return values.join(', ')\n }\n const shown = values.slice(0, MAX_LISTED_PER_GROUP)\n const more = values.length - MAX_LISTED_PER_GROUP\n return `${shown.join(', ')}, and ${more} more`\n}\n\n/**\n * Builds the canonical grouped message reported alongside a non-empty\n * `degradations` array: identical (`type`, `message`) pairs collapse into one\n * line, so a document with the same missing decorator on three spans\n * doesn't repeat the same sentence three times. Groups sort by their\n * earliest-lined entry so the message reads top-to-bottom regardless of walk\n * order: nested constructs (a blockquote inside a blockquote, say) report\n * the innermost closing first even though it opened last, and that line may\n * arrive after another entry already in the group. Groups without any lined\n * entry sort last, in their relative encounter order.\n */\nfunction buildDegradationMessage(degradations: Array<Degradation>): string {\n const groups: Array<{\n base: string\n entries: Array<Degradation>\n }> = []\n const groupIndexByKey = new Map<string, number>()\n\n for (const degradation of degradations) {\n const key = `${degradation.type}\\u0000${degradation.message}`\n let groupIndex = groupIndexByKey.get(key)\n if (groupIndex === undefined) {\n groupIndex = groups.length\n groupIndexByKey.set(key, groupIndex)\n groups.push({base: degradation.message, entries: []})\n }\n groups[groupIndex]!.entries.push(degradation)\n }\n\n const minLine = (entries: Array<Degradation>): number | undefined => {\n const definedLines = entries\n .map((entry) => entry.line)\n .filter((line): line is number => line !== undefined)\n return definedLines.length > 0 ? Math.min(...definedLines) : undefined\n }\n\n const sortedGroups = [...groups].sort((a, b) => {\n const lineA = minLine(a.entries)\n const lineB = minLine(b.entries)\n if (lineA === undefined) {\n return lineB === undefined ? 0 : 1\n }\n if (lineB === undefined) {\n return -1\n }\n return lineA - lineB\n })\n\n const lines = sortedGroups.map((group) => {\n if (group.entries.length === 1) {\n const event = group.entries[0]!\n const snippetPart =\n event.snippet === undefined ? '' : ` (\"${event.snippet}\")`\n return event.line === undefined\n ? `- ${event.message}${snippetPart}`\n : `- line ${event.line}: ${event.message}${snippetPart}`\n }\n\n const snippets = group.entries\n .map((entry) => entry.snippet)\n .filter((snippet): snippet is string => snippet !== undefined)\n\n // Read top-to-bottom regardless of encounter order, same reasoning as\n // the group sort above. Entries without a line are counted but never\n // printed: joining a maybe-`undefined` would put the literal word\n // `undefined` in the message. Deduped: several entries in the group can\n // share one line (a table's cells, all pinned to the table's start\n // line), and the count already carries how many there were.\n const linesAscending = [\n ...new Set(\n group.entries\n .map((entry) => entry.line)\n .filter((line): line is number => line !== undefined)\n .sort((a, b) => a - b),\n ),\n ]\n\n const count = group.entries.length\n const suffix =\n snippets.length === count\n ? `(${count}\\u00d7: ${capList(snippets.map((snippet) => `\"${snippet}\"`))})`\n : linesAscending.length > 0\n ? `(${count}\\u00d7: lines ${capList(linesAscending.map(String))})`\n : `(${count}\\u00d7)`\n\n return `- ${group.base} ${suffix}`\n })\n\n return ['Markdown could not be converted without loss:', ...lines].join('\\n')\n}\n\n/**\n * Converts a markdown string to an array of Portable Text blocks.\n *\n * @public\n */\nexport function markdownToPortableText(\n markdown: string,\n options?: Options,\n): Array<PortableTextBlock> {\n const consolidatedOptions = {\n schema: options?.schema ?? defaultSchema,\n keyGenerator: options?.keyGenerator ?? defaultKeyGenerator,\n html: {\n inline: options?.html?.inline ?? 'skip',\n },\n marks: {\n ...defaultOptions.marks,\n ...options?.marks,\n },\n block: {\n ...defaultOptions.block,\n ...options?.block,\n },\n listItem: {\n ...defaultOptions.listItem,\n ...options?.listItem,\n },\n types: {\n ...defaultOptions.types,\n ...options?.types,\n },\n }\n\n const degradationEvents: Array<Degradation> = []\n\n const report = (event: Degradation): void => {\n degradationEvents.push(event)\n }\n\n // A markdown-it token's `map` is `[startLine, endLine)`, 0-based. Degradation\n // events report the 1-based start line for readability.\n const lineOf = (\n candidateToken: {map?: [number, number] | null} | null | undefined,\n ): number | undefined =>\n candidateToken?.map ? candidateToken.map[0] + 1 : undefined\n\n const reportStyleFallback = (\n name: string,\n line?: number,\n snippet?: string,\n ): void => {\n if (/^h[1-6]$/.test(name)) {\n const truncated =\n snippet === undefined ? undefined : truncateSnippet(snippet)\n report({\n type: 'style-fallback',\n message: degradationMessage['style-fallback'](name),\n line,\n snippet: truncated,\n })\n return\n }\n\n if (name === 'blockquote') {\n report({\n type: 'style-fallback',\n message: degradationMessage['style-fallback'](name),\n line,\n })\n return\n }\n\n report({\n type: 'style-fallback',\n message: degradationMessage['style-fallback'](name),\n line,\n })\n }\n\n // Only a default matcher (`buildObjectMatcher`, `buildAnnotationMatcher`)\n // tags its return value this way; a consumer-supplied matcher's output\n // passes through untouched, since its own filtering is its own business.\n const reportFieldsDropped = (\n object: PortableTextObject | undefined,\n line: number | undefined,\n ): void => {\n const dropped = readDroppedFields(object)\n if (!dropped) {\n return\n }\n const names = dropped.keys.map((key) => `\\`${key}\\``).join(', ')\n report({\n type: 'fields-dropped',\n message: degradationMessage['fields-dropped'](names, dropped.construct),\n line,\n })\n }\n\n const md = markdownit({\n html: true,\n linkify: true,\n typographer: false,\n })\n .enable(['strikethrough', 'table'])\n .use(alert)\n\n const tokens = md.parse(markdown, {})\n\n // Pre-pass: detect GFM task-list checkbox prefixes (`[ ]`, `[x]`, `[X]`)\n // on the first inline content of each list item. Strip the prefix from the\n // inline content and remember which list items are tasks (and their checked\n // state) so the main walk can apply `listItem: 'task'` and `checked` when\n // processing the corresponding `list_item_open` token.\n const taskCheckedByListItemIndex = new Map<number, boolean>()\n // The item's text (after the checkbox prefix is stripped below), for the\n // `task-checkbox-stripped` degradation message's snippet.\n const taskItemTextByListItemIndex = new Map<number, string>()\n for (let i = 0; i < tokens.length; i++) {\n const token = tokens[i]\n if (token?.type !== 'list_item_open') {\n continue\n }\n // Find the first inline token within this list item.\n let inlineIndex = -1\n for (let j = i + 1; j < tokens.length; j++) {\n const candidate = tokens[j]\n if (!candidate) {\n continue\n }\n if (candidate.type === 'list_item_close') {\n break\n }\n if (candidate.type === 'inline') {\n inlineIndex = j\n break\n }\n }\n if (inlineIndex === -1) {\n continue\n }\n const inlineToken = tokens[inlineIndex]\n if (!inlineToken) {\n continue\n }\n const match = inlineToken.content.match(/^\\[([ xX])\\] /)\n if (!match) {\n continue\n }\n const checked = match[1] !== ' '\n taskCheckedByListItemIndex.set(i, checked)\n // Strip the prefix from the content and the first child text token so the\n // resulting span doesn't include the checkbox marker.\n inlineToken.content = inlineToken.content.slice(match[0].length)\n taskItemTextByListItemIndex.set(i, inlineToken.content)\n const firstChild = inlineToken.children?.[0]\n if (firstChild && typeof firstChild.content === 'string') {\n firstChild.content = firstChild.content.slice(match[0].length)\n }\n }\n\n const portableText: Array<PortableTextBlock> = []\n\n // State\n let currentBlock: PortableTextTextBlock | null = null\n const currentListStack: Array<string | null> = []\n const markDefRefs: Array<string> = [] // mark keys: 'strong', 'em', 'code', or link keys\n let currentMarkDefs: Array<PortableTextObject> = []\n let currentBlockquoteStyle: string | null = null // Track blockquote style when inside blockquote\n let inListItem = false // Track if we're inside a list item\n // Provenance for `currentBlock`, set by `startBlock`'s caller: whether the\n // block's style was actually resolved through `currentBlockquoteStyle`\n // (a paragraph, say) rather than independently landing on the same value\n // by coincidence (a heading whose own style declined to the same\n // `normal` fallback). `flushBlock`'s callout gate reads this instead of\n // comparing styles, since two independent declines can resolve to the\n // same style name without either one being the other.\n let currentBlockTookBlockquoteStyle = false\n // Provenance for `currentBlock`: whether it was created to accumulate\n // paragraph text (a `paragraph_open`, or inline content starting a block\n // with no wrapper) as opposed to a dedicated single-purpose block (a\n // heading, a table cell, a code/HTML/hr fallback-to-text). A structural\n // list's decline fallback merges adjacent item content back into the flat\n // path's shape, and the flat path only ever merges accumulated paragraph\n // blocks: a fallback-to-text block always flushes and starts its own\n // block explicitly, never sharing `currentBlock` with surrounding text.\n let currentBlockIsPlainParagraph = false\n const plainParagraphBlocks = new WeakSet<PortableTextTextBlock>()\n\n // Callout state\n let calloutStartIndex: number | null = null\n let calloutStartTarget: Array<PortableTextBlock | PortableTextObject> | null =\n null\n let calloutType: string | null = null\n let calloutStartLine: number | undefined\n // Style names that declined while setting up the callout's content style,\n // reported once `alert_title` supplies the callout's own first line (the\n // marker, not the first content line `alert_open`'s map starts at).\n let calloutPendingStyleFallbacks: Array<string> = []\n\n // Blockquote container state. When `types.blockquote` is defined and the\n // parser enters a `blockquote_open`, a frame is pushed here. Block\n // emissions inside the surrounding open/close pair are captured and\n // spliced out at close to wrap them in a `blockquote` block-object.\n // Nested blockquotes push additional frames; `blockTarget()` already\n // routes correctly because the splice happens against the same target.\n const blockquoteStack: Array<{\n startTarget: Array<PortableTextBlock | PortableTextObject>\n startIndex: number\n line: number | undefined\n }> = []\n\n // Table state\n let currentTable: {\n rows: Array<{\n _key: string\n _type: 'row'\n cells: Array<{\n _type: 'cell'\n _key: string\n value: Array<PortableTextBlock>\n }>\n }>\n headerRows: number\n emptyHeaderDropped: boolean\n alignment: Array<'left' | 'center' | 'right' | null>\n line: number | undefined\n } | null = null\n let currentTableRow: Array<{\n _type: 'cell'\n _key: string\n value: Array<PortableTextBlock>\n }> | null = null\n let inTableHead = false\n // Images demoted from block-level to inline while pushed into a table\n // cell (see the `inline` case). `td_close`/`th_close` may lift a sole\n // image child back to block-level losslessly; membership here lets that\n // lift decide whether to report the demotion, and the value carries which\n // cause to report it with (a schema-declared block image forced inline by\n // the cell vs. no block image in the schema at all).\n const demotedTableImages = new WeakMap<\n PortableTextObject,\n 'table-cell' | 'no-block-image'\n >()\n\n // List container state. When `types.list` is defined and the parser enters\n // a `bullet_list_open` / `ordered_list_open`, a structural-list frame is\n // pushed here. Block emissions inside the surrounding `list_item_open` /\n // `list_item_close` pair are diverted into the item's `content` array\n // instead of the top-level `portableText`. At list-close, the matcher is\n // called to materialize a `list` block-object, which is pushed into the\n // enclosing target (parent list item's content if nested, else top-level).\n type ListContainerItem = {\n _type: 'list-item'\n _key: string\n checked?: boolean\n content: Array<PortableTextBlock | PortableTextObject>\n }\n type ListContainerFrame = {\n kind: 'bullet' | 'number' | 'task'\n items: Array<ListContainerItem>\n currentItem: ListContainerItem | null\n line: number | undefined\n }\n // A null entry marks a list that is being handled by the flat path (either\n // because `types.list` is undefined, or because a nested list inside a\n // flat-path list should also stay flat). Parallel to `currentListStack`.\n const listContainerStack: Array<ListContainerFrame | null> = []\n\n // Flat-path `list-flattened` verdicts, parallel to `currentListStack`: a\n // list whose own kind (`bullet`/`number`) isn't in the schema doesn't\n // necessarily degrade, since every item might still resolve through a\n // `task` checkbox override. The verdict is deferred to the first item\n // that actually needs the fallback (`list_item_open`'s `listType === null`\n // branch), so a schema with only a `task` list and an all-checkbox list\n // reports nothing. `null` marks a list whose own kind resolved fine, so no\n // verdict is pending.\n const pendingListFlattenedStack: Array<{\n line: number | undefined\n kindName: 'bullet' | 'number'\n reported: boolean\n } | null> = []\n\n // Per-item task-checkbox metadata for structural list items (line + text\n // snippet), keyed by item object identity. Not stored on the item itself:\n // `frame.items` is handed verbatim to a consumer's `types.list` matcher,\n // and this bookkeeping is only needed if that matcher declines and the\n // fallback needs to reproduce the flat path's `task-checkbox-stripped`\n // report for the item.\n const taskInfoByListItem = new WeakMap<\n ListContainerItem,\n {line: number | undefined; snippet: string | undefined}\n >()\n\n /**\n * Returns the array that block emissions should land in. If the innermost\n * structural list frame has an open `currentItem`, blocks land in that\n * item's `content`. Otherwise blocks land at the top level.\n */\n const blockTarget = (): Array<PortableTextBlock | PortableTextObject> => {\n for (let i = listContainerStack.length - 1; i >= 0; i--) {\n const frame = listContainerStack[i]\n if (frame && frame.currentItem) {\n return frame.currentItem.content\n }\n }\n return portableText\n }\n\n /**\n * Pushes a block into the current target (innermost open list item, or\n * top-level `portableText` if none). Use instead of direct\n * `portableText.push(...)` for any block emission that should be captured\n * by an enclosing list container.\n */\n const pushBlock = (block: PortableTextBlock | PortableTextObject): void => {\n blockTarget().push(block as PortableTextBlock)\n }\n\n const startBlock = (\n style: string,\n provenance?: {tookBlockquoteStyle?: boolean; isPlainParagraph?: boolean},\n ) => {\n flushBlock()\n currentBlock = {\n _type: 'block' as const,\n style,\n children: [],\n _key: consolidatedOptions.keyGenerator(),\n markDefs: [],\n }\n currentMarkDefs = []\n currentBlockTookBlockquoteStyle = provenance?.tookBlockquoteStyle ?? false\n currentBlockIsPlainParagraph = provenance?.isPlainParagraph ?? false\n }\n\n const flushBlock = () => {\n if (!currentBlock) {\n return\n }\n\n // A callout with pending style fallbacks (its own style resolution\n // declined) only actually degrades once a text block actually commits\n // needing that resolved style. A heading inside a callout commits with\n // its own heading style and never touches the callout's content style,\n // so it doesn't count; a plain paragraph does, since paragraphs adopt\n // `currentBlockquoteStyle` directly. A callout holding nothing but,\n // say, a standalone image discards its placeholder block without ever\n // reaching here (see the `inline` case's standalone-image branch), and\n // never degraded.\n if (\n calloutPendingStyleFallbacks.length > 0 &&\n currentBlockTookBlockquoteStyle\n ) {\n for (const name of calloutPendingStyleFallbacks) {\n reportStyleFallback(name, calloutStartLine)\n }\n calloutPendingStyleFallbacks = []\n }\n\n // Text blocks must have at least one child span\n if (currentBlock.children.length === 0) {\n currentBlock.children.push({\n _type: consolidatedOptions.schema.span.name,\n _key: consolidatedOptions.keyGenerator(),\n text: '',\n marks: [],\n })\n }\n\n // Assign accumulated markDefs to the block\n currentBlock.markDefs = currentMarkDefs\n\n if (currentBlockIsPlainParagraph) {\n plainParagraphBlocks.add(currentBlock)\n }\n\n pushBlock(currentBlock)\n\n currentBlock = null\n currentMarkDefs = []\n }\n\n const addSpan = (text: string) => {\n if (text.length === 0) {\n return\n }\n\n if (!currentBlock) {\n const style =\n currentBlockquoteStyle ??\n consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!style) {\n reportStyleFallback('normal')\n startBlock('normal', {isPlainParagraph: true})\n } else {\n startBlock(style, {\n tookBlockquoteStyle: currentBlockquoteStyle !== null,\n isPlainParagraph: true,\n })\n }\n }\n\n if (!currentBlock) {\n throw new Error('Expected current block')\n }\n\n const lastChild = currentBlock.children.at(-1)\n\n if (\n isSpan({schema: consolidatedOptions.schema}, lastChild) &&\n lastChild.marks?.every((mark) => markDefRefs.includes(mark)) &&\n markDefRefs.every((mark) => lastChild.marks?.includes(mark))\n ) {\n // Merge with previous span if marks match\n lastChild.text += text\n } else {\n currentBlock.children.push({\n _type: consolidatedOptions.schema.span.name,\n _key: consolidatedOptions.keyGenerator(),\n text: text,\n marks: [...markDefRefs],\n })\n }\n }\n\n // Helpers for lists\n const listLevel = () => currentListStack.length\n const ensureListBlock = (listItem: string, checked?: boolean) => {\n if (!currentBlock) {\n // Use blockquote style if inside a blockquote, otherwise use normal style\n const style =\n currentBlockquoteStyle ??\n consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!style) {\n reportStyleFallback('normal')\n startBlock('normal')\n } else {\n startBlock(style, {\n tookBlockquoteStyle: currentBlockquoteStyle !== null,\n })\n }\n }\n\n if (!currentBlock) {\n throw new Error('Expected current block')\n }\n\n if (\n currentBlock.listItem !== listItem ||\n currentBlock.level !== listLevel()\n ) {\n currentBlock.listItem = listItem\n currentBlock.level = listLevel()\n }\n\n if (checked !== undefined) {\n ;(currentBlock as PortableTextTextBlock & {checked?: boolean}).checked =\n checked\n }\n }\n\n // Walk tokens\n for (let tokenIndex = 0; tokenIndex < tokens.length; tokenIndex++) {\n const token = tokens[tokenIndex]\n if (!token) {\n continue\n }\n\n switch (token.type) {\n // Paragraphs\n case 'paragraph_open': {\n // If we're in a list item but have no current block (e.g., after a code block),\n // we need to create a new list item block\n if (inListItem) {\n // Structural list path: start a plain text block; the paragraph\n // lands in the current list item's `content` via `pushBlock`.\n if (listContainerStack.at(-1)) {\n if (!currentBlock) {\n // Use blockquote style if inside a blockquote, otherwise use normal style\n const style =\n currentBlockquoteStyle ??\n consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!style) {\n reportStyleFallback('normal', lineOf(token))\n }\n\n startBlock(style ?? 'normal', {\n tookBlockquoteStyle: currentBlockquoteStyle !== null,\n isPlainParagraph: true,\n })\n }\n break\n }\n\n // Flat list path: ensure the current text block carries\n // `listItem` + `level` fields.\n if (!currentBlock) {\n const listType = currentListStack.at(-1)\n\n if (listType) {\n ensureListBlock(listType)\n }\n }\n\n break\n }\n\n // Use blockquote style if inside a blockquote, otherwise use normal style\n const style =\n currentBlockquoteStyle ??\n consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!style) {\n reportStyleFallback('normal', lineOf(token))\n startBlock('normal', {isPlainParagraph: true})\n break\n }\n\n startBlock(style, {\n tookBlockquoteStyle: currentBlockquoteStyle !== null,\n isPlainParagraph: true,\n })\n break\n }\n case 'paragraph_close':\n // In a flat list item: skip flushing, list_item_close will flush.\n // In a structural list item: flush so multiple paragraphs in one\n // item land as separate text blocks.\n if (inListItem) {\n if (listContainerStack.at(-1)) {\n flushBlock()\n }\n break\n }\n flushBlock()\n break\n\n // Headings\n case 'heading_open': {\n const level = Number(token?.tag?.slice(1))\n\n // Map level to the appropriate heading matcher\n const headingMatchers = {\n 1: consolidatedOptions.block.h1,\n 2: consolidatedOptions.block.h2,\n 3: consolidatedOptions.block.h3,\n 4: consolidatedOptions.block.h4,\n 5: consolidatedOptions.block.h5,\n 6: consolidatedOptions.block.h6,\n } as const\n\n const headingMatcher =\n headingMatchers[level as keyof typeof headingMatchers]\n\n const headingStyle = headingMatcher?.({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!headingStyle) {\n reportStyleFallback(\n `h${level}`,\n lineOf(token),\n tokens[tokenIndex + 1]?.content,\n )\n }\n\n const style =\n headingStyle ??\n consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!style) {\n reportStyleFallback('normal', lineOf(token))\n startBlock('normal')\n break\n }\n\n startBlock(style)\n break\n }\n case 'heading_close':\n flushBlock()\n break\n\n // Blockquote\n case 'blockquote_open': {\n // Flush any current block before entering blockquote\n flushBlock()\n\n // Structural-blockquote path: when the consumer registers a\n // `types.blockquote` matcher, plain blockquotes (NOT GFM alerts -\n // those use separate `alert_open`/`alert_close` tokens) become\n // block-objects with an explicit `content` array. Block emissions\n // inside the open/close pair are spliced out at close time and\n // wrapped in a `blockquote` block-object.\n if (consolidatedOptions.types.blockquote) {\n const startTarget = blockTarget()\n blockquoteStack.push({\n startTarget,\n startIndex: startTarget.length,\n line: lineOf(token),\n })\n break\n }\n\n // Flat path: set the blockquote style for paragraphs inside the\n // blockquote so they emit text blocks with `style: 'blockquote'`.\n const blockquoteStyle = consolidatedOptions.block.blockquote({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!blockquoteStyle) {\n reportStyleFallback('blockquote', lineOf(token))\n }\n\n const style =\n blockquoteStyle ??\n consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!style) {\n reportStyleFallback('normal', lineOf(token))\n }\n\n currentBlockquoteStyle = style ?? 'normal'\n break\n }\n case 'blockquote_close': {\n // Flush any blockquote content before exiting\n flushBlock()\n\n // Structural path: pop the topmost frame and splice its captured\n // content into a `blockquote` block-object via the matcher. If the\n // matcher returns undefined, fall back to flat-style by re-emitting\n // the content blocks with `style: 'blockquote'`.\n if (\n consolidatedOptions.types.blockquote &&\n blockquoteStack.length > 0\n ) {\n const frame = blockquoteStack.pop()\n if (frame) {\n const contentBlocks = frame.startTarget.splice(\n frame.startIndex,\n ) as Array<PortableTextBlock>\n\n const blockquoteObject = consolidatedOptions.types.blockquote({\n context: {\n schema: consolidatedOptions.schema,\n keyGenerator: consolidatedOptions.keyGenerator,\n },\n value: {content: contentBlocks},\n isInline: false,\n })\n\n if (blockquoteObject) {\n pushBlock(blockquoteObject)\n } else {\n // Matcher returned undefined: fall back to exactly what the\n // flat path would have produced (never registering\n // `types.blockquote` at all), including which events it would\n // have reported. Only plain paragraphs pick up the blockquote\n // style there; headings and fallback-to-text blocks (a\n // degraded code fence, say) keep whatever style they already\n // resolved to, so restyling is gated on `plainParagraphBlocks`\n // provenance, not the block's resolved style name.\n const blockquoteStyle = consolidatedOptions.block.blockquote({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!blockquoteStyle) {\n reportStyleFallback('blockquote', frame.line)\n }\n\n const resolvedStyle =\n blockquoteStyle ??\n consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!resolvedStyle) {\n reportStyleFallback('normal', frame.line)\n }\n\n const fallbackStyle = resolvedStyle ?? 'blockquote'\n for (const block of contentBlocks) {\n if (\n block._type === 'block' &&\n plainParagraphBlocks.has(block as PortableTextTextBlock)\n ) {\n const restyledBlock: PortableTextTextBlock = {\n ...(block as PortableTextTextBlock),\n style: fallbackStyle,\n }\n // The spread above makes a new object, so the enclosing\n // structural list's stampable gate (keyed on\n // `plainParagraphBlocks` object identity) would otherwise\n // lose this block's provenance across the restyle.\n plainParagraphBlocks.add(restyledBlock)\n pushBlock(restyledBlock)\n } else {\n pushBlock(block)\n }\n }\n }\n }\n break\n }\n\n currentBlockquoteStyle = null\n break\n }\n // Lists\n case 'bullet_list_open': {\n flushBlock()\n\n // Structural-container path: when the consumer registers a\n // `types.list` matcher, lists become block-objects with explicit\n // `items` arrays. Block emissions inside list items are diverted\n // into `currentItem.content` via `blockTarget()`. Mirrors the\n // `types.table` pattern.\n if (consolidatedOptions.types.list) {\n listContainerStack.push({\n kind: 'bullet',\n items: [],\n currentItem: null,\n line: lineOf(token),\n })\n currentListStack.push(null)\n pendingListFlattenedStack.push(null)\n break\n }\n\n // Flat path: lists are reconstructed from text blocks with\n // `listItem` + `level` fields at render time.\n const listItem = consolidatedOptions.listItem.bullet({\n context: {schema: consolidatedOptions.schema},\n })\n\n listContainerStack.push(null)\n if (!listItem) {\n pendingListFlattenedStack.push({\n line: lineOf(token),\n kindName: 'bullet',\n reported: false,\n })\n currentListStack.push(null)\n break\n }\n pendingListFlattenedStack.push(null)\n currentListStack.push(listItem)\n break\n }\n case 'ordered_list_open': {\n flushBlock()\n\n if (consolidatedOptions.types.list) {\n listContainerStack.push({\n kind: 'number',\n items: [],\n currentItem: null,\n line: lineOf(token),\n })\n currentListStack.push(null)\n pendingListFlattenedStack.push(null)\n break\n }\n\n const listItem = consolidatedOptions.listItem.number({\n context: {schema: consolidatedOptions.schema},\n })\n\n listContainerStack.push(null)\n if (!listItem) {\n pendingListFlattenedStack.push({\n line: lineOf(token),\n kindName: 'number',\n reported: false,\n })\n currentListStack.push(null)\n break\n }\n pendingListFlattenedStack.push(null)\n currentListStack.push(listItem)\n break\n }\n case 'bullet_list_close':\n case 'ordered_list_close': {\n const frame = listContainerStack.pop()\n currentListStack.pop()\n pendingListFlattenedStack.pop()\n\n // Structural close: materialize the list block-object and push it\n // into the enclosing target (parent list item's content if nested,\n // else top-level).\n if (frame && consolidatedOptions.types.list) {\n // Promote `kind` to 'task' if any item carries a checked state.\n const kind: 'bullet' | 'number' | 'task' = frame.items.some(\n (item) => 'checked' in item,\n )\n ? 'task'\n : frame.kind\n\n const listObject = consolidatedOptions.types.list({\n context: {\n schema: consolidatedOptions.schema,\n keyGenerator: consolidatedOptions.keyGenerator,\n },\n value: {kind, items: frame.items},\n isInline: false,\n })\n\n if (listObject) {\n pushBlock(listObject)\n } else {\n // Matcher returned undefined: fall back to exactly what the\n // flat path would have produced for this same content (never\n // registering `types.list` at all), including which events it\n // would have reported. A decline whose mirrored flat form is\n // lossless (the schema has every list kind this list needs)\n // reports nothing: going structural and being declined isn't\n // itself a degradation, only losing information is.\n const kindListItem =\n (frame.kind === 'number'\n ? consolidatedOptions.listItem.number\n : consolidatedOptions.listItem.bullet)({\n context: {schema: consolidatedOptions.schema},\n }) ?? null\n const taskListItemType =\n consolidatedOptions.listItem.task?.({\n context: {schema: consolidatedOptions.schema},\n }) ?? null\n const kindName = frame.kind === 'number' ? 'number' : 'bullet'\n\n // The just-popped list was nested at depth = remaining stack\n // length + 1 (since we already popped).\n const level = listContainerStack.length + 1\n let flattenedReported = false\n\n for (const item of frame.items) {\n let itemListType: string | null = kindListItem\n let itemChecked: boolean | undefined\n\n if (item.checked !== undefined) {\n if (taskListItemType) {\n itemListType = taskListItemType\n itemChecked = item.checked\n } else if (kindListItem !== null) {\n const info = taskInfoByListItem.get(item)\n report({\n type: 'task-checkbox-stripped',\n message: degradationMessage['task-checkbox-stripped'](\n item.checked,\n ),\n line: info?.line,\n snippet: info?.snippet,\n })\n }\n }\n\n if (itemListType === null && !flattenedReported) {\n report({\n type: 'list-flattened',\n message: degradationMessage['list-flattened'](kindName),\n line: frame.line,\n })\n flattenedReported = true\n }\n\n // Adjacent plain-paragraph blocks within one item merge into\n // a single block, mirroring the flat path: consecutive\n // `paragraph_open`/`paragraph_close` pairs inside a flat list\n // item share one `currentBlock`, since only non-paragraph\n // content (headings, code blocks, ...) flushes it. Resets per\n // item: `list_item_close` always flushes in the flat path, so\n // a merge never crosses an item boundary. Gated on\n // `plainParagraphBlocks` (provenance, not shape): a\n // fallback-to-text block (a degraded code fence, say) never\n // shares `currentBlock` with surrounding text in the flat\n // path either, even when its style happens to match, and the\n // flat path never stamps it with `listItem` at all.\n let mergeTarget: PortableTextTextBlock | null = null\n\n for (const block of item.content) {\n const isStampable =\n itemListType !== null &&\n block._type === 'block' &&\n !('listItem' in block) &&\n !/^h[1-6]$/.test(\n (block as PortableTextTextBlock).style ?? '',\n ) &&\n plainParagraphBlocks.has(block as PortableTextTextBlock)\n\n if (!isStampable) {\n mergeTarget = null\n pushBlock(block)\n continue\n }\n\n const textBlock = block as PortableTextTextBlock\n if (mergeTarget && mergeTarget.style === textBlock.style) {\n mergeTarget.children.push(...textBlock.children)\n mergeTarget.markDefs = [\n ...(mergeTarget.markDefs ?? []),\n ...(textBlock.markDefs ?? []),\n ]\n continue\n }\n\n mergeTarget = {\n ...textBlock,\n listItem: itemListType as string,\n level,\n ...(itemChecked === undefined ? {} : {checked: itemChecked}),\n }\n pushBlock(mergeTarget)\n }\n }\n }\n }\n break\n }\n case 'list_item_open': {\n const frame = listContainerStack.at(-1)\n\n // Flush any previous list item block before starting a new one\n // This is needed for proper separation of list items\n if (currentBlock) {\n flushBlock()\n }\n\n // Structural path: start a new `list-item` whose `content` becomes\n // the divert target for subsequent block emissions until\n // `list_item_close`.\n if (frame) {\n const taskChecked = taskCheckedByListItemIndex.get(tokenIndex)\n frame.currentItem = {\n _type: 'list-item',\n _key: consolidatedOptions.keyGenerator(),\n ...(taskChecked === undefined ? {} : {checked: taskChecked}),\n content: [],\n }\n if (taskChecked !== undefined) {\n taskInfoByListItem.set(frame.currentItem, {\n line: lineOf(token),\n snippet: truncateSnippet(\n taskItemTextByListItemIndex.get(tokenIndex) ?? '',\n ),\n })\n }\n inListItem = true\n break\n }\n\n // Flat path\n const baseListType = currentListStack.at(-1)\n\n if (baseListType === undefined) {\n throw new Error('Expected an open list')\n }\n\n // Resolve task list type and checked state for this specific item.\n // If the schema declares a `task` list definition, GFM checkboxes\n // (`- [ ]` / `- [x]`) override the surrounding list's type for this\n // item. Otherwise the prefix has already been stripped by the\n // pre-pass and we render as the surrounding list type.\n const taskChecked = taskCheckedByListItemIndex.get(tokenIndex)\n let listType = baseListType\n let checked: boolean | undefined\n if (taskChecked !== undefined) {\n const taskListType = consolidatedOptions.listItem.task?.({\n context: {schema: consolidatedOptions.schema},\n })\n if (taskListType) {\n listType = taskListType\n checked = taskChecked\n }\n }\n\n // If listType is null, it means there's no list definition in the schema\n // Just create a normal block without list properties\n if (listType === null) {\n const pendingFlattened = pendingListFlattenedStack.at(-1)\n if (pendingFlattened && !pendingFlattened.reported) {\n report({\n type: 'list-flattened',\n message: degradationMessage['list-flattened'](\n pendingFlattened.kindName,\n ),\n line: pendingFlattened.line,\n })\n pendingFlattened.reported = true\n }\n\n // Use blockquote style if inside a blockquote, otherwise use normal style\n const style =\n currentBlockquoteStyle ??\n consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!style) {\n reportStyleFallback('normal', lineOf(token))\n startBlock('normal')\n } else {\n startBlock(style, {\n tookBlockquoteStyle: currentBlockquoteStyle !== null,\n })\n }\n inListItem = true\n break\n }\n\n if (taskChecked !== undefined && checked === undefined) {\n const itemText = taskItemTextByListItemIndex.get(tokenIndex) ?? ''\n const snippet = truncateSnippet(itemText)\n report({\n type: 'task-checkbox-stripped',\n message: degradationMessage['task-checkbox-stripped'](taskChecked),\n line: lineOf(token),\n snippet,\n })\n }\n\n ensureListBlock(listType, checked)\n inListItem = true\n break\n }\n case 'list_item_close': {\n const frame = listContainerStack.at(-1)\n\n // Structural path: flush any current block into the item's content,\n // then push the completed item onto the frame.\n if (frame && frame.currentItem) {\n flushBlock()\n frame.items.push(frame.currentItem)\n frame.currentItem = null\n inListItem = false\n break\n }\n\n // Flat path\n inListItem = false\n flushBlock()\n break\n }\n\n // Code fences / blocks\n case 'fence': {\n flushBlock()\n\n const language = token.info.trim() || undefined\n // Remove trailing newline from code content\n const code = token.content.replace(/\\n$/, '')\n\n if (language === 'json:object') {\n const objectValue = parseJsonObjectFence(code)\n\n if (objectValue) {\n pushBlock(objectValue as PortableTextObject)\n break\n }\n\n report({\n type: 'object-carrier-invalid',\n message: degradationMessage['object-carrier-invalid'](\n 'fence',\n code,\n ),\n line: lineOf(token),\n snippet: truncateSnippet(code),\n })\n }\n\n const codeObject = consolidatedOptions.types.code({\n context: {\n schema: consolidatedOptions.schema,\n keyGenerator: consolidatedOptions.keyGenerator,\n },\n value: {language, code},\n isInline: false,\n })\n\n if (!codeObject) {\n const snippet = truncateSnippet(code.split('\\n')[0] ?? '')\n report({\n type: 'code-block-to-text',\n message: degradationMessage['code-block-to-text'](language),\n line: lineOf(token),\n snippet,\n })\n\n // Code block not in schema, fall back to text block\n const style = consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!style) {\n reportStyleFallback('normal', lineOf(token))\n startBlock('normal')\n } else {\n startBlock(style)\n }\n\n addSpan(code)\n flushBlock()\n break\n }\n\n reportFieldsDropped(codeObject, lineOf(token))\n pushBlock(codeObject)\n\n break\n }\n\n // Horizontal rule\n case 'hr': {\n flushBlock()\n\n const hrObject = consolidatedOptions.types.horizontalRule({\n context: {\n schema: consolidatedOptions.schema,\n keyGenerator: consolidatedOptions.keyGenerator,\n },\n value: {},\n isInline: false,\n })\n\n if (!hrObject) {\n report({\n type: 'horizontal-rule-to-text',\n message: degradationMessage['horizontal-rule-to-text'],\n line: lineOf(token),\n })\n\n // If there's no break definition in the schema, parse as text\n const style = consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!style) {\n reportStyleFallback('normal', lineOf(token))\n startBlock('normal')\n } else {\n startBlock(style)\n }\n\n addSpan('---')\n flushBlock()\n break\n }\n\n pushBlock(hrObject)\n\n break\n }\n\n // HTML block\n case 'html_block': {\n flushBlock()\n\n const htmlContent = token.content.trim()\n\n if (!htmlContent) {\n break\n }\n\n const htmlObject = consolidatedOptions.types.html({\n context: {\n schema: consolidatedOptions.schema,\n keyGenerator: consolidatedOptions.keyGenerator,\n },\n value: {html: htmlContent},\n isInline: false,\n })\n\n if (!htmlObject) {\n const snippet = truncateSnippet(htmlContent)\n report({\n type: 'html-block-to-text',\n message: degradationMessage['html-block-to-text'],\n line: lineOf(token),\n snippet,\n })\n\n // If there's no HTML block definition in the schema, parse as text\n const style = consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!style) {\n reportStyleFallback('normal', lineOf(token))\n startBlock('normal')\n } else {\n startBlock(style)\n }\n\n addSpan(htmlContent)\n flushBlock()\n break\n }\n\n reportFieldsDropped(htmlObject, lineOf(token))\n pushBlock(htmlObject)\n\n break\n }\n\n case 'code_block': {\n flushBlock()\n\n // Remove trailing newline from code content\n const code = token.content.replace(/\\n$/, '')\n\n const codeObject = consolidatedOptions.types.code({\n context: {\n schema: consolidatedOptions.schema,\n keyGenerator: consolidatedOptions.keyGenerator,\n },\n value: {language: undefined, code},\n isInline: false,\n })\n\n if (!codeObject) {\n const snippet = truncateSnippet(code.split('\\n')[0] ?? '')\n report({\n type: 'code-block-to-text',\n message: degradationMessage['code-block-to-text'](undefined),\n line: lineOf(token),\n snippet,\n })\n\n // Code block not in schema, fall back to text block\n const style = consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!style) {\n reportStyleFallback('normal', lineOf(token))\n startBlock('normal')\n } else {\n startBlock(style)\n }\n\n addSpan(code)\n flushBlock()\n } else {\n reportFieldsDropped(codeObject, lineOf(token))\n pushBlock(codeObject)\n }\n\n break\n }\n\n // Tables\n case 'table_open':\n flushBlock()\n currentTable = {\n rows: [],\n headerRows: 0,\n emptyHeaderDropped: false,\n alignment: [],\n line: lineOf(token),\n }\n break\n\n case 'table_close': {\n if (!currentTable) {\n break\n }\n\n // Only create table object if table type is defined\n if (consolidatedOptions.types.table) {\n const hasAlignment = currentTable.alignment.some((a) => a !== null)\n const tableObject = consolidatedOptions.types.table({\n context: {\n schema: consolidatedOptions.schema,\n keyGenerator: consolidatedOptions.keyGenerator,\n },\n value: {\n rows: currentTable.rows,\n headerRows:\n currentTable.headerRows > 0\n ? currentTable.headerRows\n : currentTable.emptyHeaderDropped\n ? 0\n : undefined,\n alignment: hasAlignment ? currentTable.alignment : undefined,\n },\n isInline: false,\n })\n\n if (tableObject) {\n reportFieldsDropped(tableObject, currentTable.line)\n pushBlock(tableObject)\n } else {\n report({\n type: 'table-flattened',\n message: degradationMessage['table-flattened'],\n line: currentTable.line,\n })\n // If table object couldn't be created, flatten the table\n flattenTable(\n currentTable,\n blockTarget() as Array<PortableTextBlock>,\n )\n }\n } else {\n report({\n type: 'table-flattened',\n message: degradationMessage['table-flattened'],\n line: currentTable.line,\n })\n // If there's no table definition in the schema, flatten the table\n flattenTable(currentTable, blockTarget() as Array<PortableTextBlock>)\n }\n\n currentTable = null\n break\n }\n\n case 'thead_open':\n inTableHead = true\n break\n\n case 'thead_close':\n inTableHead = false\n break\n\n case 'tbody_open':\n case 'tbody_close':\n // Just markers, no action needed\n break\n\n case 'tr_open':\n currentTableRow = []\n break\n\n case 'tr_close':\n if (currentTable && currentTableRow) {\n if (\n inTableHead &&\n isEmptyTableRow(currentTableRow, {\n schema: consolidatedOptions.schema,\n })\n ) {\n // An all-empty header row means \"no header\": drop it and leave\n // `headerRows` at 0 (recorded so `table_close` emits an explicit\n // 0, not `undefined`), so `portableTextToMarkdown`'s headerless\n // output round-trips back to `headerRows: 0`.\n currentTable.emptyHeaderDropped = true\n } else {\n currentTable.rows.push({\n _key: consolidatedOptions.keyGenerator(),\n _type: 'row',\n cells: currentTableRow,\n })\n if (inTableHead) {\n currentTable.headerRows++\n }\n }\n }\n currentTableRow = null\n break\n\n case 'th_open':\n case 'td_open': {\n // Alignment is per-column, set on every cell of that column. Read\n // from the header so each column contributes exactly one entry.\n if (currentTable && inTableHead && token.type === 'th_open') {\n currentTable.alignment.push(\n extractAlignmentFromStyleAttr(token.attrGet('style')),\n )\n }\n\n // Start a new block for the table cell\n const style = consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!style) {\n reportStyleFallback('normal', currentTable?.line)\n startBlock('normal')\n } else {\n startBlock(style)\n }\n break\n }\n\n case 'th_close':\n case 'td_close': {\n // Flush the current block into the cell\n flushBlock()\n\n // Get all blocks that were added since this cell started\n // We need to extract them from the current target array\n const cellBlocks: Array<PortableTextBlock> = []\n const target = blockTarget()\n\n // Check if we have blocks to extract (added after table_open)\n if (target.length > 0) {\n const lastBlock = target.at(-1)\n if (lastBlock && lastBlock._type === 'block') {\n cellBlocks.push(target.pop()! as PortableTextBlock)\n }\n }\n\n // If no blocks were created (empty cell), create an empty block\n if (cellBlocks.length === 0) {\n cellBlocks.push({\n _type: 'block' as const,\n style:\n consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n }) || 'normal',\n children: [\n {\n _type: consolidatedOptions.schema.span.name,\n _key: consolidatedOptions.keyGenerator(),\n text: '',\n marks: [],\n },\n ],\n _key: consolidatedOptions.keyGenerator(),\n markDefs: [],\n })\n }\n\n // Images pushed as inline children during this cell (see the\n // `inline` case below) were demoted from block-level at push time,\n // before it was known whether the sole-image lift below would\n // recover them losslessly. Collect them so the lift's verdict can\n // decide whether the demotion actually degraded anything.\n const demotedInlineImages: Array<PortableTextObject> = []\n for (const block of cellBlocks) {\n if (\n block._type === 'block' &&\n 'children' in block &&\n Array.isArray(block.children)\n ) {\n for (const child of block.children) {\n if (\n typeof child === 'object' &&\n child !== null &&\n demotedTableImages.has(child as PortableTextObject)\n ) {\n demotedInlineImages.push(child as PortableTextObject)\n }\n }\n }\n }\n\n // Check if the cell contains a single block with a single image child\n // If so, extract the image as a block-level image\n const firstBlock = cellBlocks[0]\n let liftedImage: PortableTextObject | undefined\n if (\n cellBlocks.length === 1 &&\n firstBlock &&\n firstBlock._type === 'block' &&\n 'children' in firstBlock &&\n Array.isArray(firstBlock.children) &&\n firstBlock.children.length === 1\n ) {\n const onlyChild = firstBlock.children[0]\n // Check if it's an image object (not a span)\n if (\n typeof onlyChild === 'object' &&\n onlyChild !== null &&\n '_type' in onlyChild &&\n onlyChild._type !== consolidatedOptions.schema.span.name &&\n onlyChild._type === 'image'\n ) {\n // Replace the block with just the image\n cellBlocks[0] = onlyChild as PortableTextBlock\n liftedImage = onlyChild as PortableTextObject\n }\n }\n\n // A demoted image the lift didn't recover is stuck as an inline\n // child of the cell's text block: report it now that the verdict is\n // known, instead of at demotion time.\n for (const demotedImage of demotedInlineImages) {\n if (demotedImage !== liftedImage) {\n const {alt, src} = demotedImage as {alt?: string; src?: string}\n report({\n type: 'image-block-to-inline',\n message: degradationMessage['image-block-to-inline'](\n demotedTableImages.get(demotedImage) ?? 'table-cell',\n ),\n line: currentTable?.line,\n snippet: truncateSnippet(alt || src || ''),\n })\n }\n }\n\n if (currentTableRow !== null) {\n currentTableRow.push({\n _type: 'cell',\n _key: consolidatedOptions.keyGenerator(),\n value: cellBlocks,\n })\n }\n break\n }\n\n // Inline container\n case 'inline': {\n // Check if we're in a table cell\n const inTableCell = currentTableRow !== null\n\n // `inline` tokens inside table cells carry no `map` of their own;\n // fall back to the enclosing table's line.\n const inlineLine = (): number | undefined =>\n lineOf(token) ?? currentTable?.line\n\n // Check if this is a standalone image (paragraph with only an image)\n if (\n token.children?.length === 1 &&\n token.children[0]?.type === 'image'\n ) {\n const imageToken = token.children[0]\n if (!imageToken) {\n break\n }\n\n const src =\n imageToken.attrs?.find(([name]) => name === 'src')?.at(1) || ''\n const alt = unescapeImageAndLinkText(imageToken.content || '')\n const title =\n imageToken.attrs?.find(([name]) => name === 'title')?.at(1) ||\n undefined\n\n const blockImageObject = consolidatedOptions.types.image({\n context: {\n schema: consolidatedOptions.schema,\n keyGenerator: consolidatedOptions.keyGenerator,\n },\n value: {src, alt, title},\n isInline: false,\n })\n\n if (blockImageObject) {\n reportFieldsDropped(blockImageObject, inlineLine())\n if (inTableCell) {\n demotedTableImages.set(\n blockImageObject as PortableTextObject,\n 'table-cell',\n )\n\n // In table cells, we can't push to portableText directly\n // The block image will be handled in th_close/td_close extraction logic\n // For now, add it as a child of the current block\n if (currentBlock && 'children' in currentBlock) {\n ;(currentBlock as PortableTextTextBlock).children.push(\n blockImageObject as PortableTextObject,\n )\n }\n } else {\n // If the current block has content, flush it before adding the block image\n // Otherwise, discard the empty block that was created by paragraph_open\n const hasContent =\n currentBlock &&\n 'children' in currentBlock &&\n (currentBlock as PortableTextTextBlock).children.length > 0\n\n if (hasContent) {\n flushBlock()\n } else {\n currentBlock = null\n currentMarkDefs = []\n }\n pushBlock(blockImageObject)\n }\n break\n }\n\n // Block image not supported, try inline image as fallback\n const inlineImageObject = consolidatedOptions.types.image({\n context: {\n schema: consolidatedOptions.schema,\n keyGenerator: consolidatedOptions.keyGenerator,\n },\n value: {src, alt, title},\n isInline: true,\n })\n\n if (inlineImageObject) {\n reportFieldsDropped(inlineImageObject, inlineLine())\n if (inTableCell) {\n // Defer to the `td_close` sole-image lift: a standalone image\n // that's the cell's only content round-trips back to\n // block-level losslessly, and reporting here would be a false\n // positive for that case.\n demotedTableImages.set(\n inlineImageObject as PortableTextObject,\n 'no-block-image',\n )\n } else {\n report({\n type: 'image-block-to-inline',\n message:\n degradationMessage['image-block-to-inline']('no-block-image'),\n line: inlineLine(),\n snippet: truncateSnippet(alt || src),\n })\n }\n // Ensure we have a block to add the inline image to\n if (!currentBlock) {\n if (inListItem) {\n // Structural list: start a plain text block; the image\n // lands in the current item's content via flushBlock.\n if (listContainerStack.at(-1)) {\n const style = consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!style) {\n reportStyleFallback('normal', inlineLine())\n }\n\n startBlock(style ?? 'normal')\n } else {\n const listType = currentListStack.at(-1)\n\n if (listType) {\n ensureListBlock(listType)\n }\n }\n } else {\n const style = consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (style) {\n startBlock(style)\n }\n }\n }\n\n if (currentBlock && 'children' in currentBlock) {\n ;(currentBlock as PortableTextTextBlock).children.push(\n inlineImageObject as PortableTextObject,\n )\n }\n break\n }\n\n // Neither block nor inline image supported, fall back to text\n const standaloneImageSnippet = truncateSnippet(alt || src)\n report({\n type: 'image-to-text',\n message: degradationMessage['image-to-text'],\n line: inlineLine(),\n snippet: standaloneImageSnippet,\n })\n addSpan(``)\n break\n }\n\n // Walk its children for text/marks/links\n const inlineChildren = token.children ?? []\n for (\n let childIndex = 0;\n childIndex < inlineChildren.length;\n childIndex++\n ) {\n const childToken = inlineChildren[childIndex]\n if (!childToken) {\n continue\n }\n\n switch (childToken.type) {\n case 'text': {\n const nextToken = inlineChildren[childIndex + 1]\n\n if (\n childToken.content.endsWith('json:object') &&\n nextToken?.type === 'code_inline' &&\n currentBlock &&\n 'children' in currentBlock\n ) {\n const objectValue = parseJsonObjectFence(nextToken.content)\n\n if (objectValue) {\n const prefix = childToken.content.slice(\n 0,\n -'json:object'.length,\n )\n\n if (prefix.length > 0) {\n addSpan(prefix)\n }\n\n ;(currentBlock as PortableTextTextBlock).children.push(\n objectValue as PortableTextObject,\n )\n\n childIndex++\n break\n }\n\n report({\n type: 'object-carrier-invalid',\n message: degradationMessage['object-carrier-invalid'](\n 'code-span',\n nextToken.content,\n ),\n line: inlineLine(),\n snippet: truncateSnippet(nextToken.content),\n })\n }\n\n addSpan(childToken.content)\n break\n }\n case 'softbreak':\n addSpan(' ')\n break\n case 'hardbreak':\n addSpan('\\n')\n break\n case 'code_inline': {\n const decorator = consolidatedOptions.marks.code({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!decorator) {\n const codeSnippet = truncateSnippet(childToken.content)\n report({\n type: 'decorator-dropped',\n message: degradationMessage['decorator-dropped']('code'),\n line: inlineLine(),\n snippet: codeSnippet,\n })\n // No code decorator defined, just add the content without marks\n addSpan(childToken.content)\n break\n }\n\n markDefRefs.push(decorator)\n addSpan(childToken.content)\n\n // code_inline is self-contained, so we need to pop the decorator\n const index = markDefRefs.lastIndexOf(decorator)\n\n if (index !== -1) {\n markDefRefs.splice(index, 1)\n }\n\n break\n }\n case 'strong_open': {\n const decorator = consolidatedOptions.marks.strong({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!decorator) {\n const strongSnippet = truncateSnippet(\n collectInlineText(\n inlineChildren,\n childIndex,\n 'strong_open',\n 'strong_close',\n ),\n )\n report({\n type: 'decorator-dropped',\n message: degradationMessage['decorator-dropped']('strong'),\n line: inlineLine(),\n snippet: strongSnippet,\n })\n break\n }\n\n markDefRefs.push(decorator)\n break\n }\n case 'strong_close': {\n const decorator = consolidatedOptions.marks.strong({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!decorator) {\n break\n }\n\n const index = markDefRefs.lastIndexOf(decorator)\n\n if (index !== -1) {\n markDefRefs.splice(index, 1)\n }\n\n break\n }\n case 'em_open': {\n const decorator = consolidatedOptions.marks.em({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!decorator) {\n const emSnippet = truncateSnippet(\n collectInlineText(\n inlineChildren,\n childIndex,\n 'em_open',\n 'em_close',\n ),\n )\n report({\n type: 'decorator-dropped',\n message: degradationMessage['decorator-dropped']('em'),\n line: inlineLine(),\n snippet: emSnippet,\n })\n break\n }\n\n markDefRefs.push(decorator)\n\n break\n }\n case 'em_close': {\n const decorator = consolidatedOptions.marks.em({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!decorator) {\n break\n }\n\n const index = markDefRefs.lastIndexOf(decorator)\n\n if (index !== -1) {\n markDefRefs.splice(index, 1)\n }\n\n break\n }\n case 's_open': {\n const decorator = consolidatedOptions.marks.strikeThrough({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!decorator) {\n const strikeSnippet = truncateSnippet(\n collectInlineText(\n inlineChildren,\n childIndex,\n 's_open',\n 's_close',\n ),\n )\n report({\n type: 'decorator-dropped',\n message:\n degradationMessage['decorator-dropped']('strikeThrough'),\n line: inlineLine(),\n snippet: strikeSnippet,\n })\n break\n }\n\n markDefRefs.push(decorator)\n\n break\n }\n case 's_close': {\n const decorator = consolidatedOptions.marks.strikeThrough({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!decorator) {\n break\n }\n\n const index = markDefRefs.lastIndexOf(decorator)\n\n if (index !== -1) {\n markDefRefs.splice(index, 1)\n }\n\n break\n }\n case 'link_open': {\n const href = childToken.attrs\n ?.find(([name]) => name === 'href')\n ?.at(1)\n\n if (!href) {\n const missingHrefSnippet = truncateSnippet(\n collectInlineText(\n inlineChildren,\n childIndex,\n 'link_open',\n 'link_close',\n ),\n )\n report({\n type: 'annotation-dropped',\n message:\n degradationMessage['annotation-dropped']('missing-url'),\n line: inlineLine(),\n snippet: missingHrefSnippet,\n })\n break\n }\n\n const title = childToken.attrs\n ?.find(([name]) => name === 'title')\n ?.at(1)\n\n const linkObject = consolidatedOptions.marks.link({\n context: {\n schema: consolidatedOptions.schema,\n keyGenerator: consolidatedOptions.keyGenerator,\n },\n value: {href, title},\n })\n\n if (!linkObject) {\n const linkSnippet = truncateSnippet(\n collectInlineText(\n inlineChildren,\n childIndex,\n 'link_open',\n 'link_close',\n ),\n )\n report({\n type: 'annotation-dropped',\n message:\n degradationMessage['annotation-dropped']('no-annotation'),\n line: inlineLine(),\n snippet: linkSnippet,\n })\n break\n }\n\n reportFieldsDropped(linkObject, inlineLine())\n currentMarkDefs.push(linkObject)\n markDefRefs.push(linkObject._key)\n break\n }\n case 'link_close': {\n // remove the last link key\n const markDefKeys = new Set(currentMarkDefs.map((d) => d._key))\n let lastLinkIndex: number | undefined\n\n for (const markDefRef of markDefRefs.reverse()) {\n if (markDefKeys.has(markDefRef)) {\n lastLinkIndex = markDefRefs.indexOf(markDefRef)\n break\n }\n }\n\n if (lastLinkIndex !== undefined) {\n const realIndex = markDefRefs.length - 1 - lastLinkIndex\n markDefRefs.splice(realIndex, 1)\n }\n break\n }\n case 'image': {\n const src =\n childToken.attrs?.find(([name]) => name === 'src')?.at(1) || ''\n const alt = unescapeImageAndLinkText(childToken.content || '')\n\n // Try to create an inline image first\n const inlineImageObject = consolidatedOptions.types.image({\n context: {\n schema: consolidatedOptions.schema,\n keyGenerator: consolidatedOptions.keyGenerator,\n },\n value: {src, alt, title: undefined},\n isInline: true,\n })\n\n if (inlineImageObject) {\n reportFieldsDropped(inlineImageObject, inlineLine())\n // Inline image is supported - add it to current block\n if (!currentBlock) {\n const style = consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!style) {\n reportStyleFallback('normal', inlineLine())\n startBlock('normal')\n } else {\n startBlock(style)\n }\n }\n\n // At this point currentBlock should exist\n if (!currentBlock) {\n throw new Error('Expected current block after startBlock')\n }\n\n // Add the image as an inline object (TypeScript assertion needed for type narrowing)\n ;(currentBlock as PortableTextTextBlock).children.push(\n inlineImageObject as PortableTextObject,\n )\n break\n }\n\n // Inline image not supported - try block image as fallback\n const blockImageObject = consolidatedOptions.types.image({\n context: {\n schema: consolidatedOptions.schema,\n keyGenerator: consolidatedOptions.keyGenerator,\n },\n value: {src, alt, title: undefined},\n isInline: false,\n })\n\n if (!blockImageObject) {\n // Neither inline nor block image supported\n const inlineImageSnippet = truncateSnippet(alt || src)\n report({\n type: 'image-to-text',\n message: degradationMessage['image-to-text'],\n line: inlineLine(),\n snippet: inlineImageSnippet,\n })\n addSpan(``)\n break\n }\n\n // Block image supported - flush current block and add as block-level\n // Skip if we're in a table cell (images in cells are handled differently)\n if (inTableCell) {\n reportFieldsDropped(blockImageObject, inlineLine())\n demotedTableImages.set(\n blockImageObject as PortableTextObject,\n 'table-cell',\n )\n\n // In table cells, add the image to current block (will be extracted later)\n if (currentBlock && 'children' in currentBlock) {\n ;(currentBlock as PortableTextTextBlock).children.push(\n blockImageObject as PortableTextObject,\n )\n }\n break\n }\n\n // Not in table - flush current block, add image as block, start new block\n reportFieldsDropped(blockImageObject, inlineLine())\n report({\n type: 'image-inline-to-block',\n message: degradationMessage['image-inline-to-block'],\n line: inlineLine(),\n snippet: truncateSnippet(alt || src),\n })\n flushBlock()\n pushBlock(blockImageObject)\n\n // Start a new block for any remaining content\n const style = consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (style) {\n startBlock(style)\n }\n\n break\n }\n case 'html_inline': {\n // Handle inline HTML based on configuration\n if (consolidatedOptions.html.inline === 'text') {\n addSpan(childToken.content)\n } else if (childToken.content) {\n const htmlInlineSnippet = truncateSnippet(childToken.content)\n report({\n type: 'inline-html-dropped',\n message: degradationMessage['inline-html-dropped'],\n line: inlineLine(),\n snippet: htmlInlineSnippet,\n })\n }\n break\n }\n default:\n // Ignore other inline token types by default\n break\n }\n }\n break\n }\n\n // Callouts (GFM alerts)\n case 'alert_open': {\n flushBlock()\n calloutStartTarget = blockTarget()\n calloutStartIndex = calloutStartTarget.length\n calloutType = token.markup\n calloutStartLine = lineOf(token)\n\n // Set blockquote style so content blocks inside the callout\n // get blockquote styling (used in fallback when callout type\n // is not in the schema)\n const blockquoteStyle = consolidatedOptions.block.blockquote({\n context: {schema: consolidatedOptions.schema},\n })\n\n calloutPendingStyleFallbacks = []\n if (!blockquoteStyle) {\n calloutPendingStyleFallbacks.push('blockquote')\n }\n\n const style =\n blockquoteStyle ??\n consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!style) {\n calloutPendingStyleFallbacks.push('normal')\n }\n\n currentBlockquoteStyle = style ?? 'normal'\n break\n }\n\n case 'alert_title': {\n // `alert_open`'s own map starts at the first content line; the\n // marker line (`> [!NOTE]`) is `alert_title`'s. Any pending style\n // fallbacks flush lazily, from `flushBlock`, once a text block\n // actually commits needing the style.\n calloutStartLine = lineOf(token)\n break\n }\n\n case 'alert_close': {\n flushBlock()\n\n // Nothing ever needed the pending style: no text block materialized\n // inside the callout, so nothing degraded.\n calloutPendingStyleFallbacks = []\n\n if (\n calloutStartIndex !== null &&\n calloutType !== null &&\n calloutStartTarget !== null\n ) {\n const contentBlocks = calloutStartTarget.splice(\n calloutStartIndex,\n ) as Array<PortableTextBlock>\n\n const calloutObject = consolidatedOptions.types.callout?.({\n context: {\n schema: consolidatedOptions.schema,\n keyGenerator: consolidatedOptions.keyGenerator,\n },\n value: {tone: calloutType, content: contentBlocks},\n isInline: false,\n })\n\n if (calloutObject) {\n reportFieldsDropped(calloutObject, calloutStartLine)\n pushBlock(calloutObject)\n } else {\n report({\n type: 'callout-fallback',\n message: degradationMessage['callout-fallback'](\n calloutType,\n currentBlockquoteStyle ?? 'normal',\n ),\n line: calloutStartLine,\n })\n for (const block of contentBlocks) {\n pushBlock(block)\n }\n }\n }\n\n calloutStartIndex = null\n calloutStartTarget = null\n calloutType = null\n calloutStartLine = undefined\n currentBlockquoteStyle = null\n break\n }\n\n default:\n break\n }\n }\n\n flushBlock()\n\n if (degradationEvents.length > 0) {\n options?.onDegradation?.({\n degradations: degradationEvents,\n message: buildDegradationMessage(degradationEvents),\n })\n }\n\n return portableText\n}\n\n/**\n * A `json:object` fence always reconstructs its object, schema or no\n * schema: the fence carries its own `_type`, and degrading it to a code\n * block would reintroduce the loss the syntax exists to remove. Returns\n * `undefined` instead of throwing, so an unusable fence falls through to\n * the regular code path.\n */\nfunction parseJsonObjectFence(\n code: string,\n): (Record<string, unknown> & {_type: string}) | undefined {\n let parsed: unknown\n\n try {\n parsed = JSON.parse(code)\n } catch {\n return undefined\n }\n\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n return undefined\n }\n\n const objectValue = parsed as Record<string, unknown>\n\n if (\n typeof objectValue['_type'] !== 'string' ||\n objectValue['_type'].length === 0\n ) {\n return undefined\n }\n\n return objectValue as Record<string, unknown> & {_type: string}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["isTextBlock"],"sources":["../src/key-generator.ts","../src/from-portable-text/build-list-index-map.ts","../src/from-portable-text/escape-plain-text.ts","../src/from-portable-text/list-item-first-block.ts","../src/from-portable-text/render-node.ts","../src/from-portable-text/renderers/block-spacing.ts","../src/from-portable-text/renderers/hard-break.ts","../src/from-portable-text/renderers/list-item.ts","../src/escape.ts","../src/from-portable-text/renderers/marks.ts","../src/from-portable-text/renderers/style.ts","../src/from-portable-text/renderers/type.ts","../src/from-portable-text/portable-text-to-markdown.ts","../src/default-schema.ts","../src/to-portable-text/degradation-messages.ts","../src/to-portable-text/matchers.ts","../src/to-portable-text/markdown-to-portable-text.ts","../src/apply-markdown-edit.ts"],"sourcesContent":["export function defaultKeyGenerator() {\n return randomKey(12)\n}\n\n/**\n * Wraps a caller-supplied key generator so one conversion can never\n * mint the same key twice. Keys attach content to its identity at\n * creation time (a span's `marks` entry points at the mark definition\n * minted with the same key), so a generator that repeats a key does\n * not just violate sibling uniqueness, it makes ownership ambiguous in\n * a way no later pass can repair: two definitions sharing a key leave\n * every referencing span attributable to either. Bounded retries, then\n * deterministic suffixing, mirroring how sibling-uniqueness repair\n * treats a generator that keeps returning claimed keys.\n */\nexport function uniqueKeyGenerator(generator: () => string): () => string {\n const mintedKeys = new Set<string>()\n return () => {\n let candidate = generator()\n for (let attempt = 0; attempt < 3 && mintedKeys.has(candidate); attempt++) {\n candidate = generator()\n }\n if (mintedKeys.has(candidate)) {\n const base = candidate\n let suffix = 2\n while (mintedKeys.has(`${base}-${suffix}`)) {\n suffix++\n }\n candidate = `${base}-${suffix}`\n }\n mintedKeys.add(candidate)\n return candidate\n }\n}\n\nconst getByteHexTable = (() => {\n let table: any[]\n return () => {\n if (table) {\n return table\n }\n\n table = []\n for (let i = 0; i < 256; ++i) {\n table[i] = (i + 0x100).toString(16).slice(1)\n }\n return table\n }\n})()\n\n// WHATWG crypto RNG - https://w3c.github.io/webcrypto/Overview.html\nfunction whatwgRNG(length = 16) {\n const rnds8 = new Uint8Array(length)\n crypto.getRandomValues(rnds8)\n return rnds8\n}\n\nfunction randomKey(length?: number): string {\n const table = getByteHexTable()\n return whatwgRNG(length)\n .reduce((str, n) => str + table[n], '')\n .slice(0, length)\n}\n","import {\n compileSchema,\n defineSchema,\n isTextBlock,\n type PortableTextBlock,\n} from '@portabletext/schema'\nimport type {ArbitraryTypedObject, TypedObject} from '@portabletext/types'\nimport {defaultKeyGenerator} from '../key-generator'\n\nconst schema = compileSchema(defineSchema({}))\n\n/**\n * Builds a map of list item `_key`s to their index, and a map of list item\n * `_key`s to the depth they should be rendered at.\n *\n * The depth is not the same as the block's `level`. A list can start at a level\n * deeper than 1, and can skip levels, but Markdown has no way to express either:\n * indentation is relative to the list item above, and indenting a first item by\n * four spaces or more makes it a code block rather than a list. So each jump to a\n * deeper level counts as a single step of nesting, however many levels it spans.\n *\n * Mutates the blocks in place by adding a `_key` if necessary.\n */\nexport function buildListIndexMap<\n Block extends TypedObject = PortableTextBlock | ArbitraryTypedObject,\n>(\n blocks: Array<Block>,\n): {listIndexMap: Map<string, number>; listDepthMap: Map<string, number>} {\n const levelIndexMaps = new Map<string, Map<number, number>>()\n const listIndexMap = new Map<string, number>()\n const listDepthMap = new Map<string, number>()\n\n // Levels of the list items this one is nested inside, shallowest first\n let levelStack: Array<number> = []\n\n function depthOf(level: number): number {\n let deepest = levelStack.at(-1)\n\n while (deepest !== undefined && deepest > level) {\n levelStack.pop()\n deepest = levelStack.at(-1)\n }\n\n if (deepest !== level) {\n levelStack.push(level)\n }\n\n return levelStack.length - 1\n }\n\n let previousListItem:\n | {\n listItem: string\n depth: number\n }\n | undefined\n\n for (let blockIndex = 0; blockIndex < blocks.length; blockIndex++) {\n const block = blocks.at(blockIndex)\n\n if (block === undefined) {\n continue\n }\n\n if (!block._key) {\n block._key = defaultKeyGenerator()\n }\n\n // Clear the state if we encounter a non-text block\n if (!isTextBlock({schema}, block)) {\n levelIndexMaps.clear()\n previousListItem = undefined\n levelStack = []\n\n continue\n }\n\n // Clear the state if we encounter a non-list text block\n if (block.listItem === undefined || block.level === undefined) {\n levelIndexMaps.clear()\n previousListItem = undefined\n levelStack = []\n\n continue\n }\n\n const depth = depthOf(block.level)\n listDepthMap.set(block._key, depth)\n\n // If we encounter a new list item, we set the initial index to 1 for the\n // list type on that level.\n if (!previousListItem) {\n const listIndex = 1\n const levelIndexMap =\n levelIndexMaps.get(block.listItem) ?? new Map<number, number>()\n levelIndexMap.set(depth, listIndex)\n levelIndexMaps.set(block.listItem, levelIndexMap)\n\n listIndexMap.set(block._key, listIndex)\n\n previousListItem = {\n listItem: block.listItem,\n depth,\n }\n\n continue\n }\n\n // If the previous list item is of the same type but on a lower level, we\n // need to reset the level index map for that type.\n if (\n previousListItem.listItem === block.listItem &&\n previousListItem.depth < depth\n ) {\n const listIndex = 1\n const levelIndexMap =\n levelIndexMaps.get(block.listItem) ?? new Map<number, number>()\n levelIndexMap.set(depth, listIndex)\n levelIndexMaps.set(block.listItem, levelIndexMap)\n\n listIndexMap.set(block._key, listIndex)\n\n previousListItem = {\n listItem: block.listItem,\n depth,\n }\n\n continue\n }\n\n // Reset other list types at current depth and deeper\n levelIndexMaps.forEach((levelIndexMap, listItem) => {\n if (listItem === block.listItem) {\n return\n }\n\n // Reset all levels that are >= current level\n const depthsToDelete: number[] = []\n\n levelIndexMap.forEach((_, existingDepth) => {\n if (existingDepth >= depth) {\n depthsToDelete.push(existingDepth)\n }\n })\n\n depthsToDelete.forEach((depthToDelete) => {\n levelIndexMap.delete(depthToDelete)\n })\n })\n\n const levelIndexMap =\n levelIndexMaps.get(block.listItem) ?? new Map<number, number>()\n const levelCounter = levelIndexMap.get(depth) ?? 0\n levelIndexMap.set(depth, levelCounter + 1)\n levelIndexMaps.set(block.listItem, levelIndexMap)\n\n listIndexMap.set(block._key, levelCounter + 1)\n\n previousListItem = {\n listItem: block.listItem,\n depth,\n }\n }\n\n return {listIndexMap, listDepthMap}\n}\n","import {isPortableTextSpan} from '@portabletext/toolkit'\nimport type {\n ArbitraryTypedObject,\n PortableTextMarkDefinition,\n PortableTextSpan,\n} from '@portabletext/types'\nimport {LinkifyIt} from 'linkify-it'\n\n/**\n * The CommonMark ASCII punctuation set. Only these characters can be\n * backslash-escaped into a literal without changing the parsed text.\n */\nconst ASCII_PUNCTUATION = /[!-/:-@[-`{-~]/\n\nconst ENTITY_REFERENCE = /&(?:[a-zA-Z][a-zA-Z0-9]*|#[0-9]+|#[xX][0-9a-fA-F]+);/g\nconst BACKSLASH_BEFORE_PUNCTUATION = new RegExp(\n `\\\\\\\\(?=${ASCII_PUNCTUATION.source})`,\n 'g',\n)\nconst TILDE_RUN = /~{2,}/g\nconst HTML_LIKE_ANGLE_BRACKET = /<(?=[a-zA-Z/!?])/g\nconst BRACKET_BEFORE_LINK_OPEN = /\\](?=[([])/g\n\n/**\n * markdown-it's own emphasis-flanking rule classifies a delimiter's\n * neighbor as punctuation using Unicode's `P` (Punctuation) and `S`\n * (Symbol) categories, not just ASCII: an emoji, an em dash, or a CJK\n * character flanks a `*`/`_` run exactly like an ASCII punctuation\n * character does, so testing ASCII alone would fabricate emphasis next to\n * non-ASCII text the real reparse wouldn't create. Matched against one\n * full code point at a time (a surrogate pair for an astral character\n * like an emoji), never a lone UTF-16 code unit, which the category\n * classes never match.\n */\nconst UNICODE_PUNCTUATION_OR_SYMBOL = /^(?:\\p{P}|\\p{S})$/u\n\n// Mirrors `md.linkify` in `markdown-to-portable-text.ts`: same default\n// schemas (http/https/ftp/'//'/mailto), the same built-in TLD list, and the\n// same `fuzzyLink`/`urlAuth` overrides, so a range this reports as a link is\n// a range the real reparse will claim too.\nconst linkify = new LinkifyIt({fuzzyLink: true, urlAuth: true})\n\ntype LeafPiece =\n | {kind: 'text'; raw: string; isLinkLabel: boolean; markSignature: string}\n | {kind: 'hardBreak'}\n | {kind: 'opaque'}\n\n/**\n * Stands in for one opaque child (an inline object, or a leaf a custom\n * renderer will replace) in a joined line's text. It can never match a\n * hazard's trigger character, so it safely walls off an in-progress\n * construct (an ordered-list marker, a ref-def label) without needing a\n * dedicated flag, while still counting as real, non-whitespace content for\n * emphasis-flanking purposes.\n */\nconst OPAQUE_CHAR = '\\0'\n\n/** Which leaf (by index into the flat `pieces` list) a line character came\n * from, and at what offset into that leaf's *prepared* text (raw text, with\n * a link-label leaf's brackets/backslashes already doubled). `null` marks\n * an opaque character: it has no leaf-local home, so no edit can target it. */\ninterface LineChar {\n pieceIndex: number\n offset: number\n}\n\n/** A single-position splice against a joined line's raw text: remove\n * `deleteCount` characters at `at` and put `insert` in their place. A pure\n * insertion (a backslash escape) has `deleteCount: 0`. */\ninterface Edit {\n at: number\n deleteCount: number\n insert: string\n /**\n * Set for an entity-reference or backtick escape: both change what\n * markdown-it's inline parser hands to its linkify pass (an entity\n * decodes first, a backtick can open a code span first), so the linkify\n * mask - built from this line's raw, undecoded text - can never be\n * trusted to have already accounted for them. Kept regardless of any\n * linkify claim overlapping it.\n */\n bypassLinkifyMask?: boolean\n}\n\n/**\n * Plans the escaped replacement for every plain-text leaf a block's children\n * will produce, in the exact left-to-right order `renderText` visits them\n * (mirroring `buildMarksTree`'s own `text.split('\\n')` leaf splitting).\n *\n * Escaping runs ahead of rendering, over the flat span sequence: some\n * hazards only exist across a leaf boundary (an ordered-list marker, a\n * ref-def label, an emphasis run) because an annotation or decorator mark\n * that introduces no markup of its own splices its children in seamlessly.\n * The plan works line by line (a block's children joined into text, split\n * at hard breaks) rather than leaf by leaf: each line's leaves are joined\n * into one string first, opaque children (inline objects, or leaves a\n * custom renderer will replace) masked with a sentinel that can't match any\n * hazard, and every hazard - inline and line-start alike - is detected once\n * against that real, complete line, with true left/right context on both\n * sides. Detected hazards become position-tracked edits against the line's\n * raw text, which are then split back into each contributing leaf's own\n * escaped text; only that composition step is leaf-scoped.\n *\n * A joined line's text that markdown-it's own linkify pass (bundled as\n * `linkify-it`) would claim as a bare URL or email is masked from most\n * edits: the linkify carve-out promises that substring round-trips\n * byte-identical, gaining only a link mark, so escaping inside it would\n * corrupt text linkify is about to claim as a link's visible text. An\n * entity-reference or backtick escape is never masked (see `computeLinkifyMask`\n * for why), and a claim spliced across a decorator boundary is never masked\n * in the first place.\n *\n * `isHeading` is set for ATX headings: only the first joined line sits\n * inside the `# ` prefix an ATX heading can never be reparsed as a block\n * construct within, so line-leading hazards are skipped there; a hard\n * break's later lines are ordinary markdown lines and get the full\n * line-start battery. That first line carries a line-*end* hazard of its\n * own instead: a trailing `#`-run reads back as the heading's own optional\n * closing sequence.\n *\n * `isListItem` is set when the block renders as list-item content: a\n * `[ ] `/`[x] `/`[X] ` at the very start of the first joined line reads\n * back as a GFM task-list checkbox, regardless of the list's own item type.\n *\n * `hardBreakOutputHasNewline` says whether the renderer's actual hard-break\n * output contains a newline. A custom `hardBreak` can render to something\n * with no newline of its own (eg `() => '<br />'`), in which case the\n * leaves on either side of it land on the same rendered line, not two: a\n * hard break like that can't be planned as a line boundary, so it's walled\n * off as an opaque segment instead, the same protection an inline object's\n * unknown rendered text already gets.\n */\nexport function planLeafEscaping(\n children: ReadonlyArray<PortableTextSpan | ArbitraryTypedObject>,\n markDefs: ReadonlyArray<PortableTextMarkDefinition>,\n options: {\n isHeading: boolean\n isListItem: boolean\n hardBreakOutputHasNewline: boolean\n },\n): Array<string> {\n const linkMarkKeys = new Set(\n markDefs.filter((def) => def._type === 'link').map((def) => def._key),\n )\n // Every markDef key a span's marks can reference (any annotation, not\n // just link): what's left after removing those from a span's `marks` is\n // its decorator set, the only marks `buildMarksTree` reliably renders as\n // delimiters by default (an unregistered annotation type falls back to\n // passing its children through unchanged, same as an unknown decorator).\n const markDefKeys = new Set(markDefs.map((def) => def._key))\n const pieces: Array<LeafPiece> = []\n\n for (const child of children) {\n if (isPortableTextSpan(child)) {\n const isLinkLabel = (child.marks ?? []).some((mark) =>\n linkMarkKeys.has(mark),\n )\n // Sorted so two spans carrying the same decorators in a different\n // order still compare equal: `buildMarksTree` nests by decorator\n // identity, not by a span's own array order, so it produces the same\n // markup boundaries either way.\n const markSignature = (child.marks ?? [])\n .filter((mark) => !markDefKeys.has(mark))\n .sort()\n .join(',')\n const lines = child.text.split('\\n')\n lines.forEach((line, index) => {\n if (index > 0) {\n pieces.push(\n options.hardBreakOutputHasNewline\n ? {kind: 'hardBreak'}\n : {kind: 'opaque'},\n )\n }\n pieces.push({kind: 'text', raw: line, isLinkLabel, markSignature})\n })\n } else {\n pieces.push({kind: 'opaque'})\n }\n }\n\n const pieceOutputs: Array<string> = pieces.map(() => '')\n\n let lineIndex = 0\n let lineText = ''\n let lineChars: Array<LineChar | null> = []\n let lineIsLinkLabelChar: Array<boolean> = []\n let lineMarkSignature: Array<string> = []\n\n const flushLine = () => {\n processLine({\n text: lineText,\n chars: lineChars,\n isLinkLabelChar: lineIsLinkLabelChar,\n markSignature: lineMarkSignature,\n lineIndex,\n isHeading: options.isHeading,\n isListItem: options.isListItem,\n pieceOutputs,\n })\n lineIndex++\n lineText = ''\n lineChars = []\n lineIsLinkLabelChar = []\n lineMarkSignature = []\n }\n\n for (let pieceIndex = 0; pieceIndex < pieces.length; pieceIndex++) {\n const piece = pieces[pieceIndex]\n\n if (!piece || piece.kind === 'hardBreak') {\n flushLine()\n continue\n }\n\n if (piece.kind === 'opaque') {\n lineText += OPAQUE_CHAR\n lineChars.push(null)\n lineIsLinkLabelChar.push(false)\n lineMarkSignature.push('')\n continue\n }\n\n // A link label's brackets and backslashes are escaped unconditionally,\n // up front: a label must stay bracket-balanced regardless of context,\n // so this doesn't depend on anything the line-level hazard scan below\n // discovers.\n const prepared = piece.isLinkLabel\n ? escapeLinkLabelBrackets(piece.raw)\n : piece.raw\n\n for (let offset = 0; offset < prepared.length; offset++) {\n lineText += prepared[offset]\n lineChars.push({pieceIndex, offset})\n lineIsLinkLabelChar.push(piece.isLinkLabel)\n lineMarkSignature.push(piece.markSignature)\n }\n }\n flushLine()\n\n const escaped: Array<string> = []\n pieces.forEach((piece, index) => {\n if (piece.kind === 'text') {\n escaped.push(pieceOutputs[index] ?? '')\n }\n })\n return escaped\n}\n\nfunction processLine(args: {\n text: string\n chars: Array<LineChar | null>\n isLinkLabelChar: Array<boolean>\n markSignature: Array<string>\n lineIndex: number\n isHeading: boolean\n isListItem: boolean\n pieceOutputs: Array<string>\n}): void {\n const {text, chars, isLinkLabelChar, markSignature, pieceOutputs} = args\n\n const linkifyMask = computeLinkifyMask(\n text,\n chars,\n isLinkLabelChar,\n markSignature,\n )\n const edits = [\n ...collectInlineEdits(text, isLinkLabelChar),\n ...collectLineStartEdits(text, args),\n ].filter((edit) => edit.bypassLinkifyMask || !isMasked(edit, linkifyMask))\n\n applyEdits(text, chars, edits, pieceOutputs)\n}\n\n/**\n * Marks every character of this line that markdown-it's linkify pass would\n * claim as part of a bare URL or email. Link-label and opaque characters\n * are blanked out first: a link label's visible text sits inside `[...]`\n * markup real linkify never reconsiders, and an opaque child's rendered\n * text is unknown at plan time, so neither should join or seed a match.\n *\n * The probe only sees this line's raw, undecoded text, one hazard pass\n * ahead of markdown-it's own pipeline: it runs linkify against inline\n * tokenization and entity decoding, not before them. A claim survives only\n * if it lies entirely inside one run of identical decorator marks: a\n * decorator boundary crossing it splices that decorator's delimiters\n * (`**`, `` ` ``, ...) into the middle of the range real linkify would see,\n * which breaks the very claim being trusted. An annotation-only boundary\n * (a link's own label text is already excluded above; any other\n * annotation type falls back to rendering with no delimiters at all,\n * same as an unregistered decorator) never splices, so it can't invalidate\n * a claim either.\n */\nfunction computeLinkifyMask(\n text: string,\n chars: Array<LineChar | null>,\n isLinkLabelChar: Array<boolean>,\n markSignature: Array<string>,\n): Array<boolean> {\n const mask = new Array<boolean>(text.length).fill(false)\n\n // Every schema `linkify-it`'s default config recognizes - `http(s):`,\n // `ftp:`, `//`, `www.`, and a `user@host` email - needs a `.`, `:`, or\n // `@` somewhere in the line; skipping the match call on a line with\n // none of those is the cheap majority-case exit, not a heuristic that\n // could miss a real claim.\n if (!/[.:@]/.test(text)) {\n return mask\n }\n\n let probe = ''\n for (let index = 0; index < text.length; index++) {\n probe += chars[index] === null || isLinkLabelChar[index] ? ' ' : text[index]\n }\n\n const matches = linkify.match(probe) ?? []\n for (const match of matches) {\n if (match.schema === '') {\n // A fuzzy claim (`www.`, bare domain): markdown-it's emphasis pass\n // beats these on reparse, so paired `*`/`_`/`~~` inside one is\n // consumed as markup and the characters vanish. Only explicit-scheme\n // claims (`http:`, `mailto:`, ...) win whole against inline\n // constructs; fuzzy forms take normal escaping instead, trading\n // their link mark for text fidelity.\n continue\n }\n const signature = markSignature[match.index]\n let staysWithinOneMarkRun = true\n for (let index = match.index; index < match.lastIndex; index++) {\n if (markSignature[index] !== signature) {\n staysWithinOneMarkRun = false\n break\n }\n }\n if (!staysWithinOneMarkRun) {\n continue\n }\n for (let index = match.index; index < match.lastIndex; index++) {\n mask[index] = true\n }\n }\n return mask\n}\n\nfunction isMasked(edit: Edit, mask: ReadonlyArray<boolean>): boolean {\n const end = edit.at + Math.max(edit.deleteCount, 1)\n for (let index = edit.at; index < end; index++) {\n if (mask[index]) {\n return true\n }\n }\n return false\n}\n\n/** Rewrites a line's raw text into each contributing leaf's escaped text by\n * walking it once, left to right, applying at most one edit per position.\n * Every hazard is keyed off its own trigger character - a backslash, a\n * tilde, a backtick, an `&`, a `<`, a `*`/`_`, a `]`, or (line-start only,\n * one hazard per line) a `#`, `>`, `[`, `-`/`+`/`*`, the `.`/`)` after an\n * ordered-list marker's digits, `=`, 4 spaces, or a tab - and no two of\n * those characters coincide at one position, so two edits can never target\n * the same position. */\nfunction applyEdits(\n text: string,\n chars: ReadonlyArray<LineChar | null>,\n edits: ReadonlyArray<Edit>,\n pieceOutputs: Array<string>,\n): void {\n const editsByPosition = new Map<number, Edit>()\n for (const edit of edits) {\n if (editsByPosition.has(edit.at)) {\n throw new Error(\n `Two hazard edits targeted the same position (${edit.at}); ` +\n 'hazard trigger characters are assumed disjoint by construction.',\n )\n }\n editsByPosition.set(edit.at, edit)\n }\n\n let index = 0\n while (index < text.length) {\n const edit = editsByPosition.get(index)\n const owner = chars[index]\n\n if (edit) {\n if (owner) {\n pieceOutputs[owner.pieceIndex] =\n (pieceOutputs[owner.pieceIndex] ?? '') + edit.insert\n }\n if (edit.deleteCount > 0) {\n index += edit.deleteCount\n continue\n }\n }\n\n if (owner) {\n pieceOutputs[owner.pieceIndex] =\n (pieceOutputs[owner.pieceIndex] ?? '') + (text[index] ?? '')\n }\n index++\n }\n}\n\n/**\n * Hazards that can appear anywhere on a line: emphasis/strikethrough runs,\n * a backtick, an entity reference, an HTML/autolink-shaped `<`, a literal\n * backslash before punctuation, and a `]` immediately before `(`/`[`\n * (which would otherwise read back as a link/image open).\n */\nfunction collectInlineEdits(\n text: string,\n isLinkLabelChar: ReadonlyArray<boolean>,\n): Array<Edit> {\n const edits: Array<Edit> = []\n\n for (const match of text.matchAll(BACKSLASH_BEFORE_PUNCTUATION)) {\n const at = match.index ?? 0\n // A link label's backslashes were already doubled unconditionally\n // while preparing its text; doubling them again here would flip their\n // parity back to unescaped.\n if (!isLinkLabelChar[at]) {\n edits.push({at, deleteCount: 0, insert: '\\\\'})\n }\n }\n\n for (const match of text.matchAll(TILDE_RUN)) {\n const start = match.index ?? 0\n for (let index = start; index < start + match[0].length; index++) {\n edits.push({at: index, deleteCount: 0, insert: '\\\\'})\n }\n }\n\n for (let index = 0; index < text.length; index++) {\n if (text[index] === '`') {\n // Every backtick is escaped outright: even a lone one can pair with\n // another lone backtick elsewhere to open a code span, which the\n // linkify mask can't see coming - a code span forms during inline\n // tokenization, before linkify ever runs - so this bypasses it.\n edits.push({\n at: index,\n deleteCount: 0,\n insert: '\\\\',\n bypassLinkifyMask: true,\n })\n }\n }\n\n for (const match of text.matchAll(ENTITY_REFERENCE)) {\n // An entity reference decodes before linkify runs, so a masked range\n // built from this line's raw text can't already account for it.\n edits.push({\n at: match.index ?? 0,\n deleteCount: 0,\n insert: '\\\\',\n bypassLinkifyMask: true,\n })\n }\n\n for (const match of text.matchAll(HTML_LIKE_ANGLE_BRACKET)) {\n edits.push({at: match.index ?? 0, deleteCount: 0, insert: '\\\\'})\n }\n\n edits.push(...collectEmphasisEdits(text))\n\n for (const match of text.matchAll(BRACKET_BEFORE_LINK_OPEN)) {\n const at = match.index ?? 0\n // A link label's `]` was already escaped unconditionally while\n // preparing its text (`escapeLinkLabelBrackets`); escaping it again\n // here would double the backslash and reopen the label early on\n // reparse, same reasoning as the backslash rule above.\n if (!isLinkLabelChar[at]) {\n edits.push({at, deleteCount: 0, insert: '\\\\'})\n }\n }\n\n return edits\n}\n\nfunction isWhitespace(char: string | undefined): boolean {\n return char === undefined || /\\s/.test(char)\n}\n\nfunction isPunctuation(char: string | undefined): boolean {\n return char !== undefined && UNICODE_PUNCTUATION_OR_SYMBOL.test(char)\n}\n\n/**\n * The full code point sitting immediately before `index`: two UTF-16 code\n * units for an astral character (eg an emoji) whose low surrogate lands at\n * `index - 1`, one otherwise.\n */\nfunction codePointBefore(text: string, index: number): string | undefined {\n if (index <= 0) {\n return undefined\n }\n if (\n index >= 2 &&\n isLowSurrogate(text[index - 1]) &&\n isHighSurrogate(text[index - 2])\n ) {\n return text.slice(index - 2, index)\n }\n return text[index - 1]\n}\n\n/**\n * The full code point sitting immediately at `index`: two UTF-16 code units\n * for an astral character whose high surrogate lands at `index`, one\n * otherwise.\n */\nfunction codePointAt(text: string, index: number): string | undefined {\n if (index >= text.length) {\n return undefined\n }\n if (isHighSurrogate(text[index]) && isLowSurrogate(text[index + 1])) {\n return text.slice(index, index + 2)\n }\n return text[index]\n}\n\nfunction isHighSurrogate(char: string | undefined): boolean {\n if (char === undefined) {\n return false\n }\n const code = char.charCodeAt(0)\n return code >= 0xd800 && code <= 0xdbff\n}\n\nfunction isLowSurrogate(char: string | undefined): boolean {\n if (char === undefined) {\n return false\n }\n const code = char.charCodeAt(0)\n return code >= 0xdc00 && code <= 0xdfff\n}\n\nfunction isLeftFlanking(\n before: string | undefined,\n after: string | undefined,\n): boolean {\n if (isWhitespace(after)) {\n return false\n }\n return !isPunctuation(after) || isWhitespace(before) || isPunctuation(before)\n}\n\nfunction isRightFlanking(\n before: string | undefined,\n after: string | undefined,\n): boolean {\n if (isWhitespace(before)) {\n return false\n }\n return !isPunctuation(before) || isWhitespace(after) || isPunctuation(after)\n}\n\n/**\n * Finds `*`/`_` runs CommonMark would treat as flanking delimiters, using\n * each run's true neighbors on the joined line (the start/end of the line\n * itself counts as whitespace, matching the spec's treatment of line\n * boundaries).\n */\nfunction collectEmphasisEdits(text: string): Array<Edit> {\n const edits: Array<Edit> = []\n let index = 0\n\n while (index < text.length) {\n const char = text[index]\n\n if (char !== '*' && char !== '_') {\n index++\n continue\n }\n\n let end = index\n while (end < text.length && text[end] === char) {\n end++\n }\n\n const before = codePointBefore(text, index)\n const after = codePointAt(text, end)\n const leftFlanking = isLeftFlanking(before, after)\n const rightFlanking = isRightFlanking(before, after)\n\n const canOpen =\n char === '_'\n ? leftFlanking && (!rightFlanking || isPunctuation(before))\n : leftFlanking\n const canClose =\n char === '_'\n ? rightFlanking && (!leftFlanking || isPunctuation(after))\n : rightFlanking\n\n if (canOpen || canClose) {\n for (let position = index; position < end; position++) {\n edits.push({at: position, deleteCount: 0, insert: '\\\\'})\n }\n }\n\n index = end\n }\n\n return edits\n}\n\n/**\n * Hazards that only matter at the start (or, for a handful of whole-line\n * constructs, the start *and* end) of a line: headings, blockquotes, list\n * markers, ref-defs, setext underlines, thematic breaks, indented code, and\n * a list item's own GFM task-checkbox prefix. A fence needs no branch of\n * its own here: the inline backtick/tilde escaping every line already\n * neutralizes the run a fence needs, so it can never open one on reparse.\n * The remaining branches are mutually exclusive by construction\n * (each targets a disjoint leading character) and return as soon as one\n * matches, mirroring how CommonMark itself commits to one block-start\n * interpretation per line; the checkbox branch above is the one exception,\n * since a list item's checkbox prefix and, say, its heading marker are two\n * independent hazards that can both apply to the same first line.\n */\nfunction collectLineStartEdits(\n text: string,\n context: {isHeading: boolean; isListItem: boolean; lineIndex: number},\n): Array<Edit> {\n const edits: Array<Edit> = []\n const isFirstLine = context.lineIndex === 0\n\n // CommonMark allows up to 3 leading spaces before a block marker without\n // affecting how it's parsed, so every hazard below (including the GFM\n // task-checkbox the parser's own pre-pass looks for, after its own\n // leading-whitespace trim) checks what follows them; the escape itself\n // still has to land right before the marker, not at the front of those\n // spaces (a backslash-space isn't an escape).\n const leadingSpaces = /^ {0,3}/.exec(text)?.[0].length ?? 0\n const rest = text.slice(leadingSpaces)\n\n if (context.isListItem && isFirstLine && /^\\[[ xX]\\] /.test(rest)) {\n edits.push({at: leadingSpaces, deleteCount: 0, insert: '\\\\'})\n }\n\n if (context.isHeading && isFirstLine) {\n // A closing sequence must be preceded by a space, unless it's *all*\n // the heading has: then the space ATX headings require after their\n // opening `#`s stands in for it.\n const closingSequence = /^(?:(.*[ \\t]))?(#+[ \\t]*)$/.exec(text)\n if (closingSequence) {\n edits.push({\n at: closingSequence[1]?.length ?? 0,\n deleteCount: 0,\n insert: '\\\\',\n })\n }\n return edits\n }\n\n const orderedListMarker = /^ {0,3}(\\d{1,9})([.)])(?=[ \\t]|$)/.exec(text)\n if (orderedListMarker) {\n edits.push({\n at: orderedListMarker[0].length - 1,\n deleteCount: 0,\n insert: '\\\\',\n })\n return edits\n }\n\n if (/^#{1,6}(?:[ \\t]|$)/.test(rest)) {\n edits.push({at: leadingSpaces, deleteCount: 0, insert: '\\\\'})\n return edits\n }\n\n if (rest.startsWith('>')) {\n edits.push({at: leadingSpaces, deleteCount: 0, insert: '\\\\'})\n return edits\n }\n\n if (/^\\[[^\\]\\n]*\\]:/.test(rest)) {\n edits.push({at: leadingSpaces, deleteCount: 0, insert: '\\\\'})\n return edits\n }\n\n if (/^[-+*](?:[ \\t]|$)/.test(rest)) {\n edits.push({at: leadingSpaces, deleteCount: 0, insert: '\\\\'})\n return edits\n }\n\n // CommonMark 4.1 allows interior spaces/tabs between a thematic break's\n // delimiter characters.\n if (/^ {0,3}([-*_])(?:[ \\t]*\\1){2,}[ \\t]*$/.test(text)) {\n edits.push({at: leadingSpaces, deleteCount: 0, insert: '\\\\'})\n return edits\n }\n\n if (/^ {0,3}=+[ \\t]*$/.test(text)) {\n edits.push({at: leadingSpaces, deleteCount: 0, insert: '\\\\'})\n return edits\n }\n\n if (/^ {0,3}-+[ \\t]*$/.test(text)) {\n edits.push({at: leadingSpaces, deleteCount: 0, insert: '\\\\'})\n return edits\n }\n\n if (/^ {4}/.test(text)) {\n // A numeric character reference decodes to the same literal character\n // during inline parsing, after block structure (and its indented-code\n // -block rule, 4 columns of leading whitespace) has already been\n // decided.\n edits.push({at: 0, deleteCount: 1, insert: ' '})\n return edits\n }\n\n const tabIndent = /^ {0,3}\\t/.exec(text)\n if (tabIndent) {\n // A tab advances to the next multiple of 4 columns, so even 0-3\n // leading spaces before one reaches the indented-code-block\n // threshold; encoding the tab itself (not the spaces before it) is\n // enough to break that count.\n edits.push({at: tabIndent[0].length - 1, deleteCount: 1, insert: '	'})\n return edits\n }\n\n return edits\n}\n\n/**\n * Text rendered inside a link label needs every `[`, `]` and `\\` escaped\n * unconditionally, on top of the general-purpose hazard escaping every\n * line gets: a link label must stay bracket-balanced, and any literal\n * backslash in it needs protecting regardless of what follows (unlike\n * plain text, where only a backslash immediately before punctuation is a\n * hazard).\n */\nfunction escapeLinkLabelBrackets(text: string): string {\n return text.replace(/[[\\]\\\\]/g, (char) => `\\\\${char}`)\n}\n","import type {PortableTextBlock} from '@portabletext/types'\n\n/**\n * Blocks currently known to be a list item's first content block: it shares\n * its first line with the list marker (and, for a task item, its GFM\n * checkbox), which changes how `renderBlock` plans line-start hazard\n * escaping. Internal to this package so the signal never reaches the\n * public `Serializable`/`RenderNode` types a custom renderer's `.d.ts`\n * would otherwise expose it through.\n *\n * A block is marked right before rendering it; the `renderNode` call that\n * dispatches to `renderBlock` consumes the membership on the way past so a\n * later, unrelated render of the same object (still possible - `renderNode`\n * accepts any `TypedObject`) doesn't inherit a stale claim.\n */\nconst listItemFirstBlocks = new WeakSet<PortableTextBlock>()\n\nexport function markListItemFirstBlock(block: PortableTextBlock): void {\n listItemFirstBlocks.add(block)\n}\n\nexport function consumeListItemFirstBlock(block: PortableTextBlock): boolean {\n const isListItemFirstBlock = listItemFirstBlocks.has(block)\n listItemFirstBlocks.delete(block)\n return isListItemFirstBlock\n}\n","import {\n buildMarksTree,\n isPortableTextBlock,\n isPortableTextListItemBlock,\n isPortableTextToolkitSpan,\n isPortableTextToolkitTextNode,\n spanToPlainText,\n type ToolkitNestedPortableTextSpan,\n type ToolkitTextNode,\n} from '@portabletext/toolkit'\nimport type {\n PortableTextBlock,\n PortableTextListItemBlock,\n PortableTextMarkDefinition,\n PortableTextSpan,\n TypedObject,\n} from '@portabletext/types'\nimport {planLeafEscaping} from './escape-plain-text'\nimport {\n consumeListItemFirstBlock,\n markListItemFirstBlock,\n} from './list-item-first-block'\nimport type {PortableTextRenderers, RenderNode, Serializable} from './types'\n\n/**\n * ATX headings are single-line, inline-only leaf blocks: an ATX heading's\n * first line sits inside its `# ` prefix and can never be reparsed as a\n * block construct, so line-leading hazards never apply there. A hard\n * break's later lines are ordinary markdown lines outside that prefix and\n * get the full line-start battery, same as any other block's continuation.\n */\nconst HEADING_STYLES = new Set(['h1', 'h2', 'h3', 'h4', 'h5', 'h6'])\n\nexport const createRenderNode = (\n renderers: PortableTextRenderers,\n listIndexMap: Map<string, number>,\n listDepthMap: Map<string, number>,\n): RenderNode => {\n // Keyed by the actual `@text` node objects `buildMarksTree` produces, not\n // by render order: a custom type/mark renderer can call `renderNode` with\n // a synthetic node of its own mid-block (eg to render a placeholder), and\n // that call must not shift which escaped string a later real leaf gets.\n // A synthetic node was never planned, so it's absent from the map and\n // renders its own raw text.\n const escapedTextByNode = new WeakMap<ToolkitTextNode, string>()\n\n // Computed once per document render: a custom `hardBreak` can render to\n // something with no newline of its own (eg `() => '<br />'`), in which\n // case a `\\n` inside a span's text is not a real line boundary in the\n // rendered output, and `planLeafEscaping` needs to know that up front.\n const hardBreakOutputHasNewline = renderers.hardBreak().includes('\\n')\n\n // Shared by `renderBlock` and `renderListItem`, whatever the block's\n // style: both flatten `node.children` into the same text/mark tree and\n // need it escaped and keyed before rendering it.\n function renderBlockChildren(\n node: PortableTextBlock | PortableTextListItemBlock,\n isHeading: boolean,\n isListItem = false,\n ): string {\n const chunks = planLeafEscaping(node.children ?? [], node.markDefs ?? [], {\n isHeading,\n isListItem,\n hardBreakOutputHasNewline,\n })\n const tree = buildMarksTree(node)\n assignEscapedText(tree, chunks)\n\n return tree\n .map((child, i) =>\n renderNode({node: child, isInline: true, index: i, renderNode}),\n )\n .join('')\n }\n\n // Walks the tree in the same left-to-right, opaque-object-skipping order\n // `planLeafEscaping` used to produce `chunks`, so each `@text` leaf gets\n // keyed to the chunk planned for it.\n function assignEscapedText(\n nodes: ReadonlyArray<\n ToolkitNestedPortableTextSpan | ToolkitTextNode | TypedObject\n >,\n chunks: ReadonlyArray<string>,\n ): void {\n let pointer = 0\n\n const visit = (\n node: ToolkitNestedPortableTextSpan | ToolkitTextNode | TypedObject,\n ) => {\n if (isPortableTextToolkitTextNode(node)) {\n if (node.text !== '\\n') {\n const escaped = chunks[pointer]\n if (escaped !== undefined) {\n escapedTextByNode.set(node, escaped)\n }\n pointer++\n }\n return\n }\n\n if (isPortableTextToolkitSpan(node)) {\n node.children.forEach(visit)\n }\n }\n\n nodes.forEach(visit)\n }\n\n function renderNode<N extends TypedObject>(options: Serializable<N>): string {\n const {node, index, isInline} = options\n\n if (isPortableTextListItemBlock(node)) {\n return renderListItem(node, index)\n }\n\n if (isPortableTextToolkitSpan(node)) {\n return renderSpan(node)\n }\n\n if (isPortableTextBlock(node)) {\n return renderBlock(node, index, isInline, consumeListItemFirstBlock(node))\n }\n\n if (isPortableTextToolkitTextNode(node)) {\n return renderText(node)\n }\n\n return renderCustomBlock(node, index, isInline)\n }\n\n function renderListItem(\n node: PortableTextListItemBlock<\n PortableTextMarkDefinition,\n PortableTextSpan\n >,\n index: number,\n ): string {\n const renderer = renderers.listItem\n const handler =\n typeof renderer === 'function' ? renderer : renderer[node.listItem]\n const itemHandler = handler || renderers.unknownListItem\n\n let children: string\n\n if (node.style && node.style !== 'normal') {\n // Wrap any other style in whatever the block component says to use.\n // `renderNode` would recurse straight back into `renderListItem` if\n // `blockNode` still carried `listItem`, so it's stripped from the\n // copy; `markListItemFirstBlock` restores the list-item context\n // (line-start hazard escaping, the GFM checkbox prefix) onto that\n // same copy so `renderBlock` picks it up via `consumeListItemFirstBlock`.\n const {listItem: _listItem, ...blockNode} = node\n markListItemFirstBlock(blockNode)\n children = renderNode({\n node: blockNode,\n index,\n isInline: false,\n renderNode,\n })\n // Strip trailing newlines from block styles - list item component handles spacing\n children = children.replace(/\\n+$/, '')\n } else {\n children = renderBlockChildren(node, false, true)\n }\n\n return itemHandler({\n value: node,\n index,\n listIndex: node._key ? listIndexMap.get(node._key) : undefined,\n listDepth: node._key ? listDepthMap.get(node._key) : undefined,\n isInline: false,\n renderNode,\n children,\n })\n }\n\n function renderSpan(node: ToolkitNestedPortableTextSpan): string {\n const {markDef, markType, markKey} = node\n const span = renderers.marks[markType] || renderers.unknownMark\n const children = node.children.map((child, childIndex) =>\n renderNode({node: child, index: childIndex, isInline: true, renderNode}),\n )\n\n return span({\n text: spanToPlainText(node),\n value: markDef,\n markType,\n markKey,\n renderNode,\n children: children.join(''),\n })\n }\n\n function renderBlock(\n node: PortableTextBlock,\n index: number,\n isInline: boolean,\n isListItem: boolean,\n ): string {\n const style = node.style || 'normal'\n const children = renderBlockChildren(\n node,\n HEADING_STYLES.has(style),\n isListItem,\n )\n const handler =\n typeof renderers.block === 'function'\n ? renderers.block\n : renderers.block[style]\n const block = handler || renderers.unknownBlockStyle\n\n return block({index, isInline, children, value: node, renderNode})\n }\n\n function renderText(node: ToolkitTextNode): string {\n if (node.text === '\\n') {\n return renderers.hardBreak()\n }\n\n return escapedTextByNode.get(node) ?? node.text\n }\n\n function renderCustomBlock(\n value: TypedObject,\n index: number,\n isInline: boolean,\n ): string {\n const component = renderers.types[value._type] ?? renderers.unknownType\n\n return component({\n value,\n isInline,\n index,\n renderNode,\n })\n }\n\n return renderNode\n}\n","import {\n isPortableTextBlock,\n isPortableTextListItemBlock,\n} from '@portabletext/toolkit'\nimport type {TypedObject} from '@portabletext/types'\n\n/**\n * @public\n */\nexport type BlockSpacingRenderer = (options: {\n current: TypedObject\n next: TypedObject\n}) => string | undefined\n\n/**\n * @public\n */\nexport const DefaultBlockSpacingRenderer: BlockSpacingRenderer = ({\n current,\n next,\n}) => {\n if (\n isPortableTextListItemBlock(current) &&\n isPortableTextListItemBlock(next)\n ) {\n return '\\n'\n }\n\n if (\n isPortableTextBlock(current) &&\n isPortableTextBlock(next) &&\n current.style === 'blockquote' &&\n next.style === 'blockquote'\n ) {\n return '\\n>\\n'\n }\n\n return '\\n\\n'\n}\n","/**\n * @public\n */\nexport const DefaultHardBreakRenderer = (): string => ' \\n'\n","import type {PortableTextListItemRenderer} from '../types'\n\n/**\n * @public\n */\nexport const DefaultListItemRenderer: PortableTextListItemRenderer = ({\n children,\n value,\n listIndex,\n listDepth,\n}) => {\n const listStyle = value.listItem || 'bullet'\n const depth = listDepth ?? (value.level || 1) - 1\n const indent = ' '.repeat(depth)\n\n if (listStyle === 'number') {\n return `${indent}${listIndex ?? 1}. ${children}`\n }\n\n if (listStyle === 'task') {\n const checked =\n 'checked' in value && typeof value.checked === 'boolean'\n ? value.checked\n : false\n const marker = checked ? '[x]' : '[ ]'\n return `${indent}- ${marker} ${children}`\n }\n\n return `${indent}- ${children}`\n}\n\n/**\n * @public\n */\nexport const DefaultUnknownListItemRenderer: PortableTextListItemRenderer = ({\n children,\n}) => {\n return `- ${children}\\n`\n}\n","/**\n * Escapes special characters in image alt texts and link texts.\n */\nexport function escapeImageAndLinkText(text: string): string {\n return text.replace(/([[\\]\\\\])/g, '\\\\$1')\n}\n\n/**\n * Unescapes special characters in image alt texts and link texts.\n */\nexport function unescapeImageAndLinkText(text: string): string {\n return text.replace(/\\\\([!\"#$%&'()*+,\\-./:;<=>?@[\\\\\\]^_`{|}~])/g, '$1')\n}\n\n/**\n * Escapes special characters in image/link titles (the part inside quotes).\n */\nexport function escapeImageAndLinkTitle(text: string): string {\n return text.replace(/([\\\\\"])/g, '\\\\$1')\n}\n\n/**\n * Escapes characters that have special meaning at the row level of a GFM\n * table cell.\n *\n * A literal `|` ends the cell, so a pipe preceded by an even number of\n * backslashes (including zero) gets one more: paired backslashes cancel\n * out to a literal backslash and leave the pipe live, so parity, not mere\n * presence, decides whether it is already escaped. Newlines end the row,\n * so they are replaced with `<br>` to keep the visible line break inside\n * the cell.\n *\n * Backslashes themselves are left alone here; only the parity check reads\n * them, so escapes already in the rendered cell (such as `\\[` and `\\]` in\n * link text) survive the pass untouched.\n */\nexport function escapeTableCell(text: string): string {\n return text\n .replace(/(\\\\*)\\|/g, (match, backslashes: string) =>\n backslashes.length % 2 === 0 ? `${backslashes}\\\\|` : match,\n )\n .replace(/\\n/g, '<br>')\n}\n","import type {TypedObject} from '@portabletext/types'\nimport {escapeImageAndLinkTitle} from '../../escape'\nimport type {PortableTextMarkRenderer} from '../types'\n\n/**\n * @public\n */\nexport const DefaultEmRenderer: PortableTextMarkRenderer = ({children}) =>\n `_${children}_`\n\n/**\n * @public\n */\nexport const DefaultStrongRenderer: PortableTextMarkRenderer = ({children}) =>\n `**${children}**`\n\n/**\n * Renders a `code` decorator from the raw span text, bypassing the escaped\n * `children`: code content is verbatim, never markdown syntax. The\n * backtick fence is widened past the longest run of backticks already in\n * the text (CommonMark: the fence must be longer than any run it encloses).\n *\n * A space is padded on each side when the content starts or ends with a\n * backtick, so the fence and the content's own backtick never merge into\n * one run, and also when the content starts and ends with a space and\n * isn't all whitespace (`text.trim() !== ''`; an all-space code span has\n * nothing else for CommonMark's strip rule to leave behind, so padding it\n * would only add visible spaces), because CommonMark itself would\n * otherwise strip one such space per side on reparse; the padding\n * pre-compensates for that strip.\n *\n * @public\n */\nexport const DefaultCodeRenderer: PortableTextMarkRenderer = ({text}) =>\n wrapInCodeSpan(text)\n\nexport function wrapInCodeSpan(text: string): string {\n const fence = '`'.repeat(longestBacktickRun(text) + 1)\n const touchesBacktick = text.startsWith('`') || text.endsWith('`')\n const wouldBeStripped =\n text.startsWith(' ') && text.endsWith(' ') && text.trim() !== ''\n const padding = touchesBacktick || wouldBeStripped ? ' ' : ''\n return `${fence}${padding}${text}${padding}${fence}`\n}\n\nfunction longestBacktickRun(text: string): number {\n let longest = 0\n for (const run of text.match(/`+/g) ?? []) {\n longest = Math.max(longest, run.length)\n }\n return longest\n}\n\n/**\n * @public\n */\nexport const DefaultUnderlineRenderer: PortableTextMarkRenderer = ({\n children,\n}) => `<u>${children}</u>`\n\n/**\n * @public\n */\nexport const DefaultStrikeThroughRenderer: PortableTextMarkRenderer = ({\n children,\n}) => `~~${children}~~`\n\ninterface DefaultLink extends TypedObject {\n _type: 'link'\n href: string\n title: string | undefined\n}\n\n/**\n * @public\n */\nexport const DefaultLinkRenderer: PortableTextMarkRenderer<DefaultLink> = ({\n children,\n value,\n}) => {\n const href = value?.href || ''\n const title = value?.title || ''\n const looksSafe = uriLooksSafe(href)\n\n if (looksSafe) {\n // Check if the URL looks like an HTML injection attempt\n // If it has quotes AND angle brackets, or other suspicious patterns, encode more aggressively\n const looksLikeInjection = /[\"'][^\"']*[<>]|[<>][^<>]*[\"']/.test(href)\n\n if (looksLikeInjection) {\n // Encode all special characters that could be used for injection\n const encodedHref = href.replace(/[\"<>() ]/g, (char) => {\n return `%${char.charCodeAt(0).toString(16).toUpperCase()}`\n })\n return `[${children}](${encodedHref})`\n }\n\n // For normal URLs, don't encode parentheses - Markdown handles balanced parens fine\n return `[${children}](${href}${title ? ` \"${escapeImageAndLinkTitle(title)}\"` : ''})`\n }\n\n // Return children without link when URL is unsafe\n return children\n}\n\nfunction uriLooksSafe(uri: string): boolean {\n const url = (uri || '').trim()\n const first = url.charAt(0)\n\n if (first === '#' || first === '/') {\n return true\n }\n\n const colonIndex = url.indexOf(':')\n if (colonIndex === -1) {\n return true\n }\n\n const allowedProtocols = ['http', 'https', 'mailto', 'tel']\n const proto = url.slice(0, colonIndex).toLowerCase()\n if (allowedProtocols.indexOf(proto) !== -1) {\n return true\n }\n\n const queryIndex = url.indexOf('?')\n if (queryIndex !== -1 && colonIndex > queryIndex) {\n return true\n }\n\n const hashIndex = url.indexOf('#')\n if (hashIndex !== -1 && colonIndex > hashIndex) {\n return true\n }\n\n return false\n}\n\n/**\n * @public\n */\nexport const DefaultUnknownMarkRenderer: PortableTextMarkRenderer = ({\n children,\n}) => {\n return children\n}\n","import type {PortableTextBlock} from '@portabletext/types'\nimport type {PortableTextRenderer} from '../types'\n\ntype PortableTextBlockRenderer = PortableTextRenderer<PortableTextBlock>\n\n/**\n * @public\n */\nexport const DefaultNormalRenderer: PortableTextBlockRenderer = ({\n children,\n}) => {\n // Empty blocks should not add extra spacing\n if (!children || children.trim() === '') {\n return ''\n }\n\n return children\n}\n\n/**\n * @public\n */\nexport const DefaultBlockquoteRenderer: PortableTextBlockRenderer = ({\n children,\n}) => {\n // Prefix each line with \"> \" for proper blockquote formatting\n // This handles multi-line content and preserves empty lines\n if (!children) {\n return '>'\n }\n\n return children\n .split('\\n')\n .map((line) => `> ${line}`)\n .join('\\n')\n}\n\n/**\n * @public\n */\nexport const DefaultH1Renderer: PortableTextBlockRenderer = ({children}) =>\n `# ${children}`\n\n/**\n * @public\n */\nexport const DefaultH2Renderer: PortableTextBlockRenderer = ({children}) =>\n `## ${children}`\n\n/**\n * @public\n */\nexport const DefaultH3Renderer: PortableTextBlockRenderer = ({children}) =>\n `### ${children}`\n\n/**\n * @public\n */\nexport const DefaultH4Renderer: PortableTextBlockRenderer = ({children}) =>\n `#### ${children}`\n\n/**\n * @public\n */\nexport const DefaultH5Renderer: PortableTextBlockRenderer = ({children}) =>\n `##### ${children}`\n\n/**\n * @public\n */\nexport const DefaultH6Renderer: PortableTextBlockRenderer = ({children}) =>\n `###### ${children}`\n\n/**\n * @public\n */\nexport const DefaultUnknownStyleRenderer: PortableTextBlockRenderer = ({\n children,\n}) => {\n return children ?? ''\n}\n","import {isTypedObject} from '@portabletext/schema'\nimport {isPortableTextBlock} from '@portabletext/toolkit'\nimport type {PortableTextBlock, TypedObject} from '@portabletext/types'\nimport markdownit from 'markdown-it'\nimport {\n escapeImageAndLinkText,\n escapeImageAndLinkTitle,\n escapeTableCell,\n} from '../../escape'\nimport {markListItemFirstBlock} from '../list-item-first-block'\nimport type {PortableTextTypeRenderer} from '../types'\nimport {wrapInCodeSpan} from './marks'\n\n/**\n * @public\n */\nexport const DefaultCodeBlockRenderer: PortableTextTypeRenderer<{\n _type: 'code'\n code: string\n language: string | undefined\n}> = (options) => {\n if (!isCodeShaped(options.value)) {\n return DefaultUnknownTypeRenderer(options)\n }\n return `\\`\\`\\`${normalizeLanguage(options.value.language)}\\n${options.value.code}\\n\\`\\`\\``\n}\n\nfunction isCodeShaped(value: unknown): value is {code: string} {\n return typeof (value as {code?: unknown} | null)?.code === 'string'\n}\n\n/**\n * A fence info string is everything after the opening fence on the same\n * line, so a real `language` can never contain a newline, and the parser\n * only ever produces a string. Junk in this optional field should not send\n * an otherwise valid code block to the fenced-JSON path, so it is treated\n * as absent instead of guarded.\n */\nfunction normalizeLanguage(language: unknown): string {\n if (typeof language !== 'string' || language.includes('\\n')) {\n return ''\n }\n if (language === 'json:object') {\n // `json:object` is reserved as the object carrier: a code block\n // emitting it as its info string would re-parse as the embedded\n // object whenever its content happens to be typed JSON, destroying\n // the code block. The language degrades to absent instead.\n return ''\n }\n return language\n}\n\n/**\n * @public\n */\nexport const DefaultHorizontalRuleRenderer: PortableTextTypeRenderer = () => {\n return '---'\n}\n\n/**\n * @public\n */\nexport const DefaultHtmlRenderer: PortableTextTypeRenderer<{\n _type: 'html'\n html: string\n}> = (options) => {\n if (!isHtmlShaped(options.value)) {\n return DefaultUnknownTypeRenderer(options)\n }\n return options.value.html\n}\n\nfunction isHtmlShaped(value: unknown): value is {html: string} {\n return typeof (value as {html?: unknown} | null)?.html === 'string'\n}\n\n/**\n * @public\n */\nexport const DefaultImageRenderer: PortableTextTypeRenderer<{\n _type: 'image'\n src: string\n alt: string | undefined\n title: string | undefined\n}> = (options) => {\n if (!isImageShaped(options.value) || !linkValidator(options.value.src)) {\n return DefaultUnknownTypeRenderer(options)\n }\n const alt = escapeImageAndLinkText(options.value.alt ?? '')\n const title = options.value.title\n ? ` \"${escapeImageAndLinkTitle(options.value.title)}\"`\n : ''\n return ``\n}\n\n// The markdown-it instance the parse side builds\n// (`to-portable-text/markdown-to-portable-text.ts`) never overrides\n// `validateLink`, so this default instance's validator is the one that\n// will run on reparse. It rejects `javascript:`/`vbscript:`/`file:` and\n// all `data:` URIs except png/gif/jpeg/webp; a `src` it rejects would\n// reparse as literal text instead of an image, so such a `src` is\n// guarded here the same way a malformed image shape is.\nconst md = new markdownit()\nconst linkValidator = (url: string) => md.validateLink(url)\n\nfunction isImageShaped(value: unknown): value is {\n src: string\n alt: string | null | undefined\n title: string | null | undefined\n} {\n const image = value as {src?: unknown; alt?: unknown; title?: unknown} | null\n return (\n typeof image?.src === 'string' &&\n // CMS payloads commonly store cleared optional strings as `null`; the\n // render body treats `null` like absent, so the guard must too\n (image.alt == null || typeof image.alt === 'string') &&\n (image.title == null || typeof image.title === 'string')\n )\n}\n\n/**\n * A table is table-shaped when everything the renderer dereferences is\n * there: `rows` an array of typed objects with a `cells` array, every cell\n * a typed object whose `value` array holds typed objects (`renderNode`'s\n * input contract). The predicate narrows to exactly what `renderTable`\n * consumes, so the renderer needs no casts. A malformed `table` value\n * (e.g. a consumer's differently-shaped `table` type) falls back to the\n * fenced-JSON path instead of throwing.\n */\nfunction isTableShaped(value: unknown): value is TableShaped {\n const rows = (value as {rows?: unknown} | null)?.rows\n return (\n Array.isArray(rows) &&\n rows.every(\n (row) =>\n isTypedObject(row) &&\n Array.isArray(row['cells']) &&\n row['cells'].every(\n (cell) =>\n isTypedObject(cell) &&\n Array.isArray(cell['value']) &&\n cell['value'].every(isTypedObject),\n ),\n )\n )\n}\n\ntype TableShaped = {\n headerRows?: unknown\n alignment?: unknown\n rows: Array<{cells: Array<{value: Array<TypedObject>}>}>\n}\n\n/**\n * Renders a Portable Text table block-object back to Markdown.\n *\n * The PT `headerRows` field decides the header. Missing `headerRows` and\n * `headerRows === 0` both render headerless: GFM has no headerless form, so\n * an empty header row is emitted and every row goes in the body (that empty\n * header reads back as `headerRows: 0` via `markdownToPortableText`).\n * `headerRows >= 1` promotes `rows[0]` to the header. GFM allows exactly one\n * header row, so header rows beyond the first flatten into the body, lossy,\n * but the extra rows stay on the Portable Text side.\n *\n * Asymmetric tables (rows of varying cell counts) are widened to match\n * the row with the most cells. Narrower rows are padded with empty cells\n * so a GFM parser doesn't silently drop the extra cells in wider rows.\n *\n * @public\n */\nexport const DefaultTableRenderer: PortableTextTypeRenderer<{\n _type: 'table'\n headerRows: number | undefined\n alignment: Array<'left' | 'center' | 'right' | null> | undefined\n rows: Array<{\n _key: string\n cells: Array<{\n _key: string\n value: Array<PortableTextBlock>\n }>\n }>\n}> = (options) => {\n const {value, renderNode} = options\n\n if (!isTableShaped(value)) {\n return DefaultUnknownTypeRenderer(options)\n }\n\n return renderTable(value, renderNode)\n}\n\nfunction renderTable(\n value: TableShaped,\n renderNode: Parameters<PortableTextTypeRenderer>[0]['renderNode'],\n): string {\n const rows = value.rows\n // `alignment` is an extension field, not part of the table shape: junk\n // here should not send an otherwise valid table to the fenced-JSON path,\n // so it is normalized away instead of guarded (`{}.at` would throw below).\n const alignment: ReadonlyArray<unknown> | undefined = Array.isArray(\n value.alignment,\n )\n ? value.alignment\n : undefined\n\n const headerRow = rows.at(0)\n\n if (!headerRow) {\n return ''\n }\n\n // Helper to extract text from cell blocks\n const getCellText = (cellBlocks: Array<TypedObject>): string => {\n return cellBlocks\n .map((block, index) => {\n const rendered = renderNode({\n node: block,\n index,\n isInline: false,\n renderNode,\n })\n const rendererOptions = {\n value: block,\n isInline: false,\n index,\n renderNode,\n }\n if (rendered === DefaultUnknownTypeRenderer(rendererOptions)) {\n // A GFM cell is one line, so the multi-line fence carrier\n // would squash into `<br>` soup that reparses as plain text;\n // the inline carrier is single-line and reparses to the same\n // value. Exact-matched against the default carrier's output,\n // so declared markdown forms and custom `unknownType` output\n // pass through untouched.\n return DefaultUnknownTypeRenderer({\n ...rendererOptions,\n isInline: true,\n })\n }\n return rendered\n })\n .join(' ')\n .trim()\n }\n\n const lines: string[] = []\n\n // GFM requires every row to have the same number of cells as the header row\n // and the delimiter row. Parsers silently drop excess cells from body rows\n // that are wider than the header, so we widen the table to the widest row\n // and pad narrower rows with empty cells to keep all data visible.\n const columnCount = rows.reduce(\n (max, row) => Math.max(max, row.cells.length),\n 0,\n )\n\n const renderCells = (texts: Array<string>): string => {\n const padded = [...texts]\n while (padded.length < columnCount) {\n padded.push('')\n }\n return `| ${padded.join(' | ')} |`\n }\n\n const renderRow = (cells: typeof headerRow.cells): string =>\n renderCells(cells.map((cell) => escapeTableCell(getCellText(cell.value))))\n\n // Delimiter row, sized to the column count. Each cell's colons encode the\n // column's alignment as defined by `value.alignment[columnIndex]`.\n const separators = Array.from({length: columnCount}, (_, index) => {\n const align = alignment?.at(index)\n if (align === 'left') {\n return ' :--- '\n }\n if (align === 'center') {\n return ' :---: '\n }\n if (align === 'right') {\n return ' ---: '\n }\n return ' --- '\n })\n const delimiter = `|${separators.join('|')}|`\n\n const hasHeader = (Number(value.headerRows) || 0) >= 1\n\n if (!hasHeader) {\n // Headerless table: emit an empty header row and keep every row in the\n // body.\n lines.push(renderCells([]))\n lines.push(delimiter)\n for (const row of rows) {\n lines.push(renderRow(row.cells))\n }\n } else {\n // `rows[0]` is the header. Header rows beyond the first flatten into the\n // body (GFM has a single header row).\n lines.push(renderRow(headerRow.cells))\n lines.push(delimiter)\n for (let i = 1; i < rows.length; i++) {\n const row = rows.at(i)\n if (row) {\n lines.push(renderRow(row.cells))\n }\n }\n }\n\n return lines.join('\\n')\n}\n\n/**\n * @public\n */\nexport const DefaultCalloutRenderer: PortableTextTypeRenderer<{\n _type: 'callout'\n tone: string\n content: Array<PortableTextBlock>\n}> = (options) => {\n if (!isCalloutShaped(options.value)) {\n return DefaultUnknownTypeRenderer(options)\n }\n const {renderNode} = options\n const renderedContent = options.value.content\n .map((block, index) =>\n renderNode({\n node: block._type === 'block' ? {...block, style: 'normal'} : block,\n index,\n isInline: false,\n renderNode,\n }),\n )\n .filter((rendered) => rendered !== '')\n .join('\\n\\n')\n\n const prefixed = renderedContent\n .split('\\n')\n .map((line) => (line === '' ? '>' : `> ${line}`))\n .join('\\n')\n\n return `> [!${options.value.tone.toUpperCase()}]\\n${prefixed}`\n}\n\nfunction isCalloutShaped(\n value: unknown,\n): value is {tone: string; content: Array<TypedObject>} {\n const callout = value as {tone?: unknown; content?: unknown} | null\n return (\n typeof callout?.tone === 'string' &&\n Array.isArray(callout.content) &&\n callout.content.every(isTypedObject)\n )\n}\n\n/**\n * Renders a structural blockquote block-object (the `types.blockquote` shape\n * produced by `markdownToPortableText` when a `types.blockquote` matcher is\n * provided) back to Markdown. Each content block is rendered via the\n * recursive renderer pipeline, joined with blank lines, and every line is\n * prefixed with `> ` to form a Markdown blockquote.\n *\n * Distinct from `DefaultBlockquoteRenderer`, which renders flat-path text\n * blocks with `style: 'blockquote'`.\n *\n * @public\n */\nexport const DefaultBlockquoteObjectRenderer: PortableTextTypeRenderer<{\n _type: 'blockquote'\n content: Array<PortableTextBlock>\n}> = ({value, renderNode}) => {\n const renderedContent = value.content\n .map((block, index) =>\n renderNode({\n node: block._type === 'block' ? {...block, style: 'normal'} : block,\n index,\n isInline: false,\n renderNode,\n }),\n )\n .filter((rendered) => rendered !== '')\n .join('\\n\\n')\n\n return renderedContent\n .split('\\n')\n .map((line) => (line === '' ? '>' : `> ${line}`))\n .join('\\n')\n}\n\n/**\n * Renders a structural list block-object (the `types.list` shape produced by\n * `markdownToPortableText` when a `types.list` matcher is provided) back to\n * Markdown. Items render as `- ` for `kind: 'bullet'`, `1. `/`2. ` for `'number'`,\n * and `- [x] ` / `- [ ] ` for `'task'`. Items can hold any blocks - text blocks,\n * code blocks, callouts, images, and nested lists - and content other than the\n * leading text block is indented to keep it inside the item.\n *\n * @public\n */\nexport const DefaultListRenderer: PortableTextTypeRenderer<{\n _type: 'list'\n kind: 'bullet' | 'number' | 'task'\n items: Array<{\n _type: 'list-item'\n _key: string\n checked?: boolean\n content: Array<PortableTextBlock | TypedObject>\n }>\n}> = ({value, renderNode}) => {\n const renderedItems = value.items.map((item) => {\n // The marker-line mark changes how `renderBlock` plans line-start\n // hazard escaping, so it must be placed before rendering, but which\n // block ends up on the marker line is only knowable after (blocks\n // rendering to '' are dropped at join time). So every text block gets\n // the mark until something renders output; a mark on a dropped empty\n // block is harmless, consumed by its own render. Whether the first\n // surviving block actually takes the marker line is decided at\n // assembly below.\n let markerLineSettled = false\n\n return item.content.map((block, blockIndex) => {\n const isNestedList = (block as TypedObject)._type === 'list'\n const isTextBlock = !isNestedList && isPortableTextBlock(block)\n if (!markerLineSettled && isTextBlock) {\n markListItemFirstBlock(block)\n }\n const text = renderNode({\n node: block as TypedObject,\n index: blockIndex,\n isInline: false,\n renderNode,\n })\n if (text !== '') {\n markerLineSettled = true\n }\n return {isNestedList, isTextBlock, text}\n })\n })\n\n // A list is \"loose\" when any item carries multiple non-list-block\n // content entries (a continuation paragraph, a code block, etc).\n // CommonMark uses blank lines between items in loose lists; tight lists\n // pack items together with single newlines. A nested list as a second\n // child of an item does NOT make the list loose, and neither does a\n // block that rendered to nothing, so we ignore both when counting.\n const isLoose = renderedItems.some((renderedBlocks) => {\n const nonNestedBlocks = renderedBlocks.filter(\n (rendered) => !rendered.isNestedList && rendered.text !== '',\n )\n return nonNestedBlocks.length > 1\n })\n const itemSeparator = isLoose ? '\\n\\n' : '\\n'\n\n const lines = value.items.map((item, itemIndex) => {\n const marker = getListMarker(value.kind, itemIndex, item.checked)\n // Continuation indent matches the marker's width so that subsequent\n // blocks attach to this item under CommonMark's lazy-continuation rule.\n // Bullet `- ` indents to 2; ordered `1. ` indents to 3, `10. ` to 4.\n // Task `- [x] ` is conceptually `- ` + a `[x] ` content prefix at the\n // markdown-it level, so its continuation indent stays at 2.\n const indentWidth = value.kind === 'task' ? 2 : marker.length\n const indent = ' '.repeat(indentWidth)\n const indentLines = (text: string) =>\n text\n .split('\\n')\n .map((line) => (line === '' ? '' : `${indent}${line}`))\n .join('\\n')\n\n const nonEmptyBlocks = (renderedItems[itemIndex] ?? []).filter(\n (rendered) => rendered.text !== '',\n )\n // A nested list never shares the marker line: fusing the markers into\n // `- - sub` reparses the same for bullet and number kinds but reads as\n // one doubled marker, and after a task checkbox the nested marker is\n // literal text, destroying the sublist. A task item's marker line only\n // takes a text block for the same reason: everything after `- [x] ` is\n // inline text, so a promoted fence or table would reparse as words.\n const markerLineCandidate = nonEmptyBlocks[0]\n const promoted =\n markerLineCandidate &&\n !markerLineCandidate.isNestedList &&\n (value.kind !== 'task' || markerLineCandidate.isTextBlock)\n ? markerLineCandidate\n : undefined\n const rest = promoted ? nonEmptyBlocks.slice(1) : nonEmptyBlocks\n // Only the promoted block's first line shares the marker line; its\n // later lines (a code fence's body, a table's rows) are ordinary\n // continuation lines that must sit under the item indent, or\n // CommonMark ends the list at the first column-0 line.\n const [promotedFirstLine = '', ...promotedRestLines] = (\n promoted?.text ?? ''\n ).split('\\n')\n // Trailing whitespace is trimmed from the joined head, reaching only\n // its last line: an empty item's `- ` becomes `-`, while a hard\n // break's trailing spaces on an earlier line survive.\n const head = [\n `${marker}${promotedFirstLine}`,\n ...(promotedRestLines.length > 0\n ? [indentLines(promotedRestLines.join('\\n'))]\n : []),\n ]\n .join('\\n')\n .trimEnd()\n if (rest.length === 0) {\n return head\n }\n\n const tail = rest\n .map((rendered) => {\n const indented = indentLines(rendered.text)\n // Nested lists hug the previous block (tight list); other content\n // gets a blank line separator (paragraph break).\n return rendered.isNestedList ? `\\n${indented}` : `\\n\\n${indented}`\n })\n .join('')\n\n return `${head}${tail}`\n })\n\n return lines.join(itemSeparator)\n}\n\nfunction getListMarker(\n kind: 'bullet' | 'number' | 'task',\n itemIndex: number,\n checked: boolean | undefined,\n): string {\n if (kind === 'number') {\n return `${itemIndex + 1}. `\n }\n if (kind === 'task') {\n return checked ? '- [x] ' : '- [ ] '\n }\n return '- '\n}\n\n/**\n * @public\n */\nexport const DefaultUnknownTypeRenderer: PortableTextTypeRenderer = ({\n value,\n isInline,\n}) => {\n if (isInline) {\n // Single-line JSON: code spans turn newlines into spaces on\n // reparse, so a pretty-printed payload would still reconstruct but\n // would not survive byte-identically, breaking the fixpoint.\n return `json:object${wrapInCodeSpan(JSON.stringify(value))}`\n }\n return `\\`\\`\\`json:object\\n${JSON.stringify(value, null, 2)}\\n\\`\\`\\``\n}\n","import type {Schema} from '@portabletext/schema'\nimport type {\n ArbitraryTypedObject,\n PortableTextBlock,\n TypedObject,\n} from '@portabletext/types'\nimport {buildListIndexMap} from './build-list-index-map'\nimport {createRenderNode} from './render-node'\nimport {\n DefaultBlockSpacingRenderer,\n type BlockSpacingRenderer,\n} from './renderers/block-spacing'\nimport {DefaultHardBreakRenderer} from './renderers/hard-break'\nimport {\n DefaultListItemRenderer,\n DefaultUnknownListItemRenderer,\n} from './renderers/list-item'\nimport {\n DefaultCodeRenderer,\n DefaultEmRenderer,\n DefaultLinkRenderer,\n DefaultStrikeThroughRenderer,\n DefaultStrongRenderer,\n DefaultUnderlineRenderer,\n DefaultUnknownMarkRenderer,\n} from './renderers/marks'\nimport {\n DefaultBlockquoteRenderer,\n DefaultH1Renderer,\n DefaultH2Renderer,\n DefaultH3Renderer,\n DefaultH4Renderer,\n DefaultH5Renderer,\n DefaultH6Renderer,\n DefaultNormalRenderer,\n DefaultUnknownStyleRenderer,\n} from './renderers/style'\nimport {\n DefaultCalloutRenderer,\n DefaultCodeBlockRenderer,\n DefaultHorizontalRuleRenderer,\n DefaultHtmlRenderer,\n DefaultImageRenderer,\n DefaultTableRenderer,\n DefaultUnknownTypeRenderer,\n} from './renderers/type'\nimport type {PortableTextRenderers} from './types'\n\nconst defaultRenderers: PortableTextRenderers = {\n types: {\n 'callout': DefaultCalloutRenderer,\n 'code': DefaultCodeBlockRenderer,\n 'horizontal-rule': DefaultHorizontalRuleRenderer,\n 'html': DefaultHtmlRenderer,\n 'image': DefaultImageRenderer,\n 'table': DefaultTableRenderer,\n },\n\n block: {\n normal: DefaultNormalRenderer,\n blockquote: DefaultBlockquoteRenderer,\n h1: DefaultH1Renderer,\n h2: DefaultH2Renderer,\n h3: DefaultH3Renderer,\n h4: DefaultH4Renderer,\n h5: DefaultH5Renderer,\n h6: DefaultH6Renderer,\n },\n marks: {\n 'em': DefaultEmRenderer,\n 'strong': DefaultStrongRenderer,\n 'code': DefaultCodeRenderer,\n 'underline': DefaultUnderlineRenderer,\n 'strike-through': DefaultStrikeThroughRenderer,\n 'link': DefaultLinkRenderer,\n },\n listItem: DefaultListItemRenderer,\n hardBreak: DefaultHardBreakRenderer,\n\n unknownType: DefaultUnknownTypeRenderer,\n unknownMark: DefaultUnknownMarkRenderer,\n unknownListItem: DefaultUnknownListItemRenderer,\n unknownBlockStyle: DefaultUnknownStyleRenderer,\n}\n\ntype Options = Partial<PortableTextRenderers> & {\n blockSpacing?: BlockSpacingRenderer\n\n /**\n * Compiled schema that gates the built-in type renderers; it never\n * validates the value. A default renderer runs only when the schema\n * declares its type (`blockObjects` for block position, `inlineObjects`\n * for inline); undeclared types render through `unknownType`, whose\n * default output reparses back to the same value. The gate checks the\n * type name only, so declare the type's fields too:\n * `markdownToPortableText` cannot rebuild a value from a fieldless\n * declaration. Renderers passed in `types` are never gated. Pass the\n * same schema to `markdownToPortableText` to keep the round trip\n * consistent. Omitted, all default renderers stay active.\n */\n schema?: Schema\n}\n\n/**\n * @public\n */\nexport function portableTextToMarkdown<\n Block extends TypedObject = PortableTextBlock | ArbitraryTypedObject,\n>(blocks: Array<Block>, options: Options = {}): string {\n const renderers = {\n block: {\n ...defaultRenderers.block,\n ...options.block,\n },\n listItem: options.listItem ?? defaultRenderers.listItem,\n marks: {\n ...defaultRenderers.marks,\n ...options.marks,\n },\n types: {\n ...gateDefaultTypeRenderers(\n defaultRenderers.types,\n options.schema,\n options.unknownType ?? defaultRenderers.unknownType,\n ),\n ...options.types,\n },\n hardBreak: options.hardBreak ?? defaultRenderers.hardBreak,\n unknownType: options.unknownType ?? defaultRenderers.unknownType,\n unknownBlockStyle:\n options.unknownBlockStyle ?? defaultRenderers.unknownBlockStyle,\n unknownListItem:\n options.unknownListItem ?? defaultRenderers.unknownListItem,\n unknownMark: options.unknownMark ?? defaultRenderers.unknownMark,\n }\n const renderBlockSpacing = options.blockSpacing ?? DefaultBlockSpacingRenderer\n\n const {listIndexMap, listDepthMap} = buildListIndexMap(blocks)\n const renderNode = createRenderNode(renderers, listIndexMap, listDepthMap)\n\n // Blocks rendering to '' are dropped before spacing is computed, so\n // `blockSpacing` only ever sees blocks that survive into the output.\n const renderedBlocks = blocks\n .map((node, index) => ({\n node,\n rendered: renderNode({node, index, isInline: false, renderNode}),\n }))\n .filter(({rendered}) => rendered !== '')\n\n return renderedBlocks\n .map(({node, rendered}, index) => {\n const nextBlock = renderedBlocks.at(index + 1)\n\n if (!nextBlock) {\n return rendered\n }\n\n const blockSpacing =\n renderBlockSpacing({\n current: node,\n next: nextBlock.node,\n }) ?? '\\n\\n'\n\n return `${rendered}${blockSpacing}`\n })\n .join('')\n}\n\nfunction gateDefaultTypeRenderers(\n defaultTypeRenderers: PortableTextRenderers['types'],\n schema: Schema | undefined,\n resolvedUnknownType: PortableTextRenderers['unknownType'],\n): PortableTextRenderers['types'] {\n if (!schema) {\n return defaultTypeRenderers\n }\n\n return Object.fromEntries(\n Object.entries(defaultTypeRenderers).map(([typeName, renderer]) => [\n typeName,\n (rendererOptions: Parameters<typeof resolvedUnknownType>[0]) => {\n const declared = (\n rendererOptions.isInline ? schema.inlineObjects : schema.blockObjects\n ).some((item) => item.name === typeName)\n\n return declared && renderer\n ? renderer(rendererOptions)\n : resolvedUnknownType(rendererOptions)\n },\n ]),\n )\n}\n","import {\n compileSchema,\n defineSchema,\n type AnnotationDefinition,\n type BlockObjectDefinition,\n type DecoratorDefinition,\n type ListDefinition,\n type StyleDefinition,\n} from '@portabletext/schema'\n\n/********************\n * Default style definitions\n ********************/\n\nexport const normalStyleDefinition = {\n name: 'normal',\n} as const satisfies StyleDefinition\n\nexport const h1StyleDefinition = {\n name: 'h1',\n} as const satisfies StyleDefinition\n\nexport const h2StyleDefinition = {\n name: 'h2',\n} as const satisfies StyleDefinition\n\nexport const h3StyleDefinition = {\n name: 'h3',\n} as const satisfies StyleDefinition\n\nexport const h4StyleDefinition = {\n name: 'h4',\n} as const satisfies StyleDefinition\n\nexport const h5StyleDefinition = {\n name: 'h5',\n} as const satisfies StyleDefinition\n\nexport const h6StyleDefinition = {\n name: 'h6',\n} as const satisfies StyleDefinition\n\nexport const blockquoteStyleDefinition = {\n name: 'blockquote',\n} as const satisfies StyleDefinition\n\n/********************\n * Default list definitions\n ********************/\n\nexport const defaultOrderedListItemDefinition = {\n name: 'number',\n} as const satisfies ListDefinition\n\nexport const defaultUnorderedListItemDefinition = {\n name: 'bullet',\n} as const satisfies ListDefinition\n\nexport const defaultTaskListItemDefinition = {\n name: 'task',\n} as const satisfies ListDefinition\n\n/********************\n * Default decorator definitions\n ********************/\n\nexport const defaultStrongDecoratorDefinition = {\n name: 'strong',\n} as const satisfies DecoratorDefinition\n\nexport const defaultEmDecoratorDefinition = {\n name: 'em',\n} as const satisfies DecoratorDefinition\n\nexport const defaultCodeDecoratorDefinition = {\n name: 'code',\n} as const satisfies DecoratorDefinition\n\nexport const defaultStrikeThroughDecoratorDefinition = {\n name: 'strike-through',\n} as const satisfies DecoratorDefinition\n\n/********************\n * Default annotation definitions\n ********************/\n\nexport const defaultLinkObjectDefinition = {\n name: 'link',\n fields: [\n {name: 'href', type: 'string'},\n {name: 'title', type: 'string'},\n ],\n} as const satisfies AnnotationDefinition\n\n/********************\n * Default object definitions\n ********************/\n\nexport const defaultCodeObjectDefinition = {\n name: 'code',\n fields: [\n {name: 'language', type: 'string'},\n {name: 'code', type: 'string'},\n ],\n} as const satisfies BlockObjectDefinition\n\nexport const defaultImageObjectDefinition = {\n name: 'image',\n fields: [\n {name: 'src', type: 'string'},\n {name: 'alt', type: 'string'},\n {name: 'title', type: 'string'},\n ],\n} as const satisfies BlockObjectDefinition\n\nexport const defaultHorizontalRuleObjectDefinition = {\n name: 'horizontal-rule',\n} as const satisfies BlockObjectDefinition\n\nexport const defaultHtmlObjectDefinition = {\n name: 'html',\n fields: [{name: 'html', type: 'string'}],\n} as const satisfies BlockObjectDefinition\n\n/**\n * Mirrors the canonical shape `@portabletext/plugin-table` expects: `table`\n * (`headerRows`, `rows`), `row` (`cells`), `cell` (`value`, text blocks or\n * standalone `image` objects). `alignment` is a `@portabletext/markdown`\n * extension field; `@portabletext/plugin-table` ignores it. Keep this in\n * sync with `packages/plugin-table/src/table-config.ts`'s\n * `defaultTableConfig`.\n */\nexport const defaultTableObjectDefinition = {\n name: 'table',\n fields: [\n {name: 'headerRows', type: 'number'},\n {name: 'alignment', type: 'array'},\n {\n name: 'rows',\n type: 'array',\n of: [\n {\n type: 'object',\n name: 'row',\n fields: [\n {\n name: 'cells',\n type: 'array',\n of: [\n {\n type: 'object',\n name: 'cell',\n fields: [\n {\n name: 'value',\n type: 'array',\n of: [{type: 'block'}, {type: 'image'}],\n },\n ],\n },\n ],\n },\n ],\n },\n ],\n },\n ],\n} as const satisfies BlockObjectDefinition\n\nexport const defaultCalloutObjectDefinition = {\n name: 'callout',\n fields: [\n {name: 'tone', type: 'string'},\n {name: 'content', type: 'array'},\n ],\n} as const satisfies BlockObjectDefinition\n\n/**\n * The default schema for converting markdown to Portable Text.\n *\n * @public\n */\nexport const defaultSchema = compileSchema(\n defineSchema({\n block: {\n fields: [{name: 'checked', type: 'boolean'}],\n },\n styles: [\n normalStyleDefinition,\n h1StyleDefinition,\n h2StyleDefinition,\n h3StyleDefinition,\n h4StyleDefinition,\n h5StyleDefinition,\n h6StyleDefinition,\n blockquoteStyleDefinition,\n ],\n lists: [\n defaultOrderedListItemDefinition,\n defaultUnorderedListItemDefinition,\n defaultTaskListItemDefinition,\n ],\n decorators: [\n defaultStrongDecoratorDefinition,\n defaultEmDecoratorDefinition,\n defaultCodeDecoratorDefinition,\n defaultStrikeThroughDecoratorDefinition,\n ],\n annotations: [defaultLinkObjectDefinition],\n blockObjects: [\n defaultCalloutObjectDefinition,\n defaultCodeObjectDefinition,\n defaultHorizontalRuleObjectDefinition,\n defaultHtmlObjectDefinition,\n defaultImageObjectDefinition,\n defaultTableObjectDefinition,\n ],\n inlineObjects: [defaultImageObjectDefinition],\n }),\n)\n","import type {DegradationType} from './markdown-to-portable-text'\n\n/**\n * The full catalog of `Degradation['message']` prose, one entry per\n * `DegradationType`, grouped and ordered to match the union in\n * `markdown-to-portable-text.ts`. Kept apart from the conversion's\n * formatting mechanism (`buildDegradationMessage` and friends, in\n * `markdown-to-portable-text.ts`) so the catalog reads as one list instead\n * of being scattered across the walk that triggers it.\n *\n * `satisfies Record<DegradationType, ...>` makes the catalog exhaustive: a\n * new `DegradationType` member without a matching entry here is a type\n * error, caught at compile time instead of surfacing as an `undefined`\n * message at runtime.\n *\n * Style rule for every message here: a declarative statement of what\n * happened, never a verb that can parse as an imperative. A verb whose past\n * and imperative forms coincide (\"split\", \"set\", \"cut\", ...) never leads a\n * message, since the reader can't tell \"X was split\" from an instruction to\n * split X. State the effect first, the cause second, naming the schema\n * declaration that's missing.\n *\n * Not exported from the package entry: `message` is unstable by contract\n * (see `Degradation['message']`'s doc comment), so the catalog producing it\n * stays internal too.\n */\nexport const degradationMessage = {\n 'decorator-dropped': (\n decorator: 'code' | 'strong' | 'em' | 'strikeThrough',\n ): string => {\n switch (decorator) {\n case 'code':\n return 'Removed inline-code formatting, kept the text: the schema has no `code` decorator'\n case 'strong':\n return 'Removed bold formatting, kept the text: the schema has no `strong` decorator'\n case 'em':\n return 'Removed italic formatting, kept the text: the schema has no `em` decorator'\n case 'strikeThrough':\n return 'Removed strikethrough formatting, kept the text: the schema has no `strike-through` decorator'\n }\n },\n\n 'annotation-dropped': (cause: 'missing-url' | 'no-annotation'): string =>\n cause === 'missing-url'\n ? 'Removed a link that has no URL, kept its text'\n : 'Removed the link, kept its text: the schema has no `link` annotation',\n\n 'style-fallback': (name: string): string => {\n if (/^h[1-6]$/.test(name)) {\n const hashes = '#'.repeat(Number(name.slice(1)))\n return `\\`${hashes}\\` heading became a normal paragraph: the schema has no \\`${name}\\` style`\n }\n\n if (name === 'blockquote') {\n return 'Blockquote became normal paragraphs: the schema has no `blockquote` style'\n }\n\n return `Fell back to \\`normal\\` style: \\`${name}\\` not in schema`\n },\n\n 'list-flattened': (kind: 'bullet' | 'number'): string => {\n const label = kind === 'number' ? 'Numbered' : 'Bullet'\n return `${label} list became plain paragraphs: the schema has no \\`${kind}\\` list`\n },\n\n 'task-checkbox-stripped': (checked: boolean): string => {\n const checkbox = checked ? '[x]' : '[ ]'\n return `Removed the \\`${checkbox}\\` checkbox, kept a plain list item: the schema has no \\`task\\` list`\n },\n\n 'table-flattened':\n 'Table became plain text blocks, rows and columns lost: the schema has no `table` block object',\n\n 'code-block-to-text': (language: string | undefined): string =>\n language\n ? `\\`${language}\\` code block became plain text: the schema has no \\`code\\` block object`\n : `Code block became plain text: the schema has no \\`code\\` block object`,\n\n 'horizontal-rule-to-text':\n 'Horizontal rule became the text `---`: the schema has no `horizontal-rule` block object',\n\n 'html-block-to-text':\n 'HTML block became plain text: the schema has no `html` block object',\n\n 'inline-html-dropped':\n 'Removed inline HTML tags, kept nothing: `html.inline` is `skip` (the default)',\n\n 'image-block-to-inline': (cause: 'table-cell' | 'no-block-image'): string =>\n cause === 'table-cell'\n ? \"The image became inline: a table cell can't hold a block-level `image`\"\n : 'The image became inline: the schema has no block-level `image`',\n\n 'image-inline-to-block':\n 'The image became its own block, splitting the paragraph: the schema has no inline `image`',\n\n 'image-to-text':\n 'Image became its markdown source as plain text: the schema has no `image` object',\n\n 'callout-fallback': (calloutType: string, style: string): string =>\n `\\`[!${calloutType.toUpperCase()}]\\` callout became ${style}-styled text: the schema has no \\`callout\\` block object`,\n\n 'fields-dropped': (names: string, construct: string): string =>\n `Dropped ${names} from \\`${construct}\\`: not in the schema's \\`${construct}\\` fields`,\n\n 'object-carrier-invalid': (\n kind: 'fence' | 'code-span',\n payload: string,\n ): string =>\n kind === 'fence'\n ? `\\`json:object\\` fence fell back to a code block: ${describeObjectCarrierFailure(payload)}`\n : `\\`json:object\\`-tagged code span fell back to a plain code span: ${describeObjectCarrierFailure(payload)}`,\n} satisfies Record<DegradationType, string | ((...args: never[]) => string)>\n\n/**\n * Names why a `json:object` payload failed to parse as an object carrier:\n * a payload that isn't a JSON object at all reads differently from one\n * that is but has no usable `_type`.\n */\nfunction describeObjectCarrierFailure(payload: string): string {\n let parsed: unknown\n\n try {\n parsed = JSON.parse(payload)\n } catch {\n return 'the payload is not valid JSON'\n }\n\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n return 'the payload is not a JSON object'\n }\n\n return 'the payload has no string `_type`'\n}\n","import type {\n PortableTextObject,\n Schema,\n SchemaDefinition,\n} from '@portabletext/schema'\n\n/**\n * Fields a default object/annotation matcher (`buildObjectMatcher`,\n * `buildAnnotationMatcher`) silently dropped while filtering a converted\n * value down to the schema's declared fields: the keys the conversion\n * supplied that the schema's field list doesn't declare, so they never made\n * it into the built object. Tagged onto the matcher's return value; read it\n * back with `readDroppedFields`. A consumer-supplied matcher's return value\n * never carries this: its own filtering, if any, is not this module's\n * business.\n */\nexport type DroppedFields = {\n construct: string\n keys: Array<string>\n}\n\nconst droppedFieldsTag = Symbol('droppedFields')\n\nexport function readDroppedFields(\n object: PortableTextObject | undefined,\n): DroppedFields | undefined {\n if (!object) {\n return undefined\n }\n return (object as unknown as Record<symbol, unknown>)[droppedFieldsTag] as\n | DroppedFields\n | undefined\n}\n\nfunction buildFilteredObject(\n schemaDefinition: {name: string; fields: ReadonlyArray<{name: string}>},\n value: Record<string, unknown>,\n keyGenerator: () => string,\n): PortableTextObject {\n const filteredValue = schemaDefinition.fields.reduce<Record<string, unknown>>(\n (filteredValue, field) => {\n const fieldValue = value[field.name]\n\n if (fieldValue !== undefined) {\n filteredValue[field.name] = fieldValue\n }\n\n return filteredValue\n },\n {},\n )\n\n const object = {\n _key: keyGenerator(),\n _type: schemaDefinition.name,\n ...filteredValue,\n }\n\n const suppliedKeys = Object.entries(value)\n .filter(([, fieldValue]) => fieldValue !== undefined)\n .map(([key]) => key)\n const droppedKeys = suppliedKeys.filter((key) => !(key in filteredValue))\n\n if (droppedKeys.length > 0) {\n Object.defineProperty(object, droppedFieldsTag, {\n value: {construct: schemaDefinition.name, keys: droppedKeys},\n enumerable: false,\n })\n }\n\n return object\n}\n\n/**\n * Matcher function for mapping markdown elements to Portable Text block styles.\n *\n * @public\n */\nexport type StyleMatcher = ({\n context,\n}: {\n context: {schema: Schema}\n}) => string | undefined\n\nexport function buildStyleMatcher<TDefinition extends {name: string}>(\n definition: TDefinition,\n): StyleMatcher {\n return ({context}) => {\n const schemaDefinition = context.schema.styles.find(\n (item) => item.name === definition.name,\n )\n\n if (!schemaDefinition) {\n return undefined\n }\n\n return schemaDefinition.name\n }\n}\n\n/**\n * Matcher function for mapping markdown list items to Portable Text list types.\n *\n * @public\n */\nexport type ListItemMatcher = ({\n context,\n}: {\n context: {schema: Schema}\n}) => string | undefined\n\nexport function buildListItemMatcher<TDefinition extends {name: string}>(\n definition: TDefinition,\n): ListItemMatcher {\n return ({context}) => {\n const schemaDefinition = context.schema.lists.find(\n (item) => item.name === definition.name,\n )\n\n if (!schemaDefinition) {\n return undefined\n }\n\n return schemaDefinition.name\n }\n}\n\n/**\n * Matcher function for mapping markdown inline formatting to Portable Text decorators.\n *\n * @public\n */\nexport type DecoratorMatcher = ({\n context,\n}: {\n context: {schema: Schema}\n}) => string | undefined\n\nexport function buildDecoratorMatcher<TDefinition extends {name: string}>(\n definition: TDefinition,\n): DecoratorMatcher {\n return ({context}) => {\n const schemaDefinition = context.schema.decorators.find(\n (item) => item.name === definition.name,\n )\n\n if (!schemaDefinition) {\n return undefined\n }\n\n return schemaDefinition.name\n }\n}\n\n/**\n * Matcher function for mapping markdown links to Portable Text annotations.\n *\n * @public\n */\nexport type AnnotationMatcher<\n TValue extends Record<string, unknown> = Record<string, never>,\n> = ({\n context,\n value,\n}: {\n context: {schema: Schema; keyGenerator: () => string}\n value: TValue\n}) => PortableTextObject | undefined\n\nexport function buildAnnotationMatcher<TDefinition extends {name: string}>(\n definition: TDefinition,\n): AnnotationMatcher<ExtractValue<TDefinition>> {\n return ({context, value}) => {\n const schemaDefinition = context.schema.annotations.find(\n (item) => item.name === definition.name,\n )\n\n if (!schemaDefinition) {\n return undefined\n }\n\n return buildFilteredObject(schemaDefinition, value, context.keyGenerator)\n }\n}\n\n/**\n * Matcher function for mapping markdown objects to Portable Text block or inline objects.\n *\n * @public\n */\nexport type ObjectMatcher<\n TValue extends Record<string, unknown> = Record<string, never>,\n> = ({\n context,\n value,\n isInline,\n}: {\n context: {schema: Schema; keyGenerator: () => string}\n value: TValue\n isInline: boolean\n}) => PortableTextObject | undefined\n\nexport function buildObjectMatcher<TDefinition extends {name: string}>(\n definition: TDefinition,\n): ObjectMatcher<ExtractValue<TDefinition>> {\n return ({context, value, isInline}) => {\n const schemaCollection = isInline\n ? context.schema.inlineObjects\n : context.schema.blockObjects\n\n const schemaDefinition = schemaCollection.find(\n (item) => item.name === definition.name,\n )\n\n if (!schemaDefinition) {\n return undefined\n }\n\n return buildFilteredObject(schemaDefinition, value, context.keyGenerator)\n }\n}\n\nexport type ExtractValue<\n TDefinition extends NonNullable<SchemaDefinition['blockObjects']>[0],\n> = TDefinition extends {fields: ReadonlyArray<{name: infer TNames}>}\n ? Record<TNames & string, unknown>\n : Record<string, never>\n","import {alert} from '@mdit/plugin-alert'\nimport {\n isSpan,\n isTextBlock,\n type PortableTextBlock,\n type PortableTextObject,\n type PortableTextTextBlock,\n type Schema,\n} from '@portabletext/schema'\nimport markdownit from 'markdown-it'\nimport type {Token} from 'markdown-it'\nimport {\n blockquoteStyleDefinition,\n defaultCalloutObjectDefinition,\n defaultCodeDecoratorDefinition,\n defaultCodeObjectDefinition,\n defaultEmDecoratorDefinition,\n defaultHorizontalRuleObjectDefinition,\n defaultHtmlObjectDefinition,\n defaultImageObjectDefinition,\n defaultLinkObjectDefinition,\n defaultOrderedListItemDefinition,\n defaultSchema,\n defaultStrikeThroughDecoratorDefinition,\n defaultStrongDecoratorDefinition,\n defaultTableObjectDefinition,\n defaultTaskListItemDefinition,\n defaultUnorderedListItemDefinition,\n h1StyleDefinition,\n h2StyleDefinition,\n h3StyleDefinition,\n h4StyleDefinition,\n h5StyleDefinition,\n h6StyleDefinition,\n normalStyleDefinition,\n} from '../default-schema'\nimport {unescapeImageAndLinkText} from '../escape'\nimport {defaultKeyGenerator, uniqueKeyGenerator} from '../key-generator'\nimport {degradationMessage} from './degradation-messages'\nimport {\n buildAnnotationMatcher,\n buildDecoratorMatcher,\n buildListItemMatcher,\n buildObjectMatcher,\n buildStyleMatcher,\n readDroppedFields,\n type AnnotationMatcher,\n type DecoratorMatcher,\n type ExtractValue,\n type ListItemMatcher,\n type ObjectMatcher,\n type StyleMatcher,\n} from './matchers'\n\n/**\n * The classification of a lossy conversion encountered while converting\n * markdown to Portable Text: a markdown construct the conversion couldn't\n * carry through losslessly, whether because the target schema (or the\n * active matchers) doesn't represent it, or because the surrounding\n * structure (a table cell, for instance) can't hold the shape markdown\n * expressed, so the conversion fell back to a lossier representation\n * instead of failing.\n *\n * @public\n */\nexport type DegradationType =\n | 'decorator-dropped'\n | 'annotation-dropped'\n | 'style-fallback'\n | 'list-flattened'\n | 'task-checkbox-stripped'\n | 'table-flattened'\n | 'code-block-to-text'\n | 'horizontal-rule-to-text'\n | 'html-block-to-text'\n | 'inline-html-dropped'\n | 'image-block-to-inline'\n | 'image-inline-to-block'\n | 'image-to-text'\n | 'callout-fallback'\n | 'fields-dropped'\n | 'object-carrier-invalid'\n\n/**\n * Reports a single lossy conversion, in encounter order: as the conversion\n * walks the markdown, that's the order nested constructs close in, not\n * necessarily top-to-bottom document order. `line` is the 1-based markdown\n * source line: usually the line of the token that degraded, but for a\n * token without its own line (an inline construct inside a table cell, for\n * instance) the enclosing construct's line instead; absent when no token\n * carries a usable line at all. `snippet` is the offending construct's\n * text, truncated to 40 characters with an ellipsis, present whenever the\n * degradation has a specific piece of source text to quote (a dropped\n * decorator's span, an unsupported image's alt text) and absent when the\n * construct has nothing to quote (a table with no `table` block object, a\n * callout whose type isn't in the schema). `message` is human-readable and\n * may change between releases; match on `type`, not `message`. The set of\n * `type` values grows in minor releases as new degradation sites report, so\n * compare against the values you handle rather than switching exhaustively.\n *\n * @public\n */\nexport type Degradation = {\n type: DegradationType\n message: string\n line?: number\n snippet?: string\n}\n\ntype Options = {\n /**\n * Compiled schema deciding which Portable Text constructs the\n * conversion may build; pairs with the same option on\n * `portableTextToMarkdown` to keep the round trip consistent.\n */\n schema?: Schema\n keyGenerator?: () => string\n /**\n * Called at most once, after the conversion has walked the whole\n * document, only when at least one construct degraded. Left unset, the\n * conversion stays silent and returns the lossiest representation it can\n * build. Passed a function, observe every degradation via\n * `report.degradations`, in encounter order. Enforce against lossy output by\n * throwing your own error from inside the callback, using\n * `report.message` (the canonical grouped text) as its message; the\n * throw propagates out of `markdownToPortableText`.\n *\n * ```ts\n * markdownToPortableText(markdown, {onDegradation: ({message}) => { throw new Error(message) }})\n * ```\n *\n * `report` is a single object, not positional parameters, so it can gain\n * fields later without a breaking change.\n */\n onDegradation?: (report: {\n degradations: Array<Degradation>\n message: string\n }) => void\n marks?: {\n strong?: DecoratorMatcher\n em?: DecoratorMatcher\n code?: DecoratorMatcher\n strikeThrough?: DecoratorMatcher\n link?: AnnotationMatcher<{href: string; title: string | undefined}>\n }\n block?: {\n normal?: StyleMatcher\n blockquote?: StyleMatcher\n h1?: StyleMatcher\n h2?: StyleMatcher\n h3?: StyleMatcher\n h4?: StyleMatcher\n h5?: StyleMatcher\n h6?: StyleMatcher\n }\n listItem?: {\n number?: ListItemMatcher\n bullet?: ListItemMatcher\n task?: ListItemMatcher\n }\n types?: {\n code?: ObjectMatcher<{language: string | undefined; code: string}>\n horizontalRule?: ObjectMatcher\n html?: ObjectMatcher<{html: string}>\n table?: ObjectMatcher<{\n headerRows: number | undefined\n alignment: Array<'left' | 'center' | 'right' | null> | undefined\n rows: Array<{\n _key: string\n _type: 'row'\n cells: Array<{\n _type: 'cell'\n _key: string\n value: Array<PortableTextBlock>\n }>\n }>\n }>\n image?: ObjectMatcher<{src: string; alt: string; title: string | undefined}>\n callout?: ObjectMatcher<{tone: string; content: Array<PortableTextBlock>}>\n blockquote?: ObjectMatcher<{content: Array<PortableTextBlock>}>\n list?: ObjectMatcher<{\n kind: 'bullet' | 'number' | 'task'\n items: Array<{\n _type: 'list-item'\n _key: string\n checked?: boolean\n content: Array<PortableTextBlock | PortableTextObject>\n }>\n }>\n }\n html?: {\n /**\n * How to handle inline HTML.\n * - 'skip': Ignore inline HTML (default)\n * - 'text': Convert inline HTML to plain text\n *\n * @defaultValue 'skip'\n */\n inline?: 'skip' | 'text'\n }\n}\n\nconst codeBlockMatcher: ObjectMatcher<\n ExtractValue<typeof defaultCodeObjectDefinition>\n> = ({context, value, isInline}) => {\n const defaultMatcher = buildObjectMatcher(defaultCodeObjectDefinition)\n const codeObject = defaultMatcher({context, value, isInline})\n\n if (!codeObject) {\n return undefined\n }\n\n if (!('code' in codeObject)) {\n return undefined\n }\n\n return codeObject\n}\n\nconst imageBlockMatcher: ObjectMatcher<\n ExtractValue<typeof defaultImageObjectDefinition>\n> = ({context, value, isInline}) => {\n const defaultMatcher = buildObjectMatcher(defaultImageObjectDefinition)\n const imageObject = defaultMatcher({context, value, isInline})\n\n if (!imageObject) {\n return undefined\n }\n\n if (!('src' in imageObject)) {\n return undefined\n }\n\n return imageObject\n}\n\nconst tableBlockMatcher: ObjectMatcher<\n ExtractValue<typeof defaultTableObjectDefinition>\n> = ({context, value, isInline}) => {\n const defaultMatcher = buildObjectMatcher(defaultTableObjectDefinition)\n const tableObject = defaultMatcher({context, value, isInline})\n\n if (!tableObject) {\n return undefined\n }\n\n if (!('rows' in tableObject)) {\n return undefined\n }\n\n return tableObject\n}\n\nconst defaultOptions = {\n schema: defaultSchema,\n keyGenerator: defaultKeyGenerator,\n html: {\n inline: 'skip',\n },\n block: {\n normal: buildStyleMatcher(normalStyleDefinition),\n blockquote: buildStyleMatcher(blockquoteStyleDefinition),\n h1: buildStyleMatcher(h1StyleDefinition),\n h2: buildStyleMatcher(h2StyleDefinition),\n h3: buildStyleMatcher(h3StyleDefinition),\n h4: buildStyleMatcher(h4StyleDefinition),\n h5: buildStyleMatcher(h5StyleDefinition),\n h6: buildStyleMatcher(h6StyleDefinition),\n },\n listItem: {\n number: buildListItemMatcher(defaultOrderedListItemDefinition),\n bullet: buildListItemMatcher(defaultUnorderedListItemDefinition),\n task: buildListItemMatcher(defaultTaskListItemDefinition),\n },\n marks: {\n strong: buildDecoratorMatcher(defaultStrongDecoratorDefinition),\n em: buildDecoratorMatcher(defaultEmDecoratorDefinition),\n code: buildDecoratorMatcher(defaultCodeDecoratorDefinition),\n strikeThrough: buildDecoratorMatcher(\n defaultStrikeThroughDecoratorDefinition,\n ),\n link: buildAnnotationMatcher(defaultLinkObjectDefinition),\n },\n types: {\n code: codeBlockMatcher,\n horizontalRule: buildObjectMatcher(defaultHorizontalRuleObjectDefinition),\n html: buildObjectMatcher(defaultHtmlObjectDefinition),\n image: imageBlockMatcher,\n callout: buildObjectMatcher(defaultCalloutObjectDefinition),\n table: tableBlockMatcher,\n },\n} as const satisfies Options\n\n/**\n * Reads a token attribute as a string. markdown-it's own `Token#attrs` type\n * also allows numeric values, for attributes like an ordered list's\n * `start`, but the attributes this file reads (`src`, `href`, `title`,\n * `style`) are always strings, set by markdown-it's own link/image/table\n * parsing.\n */\nfunction stringAttr(token: Token, name: string): string | undefined {\n const value = token.attrGet(name)\n return typeof value === 'string' ? value : undefined\n}\n\n/**\n * Reads GFM column alignment from a markdown-it cell token's `style`\n * attribute. Tolerates other CSS declarations sharing the value.\n */\nexport function extractAlignmentFromStyleAttr(\n styleAttr: string | null,\n): 'left' | 'center' | 'right' | null {\n if (!styleAttr) {\n return null\n }\n const match = styleAttr.match(/text-align\\s*:\\s*(left|center|right)/)\n if (!match) {\n return null\n }\n return match[1] as 'left' | 'center' | 'right'\n}\n\n/**\n * A table row is empty when every cell holds only blank spans, no non-empty\n * text, no inline objects, no non-text blocks. Used to detect a headerless\n * GFM table: `portableTextToMarkdown` emits an empty header row for\n * `headerRows: 0`, and an empty header must round-trip back to\n * `headerRows: 0` rather than a phantom header row.\n */\nfunction isEmptyTableRow(\n cells: Array<{value: Array<PortableTextBlock>}>,\n context: {schema: Schema},\n): boolean {\n return cells.every((cell) =>\n cell.value.every(\n (block) =>\n isTextBlock(context, block) &&\n block.children.every(\n (child) => isSpan(context, child) && (child.text ?? '').trim() === '',\n ),\n ),\n )\n}\n\n/**\n * Flattens a table structure by lifting all blocks from all cells.\n */\nfunction flattenTable(\n table: {\n rows: Array<{\n _key: string\n _type: 'row'\n cells: Array<{\n _type: 'cell'\n _key: string\n value: Array<PortableTextBlock>\n }>\n }>\n headerRows: number\n },\n portableText: Array<PortableTextBlock>,\n): void {\n // Flatten the table by lifting all blocks from all cells\n for (const row of table.rows) {\n for (const cell of row.cells) {\n for (const block of cell.value) {\n portableText.push(block)\n }\n }\n }\n}\n\n/**\n * Truncates a degradation message's snippet to keep the thrown/reported\n * message readable. Truncates on character count, not word boundaries: the\n * snippet is a diagnostic pointer back to the source, not prose. Undefined\n * for empty input, so a construct with nothing to quote (an empty link's\n * text, say) omits `snippet` entirely instead of reporting `\"\"`. Backs the\n * cut off by one unit when it would land on a lead surrogate, so a snippet\n * ending mid-emoji doesn't produce an unpaired surrogate. A literal newline\n * surviving into the snippet is escaped to `\\n`, since the reported message\n * is one line per finding.\n */\nfunction truncateSnippet(text: string, maxLength = 40): string | undefined {\n if (text.length === 0) {\n return undefined\n }\n\n if (text.length <= maxLength) {\n return text.replace(/\\n/g, '\\\\n')\n }\n\n let cut = maxLength\n const codeUnit = text.charCodeAt(cut - 1)\n if (codeUnit >= 0xd800 && codeUnit <= 0xdbff) {\n cut -= 1\n }\n\n return `${text.slice(0, cut).replace(/\\n/g, '\\\\n')}...`\n}\n\n/**\n * Concatenates the plain text between an inline open token (`strong_open`,\n * `em_open`, `s_open`, `link_open`) and its matching close, for use as a\n * degradation message snippet. Tracks nesting depth so a same-type token\n * nested inside itself doesn't stop the scan at the wrong close.\n */\nfunction collectInlineText(\n children: ReadonlyArray<{type: string; content: string}>,\n openIndex: number,\n openType: string,\n closeType: string,\n): string {\n let depth = 1\n let text = ''\n for (let i = openIndex + 1; i < children.length; i++) {\n const child = children[i]\n if (!child) {\n continue\n }\n if (child.type === openType) {\n depth++\n } else if (child.type === closeType) {\n depth--\n if (depth === 0) {\n break\n }\n } else if (child.type === 'text') {\n text += child.content\n } else if (child.type === 'softbreak') {\n text += ' '\n } else if (child.type === 'hardbreak') {\n text += '\\n'\n }\n }\n return text\n}\n\n// A per-group snippet/line list longer than this is truncated with an\n// `and N more` tail: the grouped message is a one-line-per-finding summary,\n// not a full dump of every occurrence (that's what `degradations` is for).\nconst MAX_LISTED_PER_GROUP = 5\n\nfunction capList(values: ReadonlyArray<string>): string {\n if (values.length <= MAX_LISTED_PER_GROUP) {\n return values.join(', ')\n }\n const shown = values.slice(0, MAX_LISTED_PER_GROUP)\n const more = values.length - MAX_LISTED_PER_GROUP\n return `${shown.join(', ')}, and ${more} more`\n}\n\n/**\n * Builds the canonical grouped message reported alongside a non-empty\n * `degradations` array: identical (`type`, `message`) pairs collapse into one\n * line, so a document with the same missing decorator on three spans\n * doesn't repeat the same sentence three times. Groups sort by their\n * earliest-lined entry so the message reads top-to-bottom regardless of walk\n * order: nested constructs (a blockquote inside a blockquote, say) report\n * the innermost closing first even though it opened last, and that line may\n * arrive after another entry already in the group. Groups without any lined\n * entry sort last, in their relative encounter order.\n */\nfunction buildDegradationMessage(degradations: Array<Degradation>): string {\n const groups: Array<{\n base: string\n entries: Array<Degradation>\n }> = []\n const groupIndexByKey = new Map<string, number>()\n\n for (const degradation of degradations) {\n const key = `${degradation.type}\\u0000${degradation.message}`\n let groupIndex = groupIndexByKey.get(key)\n if (groupIndex === undefined) {\n groupIndex = groups.length\n groupIndexByKey.set(key, groupIndex)\n groups.push({base: degradation.message, entries: []})\n }\n groups[groupIndex]!.entries.push(degradation)\n }\n\n const minLine = (entries: Array<Degradation>): number | undefined => {\n const definedLines = entries\n .map((entry) => entry.line)\n .filter((line): line is number => line !== undefined)\n return definedLines.length > 0 ? Math.min(...definedLines) : undefined\n }\n\n const sortedGroups = [...groups].sort((a, b) => {\n const lineA = minLine(a.entries)\n const lineB = minLine(b.entries)\n if (lineA === undefined) {\n return lineB === undefined ? 0 : 1\n }\n if (lineB === undefined) {\n return -1\n }\n return lineA - lineB\n })\n\n const lines = sortedGroups.map((group) => {\n if (group.entries.length === 1) {\n const event = group.entries[0]!\n const snippetPart =\n event.snippet === undefined ? '' : ` (\"${event.snippet}\")`\n return event.line === undefined\n ? `- ${event.message}${snippetPart}`\n : `- line ${event.line}: ${event.message}${snippetPart}`\n }\n\n const snippets = group.entries\n .map((entry) => entry.snippet)\n .filter((snippet): snippet is string => snippet !== undefined)\n\n // Read top-to-bottom regardless of encounter order, same reasoning as\n // the group sort above. Entries without a line are counted but never\n // printed: joining a maybe-`undefined` would put the literal word\n // `undefined` in the message. Deduped: several entries in the group can\n // share one line (a table's cells, all pinned to the table's start\n // line), and the count already carries how many there were.\n const linesAscending = [\n ...new Set(\n group.entries\n .map((entry) => entry.line)\n .filter((line): line is number => line !== undefined)\n .sort((a, b) => a - b),\n ),\n ]\n\n const count = group.entries.length\n const suffix =\n snippets.length === count\n ? `(${count}\\u00d7: ${capList(snippets.map((snippet) => `\"${snippet}\"`))})`\n : linesAscending.length > 0\n ? `(${count}\\u00d7: lines ${capList(linesAscending.map(String))})`\n : `(${count}\\u00d7)`\n\n return `- ${group.base} ${suffix}`\n })\n\n return ['Markdown could not be converted without loss:', ...lines].join('\\n')\n}\n\n/**\n * Converts a markdown string to an array of Portable Text blocks.\n *\n * @public\n */\nexport function markdownToPortableText(\n markdown: string,\n options?: Options,\n): Array<PortableTextBlock> {\n const consolidatedOptions = {\n schema: options?.schema ?? defaultSchema,\n keyGenerator: uniqueKeyGenerator(\n options?.keyGenerator ?? defaultKeyGenerator,\n ),\n html: {\n inline: options?.html?.inline ?? 'skip',\n },\n marks: {\n ...defaultOptions.marks,\n ...options?.marks,\n },\n block: {\n ...defaultOptions.block,\n ...options?.block,\n },\n listItem: {\n ...defaultOptions.listItem,\n ...options?.listItem,\n },\n types: {\n ...defaultOptions.types,\n ...options?.types,\n },\n }\n\n const degradationEvents: Array<Degradation> = []\n\n const report = (event: Degradation): void => {\n degradationEvents.push(event)\n }\n\n // A markdown-it token's `map` is `[startLine, endLine)`, 0-based. Degradation\n // events report the 1-based start line for readability.\n const lineOf = (\n candidateToken: {map?: [number, number] | null} | null | undefined,\n ): number | undefined =>\n candidateToken?.map ? candidateToken.map[0] + 1 : undefined\n\n const reportStyleFallback = (\n name: string,\n line?: number,\n snippet?: string,\n ): void => {\n if (/^h[1-6]$/.test(name)) {\n const truncated =\n snippet === undefined ? undefined : truncateSnippet(snippet)\n report({\n type: 'style-fallback',\n message: degradationMessage['style-fallback'](name),\n line,\n snippet: truncated,\n })\n return\n }\n\n if (name === 'blockquote') {\n report({\n type: 'style-fallback',\n message: degradationMessage['style-fallback'](name),\n line,\n })\n return\n }\n\n report({\n type: 'style-fallback',\n message: degradationMessage['style-fallback'](name),\n line,\n })\n }\n\n // Only a default matcher (`buildObjectMatcher`, `buildAnnotationMatcher`)\n // tags its return value this way; a consumer-supplied matcher's output\n // passes through untouched, since its own filtering is its own business.\n const reportFieldsDropped = (\n object: PortableTextObject | undefined,\n line: number | undefined,\n ): void => {\n const dropped = readDroppedFields(object)\n if (!dropped) {\n return\n }\n const names = dropped.keys.map((key) => `\\`${key}\\``).join(', ')\n report({\n type: 'fields-dropped',\n message: degradationMessage['fields-dropped'](names, dropped.construct),\n line,\n })\n }\n\n const md = markdownit({\n html: true,\n linkify: true,\n typographer: false,\n })\n .enable(['strikethrough', 'table'])\n .use(alert)\n\n // Fuzzy links (bare `www.example.com`) and the URL auth part scan\n // (`user:pass@host`) are off by default; this must stay in lockstep\n // with the mirror `LinkifyIt` in `escape-plain-text.ts`.\n md.linkify.set({fuzzyLink: true, urlAuth: true})\n\n const tokens = md.parse(markdown, {})\n\n // Pre-pass: detect GFM task-list checkbox prefixes (`[ ]`, `[x]`, `[X]`)\n // on the first inline content of each list item. Strip the prefix from the\n // inline content and remember which list items are tasks (and their checked\n // state) so the main walk can apply `listItem: 'task'` and `checked` when\n // processing the corresponding `list_item_open` token.\n const taskCheckedByListItemIndex = new Map<number, boolean>()\n // The item's text (after the checkbox prefix is stripped below), for the\n // `task-checkbox-stripped` degradation message's snippet.\n const taskItemTextByListItemIndex = new Map<number, string>()\n for (let i = 0; i < tokens.length; i++) {\n const token = tokens[i]\n if (token?.type !== 'list_item_open') {\n continue\n }\n // Find the first inline token within this list item.\n let inlineIndex = -1\n for (let j = i + 1; j < tokens.length; j++) {\n const candidate = tokens[j]\n if (!candidate) {\n continue\n }\n if (candidate.type === 'list_item_close') {\n break\n }\n if (candidate.type === 'inline') {\n inlineIndex = j\n break\n }\n }\n if (inlineIndex === -1) {\n continue\n }\n const inlineToken = tokens[inlineIndex]\n if (!inlineToken) {\n continue\n }\n const match = inlineToken.content.match(/^\\[([ xX])\\] /)\n if (!match) {\n continue\n }\n const checked = match[1] !== ' '\n taskCheckedByListItemIndex.set(i, checked)\n // Strip the prefix from the content and the first child text token so the\n // resulting span doesn't include the checkbox marker.\n inlineToken.content = inlineToken.content.slice(match[0].length)\n taskItemTextByListItemIndex.set(i, inlineToken.content)\n const firstChild = inlineToken.children?.[0]\n if (firstChild && typeof firstChild.content === 'string') {\n firstChild.content = firstChild.content.slice(match[0].length)\n }\n }\n\n const portableText: Array<PortableTextBlock> = []\n\n // State\n let currentBlock: PortableTextTextBlock | null = null\n const currentListStack: Array<string | null> = []\n const markDefRefs: Array<string> = [] // mark keys: 'strong', 'em', 'code', or link keys\n let currentMarkDefs: Array<PortableTextObject> = []\n let currentBlockquoteStyle: string | null = null // Track blockquote style when inside blockquote\n let inListItem = false // Track if we're inside a list item\n // Provenance for `currentBlock`, set by `startBlock`'s caller: whether the\n // block's style was actually resolved through `currentBlockquoteStyle`\n // (a paragraph, say) rather than independently landing on the same value\n // by coincidence (a heading whose own style declined to the same\n // `normal` fallback). `flushBlock`'s callout gate reads this instead of\n // comparing styles, since two independent declines can resolve to the\n // same style name without either one being the other.\n let currentBlockTookBlockquoteStyle = false\n // Provenance for `currentBlock`: whether it was created to accumulate\n // paragraph text (a `paragraph_open`, or inline content starting a block\n // with no wrapper) as opposed to a dedicated single-purpose block (a\n // heading, a table cell, a code/HTML/hr fallback-to-text). A structural\n // list's decline fallback merges adjacent item content back into the flat\n // path's shape, and the flat path only ever merges accumulated paragraph\n // blocks: a fallback-to-text block always flushes and starts its own\n // block explicitly, never sharing `currentBlock` with surrounding text.\n let currentBlockIsPlainParagraph = false\n const plainParagraphBlocks = new WeakSet<PortableTextTextBlock>()\n\n // Callout state\n let calloutStartIndex: number | null = null\n let calloutStartTarget: Array<PortableTextBlock | PortableTextObject> | null =\n null\n let calloutType: string | null = null\n let calloutStartLine: number | undefined\n // Style names that declined while setting up the callout's content style,\n // reported once `alert_title` supplies the callout's own first line (the\n // marker, not the first content line `alert_open`'s map starts at).\n let calloutPendingStyleFallbacks: Array<string> = []\n\n // Blockquote container state. When `types.blockquote` is defined and the\n // parser enters a `blockquote_open`, a frame is pushed here. Block\n // emissions inside the surrounding open/close pair are captured and\n // spliced out at close to wrap them in a `blockquote` block-object.\n // Nested blockquotes push additional frames; `blockTarget()` already\n // routes correctly because the splice happens against the same target.\n const blockquoteStack: Array<{\n startTarget: Array<PortableTextBlock | PortableTextObject>\n startIndex: number\n line: number | undefined\n }> = []\n\n // Table state\n let currentTable: {\n rows: Array<{\n _key: string\n _type: 'row'\n cells: Array<{\n _type: 'cell'\n _key: string\n value: Array<PortableTextBlock>\n }>\n }>\n headerRows: number\n emptyHeaderDropped: boolean\n alignment: Array<'left' | 'center' | 'right' | null>\n line: number | undefined\n } | null = null\n let currentTableRow: Array<{\n _type: 'cell'\n _key: string\n value: Array<PortableTextBlock>\n }> | null = null\n let inTableHead = false\n // Images demoted from block-level to inline while pushed into a table\n // cell (see the `inline` case). `td_close`/`th_close` may lift a sole\n // image child back to block-level losslessly; membership here lets that\n // lift decide whether to report the demotion, and the value carries which\n // cause to report it with (a schema-declared block image forced inline by\n // the cell vs. no block image in the schema at all).\n const demotedTableImages = new WeakMap<\n PortableTextObject,\n 'table-cell' | 'no-block-image'\n >()\n\n // List container state. When `types.list` is defined and the parser enters\n // a `bullet_list_open` / `ordered_list_open`, a structural-list frame is\n // pushed here. Block emissions inside the surrounding `list_item_open` /\n // `list_item_close` pair are diverted into the item's `content` array\n // instead of the top-level `portableText`. At list-close, the matcher is\n // called to materialize a `list` block-object, which is pushed into the\n // enclosing target (parent list item's content if nested, else top-level).\n type ListContainerItem = {\n _type: 'list-item'\n _key: string\n checked?: boolean\n content: Array<PortableTextBlock | PortableTextObject>\n }\n type ListContainerFrame = {\n kind: 'bullet' | 'number' | 'task'\n items: Array<ListContainerItem>\n currentItem: ListContainerItem | null\n line: number | undefined\n }\n // A null entry marks a list that is being handled by the flat path (either\n // because `types.list` is undefined, or because a nested list inside a\n // flat-path list should also stay flat). Parallel to `currentListStack`.\n const listContainerStack: Array<ListContainerFrame | null> = []\n\n // Flat-path `list-flattened` verdicts, parallel to `currentListStack`: a\n // list whose own kind (`bullet`/`number`) isn't in the schema doesn't\n // necessarily degrade, since every item might still resolve through a\n // `task` checkbox override. The verdict is deferred to the first item\n // that actually needs the fallback (`list_item_open`'s `listType === null`\n // branch), so a schema with only a `task` list and an all-checkbox list\n // reports nothing. `null` marks a list whose own kind resolved fine, so no\n // verdict is pending.\n const pendingListFlattenedStack: Array<{\n line: number | undefined\n kindName: 'bullet' | 'number'\n reported: boolean\n } | null> = []\n\n // Per-item task-checkbox metadata for structural list items (line + text\n // snippet), keyed by item object identity. Not stored on the item itself:\n // `frame.items` is handed verbatim to a consumer's `types.list` matcher,\n // and this bookkeeping is only needed if that matcher declines and the\n // fallback needs to reproduce the flat path's `task-checkbox-stripped`\n // report for the item.\n const taskInfoByListItem = new WeakMap<\n ListContainerItem,\n {line: number | undefined; snippet: string | undefined}\n >()\n\n /**\n * Returns the array that block emissions should land in. If the innermost\n * structural list frame has an open `currentItem`, blocks land in that\n * item's `content`. Otherwise blocks land at the top level.\n */\n const blockTarget = (): Array<PortableTextBlock | PortableTextObject> => {\n for (let i = listContainerStack.length - 1; i >= 0; i--) {\n const frame = listContainerStack[i]\n if (frame && frame.currentItem) {\n return frame.currentItem.content\n }\n }\n return portableText\n }\n\n /**\n * Pushes a block into the current target (innermost open list item, or\n * top-level `portableText` if none). Use instead of direct\n * `portableText.push(...)` for any block emission that should be captured\n * by an enclosing list container.\n */\n const pushBlock = (block: PortableTextBlock | PortableTextObject): void => {\n blockTarget().push(block as PortableTextBlock)\n }\n\n const startBlock = (\n style: string,\n provenance?: {tookBlockquoteStyle?: boolean; isPlainParagraph?: boolean},\n ) => {\n flushBlock()\n currentBlock = {\n _type: 'block' as const,\n style,\n children: [],\n _key: consolidatedOptions.keyGenerator(),\n markDefs: [],\n }\n currentMarkDefs = []\n currentBlockTookBlockquoteStyle = provenance?.tookBlockquoteStyle ?? false\n currentBlockIsPlainParagraph = provenance?.isPlainParagraph ?? false\n }\n\n const flushBlock = () => {\n if (!currentBlock) {\n return\n }\n\n // A callout with pending style fallbacks (its own style resolution\n // declined) only actually degrades once a text block actually commits\n // needing that resolved style. A heading inside a callout commits with\n // its own heading style and never touches the callout's content style,\n // so it doesn't count; a plain paragraph does, since paragraphs adopt\n // `currentBlockquoteStyle` directly. A callout holding nothing but,\n // say, a standalone image discards its placeholder block without ever\n // reaching here (see the `inline` case's standalone-image branch), and\n // never degraded.\n if (\n calloutPendingStyleFallbacks.length > 0 &&\n currentBlockTookBlockquoteStyle\n ) {\n for (const name of calloutPendingStyleFallbacks) {\n reportStyleFallback(name, calloutStartLine)\n }\n calloutPendingStyleFallbacks = []\n }\n\n // Text blocks must have at least one child span\n if (currentBlock.children.length === 0) {\n currentBlock.children.push({\n _type: consolidatedOptions.schema.span.name,\n _key: consolidatedOptions.keyGenerator(),\n text: '',\n marks: [],\n })\n }\n\n // Assign accumulated markDefs to the block\n currentBlock.markDefs = currentMarkDefs\n\n if (currentBlockIsPlainParagraph) {\n plainParagraphBlocks.add(currentBlock)\n }\n\n pushBlock(currentBlock)\n\n currentBlock = null\n currentMarkDefs = []\n }\n\n const addSpan = (text: string) => {\n if (text.length === 0) {\n return\n }\n\n if (!currentBlock) {\n const style =\n currentBlockquoteStyle ??\n consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!style) {\n reportStyleFallback('normal')\n startBlock('normal', {isPlainParagraph: true})\n } else {\n startBlock(style, {\n tookBlockquoteStyle: currentBlockquoteStyle !== null,\n isPlainParagraph: true,\n })\n }\n }\n\n if (!currentBlock) {\n throw new Error('Expected current block')\n }\n\n const lastChild = currentBlock.children.at(-1)\n\n if (\n isSpan({schema: consolidatedOptions.schema}, lastChild) &&\n lastChild.marks?.every((mark) => markDefRefs.includes(mark)) &&\n markDefRefs.every((mark) => lastChild.marks?.includes(mark))\n ) {\n // Merge with previous span if marks match\n lastChild.text += text\n } else {\n currentBlock.children.push({\n _type: consolidatedOptions.schema.span.name,\n _key: consolidatedOptions.keyGenerator(),\n text: text,\n marks: [...markDefRefs],\n })\n }\n }\n\n // Helpers for lists\n const listLevel = () => currentListStack.length\n const ensureListBlock = (listItem: string, checked?: boolean) => {\n if (!currentBlock) {\n // Use blockquote style if inside a blockquote, otherwise use normal style\n const style =\n currentBlockquoteStyle ??\n consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!style) {\n reportStyleFallback('normal')\n startBlock('normal')\n } else {\n startBlock(style, {\n tookBlockquoteStyle: currentBlockquoteStyle !== null,\n })\n }\n }\n\n if (!currentBlock) {\n throw new Error('Expected current block')\n }\n\n if (\n currentBlock.listItem !== listItem ||\n currentBlock.level !== listLevel()\n ) {\n currentBlock.listItem = listItem\n currentBlock.level = listLevel()\n }\n\n if (checked !== undefined) {\n ;(currentBlock as PortableTextTextBlock & {checked?: boolean}).checked =\n checked\n }\n }\n\n // Walk tokens\n for (let tokenIndex = 0; tokenIndex < tokens.length; tokenIndex++) {\n const token = tokens[tokenIndex]\n if (!token) {\n continue\n }\n\n switch (token.type) {\n // Paragraphs\n case 'paragraph_open': {\n // If we're in a list item but have no current block (e.g., after a code block),\n // we need to create a new list item block\n if (inListItem) {\n // Structural list path: start a plain text block; the paragraph\n // lands in the current list item's `content` via `pushBlock`.\n if (listContainerStack.at(-1)) {\n if (!currentBlock) {\n // Use blockquote style if inside a blockquote, otherwise use normal style\n const style =\n currentBlockquoteStyle ??\n consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!style) {\n reportStyleFallback('normal', lineOf(token))\n }\n\n startBlock(style ?? 'normal', {\n tookBlockquoteStyle: currentBlockquoteStyle !== null,\n isPlainParagraph: true,\n })\n }\n break\n }\n\n // Flat list path: ensure the current text block carries\n // `listItem` + `level` fields.\n if (!currentBlock) {\n const listType = currentListStack.at(-1)\n\n if (listType) {\n ensureListBlock(listType)\n }\n }\n\n break\n }\n\n // Use blockquote style if inside a blockquote, otherwise use normal style\n const style =\n currentBlockquoteStyle ??\n consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!style) {\n reportStyleFallback('normal', lineOf(token))\n startBlock('normal', {isPlainParagraph: true})\n break\n }\n\n startBlock(style, {\n tookBlockquoteStyle: currentBlockquoteStyle !== null,\n isPlainParagraph: true,\n })\n break\n }\n case 'paragraph_close':\n // In a flat list item: skip flushing, list_item_close will flush.\n // In a structural list item: flush so multiple paragraphs in one\n // item land as separate text blocks.\n if (inListItem) {\n if (listContainerStack.at(-1)) {\n flushBlock()\n }\n break\n }\n flushBlock()\n break\n\n // Headings\n case 'heading_open': {\n const level = Number(token?.tag?.slice(1))\n\n // Map level to the appropriate heading matcher\n const headingMatchers = {\n 1: consolidatedOptions.block.h1,\n 2: consolidatedOptions.block.h2,\n 3: consolidatedOptions.block.h3,\n 4: consolidatedOptions.block.h4,\n 5: consolidatedOptions.block.h5,\n 6: consolidatedOptions.block.h6,\n } as const\n\n const headingMatcher =\n headingMatchers[level as keyof typeof headingMatchers]\n\n const headingStyle = headingMatcher?.({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!headingStyle) {\n reportStyleFallback(\n `h${level}`,\n lineOf(token),\n tokens[tokenIndex + 1]?.content,\n )\n }\n\n const style =\n headingStyle ??\n consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!style) {\n reportStyleFallback('normal', lineOf(token))\n startBlock('normal')\n break\n }\n\n startBlock(style)\n break\n }\n case 'heading_close':\n flushBlock()\n break\n\n // Blockquote\n case 'blockquote_open': {\n // Flush any current block before entering blockquote\n flushBlock()\n\n // Structural-blockquote path: when the consumer registers a\n // `types.blockquote` matcher, plain blockquotes (NOT GFM alerts -\n // those use separate `alert_open`/`alert_close` tokens) become\n // block-objects with an explicit `content` array. Block emissions\n // inside the open/close pair are spliced out at close time and\n // wrapped in a `blockquote` block-object.\n if (consolidatedOptions.types.blockquote) {\n const startTarget = blockTarget()\n blockquoteStack.push({\n startTarget,\n startIndex: startTarget.length,\n line: lineOf(token),\n })\n break\n }\n\n // Flat path: set the blockquote style for paragraphs inside the\n // blockquote so they emit text blocks with `style: 'blockquote'`.\n const blockquoteStyle = consolidatedOptions.block.blockquote({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!blockquoteStyle) {\n reportStyleFallback('blockquote', lineOf(token))\n }\n\n const style =\n blockquoteStyle ??\n consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!style) {\n reportStyleFallback('normal', lineOf(token))\n }\n\n currentBlockquoteStyle = style ?? 'normal'\n break\n }\n case 'blockquote_close': {\n // Flush any blockquote content before exiting\n flushBlock()\n\n // Structural path: pop the topmost frame and splice its captured\n // content into a `blockquote` block-object via the matcher. If the\n // matcher returns undefined, fall back to flat-style by re-emitting\n // the content blocks with `style: 'blockquote'`.\n if (\n consolidatedOptions.types.blockquote &&\n blockquoteStack.length > 0\n ) {\n const frame = blockquoteStack.pop()\n if (frame) {\n const contentBlocks = frame.startTarget.splice(\n frame.startIndex,\n ) as Array<PortableTextBlock>\n\n const blockquoteObject = consolidatedOptions.types.blockquote({\n context: {\n schema: consolidatedOptions.schema,\n keyGenerator: consolidatedOptions.keyGenerator,\n },\n value: {content: contentBlocks},\n isInline: false,\n })\n\n if (blockquoteObject) {\n pushBlock(blockquoteObject)\n } else {\n // Matcher returned undefined: fall back to exactly what the\n // flat path would have produced (never registering\n // `types.blockquote` at all), including which events it would\n // have reported. Only plain paragraphs pick up the blockquote\n // style there; headings and fallback-to-text blocks (a\n // degraded code fence, say) keep whatever style they already\n // resolved to, so restyling is gated on `plainParagraphBlocks`\n // provenance, not the block's resolved style name.\n const blockquoteStyle = consolidatedOptions.block.blockquote({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!blockquoteStyle) {\n reportStyleFallback('blockquote', frame.line)\n }\n\n const resolvedStyle =\n blockquoteStyle ??\n consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!resolvedStyle) {\n reportStyleFallback('normal', frame.line)\n }\n\n const fallbackStyle = resolvedStyle ?? 'blockquote'\n for (const block of contentBlocks) {\n if (\n block._type === 'block' &&\n plainParagraphBlocks.has(block as PortableTextTextBlock)\n ) {\n const restyledBlock: PortableTextTextBlock = {\n ...(block as PortableTextTextBlock),\n style: fallbackStyle,\n }\n // The spread above makes a new object, so the enclosing\n // structural list's stampable gate (keyed on\n // `plainParagraphBlocks` object identity) would otherwise\n // lose this block's provenance across the restyle.\n plainParagraphBlocks.add(restyledBlock)\n pushBlock(restyledBlock)\n } else {\n pushBlock(block)\n }\n }\n }\n }\n break\n }\n\n currentBlockquoteStyle = null\n break\n }\n // Lists\n case 'bullet_list_open': {\n flushBlock()\n\n // Structural-container path: when the consumer registers a\n // `types.list` matcher, lists become block-objects with explicit\n // `items` arrays. Block emissions inside list items are diverted\n // into `currentItem.content` via `blockTarget()`. Mirrors the\n // `types.table` pattern.\n if (consolidatedOptions.types.list) {\n listContainerStack.push({\n kind: 'bullet',\n items: [],\n currentItem: null,\n line: lineOf(token),\n })\n currentListStack.push(null)\n pendingListFlattenedStack.push(null)\n break\n }\n\n // Flat path: lists are reconstructed from text blocks with\n // `listItem` + `level` fields at render time.\n const listItem = consolidatedOptions.listItem.bullet({\n context: {schema: consolidatedOptions.schema},\n })\n\n listContainerStack.push(null)\n if (!listItem) {\n pendingListFlattenedStack.push({\n line: lineOf(token),\n kindName: 'bullet',\n reported: false,\n })\n currentListStack.push(null)\n break\n }\n pendingListFlattenedStack.push(null)\n currentListStack.push(listItem)\n break\n }\n case 'ordered_list_open': {\n flushBlock()\n\n if (consolidatedOptions.types.list) {\n listContainerStack.push({\n kind: 'number',\n items: [],\n currentItem: null,\n line: lineOf(token),\n })\n currentListStack.push(null)\n pendingListFlattenedStack.push(null)\n break\n }\n\n const listItem = consolidatedOptions.listItem.number({\n context: {schema: consolidatedOptions.schema},\n })\n\n listContainerStack.push(null)\n if (!listItem) {\n pendingListFlattenedStack.push({\n line: lineOf(token),\n kindName: 'number',\n reported: false,\n })\n currentListStack.push(null)\n break\n }\n pendingListFlattenedStack.push(null)\n currentListStack.push(listItem)\n break\n }\n case 'bullet_list_close':\n case 'ordered_list_close': {\n const frame = listContainerStack.pop()\n currentListStack.pop()\n pendingListFlattenedStack.pop()\n\n // Structural close: materialize the list block-object and push it\n // into the enclosing target (parent list item's content if nested,\n // else top-level).\n if (frame && consolidatedOptions.types.list) {\n // Promote `kind` to 'task' if any item carries a checked state.\n const kind: 'bullet' | 'number' | 'task' = frame.items.some(\n (item) => 'checked' in item,\n )\n ? 'task'\n : frame.kind\n\n const listObject = consolidatedOptions.types.list({\n context: {\n schema: consolidatedOptions.schema,\n keyGenerator: consolidatedOptions.keyGenerator,\n },\n value: {kind, items: frame.items},\n isInline: false,\n })\n\n if (listObject) {\n pushBlock(listObject)\n } else {\n // Matcher returned undefined: fall back to exactly what the\n // flat path would have produced for this same content (never\n // registering `types.list` at all), including which events it\n // would have reported. A decline whose mirrored flat form is\n // lossless (the schema has every list kind this list needs)\n // reports nothing: going structural and being declined isn't\n // itself a degradation, only losing information is.\n const kindListItem =\n (frame.kind === 'number'\n ? consolidatedOptions.listItem.number\n : consolidatedOptions.listItem.bullet)({\n context: {schema: consolidatedOptions.schema},\n }) ?? null\n const taskListItemType =\n consolidatedOptions.listItem.task?.({\n context: {schema: consolidatedOptions.schema},\n }) ?? null\n const kindName = frame.kind === 'number' ? 'number' : 'bullet'\n\n // The just-popped list was nested at depth = remaining stack\n // length + 1 (since we already popped).\n const level = listContainerStack.length + 1\n let flattenedReported = false\n\n for (const item of frame.items) {\n let itemListType: string | null = kindListItem\n let itemChecked: boolean | undefined\n\n if (item.checked !== undefined) {\n if (taskListItemType) {\n itemListType = taskListItemType\n itemChecked = item.checked\n } else if (kindListItem !== null) {\n const info = taskInfoByListItem.get(item)\n report({\n type: 'task-checkbox-stripped',\n message: degradationMessage['task-checkbox-stripped'](\n item.checked,\n ),\n line: info?.line,\n snippet: info?.snippet,\n })\n }\n }\n\n if (itemListType === null && !flattenedReported) {\n report({\n type: 'list-flattened',\n message: degradationMessage['list-flattened'](kindName),\n line: frame.line,\n })\n flattenedReported = true\n }\n\n // Adjacent plain-paragraph blocks within one item merge into\n // a single block, mirroring the flat path: consecutive\n // `paragraph_open`/`paragraph_close` pairs inside a flat list\n // item share one `currentBlock`, since only non-paragraph\n // content (headings, code blocks, ...) flushes it. Resets per\n // item: `list_item_close` always flushes in the flat path, so\n // a merge never crosses an item boundary. Gated on\n // `plainParagraphBlocks` (provenance, not shape): a\n // fallback-to-text block (a degraded code fence, say) never\n // shares `currentBlock` with surrounding text in the flat\n // path either, even when its style happens to match, and the\n // flat path never stamps it with `listItem` at all.\n let mergeTarget: PortableTextTextBlock | null = null\n\n for (const block of item.content) {\n const isStampable =\n itemListType !== null &&\n block._type === 'block' &&\n !('listItem' in block) &&\n !/^h[1-6]$/.test(\n (block as PortableTextTextBlock).style ?? '',\n ) &&\n plainParagraphBlocks.has(block as PortableTextTextBlock)\n\n if (!isStampable) {\n mergeTarget = null\n pushBlock(block)\n continue\n }\n\n const textBlock = block as PortableTextTextBlock\n if (mergeTarget && mergeTarget.style === textBlock.style) {\n mergeTarget.children.push(...textBlock.children)\n mergeTarget.markDefs = [\n ...(mergeTarget.markDefs ?? []),\n ...(textBlock.markDefs ?? []),\n ]\n continue\n }\n\n mergeTarget = {\n ...textBlock,\n listItem: itemListType as string,\n level,\n ...(itemChecked === undefined ? {} : {checked: itemChecked}),\n }\n pushBlock(mergeTarget)\n }\n }\n }\n }\n break\n }\n case 'list_item_open': {\n const frame = listContainerStack.at(-1)\n\n // Flush any previous list item block before starting a new one\n // This is needed for proper separation of list items\n if (currentBlock) {\n flushBlock()\n }\n\n // Structural path: start a new `list-item` whose `content` becomes\n // the divert target for subsequent block emissions until\n // `list_item_close`.\n if (frame) {\n const taskChecked = taskCheckedByListItemIndex.get(tokenIndex)\n frame.currentItem = {\n _type: 'list-item',\n _key: consolidatedOptions.keyGenerator(),\n ...(taskChecked === undefined ? {} : {checked: taskChecked}),\n content: [],\n }\n if (taskChecked !== undefined) {\n taskInfoByListItem.set(frame.currentItem, {\n line: lineOf(token),\n snippet: truncateSnippet(\n taskItemTextByListItemIndex.get(tokenIndex) ?? '',\n ),\n })\n }\n inListItem = true\n break\n }\n\n // Flat path\n const baseListType = currentListStack.at(-1)\n\n if (baseListType === undefined) {\n throw new Error('Expected an open list')\n }\n\n // Resolve task list type and checked state for this specific item.\n // If the schema declares a `task` list definition, GFM checkboxes\n // (`- [ ]` / `- [x]`) override the surrounding list's type for this\n // item. Otherwise the prefix has already been stripped by the\n // pre-pass and we render as the surrounding list type.\n const taskChecked = taskCheckedByListItemIndex.get(tokenIndex)\n let listType = baseListType\n let checked: boolean | undefined\n if (taskChecked !== undefined) {\n const taskListType = consolidatedOptions.listItem.task?.({\n context: {schema: consolidatedOptions.schema},\n })\n if (taskListType) {\n listType = taskListType\n checked = taskChecked\n }\n }\n\n // If listType is null, it means there's no list definition in the schema\n // Just create a normal block without list properties\n if (listType === null) {\n const pendingFlattened = pendingListFlattenedStack.at(-1)\n if (pendingFlattened && !pendingFlattened.reported) {\n report({\n type: 'list-flattened',\n message: degradationMessage['list-flattened'](\n pendingFlattened.kindName,\n ),\n line: pendingFlattened.line,\n })\n pendingFlattened.reported = true\n }\n\n // Use blockquote style if inside a blockquote, otherwise use normal style\n const style =\n currentBlockquoteStyle ??\n consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!style) {\n reportStyleFallback('normal', lineOf(token))\n startBlock('normal')\n } else {\n startBlock(style, {\n tookBlockquoteStyle: currentBlockquoteStyle !== null,\n })\n }\n inListItem = true\n break\n }\n\n if (taskChecked !== undefined && checked === undefined) {\n const itemText = taskItemTextByListItemIndex.get(tokenIndex) ?? ''\n const snippet = truncateSnippet(itemText)\n report({\n type: 'task-checkbox-stripped',\n message: degradationMessage['task-checkbox-stripped'](taskChecked),\n line: lineOf(token),\n snippet,\n })\n }\n\n ensureListBlock(listType, checked)\n inListItem = true\n break\n }\n case 'list_item_close': {\n const frame = listContainerStack.at(-1)\n\n // Structural path: flush any current block into the item's content,\n // then push the completed item onto the frame.\n if (frame && frame.currentItem) {\n flushBlock()\n frame.items.push(frame.currentItem)\n frame.currentItem = null\n inListItem = false\n break\n }\n\n // Flat path\n inListItem = false\n flushBlock()\n break\n }\n\n // Code fences / blocks\n case 'fence': {\n flushBlock()\n\n const language = token.info.trim() || undefined\n // Remove trailing newline from code content\n const code = token.content.replace(/\\n$/, '')\n\n if (language === 'json:object') {\n const objectValue = parseJsonObjectFence(code)\n\n if (objectValue) {\n pushBlock(objectValue as PortableTextObject)\n break\n }\n\n report({\n type: 'object-carrier-invalid',\n message: degradationMessage['object-carrier-invalid'](\n 'fence',\n code,\n ),\n line: lineOf(token),\n snippet: truncateSnippet(code),\n })\n }\n\n const codeObject = consolidatedOptions.types.code({\n context: {\n schema: consolidatedOptions.schema,\n keyGenerator: consolidatedOptions.keyGenerator,\n },\n value: {language, code},\n isInline: false,\n })\n\n if (!codeObject) {\n const snippet = truncateSnippet(code.split('\\n')[0] ?? '')\n report({\n type: 'code-block-to-text',\n message: degradationMessage['code-block-to-text'](language),\n line: lineOf(token),\n snippet,\n })\n\n // Code block not in schema, fall back to text block\n const style = consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!style) {\n reportStyleFallback('normal', lineOf(token))\n startBlock('normal')\n } else {\n startBlock(style)\n }\n\n addSpan(code)\n flushBlock()\n break\n }\n\n reportFieldsDropped(codeObject, lineOf(token))\n pushBlock(codeObject)\n\n break\n }\n\n // Horizontal rule\n case 'hr': {\n flushBlock()\n\n const hrObject = consolidatedOptions.types.horizontalRule({\n context: {\n schema: consolidatedOptions.schema,\n keyGenerator: consolidatedOptions.keyGenerator,\n },\n value: {},\n isInline: false,\n })\n\n if (!hrObject) {\n report({\n type: 'horizontal-rule-to-text',\n message: degradationMessage['horizontal-rule-to-text'],\n line: lineOf(token),\n })\n\n // If there's no break definition in the schema, parse as text\n const style = consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!style) {\n reportStyleFallback('normal', lineOf(token))\n startBlock('normal')\n } else {\n startBlock(style)\n }\n\n addSpan('---')\n flushBlock()\n break\n }\n\n pushBlock(hrObject)\n\n break\n }\n\n // HTML block\n case 'html_block': {\n flushBlock()\n\n const htmlContent = token.content.trim()\n\n if (!htmlContent) {\n break\n }\n\n const htmlObject = consolidatedOptions.types.html({\n context: {\n schema: consolidatedOptions.schema,\n keyGenerator: consolidatedOptions.keyGenerator,\n },\n value: {html: htmlContent},\n isInline: false,\n })\n\n if (!htmlObject) {\n const snippet = truncateSnippet(htmlContent)\n report({\n type: 'html-block-to-text',\n message: degradationMessage['html-block-to-text'],\n line: lineOf(token),\n snippet,\n })\n\n // If there's no HTML block definition in the schema, parse as text\n const style = consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!style) {\n reportStyleFallback('normal', lineOf(token))\n startBlock('normal')\n } else {\n startBlock(style)\n }\n\n addSpan(htmlContent)\n flushBlock()\n break\n }\n\n reportFieldsDropped(htmlObject, lineOf(token))\n pushBlock(htmlObject)\n\n break\n }\n\n case 'code_block': {\n flushBlock()\n\n // Remove trailing newline from code content\n const code = token.content.replace(/\\n$/, '')\n\n const codeObject = consolidatedOptions.types.code({\n context: {\n schema: consolidatedOptions.schema,\n keyGenerator: consolidatedOptions.keyGenerator,\n },\n value: {language: undefined, code},\n isInline: false,\n })\n\n if (!codeObject) {\n const snippet = truncateSnippet(code.split('\\n')[0] ?? '')\n report({\n type: 'code-block-to-text',\n message: degradationMessage['code-block-to-text'](undefined),\n line: lineOf(token),\n snippet,\n })\n\n // Code block not in schema, fall back to text block\n const style = consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!style) {\n reportStyleFallback('normal', lineOf(token))\n startBlock('normal')\n } else {\n startBlock(style)\n }\n\n addSpan(code)\n flushBlock()\n } else {\n reportFieldsDropped(codeObject, lineOf(token))\n pushBlock(codeObject)\n }\n\n break\n }\n\n // Tables\n case 'table_open':\n flushBlock()\n currentTable = {\n rows: [],\n headerRows: 0,\n emptyHeaderDropped: false,\n alignment: [],\n line: lineOf(token),\n }\n break\n\n case 'table_close': {\n if (!currentTable) {\n break\n }\n\n // Only create table object if table type is defined\n if (consolidatedOptions.types.table) {\n const hasAlignment = currentTable.alignment.some((a) => a !== null)\n const tableObject = consolidatedOptions.types.table({\n context: {\n schema: consolidatedOptions.schema,\n keyGenerator: consolidatedOptions.keyGenerator,\n },\n value: {\n rows: currentTable.rows,\n headerRows:\n currentTable.headerRows > 0\n ? currentTable.headerRows\n : currentTable.emptyHeaderDropped\n ? 0\n : undefined,\n alignment: hasAlignment ? currentTable.alignment : undefined,\n },\n isInline: false,\n })\n\n if (tableObject) {\n reportFieldsDropped(tableObject, currentTable.line)\n pushBlock(tableObject)\n } else {\n report({\n type: 'table-flattened',\n message: degradationMessage['table-flattened'],\n line: currentTable.line,\n })\n // If table object couldn't be created, flatten the table\n flattenTable(\n currentTable,\n blockTarget() as Array<PortableTextBlock>,\n )\n }\n } else {\n report({\n type: 'table-flattened',\n message: degradationMessage['table-flattened'],\n line: currentTable.line,\n })\n // If there's no table definition in the schema, flatten the table\n flattenTable(currentTable, blockTarget() as Array<PortableTextBlock>)\n }\n\n currentTable = null\n break\n }\n\n case 'thead_open':\n inTableHead = true\n break\n\n case 'thead_close':\n inTableHead = false\n break\n\n case 'tbody_open':\n case 'tbody_close':\n // Just markers, no action needed\n break\n\n case 'tr_open':\n currentTableRow = []\n break\n\n case 'tr_close':\n if (currentTable && currentTableRow) {\n if (\n inTableHead &&\n isEmptyTableRow(currentTableRow, {\n schema: consolidatedOptions.schema,\n })\n ) {\n // An all-empty header row means \"no header\": drop it and leave\n // `headerRows` at 0 (recorded so `table_close` emits an explicit\n // 0, not `undefined`), so `portableTextToMarkdown`'s headerless\n // output round-trips back to `headerRows: 0`.\n currentTable.emptyHeaderDropped = true\n } else {\n currentTable.rows.push({\n _key: consolidatedOptions.keyGenerator(),\n _type: 'row',\n cells: currentTableRow,\n })\n if (inTableHead) {\n currentTable.headerRows++\n }\n }\n }\n currentTableRow = null\n break\n\n case 'th_open':\n case 'td_open': {\n // Alignment is per-column, set on every cell of that column. Read\n // from the header so each column contributes exactly one entry.\n if (currentTable && inTableHead && token.type === 'th_open') {\n currentTable.alignment.push(\n extractAlignmentFromStyleAttr(stringAttr(token, 'style') ?? null),\n )\n }\n\n // Start a new block for the table cell\n const style = consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!style) {\n reportStyleFallback('normal', currentTable?.line)\n startBlock('normal')\n } else {\n startBlock(style)\n }\n break\n }\n\n case 'th_close':\n case 'td_close': {\n // Flush the current block into the cell\n flushBlock()\n\n // Get all blocks that were added since this cell started\n // We need to extract them from the current target array\n const cellBlocks: Array<PortableTextBlock> = []\n const target = blockTarget()\n\n // Check if we have blocks to extract (added after table_open)\n if (target.length > 0) {\n const lastBlock = target.at(-1)\n if (lastBlock && lastBlock._type === 'block') {\n cellBlocks.push(target.pop()! as PortableTextBlock)\n }\n }\n\n // If no blocks were created (empty cell), create an empty block\n if (cellBlocks.length === 0) {\n cellBlocks.push({\n _type: 'block' as const,\n style:\n consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n }) || 'normal',\n children: [\n {\n _type: consolidatedOptions.schema.span.name,\n _key: consolidatedOptions.keyGenerator(),\n text: '',\n marks: [],\n },\n ],\n _key: consolidatedOptions.keyGenerator(),\n markDefs: [],\n })\n }\n\n // Images pushed as inline children during this cell (see the\n // `inline` case below) were demoted from block-level at push time,\n // before it was known whether the sole-image lift below would\n // recover them losslessly. Collect them so the lift's verdict can\n // decide whether the demotion actually degraded anything.\n const demotedInlineImages: Array<PortableTextObject> = []\n for (const block of cellBlocks) {\n if (\n block._type === 'block' &&\n 'children' in block &&\n Array.isArray(block.children)\n ) {\n for (const child of block.children) {\n if (\n typeof child === 'object' &&\n child !== null &&\n demotedTableImages.has(child as PortableTextObject)\n ) {\n demotedInlineImages.push(child as PortableTextObject)\n }\n }\n }\n }\n\n // A cell holding one block whose only child is an object (not a\n // span) lifts that object to block position, unless the schema\n // declares the type inline-only: that type has a legal inline home,\n // so the block reading would manufacture a placement the schema\n // forbids. Everything else (declared block, declared both, or\n // undeclared) reads as block, matching PT's table model where\n // `cell.value` is an array of blocks.\n const firstBlock = cellBlocks[0]\n let liftedObject: PortableTextObject | undefined\n if (\n cellBlocks.length === 1 &&\n firstBlock &&\n firstBlock._type === 'block' &&\n 'children' in firstBlock &&\n Array.isArray(firstBlock.children) &&\n firstBlock.children.length === 1\n ) {\n const onlyChild = firstBlock.children[0]\n if (\n typeof onlyChild === 'object' &&\n onlyChild !== null &&\n '_type' in onlyChild &&\n onlyChild._type !== consolidatedOptions.schema.span.name\n ) {\n const declaredInline =\n consolidatedOptions.schema.inlineObjects.some(\n (inlineObject) => inlineObject.name === onlyChild._type,\n )\n const declaredBlock = consolidatedOptions.schema.blockObjects.some(\n (blockObject) => blockObject.name === onlyChild._type,\n )\n if (!(declaredInline && !declaredBlock)) {\n cellBlocks[0] = onlyChild as PortableTextBlock\n liftedObject = onlyChild as PortableTextObject\n }\n }\n }\n\n // A demoted image the lift didn't recover is stuck as an inline\n // child of the cell's text block: report it now that the verdict is\n // known, instead of at demotion time.\n for (const demotedImage of demotedInlineImages) {\n if (demotedImage !== liftedObject) {\n const {alt, src} = demotedImage as {alt?: string; src?: string}\n report({\n type: 'image-block-to-inline',\n message: degradationMessage['image-block-to-inline'](\n demotedTableImages.get(demotedImage) ?? 'table-cell',\n ),\n line: currentTable?.line,\n snippet: truncateSnippet(alt || src || ''),\n })\n }\n }\n\n if (currentTableRow !== null) {\n currentTableRow.push({\n _type: 'cell',\n _key: consolidatedOptions.keyGenerator(),\n value: cellBlocks,\n })\n }\n break\n }\n\n // Inline container\n case 'inline': {\n // Check if we're in a table cell\n const inTableCell = currentTableRow !== null\n\n // `inline` tokens inside table cells carry no `map` of their own;\n // fall back to the enclosing table's line.\n const inlineLine = (): number | undefined =>\n lineOf(token) ?? currentTable?.line\n\n // Check if this is a standalone image (paragraph with only an image)\n if (\n token.children?.length === 1 &&\n token.children[0]?.type === 'image'\n ) {\n const imageToken = token.children[0]\n if (!imageToken) {\n break\n }\n\n const src = stringAttr(imageToken, 'src') || ''\n const alt = unescapeImageAndLinkText(imageToken.content || '')\n const title = stringAttr(imageToken, 'title')\n\n const blockImageObject = consolidatedOptions.types.image({\n context: {\n schema: consolidatedOptions.schema,\n keyGenerator: consolidatedOptions.keyGenerator,\n },\n value: {src, alt, title},\n isInline: false,\n })\n\n if (blockImageObject) {\n reportFieldsDropped(blockImageObject, inlineLine())\n if (inTableCell) {\n demotedTableImages.set(\n blockImageObject as PortableTextObject,\n 'table-cell',\n )\n\n // In table cells, we can't push to portableText directly\n // The block image will be handled in th_close/td_close extraction logic\n // For now, add it as a child of the current block\n if (currentBlock && 'children' in currentBlock) {\n ;(currentBlock as PortableTextTextBlock).children.push(\n blockImageObject as PortableTextObject,\n )\n }\n } else {\n // If the current block has content, flush it before adding the block image\n // Otherwise, discard the empty block that was created by paragraph_open\n const hasContent =\n currentBlock &&\n 'children' in currentBlock &&\n (currentBlock as PortableTextTextBlock).children.length > 0\n\n if (hasContent) {\n flushBlock()\n } else {\n currentBlock = null\n currentMarkDefs = []\n }\n pushBlock(blockImageObject)\n }\n break\n }\n\n // Block image not supported, try inline image as fallback\n const inlineImageObject = consolidatedOptions.types.image({\n context: {\n schema: consolidatedOptions.schema,\n keyGenerator: consolidatedOptions.keyGenerator,\n },\n value: {src, alt, title},\n isInline: true,\n })\n\n if (inlineImageObject) {\n reportFieldsDropped(inlineImageObject, inlineLine())\n if (inTableCell) {\n // Defer to the `td_close` sole-image lift: a standalone image\n // that's the cell's only content round-trips back to\n // block-level losslessly, and reporting here would be a false\n // positive for that case.\n demotedTableImages.set(\n inlineImageObject as PortableTextObject,\n 'no-block-image',\n )\n } else {\n report({\n type: 'image-block-to-inline',\n message:\n degradationMessage['image-block-to-inline']('no-block-image'),\n line: inlineLine(),\n snippet: truncateSnippet(alt || src),\n })\n }\n // Ensure we have a block to add the inline image to\n if (!currentBlock) {\n if (inListItem) {\n // Structural list: start a plain text block; the image\n // lands in the current item's content via flushBlock.\n if (listContainerStack.at(-1)) {\n const style = consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!style) {\n reportStyleFallback('normal', inlineLine())\n }\n\n startBlock(style ?? 'normal')\n } else {\n const listType = currentListStack.at(-1)\n\n if (listType) {\n ensureListBlock(listType)\n }\n }\n } else {\n const style = consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (style) {\n startBlock(style)\n }\n }\n }\n\n if (currentBlock && 'children' in currentBlock) {\n ;(currentBlock as PortableTextTextBlock).children.push(\n inlineImageObject as PortableTextObject,\n )\n }\n break\n }\n\n // Neither block nor inline image supported, fall back to text\n const standaloneImageSnippet = truncateSnippet(alt || src)\n report({\n type: 'image-to-text',\n message: degradationMessage['image-to-text'],\n line: inlineLine(),\n snippet: standaloneImageSnippet,\n })\n addSpan(``)\n break\n }\n\n // Walk its children for text/marks/links\n const inlineChildren = token.children ?? []\n for (\n let childIndex = 0;\n childIndex < inlineChildren.length;\n childIndex++\n ) {\n const childToken = inlineChildren[childIndex]\n if (!childToken) {\n continue\n }\n\n switch (childToken.type) {\n case 'text': {\n const nextToken = inlineChildren[childIndex + 1]\n\n if (\n childToken.content.endsWith('json:object') &&\n nextToken?.type === 'code_inline' &&\n currentBlock &&\n 'children' in currentBlock\n ) {\n const objectValue = parseJsonObjectFence(nextToken.content)\n\n if (objectValue) {\n const prefix = childToken.content.slice(\n 0,\n -'json:object'.length,\n )\n\n if (prefix.length > 0) {\n addSpan(prefix)\n }\n\n ;(currentBlock as PortableTextTextBlock).children.push(\n objectValue as PortableTextObject,\n )\n\n childIndex++\n break\n }\n\n report({\n type: 'object-carrier-invalid',\n message: degradationMessage['object-carrier-invalid'](\n 'code-span',\n nextToken.content,\n ),\n line: inlineLine(),\n snippet: truncateSnippet(nextToken.content),\n })\n }\n\n addSpan(childToken.content)\n break\n }\n case 'softbreak':\n addSpan(' ')\n break\n case 'hardbreak':\n addSpan('\\n')\n break\n case 'code_inline': {\n const decorator = consolidatedOptions.marks.code({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!decorator) {\n const codeSnippet = truncateSnippet(childToken.content)\n report({\n type: 'decorator-dropped',\n message: degradationMessage['decorator-dropped']('code'),\n line: inlineLine(),\n snippet: codeSnippet,\n })\n // No code decorator defined, just add the content without marks\n addSpan(childToken.content)\n break\n }\n\n markDefRefs.push(decorator)\n addSpan(childToken.content)\n\n // code_inline is self-contained, so we need to pop the decorator\n const index = markDefRefs.lastIndexOf(decorator)\n\n if (index !== -1) {\n markDefRefs.splice(index, 1)\n }\n\n break\n }\n case 'strong_open': {\n const decorator = consolidatedOptions.marks.strong({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!decorator) {\n const strongSnippet = truncateSnippet(\n collectInlineText(\n inlineChildren,\n childIndex,\n 'strong_open',\n 'strong_close',\n ),\n )\n report({\n type: 'decorator-dropped',\n message: degradationMessage['decorator-dropped']('strong'),\n line: inlineLine(),\n snippet: strongSnippet,\n })\n break\n }\n\n markDefRefs.push(decorator)\n break\n }\n case 'strong_close': {\n const decorator = consolidatedOptions.marks.strong({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!decorator) {\n break\n }\n\n const index = markDefRefs.lastIndexOf(decorator)\n\n if (index !== -1) {\n markDefRefs.splice(index, 1)\n }\n\n break\n }\n case 'em_open': {\n const decorator = consolidatedOptions.marks.em({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!decorator) {\n const emSnippet = truncateSnippet(\n collectInlineText(\n inlineChildren,\n childIndex,\n 'em_open',\n 'em_close',\n ),\n )\n report({\n type: 'decorator-dropped',\n message: degradationMessage['decorator-dropped']('em'),\n line: inlineLine(),\n snippet: emSnippet,\n })\n break\n }\n\n markDefRefs.push(decorator)\n\n break\n }\n case 'em_close': {\n const decorator = consolidatedOptions.marks.em({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!decorator) {\n break\n }\n\n const index = markDefRefs.lastIndexOf(decorator)\n\n if (index !== -1) {\n markDefRefs.splice(index, 1)\n }\n\n break\n }\n case 's_open': {\n const decorator = consolidatedOptions.marks.strikeThrough({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!decorator) {\n const strikeSnippet = truncateSnippet(\n collectInlineText(\n inlineChildren,\n childIndex,\n 's_open',\n 's_close',\n ),\n )\n report({\n type: 'decorator-dropped',\n message:\n degradationMessage['decorator-dropped']('strikeThrough'),\n line: inlineLine(),\n snippet: strikeSnippet,\n })\n break\n }\n\n markDefRefs.push(decorator)\n\n break\n }\n case 's_close': {\n const decorator = consolidatedOptions.marks.strikeThrough({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!decorator) {\n break\n }\n\n const index = markDefRefs.lastIndexOf(decorator)\n\n if (index !== -1) {\n markDefRefs.splice(index, 1)\n }\n\n break\n }\n case 'link_open': {\n const href = stringAttr(childToken, 'href')\n\n if (!href) {\n const missingHrefSnippet = truncateSnippet(\n collectInlineText(\n inlineChildren,\n childIndex,\n 'link_open',\n 'link_close',\n ),\n )\n report({\n type: 'annotation-dropped',\n message:\n degradationMessage['annotation-dropped']('missing-url'),\n line: inlineLine(),\n snippet: missingHrefSnippet,\n })\n break\n }\n\n const title = stringAttr(childToken, 'title')\n\n const linkObject = consolidatedOptions.marks.link({\n context: {\n schema: consolidatedOptions.schema,\n keyGenerator: consolidatedOptions.keyGenerator,\n },\n value: {href, title},\n })\n\n if (!linkObject) {\n const linkSnippet = truncateSnippet(\n collectInlineText(\n inlineChildren,\n childIndex,\n 'link_open',\n 'link_close',\n ),\n )\n report({\n type: 'annotation-dropped',\n message:\n degradationMessage['annotation-dropped']('no-annotation'),\n line: inlineLine(),\n snippet: linkSnippet,\n })\n break\n }\n\n reportFieldsDropped(linkObject, inlineLine())\n currentMarkDefs.push(linkObject)\n markDefRefs.push(linkObject._key)\n break\n }\n case 'link_close': {\n // remove the last link key\n const markDefKeys = new Set(currentMarkDefs.map((d) => d._key))\n let lastLinkIndex: number | undefined\n\n for (const markDefRef of markDefRefs.reverse()) {\n if (markDefKeys.has(markDefRef)) {\n lastLinkIndex = markDefRefs.indexOf(markDefRef)\n break\n }\n }\n\n if (lastLinkIndex !== undefined) {\n const realIndex = markDefRefs.length - 1 - lastLinkIndex\n markDefRefs.splice(realIndex, 1)\n }\n break\n }\n case 'image': {\n const src = stringAttr(childToken, 'src') || ''\n const alt = unescapeImageAndLinkText(childToken.content || '')\n\n // Try to create an inline image first\n const inlineImageObject = consolidatedOptions.types.image({\n context: {\n schema: consolidatedOptions.schema,\n keyGenerator: consolidatedOptions.keyGenerator,\n },\n value: {src, alt, title: undefined},\n isInline: true,\n })\n\n if (inlineImageObject) {\n reportFieldsDropped(inlineImageObject, inlineLine())\n // Inline image is supported - add it to current block\n if (!currentBlock) {\n const style = consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!style) {\n reportStyleFallback('normal', inlineLine())\n startBlock('normal')\n } else {\n startBlock(style)\n }\n }\n\n // At this point currentBlock should exist\n if (!currentBlock) {\n throw new Error('Expected current block after startBlock')\n }\n\n // Add the image as an inline object (TypeScript assertion needed for type narrowing)\n ;(currentBlock as PortableTextTextBlock).children.push(\n inlineImageObject as PortableTextObject,\n )\n break\n }\n\n // Inline image not supported - try block image as fallback\n const blockImageObject = consolidatedOptions.types.image({\n context: {\n schema: consolidatedOptions.schema,\n keyGenerator: consolidatedOptions.keyGenerator,\n },\n value: {src, alt, title: undefined},\n isInline: false,\n })\n\n if (!blockImageObject) {\n // Neither inline nor block image supported\n const inlineImageSnippet = truncateSnippet(alt || src)\n report({\n type: 'image-to-text',\n message: degradationMessage['image-to-text'],\n line: inlineLine(),\n snippet: inlineImageSnippet,\n })\n addSpan(``)\n break\n }\n\n // Block image supported - flush current block and add as block-level\n // Skip if we're in a table cell (images in cells are handled differently)\n if (inTableCell) {\n reportFieldsDropped(blockImageObject, inlineLine())\n demotedTableImages.set(\n blockImageObject as PortableTextObject,\n 'table-cell',\n )\n\n // In table cells, add the image to current block (will be extracted later)\n if (currentBlock && 'children' in currentBlock) {\n ;(currentBlock as PortableTextTextBlock).children.push(\n blockImageObject as PortableTextObject,\n )\n }\n break\n }\n\n // Not in table - flush current block, add image as block, start new block\n reportFieldsDropped(blockImageObject, inlineLine())\n report({\n type: 'image-inline-to-block',\n message: degradationMessage['image-inline-to-block'],\n line: inlineLine(),\n snippet: truncateSnippet(alt || src),\n })\n flushBlock()\n pushBlock(blockImageObject)\n\n // Start a new block for any remaining content\n const style = consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (style) {\n startBlock(style)\n }\n\n break\n }\n case 'html_inline': {\n // Handle inline HTML based on configuration\n if (consolidatedOptions.html.inline === 'text') {\n addSpan(childToken.content)\n } else if (childToken.content) {\n const htmlInlineSnippet = truncateSnippet(childToken.content)\n report({\n type: 'inline-html-dropped',\n message: degradationMessage['inline-html-dropped'],\n line: inlineLine(),\n snippet: htmlInlineSnippet,\n })\n }\n break\n }\n default:\n // Ignore other inline token types by default\n break\n }\n }\n break\n }\n\n // Callouts (GFM alerts)\n case 'alert_open': {\n flushBlock()\n calloutStartTarget = blockTarget()\n calloutStartIndex = calloutStartTarget.length\n calloutType = token.markup\n calloutStartLine = lineOf(token)\n\n // Set blockquote style so content blocks inside the callout\n // get blockquote styling (used in fallback when callout type\n // is not in the schema)\n const blockquoteStyle = consolidatedOptions.block.blockquote({\n context: {schema: consolidatedOptions.schema},\n })\n\n calloutPendingStyleFallbacks = []\n if (!blockquoteStyle) {\n calloutPendingStyleFallbacks.push('blockquote')\n }\n\n const style =\n blockquoteStyle ??\n consolidatedOptions.block.normal({\n context: {schema: consolidatedOptions.schema},\n })\n\n if (!style) {\n calloutPendingStyleFallbacks.push('normal')\n }\n\n currentBlockquoteStyle = style ?? 'normal'\n break\n }\n\n case 'alert_title': {\n // `alert_open`'s own map starts at the first content line; the\n // marker line (`> [!NOTE]`) is `alert_title`'s. Any pending style\n // fallbacks flush lazily, from `flushBlock`, once a text block\n // actually commits needing the style.\n calloutStartLine = lineOf(token)\n break\n }\n\n case 'alert_close': {\n flushBlock()\n\n // Nothing ever needed the pending style: no text block materialized\n // inside the callout, so nothing degraded.\n calloutPendingStyleFallbacks = []\n\n if (\n calloutStartIndex !== null &&\n calloutType !== null &&\n calloutStartTarget !== null\n ) {\n const contentBlocks = calloutStartTarget.splice(\n calloutStartIndex,\n ) as Array<PortableTextBlock>\n\n const calloutObject = consolidatedOptions.types.callout?.({\n context: {\n schema: consolidatedOptions.schema,\n keyGenerator: consolidatedOptions.keyGenerator,\n },\n value: {tone: calloutType, content: contentBlocks},\n isInline: false,\n })\n\n if (calloutObject) {\n reportFieldsDropped(calloutObject, calloutStartLine)\n pushBlock(calloutObject)\n } else {\n report({\n type: 'callout-fallback',\n message: degradationMessage['callout-fallback'](\n calloutType,\n currentBlockquoteStyle ?? 'normal',\n ),\n line: calloutStartLine,\n })\n for (const block of contentBlocks) {\n pushBlock(block)\n }\n }\n }\n\n calloutStartIndex = null\n calloutStartTarget = null\n calloutType = null\n calloutStartLine = undefined\n currentBlockquoteStyle = null\n break\n }\n\n default:\n break\n }\n }\n\n flushBlock()\n\n if (degradationEvents.length > 0) {\n options?.onDegradation?.({\n degradations: degradationEvents,\n message: buildDegradationMessage(degradationEvents),\n })\n }\n\n return portableText\n}\n\n/**\n * A `json:object` fence always reconstructs its object, schema or no\n * schema: the fence carries its own `_type`, and degrading it to a code\n * block would reintroduce the loss the syntax exists to remove. Returns\n * `undefined` instead of throwing, so an unusable fence falls through to\n * the regular code path.\n */\nfunction parseJsonObjectFence(\n code: string,\n): (Record<string, unknown> & {_type: string}) | undefined {\n let parsed: unknown\n\n try {\n parsed = JSON.parse(code)\n } catch {\n return undefined\n }\n\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n return undefined\n }\n\n const objectValue = parsed as Record<string, unknown>\n\n if (\n typeof objectValue['_type'] !== 'string' ||\n objectValue['_type'].length === 0\n ) {\n return undefined\n }\n\n return objectValue as Record<string, unknown> & {_type: string}\n}\n","import type {PortableTextBlock, Schema} from '@portabletext/schema'\nimport {cleanupEfficiency, makeDiff} from '@sanity/diff-match-patch'\nimport {portableTextToMarkdown} from './from-portable-text/portable-text-to-markdown'\nimport {defaultKeyGenerator} from './key-generator'\nimport {markdownToPortableText} from './to-portable-text/markdown-to-portable-text'\n\n/**\n * @public\n */\nexport type ApplyMarkdownEditOptions = {\n /**\n * One compiled schema governing both conversion directions, the same\n * object the standalone converters accept. Taking it once is what\n * keeps the internal canonical dialect consistent: serializing and\n * reparsing under different schemas would change node counts or\n * types, refuse the origin trace, and reset keys document-wide.\n */\n schema?: Schema\n /**\n * Options for the markdown → Portable Text conversion of\n * `editedMarkdown` (matchers, `keyGenerator`). The same options\n * also govern the internal canonicalization of `storedPortableText`\n * used to align the two documents, except `onDegradation`, which is\n * scoped to `editedMarkdown` alone: `keyGenerator` supplies every\n * fresh key the function mints, both for new content and for the\n * sibling-uniqueness key-rename sweep.\n */\n deserialize?: Omit<\n NonNullable<Parameters<typeof markdownToPortableText>[1]>,\n 'schema'\n >\n /**\n * Options for the Portable Text → markdown conversion. Pass the same\n * options that produced the markdown that was edited: key\n * resolution aligns the edit against a fresh canonical serialization\n * of `storedPortableText`, so the two serializations must agree for\n * that alignment to be meaningful.\n */\n serialize?: Omit<\n NonNullable<Parameters<typeof portableTextToMarkdown>[1]>,\n 'schema'\n >\n /**\n * Reports how stored `_key`s were reconciled onto the converted\n * value. Reconciliation here is key restoration against the exact\n * snapshot that produced the markdown, never a merge of concurrent\n * edits: those are the caller's job (see the README's Concurrent\n * edits section). Called at most once, and exactly once whenever the\n * option is set and `applyMarkdownEdit` returns (a conversion that\n * throws never reports), synchronously, immediately before the\n * return, including when key matching is skipped outright: that is\n * when a caller needs the report most. A node absent from a performed report's\n * `preservedKeys` was not preserved from the stored value, whether\n * it is a fresh key or a `json:object` payload key that carried its\n * own; the report does not distinguish the two. Which keys\n * survived, every `key` and `path`, and `renamedKeys` are facts of\n * that invocation, safe to branch on for that invocation; a skipped\n * report's `reason` is advisory only, since near the evidence caps\n * it can vary with machine speed, and should never drive behavior.\n *\n * @beta\n */\n onReconciliation?: (report: ReconciliationReport) => void\n}\n\n/**\n * A path segment into the value `applyMarkdownEdit` returns: a string\n * field name for container nesting (`rows`, `cells`, `value`, and the\n * like), `{_key}` wherever the path lands on a keyed array element,\n * and a number wherever it lands on a keyless one instead. A block's\n * own path is a single `{_key}` segment, or a single number for a\n * keyless top-level block; a child's path is\n * `[{_key: <block key>}, 'children', {_key: <child key>}]`. A keyless\n * node (a `json:object` payload without a `_key` of its own, at the\n * top level or wrapping keyed content in a nested array) contributes\n * its index as a number segment instead of a `{_key}` segment.\n *\n * @beta\n */\nexport type ReconciliationKeyPath = Array<{_key: string} | string | number>\n\n/**\n * What `applyMarkdownEdit` did with every key, delivered through\n * `onReconciliation`. `keyMatching: 'skipped'` means key matching gave\n * up on the whole document rather than at some local point: the\n * returned value is the plain markdown→Portable Text conversion, so\n * every key in it is fresh except a `json:object` payload key carried\n * through the markdown verbatim, and no preserved key or local key\n * fallback can have happened. `preservedKeys` and `renamedKeys` list\n * their entries in document order, each block before its children and\n * its mark definitions after them; `keyFallbacks` groups by kind\n * instead: `ambiguous-region-too-large` entries in the order their\n * gaps were resolved, then `annotation-key-conflict` entries. `basis`\n * names the matching method that preserved a key, not the edit's\n * semantics: a swap reports one block `content-moved` and its partner\n * `content-unchanged`. The report carries no edit intent: a deleted\n * stored node is not listed at all, so derive deletions as stored\n * keys minus the keys `applyMarkdownEdit` returned.\n *\n * @beta\n */\nexport type ReconciliationReport =\n | {\n keyMatching: 'performed'\n /**\n * One entry per node whose stored `_key` key resolution\n * preserved, at every depth (blocks, spans and other inline\n * children, and mark definitions). A node absent from this list\n * did not have its key preserved from the stored value: that\n * covers both a freshly generated key and a `json:object`\n * payload key carried through the markdown verbatim, which the\n * report does not distinguish. `basis` names the tier that\n * matched: `content-unchanged` for an exact content match (a\n * block-level anchor, a uniquely matched child, a preserved\n * empty-block run, or a mark definition uniquely matched by\n * content); `content-moved` for a unique leftover matched\n * across gaps; `content-split` for a fragment that kept its\n * source block's key; `content-merged` for a merge that kept\n * its first contributor's key; `same-position` for an\n * equal-count in-order pairing (also a same-content\n * mark-definition tie, or a single leftover mark definition\n * paired positionally); `similar-content` for a mutual-best\n * match under an unequal count.\n */\n preservedKeys: Array<{\n basis:\n | 'content-unchanged'\n | 'content-moved'\n | 'content-split'\n | 'content-merged'\n | 'same-position'\n | 'similar-content'\n key: string\n path: ReconciliationKeyPath\n }>\n /**\n * One entry per local point where key resolution gave up rather\n * than adopt, while the rest of the document still resolves\n * keys: `ambiguous-region-too-large` when a gap's residual\n * similarity pairing crossed the evidence pair cap, listing the\n * affected result block keys that fell back to fresh;\n * `annotation-key-conflict` when an adopted mark definition key\n * would have collided with a sibling. The span-merge pair cap,\n * the per-diff similarity timeout, and the gap's earlier\n * concatenation-search cap degrade the same way but are not\n * reported: `ambiguous-region-too-large` covers the residual\n * similarity tier only (a gap that trips the concatenation cap\n * trips it too).\n */\n keyFallbacks: Array<\n | {type: 'ambiguous-region-too-large'; keys: Array<string>}\n | {type: 'annotation-key-conflict'; path: ReconciliationKeyPath}\n >\n /**\n * One entry per key `applyMarkdownEdit` rewrote to keep\n * siblings unique, most commonly a `json:object` payload\n * duplicating a key already present elsewhere in the document.\n */\n renamedKeys: Array<{\n previousKey: string\n key: string\n path: ReconciliationKeyPath\n }>\n }\n | {\n keyMatching: 'skipped'\n /**\n * `round-trip-mismatch` when the stored value's own\n * canonicalization changed its node count, type sequence, or\n * per-position text; `document-too-large` when the document\n * holds more distinct block forms than alignment can token.\n * Advisory only, since near the evidence caps it can vary with\n * machine speed, and should never drive behavior.\n */\n reason: 'round-trip-mismatch' | 'document-too-large'\n /**\n * One entry per key `applyMarkdownEdit` rewrote to keep\n * siblings unique, most commonly a `json:object` payload\n * duplicating a key already present elsewhere in the document.\n * The sibling-uniqueness pass still runs on a skipped document:\n * a pasted duplicate `json:object` fence key still gets\n * renamed even though nothing else adopted.\n */\n renamedKeys: Array<{\n previousKey: string\n key: string\n path: ReconciliationKeyPath\n }>\n }\n\n/**\n * Deliberately high pending calibration against real agent edit\n * traces: a wrong match moves anchors onto unrelated text, a fresh\n * key resets one block.\n */\nconst MIN_BLOCK_SIMILARITY = 0.8\n\n/**\n * Caps for the split, merge, and similarity tiers: above this many\n * pairs, or past this per-diff time, evidence gathering stops and the\n * affected blocks get new keys, the safe failure mode. Without the\n * caps a single large unequal gap of long unique texts is quadratic in\n * both concatenation search and similarity scoring, and runs for tens\n * of seconds.\n */\nconst MAX_SIMILARITY_PAIRS = 2500\nconst SIMILARITY_DIFF_TIMEOUT_SECONDS = 0.05\n\n/**\n * Bounded attempts at a caller-supplied `keyGenerator` before falling\n * back to deterministic suffixing: a generator that always returns\n * the same value (a constant stub, a buggy sequence) would otherwise\n * spin the collision loop forever.\n */\nconst MAX_KEY_GENERATOR_ATTEMPTS = 3\n\n/**\n * One block, one UTF-16 code unit: past this many distinct block\n * forms the token alphabet would wrap into collisions and surrogate\n * territory, so alignment gives up and every key stays fresh.\n */\nconst MAX_DISTINCT_BLOCK_FORMS = 55_000\n\n/**\n * Converts edited markdown to Portable Text, restores stored keys, and\n * restores fields the markdown dialect cannot express (dropped by\n * serialization, so the edit could not have touched them); a field\n * markdown does express follows the edit. The same rule covers a\n * custom style or decorator markdown has no syntax for, coerced to a\n * built-in on the round trip. An empty or whitespace-only text block\n * is the same case taken to the whole block: markdown has no form for\n * either, so it is restored next to its surviving neighbor and\n * dropped along with that neighbor if the neighbor does not survive.\n * Keys aim for what the\n * same edit would have produced in an editor:\n * unchanged, moved, and rewritten-in-place content keeps its keys\n * (rewriting a paragraph in place keeps its identity, like typing over\n * it), a split keeps the key on its first non-empty fragment, a merge\n * keeps the first source block's key, and a `json:object` payload keeps\n * the key it carries, unless reconciliation matches it to stored\n * content, which takes the stored key even over a differing key in the\n * payload. When an insertion or deletion makes positions ambiguous,\n * only clear similarity evidence adopts a key and everything else gets\n * a new one; gathering that evidence is time-capped, so on very large\n * ambiguous edits the set of adopted keys can differ across machine\n * speeds, degrading toward fresh keys.\n * Output keys are unique among siblings. The function does not mutate\n * `storedPortableText` and returns a value, not patches. The `schema`\n * is taken once and governs both directions; pass the same `serialize`\n * options that produced the markdown that was edited. A throwing or\n * stateful custom matcher or renderer propagates or degrades matching\n * respectively.\n * Reconciliation never merges concurrent edits: compare the stored\n * field against the live document before writing the result back.\n * The trades in one line: same-position replacement inherits identity,\n * a count-preserving rewrite pairs positionally (block-level and\n * sibling-level alike), and evidence gathering is capped, degrading to\n * fresh keys.\n *\n * @public\n */\nexport function applyMarkdownEdit(\n storedPortableText: ReadonlyArray<PortableTextBlock>,\n editedMarkdown: string,\n options?: ApplyMarkdownEditOptions,\n): Array<PortableTextBlock> {\n const onReconciliation = options?.onReconciliation\n const recorder: ReconciliationRecorder | undefined = onReconciliation\n ? {\n preservationBasis: new WeakMap(),\n renamePreviousKey: new WeakMap(),\n annotationKeyConflicts: [],\n ambiguousRegionGroups: [],\n skipReason: undefined,\n }\n : undefined\n const result = structuredClone(\n markdownToPortableText(editedMarkdown, {\n ...options?.deserialize,\n schema: options?.schema,\n }),\n ) as unknown as Array<Node>\n const canonical = canonicalizeStored(storedPortableText, options)\n const nonEmptyStored = nonEmptyBlocks(storedPortableText, options)\n const originOf = traceOrigins(nonEmptyStored, canonical, recorder)\n const adoptedNodes: AdoptedNodes = new WeakSet()\n if (originOf) {\n const alignment = alignBlocks(canonical, result, recorder)\n if (alignment) {\n adoptAnchors(alignment.anchors, originOf, result, adoptedNodes, recorder)\n const gaps = adoptMoves(\n alignment.gaps,\n canonical,\n result,\n originOf,\n adoptedNodes,\n recorder,\n )\n for (const gap of gaps) {\n resolveGap(\n gap.storedIndexes,\n gap.editedIndexes,\n canonical,\n result,\n originOf,\n adoptedNodes,\n recorder,\n )\n }\n // Inside the successful trace only: a document-wide skip of key\n // matching means nothing adopts, and a reinserted empty block\n // would adopt its stored key past that skip, since its anchor\n // can still be found whenever a `json:object` payload carries\n // the neighboring key through the markdown verbatim.\n reinsertEmptyRuns(\n storedPortableText,\n result,\n adoptedNodes,\n options,\n recorder,\n )\n }\n }\n enforceSiblingKeyUniqueness(\n result,\n options?.deserialize?.keyGenerator ?? defaultKeyGenerator,\n adoptedNodes,\n recorder,\n )\n if (recorder && onReconciliation) {\n onReconciliation(buildReconciliationReport(result, recorder))\n }\n return result as unknown as Array<PortableTextBlock>\n}\n\ntype Node = Record<string, unknown>\n\n/**\n * Adoption targets, so the deduplication pass can tell an adopted\n * (authoritative) key from a verbatim payload duplicate.\n */\ntype AdoptedNodes = WeakSet<object>\n\ntype PreservationBasis = Extract<\n ReconciliationReport,\n {keyMatching: 'performed'}\n>['preservedKeys'][number]['basis']\n\n/**\n * Accumulates key resolution decisions against node identity as the\n * passes make them; keys and paths are only meaningful once the\n * sibling-uniqueness pass has settled every `_key`, so materializing\n * the public report happens afterward, in `buildReconciliationReport`.\n * `undefined` when the caller passed no `onReconciliation`, so every\n * call site can skip its recording work with a plain optional-chain.\n */\ntype ReconciliationRecorder = {\n preservationBasis: WeakMap<Node, PreservationBasis>\n renamePreviousKey: WeakMap<Node, string>\n annotationKeyConflicts: Array<Node>\n ambiguousRegionGroups: Array<Array<Node>>\n skipReason: 'round-trip-mismatch' | 'document-too-large' | undefined\n}\n\n/**\n * Re-expresses the stored value in the parser's dialect by serializing\n * it and parsing it right back: the parser merges same-mark sibling\n * spans, reorders marks, collapses list levels, and fills defaults, so\n * content the edit never touched only deep-equals its parsed\n * counterpart after both sides have been through the same round trip.\n * The replacement keys are positional (`__canonical_<n>`), so a match\n * against canonical node `n` can be traded back for stored node `n`'s\n * real `_key`.\n */\nfunction canonicalizeStored(\n stored: ReadonlyArray<PortableTextBlock>,\n options: ApplyMarkdownEditOptions | undefined,\n): Array<Node> {\n const storedClone = structuredClone(stored) as Array<PortableTextBlock>\n const storedMarkdown = portableTextToMarkdown(storedClone, {\n ...options?.serialize,\n schema: options?.schema,\n })\n const {onDegradation, ...canonicalDeserializeOptions} =\n options?.deserialize ?? {}\n let canonicalKeyCounter = 0\n return markdownToPortableText(storedMarkdown, {\n ...canonicalDeserializeOptions,\n schema: options?.schema,\n keyGenerator: () => `__canonical_${canonicalKeyCounter++}`,\n }) as unknown as Array<Node>\n}\n\n/**\n * Positional pairing between `stored` and `canonical` is only\n * trustworthy when serialization preserved the node count and the\n * type sequence; when it did not (heading hard-break splits, lossy\n * table normalization), no key can be traced back to its owner, so\n * nothing adopts. `stored` is already the non-empty subsequence: empty\n * text blocks have no markdown form, so `canonical` never carries them\n * either, and `reinsertEmptyRuns` restores them afterward.\n */\nfunction traceOrigins(\n stored: ReadonlyArray<PortableTextBlock>,\n canonical: ReadonlyArray<Node>,\n recorder: ReconciliationRecorder | undefined,\n): ((canonicalIndex: number) => Node) | undefined {\n if (canonical.length !== stored.length) {\n if (recorder) {\n recorder.skipReason = 'round-trip-mismatch'\n }\n return undefined\n }\n for (let index = 0; index < stored.length; index++) {\n if ((stored[index] as Node)['_type'] !== canonical[index]?.['_type']) {\n if (recorder) {\n recorder.skipReason = 'round-trip-mismatch'\n }\n return undefined\n }\n }\n for (let index = 0; index < stored.length; index++) {\n const storedNode = stored[index] as unknown as Node\n const canonicalNode = canonical[index]!\n if (isTextBlock(storedNode) && isTextBlock(canonicalNode)) {\n if (blockText(storedNode).trim() !== blockText(canonicalNode).trim()) {\n // CommonMark trims whitespace on reparse, so text identity\n // compares trimmed text.\n if (recorder) {\n recorder.skipReason = 'round-trip-mismatch'\n }\n return undefined\n }\n }\n }\n return (canonicalIndex: number): Node =>\n stored[canonicalIndex] as unknown as Node\n}\n\ntype Anchor = {canonicalIndex: number; resultIndex: number}\ntype Gap = {storedIndexes: Array<number>; editedIndexes: Array<number>}\n\n/**\n * Equal runs become anchor pairs (so repeated content pairs\n * first-to-first when adopted), and the delete/insert runs between\n * them form the gaps.\n */\nfunction alignBlocks(\n canonical: ReadonlyArray<Node>,\n result: ReadonlyArray<Node>,\n recorder: ReconciliationRecorder | undefined,\n): {anchors: Array<Anchor>; gaps: Array<Gap>} | undefined {\n const tokenByNeutral = new Map<string, string>()\n const tokenOf = (node: Node): string => {\n const neutral = neutralForm(node)\n let token = tokenByNeutral.get(neutral)\n if (token === undefined) {\n token = String.fromCharCode(tokenByNeutral.size + 1)\n tokenByNeutral.set(neutral, token)\n }\n return token\n }\n const canonicalTokens = canonical.map(tokenOf).join('')\n const resultTokens = result.map(tokenOf).join('')\n if (tokenByNeutral.size > MAX_DISTINCT_BLOCK_FORMS) {\n if (recorder) {\n recorder.skipReason = 'document-too-large'\n }\n return undefined\n }\n // The tenth distinct block form draws `'\\n'` as its token, and past\n // 100 tokens per side `makeDiff`'s line-mode heuristic would split on\n // it, making alignment shape depend on which block form drew that\n // character. Blocks are already atomic, so line mode adds nothing.\n const diffs = makeDiff(canonicalTokens, resultTokens, {checkLines: false})\n\n const anchors: Array<Anchor> = []\n const gaps: Array<Gap> = []\n let gap: Gap = {storedIndexes: [], editedIndexes: []}\n const flushGap = () => {\n if (gap.storedIndexes.length > 0 || gap.editedIndexes.length > 0) {\n gaps.push(gap)\n gap = {storedIndexes: [], editedIndexes: []}\n }\n }\n\n let canonicalIndex = 0\n let resultIndex = 0\n for (const [operation, text] of diffs) {\n if (operation === 0) {\n flushGap()\n for (let offset = 0; offset < text.length; offset++) {\n anchors.push({canonicalIndex, resultIndex})\n canonicalIndex++\n resultIndex++\n }\n } else if (operation === -1) {\n for (let offset = 0; offset < text.length; offset++) {\n gap.storedIndexes.push(canonicalIndex++)\n }\n } else {\n for (let offset = 0; offset < text.length; offset++) {\n gap.editedIndexes.push(resultIndex++)\n }\n }\n }\n flushGap()\n\n return {anchors, gaps}\n}\n\nfunction adoptAnchors(\n anchors: ReadonlyArray<Anchor>,\n originOf: (canonicalIndex: number) => Node,\n result: Array<Node>,\n adoptedNodes: AdoptedNodes,\n recorder: ReconciliationRecorder | undefined,\n): void {\n for (const anchor of anchors) {\n result[anchor.resultIndex] = adoptVerbatim(\n originOf(anchor.canonicalIndex),\n result[anchor.resultIndex]!,\n adoptedNodes,\n 'content-unchanged',\n recorder,\n )\n }\n}\n\n/**\n * An anchor or a unique exact leftover pairs a canonical block against\n * a parsed one that share the same neutral form (that is what put them\n * in the same equal-diff run or the same neutral-form bucket), so\n * everything the dialect can express is untouched and everything it\n * cannot express was invisible to the edit. The stored subtree is the\n * truth at every depth, spans, marks, markDefs, and any field the\n * dialect drops, so adoption replaces the whole node rather than\n * reconciling into the parsed shape (which would otherwise, for\n * instance, keep a parser-side span merge that collapsed an\n * unmappable mark boundary the edit never touched). `restoreFields`\n * and per-child reconciliation are skipped entirely: a verbatim clone\n * of the stored node is already complete.\n */\nfunction adoptVerbatim(\n original: Node,\n target: Node,\n adoptedNodes: AdoptedNodes,\n basis: PreservationBasis,\n recorder: ReconciliationRecorder | undefined,\n): Node {\n const clone = structuredClone(original)\n fillMissingKeysFromTarget(clone, target)\n markSubtreeAdopted(clone, adoptedNodes)\n if (recorder) {\n // The subtree tags every keyed node `content-unchanged`, the root\n // included. A non-default basis overrides the root only: a moved\n // block's children were not themselves moved.\n tagSubtreePreserved(clone, recorder)\n if (basis !== 'content-unchanged' && typeof clone['_key'] === 'string') {\n recorder.preservationBasis.set(clone, basis)\n }\n }\n return clone\n}\n\n/**\n * A stored node practically always carries its own `_key`; when it\n * genuinely does not, there is nothing to adopt, so the clone keeps\n * whatever key the plain parse already minted at the corresponding\n * position, the same key adoption would have left in place. The walk\n * follows both trees positionally (not by content matching, which is\n * exactly what an exact-signature match already guarantees agrees at\n * every position the two sides both still have).\n */\nfunction fillMissingKeysFromTarget(clone: Node, target: Node): void {\n if (typeof clone['_key'] !== 'string' && typeof target['_key'] === 'string') {\n clone['_key'] = target['_key']\n }\n for (const field of Object.keys(clone)) {\n const cloneValue = clone[field]\n const targetValue = target[field]\n if (isTypedObjectArray(cloneValue) && isTypedObjectArray(targetValue)) {\n const length = Math.min(cloneValue.length, targetValue.length)\n for (let index = 0; index < length; index++) {\n fillMissingKeysFromTarget(cloneValue[index]!, targetValue[index]!)\n }\n } else if (\n typeof cloneValue === 'object' &&\n cloneValue !== null &&\n !Array.isArray(cloneValue) &&\n typeof targetValue === 'object' &&\n targetValue !== null &&\n !Array.isArray(targetValue)\n ) {\n fillMissingKeysFromTarget(cloneValue as Node, targetValue as Node)\n }\n }\n}\n\n/**\n * Every keyed node in a verbatim clone is adopted, not only its root:\n * the sibling-key-uniqueness pass recurses into every nested keyed\n * array, and an adopted-first ordering there only favors a clone's\n * descendants if they are themselves marked adopted.\n */\nfunction markSubtreeAdopted(node: Node, adoptedNodes: AdoptedNodes): void {\n adoptedNodes.add(node)\n for (const value of Object.values(node)) {\n if (\n Array.isArray(value) &&\n value.every((item) => typeof item === 'object' && item !== null) &&\n value.length > 0\n ) {\n for (const child of value as Array<Node>) {\n markSubtreeAdopted(child, adoptedNodes)\n }\n } else if (typeof value === 'object' && value !== null) {\n markSubtreeAdopted(value as Node, adoptedNodes)\n }\n }\n}\n\n/**\n * Moves: content that left one gap and reappeared in another. Unique\n * exact pairs across all gaps adopt before any gap-local pairing can\n * consume the keys they need.\n */\nfunction adoptMoves(\n gaps: ReadonlyArray<Gap>,\n canonical: ReadonlyArray<Node>,\n result: Array<Node>,\n originOf: (canonicalIndex: number) => Node,\n adoptedNodes: AdoptedNodes,\n recorder: ReconciliationRecorder | undefined,\n): Array<Gap> {\n const consumedStored = new Set<number>()\n const consumedEdited = new Set<number>()\n const storedLeftovers = gaps.flatMap((g) => g.storedIndexes)\n const editedLeftovers = gaps.flatMap((g) => g.editedIndexes)\n const storedByNeutral = new Map<string, Array<number>>()\n for (const index of storedLeftovers) {\n const neutral = neutralForm(canonical[index]!)\n storedByNeutral.set(neutral, [\n ...(storedByNeutral.get(neutral) ?? []),\n index,\n ])\n }\n const editedByNeutral = new Map<string, Array<number>>()\n for (const index of editedLeftovers) {\n const neutral = neutralForm(result[index]!)\n editedByNeutral.set(neutral, [\n ...(editedByNeutral.get(neutral) ?? []),\n index,\n ])\n }\n for (const [neutral, storedIndexes] of storedByNeutral) {\n const editedIndexes = editedByNeutral.get(neutral)\n if (\n storedIndexes.length !== 1 ||\n !editedIndexes ||\n editedIndexes.length !== 1\n ) {\n continue\n }\n consumedStored.add(storedIndexes[0]!)\n consumedEdited.add(editedIndexes[0]!)\n const clone = adoptVerbatim(\n originOf(storedIndexes[0]!),\n result[editedIndexes[0]!]!,\n adoptedNodes,\n 'content-moved',\n recorder,\n )\n result[editedIndexes[0]!] = clone\n }\n\n return gaps.map((currentGap) => ({\n storedIndexes: currentGap.storedIndexes.filter(\n (index) => !consumedStored.has(index),\n ),\n editedIndexes: currentGap.editedIndexes.filter(\n (index) => !consumedEdited.has(index),\n ),\n }))\n}\n\n/**\n * Gap policy, in order: split/merge survivor (the first fragment or\n * first source block keeps the key, matching what pressing enter or\n * backspace does in the editor), positional zip for equal counts\n * (typing over a paragraph keeps its identity), similarity for\n * unequal counts (an insertion or deletion shifted positions, so\n * position lies and only mutual unique best evidence adopts).\n */\nfunction resolveGap(\n storedIndexes: Array<number>,\n editedIndexes: Array<number>,\n canonical: ReadonlyArray<Node>,\n result: ReadonlyArray<Node>,\n originOf: (canonicalIndex: number) => Node,\n adoptedNodes: AdoptedNodes,\n recorder: ReconciliationRecorder | undefined,\n): void {\n const remainingStored = new Set(storedIndexes)\n const remainingEdited = new Set(editedIndexes)\n\n // The concatenation search below is quadratic in the gap's size, so\n // it shares the similarity tier's pair cap: past it, splits and\n // merges get skipped too rather than adopted at tens-of-seconds cost.\n const withinConcatenationCap =\n storedIndexes.length * editedIndexes.length <= MAX_SIMILARITY_PAIRS\n\n // Splits: one stored block's text equals the concatenation of\n // adjacent edited blocks.\n if (withinConcatenationCap) {\n for (const storedIndex of storedIndexes) {\n if (!remainingStored.has(storedIndex)) {\n continue\n }\n const storedBlock = canonical[storedIndex]!\n if (!isTextBlock(storedBlock)) {\n continue\n }\n const fragments = findConcatenation(\n blockText(storedBlock),\n editedIndexes.filter((index) => remainingEdited.has(index)),\n result,\n )\n if (fragments) {\n remainingStored.delete(storedIndex)\n for (const fragment of fragments) {\n remainingEdited.delete(fragment)\n }\n const survivor =\n fragments.find(\n (fragment) => blockText(result[fragment]!).length > 0,\n ) ?? fragments[0]!\n adoptNode(\n originOf(storedIndex),\n canonical[storedIndex],\n result[survivor]!,\n adoptedNodes,\n 'content-split',\n recorder,\n )\n }\n }\n }\n\n // Merges: one edited block's text equals the concatenation of\n // adjacent stored blocks. The first source block survives.\n if (withinConcatenationCap) {\n for (const editedIndex of editedIndexes) {\n if (!remainingEdited.has(editedIndex)) {\n continue\n }\n const editedBlock = result[editedIndex]!\n if (!isTextBlock(editedBlock)) {\n continue\n }\n const sources = findConcatenation(\n blockText(editedBlock),\n storedIndexes.filter((index) => remainingStored.has(index)),\n canonical,\n )\n if (sources) {\n remainingEdited.delete(editedIndex)\n for (const source of sources) {\n remainingStored.delete(source)\n }\n adoptNode(\n originOf(sources[0]!),\n canonical[sources[0]!],\n result[editedIndex]!,\n adoptedNodes,\n 'content-merged',\n recorder,\n )\n }\n }\n }\n\n const storedRest = [...remainingStored]\n const editedRest = [...remainingEdited]\n\n if (storedRest.length === editedRest.length) {\n for (let offset = 0; offset < storedRest.length; offset++) {\n const storedBlock = canonical[storedRest[offset]!]!\n const editedBlock = result[editedRest[offset]!]!\n if (storedBlock['_type'] === editedBlock['_type']) {\n adoptNode(\n originOf(storedRest[offset]!),\n canonical[storedRest[offset]!],\n editedBlock,\n adoptedNodes,\n 'same-position',\n recorder,\n )\n }\n }\n return\n }\n\n if (storedRest.length * editedRest.length > MAX_SIMILARITY_PAIRS) {\n if (recorder) {\n recorder.ambiguousRegionGroups.push(\n editedRest.map((editedIndex) => result[editedIndex]!),\n )\n }\n return\n }\n\n const scores = new Map<string, number>()\n for (const storedIndex of storedRest) {\n for (const editedIndex of editedRest) {\n const score = blockSimilarity(\n canonical[storedIndex]!,\n result[editedIndex]!,\n )\n if (score >= MIN_BLOCK_SIMILARITY) {\n scores.set(`${storedIndex}:${editedIndex}`, score)\n }\n }\n }\n for (const storedIndex of storedRest) {\n const best = uniqueBest(editedRest, (editedIndex) =>\n scores.get(`${storedIndex}:${editedIndex}`),\n )\n if (best === undefined) {\n continue\n }\n const bestBack = uniqueBest(storedRest, (otherStoredIndex) =>\n scores.get(`${otherStoredIndex}:${best}`),\n )\n if (bestBack !== storedIndex) {\n continue\n }\n adoptNode(\n originOf(storedIndex),\n canonical[storedIndex],\n result[best]!,\n adoptedNodes,\n 'similar-content',\n recorder,\n )\n }\n}\n\n/**\n * Fragments join with nothing or a single space, since a markdown\n * merge is often a soft-wrap join that inserts one (\"alpha\\nbeta\"\n * parses to \"alpha beta\").\n */\nfunction findConcatenation(\n wholeText: string,\n candidateIndexes: Array<number>,\n nodes: ReadonlyArray<Node>,\n): Array<number> | undefined {\n if (wholeText.length === 0) {\n return undefined\n }\n for (const joiner of ['', ' ']) {\n for (let start = 0; start < candidateIndexes.length; start++) {\n let concatenated = ''\n const used: Array<number> = []\n for (\n let position = start;\n position < candidateIndexes.length;\n position++\n ) {\n const index = candidateIndexes[position]!\n if (position > start && candidateIndexes[position - 1] !== index - 1) {\n break\n }\n if (!isTextBlock(nodes[index]!)) {\n break\n }\n concatenated =\n used.length === 0\n ? blockText(nodes[index]!)\n : concatenated + joiner + blockText(nodes[index]!)\n used.push(index)\n if (concatenated.length > wholeText.length) {\n break\n }\n if (concatenated === wholeText && used.length > 1) {\n return used\n }\n }\n }\n }\n return undefined\n}\n\n/**\n * Adopts the original node's `_key`, its markdown-inexpressible\n * fields, then its `markDefs` before its other keyed children, since\n * `span.marks` references need the adopted `markDefs` keys already in\n * place.\n */\nfunction adoptNode(\n original: Node,\n canonicalCounterpart: Node | undefined,\n target: Node,\n adoptedNodes: AdoptedNodes,\n basis: PreservationBasis,\n recorder: ReconciliationRecorder | undefined,\n): void {\n adoptedNodes.add(target)\n if (typeof original['_key'] === 'string') {\n target['_key'] = original['_key']\n recorder?.preservationBasis.set(target, basis)\n }\n\n restoreFields(original, canonicalCounterpart, target)\n\n const markDefKeyMap = adoptMarkDefs(\n original,\n canonicalCounterpart,\n target,\n recorder,\n )\n rewriteMarkReferences(target, markDefKeyMap)\n\n for (const field of Object.keys(target)) {\n if (field === 'markDefs') {\n continue\n }\n const originalChildren = original[field]\n const targetChildren = target[field]\n if (\n !isTypedObjectArray(originalChildren) ||\n !isTypedObjectArray(targetChildren)\n ) {\n continue\n }\n const canonicalChildren = canonicalChildArray(\n canonicalCounterpart,\n field,\n originalChildren,\n )\n\n const matchedOriginal = new Set<number>()\n const matchedTarget = new Set<number>()\n // Children reference their parent block's `markDefs`, so their\n // neutral forms alias marks against the parent, not themselves.\n const originalGroups = groupByNeutralForm(\n originalChildren,\n matchedOriginal,\n buildAliasMap(original),\n )\n const targetGroups = groupByNeutralForm(\n targetChildren,\n matchedTarget,\n buildAliasMap(target),\n )\n\n for (const [neutral, originalIndexes] of originalGroups) {\n const targetIndexes = targetGroups.get(neutral)\n if (\n originalIndexes.length !== 1 ||\n !targetIndexes ||\n targetIndexes.length !== 1\n ) {\n continue\n }\n matchedOriginal.add(originalIndexes[0]!)\n matchedTarget.add(targetIndexes[0]!)\n adoptNode(\n originalChildren[originalIndexes[0]!]!,\n canonicalChildren?.[originalIndexes[0]!],\n targetChildren[targetIndexes[0]!]!,\n adoptedNodes,\n 'content-unchanged',\n recorder,\n )\n }\n\n // The concatenation search is quadratic in the block's span count,\n // so it shares the similarity tier's pair cap: past it, the merge\n // search is skipped and the unmatched spans fall through to fresh\n // keys rather than adopting at tens-of-seconds cost.\n if (\n field === 'children' &&\n originalChildren.length * targetChildren.length <= MAX_SIMILARITY_PAIRS\n ) {\n adoptMergedSpans(\n originalChildren,\n canonicalChildren,\n matchedOriginal,\n targetChildren,\n matchedTarget,\n adoptedNodes,\n recorder,\n )\n }\n\n adoptResidualZip(\n originalChildren,\n canonicalChildren,\n matchedOriginal,\n targetChildren,\n matchedTarget,\n adoptedNodes,\n recorder,\n )\n }\n}\n\n/**\n * The container-level counterpart of `traceOrigins`'s guard: a\n * child's canonical form is only trustworthy when the canonical\n * container holds the same field as the same typed object array,\n * equal in length and `_type` sequence to the original's, so pairing\n * by index (original child `i` to canonical child `i`) means the same\n * content on both sides.\n */\nfunction canonicalChildArray(\n canonicalCounterpart: Node | undefined,\n field: string,\n originalChildren: ReadonlyArray<Node>,\n): Array<Node> | undefined {\n if (!canonicalCounterpart) {\n return undefined\n }\n const candidate = canonicalCounterpart[field]\n if (\n !isTypedObjectArray(candidate) ||\n candidate.length !== originalChildren.length\n ) {\n return undefined\n }\n for (let index = 0; index < originalChildren.length; index++) {\n if (originalChildren[index]!['_type'] !== candidate[index]!['_type']) {\n return undefined\n }\n }\n return candidate\n}\n\n/**\n * Restores fields the markdown dialect dropped or altered: present on\n * the original, unchanged by the edit (the target still agrees with\n * the original's own canonical round trip), and different on that\n * round trip from the original (so the parse, left to itself, could\n * never have produced the original's value). Absence counts as a\n * value under both comparisons, which is what folds a wholly dropped\n * field (no markdown form at all) and a coerced one (a custom style\n * or decorator markdown silently maps to its closest built-in) into\n * one rule. Structural child arrays (`children`, `markDefs`, and\n * typed object arrays generally) are excluded: their elements adopt\n * individually through the recursive per-child walk instead, except\n * when the target has no such element to walk at all: a typed-object\n * array field the dialect drops entirely leaves nothing on the target\n * side for that walk to reconcile, so it falls through to the same\n * absent-on-target, absent-on-canonical oracle as every scalar field,\n * restored verbatim rather than left missing. Restored values are\n * cloned, since the sibling-key-uniqueness pass may rewrite `_key`s\n * inside a restored array of objects, and the original must stay\n * untouched.\n */\nfunction restoreFields(\n original: Node,\n canonicalCounterpart: Node | undefined,\n target: Node,\n): void {\n if (!canonicalCounterpart) {\n return\n }\n for (const field of Object.keys(original)) {\n if (\n field === '_key' ||\n field === '_type' ||\n field === 'markDefs' ||\n field === 'children'\n ) {\n continue\n }\n if (isTypedObjectArray(original[field])) {\n if (\n target[field] === undefined &&\n canonicalCounterpart[field] === undefined\n ) {\n target[field] = structuredClone(original[field])\n }\n continue\n }\n const canonicalValue = canonicalCounterpart[field]\n if (!valuesEqual(target[field], canonicalValue)) {\n continue\n }\n if (valuesEqual(canonicalValue, original[field])) {\n continue\n }\n target[field] = structuredClone(original[field])\n }\n}\n\n/**\n * Deep equality for restoration's before/after comparison: the\n * `encodeNeutral` encoding without alias rewriting, so it compares\n * `marks` arrays (and any other array or nested object) by value\n * rather than by identity. Absent (`undefined`) encodes to the same\n * string on both sides, so two absent fields count as equal.\n */\nfunction valuesEqual(a: unknown, b: unknown): boolean {\n return encodeNeutral(a, undefined) === encodeNeutral(b, undefined)\n}\n\n/**\n * A span in the edited output can be the merge of several stored\n * spans: the parser merges adjacent same-mark spans, and an edit that\n * removes formatting merges across the old mark boundary too. Matching\n * is by text alone, and the first contributor's key survives, matching\n * the editor's own span-merge normalization.\n */\nfunction adoptMergedSpans(\n originalChildren: ReadonlyArray<Node>,\n canonicalChildren: ReadonlyArray<Node> | undefined,\n matchedOriginal: Set<number>,\n targetChildren: ReadonlyArray<Node>,\n matchedTarget: Set<number>,\n adoptedNodes: AdoptedNodes,\n recorder: ReconciliationRecorder | undefined,\n): void {\n for (\n let targetIndex = 0;\n targetIndex < targetChildren.length;\n targetIndex++\n ) {\n if (matchedTarget.has(targetIndex)) {\n continue\n }\n const targetSpan = targetChildren[targetIndex]!\n const targetText = targetSpan['text']\n if (typeof targetText !== 'string') {\n continue\n }\n\n for (let start = 0; start < originalChildren.length; start++) {\n if (matchedOriginal.has(start)) {\n continue\n }\n let concatenated = ''\n const used: Array<number> = []\n for (let index = start; index < originalChildren.length; index++) {\n if (matchedOriginal.has(index)) {\n break\n }\n const originalSpan = originalChildren[index]!\n if (typeof originalSpan['text'] !== 'string') {\n break\n }\n concatenated += originalSpan['text']\n used.push(index)\n if (concatenated.length > targetText.length) {\n break\n }\n if (concatenated === targetText && used.length > 1) {\n matchedTarget.add(targetIndex)\n for (const usedIndex of used) {\n matchedOriginal.add(usedIndex)\n }\n adoptNode(\n originalChildren[used[0]!]!,\n canonicalChildren?.[used[0]!],\n targetSpan,\n adoptedNodes,\n 'content-merged',\n recorder,\n )\n break\n }\n }\n if (matchedTarget.has(targetIndex)) {\n break\n }\n }\n }\n}\n\n/**\n * The equal-count residual rule, mirroring `resolveGap`'s positional\n * zip: elements left unmatched after neutral-form (and, for\n * `children`, span-merge) matching are treated as in-place edits when\n * both sides leave the same count, position being the same evidence\n * the block-level zip already trusts, and the trade is the same too:\n * a reorder-plus-edit with balanced counts mispairs. Unequal counts\n * adopt nothing, since position no longer lines up.\n */\nfunction adoptResidualZip(\n originalChildren: ReadonlyArray<Node>,\n canonicalChildren: ReadonlyArray<Node> | undefined,\n matchedOriginal: ReadonlySet<number>,\n targetChildren: ReadonlyArray<Node>,\n matchedTarget: ReadonlySet<number>,\n adoptedNodes: AdoptedNodes,\n recorder: ReconciliationRecorder | undefined,\n): void {\n const originalRest = originalChildren\n .map((node, index) => ({node, index}))\n .filter(({index}) => !matchedOriginal.has(index))\n const targetRest = targetChildren.filter(\n (_, index) => !matchedTarget.has(index),\n )\n if (originalRest.length !== targetRest.length) {\n return\n }\n for (let offset = 0; offset < originalRest.length; offset++) {\n const originalNode = originalRest[offset]!.node\n const targetNode = targetRest[offset]!\n if (originalNode['_type'] === targetNode['_type']) {\n adoptNode(\n originalNode,\n canonicalChildren?.[originalRest[offset]!.index],\n targetNode,\n adoptedNodes,\n 'same-position',\n recorder,\n )\n }\n }\n}\n\n/**\n * Matches `markDefs` by definition content, `_key` excluded. Returns\n * the mapping from the target's fresh keys to the adopted stored\n * keys, for rewriting `span.marks` references.\n */\nfunction adoptMarkDefs(\n original: Node,\n canonicalCounterpart: Node | undefined,\n target: Node,\n recorder: ReconciliationRecorder | undefined,\n): Map<string, string> {\n const keyMap = new Map<string, string>()\n const originalDefs = original['markDefs']\n const targetDefs = target['markDefs']\n if (!isTypedObjectArray(originalDefs) || !isTypedObjectArray(targetDefs)) {\n return keyMap\n }\n const canonicalDefs = canonicalChildArray(\n canonicalCounterpart,\n 'markDefs',\n originalDefs,\n )\n\n const matchedOriginal = new Set<number>()\n const matchedTarget = new Set<number>()\n // Stored definitions can carry fields the dialect drops (restored\n // later from the stored side), which the parsed definitions never\n // have; fingerprinting the stored side via its canonical twin keeps\n // the two sides' fingerprints dialect-consistent.\n const originalFingerprintSource = originalDefs.map(\n (def, index) => canonicalDefs?.[index] ?? def,\n )\n const originalGroups = groupByNeutralForm(\n originalFingerprintSource,\n matchedOriginal,\n )\n const targetGroups = groupByNeutralForm(targetDefs, matchedTarget)\n\n const adoptDef = (\n originalDef: Node,\n canonicalDef: Node | undefined,\n targetDef: Node,\n basis: PreservationBasis,\n ): void => {\n restoreFields(originalDef, canonicalDef, targetDef)\n if (\n typeof originalDef['_key'] === 'string' &&\n typeof targetDef['_key'] === 'string'\n ) {\n const adoptedKey = originalDef['_key']\n const collidesWithSibling = targetDefs.some(\n (def) => def !== targetDef && def['_key'] === adoptedKey,\n )\n if (collidesWithSibling) {\n recorder?.annotationKeyConflicts.push(targetDef)\n return\n }\n keyMap.set(targetDef['_key'], adoptedKey)\n targetDef['_key'] = adoptedKey\n recorder?.preservationBasis.set(targetDef, basis)\n }\n }\n\n for (const [neutral, originalIndexes] of originalGroups) {\n const targetIndexes = targetGroups.get(neutral)\n if (!targetIndexes || targetIndexes.length !== originalIndexes.length) {\n continue\n }\n // Equal-count identical definitions pair in order, the repeated-content\n // policy: the definitions are content-equal, so no pairing can\n // mis-resolve a reference.\n for (let offset = 0; offset < originalIndexes.length; offset++) {\n matchedOriginal.add(originalIndexes[offset]!)\n matchedTarget.add(targetIndexes[offset]!)\n adoptDef(\n originalDefs[originalIndexes[offset]!]!,\n canonicalDefs?.[originalIndexes[offset]!],\n targetDefs[targetIndexes[offset]!]!,\n originalIndexes.length === 1 ? 'content-unchanged' : 'same-position',\n )\n }\n }\n\n const originalRest = originalDefs\n .map((node, index) => ({node, index}))\n .filter(({index}) => !matchedOriginal.has(index))\n const targetRest = targetDefs.filter((_, index) => !matchedTarget.has(index))\n if (\n originalRest.length === 1 &&\n targetRest.length === 1 &&\n originalRest[0]!.node['_type'] === targetRest[0]!['_type']\n ) {\n adoptDef(\n originalRest[0]!.node,\n canonicalDefs?.[originalRest[0]!.index],\n targetRest[0]!,\n 'same-position',\n )\n }\n\n return keyMap\n}\n\nfunction rewriteMarkReferences(block: Node, keyMap: Map<string, string>): void {\n if (keyMap.size === 0) {\n return\n }\n const children = block['children']\n if (!isTypedObjectArray(children)) {\n return\n }\n for (const child of children) {\n const marks = child['marks']\n if (!Array.isArray(marks)) {\n continue\n }\n child['marks'] = marks.map((mark) =>\n typeof mark === 'string' && keyMap.has(mark) ? keyMap.get(mark) : mark,\n )\n }\n}\n\nfunction groupByNeutralForm(\n nodes: ReadonlyArray<Node>,\n exclude: ReadonlySet<number>,\n aliasByKey?: Map<string, string>,\n): Map<string, Array<number>> {\n const groups = new Map<string, Array<number>>()\n for (let index = 0; index < nodes.length; index++) {\n if (exclude.has(index)) {\n continue\n }\n const neutral = aliasByKey\n ? encodeNeutral(nodes[index]!, aliasByKey)\n : neutralForm(nodes[index]!)\n const group = groups.get(neutral)\n if (group) {\n group.push(index)\n } else {\n groups.set(neutral, [index])\n }\n }\n return groups\n}\n\n/**\n * A canonical JSON encoding that erases identity: `_key` properties\n * are dropped, object properties are sorted, and annotation `_key`\n * references inside `span.marks` are rewritten to the definition's\n * position in `markDefs` (dropping `_key` alone would compare the\n * stored annotation key against the fresh one and reject an unchanged\n * link).\n */\nfunction neutralForm(node: Node): string {\n return encodeNeutral(node, buildAliasMap(node))\n}\n\n/**\n * Aliases each `markDefs` key to a key-independent spelling of the\n * definition itself, so `span.marks` references compare by what the\n * annotation is rather than which key it carries. The spelling is the\n * definition's own neutral form, not its array position: aliasing by\n * position made every annotated sibling span's neutral form shift when\n * a definition was inserted or removed before its own, so adding one\n * link re-keyed unrelated annotated spans. Identical definitions (the\n * same link twice) are disambiguated by occurrence order among\n * identical forms only, which no unrelated insertion can shift.\n */\nfunction buildAliasMap(node: Node): Map<string, string> | undefined {\n const markDefs = node['markDefs']\n if (!isTypedObjectArray(markDefs)) {\n return undefined\n }\n const aliasByKey = new Map<string, string>()\n const occurrenceByForm = new Map<string, number>()\n for (const definition of markDefs) {\n const key = definition['_key']\n if (typeof key !== 'string') {\n continue\n }\n const form = encodeNeutral(definition, undefined)\n const occurrence = occurrenceByForm.get(form) ?? 0\n occurrenceByForm.set(form, occurrence + 1)\n aliasByKey.set(key, `@annotation:${occurrence}:${form}`)\n }\n return aliasByKey\n}\n\nfunction encodeNeutral(\n value: unknown,\n aliasByKey: Map<string, string> | undefined,\n): string {\n if (Array.isArray(value)) {\n return `[${value.map((item) => encodeNeutral(item, aliasByKey)).join(',')}]`\n }\n if (typeof value === 'object' && value !== null) {\n const entries = Object.entries(value as Node)\n .filter(([field]) => field !== '_key')\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n .map(([field, fieldValue]) => {\n if (field === 'marks' && aliasByKey && Array.isArray(fieldValue)) {\n const aliased = fieldValue.map((mark) =>\n typeof mark === 'string' && aliasByKey.has(mark)\n ? aliasByKey.get(mark)\n : mark,\n )\n return `${JSON.stringify(field)}:${JSON.stringify(aliased)}`\n }\n return `${JSON.stringify(field)}:${encodeNeutral(fieldValue, aliasByKey)}`\n })\n return `{${entries.join(',')}}`\n }\n return JSON.stringify(value) ?? 'undefined'\n}\n\nconst INLINE_OBJECT_SENTINEL = '\\uFFFC'\n\n/**\n * The block's comparison text: concatenated span text with inline\n * objects as sentinels. Marks are ignored, since a formatting-only\n * edit does not change textual identity.\n */\nfunction blockText(block: Node): string {\n const children = block['children']\n if (!isTypedObjectArray(children)) {\n return ''\n }\n return children\n .map((child) =>\n typeof child['text'] === 'string'\n ? child['text']\n : INLINE_OBJECT_SENTINEL,\n )\n .join('')\n}\n\n/**\n * Text similarity in `[0, 1]`, gated to `0` unless the block shells\n * agree and the inline objects are compatible: however alike the\n * prose, blocks that disagree on structure are not the same block.\n * The length prescreen skips the diff when the size difference alone\n * puts the score under `MIN_BLOCK_SIMILARITY`.\n */\nfunction blockSimilarity(canonicalBlock: Node, resultBlock: Node): number {\n if (!shellEquals(canonicalBlock, resultBlock)) {\n return 0\n }\n if (!inlineObjectsCompatible(canonicalBlock, resultBlock)) {\n return 0\n }\n const canonicalText = blockText(canonicalBlock)\n const resultText = blockText(resultBlock)\n if (canonicalText.length === 0 || resultText.length === 0) {\n return 0\n }\n const longer = Math.max(canonicalText.length, resultText.length)\n const lengthBound =\n 1 - Math.abs(canonicalText.length - resultText.length) / longer\n if (lengthBound < MIN_BLOCK_SIMILARITY) {\n return 0\n }\n const diffs = cleanupEfficiency(\n makeDiff(canonicalText, resultText, {\n timeout: SIMILARITY_DIFF_TIMEOUT_SECONDS,\n }),\n )\n return 1 - levenshteinFromDiffs(diffs) / longer\n}\n\n/**\n * The distance derivation from diff runs: insertions and deletions\n * between equality runs accumulate as the larger of the two, so a\n * delete-plus-insert counts as one substitution.\n */\nfunction levenshteinFromDiffs(diffs: ReadonlyArray<[number, string]>): number {\n let distance = 0\n let insertions = 0\n let deletions = 0\n for (const [operation, text] of diffs) {\n if (operation === 1) {\n insertions += text.length\n } else if (operation === -1) {\n deletions += text.length\n } else {\n distance += Math.max(insertions, deletions)\n insertions = 0\n deletions = 0\n }\n }\n return distance + Math.max(insertions, deletions)\n}\n\n/**\n * The block minus its content: `_type`, `style`, `listItem`, `level`,\n * and any custom fields must agree before text similarity means\n * anything.\n */\nfunction shellEquals(a: Node, b: Node): boolean {\n const shellOf = (node: Node): string =>\n encodeNeutral(\n Object.fromEntries(\n Object.entries(node).filter(\n ([field]) => field !== 'children' && field !== 'markDefs',\n ),\n ),\n undefined,\n )\n return shellOf(a) === shellOf(b)\n}\n\n/**\n * Inline objects are opaque content: two blocks whose objects differ\n * are different blocks no matter how similar their prose is.\n */\nfunction inlineObjectsCompatible(a: Node, b: Node): boolean {\n const objectsOf = (node: Node): Array<Node> => {\n const children = node['children']\n if (!isTypedObjectArray(children)) {\n return []\n }\n return children.filter((child) => typeof child['text'] !== 'string')\n }\n const aObjects = objectsOf(a)\n const bObjects = objectsOf(b)\n if (aObjects.length !== bObjects.length) {\n return false\n }\n return aObjects.every((aObject, index) => {\n const bObject = bObjects[index]!\n if (aObject['_type'] !== bObject['_type']) {\n return false\n }\n if (\n typeof aObject['_key'] === 'string' &&\n aObject['_key'] === bObject['_key']\n ) {\n return true\n }\n return neutralForm(aObject) === neutralForm(bObject)\n })\n}\n\n/**\n * The highest-scoring candidate, or `undefined` on a tie: a tie is\n * ambiguity, and ambiguity refuses adoption rather than guessing.\n */\nfunction uniqueBest(\n candidates: ReadonlyArray<number>,\n scoreOf: (candidate: number) => number | undefined,\n): number | undefined {\n let best: number | undefined\n let bestScore = 0\n let tied = false\n for (const candidate of candidates) {\n const score = scoreOf(candidate)\n if (score === undefined) {\n continue\n }\n if (score > bestScore) {\n best = candidate\n bestScore = score\n tied = false\n } else if (score === bestScore && best !== undefined) {\n tied = true\n }\n }\n return tied ? undefined : best\n}\n\n/**\n * Deliberately loose: any node with a `children` array reconciles like\n * a text block, custom block types included.\n */\nfunction isTextBlock(node: Node): boolean {\n return Array.isArray(node['children'])\n}\n\n/**\n * A text block the round trip drops. Whether a whitespace-only block\n * survives serialize→parse depends on the converters, not on\n * structure: a heading renders `## ` and survives, a custom style\n * falls back to a plain paragraph and vanishes, and a non-breaking\n * space renders \"blank-looking\" output the parser still keeps (JS\n * `trim` folds NBSP, CommonMark does not), so any string check here is\n * a hand-written mirror of one converter or the other that drifts. The\n * block's own render-then-reparse is the authority, the same round\n * trip `canonicalizeStored` performs, so this predicate cannot\n * disagree with the canonical node count. The JS-trim pre-check only\n * keeps the per-block round trip off paths that cannot qualify: it\n * over-admits candidates (NBSP text passes it), and the reparse then\n * decides.\n */\nfunction isEmptyTextBlock(\n node: Node,\n options: ApplyMarkdownEditOptions | undefined,\n): boolean {\n if (!(isTextBlock(node) && blockText(node).trim() === '')) {\n return false\n }\n const rendered = portableTextToMarkdown(\n [structuredClone(node)] as unknown as Array<PortableTextBlock>,\n {...options?.serialize, schema: options?.schema},\n )\n if (rendered === '') {\n return true\n }\n const {onDegradation: _onDegradation, ...deserializeOptions} =\n options?.deserialize ?? {}\n let probeKeyCounter = 0\n return (\n markdownToPortableText(rendered, {\n ...deserializeOptions,\n schema: options?.schema,\n keyGenerator: () => `empty-probe-${probeKeyCounter++}`,\n }).length === 0\n )\n}\n\n/**\n * Blank lines are markdown's block separator, so an empty text block\n * has no serialized form: `canonicalizeStored`'s round trip drops it,\n * the same way `markdownToPortableText` would if it were parsed back\n * from `editedMarkdown`. Tracing origins and aligning blocks over this\n * subsequence keeps both sides the same length; `reinsertEmptyRuns`\n * restores the dropped blocks afterward.\n */\nfunction nonEmptyBlocks(\n stored: ReadonlyArray<PortableTextBlock>,\n options: ApplyMarkdownEditOptions | undefined,\n): Array<PortableTextBlock> {\n return stored.filter(\n (node) => !isEmptyTextBlock(node as unknown as Node, options),\n )\n}\n\nfunction isTypedObjectArray(value: unknown): value is Array<Node> {\n return (\n Array.isArray(value) &&\n value.length > 0 &&\n value.every(\n (item) =>\n typeof item === 'object' &&\n item !== null &&\n typeof (item as Node)['_type'] === 'string',\n )\n )\n}\n\ntype EmptyRun = {\n anchorKey: string\n insertAfter: boolean\n blocks: Array<Node>\n}\n\n/**\n * Maximal runs of empty text blocks, each paired with the surviving\n * neighbor its restoration hangs off: a run with a preceding block\n * anchors to it (insert after); a run at the document's start, with\n * none, anchors to the block that follows it (insert before). A run\n * with neither (the whole document is empty blocks) has nothing to\n * anchor to and is dropped.\n */\nfunction findEmptyRuns(\n stored: ReadonlyArray<PortableTextBlock>,\n options: ApplyMarkdownEditOptions | undefined,\n): Array<EmptyRun> {\n const runs: Array<EmptyRun> = []\n let index = 0\n while (index < stored.length) {\n if (!isEmptyTextBlock(stored[index] as unknown as Node, options)) {\n index++\n continue\n }\n const runStart = index\n while (\n index < stored.length &&\n isEmptyTextBlock(stored[index] as unknown as Node, options)\n ) {\n index++\n }\n const precedingBlock =\n runStart > 0 ? (stored[runStart - 1] as unknown as Node) : undefined\n const followingBlock =\n index < stored.length ? (stored[index] as unknown as Node) : undefined\n const anchor = precedingBlock ?? followingBlock\n if (anchor && typeof anchor['_key'] === 'string') {\n runs.push({\n anchorKey: anchor['_key'],\n insertAfter: precedingBlock !== undefined,\n blocks: stored.slice(runStart, index) as unknown as Array<Node>,\n })\n }\n }\n return runs\n}\n\n/**\n * Restores each empty run next to the result node that adopted its\n * anchor's key, found by `_key` since positions have already shifted\n * under insertion, deletion, and move. An anchor whose key did not\n * survive into `result` (its region was rewritten or deleted) drops\n * the run with it, consistent with rewrite semantics elsewhere in this\n * module. Runs are cloned and marked adopted, the same authoritative\n * status as every other restored key, so a collision resolves in\n * their favor like `enforceSiblingKeyUniqueness` already does for\n * `json:object` duplicates.\n */\nfunction reinsertEmptyRuns(\n stored: ReadonlyArray<PortableTextBlock>,\n result: Array<Node>,\n adoptedNodes: AdoptedNodes,\n options: ApplyMarkdownEditOptions | undefined,\n recorder: ReconciliationRecorder | undefined,\n): void {\n for (const run of findEmptyRuns(stored, options)) {\n // A pasted `json:object` duplicate can wear the anchor's key at an\n // earlier position until the uniqueness pass renames it, so an\n // adopted bearer of the key outranks a verbatim duplicate.\n const adoptedAnchorIndex = result.findIndex(\n (node) => node['_key'] === run.anchorKey && adoptedNodes.has(node),\n )\n const anchorIndex =\n adoptedAnchorIndex !== -1\n ? adoptedAnchorIndex\n : result.findIndex((node) => node['_key'] === run.anchorKey)\n if (anchorIndex === -1) {\n continue\n }\n const clones = run.blocks.map((block) => structuredClone(block))\n for (const clone of clones) {\n adoptedNodes.add(clone)\n if (recorder) {\n tagSubtreePreserved(clone, recorder)\n }\n }\n result.splice(run.insertAfter ? anchorIndex + 1 : anchorIndex, 0, ...clones)\n }\n}\n\n/**\n * Every keyed node in a reinserted empty run is the stored value\n * verbatim, at every depth, so key resolution reporting tags the\n * whole subtree `content-unchanged` rather than only the run's top\n * block.\n */\nfunction tagSubtreePreserved(\n node: Node,\n recorder: ReconciliationRecorder,\n): void {\n if (typeof node['_key'] === 'string') {\n recorder.preservationBasis.set(node, 'content-unchanged')\n }\n for (const value of Object.values(node)) {\n if (\n Array.isArray(value) &&\n value.every((item) => typeof item === 'object' && item !== null) &&\n value.length > 0\n ) {\n for (const child of value as Array<Node>) {\n tagSubtreePreserved(child, recorder)\n }\n } else if (typeof value === 'object' && value !== null) {\n tagSubtreePreserved(value as Node, recorder)\n }\n }\n}\n\n/**\n * `json:object` payloads transport their `_key` verbatim, so a\n * copy-pasted fence puts the same key on two siblings, and everything\n * downstream (patches, anchors, editor normalization) assumes sibling\n * keys are unique. Adopted keys are authoritative, so a duplicate that\n * was adopted from the stored value wins and the other occurrences are\n * regenerated.\n */\nfunction enforceSiblingKeyUniqueness(\n nodes: Array<Node>,\n keyGenerator: () => string,\n adoptedNodes: AdoptedNodes,\n recorder: ReconciliationRecorder | undefined,\n onKeyRewritten?: (oldKey: string, newKey: string) => void,\n): void {\n const usedKeys = new Set<string>()\n\n const claim = (node: Node): void => {\n const key = node['_key']\n if (typeof key !== 'string') {\n return\n }\n if (usedKeys.has(key)) {\n let freshKey: string | undefined\n for (let attempt = 0; attempt < MAX_KEY_GENERATOR_ATTEMPTS; attempt++) {\n const candidate = keyGenerator()\n if (!usedKeys.has(candidate)) {\n freshKey = candidate\n break\n }\n }\n if (freshKey === undefined) {\n let suffix = 1\n let candidate = `${key}_${suffix}`\n while (usedKeys.has(candidate)) {\n suffix++\n candidate = `${key}_${suffix}`\n }\n freshKey = candidate\n }\n node['_key'] = freshKey\n usedKeys.add(freshKey)\n onKeyRewritten?.(key, freshKey)\n recorder?.renamePreviousKey.set(node, key)\n return\n }\n usedKeys.add(key)\n }\n\n const adopted = nodes.filter((node) => adoptedNodes.has(node))\n const rest = nodes.filter((node) => !adoptedNodes.has(node))\n for (const node of [...adopted, ...rest]) {\n claim(node)\n }\n\n for (const node of nodes) {\n enforceNestedSiblingKeyUniqueness(\n node,\n keyGenerator,\n adoptedNodes,\n recorder,\n )\n }\n}\n\nfunction enforceNestedSiblingKeyUniqueness(\n node: Node,\n keyGenerator: () => string,\n adoptedNodes: AdoptedNodes,\n recorder: ReconciliationRecorder | undefined,\n): void {\n for (const [field, value] of Object.entries(node)) {\n if (\n Array.isArray(value) &&\n value.every((item) => typeof item === 'object' && item !== null) &&\n value.length > 0\n ) {\n enforceSiblingKeyUniqueness(\n value as Array<Node>,\n keyGenerator,\n adoptedNodes,\n recorder,\n field === 'markDefs' ? buildMarkDefKeyRewriter(node) : undefined,\n )\n } else if (typeof value === 'object' && value !== null) {\n enforceNestedSiblingKeyUniqueness(\n value as Node,\n keyGenerator,\n adoptedNodes,\n recorder,\n )\n }\n }\n}\n\n/**\n * A colliding `keyGenerator` can mint the identical string for two\n * `markDefs` entries, which means the block's spans already reference\n * that shared string ambiguously before any rewrite happens: a\n * blanket find-and-replace of the old key would move every span's\n * reference, including the one that was never renamed. The\n * `markDefs` array and each span's `marks` are walked in the same\n * fixed document order, so the Nth occurrence of a given key in one\n * lines up with the Nth occurrence in the other; the first occurrence\n * is always the survivor (`enforceSiblingKeyUniqueness` only renames\n * on collision, never the first sighting of a key), so each rename\n * event retargets the next occurrence in that shared order instead of\n * every occurrence.\n */\nfunction buildMarkDefKeyRewriter(\n block: Node,\n): (oldKey: string, newKey: string) => void {\n const children = block['children']\n if (!isTypedObjectArray(children)) {\n return () => {}\n }\n const occurrencesByKey = new Map<\n string,\n Array<{child: Node; markIndex: number}>\n >()\n for (const child of children) {\n const marks = child['marks']\n if (!Array.isArray(marks)) {\n continue\n }\n for (let markIndex = 0; markIndex < marks.length; markIndex++) {\n const mark = marks[markIndex]\n if (typeof mark !== 'string') {\n continue\n }\n const occurrences = occurrencesByKey.get(mark) ?? []\n occurrences.push({child, markIndex})\n occurrencesByKey.set(mark, occurrences)\n }\n }\n const consumedByKey = new Map<string, number>()\n return (oldKey: string, newKey: string): void => {\n const occurrences = occurrencesByKey.get(oldKey)\n // The first occurrence of `oldKey` is the survivor and is never\n // passed here, so the first rename event targets the second\n // occurrence.\n const index = consumedByKey.get(oldKey) ?? 1\n consumedByKey.set(oldKey, index + 1)\n const target = occurrences?.[index]\n if (!target) {\n return\n }\n const marks = target.child['marks']\n if (Array.isArray(marks)) {\n marks[target.markIndex] = newKey\n }\n }\n}\n\n/**\n * Materializes the public report from the recorder's node-identity\n * decisions, walking the settled result tree once so every `key` and\n * `path` matches the returned value exactly: key resolution records\n * decisions before the sibling-uniqueness pass can still rewrite a\n * key, so keys and paths are only trustworthy read back from the\n * final tree, not from the moment a decision was made.\n */\nfunction buildReconciliationReport(\n result: Array<Node>,\n recorder: ReconciliationRecorder,\n): ReconciliationReport {\n const preservedKeys: Extract<\n ReconciliationReport,\n {keyMatching: 'performed'}\n >['preservedKeys'] = []\n const renamedKeys: Extract<\n ReconciliationReport,\n {keyMatching: 'performed'}\n >['renamedKeys'] = []\n const pathByNode = new WeakMap<Node, ReconciliationKeyPath>()\n\n const walk = (node: Node, path: ReconciliationKeyPath): void => {\n pathByNode.set(node, path)\n const key = node['_key']\n if (typeof key === 'string') {\n const basis = recorder.preservationBasis.get(node)\n const previousKey = recorder.renamePreviousKey.get(node)\n if (basis && previousKey === undefined) {\n // A node that was adopted and then renamed (possible only when\n // the stored value itself carries duplicate sibling keys) did\n // not keep its stored key: the renamed-key entry carries that\n // story, and a preserved-key entry would report a key that\n // exists nowhere in the stored value.\n preservedKeys.push({basis, key, path})\n }\n if (previousKey !== undefined) {\n renamedKeys.push({previousKey, key, path})\n }\n }\n for (const [field, value] of Object.entries(node)) {\n if (\n Array.isArray(value) &&\n value.every((item) => typeof item === 'object' && item !== null) &&\n value.length > 0\n ) {\n // `enforceNestedSiblingKeyUniqueness` recurses into every\n // element of an all-object array whether or not the element\n // itself carries a `_key` (a `json:object` payload can nest a\n // keyless wrapper around keyed content); the walk enters the\n // same nodes, or a renamed key or key fallback on a keyed\n // descendant of a keyless wrapper never gets a path to report\n // against.\n value.forEach((child, index) => {\n const childKey = (child as Node)['_key']\n walk(child as Node, [\n ...path,\n field,\n typeof childKey === 'string' ? {_key: childKey} : index,\n ])\n })\n } else if (typeof value === 'object' && value !== null) {\n walk(value as Node, [...path, field])\n }\n }\n }\n\n result.forEach((block, index) => {\n const key = block['_key']\n // `enforceSiblingKeyUniqueness` enters every top-level node whether\n // or not it carries a `_key` (a `json:object` fence payload needs\n // only `_type`), so the walk must too, or a renamed key inside a\n // keyless block never gets reported. A keyless root contributes\n // its index as a number segment, like a keyless wrapper at any\n // depth.\n walk(block, [typeof key === 'string' ? {_key: key} : index])\n })\n\n if (recorder.skipReason) {\n return {keyMatching: 'skipped', reason: recorder.skipReason, renamedKeys}\n }\n\n const keyFallbacks: Extract<\n ReconciliationReport,\n {keyMatching: 'performed'}\n >['keyFallbacks'] = []\n for (const group of recorder.ambiguousRegionGroups) {\n keyFallbacks.push({\n type: 'ambiguous-region-too-large',\n keys: group\n .map((node) => node['_key'])\n .filter((key): key is string => typeof key === 'string'),\n })\n }\n for (const node of recorder.annotationKeyConflicts) {\n // The walk above reaches every node in `result`, keyless\n // intermediates included, so a key fallback's target `markDefs`\n // entry always has a path by the time the report is built.\n keyFallbacks.push({\n type: 'annotation-key-conflict',\n path: pathByNode.get(node)!,\n })\n }\n\n return {keyMatching: 'performed', preservedKeys, keyFallbacks, renamedKeys}\n}\n"],"mappings":"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"}
|