{"version":3,"sources":["../src/collectHeadings.ts"],"sourcesContent":["// The headings index extension (GROVE_AGENT_SPEC §4): `headings: [{id, text, depth}]`\n// computed from an entry's BODY with the same `@immediately-run/mdx-plugins`\n// heading-anchor canon the render paths use — so an index heading's id IS the id a\n// reader's `#fragment` lands on, and section-level questions are answerable from the\n// index without body reads.\n//\n// IDS CANNOT DRIFT: every id is `headingId(text, { sectionIds })` from mdx-plugins,\n// with the same in-document duplicate counter (`baseId-2`, `-3`, …) the remark pass\n// applies. What this module adds is the WALK — a byte-level ATX scan used where the\n// remark tree is not at hand (the bundler's metadata scan, a dispatched corpus scan).\n// It walks only what corpus entries actually contain: top-level ATX headings\n// (`#`–`######`) outside fenced code blocks; headings carrying an author id\n// (MDX `{#…}`/JSX) are invisible to a byte scan and stay the remark pass's to own.\n//\n// Additive by design: an older index simply lacks `headings`, and readers degrade to\n// body reads (ways_of_working §6).\n\nimport { headingId } from '@immediately-run/mdx-plugins';\nimport type { HeadingSummary } from './metadataQueryTool';\n\nexport type { HeadingSummary };\n\n/**\n * Collect an entry's headings from its MDX/Markdown BODY (frontmatter already\n * stripped — `parseFrontmatter(...).body`).\n *\n * Inline markup is flattened the way the remark pass flattens it (`## The **bold**\n * heading` → \"The bold heading\"), code fences are skipped, and ids follow the canon\n * with per-document duplicate suffixes.\n */\nexport function collectHeadings(body: string, opts: { sectionIds?: boolean } = {}): HeadingSummary[] {\n  const sectionIds = opts.sectionIds !== false;\n  const seen = new Map<string, number>();\n  const out: HeadingSummary[] = [];\n\n  let fenceMarker: string | null = null; // inside a ``` or ~~~ block\n  for (const line of body.split('\\n')) {\n    const fenceMatch = /^(\\s{0,3})(`{3,}|~{3,})/.exec(line);\n    if (fenceMarker) {\n      if (fenceMatch && line.trim().startsWith(fenceMarker)) fenceMarker = null;\n      continue;\n    }\n    if (fenceMatch) {\n      fenceMarker = fenceMatch[2][0].repeat(3);\n      continue;\n    }\n    const h = /^(#{1,6})[ \\t]+(\\S.*)$/.exec(line);\n    if (!h) continue;\n    const text = flattenInline(h[2].trim());\n    if (!text) continue;\n    const baseId = headingId(text, { sectionIds });\n    const n = seen.get(baseId) ?? 0;\n    seen.set(baseId, n + 1);\n    const id = n === 0 ? baseId : `${baseId}-${n}`;\n    out.push({ id, text, depth: h[1].length });\n  }\n  return out;\n}\n\n/** Flatten inline markup the way `headingText` does in the remark pass: emphasis and\n * code markers drop, link/image syntax keeps its text. Covers what corpus headings\n * actually carry; anything else passes through verbatim (and the id canon slugifies\n * punctuation anyway). */\nfunction flattenInline(s: string): string {\n  return s\n    .replace(/!?\\[([^\\]]*)\\]\\([^)]*\\)/g, '$1') // [text](url) / ![alt](url) → text\n    .replace(/`([^`]*)`/g, '$1') // `code` → code\n    .replace(/\\*\\*([^*]+)\\*\\*/g, '$1') // **bold** → bold\n    .replace(/\\*([^*]+)\\*/g, '$1') // *em* → em\n    .replace(/__([^_]+)__/g, '$1') // __bold__ → bold\n    .replace(/_([^_]+)_/g, '$1'); // _em_ → em\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAiBA,yBAA0B;AAanB,SAAS,gBAAgB,MAAc,OAAiC,CAAC,GAAqB;AACnG,QAAM,aAAa,KAAK,eAAe;AACvC,QAAM,OAAO,oBAAI,IAAoB;AACrC,QAAM,MAAwB,CAAC;AAE/B,MAAI,cAA6B;AACjC,aAAW,QAAQ,KAAK,MAAM,IAAI,GAAG;AACnC,UAAM,aAAa,0BAA0B,KAAK,IAAI;AACtD,QAAI,aAAa;AACf,UAAI,cAAc,KAAK,KAAK,EAAE,WAAW,WAAW,EAAG,eAAc;AACrE;AAAA,IACF;AACA,QAAI,YAAY;AACd,oBAAc,WAAW,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC;AACvC;AAAA,IACF;AACA,UAAM,IAAI,yBAAyB,KAAK,IAAI;AAC5C,QAAI,CAAC,EAAG;AACR,UAAM,OAAO,cAAc,EAAE,CAAC,EAAE,KAAK,CAAC;AACtC,QAAI,CAAC,KAAM;AACX,UAAM,aAAS,8BAAU,MAAM,EAAE,WAAW,CAAC;AAC7C,UAAM,IAAI,KAAK,IAAI,MAAM,KAAK;AAC9B,SAAK,IAAI,QAAQ,IAAI,CAAC;AACtB,UAAM,KAAK,MAAM,IAAI,SAAS,GAAG,MAAM,IAAI,CAAC;AAC5C,QAAI,KAAK,EAAE,IAAI,MAAM,OAAO,EAAE,CAAC,EAAE,OAAO,CAAC;AAAA,EAC3C;AACA,SAAO;AACT;AAMA,SAAS,cAAc,GAAmB;AACxC,SAAO,EACJ,QAAQ,4BAA4B,IAAI,EACxC,QAAQ,cAAc,IAAI,EAC1B,QAAQ,oBAAoB,IAAI,EAChC,QAAQ,gBAAgB,IAAI,EAC5B,QAAQ,gBAAgB,IAAI,EAC5B,QAAQ,cAAc,IAAI;AAC/B;","names":[]}