@thallylabs/migrate 0.2.27 → 0.2.29
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 +50 -0
- package/dist/index.d.ts +4 -360
- package/dist/index.js +1288 -33
- package/dist/index.js.map +1 -1
- package/dist/svg-policy.d.ts +16 -0
- package/dist/svg-policy.js +308 -0
- package/dist/svg-policy.js.map +1 -0
- package/dist/url-migration-CDjLZVaN.d.ts +512 -0
- package/dist/url-migration.d.ts +1 -0
- package/dist/url-migration.js +3872 -0
- package/dist/url-migration.js.map +1 -0
- package/package.json +13 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/url-run.ts","../src/navigation.ts","../src/mintlify-extras.ts","../src/path.ts","../src/svg-policy.ts","../src/url-asset-cache.ts","../src/url-journal-clone.ts","../src/url-images.ts","../src/url.ts","../src/analytics.ts","../src/mdx.ts","../src/builtin-components.ts","../src/inline-extraction.ts","../src/frontmatter.ts","../src/components.ts","../src/render.ts"],"sourcesContent":["/**\n * Resumable public URL migration. A bounded batch commits a JSON journal delta;\n * capture, deterministic conversion and validation share the existing adapter.\n * Checkpoints accelerate recovery but hashes and host reconciliation establish\n * which artifacts can be reused. No provider or publication authority lives here.\n */\nimport { createHash } from 'node:crypto'\nimport { load } from 'cheerio'\nimport remarkParse from 'remark-parse'\nimport remarkMdx from 'remark-mdx'\nimport { unified } from 'unified'\nimport { buildNavigationFromPages } from './navigation.js'\nimport { pageIdFromReference } from './path.js'\nimport { isPassiveSvg } from './svg-policy.js'\nimport { createMigrationAssetCache } from './url-asset-cache.js'\nimport { cloneUrlMigrationCheckpoint } from './url-journal-clone.js'\nimport { migrationImageAttribute, migrationImageCandidates, renderMigrationSrcset } from './url-images.js'\nimport { defaultMigrationFetcher, htmlPage, markdownPage, sanitizeRemoteMarkdown, validateMigrationUrl } from './url.js'\nimport type { MigrationBundle, MigrationFetchResponse, MigrationNavigationGroup, MigrationPage } from './types.js'\nimport type {\n MigrationCapture, MigrationCapturedAsset, MigrationEvidence, MigrationNavigationOccurrence,\n UrlMigrationCheckpoint, UrlMigrationIssue, UrlMigrationRecord, UrlMigrationRun,\n UrlMigrationRunOptions, UrlMigrationValidation,\n} from './url-run-types.js'\n\n// Version 2 observes declared HTML language on fresh captures. Never relabel\n// a version-1 journal whose stored page hash predates that observation.\nconst CONVERTER_VERSION = 'url-evidence-v2'\nconst parser = unified().use(remarkParse).use(remarkMdx)\nconst ACCEPT = 'text/markdown,text/html,application/xhtml+xml;q=0.9'\nconst MAX_RESPONSE_BYTES = 8_000_000\nconst MAX_ASSET_BYTES = 10_000_000\nconst MAX_RECORDS = 100_000\nconst LOCALES = new Set(['ar', 'cs', 'da', 'de', 'el', 'en', 'es', 'fi', 'fr', 'he', 'hi', 'hu', 'id', 'it', 'ja', 'ko', 'ms', 'nl', 'no', 'pl', 'pt', 'pt-br', 'ro', 'ru', 'sk', 'sv', 'th', 'tr', 'uk', 'vi', 'zh', 'zh-cn', 'zh-hans', 'zh-hant'])\n\ninterface MarkdownNode {\n type: string\n url?: string\n value?: string\n children?: Array<MarkdownNode>\n attributes?: Array<{ type: string; name?: string; value?: string | { type: string; value: string }; position?: { start: { offset?: number }; end: { offset?: number } } }>\n position?: { start: { offset?: number }; end: { offset?: number } }\n}\n\nfunction hash(value: string | Uint8Array): string {\n return createHash('sha256').update(value).digest('hex')\n}\n\nfunction pageHash(page: MigrationPage): string {\n // JSONB and other durable stores reorder object keys. Identity binds field\n // values recursively, while ordered arrays (code/navigation) keep their order.\n const canonical = (value: unknown): unknown => Array.isArray(value) ? value.map(canonical)\n : value !== null && typeof value === 'object' ? Object.fromEntries(Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, field]) => [key, canonical(field)])) : value\n return hash(JSON.stringify(canonical(page)))\n}\n\nfunction inScope(url: URL, source: URL, scope: string): boolean {\n return url.origin === source.origin && !url.username && !url.password\n && (scope === '/' || url.pathname === scope || url.pathname.startsWith(`${scope}/`))\n}\n\nfunction isDiscoveryIndex(url: URL, source: URL): boolean {\n return url.origin === source.origin && /\\/(?:llms(?:-full)?|robots)\\.txt$|\\/sitemap[^/]*\\.xml$/i.test(url.pathname)\n}\n\nfunction canonical(value: string, base: string): string | null {\n try {\n const url = validateMigrationUrl(new URL(value, base).toString())\n // Query parameters can distinguish source versions/locales; never erase them.\n url.pathname = url.pathname.replace(/\\/+$/, '') || '/'\n return url.toString()\n } catch { return null }\n}\n\n/** Classify using the final response; an index must never become article text. */\nexport function classifyMigrationResponse(document: MigrationFetchResponse): 'page' | 'index' | 'asset' | 'unsupported' {\n if (document.status && document.status >= 400) return 'unsupported'\n if (/\\/(?:llms(?:-full)?|robots)\\.txt$|\\/sitemap[^/]*\\.xml$|\\/search(?:\\.html)?$/i.test(document.finalUrl.pathname)\n || /(?:xml|json)/i.test(document.contentType)) return 'index'\n if (/^(?:image|audio|video)\\//i.test(document.contentType)\n || /\\.(?:pdf|zip|png|jpe?g|webp|gif|svg|ico|mp4|webm)$/i.test(document.finalUrl.pathname)) return 'asset'\n return /(?:html|markdown|text\\/plain)/i.test(document.contentType)\n || /\\.mdx?$/i.test(document.finalUrl.pathname) ? 'page' : 'unsupported'\n}\n\nfunction markdownNodes(body: string): Array<MarkdownNode> {\n const nodes: Array<MarkdownNode> = []\n const visit = (node: MarkdownNode): void => {\n nodes.push(node)\n for (const child of node.children ?? []) visit(child)\n }\n visit(parser.parse(body) as MarkdownNode)\n return nodes\n}\n\nfunction markdownCode(body: string): Array<string> {\n return markdownNodes(body).filter((node) => node.type === 'code').map((node) => node.value ?? '')\n}\n\nfunction alternativeBlocks(body: string, baseline: string): string {\n const blocks = (value: string): Array<string> => ((parser.parse(value) as MarkdownNode).children ?? [])\n .map((node) => value.slice(node.position!.start.offset!, node.position!.end.offset!))\n const originals = new Set(blocks(baseline))\n return blocks(body).filter((block) => !originals.has(block)).join('\\n\\n')\n}\n\nfunction linksOf(document: MigrationFetchResponse): Array<string> {\n if (/html/i.test(document.contentType)) {\n const $ = load(document.body)\n const root = $('main article,article,main,[role=\"main\"]').first()\n root.find('nav,footer,aside').remove()\n return [...root.find('a[href]').toArray(), ...$('link[rel=\"sitemap\"][href],link[rel=\"alternate\"][href]').toArray()]\n .map((node) => $(node).attr('href')!).filter(Boolean)\n }\n if (/xml/i.test(document.contentType) || /<urlset|<sitemapindex/i.test(document.body)) {\n const $ = load(document.body, { xmlMode: true })\n return $('loc').toArray().map((node) => $(node).text().trim())\n }\n return [\n ...markdownNodes(document.body).flatMap((node) => node.url ? [node.url] : []),\n ...[...document.body.matchAll(/^Sitemap:\\s*(\\S+)/gmi)].map((match) => match[1]),\n ]\n}\n\nfunction htmlEvidence(document: MigrationFetchResponse): Pick<MigrationEvidence, 'anchors' | 'code'> {\n const $ = load(document.body)\n const root = $('main article,article,main,[role=\"main\"]').first()\n const article = root.length ? root : $('body')\n article.find('nav,footer,aside,form,script,style').remove()\n return {\n anchors: article.find('[id]').toArray().map((node) => $(node).attr('id')!).filter(Boolean),\n code: article.find('pre').toArray().map((node) => {\n const lines = $(node).find('.token-line')\n return lines.length ? lines.toArray().map((line) => $(line).text()).join('\\n') : $(node).text()\n }),\n }\n}\n\nfunction evidenceOf(capture: MigrationCapture): MigrationEvidence {\n const technical = /html/i.test(capture.contentType)\n ? htmlEvidence(capture)\n : { code: markdownCode(capture.body), anchors: markdownHeadingAnchors(capture.body) }\n return {\n sha256: hash(capture.body), byteLength: Buffer.byteLength(capture.body), finalUrl: capture.finalUrl.toString(), contentType: capture.contentType,\n ...(capture.artifactRef ? { artifactRef: capture.artifactRef } : {}),\n states: (capture.states ?? []).map((state) => ({ id: state.id, label: state.label, sha256: hash(state.response.body) })),\n ...technical, links: linksOf(capture),\n }\n}\n\nfunction markdownHeadingAnchors(body: string): Array<string> {\n const seen = new Map<string, number>()\n return markdownNodes(body).filter((node) => node.type === 'heading').map((node) => {\n const text = (value: MarkdownNode): string => value.value ?? (value.children ?? []).map(text).join('')\n const base = text(node).toLowerCase().replace(/[^\\p{L}\\p{N}\\s_-]/gu, '').replace(/\\s/g, '-')\n const occurrence = seen.get(base) ?? 0\n seen.set(base, occurrence + 1)\n return occurrence ? `${base}-${occurrence}` : base\n })\n}\n\nfunction sourceNavigation(capture: MigrationCapture): Array<MigrationNavigationOccurrence> {\n if (capture.navigation) {\n const resolve = (nodes: Array<MigrationNavigationOccurrence>, depth = 0): Array<MigrationNavigationOccurrence> => {\n if (depth > 32 || nodes.length > 10_000) throw new Error('Source navigation exceeds its depth or width safety bound.')\n return nodes.map((node) => ({ label: node.label, ...(node.url ? { url: new URL(node.url, capture.finalUrl).toString() } : {}), ...(node.children ? { children: resolve(node.children, depth + 1) } : {}) }))\n }\n return resolve(capture.navigation)\n }\n if (!/html/i.test(capture.contentType)) return []\n const $ = load(capture.body)\n const candidates = ['.theme-doc-sidebar-menu,[aria-label*=\"sidebar\" i]', 'aside', '[role=\"navigation\"],nav']\n const root = candidates.map((selector) => $(selector).filter((_index, node) => $(node).find('a[href]').length > 0 && $(node).closest('header').length === 0).first()).find((candidate) => candidate.length) ?? $('aside').first()\n if (!root.length) return []\n const walk = (element: Parameters<typeof $>[0], excluded?: unknown): Array<MigrationNavigationOccurrence> => {\n const result: Array<MigrationNavigationOccurrence> = []\n $(element).children().each((_index, node) => {\n if (node === excluded) return\n const item = $(node)\n if (item.is('a[href]')) {\n result.push({ label: item.text().replace(/\\s+/g, ' ').trim(), url: new URL(item.attr('href')!, capture.finalUrl).toString() })\n } else if (item.is('li,details') || item.attr('data-migration-group') !== undefined || item.children('h2,h3,h4,h5,h6').length > 0) {\n const directLink = item.children('a[href]').first()\n const direct = directLink.length ? directLink : item.children('div').children('a[href]').first()\n const label = direct.text().trim() || item.children('summary,button,span,h2,h3,h4,h5,h6').first().text().trim()\n const children = walk(node, direct[0])\n if (label) result.push({ label, ...(direct.length ? { url: new URL(direct.attr('href')!, capture.finalUrl).toString() } : {}), ...(children.length ? { children } : {}) })\n else result.push(...children)\n } else result.push(...walk(node, excluded))\n })\n return result\n }\n try { return walk(root) } catch { return [] }\n}\n\nfunction idForUrl(url: URL, scope: string): string | null {\n const relative = scope === '/' ? url.pathname.slice(1) : url.pathname.slice(scope.length).replace(/^\\//, '')\n const id = pageIdFromReference(relative || 'introduction', true)\n // Query-selected pages get a deterministic identity, never overwrite a variant.\n return id && `${id}${url.search ? `--${hash(url.search).slice(0, 12)}` : ''}`\n}\n\nfunction convert(document: MigrationFetchResponse, id: string): MigrationPage | null {\n if (/html/i.test(document.contentType)) {\n const technical = htmlEvidence(document)\n const page = htmlPage(document, id, { preserveAnchors: true, preserveResponsiveImages: true, omitDocumentHeading: true }).page\n if (!page) return null\n // HTML already contains the source's visible article copy. Keep its SEO\n // description without inserting a second generated excerpt above the body.\n page.descriptionPlacement = 'body'\n // Restore exact payloads after the shared HTML converter. Replacements use\n // AST offsets, so code containing Markdown or JSX is never treated as prose.\n const nodes = markdownNodes(page.body).filter((node) => node.type === 'code')\n if (nodes.length === technical.code.length) {\n for (let index = nodes.length - 1; index >= 0; index--) {\n const node = nodes[index]\n const start = node.position?.start.offset\n const end = node.position?.end.offset\n if (start === undefined || end === undefined) continue\n const original = page.body.slice(start, end)\n const info = original.split('\\n', 1)[0].replace(/^`+|^~+/, '')\n const code = technical.code[index]\n const fence = '`'.repeat(Math.max(3, ...[...code.matchAll(/`+/g)].map((match) => match[0].length + 1)))\n // The final newline belongs to the fence separator; an existing source\n // newline therefore needs a second newline before the closing fence.\n page.body = `${page.body.slice(0, start)}${fence}${info}\\n${code}\\n${fence}${page.body.slice(end)}`\n }\n }\n return page\n }\n const safe = sanitizeRemoteMarkdown(document.body)\n return safe === null ? null : markdownPage({ ...document, body: safe }, id, 'generic').page\n}\n\nfunction assetReferences(capture: MigrationCapture): Array<string> {\n if (!/html/i.test(capture.contentType)) {\n return markdownNodes(capture.body).filter((node) => node.type === 'image').flatMap((node) => node.url ? [node.url] : [])\n }\n const $ = load(capture.body)\n const root = $('main article,article,main,[role=\"main\"]').first()\n return [\n ...root.find('img[src],video[poster],a[download][href]').toArray().map((node) => $(node).attr('src') ?? $(node).attr('poster') ?? $(node).attr('href')!),\n ...root.find('[srcset]').toArray().flatMap((node) => migrationImageCandidates($(node).attr('srcset') ?? '', capture.finalUrl).map((candidate) => candidate.url)),\n ].filter(Boolean)\n}\n\nfunction unresolvedStaticControls(body: string): boolean {\n const $ = load(body)\n const root = $('main article,article,main,[role=\"main\"]').first()\n const tabs = root.find('[role=\"tab\"]').toArray()\n for (const tab of tabs) {\n const controls = $(tab).attr('aria-controls')\n const panels = controls ? root.find('[id]').filter((_index, node) => $(node).attr('id') === controls) : root.find('[role=\"tabpanel\"]').eq(tabs.indexOf(tab))\n if (panels.length !== 1 || (!panels.text().trim() && !panels.find('pre,img,table,svg').length)\n || panels.find('[aria-busy=\"true\"],[role=\"progressbar\"]').length) return true\n }\n return root.find('details:not([open])').toArray().some((node) => {\n const clone = $(node).clone(); clone.children('summary').remove()\n return !clone.text().trim() && !clone.find('pre,img,table,svg').length\n })\n}\n\nasync function defaultAssetFetcher(url: URL, signal?: AbortSignal): Promise<MigrationCapturedAsset> {\n const response = await fetch(url, { signal: signal ?? AbortSignal.timeout(15_000) })\n if (!response.ok) throw new Error(`Asset returned ${response.status}.`)\n if (Number(response.headers.get('content-length')) > MAX_ASSET_BYTES) throw new Error('Asset exceeds size limit.')\n const reader = response.body?.getReader()\n const chunks: Array<Uint8Array> = []\n let size = 0\n if (reader) while (true) {\n const next = await reader.read()\n if (next.done) break\n size += next.value.length\n if (size > MAX_ASSET_BYTES) { await reader.cancel(); throw new Error('Asset exceeds size limit.') }\n chunks.push(next.value)\n }\n return { url: response.url || url.toString(), contentType: response.headers.get('content-type') ?? '', content: Buffer.concat(chunks) }\n}\n\nfunction acceptedAsset(asset: MigrationCapturedAsset): string | null {\n if (asset.content.byteLength > MAX_ASSET_BYTES) return null\n const mime = asset.contentType.split(';', 1)[0].toLowerCase().trim()\n if (mime === 'image/svg+xml') return isPassiveSvg(asset.content) ? 'svg' : null\n const signature = Buffer.from(asset.content.slice(0, 16))\n const extensions: Record<string, string> = { 'image/png': 'png', 'image/jpeg': 'jpg', 'image/gif': 'gif', 'image/webp': 'webp', 'application/pdf': 'pdf', 'image/avif': 'avif' }\n const extension = extensions[mime]\n if (!extension) return null // HTML and executable downloads are never copied into public assets.\n const isSignatureValid = extension === 'png' ? signature.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]))\n : extension === 'jpg' ? signature[0] === 255 && signature[1] === 216\n : extension === 'gif' ? /^GIF8[79]a/.test(signature.toString())\n : extension === 'webp' ? signature.toString('ascii', 0, 4) === 'RIFF' && signature.toString('ascii', 8, 12) === 'WEBP'\n : extension === 'pdf' ? signature.toString('ascii', 0, 5) === '%PDF-'\n : signature.toString('ascii', 4, 8) === 'ftyp' && /avif|avis/.test(signature.toString('ascii', 8))\n return isSignatureValid ? extension : null\n}\n\nfunction validRecord(record: UrlMigrationRecord): boolean {\n if (record.outcome !== 'imported') return true\n return Boolean(record.page && record.pageSha256 === pageHash(record.page) && record.evidence\n && pageIdFromReference(record.page.id, true) === record.page.id\n && pageIdFromReference(record.page.navigationId, true) === record.page.navigationId\n && sanitizeRemoteMarkdown(record.page.body.replace(/^<a id=\"[^\"<]*\" \\/>$/gm, '')) !== null\n && /^[a-f0-9]{64}$/.test(record.evidence.sha256)\n && record.assets?.every((asset) => /^migration-assets\\/[a-f0-9]{64}\\.[a-z0-9]+$/.test(asset.path)\n && hash(Buffer.from(asset.base64, 'base64')) === asset.sha256))\n}\n\nfunction navigationConfig(navigation: Array<MigrationNavigationOccurrence>, records: Map<string, UrlMigrationRecord>): MigrationBundle['docsConfig'] | null {\n const project = (nodes: Array<MigrationNavigationOccurrence>): Array<string | MigrationNavigationGroup> => nodes.flatMap<string | MigrationNavigationGroup>((node) => {\n const record = node.url ? records.get(canonical(node.url, node.url) ?? '') : undefined\n const target = record?.outcome === 'alias' ? records.get(record.aliasOf ?? '') : record\n const page = target?.page\n const children = project(node.children ?? [])\n if (children.length) {\n const contains = (entries: Array<string | MigrationNavigationGroup>): boolean => entries.some((entry) => typeof entry === 'string' ? entry === page?.navigationId : contains(entry.pages))\n return [{ group: node.label || 'Documentation', pages: [...(page && !contains(children) ? [page.navigationId] : []), ...children] }]\n }\n return page ? [page.navigationId] : []\n })\n const pages = project(navigation)\n return pages.length ? { tabs: [{ tab: 'Documentation', pages }] } : null\n}\n\n/** Bind language to observed capture metadata, scoped URL identity and native HTML evidence. */\nfunction observedLocale(document: MigrationCapture, id: string, scopePath: string): { locale?: string; issues: Array<UrlMigrationIssue> } {\n const issues: Array<UrlMigrationIssue> = []\n const canonicalLanguage = (value: string): string | undefined => {\n if (!/^[a-z]{2,3}(?:-[a-z0-9]{2,8}){0,4}$/i.test(value)) return undefined\n try { return Intl.getCanonicalLocales(value)[0] } catch { return undefined }\n }\n const candidates: Array<{ source: string; value: string }> = []\n if (document.locale) {\n const locale = canonicalLanguage(document.locale)\n if (!locale) throw new Error('Capture locale is not a portable language code.')\n candidates.push({ source: 'capture', value: locale })\n }\n const urlLocale = [id.split('/', 1)[0], ...scopePath.split('/'), document.finalUrl.pathname.split('/').filter(Boolean)[0]]\n .find((segment) => segment && LOCALES.has(segment.toLowerCase()))\n if (urlLocale) candidates.push({ source: 'URL', value: canonicalLanguage(urlLocale)! })\n const declared = /html/i.test(document.contentType) ? load(document.body)('html').attr('lang')?.trim() : undefined\n if (declared) {\n const locale = canonicalLanguage(declared)\n if (locale) candidates.push({ source: 'html lang', value: locale })\n else issues.push({ code: 'unsupported-component', source: document.finalUrl.href, message: 'The source declares an unsupported HTML language; review its locale configuration.' })\n }\n if (new Set(candidates.map((candidate) => candidate.value)).size > 1) {\n issues.push({ code: 'unsupported-component', source: document.finalUrl.href, message: `Observed locale metadata disagrees (${candidates.map((candidate) => `${candidate.source}: ${candidate.value}`).join(', ')}); review the source language.` })\n }\n return { locale: candidates[0]?.value, issues }\n}\n\n/** Match the runtime: primary files are unprefixed; translations require a primary slug. */\nfunction localeProjection(checkpoint: UrlMigrationCheckpoint) {\n const pages = checkpoint.records.flatMap((record) => record.page ? [record.page] : [])\n const canonicalLocale = (page: MigrationPage): string | undefined => {\n try { return page.locale ? Intl.getCanonicalLocales(page.locale)[0] : undefined } catch { return undefined }\n }\n const locales = [...new Set(pages.map(canonicalLocale).filter((locale): locale is string => Boolean(locale)))]\n // A single-language French site must not become an orphan translation of\n // invented English content. Mixed/unprefixed sources retain English primary.\n const defaultLocale = pages.some((page) => !page.locale) || locales.includes('en') ? 'en' : locales[0] ?? 'en'\n const pageId = (page: MigrationPage): string => {\n const locale = canonicalLocale(page)\n return locale ? `${locale === defaultLocale ? '' : `${locale}/`}${page.navigationId}` : page.id\n }\n const route = (page: MigrationPage): string => {\n const locale = canonicalLocale(page)\n const prefix = locale && locale !== defaultLocale ? `/${locale}` : ''\n return `${prefix}${page.navigationId === 'introduction' ? '/' : `/${page.navigationId}`}`\n }\n return { canonicalLocale, locales, defaultLocale, pageId, route }\n}\n\n/** Validate every selected outcome, source payload, internal destination and anchor. */\nexport function validateUrlMigration(checkpoint: UrlMigrationCheckpoint, cancelled = false): UrlMigrationValidation {\n const issues: Array<UrlMigrationIssue> = []\n const counts: UrlMigrationValidation['counts'] = { pending: 0, imported: 0, excluded: 0, failed: 0, alias: 0 }\n const records = new Map(checkpoint.records.map((record) => [record.url, record]))\n const source = new URL(checkpoint.sourceUrl)\n const projection = localeProjection(checkpoint)\n const primaryIds = new Set(checkpoint.records.flatMap((record) => record.page\n && (!record.page.locale || projection.canonicalLocale(record.page) === projection.defaultLocale) ? [record.page.navigationId] : []))\n const outputIds = new Set<string>()\n const resolve = (url: string): UrlMigrationRecord | undefined => {\n const record = records.get(canonical(url, checkpoint.sourceUrl) ?? '')\n return record?.outcome === 'alias' ? records.get(record.aliasOf ?? '') : record\n }\n for (const record of checkpoint.records) {\n counts[record.outcome]++\n issues.push(...record.issues ?? [])\n if (record.outcome === 'pending' || record.outcome === 'failed') issues.push({ code: 'missing-page', source: record.url, message: record.reason ?? `Inventory entry is ${record.outcome}.` })\n if (!record.page || !record.evidence) continue\n const outputId = projection.pageId(record.page)\n if (outputIds.has(outputId)) issues.push({ code: 'collision', source: record.url, message: `Multiple source pages map to runtime path ${outputId}.` })\n outputIds.add(outputId)\n if (record.page.locale && projection.canonicalLocale(record.page) !== projection.defaultLocale && !primaryIds.has(record.page.navigationId)) {\n issues.push({ code: 'missing-page', source: record.url, message: `Secondary locale ${record.page.locale} requires a primary ${projection.defaultLocale} page at ${record.page.navigationId}; the runtime cannot serve an orphan translation.` })\n }\n const actual = markdownCode(record.page.body)\n if (JSON.stringify(actual) !== JSON.stringify(record.evidence.code)) issues.push({ code: 'code-mismatch', source: record.url, message: 'Source code payloads differ from converted code payloads.' })\n for (const value of record.evidence.links) {\n let link: URL\n try { link = new URL(value, record.evidence.finalUrl) } catch { continue }\n if (!inScope(link, source, checkpoint.scopePath)) continue\n if (/\\.(?:png|jpe?g|gif|svg|webp|avif|pdf|zip|mp4|webm|css|js|json|ya?ml)$/i.test(link.pathname)) continue\n const destination = resolve(link.toString())\n if (!destination || !['imported', 'excluded'].includes(destination.outcome)) {\n issues.push({ code: 'missing-page', source: record.url, message: `Unresolved internal destination: ${link}` })\n } else if (link.hash && destination.outcome === 'imported') {\n let anchor = link.hash.slice(1)\n try { anchor = decodeURIComponent(anchor) } catch { /* Invalid encoding remains a literal anchor. */ }\n if (!destination.evidence?.anchors.includes(anchor)) issues.push({ code: 'missing-anchor', source: record.url, message: `Missing source anchor: ${link}` })\n }\n }\n }\n const visit = (nodes: Array<MigrationNavigationOccurrence>): void => {\n for (const node of nodes) {\n if (node.url) {\n const url = new URL(node.url, source)\n if (inScope(url, source, checkpoint.scopePath) && !resolve(url.toString())?.page) issues.push({ code: 'navigation-missing', source: node.url, message: `Source navigation target ${node.label} was not imported.` })\n }\n visit(node.children ?? [])\n }\n }\n visit(checkpoint.navigation)\n if (!counts.imported) issues.push({ code: 'missing-page', source: checkpoint.sourceUrl, message: 'No readable article pages were imported.' })\n return { status: cancelled ? 'cancelled' : issues.length ? 'partial' : 'passed', issues, counts }\n}\n\n/** Run capture/conversion in bounded batches and resume only reconciled artifacts. */\nexport async function runUrlMigration(options: UrlMigrationRunOptions): Promise<UrlMigrationRun> {\n const source = validateMigrationUrl(options.sourceUrl)\n const sourceUrl = canonical(source.toString(), source.toString())!\n const requestedScope = options.scopePath ?? (source.pathname.endsWith('/') ? source.pathname : source.pathname.slice(0, source.pathname.lastIndexOf('/')) || '/')\n // A regex anchored only at the end retries every slash-run start when a\n // hostile suffix prevents a match. Scan the tail once before validation.\n let scopeEnd = requestedScope.length\n while (scopeEnd > 0 && requestedScope.charCodeAt(scopeEnd - 1) === 47) scopeEnd--\n const scopePath = requestedScope.slice(0, scopeEnd) || '/'\n if (!scopePath.startsWith('/') || scopePath.includes('?') || scopePath.includes('#') || !inScope(source, source, scopePath)) throw new Error('The selected documentation scope must contain the source URL.')\n const maxPages = options.maxPages ?? 1_000\n if (!Number.isInteger(maxPages) || maxPages < 1 || maxPages > 10_000) throw new Error('maxPages must be between 1 and 10000.')\n const concurrency = Math.max(1, Math.min(32, Math.floor(options.concurrency ?? 5)))\n const batchSize = Math.max(1, Math.min(500, Math.floor(options.batchSize ?? 50)))\n if (!Number.isFinite(concurrency) || !Number.isFinite(batchSize)\n || (options.maxCaptures !== undefined && (!Number.isInteger(options.maxCaptures) || options.maxCaptures < 1))\n || (options.maxTotalBytes !== undefined && (!Number.isFinite(options.maxTotalBytes) || options.maxTotalBytes < 1))) throw new Error('Migration batch, concurrency and byte limits must be positive finite numbers.')\n const captureVersion = options.captureVersion ?? 'fetch-v1'\n if (options.checkpoint && options.checkpoint.records.length > MAX_RECORDS) throw new Error('Checkpoint inventory exceeds its bound.')\n const checkpoint: UrlMigrationCheckpoint = options.checkpoint\n ? cloneUrlMigrationCheckpoint(options.checkpoint)\n : { schemaVersion: 1, converterVersion: CONVERTER_VERSION, sourceUrl, scopePath, captureVersion, records: [], navigation: [] }\n if (checkpoint.schemaVersion !== 1 || checkpoint.converterVersion !== CONVERTER_VERSION || checkpoint.sourceUrl !== sourceUrl || checkpoint.scopePath !== scopePath || checkpoint.captureVersion !== captureVersion) throw new Error('Checkpoint does not match this source, scope or capture/converter version.')\n if (checkpoint.records.length > MAX_RECORDS) throw new Error('Checkpoint inventory exceeds its bound.')\n const records = new Map<string, UrlMigrationRecord>()\n for (const record of checkpoint.records) {\n if (canonical(record.url, sourceUrl) !== record.url || (!inScope(new URL(record.url), source, scopePath) && !isDiscoveryIndex(new URL(record.url), source))) throw new Error('Checkpoint contains an unsafe inventory URL.')\n if (records.has(record.url)) throw new Error('Checkpoint contains a duplicate inventory URL.')\n if (!validRecord(record) || (record.outcome === 'failed' && options.retryFailed !== false)\n || (record.outcome !== 'pending' && options.reconcile && !await options.reconcile(record))) {\n record.outcome = 'pending'; delete record.page; delete record.pageSha256; delete record.evidence; delete record.assets\n }\n if (record.page && pageIdFromReference(record.page.id, true) !== record.page.id) throw new Error('Checkpoint contains an unsafe page path.')\n records.set(record.url, record)\n }\n const pending: Array<UrlMigrationRecord> = [...records.values()].filter((record) => record.outcome === 'pending')\n let changed: Array<UrlMigrationRecord> = []\n let navigationChanged = false\n const enqueue = (value: string, base: string, discoveredBy: string): void => {\n const url = canonical(value, base)\n if (!url || (!inScope(new URL(url), source, scopePath) && !isDiscoveryIndex(new URL(url), source))) return\n const existing = records.get(url)\n if (existing) {\n if (!existing.discoveredBy.includes(discoveredBy)) { existing.discoveredBy.push(discoveredBy); changed.push(existing) }\n return\n }\n if (records.size >= MAX_RECORDS) throw new Error('Discovered URL inventory exceeded the 100000 URL safety bound.')\n const record: UrlMigrationRecord = { url, discoveredBy: [discoveredBy], outcome: 'pending' }\n records.set(url, record); checkpoint.records.push(record); pending.push(record); changed.push(record)\n }\n enqueue(sourceUrl, sourceUrl, 'submitted')\n for (const path of ['llms.txt', 'sitemap.xml', 'robots.txt']) enqueue(`${scopePath === '/' ? '' : scopePath}/${path}`, sourceUrl, 'discovery-index')\n if (scopePath !== '/') for (const path of ['llms.txt', 'sitemap.xml', 'robots.txt']) enqueue(`/${path}`, sourceUrl, 'discovery-index')\n const fetcher = options.fetcher ?? defaultMigrationFetcher\n const capture = options.captureProvider?.capture.bind(options.captureProvider)\n ?? (async ({ url }: { url: URL }): Promise<MigrationCapture> => fetcher(url, { accept: ACCEPT }))\n const assetFetcher = options.assetFetcher ?? (!options.fetcher && !options.captureProvider ? defaultAssetFetcher : undefined)\n const usedIds = new Map([...records.values()].filter((record) => record.page).map((record) => [record.page!.id, record.url]))\n const finalUrls = new Map([...records.values()].filter((record) => record.evidence && record.page).map((record) => [canonical(record.evidence!.finalUrl, sourceUrl)!, record.url]))\n let imported = usedIds.size\n const assetPayloads = new Map(checkpoint.records.flatMap((record) => (record.assets ?? []).map((asset) => [asset.sha256, asset] as const)))\n // URL aliases retain their own provenance, but never their own copy of an\n // identical binary payload. Normalize before a cached promise fulfills:\n // replacing only the local awaited value leaves the original Buffer alive.\n const assetBytes = new Map<string, Uint8Array>()\n const retainAsset = (asset: MigrationCapturedAsset): MigrationCapturedAsset => {\n if (!acceptedAsset(asset)) throw new Error('Asset MIME/signature is unsupported or unsafe to serve.')\n const sha256 = hash(asset.content)\n // Own the retained bytes: adapters may recycle their capture buffers after\n // returning them, while checkpoint hashes must stay bound to this snapshot.\n const content = assetBytes.get(sha256) ?? Buffer.from(asset.content)\n assetBytes.set(sha256, content)\n return { ...asset, content }\n }\n const assetRequests = createMigrationAssetCache()\n for (const record of checkpoint.records) for (const asset of record.assets ?? []) {\n if (!assetRequests.has(asset.url)) {\n const content = assetBytes.get(asset.sha256) ?? Buffer.from(asset.base64, 'base64')\n assetBytes.set(asset.sha256, content)\n assetRequests.remember(asset.url, { url: asset.url, contentType: asset.contentType, content })\n }\n }\n let totalBytes = checkpoint.records.reduce((sum, record) => sum + (record.evidence?.byteLength ?? 0), 0)\n + [...assetPayloads.values()].reduce((sum, asset) => sum + Buffer.byteLength(asset.base64, 'base64'), 0)\n let budgetExhausted = false\n let cursor = 0\n const flush = async (): Promise<void> => {\n const distinct = [...new Map(changed.map((record) => [record.url, record])).values()]\n if (options.onCheckpoint) for (let index = 0; index < distinct.length || (index === 0 && navigationChanged); index += batchSize) {\n await options.onCheckpoint({ schemaVersion: 1, converterVersion: CONVERTER_VERSION, scopePath, captureVersion, sourceUrl, records: distinct.slice(index, index + batchSize), ...(navigationChanged && index === 0 ? { navigation: checkpoint.navigation } : {}) })\n }\n changed = []; navigationChanged = false\n }\n const process = async (record: UrlMigrationRecord): Promise<void> => {\n if (options.signal?.aborted) return\n try {\n delete record.reason\n const document = await capture({ url: new URL(record.url), signal: options.signal })\n if (options.signal?.aborted) return\n if (Buffer.byteLength(document.body) > MAX_RESPONSE_BYTES) throw new Error('Captured response exceeds the 8 MB safety bound.')\n const final = canonical(document.finalUrl.toString(), sourceUrl)!\n const role = classifyMigrationResponse(document)\n if (!inScope(document.finalUrl, source, scopePath) && !(role === 'index' && isDiscoveryIndex(document.finalUrl, source))) { record.outcome = 'excluded'; record.reason = 'Redirected outside the selected origin/path.'; return }\n if (document.status && document.status >= 400) throw new Error(`Source page returned HTTP ${document.status}.`)\n if (role !== 'page') {\n totalBytes += Buffer.byteLength(document.body)\n if (totalBytes > (options.maxTotalBytes ?? 500_000_000)) { budgetExhausted = true; throw new Error('Migration exceeded its content byte budget.') }\n record.outcome = 'excluded'; record.reason = `Final response role: ${role}.`\n const links = role === 'index' ? linksOf(document) : []\n record.evidence = { sha256: hash(document.body), byteLength: Buffer.byteLength(document.body), finalUrl: final, contentType: document.contentType, ...(document.artifactRef ? { artifactRef: document.artifactRef } : {}), states: [], code: [], anchors: [], links }\n for (const link of links) enqueue(link, final, record.url)\n return\n }\n if (imported >= maxPages) { record.outcome = 'excluded'; record.reason = 'A discovery endpoint resolved to an article after the selected page limit.'; return }\n const already = finalUrls.get(final)\n if (already && already !== record.url) { record.outcome = 'alias'; record.aliasOf = already; return }\n // Claim final identity before conversion can yield to asset acquisition.\n finalUrls.set(final, record.url)\n const id = idForUrl(document.finalUrl, scopePath)\n if (!id) throw new Error('Source URL has no safe destination path.')\n if (usedIds.has(id) && usedIds.get(id) !== record.url) { record.outcome = 'failed'; record.reason = `Destination collision for ${id}.`; return }\n usedIds.set(id, record.url)\n const evidence = evidenceOf(document)\n const page = convert(document, id)\n if (!page) { record.outcome = 'failed'; record.reason = 'Content could not be represented as safe inert MDX.'; return }\n const states = document.states ?? []\n if (states.length > 64) throw new Error('Capture contains too many alternative states.')\n for (const state of states) {\n if (Buffer.byteLength(state.response.body) > MAX_RESPONSE_BYTES) throw new Error('Alternative state exceeds the 8 MB safety bound.')\n if (!inScope(state.response.finalUrl, source, scopePath) || classifyMigrationResponse(state.response) !== 'page') throw new Error('Capture state escaped its selected scope or article role.')\n const alternative = convert(state.response, id)\n if (!alternative) throw new Error(`Alternative state ${state.id} is not inert content.`)\n const stateCode = /html/i.test(state.response.contentType) ? htmlEvidence(state.response).code : markdownCode(state.response.body)\n evidence.states.find((entry) => entry.id === state.id)!.code = stateCode\n const difference = alternativeBlocks(alternative.body, page.body)\n evidence.code.push(...markdownCode(difference))\n evidence.links.push(...linksOf(state.response))\n evidence.anchors.push(...(/html/i.test(state.response.contentType) ? htmlEvidence(state.response).anchors : markdownHeadingAnchors(state.response.body)))\n if (difference) page.body += `\\n\\n### ${state.label.replace(/[<>{}#\\n\\r]/g, '')}\\n\\n${difference}`\n }\n totalBytes += Buffer.byteLength(document.body) + states.reduce((sum, state) => sum + Buffer.byteLength(state.response.body), 0)\n evidence.byteLength = Buffer.byteLength(document.body) + states.reduce((sum, state) => sum + Buffer.byteLength(state.response.body), 0)\n if (totalBytes > (options.maxTotalBytes ?? 500_000_000)) { budgetExhausted = true; throw new Error('Migration exceeded its content byte budget.') }\n record.assets = []\n record.issues = []\n if (/html/i.test(document.contentType)) {\n const $ = load(document.body)\n const root = $('main article,article,main,[role=\"main\"]').first()\n root.find('nav,footer,aside,.theme-doc-breadcrumbs,.theme-doc-toc-mobile,.table-of-contents,.hash-link').remove()\n root.find('button svg,a.hash-link svg,[aria-hidden=\"true\"] svg,svg[aria-hidden=\"true\"],svg[data-lucide],svg[data-icon]').remove()\n if (root.find('h1').length > 1) record.issues.push({ code: 'unsupported-component', source: record.url, message: 'The article contains multiple level-one headings; review their hierarchy.' })\n const unsupported = [...new Set(root.find('iframe,object,embed,canvas,svg,form,input,textarea,select,math,video,audio').toArray().map((node) => node.tagName))]\n if (unsupported.length) record.issues.push({ code: 'unsupported-component', source: record.url, message: `Rendered content requires explicit review for: ${unsupported.join(', ')}.` })\n if (!states.length && unresolvedStaticControls(document.body)) record.issues.push({ code: 'unsupported-component', source: record.url, message: 'An interactive alternative has no complete captured panel or disclosure content.' })\n }\n const suppliedAssets = new Map((document.assets ?? []).map((asset) => [canonical(asset.url, final), asset]))\n const assetUrls = [...new Set([...assetReferences(document), ...states.flatMap((state) => assetReferences(state.response))].map((url) => canonical(url, final)).filter((url): url is string => Boolean(url)))]\n if (assetUrls.length > 256 || (document.assets?.length ?? 0) > 256) throw new Error('Capture exceeds the 256 assets per page safety bound.')\n for (const url of assetUrls) {\n try {\n let assetRequest = assetRequests.get(url)\n if (!suppliedAssets.has(url) && !assetRequest && assetFetcher) {\n assetRequest = assetRequests.load(url, () => assetFetcher(new URL(url), options.signal).then(retainAsset))\n }\n const supplied = suppliedAssets.get(url)\n const asset = supplied ? retainAsset(supplied) : (assetRequest ? await assetRequest : null)\n if (!asset) throw new Error('Capture host did not supply asset bytes or a binary fetch boundary.')\n const extension = acceptedAsset(asset)\n if (!extension) throw new Error('Asset MIME/signature is unsupported or unsafe to serve.')\n const sha256 = hash(asset.content)\n const existing = assetPayloads.get(sha256)\n if (!existing) {\n totalBytes += asset.content.byteLength\n if (totalBytes > (options.maxTotalBytes ?? 500_000_000)) { budgetExhausted = true; throw new Error('Migration exceeded its content and asset byte budget.') }\n }\n const path = `migration-assets/${sha256}.${extension}`\n const receipt = { url, path, sha256, contentType: asset.contentType, base64: existing?.base64 ?? Buffer.from(asset.content).toString('base64') }\n record.assets.push(receipt)\n if (!existing) assetPayloads.set(sha256, receipt)\n if (!assetRequests.has(url)) assetRequests.remember(url, asset)\n // Only prose destinations are rewritten; example code is immutable.\n page.body = rewriteMarkdownDestinations(page.body, (destination) => canonical(destination, final) === url ? `/${path}${new URL(destination, final).hash}` : destination)\n } catch (error) {\n assetRequests.delete(url)\n record.issues.push({ code: 'asset-failed', source: record.url, message: `${url}: ${error instanceof Error ? error.message : String(error)}` })\n }\n }\n if (options.signal?.aborted) return\n if (sanitizeRemoteMarkdown(page.body.replace(/^<a id=\"[^\"<]*\" \\/>$/gm, '')) === null) throw new Error('Converted content did not pass inert MDX validation.')\n record.evidence = evidence\n record.page = page\n const language = observedLocale(document, page.id, scopePath)\n record.issues.push(...language.issues)\n const canonicalLocale = language.locale\n if (canonicalLocale) {\n page.locale = canonicalLocale\n const segments = page.id.split('/')\n const localeIndex = segments.findIndex((segment) => segment.toLowerCase() === canonicalLocale.toLowerCase())\n page.navigationId = segments.filter((_segment, index) => index !== localeIndex).join('/') || 'introduction'\n page.id = `${canonicalLocale}/${page.navigationId}`\n if (usedIds.has(page.id) && usedIds.get(page.id) !== record.url) throw new Error(`Destination collision for locale page ${page.id}.`)\n usedIds.delete(id); usedIds.set(page.id, record.url)\n }\n record.version = document.version ?? [...page.id.split('/'), ...document.finalUrl.pathname.split('/')].find((segment) => /^v\\d+(?:\\.\\d+)*$/i.test(segment))\n record.pageSha256 = pageHash(page)\n record.outcome = 'imported'\n imported++\n const navigation = checkpoint.navigation.length ? [] : sourceNavigation(document)\n if (!checkpoint.navigation.length && navigation.length) { checkpoint.navigation = navigation; navigationChanged = true }\n for (const link of evidence.links) {\n try {\n const url = new URL(link, final)\n if (!/\\.(?:png|jpe?g|gif|svg|webp|avif|pdf|zip|mp4|webm|css|js)$/i.test(url.pathname)) enqueue(link, final, record.url)\n } catch { /* Malformed links remain visible in source evidence. */ }\n }\n const visit = (nodes: Array<MigrationNavigationOccurrence>): void => {\n for (const node of nodes) { if (node.url) enqueue(node.url, final, 'source-navigation'); visit(node.children ?? []) }\n }\n visit(navigation)\n } catch (error) {\n if (options.signal?.aborted) return\n const wasImported = record.outcome === 'imported'\n record.outcome = record.discoveredBy.every((origin) => origin === 'discovery-index') ? 'excluded' : 'failed'\n record.reason = error instanceof Error ? error.message : String(error)\n if (record.page) {\n if (wasImported) imported--\n usedIds.delete(record.page.id)\n }\n delete record.page; delete record.pageSha256; delete record.assets\n } finally {\n if (record.outcome !== 'pending') {\n if (options.onRecord) await options.onRecord(record)\n changed.push(record)\n }\n }\n }\n while (cursor < pending.length && !options.signal?.aborted && !budgetExhausted && cursor < (options.maxCaptures ?? Infinity)) {\n if (imported >= maxPages) {\n const index = pending.findIndex((record, position) => position >= cursor && isDiscoveryIndex(new URL(record.url), source))\n if (index < 0) break\n pending.splice(cursor, 0, pending.splice(index, 1)[0])\n }\n const batch = pending.slice(cursor, cursor + Math.min(concurrency, imported >= maxPages ? 1 : maxPages - imported, (options.maxCaptures ?? Infinity) - cursor))\n cursor += batch.length\n await Promise.all(batch.map(process))\n if (changed.length >= batchSize) await flush()\n }\n await flush()\n // Link maps are derived from every committed route, including redirect aliases.\n const projection = localeProjection(checkpoint)\n const routeMap = new Map<string, string>()\n for (const record of checkpoint.records) {\n const target = record.outcome === 'alias' ? records.get(record.aliasOf ?? '') : record\n if (target?.page) {\n const route = projection.route(target.page)\n routeMap.set(record.url, route)\n if (target.evidence) routeMap.set(canonical(target.evidence.finalUrl, sourceUrl)!, route)\n }\n }\n const navigationLabels = new Map<string, string>()\n const navigationIssues: Array<UrlMigrationIssue> = []\n const labelPages = (nodes: Array<MigrationNavigationOccurrence>): void => {\n for (const node of nodes) {\n const entry = node.url ? records.get(canonical(node.url, sourceUrl) ?? '') : undefined\n const target = entry?.outcome === 'alias' ? records.get(entry.aliasOf ?? '') : entry\n if (target?.page && node.label && !node.children?.length) {\n const previous = navigationLabels.get(target.url)\n if (previous && previous !== node.label) navigationIssues.push({ code: 'unsupported-component', source: target.url, message: `This page has multiple source navigation labels (${previous}, ${node.label}); review the shared sidebar title.` })\n else navigationLabels.set(target.url, node.label)\n }\n labelPages(node.children ?? [])\n }\n }\n labelPages(checkpoint.navigation)\n const pages = checkpoint.records.flatMap((record) => record.page ? [{ ...record.page, id: projection.pageId(record.page), ...(record.page.locale ? { locale: projection.canonicalLocale(record.page) } : {}), ...(navigationLabels.has(record.url) ? { navTitle: navigationLabels.get(record.url) } : {}), body: rewriteMarkdownDestinations(record.page.body, (value) => {\n let url: URL\n try { url = new URL(value, record.evidence!.finalUrl) } catch { return value }\n const route = routeMap.get(canonical(url.toString(), sourceUrl)!)\n return route ? `${route}${url.hash}` : value\n }) }] : [])\n const validation = validateUrlMigration(checkpoint, Boolean(options.signal?.aborted))\n validation.issues.push(...navigationIssues)\n if (navigationIssues.length && validation.status === 'passed') validation.status = 'partial'\n const assets = new Map<string, MigrationBundle['assets'][number]>()\n for (const record of checkpoint.records) for (const asset of record.assets ?? []) {\n if (!assets.has(asset.path)) {\n const content = assetBytes.get(asset.sha256) ?? Buffer.from(asset.base64, 'base64')\n // Keep the public bundle's historical Buffer JSON shape, including when\n // an adapter supplied a plain Uint8Array or a view into a larger buffer.\n assets.set(asset.path, { path: asset.path, content: Buffer.from(content.buffer, content.byteOffset, content.byteLength) })\n }\n }\n const bundle: MigrationBundle = {\n sourceUrl, sourceKind: 'url', platform: options.platform ?? 'generic', pages, assets: [...assets.values()],\n docsConfig: navigationConfig(checkpoint.navigation, records) ?? buildNavigationFromPages(pages),\n warnings: validation.issues.map((issue) => ({ code: issue.code === 'missing-page' ? 'missing-page' : 'unsupported-config', message: issue.message, source: issue.source })),\n urlMigrationValidation: validation,\n stats: { discovered: records.size, imported: pages.length, skipped: records.size - pages.length },\n }\n if (projection.locales.length) {\n const locales = [...new Set([projection.defaultLocale, ...projection.locales])]\n bundle.docsConfig.i18n = { defaultLocale: projection.defaultLocale, locales: locales.map((code) => ({ code, label: code === 'en' ? 'English' : code })) }\n }\n const stopReason = options.signal?.aborted ? 'cancelled'\n : budgetExhausted ? 'byte-limit'\n : cursor < pending.length && cursor >= (options.maxCaptures ?? Infinity) ? 'capture-limit'\n : imported >= maxPages && cursor < pending.length ? 'page-limit'\n : cursor < pending.length ? 'capture-limit' : 'complete'\n return { bundle, checkpoint, validation, stopReason }\n}\n\nfunction rewriteMarkdownDestinations(body: string, rewrite: (value: string) => string): string {\n const edits = markdownNodes(body).filter((node) => node.url && node.position).map((node) => {\n const start = node.position!.start.offset!\n const end = node.position!.end.offset!\n const original = body.slice(start, end)\n const replacement = rewrite(node.url!)\n if (replacement === node.url) return null\n const index = original.lastIndexOf(node.url!)\n if (index < 0) return null\n return { start: start + index, end: start + index + node.url!.length, replacement }\n }).filter((edit): edit is { start: number; end: number; replacement: string } => edit !== null).sort((a, b) => b.start - a.start)\n for (const node of markdownNodes(body)) for (const attribute of node.attributes ?? []) {\n if (!attribute.name || !['src', 'href', 'srcSet', 'srcset', 'img', 'primaryHref', 'secondaryHref'].includes(attribute.name) || !attribute.position) continue\n let value = attribute.value\n if (typeof value !== 'string') {\n try { value = JSON.parse(value?.value ?? '') as string } catch { continue }\n }\n if (typeof value !== 'string') continue\n let replacement: string\n if (/^srcset$/i.test(attribute.name)) {\n const candidates = migrationImageCandidates(value, new URL('https://migration.invalid'), false)\n replacement = renderMigrationSrcset(candidates.map((candidate) => ({ ...candidate, url: rewrite(candidate.url) })))\n if (replacement === renderMigrationSrcset(candidates)) continue\n } else { replacement = rewrite(value); if (replacement === value) continue }\n const start = attribute.position.start.offset, end = attribute.position.end.offset\n if (start === undefined || end === undefined) continue\n edits.push({ start, end, replacement: `${attribute.name}=\"${migrationImageAttribute(replacement)}\"` })\n }\n edits.sort((a, b) => b.start - a.start)\n for (const edit of edits) body = body.slice(0, edit.start) + edit.replacement + body.slice(edit.end)\n return body\n}\n","/**\n * Navigation projection for imported sites. Mintlify's schema is intentionally\n * more expressive than Thally's tab/group model, so complex containers are\n * flattened predictably while preserving page order and nested groups.\n */\n\nimport { existsSync, lstatSync, readFileSync, realpathSync } from 'node:fs'\nimport { dirname, extname, relative } from 'node:path'\n\nimport { descendNavigationAccess, hasNavigationRules, isNavigationRestricted, navigationAccess, navigationGateReason, OPEN_NAVIGATION_ACCESS, playgroundDisplay } from './mintlify-extras.js'\nimport type { NavigationAccess } from './mintlify-extras.js'\nimport { mintlifyLocalizedReference, pageIdFromReference, resolveWithin, trimEdgeSlashes } from './path.js'\nimport type {\n MigrationDocsConfig,\n MigrationNavigationGroup,\n MigrationNavigationTab,\n MigrationPage,\n MigrationWarning,\n} from './types.js'\n\ninterface MintlifyPageReference {\n ref: string\n navigationId: string\n locale?: string\n}\n\nconst MAX_MINTLIFY_CONFIG_BYTES = 20_000_000\n\n/**\n * One appearance of a page reference in navigation, with the access rules of\n * every container above it. Thally reads access only from page frontmatter,\n * so these are pushed down into the frontmatter of the pages they cover.\n */\nexport interface MintlifyReferenceAccess {\n ref: string\n access: NavigationAccess\n}\n\nexport interface MintlifyNavigationResult {\n docsConfig: MigrationDocsConfig\n pageReferences: Array<MintlifyPageReference>\n /**\n * Access imposed by navigation containers, one entry per appearance of a\n * page (open appearances included, so a page listed in several places can\n * be given the strictest rule). Restricted pages stay in `docsConfig`.\n */\n referenceAccess: Array<MintlifyReferenceAccess>\n /** `sourceRef` mounts under a restricted container; never resolved, so nothing of theirs is imported. */\n gatedSourceRefs: Array<string>\n warnings: Array<MigrationWarning>\n}\n\nexport interface MintlifyProjectionOptions {\n /** Public URL migrations may expose page hrefs including the docs mount path. */\n pathPrefix?: string\n /**\n * Resolves a `sourceRef` repository (`owner/repo`) to the navigation entries\n * of its imported docs, already prefixed with their mount path. Returns null\n * when the repository was not mapped with `--source-ref`.\n */\n resolveSourceRef?: (repo: string) => Array<string | MigrationNavigationGroup> | null\n}\n\n/** Preserve Mintlify's implicit `/section` route for section landing pages. */\nexport function addMintlifyDirectoryRedirects(\n config: MigrationDocsConfig,\n pages: Array<MigrationPage>,\n): MigrationDocsConfig {\n const pageIds = new Set(pages.map((page) => page.id))\n const redirects = [...(config.redirects ?? [])]\n const redirectSources = new Set(redirects.map((redirect) => redirect.source.replace(/\\/$/, '') || '/'))\n function hasAuthoredRedirect(source: string): boolean {\n if (redirectSources.has(source)) return true\n return (config.redirects ?? []).some((redirect) => {\n // A trailing catch-all also owns its empty suffix. Do not insert an\n // exact alias that competes with that authored directory behavior.\n const match = redirect.source.match(/^(.*)\\/:[A-Za-z_][A-Za-z0-9_]*\\*\\/?$/)\n return match ? source === match[1] || source.startsWith(`${match[1]}/`) : false\n })\n }\n for (const page of pages) {\n if (!/\\/(?:overview|introduction)$/.test(page.navigationId)) continue\n const parent = page.id.replace(/\\/(?:overview|introduction)$/, '')\n if (!parent || pageIds.has(parent)) continue\n const source = `/${parent}`\n if (hasAuthoredRedirect(source)) continue\n redirects.push({ source, destination: `/${page.id}`, permanent: false })\n redirectSources.add(source)\n }\n\n const navigationOrder: Array<string> = []\n function collectPages(nodes: Array<string | MigrationNavigationGroup>): void {\n for (const node of nodes) {\n if (typeof node === 'string') navigationOrder.push(node)\n else if (!node.hidden) collectPages(node.pages)\n }\n }\n for (const tab of config.tabs) {\n if (!tab.hidden) collectPages([...(tab.pages ?? []), ...(tab.groups ?? [])])\n }\n const pagesByNavigationId = new Map<string, Array<MigrationPage>>()\n for (const page of pages) {\n if (page.hidden) continue\n const variants = pagesByNavigationId.get(page.navigationId) ?? []\n variants.push(page)\n pagesByNavigationId.set(page.navigationId, variants)\n }\n const firstDescendantByDirectory = new Map<string, MigrationPage>()\n for (const navigationId of navigationOrder) {\n for (const page of pagesByNavigationId.get(navigationId) ?? []) {\n const segments = page.id.split('/')\n for (let length = 1; length < segments.length; length++) {\n const directory = segments.slice(0, length).join('/')\n if (!firstDescendantByDirectory.has(directory)) firstDescendantByDirectory.set(directory, page)\n }\n }\n }\n // Mintlify 307s *any* nav-shaped directory path with no page of its own to\n // its first descendant page in navigation order — not only ones an\n // overview/introduction page or an authored redirect already pointed at\n // (e.g. a `product`/`tab`/`group` container path like Upstash's `/redis`,\n // or a plain mid-tree directory like `/vector/sdks/py/example_calls`).\n // `firstDescendantByDirectory` already holds every such directory (built\n // from real page ids in navigation order above), so redirect all of them\n // in one pass; `hasAuthoredRedirect` skips any directory the overview/\n // introduction pass above, or the project's own `redirects:`, already\n // covers, and `pageIds.has(directory)` never overrides a real page.\n for (const [directory, landing] of firstDescendantByDirectory) {\n if (pageIds.has(directory)) continue\n const source = `/${directory}`\n if (hasAuthoredRedirect(source)) continue\n // Match full stored ids, not locale-independent navigation ids: a French\n // directory must never be redirected to an English descendant.\n redirects.push({ source, destination: `/${landing.id}`, permanent: false })\n redirectSources.add(source)\n }\n return redirects.length > 0 ? { ...config, redirects } : config\n}\n\n/**\n * Keep authored page URLs while resolving bare documentation roots to the\n * first visible navigation page. An unrelated `home.mdx` is not evidence that\n * Mintlify served it at `/`; explicit source redirects always take priority.\n */\nexport function addMintlifyHomepageRedirects(\n config: MigrationDocsConfig,\n pages: Array<MigrationPage>,\n): MigrationDocsConfig {\n const defaultPages = new Set(pages\n .filter((page) => !page.locale || page.locale === config.i18n?.defaultLocale)\n .map((page) => page.navigationId))\n function firstPage(nodes: Array<string | MigrationNavigationGroup>): string | undefined {\n for (const node of nodes) {\n if (typeof node === 'string') {\n if (defaultPages.has(node)) return node\n } else if (!node.hidden) {\n const page = firstPage(node.pages)\n if (page) return page\n }\n }\n }\n const homepage = config.tabs.filter((tab) => !tab.hidden)\n .map((tab) => firstPage([...(tab.pages ?? []), ...(tab.groups ?? [])]))\n .find(Boolean)\n // Mintlify sends `/` to the first page even when that page is `introduction`, so the\n // renderer's root alias for it is not served and links, canonicals and the sitemap agree on one URL.\n if (!homepage) return config\n const redirects = [...(config.redirects ?? [])]\n const sources = new Set(redirects.map((redirect) => redirect.source.replace(/\\/$/, '') || '/'))\n const roots = ['', ...(config.i18n?.locales ?? [])\n .filter((locale) => locale.code !== config.i18n?.defaultLocale)\n .filter((locale) => homepage !== 'introduction' || pages.some((page) => page.locale === locale.code && page.navigationId === homepage))\n .map((locale) => locale.code)]\n for (const locale of roots) {\n const source = locale ? `/${locale}` : '/'\n if (sources.has(source)) continue\n redirects.push({ source, destination: `${locale ? `/${locale}` : ''}/${homepage}`, permanent: true })\n }\n return { ...config, redirects }\n}\n\n/** Native names, as Mintlify's language picker shows them. */\nconst LANGUAGE_LABELS: Record<string, string> = {\n ar: 'العربية',\n de: 'Deutsch',\n en: 'English',\n es: 'Español',\n fr: 'Français',\n hi: 'हिन्दी',\n it: 'Italiano',\n ja: '日本語',\n ko: '한국어',\n nl: 'Nederlands',\n pl: 'Polski',\n pt: 'Português',\n 'pt-BR': 'Português (BR)',\n ru: 'Русский',\n tr: 'Türkçe',\n uk: 'Українська',\n zh: '简体中文',\n 'zh-Hans': '简体中文',\n 'zh-Hant': '繁體中文',\n}\n\nfunction objectValue(value: unknown): Record<string, unknown> | null {\n return value !== null && typeof value === 'object' && !Array.isArray(value)\n ? value as Record<string, unknown>\n : null\n}\n\n/**\n * Some Mintlify sites (e.g. Upstash's docs.json) group `products` under an\n * extra `productGroups: [{ group, products }]` wrapper instead of a bare\n * `products` array directly on the tab. Flatten it into `products` so the\n * existing container handling picks the products up unchanged — otherwise\n * every page and `openapi`/`asyncapi` reference under it is silently\n * invisible to navigation projection and API-spec resolution alike.\n */\nfunction withFlattenedProductGroups(container: Record<string, unknown>): Record<string, unknown> {\n if (!Array.isArray(container.productGroups)) return container\n const flattened = (container.productGroups as Array<unknown>).flatMap((entry) => {\n const group = objectValue(entry)\n return group && Array.isArray(group.products) ? group.products : []\n })\n if (flattened.length === 0) return container\n return {\n ...container,\n products: [...(Array.isArray(container.products) ? container.products : []), ...flattened],\n }\n}\n\nfunction projectedHref(value: unknown): string | undefined {\n if (typeof value !== 'string') return undefined\n const href = value.trim()\n if (!href || /[\\u0000-\\u001f\\u007f]/.test(href)) return undefined\n if (/^(?:javascript|data|vbscript|file):/i.test(href)) return undefined\n const scheme = href.match(/^([a-z][a-z0-9+.-]*):/i)?.[1].toLowerCase()\n if (scheme && !['http', 'https', 'mailto', 'tel'].includes(scheme)) return undefined\n return href\n}\n\nfunction labelFor(value: Record<string, unknown>, fallback: string): string {\n for (const key of ['tab', 'group', 'anchor', 'product', 'dropdown', 'version', 'menu', 'item', 'label', 'name', 'title']) {\n if (typeof value[key] === 'string' && value[key]) return String(value[key])\n }\n return fallback\n}\n\nfunction localReference(value: string): string {\n return value.split('#', 1)[0]\n}\n\nfunction jsonPointer(root: unknown, pointer: string): unknown {\n if (!pointer || pointer === '#') return root\n const tokens = pointer.replace(/^#\\/?/, '').split('/').filter(Boolean)\n return tokens.reduce<unknown>((current, token) => {\n if (Array.isArray(current)) {\n const index = Number(token)\n return Number.isSafeInteger(index) && index >= 0 ? current[index] : undefined\n }\n const object = objectValue(current)\n if (!object) return undefined\n return object[token.replace(/~1/g, '/').replace(/~0/g, '~')]\n }, root)\n}\n\nfunction readMintlifyJson(path: string, repositoryRoot: string): unknown {\n if (!existsSync(path) || !lstatSync(path).isFile()) {\n throw new Error(`Mintlify config reference is not a regular file: ${relative(repositoryRoot, path)}`)\n }\n const realPath = realpathSync(path)\n const realRoot = realpathSync(repositoryRoot)\n resolveWithin(realRoot, relative(realRoot, realPath))\n if (lstatSync(realPath).size > MAX_MINTLIFY_CONFIG_BYTES) {\n throw new Error(`Mintlify config reference exceeded ${MAX_MINTLIFY_CONFIG_BYTES / 1_000_000} MB: ${relative(repositoryRoot, path)}`)\n }\n return JSON.parse(readFileSync(realPath, 'utf8')) as unknown\n}\n\nfunction resolveJsonReferences(\n value: unknown,\n currentFile: string,\n repositoryRoot: string,\n stack: Set<string>,\n): unknown {\n if (Array.isArray(value)) {\n return value.map((entry) => resolveJsonReferences(entry, currentFile, repositoryRoot, stack))\n }\n const object = objectValue(value)\n if (!object) return value\n if (typeof object.$ref === 'string') {\n const [filePart, pointer = ''] = object.$ref.split('#', 2)\n const referencedFile = filePart\n ? resolveWithin(dirname(currentFile), filePart)\n : currentFile\n const relativePath = relative(repositoryRoot, referencedFile)\n resolveWithin(repositoryRoot, relativePath)\n const stackKey = `${referencedFile}#${pointer}`\n if (stack.has(stackKey)) throw new Error(`Circular Mintlify $ref: ${object.$ref}`)\n stack.add(stackKey)\n const referencedRoot = readMintlifyJson(referencedFile, repositoryRoot)\n const referenced = jsonPointer(referencedRoot, pointer ? `#${pointer}` : '')\n const resolved = resolveJsonReferences(referenced, referencedFile, repositoryRoot, stack)\n stack.delete(stackKey)\n const siblings = Object.fromEntries(\n Object.entries(object)\n .filter(([key]) => key !== '$ref')\n .map(([key, entry]) => [\n key,\n resolveJsonReferences(entry, currentFile, repositoryRoot, stack),\n ]),\n )\n // Mintlify merges sibling keys over object-valued references. Retaining\n // this behavior matters for split navigation files that override one\n // label, link, or visibility flag at the reference site.\n return objectValue(resolved) ? { ...resolved as Record<string, unknown>, ...siblings } : resolved\n }\n return Object.fromEntries(\n Object.entries(object).map(([key, entry]) => [\n key,\n resolveJsonReferences(entry, currentFile, repositoryRoot, stack),\n ]),\n )\n}\n\n/** Read `docs.json`/`mint.json`, including bounded local JSON references. */\nexport function readMintlifyConfig(repositoryRoot: string): Record<string, unknown> | null {\n const configPath = ['docs.json', 'mint.json']\n .map((filename) => resolveWithin(repositoryRoot, filename))\n .find(existsSync)\n if (!configPath) return null\n const raw = readMintlifyJson(configPath, repositoryRoot)\n return resolveJsonReferences(raw, configPath, repositoryRoot, new Set()) as Record<string, unknown>\n}\n\n/** One `openapi`/`asyncapi` value found anywhere in Mintlify's navigation tree. */\nexport interface MintlifyApiSpecReference {\n /** The field's raw value: a repository-relative path, or an `http(s)://` URL. */\n value: string\n kind: 'openapi' | 'asyncapi'\n /** Label of the tab the resolved spec binds to (created if the source tab was dropped). Undefined at the navigation root. */\n tabLabel?: string\n /** Set when `tabLabel` is a per-item sibling tab (`<Tab>: <Item>`): the tab it should be placed after. */\n parentTab?: string\n /** Menu-item presentation carried onto its sibling tab. */\n icon?: string\n hidden?: boolean\n /** Mintlify's `{ source, directory }` output folder, which Thally does not use (endpoints live under `/api/`). */\n directory?: string\n}\n\n/** `openapi`/`asyncapi` is a path/URL string, or Mintlify's `{ source, directory }` object. */\nfunction specSource(value: unknown): string | undefined {\n const source = typeof value === 'string' ? value : objectValue(value)?.source\n return typeof source === 'string' && source.trim() ? source.trim() : undefined\n}\n\nconst hasNavigationChildren = (node: Record<string, unknown>): boolean =>\n ['groups', 'pages'].some((key) => Array.isArray(node[key]) && (node[key] as Array<unknown>).length > 0)\n\n/**\n * Mintlify lets `openapi`/`asyncapi` appear not just at the top-level `api`\n * key but on any navigation container (most commonly a tab:\n * `navigation.tabs[].openapi`, but the same field is honored on an anchor,\n * dropdown, product, version or group too) — Mintlify binds the resulting\n * API reference to whichever tab that container renders under. This walks\n * the whole raw navigation tree (the same shape `convertContainerToTabs`\n * projects) collecting every occurrence, so the caller can resolve and bind\n * each one instead of only ever seeing the single top-level `api` field.\n *\n * A tab `menu` item with `openapi` cannot become a group (Thally groups hold\n * pages, and the generated endpoints live in an API-bound tab). It becomes a\n * sibling tab `<Tab>: <Item>` bound to its own spec, or binds to the tab\n * itself when it is the tab's only content.\n */\nexport function mintlifyNavigationApiReferences(config: Record<string, unknown>): Array<MintlifyApiSpecReference> {\n const navigation = objectValue(config.navigation) ?? config\n const references: Array<MintlifyApiSpecReference> = []\n const containerKeys = ['tabs', 'anchors', 'products', 'dropdowns', 'versions', 'menus', 'languages'] as const\n const nestedKeys = ['groups', 'pages'] as const\n\n function collect(node: Record<string, unknown>, tabLabel: string | undefined, sibling?: Pick<MintlifyApiSpecReference, 'parentTab' | 'icon' | 'hidden'>): void {\n for (const kind of ['openapi', 'asyncapi'] as const) {\n const value = specSource(node[kind])\n const directory = objectValue(node[kind])?.directory\n if (value) {\n references.push({\n value, kind, tabLabel, ...sibling,\n ...(typeof directory === 'string' && directory.trim() ? { directory: directory.trim() } : {}),\n })\n }\n }\n }\n\n // Two menu items can share a label (the same item name under two versions):\n // keep both tabs instead of letting the later spec replace the earlier one.\n const siblingSources = new Map<string, string>()\n function uniqueSiblingLabel(label: string, source: string): string {\n let candidate = label\n for (let n = 2; siblingSources.has(candidate) && siblingSources.get(candidate) !== source; n++) candidate = `${label} (${n})`\n siblingSources.set(candidate, source)\n return candidate\n }\n\n function visitChildren(rawNode: Record<string, unknown>, tabLabel: string | undefined): void {\n const node = withFlattenedProductGroups(rawNode)\n for (const key of containerKeys) {\n const entries = node[key]\n if (!Array.isArray(entries)) continue\n for (const entry of entries) {\n const object = objectValue(entry)\n if (!object) continue\n // A nested container becomes the new binding tab only at the outer\n // level (Mintlify tabs aren't themselves nested inside other tabs in\n // practice, but anchors/dropdowns can sit inside a tab) — reuse the\n // enclosing tab's label unless this container names its own.\n visit(object, labelFor(object, tabLabel ?? 'Documentation'))\n }\n }\n for (const key of nestedKeys) {\n const entries = node[key]\n if (!Array.isArray(entries)) continue\n for (const entry of entries) {\n if (typeof entry === 'string') continue\n const object = objectValue(entry)\n if (object) visit(object, tabLabel)\n }\n }\n if (Array.isArray(node.menu)) {\n const tab = tabLabel ?? 'Documentation'\n const items = node.menu.flatMap((entry): Array<Record<string, unknown>> => {\n const object = objectValue(entry)\n return object ? [object] : []\n })\n const apiItems = items.filter((item) => specSource(item.openapi) || specSource(item.asyncapi))\n const soleContent = apiItems.length === 1 && !hasNavigationChildren(node) && !items.some(hasNavigationChildren)\n items.forEach((item, index) => {\n if (soleContent && apiItems[0] === item) collect(item, tab)\n else if (apiItems.includes(item)) {\n const label = uniqueSiblingLabel(\n `${tab}: ${labelFor(item, `Menu item ${index + 1}`)}`,\n `${specSource(item.openapi) ?? ''}|${specSource(item.asyncapi) ?? ''}`,\n )\n const icon = iconName(item.icon)\n collect(item, label, { parentTab: tab, ...(icon ? { icon } : {}), ...(item.hidden === true ? { hidden: true } : {}) })\n }\n visitChildren(item, tabLabel)\n })\n }\n }\n\n function visit(node: Record<string, unknown>, tabLabel: string | undefined): void {\n collect(node, tabLabel)\n visitChildren(node, tabLabel)\n }\n visit(navigation, undefined)\n return references\n}\n\n/** Add an API-bound tab, right after `parentTab` (and its earlier per-item siblings) when given, else at the end. */\nexport function insertApiTab<T extends { tab: string }>(tabs: Array<T>, tab: T, parentTab?: string): Array<T> {\n let index = -1\n if (parentTab) {\n tabs.forEach((candidate, position) => {\n if (candidate.tab === parentTab || candidate.tab.startsWith(`${parentTab}: `)) index = position\n })\n }\n tabs.splice(index === -1 ? tabs.length : index + 1, 0, tab)\n return tabs\n}\n\nfunction normalizePageRef(value: string, pathPrefix = ''): string | null {\n if (/^(?:https?:)?\\/\\//i.test(value) || value.startsWith('#')) return null\n let ref = localReference(value).split('?', 1)[0].replace(/^\\/+/, '')\n const prefix = trimEdgeSlashes(pathPrefix)\n if (prefix && (ref === prefix || ref.startsWith(`${prefix}/`))) {\n ref = ref === prefix ? 'introduction' : ref.slice(prefix.length + 1)\n }\n ref = ref.replace(/\\.(?:mdx?|rst|txt)$/i, '')\n // Mintlify routes are case-sensitive and commonly use camelCase filenames\n // (for example `additionalFiles`). Lowercasing here breaks both authored\n // navigation and links even though the source site resolves them correctly.\n return pageIdFromReference(ref, true)\n}\n\ninterface ProjectionContext {\n locale?: string\n defaultPageIds?: ReadonlySet<string>\n pathPrefix?: string\n resolveSourceRef?: MintlifyProjectionOptions['resolveSourceRef']\n references: Array<MintlifyPageReference>\n seenReferences: Set<string>\n warnings: Array<MigrationWarning>\n warningKeys: Set<string>\n appearances: Array<MintlifyReferenceAccess>\n /** Access accumulated from the containers above the node being walked. */\n access: NavigationAccess\n gatedSourceRefs: Array<string>\n /** `sourceRef` nodes found under any restricted container, whichever container kind holds them. */\n gatedNodes: Map<object, string>\n /** Set when the navigation nests deeper than the access sweeps can follow; every mount and page is then withheld. */\n limits: { tooDeep: boolean }\n /** Label of the nearest enclosing navigation group, for warnings. */\n groupLabel?: string\n}\n\n/** How deep the access sweeps follow the navigation; anything deeper is withheld rather than guessed at. */\nconst SWEEP_DEPTH_LIMIT = 256\n\n/**\n * Container-agnostic sweep for `sourceRef` mounts under a restricted node of any kind, so a container\n * the projection does not special-case can never publish a mount. Runs before conversion.\n */\nfunction collectGatedSourceRefs(node: unknown, gate: string | undefined, out: Map<object, string>, limits: { tooDeep: boolean }, depth = 0): void {\n if (depth > SWEEP_DEPTH_LIMIT) {\n limits.tooDeep = true\n return\n }\n if (Array.isArray(node)) {\n for (const item of node) collectGatedSourceRefs(item, gate, out, limits, depth + 1)\n return\n }\n const object = objectValue(node)\n if (!object) return\n const reason = gate ?? navigationGateReason(object)\n if (reason && typeof object.sourceRef === 'string') out.set(object, reason)\n for (const value of Object.values(object)) collectGatedSourceRefs(value, reason, out, limits, depth + 1)\n}\n\n/**\n * Container-agnostic sweep for pages under a node that sets access rules. The\n * projection only walks container kinds it understands; a page under any\n * other one (a tab's `menu` items, say) would otherwise migrate without the\n * rules of the container that restricted it. Open appearances are left to the\n * projection, so a stray `href` elsewhere in the config never counts as one.\n */\nfunction collectReferenceAccess(\n node: unknown,\n access: NavigationAccess,\n pathPrefix: string | undefined,\n out: Array<MintlifyReferenceAccess>,\n depth = 0,\n): void {\n if (depth > SWEEP_DEPTH_LIMIT) return\n if (Array.isArray(node)) {\n for (const item of node) collectReferenceAccess(item, access, pathPrefix, out, depth + 1)\n return\n }\n const object = objectValue(node)\n if (!object) return\n const nested = descendNavigationAccess(access, navigationAccess(object))\n if (hasNavigationRules(nested)) {\n // `href` names a page too (`{ href: '/secret' }` under a group); external\n // links never normalize to a page reference.\n const refs = [object.page, object.root, object.href, ...(Array.isArray(object.pages) ? object.pages : [])]\n for (const ref of refs) {\n if (typeof ref === 'string' && normalizePageRef(ref, pathPrefix)) out.push({ ref, access: nested })\n }\n }\n for (const value of Object.values(object)) collectReferenceAccess(value, nested, pathPrefix, out, depth + 1)\n}\n\n/** Walk `walk` one container deeper, restoring the outer access afterwards. */\nfunction withContainerAccess<T>(object: Record<string, unknown>, context: ProjectionContext, walk: () => T): T {\n const outer = context.access\n context.access = descendNavigationAccess(outer, navigationAccess(object))\n try {\n return walk()\n } finally {\n context.access = outer\n }\n}\n\nfunction warnOnce(context: ProjectionContext, key: string, message: string): void {\n if (context.warningKeys.has(key)) return\n context.warningKeys.add(key)\n context.warnings.push({ code: 'unsupported-config', message })\n}\n\nfunction iconName(value: unknown): string | undefined {\n if (typeof value === 'string' && value) return value\n const icon = objectValue(value)\n return typeof icon?.name === 'string' && icon.name ? icon.name : undefined\n}\n\nfunction containerPresentation(value: Record<string, unknown>): {\n description?: string\n icon?: string\n} {\n return {\n ...(typeof value.description === 'string' && value.description.trim()\n ? { description: value.description.trim() }\n : {}),\n ...(iconName(value.icon) ? { icon: iconName(value.icon) } : {}),\n }\n}\n\nfunction registerReference(value: string, context: ProjectionContext): string | null {\n if (context.limits.tooDeep) {\n // Access rules cannot be checked reliably: the page fails closed\n // (quarantined) and leaves the navigation.\n const problem = 'its navigation is too deeply nested to check access rules'\n if (normalizePageRef(value, context.pathPrefix)) context.appearances.push({ ref: value, access: { ...context.access, problem } })\n return null\n }\n // Every appearance is recorded, open ones included: a page listed under a\n // restricted and an open container gets the strictest rule, never the open one.\n if (normalizePageRef(value, context.pathPrefix)) context.appearances.push({ ref: value, access: context.access })\n const localizedValue = context.locale\n ? mintlifyLocalizedReference(value, context.locale, context.defaultPageIds)\n : value\n const navigationId = normalizePageRef(localizedValue, context.pathPrefix)\n if (!navigationId) return null\n const key = `${context.locale ?? ''}:${value}`\n if (!context.seenReferences.has(key)) {\n context.seenReferences.add(key)\n context.references.push({ ref: value, navigationId, locale: context.locale })\n }\n return navigationId\n}\n\n/**\n * Mintlify lets a `pages` entry name a single OpenAPI operation directly\n * (`\"GET /users\"`) to hand-pick or reorder which operations of an\n * already-`openapi`-scoped ancestor appear, instead of a page file\n * reference. Thally always renders every operation of a bound spec and\n * has no manual-selection equivalent, so these are not migratable pages —\n * treating them as one is what produced a \"did not resolve to a source\n * page\" warning per operation instead of one explanation for the group.\n */\nconst OPENAPI_OPERATION_REF = /^(?:GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\\s+\\S/\n\nfunction convertPage(\n value: unknown,\n context: ProjectionContext,\n): string | MigrationNavigationGroup | null {\n if (typeof value === 'string') {\n if (OPENAPI_OPERATION_REF.test(value)) {\n warnOnce(\n context,\n 'openapi-operation-list',\n 'One or more navigation groups hand-pick or reorder individual OpenAPI operations (e.g. \"GET /users\"); '\n + \"Thally always renders every operation of a bound spec and has no manual-selection equivalent, so that \"\n + 'list was dropped. The operations themselves are still available through the bound API reference.',\n )\n return null\n }\n return registerReference(value, context)\n }\n const object = objectValue(value)\n if (!object) return null\n return withContainerAccess(object, context, () => convertPageObject(object, context))\n}\n\n/** A `sourceRef` mount under a restricted container is never resolved, so its pages, images and components stay out of the output. */\nfunction gatedSourceRef(repo: string, context: ProjectionContext): null {\n if (!context.gatedSourceRefs.includes(repo)) context.gatedSourceRefs.push(repo)\n warnOnce(\n context,\n `source-ref-gated:${repo}`,\n `Navigation mounts the docs of the repository ${repo} (sourceRef) inside a restricted container. `\n + 'This restricted mount was left out of the navigation. To publish it, make the container public in docs.json, '\n + 'or mount the repository under a public section.',\n )\n return null\n}\n\nfunction convertPageObject(\n object: Record<string, unknown>,\n context: ProjectionContext,\n): string | MigrationNavigationGroup | null {\n if (typeof object.page === 'string') return registerReference(object.page, context)\n const href = projectedHref(object.href)\n if (href && !object.pages && !object.groups) {\n const page = registerReference(href, context)\n if (page) return page\n warnOnce(\n context,\n 'external-page-link',\n 'External links nested inside a Mintlify sidebar cannot be represented as Thally pages and were omitted.',\n )\n return null\n }\n if (typeof object.sourceRef === 'string') {\n // `context.access` already includes this node's own rules (convertPage descends first).\n if (isNavigationRestricted(context.access) || context.gatedNodes.has(object) || context.limits.tooDeep) return gatedSourceRef(object.sourceRef, context)\n const imported = context.resolveSourceRef?.(object.sourceRef)\n // Directly inside a group the entries are spliced into it (see below);\n // anywhere else they get a group named after the repository.\n if (imported) return { group: object.sourceRef.split('/').pop() ?? object.sourceRef, pages: imported }\n const where = context.groupLabel ? `the \"${context.groupLabel}\" navigation group` : 'the navigation'\n warnOnce(\n context,\n `source-ref:${context.groupLabel ?? ''}:${object.sourceRef}`,\n `Navigation in ${where} mounts the docs of the repository ${object.sourceRef} (sourceRef). `\n + `Those pages were not migrated; pass --source-ref ${object.sourceRef}=<path> to import them.`,\n )\n return null\n }\n const pages = Array.isArray(object.pages) ? object.pages : []\n if ('group' in object || pages.length > 0) {\n const children: Array<string | MigrationNavigationGroup> = []\n const outerGroupLabel = context.groupLabel\n context.groupLabel = labelFor(object, 'Documentation')\n if (typeof object.root === 'string') {\n const root = registerReference(object.root, context)\n if (root) children.push(root)\n }\n try {\n for (const page of pages) {\n const pageObject = objectValue(page)\n const sourceRef = pageObject?.sourceRef\n // A restricted mount (its own rules or an enclosing container's) falls through to convertPage, which withholds it.\n const imported = typeof sourceRef === 'string' && !isNavigationRestricted(context.access) && !navigationGateReason(pageObject!) && !context.gatedNodes.has(pageObject!) && !context.limits.tooDeep\n ? context.resolveSourceRef?.(sourceRef)\n : null\n if (imported) {\n children.push(...imported)\n continue\n }\n const converted = convertPage(page, context)\n if (converted) children.push(converted)\n }\n } finally {\n context.groupLabel = outerGroupLabel\n }\n if (children.length === 0) return null\n return {\n group: labelFor(object, 'Documentation'),\n ...(iconName(object.icon) ? { icon: iconName(object.icon) } : {}),\n ...(object.hidden === true ? { hidden: true } : {}),\n pages: children,\n }\n }\n return null\n}\n\nconst KNOWN_NAVIGATION_KEYS = new Set([\n 'tabs', 'anchors', 'products', 'dropdowns', 'versions', 'menus', 'languages', 'productGroups',\n 'groups', 'pages', 'menu', 'global',\n])\n\n/** A Mintlify tab `menu`: each item becomes a top-level group of the enclosing tab. */\nfunction convertMenuItems(\n items: Array<unknown>,\n context: ProjectionContext,\n): Array<MigrationNavigationGroup> {\n return items.flatMap((value, index) => {\n const item = objectValue(value)\n if (!item) return []\n const label = labelFor(item, `Menu item ${index + 1}`)\n if (typeof item.description === 'string' && item.description.trim()) {\n warnOnce(\n context,\n `menu-item-description:${label}`,\n `Mintlify menu item \"${label}\" has a description, which Thally groups cannot display; it was omitted.`,\n )\n }\n const hasUnsourcedSpec = Boolean(item.openapi || item.asyncapi) && !specSource(item.openapi) && !specSource(item.asyncapi)\n if (hasUnsourcedSpec) {\n warnOnce(\n context,\n `menu-item-spec:${label}`,\n `Mintlify menu item \"${label}\" has an openapi/asyncapi value without a \"source\", so its API reference was not migrated.`,\n )\n }\n const children = convertNavigationValues([\n ...(Array.isArray(item.groups) ? item.groups : []),\n ...(Array.isArray(item.pages) ? item.pages : []),\n ], context)\n if (typeof item.href === 'string' && item.href) {\n warnOnce(\n context,\n `menu-item-href:${label}`,\n `Mintlify menu item \"${label}\" links to ${item.href}, which cannot be represented as a Thally group; the link was omitted.`,\n )\n }\n if (children.length === 0) {\n if (!specSource(item.openapi) && !specSource(item.asyncapi) && !hasUnsourcedSpec) {\n warnOnce(context, `menu-item-empty:${label}`, `Mintlify menu item \"${label}\" has no projectable pages and was omitted.`)\n }\n return []\n }\n return [{\n group: label,\n ...(iconName(item.icon) ? { icon: iconName(item.icon) } : {}),\n ...(item.hidden === true ? { hidden: true } : {}),\n pages: children,\n }]\n })\n}\n\nfunction convertNavigationValues(\n values: Array<unknown>,\n context: ProjectionContext,\n): Array<string | MigrationNavigationGroup> {\n return values.flatMap<string | MigrationNavigationGroup>((value) => {\n const page = convertPage(value, context)\n return page ? [page] : []\n })\n}\n\ninterface NavigationProjectionTrace {\n rootContainerKind?: 'tabs' | 'anchors' | 'products' | 'dropdowns' | 'versions' | 'menus'\n}\n\nfunction convertContainerToTabs(\n containerValue: unknown,\n context: ProjectionContext,\n fallbackTab: string,\n trace?: NavigationProjectionTrace,\n depth = 0,\n): Array<MigrationNavigationTab> {\n const rawContainer = objectValue(containerValue)\n if (!rawContainer) return []\n const container = withFlattenedProductGroups(rawContainer)\n const containerKeys = ['tabs', 'anchors', 'products', 'dropdowns', 'versions', 'menus'] as const\n for (const key of Object.keys(container)) {\n if (KNOWN_NAVIGATION_KEYS.has(key)) continue\n const entries = container[key]\n if (Array.isArray(entries) && entries.some((entry) => objectValue(entry))) {\n warnOnce(context, `unknown-container:${key}`, `Mintlify navigation key \"${key}\" is not supported and its contents were not projected.`)\n }\n }\n for (const key of containerKeys) {\n if (!Array.isArray(container[key])) continue\n const entries = [...container[key] as Array<unknown>]\n if (key === 'versions') {\n entries.sort((left, right) => Number(objectValue(right)?.default === true) - Number(objectValue(left)?.default === true))\n }\n const tabs = entries.flatMap((value, index) => {\n const object = objectValue(value)\n if (!object) return []\n const tab = labelFor(object, `${fallbackTab} ${index + 1}`)\n const href = projectedHref(object.href)\n const hasNestedContainers = [...containerKeys, 'menu'].some((containerKey) => Array.isArray(object[containerKey]))\n if (href && !object.pages && !object.groups && !hasNestedContainers) {\n return [{\n tab,\n href,\n ...containerPresentation(object),\n ...(object.hidden === true ? { hidden: true } : {}),\n }]\n }\n const nested = withContainerAccess(object, context, () => convertContainerToTabs(object, context, tab, trace, depth + 1))\n if (nested.length > 0) {\n if (nested.length === 1) {\n return [{\n ...nested[0],\n tab,\n ...containerPresentation(object),\n ...(href ? { href } : {}),\n ...(object.hidden === true ? { hidden: true } : {}),\n }]\n }\n const presentation = containerPresentation(object)\n return nested.map((item, childIndex) => ({\n ...presentation,\n ...item,\n tab: `${tab}: ${item.tab}`,\n ...(childIndex === 0 && href && !item.href ? { href } : {}),\n ...(object.hidden === true ? { hidden: true } : {}),\n }))\n }\n return []\n })\n if (tabs.length > 0) {\n for (const sibling of ['groups', 'pages', 'menu'] as const) {\n if (Array.isArray(container[sibling]) && (container[sibling] as Array<unknown>).length > 0) {\n warnOnce(context, `container-sibling:${key}:${sibling}`, `Mintlify \"${sibling}\" next to \"${key}\" was not projected; only the \"${key}\" containers were kept.`)\n }\n }\n if (depth === 0 && trace) trace.rootContainerKind = key\n return tabs\n }\n }\n const rawGroups = Array.isArray(container.groups) ? container.groups : []\n const rawPages = Array.isArray(container.pages) ? container.pages : []\n const hasRootNodes = typeof container.root === 'string' || rawPages.length > 0\n const values = [\n ...(typeof container.root === 'string' ? [container.root] : []),\n ...rawGroups,\n ...rawPages,\n ]\n const children = [\n ...convertNavigationValues(values, context),\n ...(Array.isArray(container.menu) ? convertMenuItems(container.menu, context) : []),\n ]\n return children.length > 0\n ? [{\n tab: fallbackTab,\n ...(hasRootNodes\n ? { pages: children }\n : { groups: children as Array<MigrationNavigationGroup> }),\n ...containerPresentation(container),\n ...(projectedHref(container.href) ? { href: projectedHref(container.href) } : {}),\n ...(container.hidden === true ? { hidden: true } : {}),\n }]\n : []\n}\n\nfunction projectedTheme(value: unknown): MigrationDocsConfig['theme'] {\n // Aspen rounds its sidebar highlight (12px) and search box like Maple, not like Sharp's 3px corners.\n if (value === 'maple' || value === 'aspen') return 'maple'\n if (['luma', 'sequoia'].includes(String(value))) return 'sharp'\n if (['almond', 'palm'].includes(String(value))) return 'minimal'\n return typeof value === 'string' ? 'default' : undefined\n}\n\n/**\n * Mintlify `colors`: `primary` is the brand colour in light mode, `light` the\n * one used in dark mode and `dark` the button fill. Three-digit hex is\n * expanded because the renderer only accepts six digits.\n */\nfunction projectedColors(value: unknown, warnings: Array<MigrationWarning>): MigrationDocsConfig['colors'] {\n const colors = objectValue(value)\n if (!colors) {\n if (value !== undefined) warnings.push({ code: 'unsupported-config', message: 'colors is not an object with a primary hex colour and was dropped.', source: 'docs.json' })\n return undefined\n }\n const hex = (entry: unknown, key: string): string | undefined => {\n const match = typeof entry === 'string' ? /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(entry.trim()) : null\n if (!match) {\n if (entry !== undefined) warnings.push({ code: 'unsupported-config', message: `colors.${key} ${JSON.stringify(entry)} is not a 3- or 6-digit hex colour (for example #16a34a) and was dropped.`, source: 'docs.json' })\n return undefined\n }\n const digits = match[1].length === 3 ? [...match[1]].map((digit) => digit + digit).join('') : match[1]\n return `#${digits.toLowerCase()}`\n }\n const primary = hex(colors.primary, 'primary')\n const light = hex(colors.light, 'light') ?? primary\n const dark = hex(colors.dark, 'dark') ?? primary\n if (!primary) {\n if (colors.primary === undefined) warnings.push({ code: 'unsupported-config', message: 'colors has no primary hex colour, so none of its colours were migrated.', source: 'docs.json' })\n return undefined\n }\n return { light: { accent: primary, primary: dark ?? primary }, dark: { accent: light ?? primary, primary: light ?? primary } }\n}\n\nfunction projectedNavbar(value: unknown): MigrationDocsConfig['navbar'] {\n const navbar = objectValue(value)\n if (!navbar) return undefined\n const links = Array.isArray(navbar.links)\n ? navbar.links.flatMap((entry) => {\n const link = objectValue(entry)\n const href = projectedHref(link?.href)\n if (!link || !href) return []\n const label = typeof link.label === 'string' ? link.label : typeof link.name === 'string' ? link.name : ''\n if (!label) return []\n return [{ label, href, ...(link.type === 'github' ? { type: 'github' as const } : {}) }]\n })\n : []\n const primaryValue = objectValue(navbar.primary)\n const primaryHref = projectedHref(primaryValue?.href)\n const primary = primaryValue && primaryHref\n ? {\n label: typeof primaryValue.label === 'string'\n ? primaryValue.label\n : primaryValue.type === 'github' ? 'GitHub' : 'Get started',\n href: primaryHref,\n ...(primaryValue.type === 'github' ? { type: 'github' as const } : {}),\n }\n : undefined\n return links.length > 0 || primary ? { ...(links.length > 0 ? { links } : {}), ...(primary ? { primary } : {}) } : undefined\n}\n\n/**\n * Legacy mint.json `topbarLinks` (`{ name, url }`) and `topbarCtaButton`\n * (`{ name, url }` or `{ type: 'github', url }`), reshaped as docs.json\n * `navbar` so both sources share `projectedNavbar`.\n */\nfunction legacyTopbarNavbar(config: Record<string, unknown>): Record<string, unknown> | undefined {\n const links = Array.isArray(config.topbarLinks)\n ? config.topbarLinks.flatMap((entry) => {\n const link = objectValue(entry)\n if (!link) return []\n return [{\n label: link.name ?? link.label ?? (link.type === 'github' ? 'GitHub' : undefined),\n href: link.url ?? link.href,\n ...(link.type === 'github' ? { type: 'github' } : {}),\n }]\n })\n : []\n const cta = objectValue(config.topbarCtaButton)\n const primary = cta ? { label: cta.name ?? cta.label, href: cta.url ?? cta.href, type: cta.type } : undefined\n return links.length > 0 || primary ? { ...(links.length > 0 ? { links } : {}), ...(primary ? { primary } : {}) } : undefined\n}\n\nfunction projectedGlobalNavigationLinks(\n value: unknown,\n): NonNullable<NonNullable<MigrationDocsConfig['navbar']>['links']> {\n const global = objectValue(value)\n if (!global) return []\n const links: Array<{ label: string; href: string }> = []\n const visit = (entry: unknown): void => {\n if (Array.isArray(entry)) {\n entry.forEach(visit)\n return\n }\n const item = objectValue(entry)\n if (!item) return\n const href = projectedHref(item.href)\n if (href) {\n const label = labelFor(item, '')\n if (label) links.push({ label, href })\n }\n for (const key of ['tabs', 'anchors', 'dropdowns', 'products', 'versions', 'languages', 'menus', 'menu']) {\n if (Array.isArray(item[key])) visit(item[key])\n }\n }\n visit(global)\n return links\n}\n\nfunction projectedFooter(value: unknown): MigrationDocsConfig['footer'] {\n const footer = objectValue(value)\n if (!footer) return undefined\n const socials = objectValue(footer.socials)\n const socialLinks = socials\n ? Object.fromEntries(Object.entries(socials).flatMap(([key, value]) => {\n const href = projectedHref(value)\n return href ? [[key, href]] : []\n }))\n : undefined\n const links = Array.isArray(footer.links)\n ? footer.links.flatMap((entry) => {\n const column = objectValue(entry)\n if (!column || !Array.isArray(column.items)) return []\n const heading = typeof column.heading === 'string'\n ? column.heading\n : typeof column.header === 'string' ? column.header : ''\n const items = column.items.flatMap((item) => {\n const link = objectValue(item)\n const href = projectedHref(link?.href)\n return link && typeof link.label === 'string' && href\n ? [{ label: link.label, href }]\n : []\n })\n return heading && items.length > 0 ? [{ heading, items }] : []\n })\n : []\n return (socialLinks && Object.keys(socialLinks).length > 0) || links.length > 0\n ? { ...(socialLinks && Object.keys(socialLinks).length > 0 ? { socials: socialLinks } : {}), ...(links.length > 0 ? { links } : {}) }\n : undefined\n}\n\nfunction projectedFont(value: unknown): { family: string; weight?: Array<string> } | undefined {\n if (typeof value === 'string' && value) return { family: value }\n const font = objectValue(value)\n if (!font) return undefined\n const family = typeof font.family === 'string'\n ? font.family\n : typeof font.name === 'string' ? font.name : undefined\n if (!family) return undefined\n const weight = Array.isArray(font.weight)\n ? font.weight.map(String)\n : Array.isArray(font.weights) ? font.weights.map(String) : undefined\n return { family, ...(weight && weight.length > 0 ? { weight } : {}) }\n}\n\nfunction projectedCompatibleConfig(config: Record<string, unknown>, warnings: Array<MigrationWarning>): Omit<MigrationDocsConfig, 'tabs' | 'i18n' | 'redirects'> {\n const banner = objectValue(config.banner)\n const bannerContent = banner && (typeof banner.content === 'string' || objectValue(banner.content))\n ? banner.content as string | Record<string, string>\n : undefined\n const bannerType = banner && ['info', 'warning', 'critical'].includes(String(banner.type ?? banner.variant))\n ? String(banner.type ?? banner.variant) as 'info' | 'warning' | 'critical'\n : undefined\n const bannerColor = objectValue(banner?.color)\n const bodyFont = projectedFont(objectValue(config.fonts)?.body ?? config.fonts)\n const headingFont = projectedFont(objectValue(config.fonts)?.heading)\n const feedback = objectValue(config.feedback)\n const seo = objectValue(config.seo)\n const contextualOptions = Array.isArray(objectValue(config.contextual)?.options) ? objectValue(config.contextual)!.options as Array<unknown> : []\n const projectedContextual = contextualOptions.filter((option): option is string => typeof option === 'string' && ['copy', 'view', 'chatgpt', 'claude', 'perplexity'].includes(option))\n const droppedContextual = contextualOptions.filter((option) => !projectedContextual.includes(option as string))\n if (droppedContextual.length > 0) {\n warnings.push({ code: 'unsupported-config', message: `contextual.options entries not supported by the page menu were dropped: ${droppedContextual.map((option) => typeof option === 'string' ? option : 'custom entry').join(', ')}.`, source: 'docs.json' })\n }\n const rawMetatags = Object.entries(objectValue(seo?.metatags) ?? {})\n // Same rules as the renderer's `validMetatags`: a plain name, a short string value, and no http-equiv directive.\n const httpEquivNames = new Set(['refresh', 'set-cookie', 'location', 'content-security-policy', 'content-security-policy-report-only', 'content-type', 'default-style', 'x-ua-compatible'])\n const validMetatag = (entry: [string, unknown]): entry is [string, string] => /^[\\w:.-]+$/.test(entry[0])\n && !entry[0].toLowerCase().startsWith('http-equiv') && !httpEquivNames.has(entry[0].toLowerCase())\n && typeof entry[1] === 'string' && entry[1].length <= 1000 && ![...entry[1]].some((character) => character.charCodeAt(0) < 32)\n const metatagEntries = rawMetatags.filter(validMetatag)\n const droppedMetatags = rawMetatags.filter((entry) => !validMetatag(entry)).map(([key]) => key)\n if (droppedMetatags.length > 0) {\n warnings.push({ code: 'unsupported-config', message: `seo.metatags entries were dropped because the name is not a plain meta name, is an http-equiv directive, or the value is not a string of at most 1000 characters: ${droppedMetatags.join(', ')}.`, source: 'docs.json' })\n }\n const projectedMetatags = metatagEntries.length > 0 ? Object.fromEntries(metatagEntries) : undefined\n const projectedColorsValue = projectedColors(config.colors, warnings)\n const iconLibrary = objectValue(config.icons)?.library\n // Mintlify's default icon library is Font Awesome when docs.json names none.\n const projectedIconLibrary = iconLibrary === undefined\n ? 'fontawesome'\n : ['lucide', 'fontawesome', 'tabler'].includes(String(iconLibrary))\n ? String(iconLibrary) as 'lucide' | 'fontawesome' | 'tabler'\n : undefined\n const docsNavbar = projectedNavbar(config.navbar)\n const legacyRaw = legacyTopbarNavbar(config)\n const legacyNavbar = projectedNavbar(legacyRaw)\n const legacyLinkCount = Array.isArray(legacyRaw?.links) ? legacyRaw.links.length : 0\n if (legacyNavbar?.links && legacyNavbar.links.length < legacyLinkCount) {\n warnings.push({ code: 'unsupported-config', message: `${legacyLinkCount - legacyNavbar.links.length} mint.json topbarLinks entr${legacyLinkCount - legacyNavbar.links.length === 1 ? 'y' : 'ies'} without a valid name and url were skipped.` })\n }\n if (docsNavbar?.links && legacyNavbar?.links) {\n warnings.push({ code: 'unsupported-config', message: 'Both docs.json navbar.links and mint.json topbarLinks are set; the docs.json navbar.links were used and topbarLinks ignored.' })\n }\n if (docsNavbar?.primary && legacyNavbar?.primary) {\n warnings.push({ code: 'unsupported-config', message: 'Both docs.json navbar.primary and mint.json topbarCtaButton are set; navbar.primary was used and topbarCtaButton ignored.' })\n }\n if (legacyRaw?.primary && !legacyNavbar?.primary) {\n warnings.push({ code: 'unsupported-config', message: 'mint.json topbarCtaButton has no valid url and was skipped.' })\n }\n const navbar = docsNavbar || legacyNavbar\n ? {\n ...(docsNavbar?.links ?? legacyNavbar?.links ? { links: docsNavbar?.links ?? legacyNavbar?.links } : {}),\n ...(docsNavbar?.primary ?? legacyNavbar?.primary ? { primary: docsNavbar?.primary ?? legacyNavbar?.primary } : {}),\n }\n : undefined\n const globalLinks = projectedGlobalNavigationLinks(objectValue(config.navigation)?.global)\n const navbarLinks = [...new Map(\n [...(navbar?.links ?? []), ...globalLinks].map((link) => [`${link.label}:${link.href}`, link]),\n ).values()]\n const projectedNavigation = navbarLinks.length > 0 || navbar?.primary\n ? { ...(navbarLinks.length > 0 ? { links: navbarLinks } : {}), ...(navbar?.primary ? { primary: navbar.primary } : {}) }\n : undefined\n return {\n // Mintlify pages show a group eyebrow above the title and no breadcrumb trail.\n breadcrumbs: false,\n ...(projectedTheme(config.theme) ? { theme: projectedTheme(config.theme) } : {}),\n ...(projectedContextual.length > 0 ? { contextual: { options: projectedContextual } } : {}),\n ...(projectedColorsValue ? { colors: projectedColorsValue } : {}),\n ...(projectedIconLibrary ? { icons: { library: projectedIconLibrary } } : {}),\n ...(bannerContent ? {\n banner: {\n content: bannerContent,\n ...(banner?.dismissible === false ? { dismissible: false } : {}),\n ...(typeof banner?.id === 'string' ? { id: banner.id } : {}),\n ...(typeof banner?.revision === 'string' ? { revision: banner.revision } : {}),\n ...(bannerType ? { type: bannerType } : {}),\n ...(bannerColor ? { color: {\n ...(typeof bannerColor.light === 'string' ? { light: bannerColor.light } : {}),\n ...(typeof bannerColor.dark === 'string' ? { dark: bannerColor.dark } : {}),\n } } : {}),\n },\n } : {}),\n ...(projectedNavigation ? { navbar: projectedNavigation } : {}),\n ...(projectedFooter(config.footer) ? { footer: projectedFooter(config.footer) } : {}),\n ...(bodyFont || headingFont ? { fonts: { ...(bodyFont ? { body: bodyFont } : {}), ...(headingFont ? { heading: headingFont } : {}) } } : {}),\n // Mintlify titles pages \"<title> - <site name>\" unless `og:title` overrides them,\n // and its sitemap lists navigation pages only.\n seo: { ...(seo?.indexing === 'all' ? { indexing: 'all' as const } : {}), titleSeparator: ' - ', sitemap: 'navigable', ...(projectedMetatags ? { metatags: projectedMetatags } : {}) },\n ...(typeof feedback?.thumbsRating === 'boolean' ? { feedback: { thumbsRating: feedback.thumbsRating } } : {}),\n }\n}\n\n/**\n * Mintlify's `api.mdx.server` / `api.mdx.auth` (defaults for manual `api:`\n * pages). Only well-formed values are kept; each dropped one is reported.\n */\nfunction projectedApi(config: Record<string, unknown>, warnings: Array<MigrationWarning>): Pick<MigrationDocsConfig, 'api'> {\n const mdx = projectedApiMdx(config, warnings).api?.mdx\n const raw = objectValue(objectValue(config.api)?.playground)?.display\n const display = raw === undefined ? undefined : playgroundDisplay(raw, 'api.playground.display', (message) => warnings.push({ code: 'unsupported-config', message }))\n if (!mdx && !display) return {}\n return { api: { ...(mdx ? { mdx } : {}), ...(display ? { playground: { display } } : {}) } }\n}\n\nfunction projectedApiMdx(config: Record<string, unknown>, warnings: Array<MigrationWarning>): Pick<MigrationDocsConfig, 'api'> {\n const mdx = objectValue(objectValue(config.api)?.mdx)\n if (!mdx) return {}\n const warn = (message: string) => warnings.push({ code: 'unsupported-config', message })\n const rawServers = mdx.server === undefined ? [] : Array.isArray(mdx.server) ? mdx.server : [mdx.server]\n const servers = rawServers.filter((entry): entry is string => {\n const valid = typeof entry === 'string' && /^https?:\\/\\/[^\\s/@?#]+(?:\\/[^\\s?#]*)?$/i.test(entry.trim())\n if (!valid) warn(`api.mdx.server entry ${JSON.stringify(entry)} is not an absolute http(s) URL and was dropped.`)\n return valid\n }).map((entry) => entry.trim())\n const auth = objectValue(mdx.auth)\n const method = typeof auth?.method === 'string' ? auth.method.toLowerCase() : undefined\n const name = typeof auth?.name === 'string' && auth.name.trim() ? auth.name.trim() : undefined\n let projectedAuth: { method: 'bearer' | 'basic' | 'key'; name?: string } | undefined\n if (mdx.auth !== undefined && auth && method !== undefined) {\n if (method === 'bearer' || method === 'basic' || (method === 'key' && name)) {\n projectedAuth = { method, ...(name ? { name } : {}) }\n } else {\n warn(`api.mdx.auth method ${JSON.stringify(auth.method)} is not supported (bearer, basic, or key with a name) and was dropped.`)\n }\n } else if (mdx.auth !== undefined && !auth) {\n warn('api.mdx.auth is not an object and was dropped.')\n }\n if (servers.length === 0 && !projectedAuth) return {}\n return {\n api: {\n mdx: {\n ...(servers.length > 0 ? { server: servers.length === 1 && typeof mdx.server === 'string' ? servers[0] : servers } : {}),\n ...(projectedAuth ? { auth: projectedAuth } : {}),\n },\n },\n }\n}\n\nconst NEXT_REDIRECT_PARAM_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/\n\n/**\n * Next.js redirects compile through path-to-regexp and reject a bare `*`\n * segment (Mintlify and Fern's trailing wildcard syntax) with \"Invalid\n * redirects found\", crashing the whole build. Both platforms only document\n * `*` as a trailing catch-all segment, so it is translated to Next's named\n * catch-all (`/:path*`) here; a `*` anywhere else, or a syntax Next can't\n * express, is dropped rather than guessed at. Source text already written as\n * path-to-regexp (e.g. `:path*`) passes through unchanged: only a literal\n * `*` segment is rewritten, and the final `isValidNextRedirectPath` check\n * still accepts it.\n */\nfunction translateRedirectWildcards(\n source: string,\n destination: string,\n): { source: string; destination: string } | null {\n const sourceSegments = source.split('/')\n const destinationSegments = destination.split('/')\n const sourceWildcardIndex = sourceSegments.indexOf('*')\n if (sourceWildcardIndex !== -1 && sourceWildcardIndex !== sourceSegments.length - 1) return null\n if (sourceSegments.slice(0, -1).includes('*') || destinationSegments.slice(0, -1).includes('*')) return null\n\n const translatedSource = [...sourceSegments]\n if (sourceWildcardIndex !== -1) translatedSource[sourceWildcardIndex] = ':path*'\n\n const destinationHasWildcard = destinationSegments.at(-1) === '*'\n if (destinationHasWildcard && sourceWildcardIndex === -1) return null\n const translatedDestination = [...destinationSegments]\n if (destinationHasWildcard) translatedDestination[translatedDestination.length - 1] = ':path*'\n\n const finalSource = translatedSource.join('/')\n const finalDestination = translatedDestination.join('/')\n if (!isValidNextRedirectPath(finalSource) || !isValidNextRedirectPath(finalDestination)) return null\n return { source: finalSource, destination: finalDestination }\n}\n\n/** Conservative structural check for the path-to-regexp syntax Next.js redirects accept. */\nfunction isValidNextRedirectPath(path: string): boolean {\n return path.split('/').every((segment) => {\n if (segment === '' || segment === '*') return segment === ''\n if (!segment.startsWith(':')) return !segment.includes('*') && !segment.includes(':')\n const name = segment.endsWith('*') ? segment.slice(1, -1) : segment.slice(1)\n return NEXT_REDIRECT_PARAM_NAME.test(name)\n })\n}\n\n/**\n * Shared redirect-safety check for Mintlify and Fern: both require a\n * site-relative, `/`-rooted source and destination (Mintlify's own redirect\n * docs give only rooted examples; neither documents an absolute-URL\n * destination), so a protocol-relative or absolute value (`//evil.example`\n * reads as same-scheme cross-origin to a browser and to Next.js) is rejected.\n */\n// Matches every ASCII control character (incl. DEL, \\x7f) and every Unicode\n// whitespace character (`\\s` already covers the line/paragraph separators\n// U+2028/U+2029, along with tab, CR, LF, NBSP, etc).\nconst UNSAFE_REDIRECT_CHARS = /[\\s\\x00-\\x1f\\x7f]/\n\nexport function isRedirectPathSafe(rawSource: string, rawDestination: string): boolean {\n for (const value of [rawSource, rawDestination]) {\n if (!value.startsWith('/')) return false\n if (value.startsWith('//') || value.startsWith('/\\\\')) return false\n if (value.includes('\\\\')) return false\n // A browser strips whitespace/control characters (tab, CR, LF, ...) from\n // a URL before navigating, so a literal tab in `/\\t/evil.example` passes\n // every check above yet reaches the browser as `//evil.example`. Reject\n // any such character anywhere in the raw value.\n if (UNSAFE_REDIRECT_CHARS.test(value)) return false\n // Browsers unescape a leading `%2f%2f`/`%5c` before treating it as `//`/`\\`.\n const lower = value.toLowerCase()\n if (lower.startsWith('/%2f%2f') || lower.startsWith('/%5c')) return false\n }\n return true\n}\n\n/**\n * Translate a redirect's trailing wildcard for Next.js, once its source and\n * destination have already passed {@link isRedirectPathSafe}.\n */\nexport { translateRedirectWildcards }\n\n/**\n * Mintlify's `versions` container authors every version's own page paths\n * with the version identifier as a literal leading segment (`v1.15.22/en/\n * introduction`), including the default version's — the live site still\n * serves that path, but also serves the same page with the segment\n * dropped (`/en/introduction` 307s to the versioned URL). Thally has no\n * multi-version content model of its own (each version becomes its own\n * tab, per existing behavior), so an in-repo link authored the second way\n * has nothing to resolve to. Collecting each `versions` container's\n * default identifier lets the repository scanner add a matching redirect\n * alongside the one it already adds for a page's literal source path.\n */\nexport function mintlifyDefaultVersionPrefixes(config: Record<string, unknown>): Set<string> {\n const navigation = objectValue(config.navigation) ?? config\n const prefixes = new Set<string>()\n function collect(container: Record<string, unknown>): void {\n if (Array.isArray(container.versions)) {\n const entries = container.versions\n .map((value) => objectValue(value))\n .filter((value): value is Record<string, unknown> => value !== null)\n const defaultEntry = entries.find((entry) => entry.default === true) ?? entries[0]\n if (defaultEntry && typeof defaultEntry.version === 'string' && defaultEntry.version.trim()) {\n prefixes.add(defaultEntry.version.trim())\n }\n }\n if (Array.isArray(container.languages)) {\n for (const value of container.languages) {\n const language = objectValue(value)\n if (language) collect(language)\n }\n }\n }\n collect(navigation)\n return prefixes\n}\n\n/**\n * Every version identifier declared across all `versions` containers (not\n * just the default one) — used to label which versions' pages were dropped\n * when a repository exceeds the source-file discovery budget.\n */\nexport function mintlifyAllVersionPrefixes(config: Record<string, unknown>): Set<string> {\n const navigation = objectValue(config.navigation) ?? config\n const prefixes = new Set<string>()\n function collect(container: Record<string, unknown>): void {\n if (Array.isArray(container.versions)) {\n for (const value of container.versions) {\n const entry = objectValue(value)\n if (entry && typeof entry.version === 'string' && entry.version.trim()) prefixes.add(entry.version.trim())\n }\n }\n if (Array.isArray(container.languages)) {\n for (const value of container.languages) {\n const language = objectValue(value)\n if (language) collect(language)\n }\n }\n }\n collect(navigation)\n return prefixes\n}\n\n/** Convert current and legacy Mintlify navigation into Thally's schema. */\nexport function projectMintlifyNavigation(\n config: Record<string, unknown>,\n options: MintlifyProjectionOptions = {},\n): MintlifyNavigationResult {\n const warnings: Array<MigrationWarning> = []\n const references: Array<MintlifyPageReference> = []\n const seenReferences = new Set<string>()\n const warningKeys = new Set<string>()\n const appearances: Array<MintlifyReferenceAccess> = []\n const gatedSourceRefs: Array<string> = []\n const gatedNodes = new Map<object, string>()\n const navigation = objectValue(config.navigation) ?? config\n // Rules on the navigation root itself (and on a language, below) cover\n // every page under it, as the container-agnostic sweep sees them too.\n const rootAccess = descendNavigationAccess(OPEN_NAVIGATION_ACCESS, navigationAccess(navigation))\n const limits = { tooDeep: false }\n collectGatedSourceRefs(navigation, undefined, gatedNodes, limits)\n if (limits.tooDeep) {\n warnings.push({\n code: 'unsupported-config',\n message: 'The navigation is too deeply nested to check its access rules, so no pages or sourceRef mounts were published from it. Flatten the navigation nesting in docs.json.',\n })\n }\n const languages = Array.isArray(navigation.languages)\n ? navigation.languages.flatMap((value): Array<Record<string, unknown>> => {\n const language = objectValue(value)\n if (!language) return []\n try {\n const code = Intl.getCanonicalLocales(String(language.language ?? language.locale ?? 'en'))[0]\n return [{ ...language, language: code }]\n } catch {\n warnings.push({ code: 'unsupported-config', message: 'A navigation language with an invalid locale code was skipped.' })\n return []\n }\n })\n : []\n let tabs: Array<MigrationNavigationTab> = []\n let i18n: MigrationDocsConfig['i18n']\n const localizedNavigation: Record<string, Array<MigrationNavigationTab>> = {}\n const projectionTrace: NavigationProjectionTrace = {}\n\n if (languages.length > 0) {\n const defaultLanguage = languages.find((entry) => entry.default === true) ?? languages[0]\n const defaultLocale = String(defaultLanguage.language)\n const locales = languages.map((entry) => {\n const code = String(entry.language)\n let label: string | undefined = LANGUAGE_LABELS[code]\n try {\n label ??= new Intl.DisplayNames(['en'], { type: 'language' }).of(code) ?? undefined\n } catch {\n // Unknown extension tags remain readable as their normalized code.\n }\n return { code, label: label ?? code.toUpperCase() }\n })\n i18n = { defaultLocale, locales }\n const defaultPageIds = new Set<string>()\n // The default language supplies canonical page identities even when it\n // appears after translations in the authored language picker.\n for (const language of [defaultLanguage, ...languages.filter((entry) => entry !== defaultLanguage)]) {\n const locale = String(language.language)\n const context: ProjectionContext = {\n locale,\n defaultPageIds,\n pathPrefix: options.pathPrefix,\n resolveSourceRef: options.resolveSourceRef,\n references,\n seenReferences,\n warnings,\n warningKeys,\n appearances,\n access: descendNavigationAccess(rootAccess, navigationAccess(language)),\n gatedSourceRefs,\n gatedNodes,\n limits,\n }\n const languageTabs = convertContainerToTabs(\n language,\n context,\n 'Documentation',\n language === defaultLanguage ? projectionTrace : undefined,\n )\n if (language === defaultLanguage) {\n tabs = languageTabs\n for (const reference of references) defaultPageIds.add(reference.navigationId)\n } else if (languageTabs.length > 0) {\n localizedNavigation[locale] = languageTabs\n }\n }\n if (Object.keys(localizedNavigation).length > 0) i18n.navigation = localizedNavigation\n } else {\n const context: ProjectionContext = { references, seenReferences, warnings, warningKeys, appearances, access: rootAccess, gatedSourceRefs, gatedNodes, limits, pathPrefix: options.pathPrefix, resolveSourceRef: options.resolveSourceRef }\n tabs = convertContainerToTabs(navigation, context, 'Documentation', projectionTrace)\n if (tabs.length === 0 && Array.isArray(config.navigation)) {\n const children = convertNavigationValues(config.navigation, context)\n if (children.length > 0) {\n const hasRootPages = children.some((page) => typeof page === 'string')\n tabs = [{\n tab: 'Documentation',\n ...(hasRootPages\n ? { pages: children }\n : { groups: children as Array<MigrationNavigationGroup> }),\n }]\n }\n }\n }\n\n if (tabs.length === 0) {\n warnings.push({\n code: 'unsupported-config',\n message: 'Mintlify navigation could not be projected; generated navigation will be used.',\n })\n }\n // A version selector is a separate control from the sibling tabs. Keep\n // the source tab identity unique while giving Thally a clean visible label\n // and a route prefix for selecting the matching version on deep links.\n const versionOwner = languages.length > 0\n ? languages.find((entry) => entry.default === true) ?? languages[0]\n : navigation\n const globalNavigation = objectValue(versionOwner.global) ?? objectValue(navigation.global)\n const shortcuts = Array.isArray(globalNavigation?.anchors)\n ? globalNavigation.anchors.flatMap((value) => {\n const anchor = objectValue(value)\n const label = typeof anchor?.anchor === 'string' ? anchor.anchor.trim() : ''\n const href = projectedHref(anchor?.href)\n if (!label || !href) return []\n return [{ label, href, ...(iconName(anchor?.icon) ? { icon: iconName(anchor?.icon) } : {}) }]\n })\n : []\n const rawVersions = Array.isArray(versionOwner.versions)\n ? versionOwner.versions.map(objectValue).filter((value): value is Record<string, unknown> => value !== null)\n .sort((left, right) => Number(right.default === true) - Number(left.default === true))\n : []\n const versionNames = rawVersions\n .map((entry) => typeof entry.version === 'string' ? entry.version.trim() : '')\n .filter(Boolean)\n const annotateVersions = (items: Array<MigrationNavigationTab>): Array<MigrationNavigationTab> => items.map((item) => {\n const version = versionNames.find((name) => item.tab.startsWith(`${name}: `))\n return version ? { ...item, version, displayLabel: item.tab.slice(version.length + 2) } : item\n })\n const firstPage = (item: MigrationNavigationTab): string | undefined => {\n const visit = (entries: Array<string | MigrationNavigationGroup>): string | undefined => {\n for (const entry of entries) {\n if (typeof entry === 'string') return entry\n const nested = visit(entry.pages)\n if (nested) return nested\n }\n return undefined\n }\n return visit([...(item.pages ?? []), ...(item.groups ?? [])])\n }\n const defaultVersion = rawVersions.find((entry) => entry.default === true) ?? rawVersions[0]\n const versions = rawVersions.length > 1 ? rawVersions.flatMap((entry) => {\n const label = typeof entry.version === 'string' ? entry.version.trim() : ''\n if (!label) return []\n const candidates = tabs.filter((item) => item.tab.startsWith(`${label}: `))\n .map(firstPage).filter((page): page is string => Boolean(page))\n // Versions sometimes reuse unversioned guide pages but still own a\n // versioned API tab. Enter through an owned route when one exists.\n const landing = candidates.find((page) => page.startsWith(`${label}/`)) ?? candidates[0]\n if (!landing) return []\n const firstSegment = landing.split('/')[0]\n const prefix = firstSegment === label ? label : ''\n return [{ label, prefix, href: `/${landing}`, ...(entry === defaultVersion ? { default: true } : {}), ...(entry.hidden === true ? { hidden: true } : {}) }]\n }) : []\n if (versions.length > 1) {\n tabs = annotateVersions(tabs)\n for (const [locale, items] of Object.entries(localizedNavigation)) localizedNavigation[locale] = annotateVersions(items)\n }\n const redirects = Array.isArray(config.redirects)\n ? config.redirects.flatMap((value) => {\n const redirect = objectValue(value)\n if (!redirect || typeof redirect.source !== 'string' || typeof redirect.destination !== 'string') return []\n const rawSource = redirect.source.trim()\n const rawDestination = redirect.destination.trim()\n if (!isRedirectPathSafe(rawSource, rawDestination)) return []\n const translated = translateRedirectWildcards(rawSource, rawDestination)\n if (!translated) {\n warnings.push({\n code: 'unsupported-config',\n message: `Redirect from ${rawSource} uses a wildcard Next.js cannot express and was dropped.`,\n })\n return []\n }\n return [{\n source: translated.source,\n destination: translated.destination,\n ...(typeof redirect.permanent === 'boolean' ? { permanent: redirect.permanent } : {}),\n }]\n })\n : []\n collectReferenceAccess(navigation, OPEN_NAVIGATION_ACCESS, options.pathPrefix, appearances)\n return {\n docsConfig: {\n tabs,\n ...(projectionTrace.rootContainerKind === 'dropdowns' || versions.length > 1 || shortcuts.length > 0\n ? { navigation: {\n ...(projectionTrace.rootContainerKind === 'dropdowns' ? { display: 'dropdown' as const } : {}),\n ...(versions.length > 1 ? { versions } : {}),\n ...(shortcuts.length > 0 ? { shortcuts } : {}),\n } }\n : {}),\n ...projectedCompatibleConfig(config, warnings),\n ...projectedApi(config, warnings),\n ...(i18n ? { i18n } : {}),\n ...(redirects.length > 0 ? { redirects } : {}),\n },\n pageReferences: references,\n referenceAccess: appearances,\n gatedSourceRefs,\n warnings,\n }\n}\n\nfunction titleCase(value: string): string {\n return value\n .split(/[-_]/)\n .map((word) => ['api', 'cli', 'sdk', 'ui'].includes(word.toLowerCase())\n ? word.toUpperCase()\n : word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ')\n}\n\ninterface GeneratedNavigationOptions {\n /** Dedicated docs platforms expose their primary sections as top-level tabs. */\n topLevelTabs?: boolean\n /** Ordered tabs recovered from the source site's rendered navigation. */\n topLevelNavigation?: ReadonlyArray<{\n section: string\n label: string\n pageId: string\n }>\n}\n\nconst TOP_LEVEL_LABELS: Record<string, string> = {\n 'api-reference': 'API Reference',\n cli: 'CLI',\n faqs: 'FAQs',\n introduction: 'Overview',\n 'mcp-server': 'MCP Server',\n sdk: 'SDK',\n}\n\nfunction topLevelLabel(segment: string): string {\n return TOP_LEVEL_LABELS[segment] ?? titleCase(segment)\n}\n\nfunction groupsWithinSection(\n section: string,\n pageIds: Array<string>,\n sectionLabel = topLevelLabel(section),\n): Array<MigrationNavigationGroup> {\n const groups = new Map<string, Array<string>>()\n for (const id of pageIds) {\n const relative = id === 'introduction'\n ? ''\n : id.startsWith(`${section}/`)\n ? id.slice(section.length + 1)\n : id\n const nestedSegment = relative.includes('/') ? relative.split('/', 1)[0] : 'overview'\n const group = groups.get(nestedSegment) ?? []\n group.push(id)\n groups.set(nestedSegment, group)\n }\n return [...groups].map(([segment, pages]) => ({\n group: segment === 'overview' ? sectionLabel : titleCase(segment),\n pages,\n }))\n}\n\nfunction preferredLandingPage(\n section: string,\n pageIds: Array<string>,\n): string | undefined {\n const candidates = [\n section === 'introduction' ? 'introduction' : undefined,\n `${section}/overview`,\n `${section}/introduction`,\n section,\n ]\n return candidates.find((candidate): candidate is string => Boolean(candidate && pageIds.includes(candidate)))\n}\n\n/** Build deterministic fallback navigation from imported default-locale pages. */\nexport function buildNavigationFromPages(\n pages: Array<MigrationPage>,\n options: GeneratedNavigationOptions = {},\n): MigrationDocsConfig {\n const ids = pages.filter((page) => !page.locale || page.locale === 'en').map((page) => page.navigationId)\n const ordered = [...new Set(ids)]\n if (options.topLevelTabs) {\n const sourceNavigation = (options.topLevelNavigation ?? []).flatMap((entry) => {\n const sectionPages = ordered.filter((id) => id === entry.section || id.startsWith(`${entry.section}/`))\n const pageId = ordered.includes(entry.pageId)\n ? entry.pageId\n : preferredLandingPage(entry.section, sectionPages) ?? sectionPages[0]\n return pageId ? [{ ...entry, pageId }] : []\n })\n if (sourceNavigation.length > 1) {\n const claimedIds = new Set<string>()\n const tabs = sourceNavigation.map((entry) => {\n const pageIds = ordered.filter((id) => {\n const matches = id === entry.pageId\n || id === entry.section\n || id.startsWith(`${entry.section}/`)\n if (matches) claimedIds.add(id)\n return matches\n })\n return { entry, pageIds }\n })\n // Mintlify's first tab is the documentation home and owns pages that do\n // not belong to another product tab (for example /create and /deploy).\n tabs[0].pageIds.push(...ordered.filter((id) => !claimedIds.has(id)))\n return {\n tabs: tabs.map(({ entry, pageIds }) => ({\n tab: entry.label,\n href: entry.pageId === 'introduction' ? '/' : `/${entry.pageId}`,\n groups: groupsWithinSection(entry.section, [...new Set(pageIds)], entry.label),\n })),\n }\n }\n const sectionNames = [...new Set(ordered\n .filter((id) => id.includes('/'))\n .map((id) => id.split('/', 1)[0]))]\n if (sectionNames.length > 1) {\n const defaultSection = sectionNames.includes('introduction')\n ? 'introduction'\n : sectionNames[0]\n const sections = new Map<string, Array<string>>()\n for (const id of ordered) {\n const section = id.includes('/') ? id.split('/', 1)[0] : defaultSection\n const bucket = sections.get(section) ?? []\n bucket.push(id)\n sections.set(section, bucket)\n }\n return {\n tabs: [...sections].map(([section, pageIds]) => {\n const label = topLevelLabel(section)\n const landingPage = preferredLandingPage(section, pageIds)\n return {\n tab: label,\n ...(landingPage\n ? { href: landingPage === 'introduction' ? '/' : `/${landingPage}` }\n : {}),\n groups: groupsWithinSection(section, pageIds),\n }\n }),\n }\n }\n }\n const buckets = new Map<string, Array<string>>()\n for (const id of ordered) {\n const segment = id.includes('/') ? id.split('/', 1)[0] : 'overview'\n const bucket = buckets.get(segment) ?? []\n bucket.push(id)\n buckets.set(segment, bucket)\n }\n const groups = [...buckets].map(([segment, pageIds]) => ({\n group: segment === 'overview' ? 'Overview' : titleCase(segment),\n pages: pageIds,\n }))\n return { tabs: [{ tab: 'Documentation', groups }] }\n}\n\n/** Exposed for repository discovery and focused unit tests. */\nexport function isDocumentationExtension(filename: string): boolean {\n return ['.md', '.mdx', '.rst', '.txt'].includes(extname(filename).toLowerCase())\n}\n\n/**\n * A source page can be excluded after navigation is projected (invalid MDX,\n * a client-boundary function prop, a duplicate id). Its reference still sits\n * in the projected tabs/groups at that point, across every tab, group, and\n * locale, since the nav tree and the page list are built from the same\n * source config independently. Drop those dangling references here so\n * `thally check` never reports a nav entry with no backing MDX file, and\n * drop any group left with no pages as a result. Shared by every platform's\n * navigation projection (Mintlify, Fern, Docusaurus).\n */\nexport function pruneMissingNavigationPages(\n config: MigrationDocsConfig,\n availableIds: ReadonlySet<string>,\n): MigrationDocsConfig {\n const pruneNodes = (nodes: Array<string | MigrationNavigationGroup>): Array<string | MigrationNavigationGroup> => {\n const seenSiblings = new Set<string>()\n return nodes.flatMap((node): Array<string | MigrationNavigationGroup> => {\n if (typeof node === 'string') {\n // Distinct source files can resolve to one final frontmatter slug.\n // Showing that route twice in one sidebar group is misleading even\n // though both references passed the existence check.\n if (!availableIds.has(node) || seenSiblings.has(node)) return []\n seenSiblings.add(node)\n return [node]\n }\n const pages = pruneNodes(node.pages)\n return pages.length > 0 ? [{ ...node, pages }] : []\n })\n }\n const tabs = config.tabs.flatMap((tab) => {\n const hadPages = (tab.pages?.length ?? 0) > 0 || (tab.groups?.length ?? 0) > 0\n const pages = tab.pages ? pruneNodes(tab.pages) : undefined\n const groups = tab.groups ? (pruneNodes(tab.groups) as Array<MigrationNavigationGroup>) : undefined\n const hasPages = (pages?.length ?? 0) > 0 || (groups?.length ?? 0) > 0\n // A tab that never referenced pages/groups (href-only, api-only) is untouched.\n if (hadPages && !hasPages) return []\n return [{\n ...tab,\n ...(tab.pages ? { pages } : {}),\n ...(tab.groups ? { groups } : {}),\n }]\n })\n return { ...config, tabs }\n}\n","/**\n * Mintlify-only helpers: page access rules, site-wide CSS/JS/font assets,\n * and appearance settings. Kept apart from the generic repository walk so the\n * rules that decide how restricted pages migrate (and what is quarantined)\n * are auditable in one place.\n *\n * Access invariant: a restricted page migrates with normalized `groups` /\n * `public` frontmatter that the Thally runtime enforces; anything ambiguous\n * (malformed values, group lists that share no group) fails closed and the\n * page is quarantined instead.\n */\n\nimport { extname } from 'node:path'\n\nimport { normalizeAssetPath } from './path.js'\nimport type { MigrationDocsConfig, MigrationWarning } from './types.js'\n\nfunction objectValue(value: unknown): Record<string, unknown> | null {\n return value !== null && typeof value === 'object' && !Array.isArray(value)\n ? value as Record<string, unknown>\n : null\n}\n\n/**\n * Access rules read from one page's frontmatter or one navigation container,\n * normalized the way the Thally runtime reads page frontmatter.\n */\nexport interface MintlifyAccess {\n /** Trimmed, non-empty, de-duplicated group names; absent when none are named. */\n groups?: Array<string>\n /** `true` opens a page to anyone; `false` requires any signed-in reader. */\n isPublic?: boolean\n /** Set when a value cannot be read unambiguously: the page fails closed. */\n problem?: string\n}\n\n/** Access imposed by every navigation container above one appearance of a page. */\nexport interface NavigationAccess {\n /** Intersection of every container group list on the path. */\n groups?: Array<string>\n /** Some container on the path sets `public: false`. */\n hasPublicFalse: boolean\n /** Some container on the path sets `public: true` (Mintlify's documented group flag). */\n hasPublicTrue: boolean\n /** Set when a container value is malformed or nested group lists share no group. */\n problem?: string\n}\n\n/** Access frontmatter a migrated page is written with. */\nexport interface ResolvedPageAccess {\n groups?: Array<string>\n isPublic?: boolean\n}\n\n/** Either the access a page migrates with, or why it cannot migrate safely. */\nexport interface PageAccessVerdict {\n access?: ResolvedPageAccess\n problem?: string\n}\n\nexport const OPEN_NAVIGATION_ACCESS: NavigationAccess = Object.freeze({ hasPublicFalse: false, hasPublicTrue: false })\n\n// Mirrors `parsePublic` / `parseGroups` in src/lib/reader-auth/access.ts\n// (duplicated because packages cannot import the app). The migration must\n// write values the runtime reads identically, and treat as malformed exactly\n// what the runtime would withhold from everyone.\nconst PUBLIC_FALSE_STRINGS = new Set(['false', 'no', 'off', '0'])\nconst PUBLIC_TRUE_STRINGS = new Set(['true'])\n\nfunction parsePublic(value: unknown): { isPublic?: boolean; isMalformed: boolean } {\n if (value === undefined || value === null) return { isMalformed: false }\n if (value === true) return { isPublic: true, isMalformed: false }\n if (value === false || value === 0) return { isPublic: false, isMalformed: false }\n if (typeof value === 'string') {\n const normalized = value.trim().toLowerCase()\n if (PUBLIC_TRUE_STRINGS.has(normalized)) return { isPublic: true, isMalformed: false }\n if (PUBLIC_FALSE_STRINGS.has(normalized)) return { isPublic: false, isMalformed: false }\n }\n return { isMalformed: true }\n}\n\nfunction parseGroups(value: unknown): { groups: Array<string>; isMalformed: boolean } {\n if (value === undefined || value === null || value === '' || value === false) return { groups: [], isMalformed: false }\n if (typeof value === 'string') {\n // A comma-separated string is a common hand-authored mistake for a list.\n const groups = value.split(',').map((group) => group.trim()).filter(Boolean)\n return { groups: [...new Set(groups)], isMalformed: groups.length === 0 }\n }\n if (Array.isArray(value)) {\n if (value.some((group) => typeof group !== 'string')) return { groups: [], isMalformed: true }\n const groups = value.map((group: string) => group.trim()).filter(Boolean)\n // `groups: [\"\"]` names a group nobody can hold: malformed, never open.\n return { groups: [...new Set(groups)], isMalformed: value.length > 0 && groups.length === 0 }\n }\n return { groups: [], isMalformed: true }\n}\n\nfunction accessFrom(groupsValue: unknown, publicValue: unknown, where: string): MintlifyAccess {\n const groups = parseGroups(groupsValue)\n const visibility = parsePublic(publicValue)\n const problem = groups.isMalformed\n ? `${where} \\`groups\\` is not a list of group names`\n : visibility.isMalformed ? `${where} \\`public\\` is neither true nor false` : undefined\n return {\n ...(groups.groups.length > 0 ? { groups: groups.groups } : {}),\n ...(visibility.isPublic !== undefined ? { isPublic: visibility.isPublic } : {}),\n ...(problem ? { problem } : {}),\n }\n}\n\n/**\n * A page's own access frontmatter (Mintlify semantics, which Thally shares):\n * `groups` limits the page to those groups, `public: true` opens it,\n * `public: false` requires a signed-in reader. Key casing (`Groups:`) is not\n * read, as on Mintlify.\n */\nexport function frontmatterAccess(data: Record<string, unknown>): MintlifyAccess {\n return accessFrom(data.groups, data.public, 'frontmatter')\n}\n\n/** Whether access limits who may read a page (anything but open or `public: true`). */\nexport function isRestrictedAccess(access: { groups?: Array<string>; isPublic?: boolean; problem?: string }): boolean {\n return Boolean(access.problem) || (access.groups?.length ?? 0) > 0 || access.isPublic === false\n}\n\nconst NAVIGATION_CONTAINER_KEYS = ['group', 'pages', 'tab', 'groups', 'page', 'root', 'menu', 'href']\n\n/**\n * The access signals on a navigation group/tab, or undefined when it sets\n * none. Mintlify documents only `\"public\": true` on a group; `groups` (a list\n * of strings, never the nested navigation-group objects a tab also calls\n * `groups`) and `public: false` are honored as well so an authored\n * restriction is never published.\n */\nexport function navigationAccess(node: Record<string, unknown>): MintlifyAccess | undefined {\n const isContainer = (entry: unknown): boolean => typeof entry === 'object' && entry !== null && !Array.isArray(entry)\n && NAVIGATION_CONTAINER_KEYS.some((key) => key in entry)\n // A tab also calls its nested navigation group objects `groups`, so an array\n // made only of such containers is navigation, not an access list. A mix of\n // containers and anything else is ambiguous and fails closed.\n const groups = Array.isArray(node.groups) && node.groups.length > 0 && node.groups.every(isContainer) ? undefined : node.groups\n const access = accessFrom(groups, node.public, 'its navigation container\\'s')\n return Object.keys(access).length > 0 ? access : undefined\n}\n\n/** Why a navigation container restricts the pages below it, or undefined (never for `public: true` alone). */\nexport function navigationGateReason(node: Record<string, unknown>): string | undefined {\n const access = navigationAccess(node)\n if (!access || !isRestrictedAccess(access)) return undefined\n return access.problem ?? (access.groups ? 'its navigation container sets `groups`' : 'its navigation container sets `public: false`')\n}\n\nfunction intersect(left: Array<string> | undefined, right: Array<string> | undefined): Array<string> | undefined {\n if (!left) return right\n if (!right) return left\n return left.filter((group) => right.includes(group))\n}\n\n/**\n * Access one level deeper in navigation. Nested containers combine into the\n * strictest rule: the intersection of every group list (empty means nobody\n * qualifies, so it fails closed) and `public: false` anywhere on the path.\n */\nexport function descendNavigationAccess(outer: NavigationAccess, container: MintlifyAccess | undefined): NavigationAccess {\n if (!container) return outer\n const groups = intersect(outer.groups, container.groups)\n const problem = outer.problem ?? container.problem\n ?? (groups && groups.length === 0 ? 'nested navigation containers set `groups` that share no group' : undefined)\n return {\n ...(groups ? { groups } : {}),\n hasPublicFalse: outer.hasPublicFalse || container.isPublic === false,\n hasPublicTrue: outer.hasPublicTrue || container.isPublic === true,\n ...(problem ? { problem } : {}),\n }\n}\n\n/**\n * One page listed in several navigation places gets the strictest of them\n * (as the runtime merges the access of files serving one response): group\n * lists intersect, `public: false` anywhere wins, and `public: true` holds\n * only when every appearance is under a `public: true` container.\n */\nexport function mergeNavigationAppearances(appearances: ReadonlyArray<NavigationAccess>): NavigationAccess {\n if (appearances.length === 0) return OPEN_NAVIGATION_ACCESS\n let groups: Array<string> | undefined\n for (const appearance of appearances) groups = intersect(groups, appearance.groups)\n const problem = appearances.find((appearance) => appearance.problem)?.problem\n ?? (groups && groups.length === 0 ? 'it is listed under navigation containers whose `groups` share no group' : undefined)\n return {\n ...(groups ? { groups } : {}),\n hasPublicFalse: appearances.some((appearance) => appearance.hasPublicFalse),\n hasPublicTrue: appearances.every((appearance) => appearance.hasPublicTrue),\n ...(problem ? { problem } : {}),\n }\n}\n\n/** Whether any container on the path sets an access rule, `public: true` included. */\nexport function hasNavigationRules(access: NavigationAccess): boolean {\n return isNavigationRestricted(access) || access.hasPublicTrue\n}\n\n/** Whether navigation restricts a page listed under it. */\nexport function isNavigationRestricted(access: NavigationAccess): boolean {\n return Boolean(access.problem) || (access.groups?.length ?? 0) > 0 || access.hasPublicFalse\n}\n\n/**\n * The access a page migrates with: its own frontmatter combined with the\n * containers above it. Groups intersect (an empty intersection fails closed);\n * `public: false` from either wins; a container's `public: true` is stamped\n * only on a page that names no groups and is not itself `public: false`.\n */\nexport function resolvePageAccess(own: MintlifyAccess, navigation: NavigationAccess): PageAccessVerdict {\n const problem = own.problem ?? navigation.problem\n if (problem) return { problem }\n const groups = intersect(own.groups, navigation.groups)\n if (groups && groups.length === 0) {\n return { problem: 'its frontmatter `groups` and its navigation container\\'s `groups` share no group' }\n }\n const isPublic = own.isPublic === false || navigation.hasPublicFalse\n ? false\n : own.isPublic === true || (navigation.hasPublicTrue && !groups) ? true : undefined\n return { access: { ...(groups ? { groups } : {}), ...(isPublic !== undefined ? { isPublic } : {}) } }\n}\n\nconst CONFIG_BASENAME = /(?:^|[.-])(?:config|rc)\\.[cm]?js$|^(?:tailwind|postcss|next|eslint|prettier|babel|webpack|vite|vitest|jest|rollup|gulpfile|gruntfile|stylelint|commitlint|lint-staged|svgo|metro)[.-]/i\nconst BUILD_OUTPUT_SEGMENTS = new Set(['dist', 'build', 'coverage'])\nconst FONT_EXTENSIONS = new Set(['.woff', '.woff2', '.ttf', '.otf'])\n\n/**\n * Mintlify serves every `.css` and `.js` file in the content directory\n * site-wide. Tooling config files and build output are not part of that, and\n * (unlike Mintlify) are never published.\n */\nexport function isMintlifyServedScriptOrStyle(relativePath: string): boolean {\n const extension = extname(relativePath).toLowerCase()\n if (extension !== '.css' && extension !== '.js') return false\n const segments = relativePath.split('/')\n if (segments.slice(0, -1).some((segment) => BUILD_OUTPUT_SEGMENTS.has(segment.toLowerCase()))) return false\n return extension === '.css' || !CONFIG_BASENAME.test(segments[segments.length - 1])\n}\n\nexport interface MintlifyFontSource {\n family: string\n /** Value as authored. */\n source: string\n /** Content-relative path when the source is a local file, else null. */\n path: string | null\n remote: boolean\n}\n\n/** Self-hosted font references from docs.json `fonts` (top level, `heading`, `body`). */\nexport function mintlifyFontSources(config: Record<string, unknown> | null): Array<MintlifyFontSource> {\n const fonts = objectValue(config?.fonts)\n if (!fonts) return []\n const found: Array<MintlifyFontSource> = []\n for (const entry of [fonts, objectValue(fonts.heading), objectValue(fonts.body)]) {\n if (!entry || typeof entry.source !== 'string' || !entry.source.trim()) continue\n const source = entry.source.trim()\n const family = typeof entry.family === 'string' ? entry.family : 'a font'\n const remote = /^(?:[a-z][a-z0-9+.-]*:|\\/\\/)/i.test(source)\n // A leading slash is relative to the content root, as on Mintlify; the\n // path is only accepted when it stays traversal-free and is a font file.\n const path = remote || /^[a-z]:[\\\\/]/i.test(source) ? null : normalizeAssetPath(source)\n found.push({ family, source, path: path && FONT_EXTENSIONS.has(extname(path).toLowerCase()) ? path : null, remote })\n }\n return found\n}\n\n/**\n * `appearance.default` / `appearance.strict`, with legacy `modeToggle`\n * (`default`, `isHidden`) as fallback per field. docs.json wins when both\n * define a field; invalid values are skipped with a warning.\n */\nexport function mintlifyAppearance(\n config: Record<string, unknown>,\n warnings: Array<MigrationWarning>,\n): NonNullable<MigrationDocsConfig['appearance']> {\n const appearance = objectValue(config.appearance)\n const legacy = objectValue(config.modeToggle)\n const result: NonNullable<MigrationDocsConfig['appearance']> = {}\n const isMode = (value: unknown): value is 'system' | 'light' | 'dark' =>\n value === 'system' || value === 'light' || value === 'dark'\n const skip = (field: string, value: unknown): void => {\n warnings.push({\n code: 'unsupported-config',\n message: `Ignored invalid ${field} value ${JSON.stringify(value)?.slice(0, 40)}; the appearance default was left unchanged.`,\n })\n }\n if (appearance?.default !== undefined) {\n if (isMode(appearance.default)) result.default = appearance.default\n else skip('appearance.default', appearance.default)\n } else if (legacy?.default !== undefined) {\n if (isMode(legacy.default)) result.default = legacy.default\n else skip('modeToggle.default', legacy.default)\n }\n if (appearance?.strict !== undefined) {\n if (typeof appearance.strict === 'boolean') {\n if (appearance.strict) result.showToggle = false\n } else skip('appearance.strict', appearance.strict)\n } else if (legacy?.isHidden !== undefined) {\n if (typeof legacy.isHidden === 'boolean') {\n if (legacy.isHidden) result.showToggle = false\n } else skip('modeToggle.isHidden', legacy.isHidden)\n }\n return result\n}\n\n/**\n * Mintlify's playground display mode. `auth` shows the playground only to signed-in\n * readers; Thally has no reader sign-in, so it is carried as `simple` (no playground).\n */\nexport function playgroundDisplay(value: unknown, where: string, warn?: (message: string) => void): 'interactive' | 'simple' | 'none' | undefined {\n const mode = typeof value === 'string' ? value.trim().toLowerCase() : ''\n if (mode === 'interactive' || mode === 'simple' || mode === 'none') return mode\n if (mode === 'auth') {\n warn?.(`${where} \"auth\" requires reader sign-in, which Thally does not support. Migrated as \"simple\" (no Try it). Set it to \"interactive\" to allow everyone.`)\n return 'simple'\n }\n warn?.(`${where} is not one of interactive, simple, none, auth and was dropped.`)\n return undefined\n}\n","/** Safe path normalization used before reading or writing migrated content. */\n\nimport { realpathSync } from 'node:fs'\nimport { isAbsolute, relative, resolve, sep } from 'node:path'\n\nconst SAFE_SEGMENT = /[^a-z0-9._-]+/gi\n\n/**\n * Remove trailing forward slashes in linear time. Keeping leading slashes is\n * important because callers may still need to reject absolute paths.\n */\nexport function trimTrailingSlashes(value: string): string {\n let end = value.length\n while (end > 0 && value.charCodeAt(end - 1) === 47) end--\n return value.slice(0, end)\n}\n\n/** Remove forward slashes from both ends without a backtracking expression. */\nexport function trimEdgeSlashes(value: string): string {\n let start = 0\n let end = value.length\n while (start < end && value.charCodeAt(start) === 47) start++\n while (end > start && value.charCodeAt(end - 1) === 47) end--\n return value.slice(start, end)\n}\n\n/** Turn a source path segment into a stable URL/file-system slug. */\nexport function slugifySegment(value: string, preserveCase = false): string {\n let decoded = value\n try {\n decoded = decodeURIComponent(value)\n } catch {\n // Malformed percent escapes are legal in source filenames. Treat them as\n // literal slug input instead of letting one file abort the whole import.\n }\n const normalized = preserveCase ? decoded : decoded.toLowerCase()\n const slug = normalized\n .replace(/\\.(?:html?|mdx?)$/i, '')\n .replace(SAFE_SEGMENT, '-')\n .replace(/(^-|-$)/g, '')\n // A dot segment (also spelled `%2e%2e`) must never reach a page id or file path.\n return slug === '.' || slug === '..' ? '' : slug\n}\n\n/**\n * Normalize a page reference to a Thally content id. Invalid or traversal\n * references return `null` instead of being allowed near the file system.\n */\nexport function pageIdFromReference(value: string, preserveCase = false): string | null {\n const withoutQuery = value.split(/[?#]/, 1)[0].replace(/\\\\/g, '/').replace(/^\\/+/, '')\n if (!withoutQuery || withoutQuery.includes('\\0')) return null\n const rawSegments = withoutQuery.split('/').filter(Boolean)\n if (rawSegments.some((segment) => segment === '..' || segment === '.')) return null\n const last = rawSegments.at(-1)?.replace(/\\.(?:mdx?|rst|txt)$/i, '') ?? ''\n // `index` collapses to its parent directory at any depth, matching how\n // Mintlify and Next.js both treat directory indexes. `readme` only does the\n // same at the content root (`readme.md` -> `/`); a nested `readme` is a\n // real, addressable page (`migration/readme.md` -> `/migration/readme`)\n // and must not collide with a sibling `migration/index.md`.\n const isRootLevelReadme = rawSegments.length === 1 && /^readme$/i.test(last)\n const collapsesToParent = /^index$/i.test(last) || isRootLevelReadme\n const baseSegments = collapsesToParent ? rawSegments.slice(0, -1) : rawSegments\n if (!collapsesToParent) {\n baseSegments[baseSegments.length - 1] = last\n }\n const segments = baseSegments.map((segment) => slugifySegment(segment, preserveCase)).filter(Boolean)\n return segments.join('/') || 'introduction'\n}\n\n/**\n * Resolve language directories and legacy language-prefixed sections to the\n * same content identity used by default-language navigation. A hyphenated\n * section is only an alias when its unprefixed counterpart actually exists.\n */\nexport function mintlifyLocalizedReference(\n value: string,\n locale: string,\n defaultPageIds: ReadonlySet<string> = new Set(),\n): string {\n const reference = value.replace(/^\\/+/, '')\n const localePrefix = `${locale.toLowerCase()}/`\n if (reference.toLowerCase().startsWith(localePrefix)) return reference.slice(localePrefix.length)\n const sectionPrefix = `${locale.toLowerCase()}-`\n if (reference.toLowerCase().startsWith(sectionPrefix)) {\n const candidate = reference.slice(sectionPrefix.length)\n const candidateId = pageIdFromReference(candidate, true)\n if (candidateId && defaultPageIds.has(candidateId)) return candidate\n }\n // A versioned site nests the locale one level in instead of leading\n // (\"edge/ar/introduction\", not \"ar/edge/introduction\") — every version's\n // page references repeat this shape for every language, so the locale\n // segment shows up mid-path rather than only as a directory prefix. Drop\n // the first segment that exactly matches the locale wherever it falls,\n // so the resulting identity is shared across languages instead of the\n // caller re-adding a locale prefix on top of one already embedded here.\n const segments = reference.split('/')\n const localeIndex = segments.findIndex((segment) => segment.toLowerCase() === locale.toLowerCase())\n if (localeIndex > 0) {\n return [...segments.slice(0, localeIndex), ...segments.slice(localeIndex + 1)].join('/')\n }\n return value\n}\n\n/** Resolve an untrusted relative path and prove it remains below `root`. */\nexport function resolveWithin(root: string, candidate: string): string {\n return resolveWithinRoot(root, candidate, root)\n}\n\n/**\n * Resolve an untrusted relative path against `baseDir`, but only require the\n * result to stay below `confineRoot` (which may be an ancestor of `baseDir`).\n * Used where a config file's relative paths are conventionally resolved\n * against the file's own directory, yet the security boundary is a wider\n * root (e.g. the whole repository checkout).\n */\nexport function resolveWithinRoot(baseDir: string, candidate: string, confineRoot: string): string {\n if (isAbsolute(candidate) || candidate.includes('\\0')) {\n throw new Error(`Unsafe migration path: ${candidate}`)\n }\n const target = resolve(baseDir, candidate)\n const fromRoot = relative(resolve(confineRoot), target)\n if (fromRoot === '..' || fromRoot.startsWith(`..${sep}`) || isAbsolute(fromRoot)) {\n throw new Error(`Migration path escapes its root: ${candidate}`)\n }\n return target\n}\n\n/** Return a slash-separated, traversal-free asset path. */\nexport function normalizeAssetPath(value: string): string | null {\n const normalized = value.replace(/\\\\/g, '/').replace(/^\\/+/, '')\n if (!normalized || normalized.includes('\\0')) return null\n const segments = normalized.split('/').filter(Boolean)\n if (segments.some((segment) => segment === '.' || segment === '..')) return null\n return segments.join('/')\n}\n\n/** Throws when an existing path resolves, through a symlink, outside the repository; a path that does not exist is returned unchanged. */\nexport function withinRealRoot(candidate: string, repositoryRoot: string): string {\n let real: string\n try { real = realpathSync(candidate) } catch { return candidate }\n const realRoot = realpathSync(repositoryRoot)\n if (real !== realRoot && !real.startsWith(realRoot.endsWith(sep) ? realRoot : realRoot + sep)) {\n throw new Error(`Migration path escapes its root: ${candidate}`)\n }\n return candidate\n}\n\n/** Whether `error` came from a path that leaves its root or is absolute, as thrown by the helpers above. */\nexport function isPathEscapeError(error: unknown): boolean {\n return error instanceof Error && /escapes its root|Unsafe migration path/.test(error.message)\n}\n","/**\n * Allowlist policy for imported SVG files.\n *\n * An SVG served from the docs origin and opened directly is a full document:\n * script, event handlers, `javascript:` links, animations that rewrite\n * `href`, foreign HTML, and remote references all run or load there. Regular\n * expressions miss namespace prefixes and entity-encoded values, so the file\n * is parsed as XML with entities decoded. It passes only if every element and\n * attribute is on a fixed allowlist of static drawing primitives, and every\n * reference stays inside the document. Anything else, including a\n * DOCTYPE, CDATA, or a parser error, sends the file to review.\n */\n\nimport { Parser } from \"htmlparser2\";\n\nconst SVG_NAMESPACE = \"http://www.w3.org/2000/svg\";\nconst XLINK_NAMESPACE = \"http://www.w3.org/1999/xlink\";\n\n/** Static drawing elements; no script, style, animation, link, or image. */\nconst ALLOWED_ELEMENTS = new Set([\n \"svg\", \"g\", \"defs\", \"symbol\", \"use\", \"title\", \"desc\",\n \"path\", \"rect\", \"circle\", \"ellipse\", \"line\", \"polyline\", \"polygon\",\n \"text\", \"tspan\", \"textpath\",\n \"lineargradient\", \"radialgradient\", \"stop\",\n \"clippath\", \"mask\", \"pattern\", \"marker\",\n \"filter\", \"feblend\", \"fecolormatrix\", \"fecomponenttransfer\", \"fecomposite\",\n \"feconvolvematrix\", \"fediffuselighting\", \"fedisplacementmap\",\n \"fedistantlight\", \"fedropshadow\", \"feflood\", \"fefunca\", \"fefuncb\",\n \"fefuncg\", \"fefuncr\", \"fegaussianblur\", \"femerge\", \"femergenode\",\n \"femorphology\", \"feoffset\", \"fepointlight\", \"fespecularlighting\",\n \"fespotlight\", \"fetile\", \"feturbulence\",\n]);\n\n/** Geometry, paint, text, gradient, and filter attributes (lowercased). */\nconst ALLOWED_ATTRIBUTES = new Set([\n \"xmlns\", \"xmlns:xlink\", \"xml:space\", \"version\", \"baseprofile\",\n \"id\", \"class\", \"viewbox\", \"preserveaspectratio\", \"width\", \"height\",\n \"x\", \"y\", \"x1\", \"y1\", \"x2\", \"y2\", \"cx\", \"cy\", \"r\", \"rx\", \"ry\",\n \"d\", \"points\", \"pathlength\", \"transform\", \"transform-origin\",\n \"fill\", \"fill-opacity\", \"fill-rule\", \"stroke\", \"stroke-width\",\n \"stroke-opacity\", \"stroke-linecap\", \"stroke-linejoin\",\n \"stroke-miterlimit\", \"stroke-dasharray\", \"stroke-dashoffset\",\n \"opacity\", \"color\", \"display\", \"visibility\", \"overflow\",\n \"clip-path\", \"clip-rule\", \"clippathunits\", \"mask\", \"maskunits\",\n \"maskcontentunits\", \"filter\", \"filterunits\", \"primitiveunits\",\n \"marker-start\", \"marker-mid\", \"marker-end\", \"markerwidth\", \"markerheight\",\n \"markerunits\", \"refx\", \"refy\", \"orient\",\n \"patternunits\", \"patterncontentunits\", \"patterntransform\",\n \"gradientunits\", \"gradienttransform\", \"spreadmethod\", \"fx\", \"fy\", \"fr\",\n \"offset\", \"stop-color\", \"stop-opacity\",\n \"font-family\", \"font-size\", \"font-weight\", \"font-style\", \"font-variant\",\n \"letter-spacing\", \"word-spacing\", \"text-anchor\", \"dominant-baseline\",\n \"alignment-baseline\", \"baseline-shift\", \"text-decoration\", \"dx\", \"dy\",\n \"rotate\", \"textlength\", \"lengthadjust\", \"startoffset\", \"writing-mode\",\n \"direction\", \"unicode-bidi\", \"shape-rendering\", \"text-rendering\",\n \"image-rendering\", \"color-interpolation\", \"color-interpolation-filters\",\n \"vector-effect\", \"paint-order\", \"mix-blend-mode\", \"isolation\",\n \"in\", \"in2\", \"result\", \"stddeviation\", \"mode\", \"type\", \"values\",\n \"tablevalues\", \"slope\", \"intercept\", \"amplitude\", \"exponent\",\n \"operator\", \"k1\", \"k2\", \"k3\", \"k4\", \"radius\", \"scale\",\n \"xchannelselector\", \"ychannelselector\", \"basefrequency\", \"numoctaves\",\n \"seed\", \"stitchtiles\", \"flood-color\", \"flood-opacity\", \"lighting-color\",\n \"surfacescale\", \"diffuseconstant\", \"specularconstant\",\n \"specularexponent\", \"kernelmatrix\", \"kernelunitlength\", \"order\",\n \"divisor\", \"bias\", \"targetx\", \"targety\", \"edgemode\", \"preservealpha\",\n \"azimuth\", \"elevation\", \"z\", \"pointsatx\", \"pointsaty\", \"pointsatz\",\n \"limitingconeangle\", \"aria-hidden\", \"aria-label\", \"role\", \"focusable\",\n // Validated below: only in-document fragment references.\n \"href\", \"xlink:href\",\n]);\n\n/** Paint and reference values may point only at in-document fragments. */\nfunction hasExternalReference(value: string): boolean {\n // CSS escapes can spell url()/schemes without their literal bytes. Drawing\n // attributes do not need escapes, so reject them rather than parse CSS twice.\n if (value.includes(\"\\\\\")) return true;\n const compact = value.replace(/[\\u0000- ]/gu, \"\").toLowerCase();\n if (/(?:java|vb)script:|data:|expression\\(|@import|<|>/u.test(compact)) {\n return true;\n }\n for (const match of compact.matchAll(/url\\(([^)]*)\\)/gu)) {\n if (!/^[\"']?#[a-z0-9_.:-]+[\"']?$/u.test(match[1])) return true;\n }\n // CSS consumes an unterminated url() through EOF and browsers can still load\n // it. A balanced-match loop alone therefore misses an external paint server.\n return compact.replace(/url\\(([^)]*)\\)/gu, \"\").includes(\"url(\");\n}\n\n/**\n * Only UTF-8 is accepted: a UTF-16/32 byte order mark or a declared\n * non-UTF-8 encoding would make browsers read different markup than the\n * policy parsed.\n */\nfunction utf8SvgText(content: Buffer): string | null {\n const hasUtf8Bom =\n content[0] === 0xef && content[1] === 0xbb && content[2] === 0xbf;\n const body = hasUtf8Bom ? content.subarray(3) : content;\n if (\n (body[0] === 0xfe && body[1] === 0xff) ||\n (body[0] === 0xff && body[1] === 0xfe) ||\n (body[0] === 0x00 && body[1] === 0x00) ||\n body.includes(0x00)\n ) {\n return null;\n }\n const text = body.toString(\"utf8\");\n if (text.includes(\"\\uFFFD\")) return null;\n const declaration = /^<\\?xml\\b[^>]*\\?>/iu.exec(text);\n if (declaration) {\n const encoding = /\\bencoding\\s*=\\s*[\"']([^\"']*)[\"']/iu.exec(declaration[0]);\n if (encoding && !/^utf-?8$/iu.test(encoding[1].trim())) return null;\n } else if (/<\\?xml\\b/iu.test(text)) {\n // A declaration anywhere but the very start is malformed XML.\n return null;\n }\n return text;\n}\n\n/** True when an SVG uses only static, self-contained drawing features. */\nexport function isPassiveSvg(content: Uint8Array): boolean {\n const text = utf8SvgText(Buffer.from(content.buffer, content.byteOffset, content.byteLength));\n if (text === null) return false;\n let isPassive = true;\n let depth = 0;\n let sawRoot = false;\n // Start offsets of open elements: an implied close is legitimate only for\n // a self-closing tag (same token); otherwise the markup is malformed.\n const openStarts: Array<number> = [];\n const fail = () => {\n isPassive = false;\n };\n const parser = new Parser(\n {\n onopentag(name, attributes) {\n const element = name.toLowerCase();\n if (depth === 0) {\n if (sawRoot || element !== \"svg\") fail();\n sawRoot = true;\n }\n depth += 1;\n openStarts.push(parser.startIndex);\n // Prefixed names (`s:script`) are rejected outright: the allowlist\n // is defined for the SVG namespace only.\n if (element.includes(\":\") || !ALLOWED_ELEMENTS.has(element)) fail();\n for (const [rawName, value] of Object.entries(attributes)) {\n const attribute = rawName.toLowerCase();\n if (!ALLOWED_ATTRIBUTES.has(attribute)) {\n fail();\n continue;\n }\n if (attribute === \"xmlns\" && value !== SVG_NAMESPACE) fail();\n if (attribute === \"xmlns:xlink\" && value !== XLINK_NAMESPACE) fail();\n if (\n (attribute === \"href\" || attribute === \"xlink:href\") &&\n !/^#[A-Za-z0-9_.:-]+$/u.test(value.trim())\n ) {\n fail();\n }\n if (hasExternalReference(value)) fail();\n }\n },\n onclosetag(_name, isImplied) {\n depth -= 1;\n const start = openStarts.pop();\n if (isImplied && start !== parser.startIndex) fail();\n },\n // DOCTYPE (and with it custom entities) or any other declaration other\n // than the XML prolog.\n onprocessinginstruction(name) {\n if (name.toLowerCase() !== \"?xml\") fail();\n },\n oncdatastart: fail,\n ontext(value) { if (depth === 0 && value.trim()) fail(); },\n onerror: fail,\n },\n { xmlMode: true, decodeEntities: true, recognizeSelfClosing: true },\n );\n try {\n parser.write(text);\n parser.end();\n } catch {\n return false;\n }\n return isPassive && sawRoot && depth === 0;\n}\n","/**\n * Acquisition cache: retain at most 480 settled URLs and the runner's at most\n * 32 concurrent acquisitions. Pending requests are pinned until settlement so\n * a slow shared URL cannot be evicted and fetched again concurrently.\n */\nimport type { MigrationCapturedAsset } from './url-run-types.js'\n\n/** Create a per-run cache; durable asset provenance remains in the journal. */\nexport function createMigrationAssetCache() {\n const settled = new Map<string, MigrationCapturedAsset>()\n const pending = new Map<string, Promise<MigrationCapturedAsset>>()\n const remember = (url: string, asset: MigrationCapturedAsset): MigrationCapturedAsset => {\n settled.delete(url)\n settled.set(url, asset)\n if (settled.size > 480) settled.delete(settled.keys().next().value!)\n return asset\n }\n const get = (url: string): Promise<MigrationCapturedAsset> | undefined => {\n const asset = settled.get(url)\n if (asset) return Promise.resolve(remember(url, asset))\n return pending.get(url)\n }\n return {\n remember,\n get,\n has(url: string) { return settled.has(url) || pending.has(url) },\n get size() { return settled.size + pending.size },\n load(url: string, acquire: () => Promise<MigrationCapturedAsset>): Promise<MigrationCapturedAsset> {\n const existing = get(url)\n if (existing) return existing\n // Deferred invocation also makes synchronous host failures follow the\n // same cleanup contract as rejected asynchronous acquisitions.\n const request = Promise.resolve().then(acquire).then((asset) => {\n pending.delete(url)\n return remember(url, asset)\n }, (error: unknown) => {\n pending.delete(url)\n throw error\n })\n pending.set(url, request)\n return request\n },\n delete(url: string): void { settled.delete(url) },\n }\n}\n","/**\n * Clone JSON journal containers without materializing a complete serialized\n * corpus. Immutable strings (especially shared asset base64) can remain shared,\n * while every mutable container belongs exclusively to the resumed runner.\n */\nimport type { UrlMigrationCheckpoint } from './url-run-types.js'\n\n/** Preserve JSON value semantics for the checkpoint's plain data containers. */\nexport function cloneUrlMigrationCheckpoint(checkpoint: UrlMigrationCheckpoint): UrlMigrationCheckpoint {\n const ancestors = new Set<object>()\n const clone = (value: unknown): unknown => {\n if (value === null || typeof value === 'string' || typeof value === 'boolean') return value\n if (typeof value === 'number') return Number.isFinite(value) ? (value === 0 ? 0 : value) : null\n if (typeof value === 'undefined' || typeof value === 'function' || typeof value === 'symbol') return undefined\n if (typeof value !== 'object') throw new TypeError('Checkpoint must contain JSON-safe values.')\n if (ancestors.has(value)) throw new TypeError('Checkpoint must not contain circular references.')\n if (!Array.isArray(value) && Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) throw new TypeError('Checkpoint must contain plain JSON containers.')\n ancestors.add(value)\n try {\n if (Array.isArray(value)) return Array.from(value, (entry) => clone(entry) ?? null)\n // fromEntries creates own properties even for \"__proto__\"; assignment\n // would instead mutate the clone's prototype for untrusted JSON keys.\n return Object.fromEntries(Object.entries(value).flatMap(([key, entry]) => {\n const copied = clone(entry)\n return copied === undefined ? [] : [[key, copied]]\n }))\n } finally { ancestors.delete(value) }\n }\n return clone(checkpoint) as UrlMigrationCheckpoint\n}\n","/** Static responsive-image projection; source attributes never become expressions. */\nexport interface MigrationImageCandidate {\n url: string\n descriptor: string\n}\n\n/** Resolve an image URL without accepting credentials or active URL schemes. */\nexport function migrationImageUrl(value: string, base: URL): string {\n const url = new URL(value, base)\n if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password) throw new Error('Responsive image URL is not a credential-free HTTP(S) URL.')\n return url.toString()\n}\n\n/**\n * Parse URL tokens before descriptors, preserving commas inside URL tokens.\n * Data URLs are intentionally rejected by migrationImageUrl rather than split.\n */\nexport function migrationImageCandidates(value: string, base: URL, resolve = true): Array<MigrationImageCandidate> {\n if (value.length > 64_000) throw new Error('Responsive image candidate list exceeds its safety bound.')\n const result: Array<MigrationImageCandidate> = []\n let index = 0\n while (index < value.length) {\n while (index < value.length && /[\\t\\n\\f\\r ,]/.test(value[index])) index++\n if (index === value.length) break\n const start = index\n while (index < value.length && !/[\\t\\n\\f\\r ]/.test(value[index])) index++\n let url = value.slice(start, index)\n const hasTerminatingComma = url.endsWith(',')\n if (hasTerminatingComma) url = url.replace(/,+$/, '')\n const descriptorStart = index\n if (!hasTerminatingComma) while (index < value.length && value[index] !== ',') index++\n const descriptor = hasTerminatingComma ? '' : value.slice(descriptorStart, index).trim()\n if (descriptor && !/^(?:[1-9]\\d*w|(?:\\d+(?:\\.\\d+)?|\\.\\d+)x)$/.test(descriptor)) throw new Error('Responsive image has an unsupported descriptor.')\n if (descriptor.endsWith('x') && Number(descriptor.slice(0, -1)) <= 0) throw new Error('Responsive image density must be positive.')\n const resolved = migrationImageUrl(url, base)\n result.push({ url: resolve ? resolved : url, descriptor })\n if (result.length > 128) throw new Error('Responsive image has too many candidates.')\n if (value[index] === ',') index++\n }\n if (!result.length) throw new Error('Responsive image has no candidates.')\n return result\n}\n\n/** Serialize normalized candidates with their authored width/density choices. */\nexport function renderMigrationSrcset(candidates: Array<MigrationImageCandidate>): string {\n return candidates.map((candidate) => `${candidate.url}${candidate.descriptor ? ` ${candidate.descriptor}` : ''}`).join(', ')\n}\n\n/** Encode literal JSX attributes; entity decoding happens after syntax parsing. */\nexport function migrationImageAttribute(value: string): string {\n return value.replace(/&/g, '&').replace(/\"/g, '"').replace(/</g, '<').replace(/>/g, '>').replace(/\\{/g, '{').replace(/\\}/g, '}').replace(/\\r/g, ' ').replace(/\\n/g, ' ')\n}\n","/**\n * Public URL adapter for the shared migration engine. Discovery prefers\n * machine-readable Markdown and sitemaps, remains scoped to the submitted docs\n * path, then falls back to bounded navigation crawling and HTML extraction.\n */\n\nimport { load } from 'cheerio'\nimport remarkMdx from 'remark-mdx'\nimport remarkParse from 'remark-parse'\nimport TurndownService from 'turndown'\nimport { unified } from 'unified'\nimport { parse as parseYaml, stringify as stringifyYaml } from 'yaml'\n\nimport { projectMintlifyIntegrations } from './analytics.js'\nimport { parseMarkdownPage } from './mdx.js'\nimport { addMintlifyDirectoryRedirects, buildNavigationFromPages, projectMintlifyNavigation } from './navigation.js'\nimport { pageIdFromReference } from './path.js'\nimport { migrationImageAttribute, migrationImageCandidates, migrationImageUrl, renderMigrationSrcset } from './url-images.js'\nimport type {\n MigrationBundle,\n MigrationFetcher,\n MigrationFetchResponse,\n MigrationNavigationGroup,\n MigrationPage,\n MigrationPlatform,\n MigrationWarning,\n} from './types.js'\n\nconst DEFAULT_MAX_PAGES = 1_000\nconst DEFAULT_MAX_TOTAL_BYTES = 100_000_000\nconst MAX_DISCOVERED_URLS = 100_000\nconst MAX_LOCAL_RESPONSE_BYTES = 2_000_000\nconst MAX_SITEMAP_DOCUMENTS = 25\nconst PORTABLE_MDX_COMPONENTS = new Set([\n 'Accordion', 'AccordionGroup', 'Badge', 'Callout', 'Card', 'CardGroup',\n 'Check', 'CodeBlock', 'CodeGroup', 'Color', 'Column', 'Columns', 'ContentPanel', 'Danger', 'Error', 'Expandable',\n 'Agent', 'AgentPrompt', 'Banner', 'BannerPreview', 'Embed', 'File', 'Folder', 'Frame', 'GitHub', 'Github', 'Hero', 'Human',\n 'Icon', 'Info', 'InlinePanel', 'InlineRequestExample', 'InlineResponseExample',\n 'Latex', 'LegacyView', 'LiteYouTubeEmbed', 'MDX', 'Mermaid', 'Note', 'Panel', 'ParamField', 'Prompt',\n 'PromptAssistant', 'PromptUser',\n 'RequestExample', 'ResponseExample', 'ResponseField', 'Step', 'Steps',\n 'Tab', 'Tabs', 'Terminal', 'TerminalInput', 'TerminalOutput', 'Tile', 'TileGroup',\n 'Tip', 'Tooltip', 'Tree', 'Update', 'Video', 'View', 'Visibility', 'Warning', 'YouTube',\n 'picture', 'source',\n])\nconst LOCALE_CODES = new Set([\n 'ar', 'cs', 'da', 'de', 'el', 'es', 'fi', 'fr', 'he', 'hi', 'hu', 'id',\n 'it', 'ja', 'ko', 'ms', 'nl', 'no', 'pl', 'pt', 'ro', 'ru', 'sk', 'sv',\n 'th', 'tr', 'uk', 'vi', 'zh', 'pt-br', 'zh-hans', 'zh-hant',\n])\nconst PORTABLE_URL_PROPS = 'href|src|img|primaryHref|secondaryHref'\nconst PORTABLE_URL_PROP_NAMES = new Set(PORTABLE_URL_PROPS.split('|'))\nconst remoteMarkdownParser = unified().use(remarkParse).use(remarkMdx)\n\ninterface RemoteMarkdownAttributeValue {\n type: string\n value: string\n}\n\ninterface RemoteMarkdownAttribute {\n type: string\n name?: string\n value?: string | null | RemoteMarkdownAttributeValue\n}\n\ninterface RemoteMarkdownNode {\n type: string\n name?: string\n alt?: string | null\n attributes?: Array<RemoteMarkdownAttribute>\n identifier?: string\n url?: string\n value?: string\n children?: Array<RemoteMarkdownNode>\n position?: {\n start: { offset?: number }\n end: { offset?: number }\n }\n}\n\ninterface EmbeddedOpenApiFragment {\n method: string\n path: string\n document: Record<string, unknown>\n}\n\nexport interface UrlMigrationOptions {\n sourceUrl: string\n /** Explicit caller selection; omitted callers retain source auto-detection. */\n platform?: MigrationPlatform\n fetcher?: MigrationFetcher\n maxPages?: number\n concurrency?: number\n maxTotalBytes?: number\n}\n\n/** Validate the transport-level shape of a docs URL; hosted callers add SSRF checks. */\nexport function validateMigrationUrl(value: string): URL {\n let url: URL\n try {\n url = new URL(value)\n } catch {\n throw new Error('Enter a complete documentation URL, including https://.')\n }\n if (!['http:', 'https:'].includes(url.protocol)) {\n throw new Error('Documentation imports support only HTTP and HTTPS URLs.')\n }\n if (url.username || url.password) throw new Error('Documentation URLs cannot contain credentials.')\n url.hash = ''\n return url\n}\n\nasync function readResponseBody(response: Response): Promise<string> {\n const contentLength = Number(response.headers.get('content-length') ?? 0)\n if (contentLength > MAX_LOCAL_RESPONSE_BYTES) throw new Error('A documentation response exceeded the 2 MB import limit.')\n if (!response.body) return ''\n const reader = response.body.getReader()\n const chunks: Array<Uint8Array> = []\n let size = 0\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n size += value.byteLength\n if (size > MAX_LOCAL_RESPONSE_BYTES) {\n await reader.cancel()\n throw new Error('A documentation response exceeded the 2 MB import limit.')\n }\n chunks.push(value)\n }\n const body = new Uint8Array(size)\n let offset = 0\n for (const chunk of chunks) {\n body.set(chunk, offset)\n offset += chunk.byteLength\n }\n return new TextDecoder().decode(body)\n}\n\n/** Fetch implementation for local CLI usage. Cloud injects its pinned network boundary. */\nexport const defaultMigrationFetcher: MigrationFetcher = async (url, request) => {\n const response = await fetch(url, {\n headers: {\n Accept: request.accept,\n 'User-Agent': 'Thally-Migrate/1.0 (+https://thally.io)',\n },\n redirect: 'follow',\n signal: AbortSignal.timeout(15_000),\n })\n if (!response.ok) throw new Error(`Documentation server returned ${response.status}.`)\n return {\n finalUrl: new URL(response.url),\n body: await readResponseBody(response),\n contentType: response.headers.get('content-type') ?? '',\n headers: Object.fromEntries(response.headers.entries()),\n }\n}\n\nfunction docsScopePath(source: URL): string {\n const path = source.pathname.replace(/\\/+$/, '') || '/'\n if (path === '/') return '/'\n const segments = path.split('/').filter(Boolean)\n return `/${segments.length > 1 ? segments[0] : segments.join('/')}`\n}\n\nfunction machineIndexScopePath(\n source: URL,\n documents: Array<MigrationFetchResponse | null>,\n): string | null {\n const candidates = new Set<string>()\n for (const document of documents) {\n if (!document) continue\n const references = [...headerLinks(document)]\n if (/html/i.test(document.contentType)) {\n const $ = load(document.body)\n references.push(...$('link[href], a[href]')\n .map((_index, element) => $(element).attr('href') ?? '')\n .get()\n .filter((href) => /\\/(?:llms(?:-full)?\\.txt|sitemap\\.xml)(?:[?#]|$)/i.test(href)))\n }\n for (const reference of references) {\n try {\n const indexUrl = new URL(reference, document.finalUrl)\n if (indexUrl.origin !== source.origin) continue\n const scope = indexUrl.pathname.replace(/\\/(?:llms(?:-full)?\\.txt|sitemap\\.xml)$/i, '') || '/'\n if (isInScope(source, source, scope)) candidates.add(scope)\n } catch {\n // Malformed discovery hints are ignored just like malformed page links.\n }\n }\n }\n return [...candidates].sort((left, right) => right.length - left.length)[0] ?? null\n}\n\nfunction isInScope(url: URL, source: URL, scopePath: string): boolean {\n if (url.origin !== source.origin || !['http:', 'https:'].includes(url.protocol)) return false\n const path = url.pathname.replace(/\\/+$/, '') || '/'\n if (scopePath === '/') return true\n return path === scopePath || path === `${scopePath}.md` || path.startsWith(`${scopePath}/`)\n}\n\nfunction normalizeCandidate(value: string, base: URL, source: URL, scopePath: string): URL | null {\n try {\n const url = new URL(value, base)\n url.hash = ''\n if (!isInScope(url, source, scopePath)) return null\n if (url.pathname.startsWith('/cdn-cgi/')) return null\n if (/\\/(?:llms(?:-full)?|robots)\\.txt$/i.test(url.pathname)\n || /\\/sitemap\\.xml$/i.test(url.pathname)) return null\n if (/\\.(?:avif|bmp|gif|ico|jpe?g|mp[34]|pdf|png|svg|ya?ml|webm|webp|zip)$/i.test(url.pathname)\n || /\\/(?:openapi|swagger|asyncapi)[^/]*\\.json$/i.test(url.pathname)) return null\n return url\n } catch {\n return null\n }\n}\n\nfunction candidateIdentity(url: URL): string {\n const pathname = url.pathname.replace(/\\.mdx?$/i, '').replace(/\\/+$/, '') || '/'\n return `${url.origin}${pathname}${url.search}`\n}\n\nfunction pageIdForUrl(url: URL, scopePath: string): string | null {\n let path = url.pathname.replace(/\\/+$/, '')\n if (scopePath !== '/') {\n if (path === `${scopePath}.md`) path = ''\n else if (path === scopePath) path = ''\n else if (path.startsWith(`${scopePath}/`)) path = path.slice(scopePath.length + 1)\n }\n return pageIdFromReference(path.replace(/^\\/+/, '') || 'introduction', true)\n}\n\nfunction canonicalLocalizedPageId(id: string): string {\n const [segment, ...rest] = id.split('/')\n if (LOCALE_CODES.has(segment)) return rest.length > 0 ? id : `${segment}/introduction`\n for (const locale of LOCALE_CODES) {\n if (segment !== `${locale}-api-reference`) continue\n return [locale, 'api-reference', ...rest].join('/')\n }\n return id\n}\n\nfunction detectUrlPlatform(document: MigrationFetchResponse): MigrationPlatform {\n const headers = Object.entries(document.headers ?? {})\n .map(([key, value]) => `${key}:${value ?? ''}`)\n .join(' ')\n const value = `${document.body.slice(0, 200_000)} ${headers}`.toLowerCase()\n if (value.includes('__mintlify') || value.includes('/mintlify-assets/')\n || value.includes('x-mintlify-') || value.includes('/_mintlify/')) return 'mintlify'\n if (value.includes('docusaurus') || value.includes('__docusaurus')) return 'docusaurus'\n if (value.includes('gitbook')) return 'gitbook'\n if (value.includes('nextra')) return 'nextra'\n if (value.includes('vitepress')) return 'vitepress'\n if (value.includes('starlight')) return 'starlight'\n return 'generic'\n}\n\nfunction jsonObjectAfterKey(value: string, key: string): Record<string, unknown> | null {\n let cursor = 0\n const marker = `\"${key}\":`\n while (cursor < value.length) {\n const markerIndex = value.indexOf(marker, cursor)\n if (markerIndex < 0) return null\n let start = markerIndex + marker.length\n while (/\\s/.test(value[start] ?? '')) start += 1\n if (value[start] !== '{') {\n cursor = start + 1\n continue\n }\n let depth = 0\n let isQuoted = false\n let isEscaped = false\n for (let index = start; index < value.length; index += 1) {\n const character = value[index]\n if (isQuoted) {\n if (isEscaped) isEscaped = false\n else if (character === '\\\\') isEscaped = true\n else if (character === '\"') isQuoted = false\n continue\n }\n if (character === '\"') {\n isQuoted = true\n continue\n }\n if (character === '{') depth += 1\n if (character === '}') {\n depth -= 1\n if (depth !== 0) continue\n try {\n const parsed = JSON.parse(value.slice(start, index + 1)) as unknown\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed)\n ? parsed as Record<string, unknown>\n : null\n } catch {\n break\n }\n }\n }\n cursor = start + 1\n }\n return null\n}\n\n/**\n * Current Mintlify themes embed the resolved docs config and scoped navigation\n * in React Flight. Reading that data is substantially more faithful than\n * inferring information architecture from whichever responsive nav is visible.\n */\nfunction embeddedMintlifyConfig(body: string): Record<string, unknown> | null {\n const $ = load(body)\n const flightPayloads: Array<string> = []\n $('script').each((_index, element) => {\n const script = $(element).text()\n for (const match of script.matchAll(/self\\.__next_f\\.push\\(\\[1,(\"(?:\\\\.|[^\"\\\\])*\")\\]\\)/g)) {\n try {\n const payload = JSON.parse(match[1])\n if (typeof payload === 'string') flightPayloads.push(payload)\n } catch {\n // Older or customized shells fall back to bounded DOM inference.\n }\n }\n })\n if (flightPayloads.length === 0) return null\n const payload = flightPayloads.join('\\n')\n const docsConfig = jsonObjectAfterKey(payload, 'docsConfig')\n const scopedNavigation = jsonObjectAfterKey(payload, 'scopedNav')\n if (!docsConfig && !scopedNavigation) return null\n return {\n ...(docsConfig ?? {}),\n ...(scopedNavigation ? { navigation: scopedNavigation } : {}),\n }\n}\n\nfunction plainDescription(value: string): string {\n return value\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, '$1')\n .replace(/[`*_>#-]/g, '')\n .replace(/\\s+/g, ' ')\n .trim()\n .slice(0, 240)\n}\n\nfunction withoutFencedCode(body: string): string {\n let fenceCharacter = ''\n let fenceLength = 0\n return body.split('\\n').map((line) => {\n const match = line.match(/^\\s{0,3}(`{3,}|~{3,})(.*)$/)\n if (match && !fenceCharacter) {\n fenceCharacter = match[1][0]\n fenceLength = match[1].length\n return ''\n }\n if (match && match[1][0] === fenceCharacter && match[1].length >= fenceLength\n && match[2].trim() === '') {\n fenceCharacter = ''\n fenceLength = 0\n return ''\n }\n return fenceCharacter ? '' : line\n }).join('\\n')\n}\n\nfunction eventHandlerPropEnd(value: string, start: number): number | null {\n const opening = value[start]\n if (opening === '\"' || opening === \"'\") {\n for (let index = start + 1; index < value.length; index += 1) {\n if (value[index] === opening && value[index - 1] !== '\\\\') return index + 1\n }\n return null\n }\n if (opening !== '{') {\n const match = value.slice(start).match(/^[^\\s>]+/)\n return match ? start + match[0].length : null\n }\n let depth = 0\n let quote = ''\n for (let index = start; index < value.length; index += 1) {\n const character = value[index]\n if (quote) {\n if (character === quote && value[index - 1] !== '\\\\') quote = ''\n continue\n }\n if (character === '\"' || character === \"'\" || character === '`') {\n quote = character\n continue\n }\n if (character === '{') depth += 1\n if (character === '}') {\n depth -= 1\n if (depth === 0) return index + 1\n }\n }\n return null\n}\n\nfunction stripEventHandlerProps(line: string): string {\n const matcher = /\\s+on[A-Z][A-Za-z0-9]*\\s*=\\s*/g\n let cursor = 0\n let result = ''\n for (const match of line.matchAll(matcher)) {\n const start = match.index\n const valueStart = start + match[0].length\n const end = eventHandlerPropEnd(line, valueStart)\n if (end === null) continue\n result += line.slice(cursor, start)\n cursor = end\n }\n return cursor === 0 ? line : `${result}${line.slice(cursor)}`\n}\n\nfunction isSafePortableUrl(value: string): boolean {\n try {\n const parsed = new URL(value, 'https://relative.thally.invalid')\n return ['http:', 'https:', 'mailto:', 'tel:'].includes(parsed.protocol)\n } catch {\n return false\n }\n}\n\nfunction decodeHtmlAttribute(value: string): string {\n const document = load(`<i data-thally-value=\"${value.replace(/\"/g, '"')}\"></i>`)\n return document('i').attr('data-thally-value') ?? ''\n}\n\n/** Escape imported copy for Markdown text and MDX's additional delimiters. */\nfunction escapeMarkdownText(value: string, escapeTableSeparator = false): string {\n let escaped = ''\n for (const character of value) {\n if (character === '\\\\') escaped += '\\\\\\\\'\n else if (character === '[' || character === ']') escaped += `\\\\${character}`\n else if (character === '<') escaped += '<'\n else if (character === '>') escaped += '>'\n else if (character === '{') escaped += '{'\n else if (character === '}') escaped += '}'\n else if (character === '|' && escapeTableSeparator) escaped += '\\\\|'\n else escaped += character\n }\n return escaped\n}\n\nfunction markdownLinkDestination(value: string): string | null {\n try {\n return encodeURI(value)\n .replace(/\\(/g, '%28')\n .replace(/\\)/g, '%29')\n .replace(/&/g, '&')\n } catch {\n return null\n }\n}\n\nfunction htmlOpeningTagEnd(value: string, start: number): number | null {\n let quote = ''\n for (let index = start; index < value.length; index += 1) {\n const character = value[index]\n if (quote) {\n if (character === quote) quote = ''\n continue\n }\n if (character === '\"' || character === \"'\") {\n quote = character\n continue\n }\n if (character === '>') return index + 1\n }\n return null\n}\n\nfunction replacePortableHtmlImages(value: string): string {\n const matcher = /<img\\b/gi\n let cursor = 0\n let output = ''\n for (const match of value.matchAll(matcher)) {\n const start = match.index\n if (start < cursor) continue\n const end = htmlOpeningTagEnd(value, start + match[0].length)\n if (end === null) continue\n const fragment = load(value.slice(start, end))\n const image = fragment('img').first()\n const source = image.attr('src')\n const destination = source && isSafePortableUrl(source) ? markdownLinkDestination(source) : null\n const alternative = escapeMarkdownText(image.attr('alt') ?? '')\n output += value.slice(cursor, start)\n output += destination ? `` : alternative\n cursor = end\n }\n return cursor === 0 ? value : `${output}${value.slice(cursor)}`\n}\n\nfunction replacePortableHtmlAnchors(value: string): string {\n const matcher = /<a\\b/gi\n let cursor = 0\n let output = ''\n for (const match of value.matchAll(matcher)) {\n const start = match.index\n if (start < cursor) continue\n const openingEnd = htmlOpeningTagEnd(value, start + match[0].length)\n if (openingEnd === null) continue\n const closingMatch = /<\\/a\\s*>/gi\n closingMatch.lastIndex = openingEnd\n const closing = closingMatch.exec(value)\n if (!closing) continue\n const end = closing.index + closing[0].length\n const fragment = load(value.slice(start, end))\n const anchor = fragment('a').first()\n const href = anchor.attr('href')\n const label = escapeMarkdownText(anchor.text().replace(/\\s+/g, ' ').trim())\n const destination = href && isSafePortableUrl(href) ? markdownLinkDestination(href) : null\n output += value.slice(cursor, start)\n output += destination ? `[${label}](${destination})` : label\n cursor = end\n }\n return cursor === 0 ? value : `${output}${value.slice(cursor)}`\n}\n\nfunction markdownNodeText(node: RemoteMarkdownNode): string {\n if (node.type === 'text' || node.type === 'inlineCode') return node.value ?? ''\n if (node.type === 'image' || node.type === 'imageReference') return node.alt ?? ''\n return (node.children ?? []).map(markdownNodeText).join('')\n}\n\n/**\n * Remove unsafe Markdown links using the actual Markdown/MDX parse tree. This\n * catches entity-encoded schemes and balanced destinations that regexes cannot\n * classify reliably, while leaving code fences and ordinary prose untouched.\n */\nfunction sanitizeMarkdownUrls(value: string): string | null {\n let root: RemoteMarkdownNode\n try {\n root = remoteMarkdownParser.parse(value) as RemoteMarkdownNode\n } catch {\n return null\n }\n\n let hasUnsafeMdxSyntax = false\n const validateMdxSyntax = (node: RemoteMarkdownNode) => {\n if (node.type === 'mdxTextExpression'\n || node.type === 'mdxFlowExpression'\n || node.type === 'mdxjsEsm') {\n hasUnsafeMdxSyntax = true\n return\n }\n if (node.type === 'mdxJsxTextElement' || node.type === 'mdxJsxFlowElement') {\n for (const attribute of node.attributes ?? []) {\n if (attribute.type !== 'mdxJsxAttribute' || !attribute.name\n || /^on[A-Z]/.test(attribute.name)) {\n hasUnsafeMdxSyntax = true\n return\n }\n let staticValue: unknown = attribute.value\n if (attribute.value && typeof attribute.value === 'object') {\n if (attribute.value.type !== 'mdxJsxAttributeValueExpression') {\n hasUnsafeMdxSyntax = true\n return\n }\n try {\n staticValue = JSON.parse(attribute.value.value)\n } catch {\n hasUnsafeMdxSyntax = true\n return\n }\n }\n if (PORTABLE_URL_PROP_NAMES.has(attribute.name)\n && (typeof staticValue !== 'string'\n || !isSafePortableUrl(decodeHtmlAttribute(staticValue)))) {\n hasUnsafeMdxSyntax = true\n return\n }\n if (node.name === 'source' || node.name === 'picture') {\n const allowed = node.name === 'picture' ? [] : ['src', 'srcSet', 'srcset', 'sizes', 'media', 'type', 'width', 'height', 'id']\n if (!allowed.includes(attribute.name) || typeof attribute.value !== 'string') {\n hasUnsafeMdxSyntax = true\n return\n }\n try {\n if (/^srcset$/i.test(attribute.name)) migrationImageCandidates(decodeHtmlAttribute(attribute.value), new URL('https://migration.invalid'))\n if (attribute.name === 'src') migrationImageUrl(decodeHtmlAttribute(attribute.value), new URL('https://migration.invalid'))\n } catch { hasUnsafeMdxSyntax = true; return }\n }\n }\n }\n for (const child of node.children ?? []) validateMdxSyntax(child)\n }\n validateMdxSyntax(root)\n if (hasUnsafeMdxSyntax) return null\n\n const unsafeDefinitions = new Set<string>()\n const visitDefinitions = (node: RemoteMarkdownNode) => {\n if (node.type === 'definition' && node.identifier && node.url\n && !isSafePortableUrl(node.url)) unsafeDefinitions.add(node.identifier.toLowerCase())\n for (const child of node.children ?? []) visitDefinitions(child)\n }\n visitDefinitions(root)\n\n const replacements: Array<{ start: number; end: number; value: string }> = []\n const replaceNode = (node: RemoteMarkdownNode, replacement: string): boolean => {\n const start = node.position?.start.offset\n const end = node.position?.end.offset\n if (start === undefined || end === undefined) return false\n replacements.push({ start, end, value: replacement })\n return true\n }\n const visitUrls = (node: RemoteMarkdownNode) => {\n const hasUnsafeDirectUrl = (node.type === 'link' || node.type === 'image')\n && typeof node.url === 'string'\n && !isSafePortableUrl(node.url)\n const hasUnsafeReference = (node.type === 'linkReference' || node.type === 'imageReference')\n && typeof node.identifier === 'string'\n && unsafeDefinitions.has(node.identifier.toLowerCase())\n if (hasUnsafeDirectUrl || hasUnsafeReference) {\n if (replaceNode(node, escapeMarkdownText(markdownNodeText(node)))) return\n }\n if (node.type === 'definition' && node.identifier\n && unsafeDefinitions.has(node.identifier.toLowerCase())) {\n if (replaceNode(node, '')) return\n }\n for (const child of node.children ?? []) visitUrls(child)\n }\n visitUrls(root)\n\n return replacements\n .sort((left, right) => right.start - left.start)\n .reduce((result, replacement) => (\n `${result.slice(0, replacement.start)}${replacement.value}${result.slice(replacement.end)}`\n ), value)\n}\n\nfunction hasUnsafePortableUrlProp(value: string): boolean {\n const quotedProps = new RegExp(`\\\\b(?:${PORTABLE_URL_PROPS})\\\\s*=\\\\s*(['\"])([^'\"\\\\r\\\\n]*)\\\\1`, 'gi')\n for (const match of value.matchAll(quotedProps)) {\n if (!isSafePortableUrl(decodeHtmlAttribute(match[2]))) return true\n }\n const expressionProps = new RegExp(`\\\\b(?:${PORTABLE_URL_PROPS})\\\\s*=\\\\s*\\\\{\\\\s*(\"(?:\\\\\\\\.|[^\"\\\\\\\\])*\")\\\\s*\\\\}`, 'gi')\n for (const match of value.matchAll(expressionProps)) {\n try {\n if (!isSafePortableUrl(decodeHtmlAttribute(JSON.parse(match[1])))) return true\n } catch {\n return true\n }\n }\n return false\n}\n\nfunction portableHtmlToMarkdown(value: string): string {\n return replacePortableHtmlAnchors(replacePortableHtmlImages(value))\n .replace(/<span\\b[^>]*>|<\\/span>/gi, '')\n .replace(/<h([1-6])\\b[^>]*>([\\s\\S]*?)<\\/h\\1>/gi, (_original, level: string, content: string) => {\n return `\\n${'#'.repeat(Number(level))} ${content.trim()}\\n`\n })\n .replace(/<p\\b[^>]*>([\\s\\S]*?)<\\/p>/gi, (_original, content: string) => `\\n${content.trim()}\\n`)\n .replace(/<br\\s*\\/?\\s*>/gi, '\\n')\n .replace(/<\\/?(?:article|div|main|section)\\b[^>]*>/gi, '')\n}\n\nfunction transformMarkdownProse(body: string, transform: (value: string) => string): string {\n const output: Array<string> = []\n let prose: Array<string> = []\n let fenceCharacter = ''\n let fenceLength = 0\n const flushProse = () => {\n if (prose.length === 0) return\n output.push(transform(prose.join('\\n')))\n prose = []\n }\n for (const line of body.split('\\n')) {\n const fence = line.match(/^\\s{0,3}(`{3,}|~{3,})(.*)$/)\n if (fence && !fenceCharacter) {\n flushProse()\n fenceCharacter = fence[1][0]\n fenceLength = fence[1].length\n output.push(line)\n continue\n }\n if (fenceCharacter) {\n output.push(line)\n if (fence && fence[1][0] === fenceCharacter && fence[1].length >= fenceLength\n && fence[2].trim() === '') {\n fenceCharacter = ''\n fenceLength = 0\n }\n continue\n }\n prose.push(line)\n }\n flushProse()\n return output.join('\\n')\n}\n\n/** Normalize inert public Markdown; return null when execution cannot be excluded. */\nexport function sanitizeRemoteMarkdown(body: string): string | null {\n let fenceCharacter = ''\n let fenceLength = 0\n const portableBody = transformMarkdownProse(body, portableHtmlToMarkdown)\n const sanitized = portableBody.split('\\n').map((line) => {\n const fence = line.match(/^\\s{0,3}(`{3,}|~{3,})(.*)$/)\n if (fence && !fenceCharacter) {\n fenceCharacter = fence[1][0]\n fenceLength = fence[1].length\n return line\n }\n if (fence && fence[1][0] === fenceCharacter && fence[1].length >= fenceLength\n && fence[2].trim() === '') {\n fenceCharacter = ''\n fenceLength = 0\n return line\n }\n if (fenceCharacter) return line\n return portableHtmlToMarkdown(stripEventHandlerProps(line\n // Remove obviously unsafe image destinations before MDX parsing. A data\n // URL can itself contain raw `<svg ...>` syntax that makes an otherwise\n // removable Markdown image fail the MDX grammar before the AST exists.\n .replace(/!\\[([^\\]]*)\\]\\(\\s*([^)]+)\\)/g, (original, alternative: string, destination: string) => (\n isSafePortableUrl(decodeHtmlAttribute(destination.replace(/^<|>$/g, '')))\n ? original\n : escapeMarkdownText(alternative)\n ))))\n }).join('\\n')\n const urlSafeMarkdown = sanitizeMarkdownUrls(sanitized)\n if (urlSafeMarkdown === null) return null\n const executable = withoutFencedCode(urlSafeMarkdown)\n .replace(/\\{\\/\\*[\\s\\S]*?\\*\\/\\}/g, '')\n .replace(/<(?:https?:\\/\\/|mailto:)[^>]+>/gi, '')\n .replace(/(`+)[\\s\\S]*?\\1/g, '')\n if (/^\\s*(?:import|export)\\s/m.test(executable)) return null\n // A public Markdown endpoint is still attacker-controlled MDX. Preserve\n // known documentation primitives and JSON-literal props, but never compile\n // executable expressions, raw HTML, event handlers, or local components.\n let hasUnsafeExpression = false\n const withoutStaticProps = executable.replace(/=\\{([^{}\\n]*)\\}/g, (original, value: string) => {\n try {\n JSON.parse(value)\n return '=\"\"'\n } catch {\n hasUnsafeExpression = true\n return original\n }\n }).replace(/\\\\[{}]/g, '')\n if (hasUnsafeExpression || /[{}]/.test(withoutStaticProps)\n || /\\bon[A-Z][A-Za-z]*\\s*=/g.test(withoutStaticProps)\n || hasUnsafePortableUrlProp(executable)) return null\n for (const match of withoutStaticProps.matchAll(/<\\/?([A-Za-z][A-Za-z0-9.]*)(?:\\s|>|\\/)/g)) {\n const component = match[1].split('.', 1)[0]\n if (!PORTABLE_MDX_COMPONENTS.has(component)) return null\n }\n return urlSafeMarkdown\n}\n\nfunction migratedHref(\n href: string,\n currentUrl: URL,\n source: URL,\n scopePath: string,\n importedIds: Set<string>,\n sourceHomePageId?: string,\n): string {\n if (/^(?:javascript|data|vbscript):/i.test(href)) return '#'\n if (href.startsWith('#') || /^(?:mailto|tel):/i.test(href)) return href\n try {\n const target = new URL(href, currentUrl)\n if (target.origin !== source.origin) return href\n if (scopePath === '/' && candidateIdentity(target) === candidateIdentity(source)) {\n return `/${target.search}${target.hash}`\n }\n let path = target.pathname\n if (scopePath !== '/' && (path === scopePath || path.startsWith(`${scopePath}/`))) {\n path = path.slice(scopePath.length)\n }\n const id = pageIdFromReference(path.replace(/^\\/+/, '') || 'introduction')\n const localizedId = id ? canonicalLocalizedPageId(id) : id\n const destinationId = localizedId === sourceHomePageId ? 'introduction' : localizedId\n let importedDestinationId = destinationId && importedIds.has(destinationId)\n ? destinationId\n : undefined\n if (destinationId && !importedDestinationId) {\n importedDestinationId = [`${destinationId}/overview`, `${destinationId}/introduction`]\n .find((candidate) => importedIds.has(candidate))\n ?? [...importedIds].find((candidate) => candidate.startsWith(`${destinationId}/`))\n }\n if (importedDestinationId) {\n const destination = importedDestinationId === 'introduction' ? '/' : `/${importedDestinationId}`\n return `${destination}${target.search}${target.hash}`\n }\n // Same-origin marketing and intentionally missing example links remain\n // valid external URLs instead of becoming broken links in the new site.\n return target.toString()\n } catch {\n return href\n }\n}\n\nfunction rewriteInternalLinks(\n body: string,\n currentUrl: URL,\n source: URL,\n scopePath: string,\n importedIds: Set<string>,\n sourceHomePageId?: string,\n): string {\n const markdown = body.replace(/(?<!!)\\[([^\\]]+)\\]\\(([^)\\s]+)(\\s+['\"][^'\"]*['\"])?\\)/g, (\n original,\n label: string,\n href: string,\n title = '',\n ) => {\n const destination = migratedHref(href, currentUrl, source, scopePath, importedIds, sourceHomePageId)\n return destination === href ? original : `[${label}](${destination}${title})`\n })\n return markdown.replace(/\\bhref=(['\"])([^'\"]+)\\1/g, (_original, quote: string, href: string) => {\n const destination = migratedHref(href, currentUrl, source, scopePath, importedIds, sourceHomePageId)\n return `href=${quote}${destination}${quote}`\n })\n}\n\nfunction remoteMarkdownMetadata(body: string): {\n content: string\n title?: string\n description?: string\n sidebarTitle?: string\n tag?: string\n mode?: string\n hidden?: boolean\n noindex?: boolean\n} {\n if (!/^---\\r?\\n/.test(body)) return { content: body }\n const end = body.slice(4, 65_540).search(/\\r?\\n---(?:\\r?\\n|$)/)\n if (end < 0) return { content: body }\n const frontmatter = body.slice(4, end + 4)\n const contentStart = end + 4 + body.slice(end + 4).match(/^\\r?\\n---(?:\\r?\\n|$)/)![0].length\n function scalar(key: string): string | undefined {\n const value = frontmatter.match(new RegExp(`^${key}:\\\\s*(.+)$`, 'm'))?.[1].trim()\n if (!value) return undefined\n if (/^['\"].*['\"]$/.test(value)) {\n try {\n const parsed = JSON.parse(value.replace(/^'/, '\"').replace(/'$/, '\"'))\n return typeof parsed === 'string' ? parsed : undefined\n } catch {\n return value.slice(1, -1)\n }\n }\n return value.slice(0, 500)\n }\n return {\n content: body.slice(contentStart),\n title: scalar('title'),\n description: scalar('description'),\n sidebarTitle: scalar('sidebarTitle'),\n tag: scalar('tag'),\n mode: scalar('mode'),\n hidden: scalar('hidden') === 'true' ? true : undefined,\n noindex: scalar('noindex') === 'true' ? true : undefined,\n }\n}\n\nfunction stripMintlifyDocumentationIndex(body: string): string {\n const lines = body.split(/\\r?\\n/)\n if (!/^>\\s*##\\s+Documentation Index\\s*$/i.test(lines[0] ?? '')\n || !/^>.*\\/llms(?:-full)?\\.txt/i.test(lines[1] ?? '')\n || !/^>.*discover all available pages/i.test(lines[2] ?? '')) return body\n let index = 3\n while (index < lines.length && /^>/.test(lines[index])) index += 1\n while (index < lines.length && lines[index].trim() === '') index += 1\n return lines.slice(index).join('\\n')\n}\n\nfunction extractEmbeddedOpenApi(content: string): {\n content: string\n openapi?: string\n fragment?: EmbeddedOpenApiFragment\n} {\n const fencePattern = /^\\s{0,3}(`{3,}|~{3,})(?:yaml|yml|json)\\s+(\\S+\\.(?:yaml|yml|json))\\s+(delete|get|head|options|patch|post|put|trace)\\s+(\\/\\S+)\\s*\\r?\\n([\\s\\S]*?)^\\s{0,3}\\1\\s*$/im\n const match = fencePattern.exec(content)\n if (!match) return { content }\n try {\n const parsed = parseYaml(match[5], { maxAliasCount: 50 })\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return { content }\n const document = parsed as Record<string, unknown>\n if (typeof document.openapi !== 'string' && typeof document.swagger !== 'string') return { content }\n const method = match[3].toUpperCase()\n const path = match[4]\n const paths = document.paths\n if (!paths || typeof paths !== 'object' || Array.isArray(paths)) return { content }\n const pathItem = (paths as Record<string, unknown>)[path]\n if (!pathItem || typeof pathItem !== 'object' || Array.isArray(pathItem)\n || !(match[3].toLowerCase() in pathItem)) return { content }\n const before = content.slice(0, match.index).replace(/(?:^|\\n)##\\s+OpenAPI\\s*\\n\\s*$/i, '\\n')\n const after = content.slice(match.index + match[0].length)\n return {\n content: `${before}${after}`.trim(),\n openapi: `${method} ${path}`,\n fragment: {\n method,\n path,\n document,\n },\n }\n } catch {\n return { content }\n }\n}\n\n/** Convert an already sanitized Markdown response through the shared representation. */\nexport function markdownPage(document: MigrationFetchResponse, id: string, platform: MigrationPlatform, warn?: (message: string) => void): {\n page: MigrationPage | null\n openApiFragment?: EmbeddedOpenApiFragment\n} {\n const parsed = remoteMarkdownMetadata(document.body)\n let content = stripMintlifyDocumentationIndex(parsed.content).trimStart()\n const titleMatch = content.match(/^#\\s+(.+)\\r?\\n/)\n const title = parsed.title ?? titleMatch?.[1] ?? id.split('/').at(-1) ?? 'Introduction'\n if (titleMatch) content = content.slice(titleMatch[0].length).trimStart()\n let description = parsed.description\n if (!description) {\n const quoteMatch = content.match(/^(>[^\\r\\n]*(?:\\r?\\n>[^\\r\\n]*)*)\\r?\\n/)\n if (quoteMatch) {\n description = plainDescription(quoteMatch[1].replace(/^>\\s?/gm, ''))\n content = content.slice(quoteMatch[0].length).trimStart()\n }\n }\n // Re-emit only scalar metadata we parsed ourselves. The shared YAML parser\n // handles trusted repository sources but never receives attacker-controlled\n // aliases from a public URL.\n const embeddedOpenApi = extractEmbeddedOpenApi(content)\n const raw = [\n '---',\n `title: ${JSON.stringify(title)}`,\n ...(description ? [`description: ${JSON.stringify(description)}`] : []),\n ...(parsed.sidebarTitle ? [`sidebarTitle: ${JSON.stringify(parsed.sidebarTitle)}`] : []),\n ...(parsed.tag ? [`tag: ${JSON.stringify(parsed.tag)}`] : []),\n ...(parsed.mode ? [`mode: ${JSON.stringify(parsed.mode)}`] : []),\n ...(parsed.hidden ? ['hidden: true'] : []),\n ...(parsed.noindex ? ['noindex: true'] : []),\n ...(embeddedOpenApi.openapi ? [`openapi: ${JSON.stringify(embeddedOpenApi.openapi)}`] : []),\n '---',\n '',\n embeddedOpenApi.content,\n ].join('\\n')\n return {\n // The crawler knows the source platform (detected or caller-provided)\n // before it ever parses a page; an omitted platform would otherwise\n // apply every platform's textual renames to content of unknown origin.\n page: parseMarkdownPage({ id, raw, source: document.finalUrl.toString(), platform, ...(warn ? { warn } : {}) }),\n openApiFragment: embeddedOpenApi.fragment,\n }\n}\n\nfunction mergeRecord(target: Record<string, unknown>, source: unknown): void {\n if (!source || typeof source !== 'object' || Array.isArray(source)) return\n for (const [key, value] of Object.entries(source)) {\n if (['__proto__', 'constructor', 'prototype'].includes(key)) continue\n if (!Object.prototype.hasOwnProperty.call(target, key)) {\n target[key] = value\n continue\n }\n const current = target[key]\n if (current && value && typeof current === 'object' && typeof value === 'object'\n && !Array.isArray(current) && !Array.isArray(value)) {\n mergeRecord(current as Record<string, unknown>, value)\n }\n }\n}\n\nfunction mergeEmbeddedOpenApi(fragments: Array<EmbeddedOpenApiFragment>): Uint8Array | null {\n const first = fragments[0]\n if (!first) return null\n const combined: Record<string, unknown> = {\n ...first.document,\n paths: {},\n components: {},\n }\n const combinedPaths = combined.paths as Record<string, unknown>\n const combinedComponents = combined.components as Record<string, unknown>\n const tags = new Map<string, unknown>()\n for (const fragment of fragments) {\n const sourcePaths = fragment.document.paths as Record<string, unknown>\n const sourcePathItem = sourcePaths?.[fragment.path]\n if (!sourcePathItem || typeof sourcePathItem !== 'object' || Array.isArray(sourcePathItem)) continue\n const sourceOperation = (sourcePathItem as Record<string, unknown>)[fragment.method.toLowerCase()]\n if (!sourceOperation || typeof sourceOperation !== 'object' || Array.isArray(sourceOperation)) continue\n const operation = { ...(sourceOperation as Record<string, unknown>) }\n if (!operation.servers && Array.isArray(fragment.document.servers)) operation.servers = fragment.document.servers\n if (!operation.security && Array.isArray(fragment.document.security)) operation.security = fragment.document.security\n const existingPath = combinedPaths[fragment.path]\n const pathItem = existingPath && typeof existingPath === 'object' && !Array.isArray(existingPath)\n ? existingPath as Record<string, unknown>\n : {}\n for (const sharedKey of ['parameters', 'servers', 'summary', 'description']) {\n const sharedValue = (sourcePathItem as Record<string, unknown>)[sharedKey]\n if (sharedValue !== undefined && pathItem[sharedKey] === undefined) pathItem[sharedKey] = sharedValue\n }\n pathItem[fragment.method.toLowerCase()] = operation\n combinedPaths[fragment.path] = pathItem\n mergeRecord(combinedComponents, fragment.document.components)\n for (const tag of Array.isArray(fragment.document.tags) ? fragment.document.tags : []) {\n if (!tag || typeof tag !== 'object' || Array.isArray(tag)) continue\n const name = (tag as Record<string, unknown>).name\n if (typeof name === 'string' && !tags.has(name)) tags.set(name, tag)\n }\n }\n if (Object.keys(combinedPaths).length === 0) return null\n if (Object.keys(combinedComponents).length === 0) delete combined.components\n if (tags.size > 0) combined.tags = [...tags.values()]\n return new TextEncoder().encode(stringifyYaml(combined))\n}\n\n/** Project rendered article HTML into inert content using shared conversion rules. */\nexport function htmlPage(\n document: MigrationFetchResponse,\n id: string,\n options: { preserveAnchors?: boolean; preserveResponsiveImages?: boolean; omitDocumentHeading?: boolean } = {},\n): { page: MigrationPage | null; links: Array<string> } {\n const $ = load(document.body)\n const links = $('nav a[href], main a[href], article a[href], [role=\"main\"] a[href]')\n .map((_index, element) => $(element).attr('href') ?? '')\n .get()\n .filter(Boolean)\n const scopedTitle = options.omitDocumentHeading\n ? $('main article, article, main, [role=\"main\"]').first().find('h1').first().text().trim()\n : ''\n const title = scopedTitle || $('main h1, article h1, h1').first().text().trim()\n || $('meta[property=\"og:title\"]').attr('content')?.trim()\n || $('title').text().split('|')[0].trim()\n const description = $('meta[name=\"description\"]').attr('content')?.trim()\n || $('meta[property=\"og:description\"]').attr('content')?.trim()\n const content = $('main article, article, main, [role=\"main\"]').first()\n const root = content.length ? content : $('body')\n let documentHeadingAnchor = ''\n const excludedElements = options.omitDocumentHeading\n ? 'script,style,noscript,nav,footer,aside,form,button,svg,iframe,object,embed,link,meta'\n : 'script,style,noscript,nav,header,footer,aside,form,button,svg,iframe,object,embed,link,meta'\n root.find(excludedElements).remove()\n root.find('.theme-doc-version-badge,.theme-doc-breadcrumbs,.theme-doc-toc-mobile,.table-of-contents,.hash-link').remove()\n if (options.omitDocumentHeading) {\n const heading = root.find('h1').first()\n if (heading.length && heading.text().trim() === title) {\n // Thally renders this title from frontmatter. Retain its source anchor at\n // the same boundary without generating a second article-level heading.\n const anchorId = heading.attr('id')\n if (anchorId) documentHeadingAnchor = `<a id=\"${migrationImageAttribute(anchorId)}\" />\\n\\n`\n heading.remove()\n }\n }\n root.find('a[href]').each((_index, element) => {\n const href = $(element).attr('href')\n if (!href) return\n try {\n const resolved = new URL(href, document.finalUrl)\n if (['http:', 'https:', 'mailto:', 'tel:'].includes(resolved.protocol)) {\n $(element).attr('href', resolved.toString())\n } else {\n $(element).removeAttr('href')\n }\n } catch {\n $(element).removeAttr('href')\n }\n })\n root.find('img[src]').each((_index, element) => {\n const src = $(element).attr('src')\n if (!src) return\n try {\n const resolved = new URL(src, document.finalUrl)\n if (['http:', 'https:'].includes(resolved.protocol)) {\n $(element).attr('src', resolved.toString())\n } else {\n $(element).removeAttr('src')\n }\n } catch {\n $(element).removeAttr('src')\n }\n })\n const turndown = new TurndownService({ codeBlockStyle: 'fenced', emDelimiter: '_', headingStyle: 'atx' })\n turndown.remove(['script', 'style', 'noscript'])\n const anchor = (element: HTMLElement): string => {\n const value = element.getAttribute('id')\n return options.preserveAnchors && value\n ? `<a id=\"${value.replace(/&/g, '&').replace(/\"/g, '"').replace(/</g, '<').replace(/\\{/g, '{')}\" />\\n\\n`\n : ''\n }\n if (options.preserveAnchors) turndown.addRule('migration-source-anchor', {\n filter: (node) => (node as HTMLElement).hasAttribute('id') && node.nodeName !== 'PRE',\n replacement: (content, node) => {\n const element = node as HTMLElement\n const heading = element.nodeName.match(/^H([1-6])$/)\n return `\\n\\n${anchor(element)}${heading ? `${'#'.repeat(Number(heading[1]))} ` : ''}${content}\\n\\n`\n },\n })\n if (options.preserveResponsiveImages) {\n const image = (element: HTMLElement): string => {\n const tag = element.nodeName.toLowerCase()\n const attributes: Array<string> = []\n for (const name of ['src', 'srcset', 'sizes', 'alt', 'title', 'media', 'type', 'width', 'height', 'id']) {\n let value = element.getAttribute(name)\n if (value === null) continue\n if (name === 'src') value = migrationImageUrl(value, document.finalUrl)\n if (name === 'srcset') value = renderMigrationSrcset(migrationImageCandidates(value, document.finalUrl))\n const prop = name === 'srcset' ? 'srcSet' : name\n attributes.push(`${prop}=\"${migrationImageAttribute(value)}\"`)\n }\n return `<${tag}${attributes.length ? ` ${attributes.join(' ')}` : ''} />`\n }\n turndown.addRule('migration-responsive-image', {\n filter: (node) => ['IMG', 'SOURCE'].includes(node.nodeName),\n replacement: (_content, node) => image(node as HTMLElement),\n })\n turndown.addRule('migration-responsive-picture', {\n filter: 'picture',\n replacement: (content) => `\\n\\n<picture>\\n${content.trim()}\\n</picture>\\n\\n`,\n })\n }\n turndown.addRule('docusaurus-admonition', {\n filter: (node) => node.nodeName === 'DIV'\n && (node as HTMLElement).classList.contains('theme-admonition'),\n replacement: (_content, node) => {\n const element = node as HTMLElement\n const className = element.className\n const tag = /admonition-(?:danger)/.test(className)\n ? 'Error'\n : /admonition-(?:caution|warning)/.test(className)\n ? 'Warning'\n : /admonition-info/.test(className)\n ? 'Info'\n : 'Note'\n const fragment = load(element.outerHTML)\n const clone = fragment('.theme-admonition').first()\n const title = clone.find('[class*=\"admonitionHeading\"],.admonition-heading').first()\n const titleText = title.text().trim()\n title.remove()\n const markdown = turndown.turndown(clone.html() ?? '').trim()\n return `\\n\\n${anchor(element)}<${tag}>\\n${titleText ? `**${escapeMarkdownText(titleText)}**\\n\\n` : ''}${markdown}\\n</${tag}>\\n\\n`\n },\n })\n turndown.addRule('docusaurus-tabs', {\n filter: (node) => node.nodeName === 'DIV'\n && (node as HTMLElement).classList.contains('tabs-container'),\n replacement: (_content, node) => {\n const element = node as HTMLElement\n const fragment = load(element.outerHTML)\n const container = fragment('.tabs-container').first()\n const labels = container.children('ul.tabs').children('li').toArray()\n .map((item) => fragment(item).text().trim() || 'Tab')\n const panels = container.find('[role=\"tabpanel\"]').toArray()\n .filter((panel) => fragment(panel).closest('.tabs-container')[0] === container[0])\n if (panels.length === 0) return _content\n const tabs = panels.map((panel, index) => {\n const panelId = fragment(panel).attr('id')\n const tab = panelId ? container.find('[role=\"tab\"]').toArray().find((item) => fragment(item).attr('aria-controls') === panelId) : undefined\n const title = migrationImageAttribute((tab ? fragment(tab).text().trim() : labels[index]) || `Tab ${index + 1}`)\n const panelAnchor = options.preserveAnchors && panelId ? `<a id=\"${migrationImageAttribute(panelId)}\" />\\n\\n` : ''\n return `<Tab title=\"${title}\">\\n${panelAnchor}${turndown.turndown(fragment(panel).html() ?? '').trim()}\\n</Tab>`\n })\n return `\\n\\n${anchor(element)}<Tabs>\\n${tabs.join('\\n\\n')}\\n</Tabs>\\n\\n`\n },\n })\n turndown.addRule('gfm-table', {\n filter: 'table',\n replacement: (_content, node) => {\n const fragment = load((node as HTMLElement).outerHTML)\n const rows = fragment('tr').toArray().map((row) => (\n fragment(row).children('th,td').toArray().map((cell) => escapeMarkdownText(\n fragment(cell).text().replace(/\\s+/g, ' ').trim(),\n true,\n ))\n )).filter((row) => row.length > 0)\n if (rows.length === 0) return ''\n const width = Math.max(...rows.map((row) => row.length))\n const normalized = rows.map((row) => [...row, ...Array<string>(width - row.length).fill('')])\n return `\\n\\n| ${normalized[0].join(' | ')} |\\n| ${Array<string>(width).fill('---').join(' | ')} |\\n${normalized.slice(1).map((row) => `| ${row.join(' | ')} |`).join('\\n')}\\n\\n`\n },\n })\n turndown.addRule('migration-safe-fenced-code', {\n filter: (node) => node.nodeName === 'PRE',\n replacement: (_content, node) => {\n const element = node as HTMLElement\n const fragment = load(element.outerHTML)\n const tokenLines = fragment('.token-line').toArray()\n const code = (tokenLines.length > 0\n ? tokenLines.map((line) => fragment(line).text()).join('\\n')\n : fragment('pre').text()).replace(/\\n$/, '')\n const language = fragment('code').attr('class')?.match(/(?:language-|lang-)([A-Za-z0-9_+-]+)/)?.[1] ?? ''\n const longestFence = Math.max(0, ...[...code.matchAll(/`+/g)].map((match) => match[0].length))\n const fence = '`'.repeat(Math.max(3, longestFence + 1))\n return `\\n\\n${anchor(element)}${fence}${language}\\n${code}\\n${fence}\\n\\n`\n },\n })\n const escape = turndown.escape.bind(turndown)\n turndown.escape = (text: string) => escape(text).replace(/[<{}]/g, (match) => `\\\\${match}`)\n const body = `${documentHeadingAnchor}${turndown.turndown(root.html() ?? '').trim()}`\n // Docusaurus can intentionally publish title-only reference/partial pages.\n // Keep those real article routes while still rejecting empty search/app\n // shells that happen to expose a document title.\n if (!title || (body.length < 40 && $('article').length === 0)) return { page: null, links }\n return {\n page: {\n id,\n navigationId: id,\n title,\n description: description || plainDescription(body) || `Documentation imported from ${document.finalUrl.hostname}.`,\n keywords: [],\n body,\n source: document.finalUrl.toString(),\n },\n links,\n }\n}\n\nfunction htmlNavigationLinks(document: MigrationFetchResponse): Array<string> {\n const $ = load(document.body)\n return $('.nav-tabs a[href], nav a[href], aside a[href], [role=\"navigation\"] a[href]')\n .map((_index, element) => $(element).attr('href') ?? '')\n .get()\n .filter(Boolean)\n}\n\ninterface SourceNavigationTab {\n section: string\n label: string\n pageId: string\n}\n\ninterface DocusaurusSidebarItem {\n label: string\n href?: string\n items?: Array<DocusaurusSidebarItem>\n isCategory?: boolean\n}\n\ninterface DocusaurusSidebarSnapshot {\n items: Array<DocusaurusSidebarItem>\n}\n\ninterface DocusaurusDiscovery {\n scopePath: string\n excludedVersionPrefixes: Array<string>\n sidebar: DocusaurusSidebarSnapshot | null\n}\n\nfunction normalizedLinkLabel(value: string): string {\n return value.replace(/\\s+/g, ' ').trim()\n}\n\nfunction readDocusaurusSidebar(document: MigrationFetchResponse): DocusaurusSidebarSnapshot | null {\n if (!/html/i.test(document.contentType)) return null\n const $ = load(document.body)\n const sidebar = $('ul.theme-doc-sidebar-menu').first()\n if (sidebar.length === 0) return null\n\n type CheerioSelector = Exclude<Parameters<ReturnType<typeof load>>[0], undefined>\n function readList(list: CheerioSelector): Array<DocusaurusSidebarItem> {\n return $(list).children('li').toArray().flatMap((item) => {\n const directLink = $(item).children('a.menu__link').first()\n const collapsibleLink = $(item)\n .children('.menu__list-item-collapsible')\n .children('a.menu__link')\n .first()\n const link = directLink.length > 0 ? directLink : collapsibleLink\n const label = normalizedLinkLabel(link.text())\n const href = link.attr('href')\n const nested = $(item).children('ul.menu__list').first()\n const children = nested.length > 0 ? readList(nested.get(0)!) : []\n const isCategory = $(item).hasClass('theme-doc-sidebar-item-category')\n || link.hasClass('menu__link--sublist')\n if (!label && children.length === 0) return []\n return [{\n label: label || 'Documentation',\n ...(href ? { href: new URL(href, document.finalUrl).toString() } : {}),\n ...(children.length > 0 ? { items: children } : {}),\n ...(isCategory ? { isCategory: true } : {}),\n }]\n })\n }\n\n return { items: readList(sidebar.get(0)!) }\n}\n\nfunction docusaurusSidebarHrefs(snapshot: DocusaurusSidebarSnapshot | null): Array<string> {\n const hrefs: Array<string> = []\n const visit = (items: Array<DocusaurusSidebarItem>) => {\n for (const item of items) {\n if (item.href) hrefs.push(item.href)\n if (item.items) visit(item.items)\n }\n }\n if (snapshot) visit(snapshot.items)\n return hrefs\n}\n\nfunction deriveDocusaurusScope(\n source: URL,\n document: MigrationFetchResponse,\n submittedScopePath: string,\n): DocusaurusDiscovery {\n const $ = load(document.body)\n const sidebar = readDocusaurusSidebar(document)\n const internalPaths = docusaurusSidebarHrefs(sidebar).flatMap((href) => {\n try {\n const url = new URL(href)\n return url.origin === source.origin ? [url.pathname.replace(/\\/+$/, '') || '/'] : []\n } catch {\n return []\n }\n })\n const counts = new Map<string, number>()\n for (const path of internalPaths) {\n const segments = path.split('/').filter(Boolean)\n for (let length = 1; length < segments.length; length++) {\n const prefix = `/${segments.slice(0, length).join('/')}`\n counts.set(prefix, (counts.get(prefix) ?? 0) + 1)\n }\n }\n const threshold = Math.max(2, Math.ceil(internalPaths.length * 0.6))\n const scopePath = [...counts]\n .filter(([, count]) => count >= threshold)\n .sort((left, right) => right[0].split('/').length - left[0].split('/').length\n || right[1] - left[1])[0]?.[0]\n ?? submittedScopePath\n\n const excludedVersionPrefixes = $('.dropdown__menu a[href]').toArray().flatMap((element) => {\n const href = $(element).attr('href')\n if (!href) return []\n try {\n const target = new URL(href, document.finalUrl)\n if (target.origin !== source.origin || !isInScope(target, source, scopePath)) return []\n const relativePath = target.pathname.slice(scopePath.length).replace(/^\\/+/, '')\n const version = relativePath.split('/', 1)[0]\n if (!/^(?:legacy|next|v\\d[\\w.-]*)$/i.test(version)) return []\n return [`${scopePath}/${version}`]\n } catch {\n return []\n }\n })\n\n return {\n scopePath,\n excludedVersionPrefixes: [...new Set(excludedVersionPrefixes)],\n sidebar,\n }\n}\n\nfunction docusaurusRouteIdForUrl(url: URL, scopePath: string): string | null {\n let path = url.pathname.replace(/\\/+$/, '')\n if (scopePath === '/') path = path.replace(/^\\/+/, '')\n else if (path === scopePath) path = ''\n else if (path.startsWith(`${scopePath}/`)) path = path.slice(scopePath.length + 1)\n else return null\n const segments = path.split('/').filter(Boolean).map((segment) => {\n let decoded = segment\n try {\n decoded = decodeURIComponent(segment)\n } catch {\n // Preserve malformed source segments as literal route input.\n }\n return decoded\n .replace(/\\.(?:html?|mdx?)$/i, '')\n .replace(/[^A-Za-z0-9._-]+/g, '-')\n .replace(/(^-|-$)/g, '')\n }).filter(Boolean)\n return segments.join('/') || 'introduction'\n}\n\nfunction storageIdForDocusaurusRoute(routeId: string): string {\n return /(?:^|\\/)(?:index|readme)$/i.test(routeId) ? `${routeId}/index` : routeId\n}\n\nfunction docusaurusNavigationFromSnapshots(\n snapshots: Array<DocusaurusSidebarSnapshot>,\n pages: Array<MigrationPage>,\n scopePath: string,\n): ReturnType<typeof buildNavigationFromPages> {\n const imported = new Set(pages.map((page) => page.navigationId))\n const claimed = new Set<string>()\n const uniqueSnapshots = [...new Map(snapshots.map((snapshot) => [\n JSON.stringify(snapshot.items),\n snapshot,\n ])).values()]\n const mergedSnapshots: Array<DocusaurusSidebarSnapshot> = []\n function mergeItems(\n target: Array<DocusaurusSidebarItem>,\n incoming: Array<DocusaurusSidebarItem>,\n ): void {\n for (const item of incoming) {\n const existing = target.find((candidate) => candidate.label === item.label\n && (candidate.items || item.items))\n ?? target.find((candidate) => candidate.label === item.label && candidate.href === item.href)\n if (!existing) {\n target.push(structuredClone(item))\n continue\n }\n if (!existing.href && item.href) existing.href = item.href\n if (item.items) {\n existing.items ??= []\n mergeItems(existing.items, item.items)\n }\n }\n }\n for (const snapshot of uniqueSnapshots) {\n const incomingLabels = new Set(snapshot.items.map((item) => item.label))\n const cluster = mergedSnapshots.find((candidate) => {\n const candidateLabels = new Set(candidate.items.map((item) => item.label))\n const overlap = [...incomingLabels].filter((label) => candidateLabels.has(label)).length\n return overlap >= Math.max(2, Math.ceil(Math.min(incomingLabels.size, candidateLabels.size) * 0.5))\n })\n if (cluster) mergeItems(cluster.items, snapshot.items)\n else mergedSnapshots.push(structuredClone(snapshot))\n }\n const pageIdsFor = (snapshot: DocusaurusSidebarSnapshot): Array<string> => docusaurusSidebarHrefs(snapshot)\n .flatMap((href) => {\n const id = docusaurusRouteIdForUrl(new URL(href), scopePath)\n return id && imported.has(id) ? [id] : []\n })\n mergedSnapshots.sort((left, right) => pageIdsFor(right).length - pageIdsFor(left).length)\n\n const tabs = mergedSnapshots.flatMap((snapshot, snapshotIndex) => {\n function convert(items: Array<DocusaurusSidebarItem>): Array<string | MigrationNavigationGroup> {\n const converted: Array<string | MigrationNavigationGroup> = []\n for (const item of items) {\n const routeId = item.href\n ? docusaurusRouteIdForUrl(new URL(item.href), scopePath)\n : null\n const page = routeId && imported.has(routeId) && !claimed.has(routeId) ? routeId : null\n if (page) claimed.add(page)\n const children = item.items ? convert(item.items) : []\n if (children.length > 0 || item.isCategory) {\n converted.push({ group: item.label, pages: page ? [page, ...children] : children })\n continue\n }\n if (page) converted.push(page)\n }\n return converted\n }\n const converted = convert(snapshot.items)\n const loosePages = converted.filter((item): item is string => typeof item === 'string')\n const groups = converted.filter((item): item is Exclude<typeof item, string> => typeof item !== 'string')\n if (loosePages.length > 0) groups.unshift({ group: 'Overview', pages: loosePages })\n if (groups.length === 0) return []\n const snapshotPageIds = pageIdsFor(snapshot)\n const firstSegments = new Set(snapshotPageIds.map((id) => id.split('/', 1)[0]))\n const tab = snapshotIndex > 0 && firstSegments.size === 1\n ? normalizedLinkLabel([...firstSegments][0].replace(/[-_]/g, ' ')).replace(/\\b\\w/g, (letter) => letter.toUpperCase())\n : 'Documentation'\n return [{ tab, groups }]\n })\n\n const additional = pages\n .map((page) => page.navigationId)\n .filter((id) => !claimed.has(id))\n if (additional.length > 0) {\n if (tabs.length === 0) return buildNavigationFromPages(pages)\n tabs[0].groups?.push({ group: 'Additional', pages: additional })\n }\n return { tabs }\n}\n\nfunction htmlTopLevelNavigation(\n document: MigrationFetchResponse,\n source: URL,\n scopePath: string,\n): Array<SourceNavigationTab> {\n const $ = load(document.body)\n interface NavigationCandidate {\n entries: Array<SourceNavigationTab>\n anchorCount: number\n }\n const collectCandidates = (selector: string): Array<NavigationCandidate> => {\n const candidates: Array<NavigationCandidate> = []\n $(selector).each((_containerIndex, container) => {\n const entries: Array<SourceNavigationTab> = []\n const seenSections = new Set<string>()\n let anchorCount = 0\n $(container).find('a[href]').each((_index, element) => {\n const label = $(element).text().replace(/\\s+/g, ' ').trim()\n const href = $(element).attr('href')\n if (!label || !href) return\n try {\n const target = new URL(href, document.finalUrl)\n if (!isInScope(target, source, scopePath)) return\n anchorCount += 1\n const targetId = canonicalLocalizedPageId(pageIdForUrl(target, scopePath) ?? '')\n const pageId = targetId\n const section = targetId.split('/', 1)[0] || 'introduction'\n if (!pageId || seenSections.has(section)) return\n seenSections.add(section)\n entries.push({ section, label, pageId })\n } catch {\n // Ignore malformed navigation targets; discovery applies the same rule.\n }\n })\n if (entries.length > 1) {\n candidates.push({ entries, anchorCount })\n }\n })\n return candidates\n }\n // Mintlify marks its product-tab strip consistently. Prefer that explicit\n // platform signal; generic nav elements usually describe only the sidebar.\n const tabCandidates = collectCandidates('.nav-tabs')\n const candidates = tabCandidates.length > 0\n ? tabCandidates\n : collectCandidates('nav, [role=\"navigation\"]')\n const entries = candidates.sort((left, right) => {\n const densityDifference = (right.entries.length / right.anchorCount)\n - (left.entries.length / left.anchorCount)\n return densityDifference || right.entries.length - left.entries.length\n })[0]?.entries ?? []\n // A product switcher with dozens of entries is invariably a sidebar or\n // responsive page strip, not authored top-level navigation. Refuse to turn\n // that ambiguity into an unusable header.\n const normalizedLabels = entries.map((entry) => entry.label.trim().toLowerCase())\n if (entries.length > 12 || new Set(normalizedLabels).size !== normalizedLabels.length) return []\n return entries\n}\n\nfunction remapNavigationHome(\n config: MigrationBundle['docsConfig'],\n sourceHomePageId: string | undefined,\n): MigrationBundle['docsConfig'] {\n if (!sourceHomePageId || sourceHomePageId === 'introduction') return config\n const remapPages = (\n pages: Array<string | MigrationNavigationGroup>,\n ): Array<string | MigrationNavigationGroup> => pages.map((page) => typeof page === 'string'\n ? page === sourceHomePageId ? 'introduction' : page\n : { ...page, pages: remapPages(page.pages) })\n const remapGroups = (groups: Array<MigrationNavigationGroup>): Array<MigrationNavigationGroup> => groups.map((group) => ({\n ...group,\n pages: remapPages(group.pages),\n }))\n return {\n ...config,\n tabs: config.tabs.map((tab) => ({\n ...tab,\n ...(tab.href === `/${sourceHomePageId}` ? { href: '/' } : {}),\n ...(tab.pages ? { pages: remapPages(tab.pages) } : {}),\n ...(tab.groups ? { groups: remapGroups(tab.groups) } : {}),\n })),\n }\n}\n\nfunction markdownLinks(body: string): Array<string> {\n return [...body.matchAll(/\\[[^\\]]+\\]\\(([^)\\s]+)(?:\\s+['\"][^'\"]*['\"])?\\)/g)]\n .map((match) => match[1])\n}\n\nfunction sitemapLinks(body: string): Array<string> {\n const $ = load(body, { xmlMode: true })\n return $('url > loc, sitemap > loc').map((_index, element) => $(element).text().trim()).get().filter(Boolean)\n}\n\nfunction robotsSitemaps(body: string): Array<string> {\n return body.split('\\n')\n .map((line) => line.match(/^\\s*Sitemap:\\s*(\\S+)/i)?.[1])\n .filter((value): value is string => Boolean(value))\n}\n\nfunction headerLinks(document: MigrationFetchResponse): Array<string> {\n const links: Array<string> = []\n const llms = document.headers?.['x-llms-txt']\n if (llms) links.push(llms)\n const linkHeader = document.headers?.link ?? ''\n for (const match of linkHeader.matchAll(/<([^>]+)>\\s*;[^,]*(?:rel=[\"']?(?:alternate|llms-txt)[\"']?|type=[\"']text\\/(?:markdown|plain)[\"'])/gi)) {\n links.push(match[1])\n }\n return links\n}\n\nasync function safeFetch(\n fetcher: MigrationFetcher,\n url: URL,\n accept: string,\n): Promise<MigrationFetchResponse | null> {\n try {\n return await fetcher(url, { accept })\n } catch {\n return null\n }\n}\n\n/** Crawl one public docs origin into a canonical, caller-independent bundle. */\nexport async function migrateUrl(options: UrlMigrationOptions): Promise<MigrationBundle> {\n const source = validateMigrationUrl(options.sourceUrl)\n const fetcher = options.fetcher ?? defaultMigrationFetcher\n const maxPages = Math.max(1, Math.min(options.maxPages ?? DEFAULT_MAX_PAGES, 10_000))\n const concurrency = Math.max(1, Math.min(options.concurrency ?? 5, 10))\n const maxTotalBytes = Math.max(1_000_000, Math.min(options.maxTotalBytes ?? DEFAULT_MAX_TOTAL_BYTES, 500_000_000))\n const submittedScopePath = docsScopePath(source)\n const initial = await fetcher(source, { accept: 'text/markdown,text/html,application/xhtml+xml;q=0.9' })\n if (!isInScope(initial.finalUrl, source, submittedScopePath)) {\n throw new Error('The documentation URL redirected outside the submitted docs origin or path.')\n }\n const htmlProbe = /html/i.test(initial.contentType)\n ? initial\n : await safeFetch(fetcher, source, 'text/html,application/xhtml+xml')\n // Content negotiation must not let an HTML-only redirect escape the path\n // boundary already validated for the submitted documentation response.\n const scopedHtmlProbe = htmlProbe && isInScope(htmlProbe.finalUrl, source, submittedScopePath)\n ? htmlProbe\n : null\n const platform = options.platform ?? detectUrlPlatform(scopedHtmlProbe ?? initial)\n let docusaurusHtmlProbe = platform === 'docusaurus' ? scopedHtmlProbe : null\n if (docusaurusHtmlProbe && !readDocusaurusSidebar(docusaurusHtmlProbe)) {\n const $ = load(docusaurusHtmlProbe.body)\n const docsHref = $('nav a[href]').toArray().map((element) => ({\n href: $(element).attr('href'),\n label: normalizedLinkLabel($(element).text()),\n })).find((link) => /^docs(?:umentation)?$/i.test(link.label))?.href\n if (docsHref) {\n try {\n const docsUrl = new URL(docsHref, docusaurusHtmlProbe.finalUrl)\n if (docsUrl.origin === source.origin && isInScope(docsUrl, source, submittedScopePath)) {\n const probe = await safeFetch(fetcher, docsUrl, 'text/html,application/xhtml+xml')\n if (probe && probe.finalUrl.origin === source.origin && /html/i.test(probe.contentType)) {\n docusaurusHtmlProbe = probe\n }\n }\n } catch {\n // A malformed navbar link falls back to the submitted path boundary.\n }\n }\n }\n const docusaurusDiscovery = platform === 'docusaurus' && docusaurusHtmlProbe\n ? deriveDocusaurusScope(source, docusaurusHtmlProbe, submittedScopePath)\n : null\n // A Mintlify site may own an entire docs origin or a path below a marketing\n // site. Its llms/sitemap location is the authoritative crawl boundary.\n const scopePath = platform === 'mintlify'\n ? machineIndexScopePath(source, [initial, scopedHtmlProbe]) ?? submittedScopePath\n : docusaurusDiscovery?.scopePath ?? submittedScopePath\n const sourceMintlifyConfig = platform === 'mintlify' && scopedHtmlProbe && /html/i.test(scopedHtmlProbe.contentType)\n ? embeddedMintlifyConfig(scopedHtmlProbe.body)\n : null\n const sourceMintlifyProjection = sourceMintlifyConfig\n ? projectMintlifyNavigation(sourceMintlifyConfig, { pathPrefix: scopePath })\n : null\n const hasStructuredMintlifyNavigation = Boolean(\n sourceMintlifyProjection?.docsConfig.tabs.length\n && sourceMintlifyProjection.pageReferences.length,\n )\n const sourceTopLevelNavigation = platform === 'mintlify' && !hasStructuredMintlifyNavigation\n && scopedHtmlProbe && /html/i.test(scopedHtmlProbe.contentType)\n ? htmlTopLevelNavigation(scopedHtmlProbe, source, scopePath)\n : undefined\n // The submitted URL can be any page. Mintlify's first authored product tab,\n // rather than the submitted page, defines which source page becomes `/`.\n // Older single-section themes do not expose a tab strip, so they retain the\n // legacy dedicated-origin fallback when no authored home can be recovered.\n const sourceHomePageId = sourceMintlifyProjection?.pageReferences[0]?.navigationId\n ?? sourceTopLevelNavigation?.[0]?.pageId\n ?? (platform === 'mintlify' && scopePath === '/'\n ? canonicalLocalizedPageId(pageIdForUrl(source, scopePath) ?? '') || undefined\n : undefined)\n const cache = new Map([[source.toString(), initial]])\n if (docusaurusHtmlProbe) cache.set(docusaurusHtmlProbe.finalUrl.toString(), docusaurusHtmlProbe)\n const queue: Array<URL> = [source]\n const queued = new Set([candidateIdentity(source)])\n const warnings: Array<MigrationWarning> = []\n const fetchedSitemaps = new Set<string>()\n const docusaurusSidebars: Array<DocusaurusSidebarSnapshot> = docusaurusDiscovery?.sidebar\n ? [docusaurusDiscovery.sidebar]\n : []\n const docusaurusRedirects: Array<{ source: string; destination: string }> = []\n\n function enqueue(value: string, base: URL): void {\n if (queued.size >= MAX_DISCOVERED_URLS) return\n const url = normalizeCandidate(value, base, source, scopePath)\n if (!url || queued.has(candidateIdentity(url))) return\n if (platform === 'docusaurus' && url.pathname.replace(/\\/+$/, '') === `${scopePath}/search`) return\n if (docusaurusDiscovery?.excludedVersionPrefixes.some((prefix) => {\n const path = url.pathname.replace(/\\/+$/, '')\n return path === prefix || path.startsWith(`${prefix}/`)\n })) return\n queued.add(candidateIdentity(url))\n queue.push(url)\n }\n\n for (const reference of sourceMintlifyProjection?.pageReferences ?? []) {\n let href = reference.ref\n if (scopePath !== '/' && href.startsWith('/')\n && href !== scopePath && !href.startsWith(`${scopePath}/`)) {\n href = `${scopePath}${href}`\n }\n enqueue(href, source)\n }\n\n // Mintlify renders the authored navigation into the HTML shell. Seed the\n // queue from that order before machine indexes add completeness fallbacks.\n const navigationProbe = docusaurusHtmlProbe ?? scopedHtmlProbe\n if (navigationProbe && /html/i.test(navigationProbe.contentType)) {\n for (const link of htmlNavigationLinks(navigationProbe)) enqueue(link, navigationProbe.finalUrl)\n }\n\n async function discoverDocumentLinks(document: MigrationFetchResponse, depth = 0): Promise<void> {\n const links = sitemapLinks(document.body)\n for (const link of links) {\n let url: URL\n try {\n url = new URL(link, document.finalUrl)\n } catch {\n continue\n }\n if (depth < 2 && /\\.xml(?:$|\\?)/i.test(url.pathname)) {\n if (url.origin !== source.origin || fetchedSitemaps.size >= MAX_SITEMAP_DOCUMENTS\n || fetchedSitemaps.has(url.toString())) continue\n fetchedSitemaps.add(url.toString())\n const nested = await safeFetch(fetcher, url, 'application/xml,text/xml')\n if (nested && nested.finalUrl.origin === source.origin) await discoverDocumentLinks(nested, depth + 1)\n } else {\n enqueue(link, document.finalUrl)\n }\n }\n }\n\n for (const link of headerLinks(initial)) {\n let url: URL\n try {\n url = new URL(link, initial.finalUrl)\n } catch {\n continue\n }\n if (url.origin !== source.origin) continue\n const document = await safeFetch(fetcher, url, 'text/plain,text/markdown')\n if (!document || document.finalUrl.origin !== source.origin) continue\n for (const candidate of markdownLinks(document.body)) enqueue(candidate, document.finalUrl)\n }\n\n const pathRoot = scopePath === '/' ? source.origin : `${source.origin}${scopePath}`\n const discoveryUrls = [\n new URL('/llms.txt', source.origin),\n new URL(`${pathRoot.replace(source.origin, '')}/llms.txt`, source.origin),\n new URL('/robots.txt', source.origin),\n new URL('/sitemap.xml', source.origin),\n new URL(`${pathRoot.replace(source.origin, '')}/sitemap.xml`, source.origin),\n ]\n const uniqueDiscovery = [...new Map(discoveryUrls.map((url) => [url.toString(), url])).values()]\n for (const url of uniqueDiscovery) {\n const document = await safeFetch(fetcher, url, 'text/plain,application/xml,text/xml;q=0.9')\n if (!document || document.finalUrl.origin !== source.origin) continue\n const isRobots = url.pathname.endsWith('robots.txt')\n const isSitemap = url.pathname.endsWith('sitemap.xml') || document.contentType.includes('xml')\n if (isRobots) {\n for (const link of robotsSitemaps(document.body)) {\n let sitemapUrl: URL\n try {\n sitemapUrl = new URL(link, document.finalUrl)\n } catch {\n continue\n }\n if (sitemapUrl.origin !== source.origin || fetchedSitemaps.size >= MAX_SITEMAP_DOCUMENTS\n || fetchedSitemaps.has(sitemapUrl.toString())) continue\n fetchedSitemaps.add(sitemapUrl.toString())\n const sitemap = await safeFetch(fetcher, sitemapUrl, 'application/xml,text/xml')\n if (sitemap && sitemap.finalUrl.origin === source.origin) await discoverDocumentLinks(sitemap)\n }\n } else if (isSitemap) {\n await discoverDocumentLinks(document)\n } else {\n for (const link of markdownLinks(document.body)) enqueue(link, document.finalUrl)\n }\n }\n\n const pages: Array<MigrationPage> = []\n const openApiFragments: Array<EmbeddedOpenApiFragment> = []\n const seenIds = new Set<string>()\n const seenSources = new Set<string>()\n const visited = new Set<string>()\n let importedBytes = 0\n let isByteBudgetExhausted = false\n while (queue.length > 0 && pages.length < maxPages && !isByteBudgetExhausted) {\n const batch: Array<URL> = []\n while (queue.length > 0 && batch.length < Math.min(concurrency, maxPages - pages.length)) {\n const candidate = queue.shift()\n if (!candidate || visited.has(candidate.toString())) continue\n visited.add(candidate.toString())\n batch.push(candidate)\n }\n if (batch.length === 0) continue\n const results = await Promise.all(batch.map(async (candidate) => {\n let document = cache.get(candidate.toString())\n ?? await safeFetch(fetcher, candidate, 'text/markdown,text/html,application/xhtml+xml;q=0.9')\n if (!document) {\n return { candidate, page: null, links: [] as Array<string>, failure: true }\n }\n if (!isInScope(document.finalUrl, source, scopePath)) {\n return { candidate, page: null, links: [] as Array<string>, failure: false }\n }\n // A .md mirror can redirect to an index. Classify the final response,\n // rather than trusting the requested extension or content negotiation.\n if (/\\/(?:llms(?:-full)?|robots)\\.txt$|\\/sitemap[^/]*\\.xml$/i.test(document.finalUrl.pathname)\n || /(?:xml|json)/i.test(document.contentType)) {\n return { candidate, page: null, links: markdownLinks(document.body), base: document.finalUrl, failure: false }\n }\n const isMarkdown = /(?:markdown|text\\/plain)/i.test(document.contentType)\n || /\\.mdx?$/i.test(document.finalUrl.pathname)\n if (!isMarkdown && !/\\.mdx?$/i.test(candidate.pathname)) {\n const markdownUrl = new URL(candidate)\n markdownUrl.pathname = `${markdownUrl.pathname.replace(/\\/$/, '')}.md`\n const markdownDocument = await safeFetch(fetcher, markdownUrl, 'text/markdown,text/plain')\n if (markdownDocument && isInScope(markdownDocument.finalUrl, source, scopePath)\n && !/\\/(?:llms(?:-full)?|robots)\\.txt$|\\/sitemap[^/]*\\.xml$/i.test(markdownDocument.finalUrl.pathname)\n && /(?:markdown|text\\/plain)/i.test(markdownDocument.contentType)) {\n document = markdownDocument\n }\n }\n const isResolvedMarkdown = /(?:markdown|text\\/plain)/i.test(document.contentType)\n || /\\.mdx?$/i.test(document.finalUrl.pathname)\n const sanitizedMarkdown = isResolvedMarkdown ? sanitizeRemoteMarkdown(document.body) : null\n if (isResolvedMarkdown && sanitizedMarkdown === null) {\n const htmlUrl = new URL(candidate)\n htmlUrl.pathname = htmlUrl.pathname.replace(/\\.mdx?$/i, '') || '/'\n const htmlDocument = await safeFetch(fetcher, htmlUrl, 'text/html,application/xhtml+xml')\n if (htmlDocument && isInScope(htmlDocument.finalUrl, source, scopePath)\n && /html/i.test(htmlDocument.contentType)) {\n document = htmlDocument\n } else {\n // Never recover by compiling the rejected MDX. A source that does\n // not expose a safe rendered representation is skipped instead.\n return { candidate, page: null, links: [] as Array<string>, failure: false }\n }\n } else if (sanitizedMarkdown !== null) {\n document = { ...document, body: sanitizedMarkdown }\n }\n const responseBytes = Buffer.byteLength(document.body, 'utf8')\n if (importedBytes + responseBytes > maxTotalBytes) {\n isByteBudgetExhausted = true\n return { candidate, page: null, links: [] as Array<string>, failure: false, budgetExceeded: true }\n }\n importedBytes += responseBytes\n const discoveredNavigationId = platform === 'docusaurus'\n ? docusaurusRouteIdForUrl(document.finalUrl, scopePath)\n ?? docusaurusRouteIdForUrl(candidate, scopePath)\n : pageIdForUrl(document.finalUrl, scopePath)\n ?? pageIdForUrl(candidate, scopePath)\n const navigationId = discoveredNavigationId\n && canonicalLocalizedPageId(discoveredNavigationId) === sourceHomePageId\n ? 'introduction'\n : discoveredNavigationId\n if (!navigationId) return { candidate, page: null, links: [] as Array<string>, failure: false }\n const id = platform === 'docusaurus'\n ? storageIdForDocusaurusRoute(navigationId)\n : navigationId\n if (/(?:markdown|text\\/plain)/i.test(document.contentType) || /\\.mdx?$/i.test(document.finalUrl.pathname)) {\n const pageWarnings: Array<string> = []\n const markdown = markdownPage(document, id, platform, (message) => pageWarnings.push(message))\n if (markdown.page) markdown.page.navigationId = navigationId\n return {\n candidate,\n page: markdown.page,\n pageWarnings,\n openApiFragment: markdown.openApiFragment,\n links: markdownLinks(document.body),\n base: document.finalUrl,\n failure: false,\n }\n }\n const extracted = htmlPage(document, id)\n if (extracted.page) extracted.page.navigationId = navigationId\n const sidebar = platform === 'docusaurus' ? readDocusaurusSidebar(document) : null\n return { candidate, page: extracted.page, links: extracted.links, base: document.finalUrl, sidebar, failure: false }\n }))\n for (const result of results) {\n if (result.failure) {\n warnings.push({ code: 'fetch-failed', message: 'A discovered documentation page could not be fetched.', source: result.candidate.toString() })\n continue\n }\n if (result.budgetExceeded) continue\n if (result.page && platform !== 'docusaurus') {\n result.page.id = canonicalLocalizedPageId(result.page.id)\n result.page.navigationId = canonicalLocalizedPageId(result.page.navigationId)\n }\n const sourceIdentity = result.page\n ? candidateIdentity(new URL(result.page.source))\n : null\n if (result.page && sourceIdentity && !seenSources.has(sourceIdentity)\n && !seenIds.has(result.page.id) && pages.length < maxPages) {\n pages.push(result.page)\n if ('pageWarnings' in result && result.pageWarnings) {\n for (const message of result.pageWarnings) warnings.push({ code: 'unsupported-config', message, source: result.page.source })\n }\n if ('openApiFragment' in result && result.openApiFragment) {\n openApiFragments.push(result.openApiFragment)\n }\n seenIds.add(result.page.id)\n seenSources.add(sourceIdentity)\n }\n if (platform === 'docusaurus' && result.page) {\n const candidateRoute = docusaurusRouteIdForUrl(result.candidate, scopePath)\n if (candidateRoute && candidateRoute !== result.page.navigationId) {\n docusaurusRedirects.push({\n source: candidateRoute === 'introduction' ? '/' : `/${candidateRoute}`,\n destination: result.page.navigationId === 'introduction' ? '/' : `/${result.page.navigationId}`,\n })\n }\n }\n if ('sidebar' in result && result.sidebar) docusaurusSidebars.push(result.sidebar)\n for (const link of result.links) enqueue(link, result.base ?? result.candidate)\n }\n }\n\n if (pages.length === 0) throw new Error('No readable documentation pages were found at that URL.')\n for (const page of pages) {\n const [locale, ...navigationSegments] = page.id.split('/')\n if (LOCALE_CODES.has(locale)) {\n if (navigationSegments.length === 0) page.id = `${locale}/introduction`\n page.locale = locale\n page.navigationId = navigationSegments.join('/') || 'introduction'\n }\n }\n const importedIds = new Set(pages.map((page) => page.navigationId))\n for (const page of pages) {\n page.body = rewriteInternalLinks(\n page.body,\n new URL(page.source),\n source,\n scopePath,\n importedIds,\n sourceHomePageId,\n )\n }\n if (isByteBudgetExhausted) {\n warnings.push({ code: 'limit-reached', message: `Import stopped after reaching the ${Math.round(maxTotalBytes / 1_000_000)} MB content budget.` })\n } else if (queue.length > 0 || queued.size >= MAX_DISCOVERED_URLS) {\n warnings.push({ code: 'limit-reached', message: `Import stopped after ${pages.length} pages; narrow the URL or raise the caller limit to import more.` })\n }\n const topLevelNavigation = sourceTopLevelNavigation?.map((entry) => ({\n ...entry,\n pageId: entry.pageId === sourceHomePageId ? 'introduction' : entry.pageId,\n }))\n let docsConfig = platform === 'docusaurus'\n ? docusaurusNavigationFromSnapshots(docusaurusSidebars, pages, scopePath)\n : hasStructuredMintlifyNavigation && sourceMintlifyProjection\n ? remapNavigationHome(sourceMintlifyProjection.docsConfig, sourceHomePageId)\n : buildNavigationFromPages(pages, {\n topLevelTabs: platform === 'mintlify',\n topLevelNavigation,\n })\n if (sourceMintlifyProjection) warnings.push(...sourceMintlifyProjection.warnings)\n if (sourceMintlifyConfig) {\n const analytics = projectMintlifyIntegrations(sourceMintlifyConfig)\n warnings.push(...analytics.warnings)\n if (analytics.integrations) docsConfig.integrations = analytics.integrations\n }\n if (docusaurusRedirects.length > 0) {\n docsConfig.redirects = [...new Map(docusaurusRedirects.map((redirect) => [\n `${redirect.source}:${redirect.destination}`,\n redirect,\n ])).values()]\n }\n const openApiAsset = mergeEmbeddedOpenApi(openApiFragments)\n if (openApiAsset) {\n const operationPageIds = new Set(pages.filter((page) => page.openapi).map((page) => page.navigationId))\n const containsOperation = (\n navigationPages: Array<string | MigrationNavigationGroup> = [],\n ): boolean => navigationPages.some((page) => typeof page === 'string'\n ? operationPageIds.has(page)\n : containsOperation(page.pages))\n const apiTab = docsConfig.tabs.find((tab) => (\n containsOperation(tab.pages) || containsOperation(tab.groups)\n )) ?? docsConfig.tabs[0]\n if (apiTab) {\n // Migrated operation pages preserve the source URLs and sidebar order.\n // The spec is still configured for operation rendering, but its derived\n // `/api/*` navigation would duplicate every imported endpoint.\n apiTab.api = { source: '/openapi.yaml', navigation: false }\n }\n }\n const locales = [...new Set(pages.map((page) => page.locale).filter((value): value is string => Boolean(value)))]\n if (locales.length > 0) {\n docsConfig.i18n = {\n defaultLocale: 'en',\n locales: [\n { code: 'en', label: 'English' },\n ...locales.filter((locale) => locale !== 'en').map((locale) => ({ code: locale, label: locale.toUpperCase() })),\n ],\n }\n }\n if (platform === 'mintlify') docsConfig = addMintlifyDirectoryRedirects(docsConfig, pages)\n return {\n sourceUrl: source.toString(),\n sourceKind: 'url',\n platform,\n pages,\n assets: openApiAsset ? [{ path: 'openapi.yaml', content: openApiAsset }] : [],\n docsConfig,\n ...(sourceMintlifyConfig ? {\n site: {\n ...(typeof sourceMintlifyConfig.name === 'string' ? { name: sourceMintlifyConfig.name } : {}),\n ...(typeof sourceMintlifyConfig.description === 'string' ? { description: sourceMintlifyConfig.description } : {}),\n },\n } : {}),\n warnings,\n stats: {\n discovered: visited.size,\n imported: pages.length,\n skipped: Math.max(0, visited.size - pages.length),\n },\n }\n}\n","import type { MigrationDocsConfig, MigrationWarning } from './types.js'\n\n/**\n * Mintlify analytics -> Thally `docs.json` `integrations`.\n *\n * Reads docs.json `integrations` and the legacy mint.json `analytics` block.\n * Values land in an inline script on the rendered site, so each is validated\n * here with the same patterns the renderer applies (src/lib/analytics-config.ts;\n * a test keeps the two in agreement). Invalid values are skipped with a warning,\n * never coerced, and no warning echoes a full value.\n */\n\nexport type MigrationIntegrations = NonNullable<MigrationDocsConfig['integrations']>\n\nconst GA4_ID = /^G-[A-Z0-9]{4,20}$/\nconst GTM_ID = /^GTM-[A-Z0-9]{4,12}$/\nconst POSTHOG_KEY = /^phc_[A-Za-z0-9]{20,64}$/\nconst HOSTNAME_LABEL = '[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?'\nconst HOSTNAME = new RegExp(`^${HOSTNAME_LABEL}(?:\\\\.${HOSTNAME_LABEL})*$`)\nconst SAFE_PATH = /^\\/[A-Za-z0-9._~\\-/]*$/\nconst MAX_PLAUSIBLE_DOMAINS = 10\n\nconst SUPPORTED = ['ga4', 'gtm', 'posthog', 'plausible'] as const\n/** Legacy mint.json spelling of GA4; same `measurementId` field. */\nconst LEGACY_GA4_ALIAS = 'googleAnalytics'\n\nfunction own(record: Record<string, unknown>, key: string): unknown {\n return Object.prototype.hasOwnProperty.call(record, key) ? record[key] : undefined\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === 'object' && !Array.isArray(value)\n}\n\nfunction redact(value: unknown): string {\n if (typeof value !== 'string') return Array.isArray(value) ? 'array' : value === null ? 'null' : typeof value\n // Printable ASCII only: control and line-separator characters must not reach logs or reports.\n return value.length === 0 ? 'empty string' : `\"${value.trim().slice(0, 4).replace(/[^\\x20-\\x7e]/g, '?')}...\" (${value.length} chars)`\n}\n\n/** Public DNS names only: no localhost, and no IP literal in any form `URL` normalises (`0x7f.1`, `2130706433`, `[::1]`). */\nfunction isPublicHostname(hostname: string): boolean {\n return !/(^|\\.)localhost$/.test(hostname) && !/(^|\\.)\\d+$/.test(hostname) && !hostname.includes(':') && !hostname.startsWith('[')\n}\n\nfunction normalizeHttpsUrl(value: string, allowPath: boolean): string | null {\n if (!/^https:\\/\\//i.test(value) || /[?#\\\\]/.test(value)) return null\n let url: URL\n try {\n url = new URL(value)\n } catch {\n return null\n }\n if (url.protocol !== 'https:' || url.username || url.password || url.search || url.hash) return null\n if (!HOSTNAME.test(url.hostname) || !isPublicHostname(url.hostname) || url.port === '0') return null\n // Judge the path as written: URL parsing would silently resolve `..` segments.\n const fullPath = /^https:\\/\\/[^/]*(\\/.*)?$/i.exec(value)?.[1] ?? ''\n if (fullPath.includes('//')) return null\n const path = fullPath.replace(/\\/+$/, '')\n if (path && (!allowPath || !SAFE_PATH.test(path) || path.split('/').some((s) => s === '..' || s === '.'))) return null\n return `${url.origin}${path}`\n}\n\nfunction normalizeDomains(value: string): string | null {\n const parts = value.split(',').map((part) => part.trim().toLowerCase())\n if (parts.length > MAX_PLAUSIBLE_DOMAINS) return null\n return parts.every((part) => part.length <= 253 && HOSTNAME.test(part)) ? parts.join(',') : null\n}\n\n/** Bare hostname[:port] or an https origin; the script path is fixed by the renderer. */\nfunction normalizeServer(value: string): string | null {\n if (/^https:\\/\\//i.test(value)) {\n const origin = normalizeHttpsUrl(value, false)\n return origin ? origin.replace(/^https:\\/\\//, '') : null\n }\n const bare = /^([a-z0-9.-]+)(?::(\\d{1,5}))?$/i.exec(value)\n if (!bare || (bare[2] !== undefined && (Number(bare[2]) < 1 || Number(bare[2]) > 65_535))) return null\n const origin = normalizeHttpsUrl(`https://${bare[1].toLowerCase()}${bare[2] === undefined ? '' : `:${Number(bare[2])}`}`, false)\n return origin ? origin.replace(/^https:\\/\\//, '') : null\n}\n\nfunction text(value: unknown): string | null {\n return typeof value === 'string' && value.trim() && value.length <= 2_048 ? value.trim() : null\n}\n\ninterface Source {\n label: string\n block: Record<string, unknown>\n}\n\nexport function projectMintlifyIntegrations(config: Record<string, unknown>): {\n integrations?: MigrationIntegrations\n warnings: Array<MigrationWarning>\n} {\n // Analytics is optional: an unreadable config (non-object, throwing getter)\n // must never fail the migration.\n if (!isRecord(config)) return { warnings: [] }\n try {\n return project(config)\n } catch {\n return { warnings: [{ code: 'unsupported-config', message: 'Mintlify analytics configuration could not be read; analytics were not imported.' }] }\n }\n}\n\nfunction project(config: Record<string, unknown>): {\n integrations?: MigrationIntegrations\n warnings: Array<MigrationWarning>\n} {\n const warnings: Array<MigrationWarning> = []\n const warn = (message: string) => warnings.push({ code: 'unsupported-config', message })\n const sources: Array<Source> = []\n for (const [label, key] of [['integrations', 'integrations'], ['analytics', 'analytics']] as const) {\n const value = own(config, key)\n if (value === undefined) continue\n if (isRecord(value)) sources.push({ label, block: value })\n else warn(`Mintlify ${label} must be an object (found ${redact(value)}); analytics from it were not imported.`)\n }\n const integrations: MigrationIntegrations = {}\n\n // Sources are ordered docs.json `integrations` first, so it wins per provider.\n const pick = (name: (typeof SUPPORTED)[number]): Array<{ label: string; value: unknown }> => {\n const found: Array<{ label: string; value: unknown }> = []\n for (const source of sources) {\n let value = own(source.block, name)\n if (value === undefined && name === 'ga4' && source.label === 'analytics') value = own(source.block, LEGACY_GA4_ALIAS)\n if (value !== undefined) found.push({ label: `${source.label}.${name}`, value })\n }\n if (found.length > 1) {\n warn(`Mintlify ${found[0].label} and ${found[1].label} are both set; using ${found[0].label} for ${name}.`)\n }\n return found\n }\n\n const field = (label: string, block: Record<string, unknown>, key: string, required: boolean): string | null | undefined => {\n const raw = own(block, key)\n if (raw === undefined) {\n if (required) warn(`Mintlify ${label}.${key} is missing; ${label} was not imported.`)\n return required ? null : undefined\n }\n const value = text(raw)\n if (value === null) warn(`Mintlify ${label}.${key} is not a usable string (found ${redact(raw)}); ${label} was not imported.`)\n return value\n }\n\n const objectFor = (entry: { label: string; value: unknown }): Record<string, unknown> | null => {\n if (isRecord(entry.value)) return entry.value\n warn(`Mintlify ${entry.label} must be an object (found ${redact(entry.value)}); provider was not imported.`)\n return null\n }\n\n const ga4 = pick('ga4')[0]\n const ga4Block = ga4 ? objectFor(ga4) : null\n if (ga4 && ga4Block) {\n const id = field(ga4.label, ga4Block, 'measurementId', true)\n if (id && GA4_ID.test(id)) integrations.ga4 = { measurementId: id }\n else if (id) warn(`Mintlify ${ga4.label}.measurementId ${redact(id)} is not a GA4 ID (G-XXXXXXXXXX); Universal Analytics and lowercase IDs are not imported.`)\n }\n\n const gtm = pick('gtm')[0]\n const gtmBlock = gtm ? objectFor(gtm) : null\n if (gtm && gtmBlock) {\n const id = field(gtm.label, gtmBlock, 'tagId', true)\n if (id && GTM_ID.test(id)) integrations.gtm = { tagId: id }\n else if (id) warn(`Mintlify ${gtm.label}.tagId ${redact(id)} is not a GTM container ID (GTM-XXXXXXX); GTM was not imported.`)\n }\n\n const plausible = pick('plausible')[0]\n const plausibleBlock = plausible ? objectFor(plausible) : null\n if (plausible && plausibleBlock) {\n const domainRaw = field(plausible.label, plausibleBlock, 'domain', true)\n const serverRaw = field(plausible.label, plausibleBlock, 'server', false)\n const domain = domainRaw ? normalizeDomains(domainRaw) : null\n const server = serverRaw ? normalizeServer(serverRaw) : serverRaw\n if (domainRaw && !domain) warn(`Mintlify ${plausible.label}.domain ${redact(domainRaw)} must be hostname(s) without a protocol or path; Plausible was not imported.`)\n if (serverRaw && !server) warn(`Mintlify ${plausible.label}.server ${redact(serverRaw)} must be a hostname; Plausible was not imported.`)\n if (domain && server !== null) integrations.plausible = { domain, ...(server ? { server } : {}) }\n }\n\n const posthog = pick('posthog')[0]\n const posthogBlock = posthog ? objectFor(posthog) : null\n if (posthog && posthogBlock) {\n const key = field(posthog.label, posthogBlock, 'apiKey', true)\n const hostRaw = field(posthog.label, posthogBlock, 'apiHost', false)\n const host = hostRaw ? normalizeHttpsUrl(hostRaw, true) : hostRaw\n const recording = own(posthogBlock, 'sessionRecording')\n if (recording !== undefined && typeof recording !== 'boolean') warn(`Mintlify ${posthog.label}.sessionRecording must be a boolean (found ${redact(recording)}); session recording disabled.`)\n if (key && !POSTHOG_KEY.test(key)) warn(`Mintlify ${posthog.label}.apiKey ${redact(key)} is not a PostHog project key (phc_...); PostHog was not imported.`)\n if (hostRaw && !host) warn(`Mintlify ${posthog.label}.apiHost ${redact(hostRaw)} must be an https URL without credentials, query, or fragment; PostHog was not imported.`)\n if (key && POSTHOG_KEY.test(key) && host !== null) {\n integrations.posthog = {\n apiKey: key,\n ...(host ? { apiHost: host } : {}),\n // Fail closed: a present non-boolean value turns recording off.\n ...(recording !== undefined ? { sessionRecording: recording === true } : {}),\n }\n }\n }\n\n const unsupported = new Set<string>()\n for (const source of sources) {\n for (const key of Object.keys(source.block)) {\n if ((SUPPORTED as ReadonlyArray<string>).includes(key)) continue\n if (key === LEGACY_GA4_ALIAS && source.label === 'analytics') continue\n unsupported.add(/^[A-Za-z0-9_-]{1,40}$/.test(key) ? key : '(invalid name)')\n }\n }\n if (unsupported.size > 0) {\n const names = [...unsupported].slice(0, 30)\n warn(`Mintlify analytics/integration providers Thally cannot render were not imported: ${names.join(', ')}${unsupported.size > names.length ? ', ...' : ''}. Supported: ga4, gtm, posthog, plausible.`)\n }\n\n return { ...(Object.keys(integrations).length > 0 ? { integrations } : {}), warnings }\n}\n\n/**\n * Validate an authored Thally `docs.json` `integrations` value with the same\n * rules as the import and the renderer; one value-free message per problem.\n */\nexport function validateIntegrations(integrations: unknown): Array<string> {\n if (integrations === undefined) return []\n const result = projectMintlifyIntegrations({ integrations })\n const messages = result.warnings.map((warning) =>\n warning.message\n .replace(/^Mintlify analytics\\/integration providers Thally cannot render were not imported: /, 'docs.json integrations: Thally does not support these providers, so they will not be rendered: ')\n .replace(/^Mintlify /, 'docs.json ').replace(/\\b(?:was|were|are) not imported\\b/g, 'will not be rendered'),\n )\n if (result.integrations?.ga4 && result.integrations.gtm) {\n messages.push('docs.json integrations enable both ga4 and gtm; a GA4 tag inside the GTM container would double-count page views.')\n }\n return messages\n}\n","/** Markdown/MDX normalization that preserves every component Thally supports. */\n\nimport type * as acorn from 'acorn'\nimport { posix } from 'node:path'\nimport { compileSync } from '@mdx-js/mdx'\nimport { nameToEmoji } from 'gemoji'\nimport remarkGfm from 'remark-gfm'\nimport remarkMdx from 'remark-mdx'\nimport remarkParse from 'remark-parse'\nimport { unified } from 'unified'\n\nimport { isThallyBuiltinComponent } from './builtin-components.js'\nimport { pageScopeNames, unresolvedExpressionNames } from './inline-extraction.js'\nimport { playgroundDisplay } from './mintlify-extras.js'\nimport { parseFrontmatter } from './frontmatter.js'\nimport type { MigrationPage, MigrationPlatform } from './types.js'\n\nexport interface MarkdownPageIdentity {\n id: string\n navigationId: string\n locale?: string\n}\n\nfunction titleFromId(id: string): string {\n const value = id.split('/').at(-1) ?? id\n if (value === 'introduction') return 'Introduction'\n return value\n .split(/[-_]/)\n .filter(Boolean)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ')\n}\n\ninterface DescriptionNode {\n type: string\n value?: string\n alt?: string | null\n children?: Array<DescriptionNode>\n}\n\nconst descriptionParser = unified().use(remarkParse).use(remarkGfm).use(remarkMdx)\n\nfunction plainText(node: DescriptionNode): string {\n if (node.type === 'text' || node.type === 'inlineCode') return node.value ?? ''\n if (node.type === 'image') return node.alt ?? ''\n if (node.type === 'break') return ' '\n return (node.children ?? []).map(plainText).join('')\n}\n\n/** Read visible inline text without including JSX attributes or Markdown syntax. */\nexport function mdxVisibleText(content: string, definitions?: ReadonlyMap<string, string>): string {\n const references = [...content.matchAll(/\\[([^\\]\\n]+)\\]/g)]\n .map((match) => definitions?.get(match[1].trim().replace(/\\s+/g, ' ').toLowerCase()) ?? '').join('\\n')\n try { return plainText(descriptionParser.parse(`# ${content}\\n\\n${references}`) as DescriptionNode).trim() }\n catch { return content }\n}\n\nfunction firstParagraph(content: string): string {\n function findParagraph(node: DescriptionNode): string {\n if (node.type === 'paragraph') {\n const value = plainText(node).replace(/\\s+/g, ' ').trim()\n if (value) return value.slice(0, 240)\n }\n for (const child of node.children ?? []) {\n const value = findParagraph(child)\n if (value) return value\n }\n return ''\n }\n // Component wrappers, imports, expressions, and code fences are syntax, not\n // description copy. Reading paragraph nodes also finds prose nested inside\n // accordions without leaking their closing tags into page metadata.\n try {\n return findParagraph(descriptionParser.parse(content) as DescriptionNode)\n } catch {\n // Unparseable source is already retained for the migration validator. An\n // empty generated description is safer than exposing source-code fragments.\n return ''\n }\n}\n\nfunction docusaurusAdmonitionTag(kind: string): 'Error' | 'Info' | 'Note' | 'Warning' {\n if (kind === 'danger') return 'Error'\n if (kind === 'info') return 'Info'\n if (kind === 'caution' || kind === 'warning') return 'Warning'\n return 'Note'\n}\n\n/**\n * Scans the opening tag of `<name` at `start` to its closing `>`, skipping\n * quoted values and `{expressions}` (strings and any brace depth) so a `>` inside\n * them does not end the tag. The scan also stops at the next `<name` opener, so\n * a run of unterminated openers is scanned once in total, not once per opener.\n * `attributes` is what was scanned; `next` advances callers past that span.\n */\nexport function scanJsxOpeningTag(source: string, start: number, name: string): { attributes: string; end: number | null; next: number } {\n const opener = `<${name}`\n let quote: string | null = null\n let depth = 0\n let braceQuote: string | null = null\n let lineComment = false\n let blockComment = false\n const attributesStart = start + opener.length\n for (let index = attributesStart; index < source.length; index++) {\n const char = source[index]\n if (char === '<' && !quote && !braceQuote && depth === 0 && !lineComment && !blockComment\n && source.startsWith(opener, index) && !/[\\w$.-]/.test(source[index + opener.length] ?? '')) {\n return { attributes: source.slice(attributesStart, index), end: null, next: index }\n }\n if (lineComment) {\n if (char === '\\n' || char === '\\r') lineComment = false\n } else if (blockComment) {\n if (char === '*' && source[index + 1] === '/') { blockComment = false; index++ }\n } else if (braceQuote) {\n if (char === '\\\\') index++\n else if (char === braceQuote) braceQuote = null\n } else if (depth > 0) {\n if (char === '/' && source[index + 1] === '/') { lineComment = true; index++ }\n else if (char === '/' && source[index + 1] === '*') { blockComment = true; index++ }\n else if (char === '\"' || char === \"'\" || char === '`') braceQuote = char\n else if (char === '{') depth++\n else if (char === '}') depth--\n } else if (quote) {\n if (char === quote) quote = null\n } else if (char === '\"' || char === \"'\") quote = char\n else if (char === '{') depth++\n else if (char === '>') return { attributes: source.slice(attributesStart, index), end: index, next: index + 1 }\n }\n return { attributes: source.slice(attributesStart), end: null, next: source.length }\n}\n\n/**\n * `<Admonition type=\"tip\" title=\"X\">` is the JSX spelling of a colon-fence\n * admonition and maps to the same callouts.\n */\nexport function normalizeDocusaurusAdmonitionTags(segment: string): string {\n const open: Array<string> = []\n const marker = /<\\/Admonition>|<Admonition(?![\\w$.-])/g\n let output = ''\n let cursor = 0\n for (let match = marker.exec(segment); match; match = marker.exec(segment)) {\n if (match[0] === '</Admonition>') {\n output += `${segment.slice(cursor, match.index)}\\n</${open.pop() ?? 'Note'}>`\n cursor = marker.lastIndex\n continue\n }\n const tag = scanJsxOpeningTag(segment, match.index, 'Admonition')\n marker.lastIndex = tag.next\n if (tag.end === null) continue\n const type = tag.attributes.match(/\\btype=(?:\"([^\"]*)\"|'([^']*)')/)?.slice(1).find(Boolean)?.toLowerCase() ?? 'note'\n const title = tag.attributes.match(/\\btitle=(?:\"([^\"]*)\"|'([^']*)')/)?.slice(1).find(Boolean)?.trim()\n const callout = docusaurusAdmonitionTag(type)\n open.push(callout)\n output += `${segment.slice(cursor, match.index)}${title ? `<${callout}>\\n**${title}**\\n` : `<${callout}>\\n`}`\n cursor = tag.end + 1\n marker.lastIndex = cursor\n }\n return output + segment.slice(cursor)\n}\n\n/** `<Link to=...>` becomes `<a href=...>`; a tag that never closes is left as written. */\nexport function normalizeDocusaurusLinkTags(segment: string): string {\n const marker = /<Link(?![\\w$.-])/g\n let output = ''\n let cursor = 0\n for (let match = marker.exec(segment); match; match = marker.exec(segment)) {\n const tag = scanJsxOpeningTag(segment, match.index, 'Link')\n marker.lastIndex = tag.next\n if (tag.end === null) continue\n output += `${segment.slice(cursor, match.index)}<a${tag.attributes.replace(/(^|\\s)to=/, '$1href=')}>`\n cursor = tag.end + 1\n marker.lastIndex = cursor\n }\n return (output + segment.slice(cursor)).replace(/<\\/Link>/g, '</a>')\n}\n\n/**\n * Thally has no light/dark image component, so `<ThemedImage sources={{ light, dark }}>`\n * keeps its light image (a literal path, or `useBaseUrl('...')` of one).\n */\nfunction normalizeDocusaurusThemedImages(segment: string): string {\n return segment.replace(/<ThemedImage\\b([^<]*?)\\/>/g, (original: string, attributes: string) => {\n const light = attributes.match(/\\blight\\s*:\\s*(?:useBaseUrl\\(\\s*)?(?:\"([^\"]+)\"|'([^']+)')/)?.slice(1).find(Boolean)\n if (!light) return original\n const alt = attributes.match(/\\balt=(?:\"([^\"]*)\"|'([^']*)')/)?.slice(1).find((value) => value !== undefined)\n return `<img src=\"${light.replace(/\"/g, '"')}\"${alt === undefined ? '' : ` alt=\"${alt.replace(/\"/g, '"')}\"`} />`\n })\n}\n\n/**\n * Convert Docusaurus' colon-fence admonitions without interpreting code-fence\n * contents. Longer delimiters support nested admonitions in the same way as\n * the source renderer.\n */\nfunction normalizeDocusaurusAdmonitions(body: string): string {\n const lines = body.split('\\n')\n const openAdmonitions: Array<{ delimiter: string; tag: string; openedAt: number }> = []\n const headingLines: Array<number> = []\n let codeFence: string | null = null\n\n const output = lines.map((line, index) => {\n const codeMatch = line.match(/^\\s{0,3}(`{3,}|~{3,})/)\n if (codeMatch) {\n if (!codeFence) codeFence = codeMatch[1]\n else if (codeMatch[1][0] === codeFence[0] && codeMatch[1].length >= codeFence.length\n && /^\\s*$/.test(line.slice(codeMatch[0].length))) codeFence = null\n return line\n }\n if (codeFence) return line\n if (/^#{1,6}\\s/.test(line)) headingLines.push(index)\n\n const opening = line.match(/^\\s*(:{3,})(note|tip|info|warning|caution|danger)(?:\\[([^\\]]+)\\]|\\s+(.+))?\\s*$/i)\n if (opening) {\n const tag = docusaurusAdmonitionTag(opening[2].toLowerCase())\n openAdmonitions.push({ delimiter: opening[1], tag, openedAt: index })\n const title = (opening[3] ?? opening[4])?.trim()\n return title ? `<${tag}>\\n**${title}**` : `<${tag}>`\n }\n\n const closing = line.match(/^\\s*(:{3,})\\s*$/)\n const current = openAdmonitions.at(-1)\n if (closing && current?.delimiter === closing[1]) {\n openAdmonitions.pop()\n return `</${current.tag}>`\n }\n return line\n })\n // Docusaurus tolerates an unterminated admonition in some archived docs.\n // Close it before the next top-level heading (or at EOF) so an otherwise\n // complete page remains importable instead of being discarded by MDX.\n const closingByLine = new Map<number, Array<string>>()\n for (const open of openAdmonitions.reverse()) {\n const at = headingLines.find((line) => line > open.openedAt) ?? output.length\n closingByLine.set(at, [...(closingByLine.get(at) ?? []), `</${open.tag}>`])\n }\n for (const [at, closing] of [...closingByLine].sort(([left], [right]) => right - left)) output.splice(at, 0, ...closing)\n return output.join('\\n')\n}\n\n/**\n * Docusaurus' `mdx-code-block` fence is an escape hatch for live MDX syntax,\n * often used to open a JSX element around ordinary Markdown and code blocks.\n * Remove only the special fence itself; its contents must pass through the\n * same component and link normalization as the rest of the page.\n */\nfunction unwrapDocusaurusMdxCodeBlocks(body: string): string {\n const output: Array<string> = []\n let fence: string | undefined\n let isMdxCodeBlock = false\n for (const line of body.split('\\n')) {\n const opening = !fence && line.match(/^ {0,3}(`{3,}|~{3,})mdx-code-block\\s*$/)\n if (opening) {\n fence = opening[1]\n isMdxCodeBlock = true\n continue\n }\n if (fence) {\n const closing = line.match(/^ {0,3}(`{3,}|~{3,})\\s*$/)\n if (closing && closing[1][0] === fence[0] && closing[1].length >= fence.length) {\n if (!isMdxCodeBlock) output.push(line)\n fence = undefined\n isMdxCodeBlock = false\n continue\n }\n // These blocks may be Docusaurus' only declaration of a theme or\n // npm component used later on the page. Opening just the JSX would\n // promote an unresolved import into Thally's server build, so leave\n // the entire page's source-specific fences intact.\n if (isMdxCodeBlock && /^\\s*(?:import|export)\\b/.test(line)) return body\n output.push(line)\n continue\n }\n const ordinaryFence = line.match(/^ {0,3}(`{3,}|~{3,})/)\n if (ordinaryFence) {\n fence = ordinaryFence[1]\n isMdxCodeBlock = false\n }\n output.push(line)\n }\n // An unmatched source fence remains visible to the MDX compiler, which\n // reports the malformed page rather than silently deleting its contents.\n return isMdxCodeBlock ? body : output.join('\\n')\n}\n\n// Any HTML5 id: non-empty, no whitespace. `\"` `'` `<` `>` `&` and backticks\n// would break the generated `id=\"...\"` attribute and `{` `}` would reopen an MDX\n// expression. A single character class, so it cannot backtrack.\nconst HTML_ID = /^[^\\s\"'<>`{}&]+$/u\n\n/**\n * Keep explicit heading anchors without leaving `{#id}` as an MDX expression.\n * An id that is not a valid HTML id is stripped (and reported through `warn`)\n * so the page still compiles. Frontmatter is left untouched.\n */\nexport function normalizeExplicitHeadingIds(\n raw: string,\n warn?: (message: string) => void,\n options: { headingMarkers?: boolean; keepIdComments?: boolean } = {},\n): string {\n const { front, body } = splitFrontmatterBlock(raw)\n return front + replaceOutsideCode(body, (segment) => segment.split('\\n').map((line) => {\n if (!/^[ \\t]{0,3}#{1,6}[ \\t]+/.test(line)) return line\n const trimmed = line.trimEnd()\n // Check suffixes from the end instead of matching unbounded whitespace\n // against unbounded heading text; that combination backtracks on long\n // headings with no valid anchor.\n if (trimmed.endsWith('}')) {\n const marker = trimmed.lastIndexOf(' {#')\n if (marker >= 0) {\n const id = trimmed.slice(marker + 3, -1)\n const heading = trimmed.slice(0, marker).trimEnd()\n // Braces in the \"id\" mean this is prose like `{#if} blocks {x}`, not one anchor.\n if (/[{}]/.test(id)) return line\n // Mintlify keeps the authored id on the heading itself. The renderer\n // reads `{/* #id */}`, so the id may hold any character React can put\n // in an attribute; only whitespace and a comment terminator are unsafe.\n const usable = options.headingMarkers ? id && !/\\s/.test(id) && !id.includes('*/') : HTML_ID.test(id)\n if (usable && options.headingMarkers) return `${heading} ${headingIdMarker(id)}`\n // Numeric starts are valid here; Mintlify uses ids such as 429-responses.\n if (usable) return `<a id=\"${id}\"></a>\\n${heading}`\n warn?.(`Heading anchor {#${id}} ${options.headingMarkers ? 'contains whitespace or \"*/\"' : 'is not a valid HTML id'} and was removed from \"${heading.replace(/^\\s*#+\\s*/, '')}\".`)\n return heading\n }\n }\n // Docusaurus' heading plugin also accepts a trailing MDX comment.\n // (Mintlify output uses that same comment as its marker, so it stays.)\n if (!options.headingMarkers && !options.keepIdComments && trimmed.endsWith('*/}')) {\n const marker = trimmed.lastIndexOf(' {/*')\n if (marker >= 0) {\n const comment = trimmed.slice(marker + 4, -3).trim()\n const id = comment.startsWith('#') ? comment.slice(1) : ''\n if (HTML_ID.test(id)) {\n return `<a id=\"${id}\"></a>\\n${trimmed.slice(0, marker).trimEnd()}`\n }\n }\n }\n return line\n }).join('\\n'))\n}\n\n/** The trailing comment that gives a heading an explicit id; read by the renderer, `thally check` and the content parser. */\nfunction headingIdMarker(id: string): string {\n return `{/* #${id} */}`\n}\n\n/**\n * The id Mintlify gives a heading with no explicit `{#id}`. Derived from the\n * live ids of a Mintlify site: whitespace and `.` become `-`, and `+ & / _`,\n * typographic quotes and dashes and arrows are kept, other punctuation and\n * emoji are dropped. Repeats are numbered `-2`, `-3`, ... by the renderer.\n */\nexport function mintlifyHeadingSlug(text: string): string {\n return text\n .replace(/\\u200b/g, '')\n .normalize('NFC')\n .trim()\n .toLowerCase()\n .replace(/[.\\s]+/g, '-')\n .replace(/-{2,}/g, '-')\n .replace(/^-|-$/g, '')\n .replace(/[^\\p{L}\\p{M}\\p{N}_\\-+&/\\u2019\\u201c\\u201d\\u2014\\u2013\\u2192]/gu, '')\n}\n\n/** Thally's own heading slug (`slugify` in `@thallylabs/core`), used to tell which headings already match. */\nfunction thallyHeadingSlug(text: string): string {\n return text.normalize('NFC').toLowerCase().replace(/[^\\p{L}\\p{M}\\p{N}]+/gu, '-').replace(/(^-|-$)/g, '')\n}\n\n/** Mintlify typesets straight quotes in heading text, and its ids keep the typographic characters. */\nfunction typesetQuotes(text: string): string {\n return text.split(/(`[^`]*`|<[^>]*>|\\]\\([^)]*\\))/).map((part, index) => {\n if (index % 2) return part\n return part\n .replace(/(^|[\\s([{\\u2014\\u2013])\"/g, '$1\\u201c').replace(/\"/g, '\\u201d')\n .replace(/(^|[\\s([{\\u2014\\u2013\\u201c])'(?=\\S)/g, '$1\\u2018').replace(/'/g, '\\u2019')\n }).join('')\n}\n\n/** Visible text of a heading's inline Markdown. */\nfunction plainHeadingText(source: string): string {\n return source\n // An authored MDX comment is not rendered, so Mintlify's id never sees it.\n .replace(/\\{\\/\\*[\\s\\S]*?\\*\\/\\}/g, ' ')\n .replace(/!?\\[([^\\]]*)\\]\\([^)]*\\)/g, '$1')\n .replace(/<\\/?[A-Za-z][^>]*>/g, '')\n .replace(/`([^`]*)`/g, '$1')\n .replace(/(\\*\\*|__|~~|\\*)/g, '')\n .replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '\"')\n .replace(/\\\\([\\\\`*_{}[\\]()#+.!&<>|~-])/g, '$1')\n}\n\n/**\n * Give each Mintlify heading the id Mintlify serves, so in-page and cross-page\n * `#anchor` links from the source still land. Only headings whose id differs\n * from Thally's own slug get a marker, which leaves every other anchor alone.\n * Straight quotes in heading text are typeset as Mintlify does.\n */\nexport function markMintlifyHeadings(body: string): string {\n let fence: string | undefined\n return body.split('\\n').map((line) => {\n const marker = line.match(/^ {0,3}(`{3,}|~{3,})/)\n if (marker) {\n if (!fence) fence = marker[1]\n else if (marker[1][0] === fence[0] && marker[1].length >= fence.length\n && /^\\s*$/.test(line.slice(marker[0].length))) fence = undefined\n return line\n }\n if (fence) return line\n const heading = line.match(/^( {0,3}#{2,6}[ \\t]+)(.+?)(?:[ \\t]+#+)?[ \\t]*$/)\n if (!heading) return line\n // An explicit `{#id}` is already a marker and wins over the computed id.\n const explicit = heading[2].match(/^(.*?)([ \\t]*\\{\\/\\*[ \\t]*#\\S+[ \\t]*\\*\\/\\})$/)\n const source = typesetQuotes(explicit ? explicit[1] : heading[2])\n if (explicit) return `${heading[1]}${source}${explicit[2]}`\n const text = plainHeadingText(source)\n const id = mintlifyHeadingSlug(text)\n return `${heading[1]}${source}${id && id !== thallyHeadingSlug(text) ? ` ${headingIdMarker(id)}` : ''}`\n }).join('\\n')\n}\n\nconst GLOBAL_DOCUSARUS_COMPONENTS = new Set([\n 'Tabs',\n 'TabItem',\n 'Link',\n 'DocCardList',\n 'TOCInline',\n])\n\n/** Match the one simple import form Docusaurus injects without backtracking. */\nfunction removeGlobalDocusaurusImports(body: string): string {\n return replaceOutsideCode(body, (segment) => segment.replace(\n /\\bimport\\s+([A-Za-z_$][\\w$]*)\\s+from\\s+(['\"])(@(?:theme|docusaurus)\\/[^'\"]+)\\2\\s*;?/g,\n (statement: string, component: string) => GLOBAL_DOCUSARUS_COMPONENTS.has(component) ? '' : statement,\n ))\n}\n\nconst JS_LITERAL_KEYWORDS = new Set(['true', 'false', 'null', 'undefined'])\n// A leading identifier segment (`x` in `x.y.z`) may be any valid JS/Unicode\n// identifier, not just ASCII — `{café}`, `{$var}` are both real bare\n// references a page's own ESM could bind, and Fern's literal-brace prose\n// (the thing this whole pass is trying to leave alone) never happens to\n// look like one, so escaping stays conservative either way.\nconst BARE_IDENTIFIER_PATH = /^[\\p{ID_Start}$_][\\p{ID_Continue}$]*(?:\\.[\\p{ID_Start}$_][\\p{ID_Continue}$]*)*$/u\n// Double-mustache templating syntax (`{{now}}`, `{{customer}}`) is common in\n// prose copied from other platforms (Liquid/Handlebars-style variable\n// interpolation). MDX parses the outer `{...}` as an expression container and\n// the inner `{now}` as a JS object-literal shorthand property, so the node's\n// value looks like `{now}` — a single bare identifier wrapped in its own\n// braces — never a real JSX prop (which appears inside an attribute, not\n// standalone body/flow text). Matching this shape lets the same \"leave a\n// $ref alone unless we can hint an undeclared reference\" caution apply to\n// double-brace template text as it already does to single-brace text.\nconst BARE_OBJECT_SHORTHAND = /^\\{\\s*([\\p{ID_Start}$_][\\p{ID_Continue}$]*)\\s*\\}$/u\n/**\n * Pure JS built-ins that exist identically in the server and browser render.\n * Only a dotted path rooted at one (`{Math.PI}`, `{Number.MAX_SAFE_INTEGER}`)\n * is left as a live expression. A bare name (`{window}`, `{Date}`,\n * `{console}`) is always escaped: it would render an object/function (a\n * React crash or silently empty text), and host globals such as `window`,\n * `document`, `navigator`, or `process` do not exist on both sides at all.\n */\nconst SAFE_BUILTIN_ROOTS = new Set([\n 'Math', 'JSON', 'Number', 'String', 'Object', 'Array', 'Intl', 'Boolean', 'Symbol', 'BigInt', 'Reflect',\n])\n\ninterface MdxOffsetNode {\n type: string\n depth?: number\n value?: string\n name?: string | null\n data?: { estree?: acorn.Program | null }\n children?: Array<MdxOffsetNode>\n position?: { start: { offset?: number }; end: { offset?: number } }\n}\n\n/** Bind static partial invocation props inside expressions, preserving literals and code examples. */\nexport function bindSnippetProps(body: string, properties: ReadonlyMap<string, string>): string {\n if (!body.includes('props') || properties.size === 0) return body\n let tree: unknown\n try { tree = descriptionParser.parse(body) } catch { return body }\n const edits: Array<{ start: number; end: number; value: string }> = []\n const inspectExpression = (value: unknown): void => {\n if (!value || typeof value !== 'object') return\n const node = value as acorn.AnyNode\n if (node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression' || node.type === 'FunctionDeclaration') {\n const names = new Set<string>()\n node.params.forEach((param) => collectPatternNames(param, names))\n if (names.has('props')) return\n }\n if (node.type === 'MemberExpression' && node.object.type === 'Identifier' && node.object.name === 'props') {\n const key = !node.computed && node.property.type === 'Identifier' ? node.property.name\n : node.computed && node.property.type === 'Literal' && typeof node.property.value === 'string' ? node.property.value : undefined\n const replacement = key === undefined ? undefined : properties.get(key)\n if (replacement !== undefined && body.slice(node.start, node.end).startsWith('props')) {\n edits.push({ start: node.start, end: node.end, value: JSON.stringify(replacement) })\n return\n }\n }\n for (const child of Object.values(value)) {\n if (Array.isArray(child)) child.forEach(inspectExpression)\n else if (child && typeof child === 'object') inspectExpression(child)\n }\n }\n const visit = (value: unknown): void => {\n if (!value || typeof value !== 'object') return\n const node = value as MdxOffsetNode & { attributes?: Array<{ value?: unknown }> }\n if (node.data?.estree) inspectExpression(node.data.estree)\n for (const attribute of node.attributes ?? []) visit(attribute.value)\n for (const child of node.children ?? []) visit(child)\n }\n visit(tree)\n return edits.sort((a, b) => b.start - a.start).reduce((text, edit) => text.slice(0, edit.start) + edit.value + text.slice(edit.end), body)\n}\n\n/** Use a page's leading H1 as its title without rendering it twice. */\nfunction leadingPageHeading(body: string): { title: string; start: number; end: number } | undefined {\n if (!/^ {0,3}(?:#(?:[ \\t]|$)|=+[ \\t\\r]*$)/m.test(body)) return undefined\n try {\n const root = descriptionParser.parse(body) as MdxOffsetNode\n const first = root.children?.find((node) => node.type !== 'mdxjsEsm' && node.type !== 'html'\n && !(node.type === 'mdxFlowExpression' && node.data?.estree?.body.length === 0))\n if (first?.type !== 'heading' || first.depth !== 1) return undefined\n const text = (node: MdxOffsetNode): string => node.type === 'mdxTextExpression' || node.type === 'mdxFlowExpression'\n ? ''\n : node.value ?? (node.children ?? []).map(text).join('')\n const title = text(first).replace(/\\s+/g, ' ').trim()\n const start = first.position?.start.offset\n const end = first.position?.end.offset\n return title && start !== undefined && end !== undefined ? { title, start, end } : undefined\n } catch {\n return undefined\n }\n}\n\n/** Recheck after component fallbacks, which can expose a page's leading H1. */\nexport function resolvePageDisplayHeading(page: Pick<MigrationPage, 'title' | 'body' | 'headingTitle'>, platform?: MigrationPlatform): void {\n const heading = leadingPageHeading(page.body)\n if (!heading || (heading.title !== page.title && platform !== 'docusaurus')) return\n if (heading.title !== page.title) page.headingTitle = heading.title\n page.body = `${page.body.slice(0, heading.start)}${page.body.slice(heading.end)}`.trim()\n}\n\n/** Recursively collects every name a binding pattern introduces (`{a, b: {c}}`, `[p, ...rest]`, `x = 1`). */\nfunction collectPatternNames(pattern: acorn.AnyNode, names: Set<string>): void {\n switch (pattern.type) {\n case 'Identifier':\n names.add((pattern as acorn.Identifier).name)\n break\n case 'ObjectPattern':\n for (const property of (pattern as acorn.ObjectPattern).properties) {\n collectPatternNames(property.type === 'RestElement' ? property.argument : property.value, names)\n }\n break\n case 'ArrayPattern':\n for (const element of (pattern as acorn.ArrayPattern).elements) {\n if (element) collectPatternNames(element, names)\n }\n break\n case 'AssignmentPattern':\n collectPatternNames((pattern as acorn.AssignmentPattern).left, names)\n break\n case 'RestElement':\n collectPatternNames((pattern as acorn.RestElement).argument, names)\n break\n default:\n break\n }\n}\n\n/** Adds the name(s) a single top-level declaration statement introduces (`const`/`let`/`var`, `function`, `class`). */\nfunction collectDeclarationNames(node: acorn.AnyNode | null | undefined, names: Set<string>): void {\n if (!node) return\n if (node.type === 'VariableDeclaration') {\n for (const declarator of (node as acorn.VariableDeclaration).declarations) collectPatternNames(declarator.id, names)\n } else if ((node.type === 'FunctionDeclaration' || node.type === 'ClassDeclaration') && (node as acorn.FunctionDeclaration | acorn.ClassDeclaration).id) {\n names.add(((node as acorn.FunctionDeclaration | acorn.ClassDeclaration).id as acorn.Identifier).name)\n }\n}\n\n/**\n * Bind every name a page's own ESM statement introduces — an import\n * (default, namespace, or named/destructured, however deeply nested its\n * destructuring goes), or a top-level `const`/`let`/`var`/`function`/`class`\n * — so a prose brace referencing one of them is treated as real,\n * already-bound code rather than ambiguous Fern prose. Read from the ESTree\n * remark-mdx attaches to the `mdxjsEsm` node (real ESM syntax including JSX,\n * not a regex approximation), so `export const a = 1, b = 2`, nested\n * destructuring, and multi-declarator statements all resolve\n * correctly. `export { x } from './y'` (a re-export) and a local `export {\n * x }` (which only re-exposes an already-declared local) never introduce a\n * new binding, so neither is added here — matching real JS module\n * semantics, since referencing `x` in prose is exactly as undefined as it\n * would be in the compiled component.\n */\nfunction collectEsmBindings(node: MdxOffsetNode, declared: Set<string>): void {\n // remark-mdx already parsed this ESM (with JSX support) and keeps the\n // result on the node; re-parsing it with plain acorn fails on any JSX.\n const program = node.data?.estree\n if (!program) {\n collectEsmBindingsByPattern(node.value ?? '', declared)\n return\n }\n for (const statement of program.body) {\n if (statement.type === 'ImportDeclaration') {\n for (const specifier of statement.specifiers) declared.add(specifier.local.name)\n } else if (statement.type === 'ExportNamedDeclaration') {\n collectDeclarationNames(statement.declaration, declared)\n } else if (statement.type === 'ExportDefaultDeclaration') {\n collectDeclarationNames(statement.declaration, declared)\n } else {\n collectDeclarationNames(statement, declared)\n }\n }\n}\n\n/**\n * Fallback when no parsed ESM tree is available: collect simple declared and\n * imported names by pattern, so the page's own bindings are still recognized.\n * Approximate (destructuring is not followed, nested declarations are\n * included), but never records nothing for a page that does bind names.\n */\nfunction collectEsmBindingsByPattern(source: string, declared: Set<string>): void {\n for (const match of source.matchAll(/\\b(?:const|let|var|class|function\\s*\\*?)\\s+([\\p{ID_Start}$_][\\p{ID_Continue}$]*)/gu)) declared.add(match[1])\n for (const match of source.matchAll(/\\bimport\\s+([^'\";]*?)\\s+from\\b/g)) {\n const clause = match[1]\n for (const name of clause.replace(/\\{[^}]*\\}/, '').matchAll(/(?:\\*\\s+as\\s+)?([\\p{ID_Start}$_][\\p{ID_Continue}$]*)/gu)) declared.add(name[1])\n for (const entry of clause.match(/\\{([^}]*)\\}/)?.[1].split(',') ?? []) {\n const local = entry.trim().split(/\\s+as\\s+/).at(-1)?.trim()\n if (local) declared.add(local)\n }\n }\n}\n\n/**\n * Fern's own MDX renderer tolerates a bare `{word}` in prose as literal text\n * (e.g. `\"connection to {vendor} failed\"`, `<Note>connection to {vendor}\n * failed</Note>`); Thally's MDX pipeline evaluates `{...}` as a JS expression\n * and throws `ReferenceError` when the identifier isn't defined. `{vendor}`\n * is syntactically valid MDX either way — a text expression — so the fix\n * isn't a character scan but telling real code from prose: parse the page,\n * then escape a bare identifier/dotted-path text expression\n * (`mdxTextExpression`/`mdxFlowExpression`) anywhere in the body tree,\n * including inside a JSX element's children (a `<Note>`/`<Tabs>` callout is\n * the common Fern case) — but never a JSX *attribute* expression\n * (`prop={x}`, which lives on the node's `attributes`, not its `children`,\n * and this walk never visits it), never ESM, never code. Real component\n * code — `{items.map((item) => <li>{item}</li>)}` — parses as a single\n * expression node whose JSX lives only in its `estree`, not as further mdast\n * children, so this walk cannot (and must not) descend into it and escape\n * the inner `{item}`; only a leaf expression node with every mdast ancestor\n * being JSX/markdown is ever a candidate. `props` (MDX's implicit prop) and\n * anything the page's own ESM imports or declares are exempt. A page whose\n * source doesn't parse is left untouched; the migration's MDX-compile check\n * reports the real problem instead.\n */\n/** Trimmed line begins a fenced code block (```` ``` ```` or `~~~`, 3+ characters). */\nconst FENCE_OPEN = /^(`{3,}|~{3,})/\n\n/**\n * KaTeX-style `$$...$$` math (block, on its own lines, or inline mid-\n * paragraph) is common in Fern docs (and any other Markdown source) but has\n * no Thally renderer (no `remark-math`/`rehype-katex`) — worse, the raw\n * LaTeX inside (`\\begin{align*}`, bare `{...}`) crashes the MDX parser\n * before `escapeFernLiteralBraces` below even gets a chance to run, so the\n * whole page fails to compile and is silently excluded. Content is\n * preserved, never dropped: a block becomes a fenced ` ```math ` code\n * block, an inline span becomes an inline code span — both opaque to MDX's\n * `{...}` expression parsing. This is a plain line scan (not an MDX parse,\n * which is exactly what the source can't survive yet) that tracks fenced\n * code blocks so a real code sample's own `$$` is never touched, and must\n * run before any MDX-aware pass.\n */\n/**\n * Mask, within a single line, every backtick code span and every JSX/HTML\n * tag (open, close, or self-closing — including its attribute values) so\n * `protectMathBlocks`'s inline `$...$` scan below can never mistake a `$`\n * inside either for a math delimiter, and a matched math span can never\n * straddle a masked region (the placeholder has no `$` in it, so the regex\n * simply can't span across one). A JSX tag never spans a `$...$` scan's own\n * line here because `protectMathBlocks` processes text line by line; a tag\n * split across lines is already opaque to it for other reasons.\n */\nfunction maskInlineSpansForMath(line: string): { masked: string; unmask: (text: string) => string } {\n const blocks: Array<string> = []\n const stash = (text: string): string => {\n blocks.push(text)\n return `\\u0000${blocks.length - 1}\\u0000`\n }\n const codeMasked = line.split(/(`[^`]*`)/).map((segment, index) => (\n index % 2 === 1 ? stash(segment) : segment\n )).join('')\n const tagMasked = codeMasked.replace(/<\\/?[a-zA-Z][^<>]*>/g, stash)\n // Also mask a JSX expression container (`{...}`), one level of nesting\n // deep, so `$` inside one (e.g. a prop value built from a template\n // literal that isn't itself backtick-quoted) is never read as a math\n // delimiter. Two passes handle one level of `{...{...}...}` nesting; a\n // line-local regex can't balance further than that, which is fine here —\n // anything left over simply isn't masked, the conservative default. A\n // `{` immediately after `$` is deliberately left unmasked: that is a\n // template-literal interpolation (`${...}`), not a JSX container, and the\n // caller's own `$(?!{)` guard needs to see the literal `{` to reject it.\n const masked = [tagMasked, tagMasked].reduce((text) => text.replace(/(?<!\\$)\\{[^{}]*\\}/g, stash), tagMasked)\n return {\n masked,\n unmask: (text) => text.replace(/\\u0000(\\d+)\\u0000/g, (_match, index: string) => blocks[Number(index)]),\n }\n}\n\n/**\n * Whether `content` (the real, unmasked text between a matched pair of\n * inline `$...$`) looks like actual TeX rather than two unrelated dollar\n * amounts or a stray currency symbol: a backslash command (`\\times`,\n * `\\text{...}`), `^`/`_` (super/subscript), or a brace group. A bare\n * variable-style token with no space and no digit (`$S$`, `$x$`) is also\n * accepted without any of those — KaTeX/Pandoc's own convention for a\n * single-symbol inline reference, and the shape of the paradex-docs repro.\n */\nconst TEX_SIGNAL = /\\\\[a-zA-Z]|[\\^_{]/\nfunction hasTexSignal(content: string): boolean {\n return TEX_SIGNAL.test(content) || (!/\\s/.test(content) && !/\\d/.test(content))\n}\n\n/**\n * Net count of `{`, `(`, `[` minus their closers on `line` — a lazy,\n * string/comment-unaware bracket count (not a real JS parse) used only to\n * tell when a multi-line `import`/`export` ESM statement has closed. Real\n * component source rarely has an unbalanced bracket inside a string on the\n * same line as an ESM statement's own delimiters, and this is a heuristic\n * for skipping a scan, not a correctness-critical parse.\n * TODO: raw character count, ignores strings/comments; a real AST pass\n * would be exact, add one if this heuristic misfires on real content.\n */\nfunction bracketDelta(line: string): number {\n let delta = 0\n for (const ch of line) {\n if (ch === '{' || ch === '(' || ch === '[') delta += 1\n else if (ch === '}' || ch === ')' || ch === ']') delta -= 1\n }\n return delta\n}\n\n/** Trimmed line opens a top-level `import`/`export` ESM statement (Docusaurus/MDX allow one anywhere a block can start, not just at the top of the file). */\nconst ESM_OPEN = /^(?:import|export)\\b/\n\n/** Keep TeX as a literal JSX string so braces and backslashes survive MDX parsing. */\nfunction latexElement(source: string, block = false): string {\n return `<Latex${block ? ' block' : ''}>{${JSON.stringify(source)}}</Latex>`\n}\n\nexport function protectMathBlocks(raw: string): { body: string; converted: boolean; guardTriggered?: boolean } {\n const { front, body } = splitFrontmatterBlock(raw)\n const lines = body.split(/\\r\\n|\\r|\\n/)\n const output: Array<string> = []\n let inFence = false\n let fenceToken = ''\n // Fern also allows a block delimited by a bare `$` alone on its own line\n // (not just `$$`), e.g. `$\\n\\text{...}\\n$`; a block must close on the\n // same delimiter that opened it.\n let mathBlockDelimiter: '$$' | '$' | null = null\n let mathLines: Array<string> = []\n let converted = false\n // Depth of an open `import`/`export` ESM statement (counted in\n // `bracketDelta`): while positive, every line belongs to page-authored\n // JS/JSX source (e.g. an inlined snippet's `export const X = () => {...}`\n // component body), not markdown/math prose, and is copied through\n // untouched — never scanned for `$`, so a template literal's `${expr}`\n // inside it can never be misread as a math delimiter.\n let esmDepth = 0\n for (const line of lines) {\n const trimmed = line.trim()\n if (mathBlockDelimiter) {\n if (trimmed === mathBlockDelimiter) {\n output.push(latexElement(mathLines.join('\\n'), true))\n mathBlockDelimiter = null\n mathLines = []\n converted = true\n } else {\n mathLines.push(line)\n }\n continue\n }\n if (inFence) {\n if (trimmed.startsWith(fenceToken)) inFence = false\n output.push(line)\n continue\n }\n if (esmDepth > 0) {\n output.push(line)\n esmDepth = Math.max(0, esmDepth + bracketDelta(line))\n continue\n }\n const fenceOpen = FENCE_OPEN.exec(trimmed)\n if (fenceOpen) {\n inFence = true\n fenceToken = fenceOpen[1].slice(0, 3)\n output.push(line)\n continue\n }\n if (trimmed === '$$' || trimmed === '$') {\n mathBlockDelimiter = trimmed\n continue\n }\n if (ESM_OPEN.test(trimmed)) {\n output.push(line)\n esmDepth = Math.max(0, bracketDelta(line))\n continue\n }\n if (line.includes('$')) {\n // Mask inline code spans (`` `$FOO` ``), JSX/HTML tags (including\n // their attribute values, e.g. `<Badge color=\"$primary\">`), and a\n // JSX expression container (`{...}`) before scanning for math, so\n // none of them can be mistaken for a math delimiter and a math span\n // can never cross into one — a masked placeholder has no `$` in it,\n // so the scan below simply can't see inside one or pair a `$`\n // outside it with one that was inside it. What's left unmasked is\n // ordinary prose/JSX text content.\n const { masked, unmask } = maskInlineSpansForMath(line)\n // One combined pass, `$$...$$` tried before single-`$...$` at each\n // position: doing these as two sequential passes let the second\n // (single-`$`) regex re-match dollar signs the first pass had just\n // wrapped in backticks, corrupting its own output. Single-`$` inline\n // math (the other half of the KaTeX convention, e.g. `$F = S \\times\n // e^{\\,f\\,T}$`, or a bare `$S$`) only counts when its content\n // doesn't start/end with whitespace, holds no further `$`, and looks\n // like real TeX (`hasTexSignal`) — the same rule KaTeX/Pandoc use to\n // tell real inline math from an ordinary sentence mentioning two\n // dollar amounts (`$50 and $100`, whose span content ends in a space\n // and so never matches). Neither alternative ever opens on `${`\n // (a template-literal interpolation, not a math delimiter). An\n // author-escaped `\\$` (a literal dollar sign) is never treated as a\n // delimiter.\n const updatedMasked = masked.replace(\n /\\$\\$(?!\\{)([^\\n]+?)\\$\\$|(?<!\\\\)\\$(?!\\{)([^\\s$](?:[^$\\n]*[^\\s$])?)\\$/g,\n (whole: string, block: string | undefined, inline: string | undefined) => {\n if (block !== undefined) {\n converted = true\n return latexElement(unmask(block))\n }\n if (inline !== undefined && hasTexSignal(unmask(inline))) {\n converted = true\n return latexElement(unmask(inline))\n }\n return whole\n },\n )\n output.push(unmask(updatedMasked))\n continue\n }\n output.push(line)\n }\n // An unterminated `$$` block means the source was malformed to begin\n // with; leave it untouched rather than eating the rest of the page into\n // one giant fenced block.\n if (mathBlockDelimiter) return { body: raw, converted: false }\n const protectedBody = front + output.join('\\n')\n if (!converted) return { body: protectedBody, converted }\n // Math conversion must never turn a page that used to compile into one\n // that doesn't (e.g. a false-positive match that corrupts real code).\n // If the protected body fails to compile as MDX but the untouched\n // original did, the conversion made things worse — keep the original and\n // let the caller warn, rather than silently excluding the page later.\n try {\n compileSync(protectedBody, { outputFormat: 'program' })\n return { body: protectedBody, converted }\n } catch {\n try {\n compileSync(raw, { outputFormat: 'program' })\n return { body: raw, converted: false, guardTriggered: true }\n } catch {\n // The original didn't compile either (the usual case math protection\n // exists for) — the protected attempt is still the better bet.\n return { body: protectedBody, converted }\n }\n }\n}\n\nexport function escapeFernLiteralBraces(raw: string): string {\n // Never touch the YAML frontmatter block: its `{...}` values (e.g. a\n // description containing a literal brace) are YAML scalars, not MDX\n // expressions, and running the MDX parser over them corrupts the block.\n // Split it off first — mirroring `parseFrontmatter`'s own delimiter\n // handling, including a leading BOM and CRLF line endings, which a naive\n // `/^---\\n/` guard misses and lets the frontmatter get parsed as MDX.\n const { front, body } = splitFrontmatterBlock(raw)\n\n let root: MdxOffsetNode\n try {\n root = descriptionParser.parse(body) as MdxOffsetNode\n } catch {\n return raw\n }\n\n const declared = new Set<string>(['props'])\n const collectEsm = (node: MdxOffsetNode) => {\n if (node.type === 'mdxjsEsm') collectEsmBindings(node, declared)\n for (const child of node.children ?? []) collectEsm(child)\n }\n collectEsm(root)\n\n const edits: Array<{ start: number; end: number; value: string }> = []\n const visit = (node: MdxOffsetNode) => {\n if (node.type === 'mdxTextExpression' || node.type === 'mdxFlowExpression') {\n const value = (node.value ?? '').trim()\n const start = node.position?.start.offset\n const end = node.position?.end.offset\n const root = value.split('.')[0]\n const safeBuiltinPath = value.includes('.') && SAFE_BUILTIN_ROOTS.has(root)\n const isBareIdentifier = BARE_IDENTIFIER_PATH.test(value) && !JS_LITERAL_KEYWORDS.has(value)\n && !declared.has(root) && !safeBuiltinPath\n const shorthandMatch = value.match(BARE_OBJECT_SHORTHAND)\n const isDoubleBraceTemplate = shorthandMatch !== null && !declared.has(shorthandMatch[1])\n const isIssueList = /^[A-Za-z][A-Za-z0-9]*-\\d+(?:\\s*,\\s*[A-Za-z][A-Za-z0-9]*-\\d+)*$/.test(value)\n if ((isBareIdentifier || isDoubleBraceTemplate || isIssueList) && start !== undefined && end !== undefined) {\n // Escape every literal brace in the matched span rather than\n // rebuilding it from `value`, so this handles both `{name}` and\n // `{{name}}` (and any other brace nesting) the same way.\n const rawSpan = body.slice(start, end)\n edits.push({ start, end, value: rawSpan.replace(/\\\\/g, '\\\\\\\\').replace(/[{}]/g, '\\\\$&') })\n }\n // A leaf node: mdast never gives it further `children` (its JSX, if\n // any, lives only inside `data.estree`), so there is nothing to recurse into.\n return\n }\n for (const child of node.children ?? []) visit(child)\n }\n visit(root)\n if (edits.length === 0) return raw\n\n const escapedBody = edits\n .sort((left, right) => right.start - left.start)\n .reduce((result, edit) => `${result.slice(0, edit.start)}${edit.value}${result.slice(edit.end)}`, body)\n return front + escapedBody\n}\n\n/**\n * Split `raw` into its untouched YAML frontmatter block (including a leading\n * BOM, if present) and the remaining MDX body, using the same delimiter\n * rules as `parseFrontmatter` (BOM-aware, CRLF-safe) so the two always agree\n * on where the frontmatter ends. Unlike `parseFrontmatter`, this returns the\n * frontmatter block verbatim (not parsed) because callers here only need to\n * avoid rewriting it, not read its values.\n */\nfunction splitFrontmatterBlock(raw: string): { front: string; body: string } {\n const hasBom = raw.charCodeAt(0) === 0xfeff\n const bom = hasBom ? raw[0] : ''\n const source = hasBom ? raw.slice(1) : raw\n const opening = /^---([^\\r\\n]*)\\r?\\n/.exec(source)\n if (!opening || opening[1].startsWith('-')) return { front: bom, body: source }\n const remainder = source.slice(opening[0].length)\n const closing = /^---[ \\t]*\\r?$/m.exec(remainder)\n if (!closing) return { front: bom, body: source }\n const end = opening[0].length + closing.index + closing[0].length\n return { front: bom + source.slice(0, end), body: source.slice(end) }\n}\n\n/**\n * `components.ts`'s `createComponentMigrator` renames a successfully copied\n * or extracted component's JSX tag to `Migrated<12-hex-char-hash>` and\n * registers it in the project-wide `src/mdx/custom-components.tsx` (merged\n * into every page by `mdx-components.tsx`, not imported per-page) — so a\n * page's own ESM never mentions it, even though it does resolve. Recognize\n * the naming convention rather than re-deriving it from `components.ts`,\n * which would need this module to depend on that one's private `hash()`.\n */\nconst MIGRATED_COMPONENT_TAG = /^Migrated[0-9a-f]{12}$/\n\n/** True when the JSX span itself (not merely its opening tag) is self-closing, e.g. `<Foo />`. */\nfunction isSelfClosingJsx(text: string): boolean {\n return /\\/>\\s*$/.test(text)\n}\n\n/** Replace the leading `<name` / trailing `</name` occurrences of a tag with `newName`, for a tag with no children to preserve (self-closing, or an empty pair). */\nfunction renameTagOccurrences(text: string, name: string, newName: string): string {\n let result = text\n const openIndex = result.indexOf(`<${name}`)\n if (openIndex >= 0) result = `${result.slice(0, openIndex + 1)}${newName}${result.slice(openIndex + 1 + name.length)}`\n const closeIndex = result.lastIndexOf(`</${name}`)\n if (closeIndex >= 0) result = `${result.slice(0, closeIndex + 2)}${newName}${result.slice(closeIndex + 2 + name.length)}`\n return result\n}\n\n/**\n * Reconstructs `body.slice(loStart, hiEnd)` verbatim except through\n * `renderNode` at each of `children`'s own spans — the gaps between/around\n * them (markdown or JSX syntax the children's own positions don't cover,\n * such as a parent JSX element's attributes) are copied through unchanged.\n */\nfunction renderChildrenSpan(\n children: Array<MdxOffsetNode>,\n loStart: number,\n hiEnd: number,\n body: string,\n renderNode: (node: MdxOffsetNode) => string,\n): string {\n let cursor = loStart\n let out = ''\n for (const child of children) {\n const start = child.position?.start.offset\n const end = child.position?.end.offset\n if (start === undefined || end === undefined) continue\n out += body.slice(cursor, start)\n out += renderNode(child)\n cursor = end\n }\n out += body.slice(cursor, hiEnd)\n return out\n}\n\n/**\n * Replaces a page-authored (or Docusaurus/Fern-only) `<Link href=\"...\">`\n * with a plain `<a href=\"...\">`, keeping every attribute and all children\n * exactly as authored — only the tag name changes, so nested content\n * (including any unknown component the fallback below also rewrites) is\n * preserved. A page that declares or imports its own `Link` keeps it\n * untouched; that binding, not this generic one, is what actually renders.\n */\nexport function replaceLinkWithAnchor(raw: string): string {\n return replaceUnknownComponents(raw, () => {}, { onlyRenameLinkTag: true })\n}\n\n/**\n * Fallback for any capitalized JSX tag Thally cannot render: not a builtin\n * (`isThallyBuiltinComponent`) and not declared or imported by the page\n * itself. Mintlify/Docusaurus content commonly references a project-local or\n * npm component that `components.ts`'s copy step already handles when it can\n * be resolved — this only ever fires for what's left: a name with nothing\n * backing it at all, which would otherwise throw \"Expected component X to be\n * defined\" at render. A paired tag becomes a plain `<div>`, keeping its\n * children (so nested prose/markup survives); a self-closing tag is removed\n * outright, since it has no content to keep. `warn` is called once per\n * distinct component name found. `<Link>` is renamed to `<a>` instead of\n * falling into that generic path, since it's common enough to warrant a\n * real mapping (Mintlify/Docusaurus content routinely writes `<Link\n * href=\"...\">`) rather than losing the link.\n *\n * Frontmatter is split off first (see `escapeFernLiteralBraces`) so a YAML\n * value can never be mistaken for JSX.\n */\nexport function replaceUnknownComponents(\n raw: string,\n warn: (name: string) => void,\n options: { onlyRenameLinkTag?: boolean } = {},\n): string {\n const { front, body } = splitFrontmatterBlock(raw)\n\n let root: MdxOffsetNode\n try {\n root = descriptionParser.parse(body) as MdxOffsetNode\n } catch {\n return raw\n }\n\n const declared = new Set<string>(['props'])\n const collectEsm = (node: MdxOffsetNode) => {\n if (node.type === 'mdxjsEsm') collectEsmBindings(node, declared)\n for (const child of node.children ?? []) collectEsm(child)\n }\n collectEsm(root)\n\n const warned = new Set<string>()\n const renderNode = (node: MdxOffsetNode): string => {\n const start = node.position?.start.offset\n const end = node.position?.end.offset\n if (start === undefined || end === undefined) return ''\n const isJsx = node.type === 'mdxJsxFlowElement' || node.type === 'mdxJsxTextElement'\n const tagRoot = node.name ? node.name.split('.')[0] : undefined\n const text = body.slice(start, end)\n\n if (isJsx && tagRoot === 'Link' && !declared.has('Link')) {\n if (isSelfClosingJsx(text) || !node.children?.length) return renameTagOccurrences(text, node.name!, 'a')\n const openEnd = node.children[0].position?.start.offset ?? start\n const closeStart = node.children.at(-1)!.position?.end.offset ?? end\n return renameTagOccurrences(body.slice(start, openEnd), node.name!, 'a')\n + renderChildrenSpan(node.children, openEnd, closeStart, body, renderNode)\n + renameTagOccurrences(body.slice(closeStart, end), node.name!, 'a')\n }\n\n if (!options.onlyRenameLinkTag && isJsx && tagRoot && /^[A-Z]/.test(tagRoot)\n && !isThallyBuiltinComponent(node.name!) && !declared.has(tagRoot) && !MIGRATED_COMPONENT_TAG.test(tagRoot)) {\n if (!warned.has(node.name!)) {\n warned.add(node.name!)\n warn(node.name!)\n }\n if (isSelfClosingJsx(text)) return ''\n if (!node.children?.length) return '<div></div>'\n const openEnd = node.children[0].position?.start.offset ?? start\n const closeStart = node.children.at(-1)!.position?.end.offset ?? end\n const children = renderChildrenSpan(node.children, openEnd, closeStart, body, renderNode)\n // A flow (block-level) unknown component can wrap block content —\n // a fenced code block, a heading, another flow element — and MDX\n // only recognizes that as block content when it's set off from the\n // surrounding tag by a blank line; gluing it straight onto `<div>`\n // (the previous behavior) reads as inline text, which derails the\n // parser and can surface as an unrelated \"unclosed `<div>`\" error\n // once it hits the real closing tag. The source component's own\n // markup may have had no blank line there at all (a permissive\n // renderer doesn't require one), so always add it rather than\n // trying to detect when it's needed. An inline (text-level) unknown\n // component never contains block content, so it stays glued.\n return node.type === 'mdxJsxFlowElement' ? `<div>\\n\\n${children}\\n\\n</div>` : `<div>${children}</div>`\n }\n\n if (!node.children?.length) return text\n return renderChildrenSpan(node.children, start, end, body, renderNode)\n }\n\n return front + renderNode(root)\n}\n\n/**\n * Replace every fenced code block and inline `code span` in `body` with an\n * opaque single-line placeholder, so a textual rename can run as one pass\n * over the WHOLE body — including a tag or comment that spans multiple\n * lines — without ever matching inside code, then restore the original code\n * text afterward. A rename that only ever sees one line at a time (the old\n * approach) misses anything spanning lines: a `<!--\\n...\\n-->` comment, a\n * `<Warn\\n title=\"x\">` opening tag whose `</Warn>` gets renamed with no\n * matching open, a Docusaurus `<Link\\n to=\"/a\">` likewise. Every rename in\n * this module that isn't already AST-based goes through this one masker\n * instead of re-implementing fence tracking.\n */\nfunction maskCode(body: string): { masked: string; unmask: (text: string) => string; openFence: string | null } {\n // NUL is never valid in MDX; strip it so it can't collide with the \\u0000\n // placeholder markers stashed below.\n const source = body.replace(/\\u0000/g, '')\n\n const blocks: Array<string> = []\n const stash = (text: string): string => {\n blocks.push(text)\n return `\\u0000${blocks.length - 1}\\u0000`\n }\n\n const lines: Array<string> = []\n let codeFence: string | null = null\n let fenceBuffer: Array<string> = []\n // Note: MDX (mdx-js) disables CommonMark indented code blocks, so\n // 4-space-indented text is never code here and is intentionally left\n // unmasked, matching @mdx-js/mdx's own parsing.\n for (const line of source.split('\\n')) {\n const fenceMatch = line.match(/^\\s{0,3}(`{3,}|~{3,})/)\n if (codeFence) {\n fenceBuffer.push(line)\n // A closing fence has no info string. ` ```python` inside an open\n // Markdown fence is content, not a close; treating it as one can mask\n // every later HTML tag and leave React-invalid attributes untouched.\n if (fenceMatch && fenceMatch[1][0] === codeFence[0]\n && fenceMatch[1].length >= codeFence.length\n && /^\\s*$/.test(line.slice(fenceMatch[0].length))) {\n lines.push(stash(fenceBuffer.join('\\n')))\n codeFence = null\n fenceBuffer = []\n }\n continue\n }\n if (fenceMatch) {\n codeFence = fenceMatch[1]\n fenceBuffer = [line]\n continue\n }\n lines.push(line.split(/(`[^`]*`)/).map((segment, index) => (\n index % 2 === 1 ? stash(segment) : segment\n )).join(''))\n }\n // An unterminated fence (malformed source) still must not be rewritten.\n if (fenceBuffer.length) lines.push(stash(fenceBuffer.join('\\n')))\n\n return {\n masked: lines.join('\\n'),\n unmask: (text) => text.replace(/\\u0000(\\d+)\\u0000/g, (_match, index: string) => blocks[Number(index)]),\n openFence: codeFence,\n }\n}\n\n/**\n * Mintlify compiles every snippet as its own MDX file, where a fence left open\n * ends at EOF. Inlined into a page it would swallow the tags that follow, so\n * close it (same character, same length) on its own line. Balanced input is\n * returned unchanged.\n */\nexport function closeOpenFence(body: string): string {\n const { openFence } = maskCode(body)\n return openFence ? `${body}${body.endsWith('\\n') ? '' : '\\n'}${openFence}\\n` : body\n}\n\n/**\n * Apply a textual rename to the WHOLE body outside fenced/inline code (via\n * `maskCode`), so a match may span multiple lines.\n */\nexport function replaceOutsideCode(body: string, transform: (whole: string) => string): string {\n const { masked, unmask } = maskCode(body)\n return unmask(transform(masked))\n}\n\n/**\n * The one way the migrator emits an MDX comment. A `*\\/` inside `text` would\n * close the comment early, so interpolated paths and source text are defused.\n */\nexport function mdxComment(text: string): string {\n return `{/*${text.replace(/\\*\\//g, '* /')}*/}`\n}\n\n/**\n * `replaceOutsideCode` that also leaves existing `{/* ... *\\/}` comments alone,\n * so a rewrite cannot nest (and thereby corrupt) a comment inside another one.\n */\nexport function replaceOutsideCodeAndComments(body: string, transform: (whole: string) => string): string {\n return replaceOutsideCode(body, (masked) => {\n // Equivalent to splitting on /\\{\\s*\\/\\*[\\s\\S]*?\\*\\/\\s*\\}/ but linear: each `*/` that is followed by `\\s*}` is\n // found once, and every opener takes the first such end after its own `/*`.\n const ends: Array<number> = []\n const closer = /\\*\\/\\s*\\}/y\n for (let at = masked.indexOf('*/'); at !== -1; at = masked.indexOf('*/', at + 1)) {\n closer.lastIndex = at\n if (closer.test(masked)) ends.push(at)\n }\n const opener = /\\{\\s*\\/\\*/y\n const out: Array<string> = []\n let last = 0\n let next = 0\n for (let start = masked.indexOf('{'); start !== -1; start = masked.indexOf('{', start + 1)) {\n opener.lastIndex = start\n if (!opener.test(masked)) continue\n while (next < ends.length && ends[next] < opener.lastIndex) next++\n if (next === ends.length) break\n const end = /\\*\\/\\s*\\}/y\n end.lastIndex = ends[next]\n end.test(masked)\n out.push(transform(masked.slice(last, start)), masked.slice(start, end.lastIndex))\n last = end.lastIndex\n start = last - 1\n }\n out.push(transform(masked.slice(last)))\n return out.join('')\n })\n}\n\n/**\n * Fern authors often link sibling MDX files from Card props and Markdown.\n * Resolve only links to files that actually became pages; unknown paths,\n * external URLs, assets, and examples retain their authored destinations.\n */\nexport function rewriteFernRelativePageLinks(\n body: string,\n currentSourcePath: string,\n routesBySourcePath: ReadonlyMap<string, string>,\n): string {\n const rewrite = (target: string): string => {\n if (!target || target.startsWith('/') || target.startsWith('#') || target.startsWith('?')\n || target.includes('\\\\') || /[\\u0000-\\u001f\\u007f]/.test(target)\n || /^[A-Za-z][A-Za-z\\d+.-]*:/.test(target)) return target\n const suffixAt = target.search(/[?#]/)\n const pathname = suffixAt < 0 ? target : target.slice(0, suffixAt)\n const suffix = suffixAt < 0 ? '' : target.slice(suffixAt)\n if (!pathname || pathname.startsWith('//') || pathname.split('/').some((part) => /%2f|%5c/i.test(part))) return target\n const source = posix.normalize(posix.join(posix.dirname(currentSourcePath.replace(/\\\\/g, '/')), pathname))\n const candidates = /\\.mdx?$/i.test(source) ? [source] : [source, `${source}.mdx`, `${source}.md`, `${source}/index.mdx`, `${source}/index.md`]\n const route = candidates.map((candidate) => routesBySourcePath.get(candidate)).find(Boolean)\n return route ? `/${route.replace(/^\\/+/, '')}${suffix}` : target\n }\n return replaceOutsideCode(body, (text) => text\n .replace(/(\\]\\()(<[^>\\n]+>|[^\\s)]+)(?=\\s|\\))/g,\n (_match, opening: string, target: string) => `${opening}${target.startsWith('<') && target.endsWith('>') ? `<${rewrite(target.slice(1, -1))}>` : rewrite(target)}`)\n .replace(/(\\b(?:href|to)\\s*=\\s*)([\"'])([^\"'\\n]+)\\2/g,\n (_match, opening: string, quote: string, target: string) => `${opening}${quote}${rewrite(target)}${quote}`)\n .replace(/(\\b(?:href|to)\\s*=\\s*\\{\\s*)([\"'])([^\"'\\n]+)\\2(?=\\s*\\})/g,\n (_match, opening: string, quote: string, target: string) => `${opening}${quote}${rewrite(target)}${quote}`))\n}\n\n/** Convert Fern's indented file-list shorthand to Thally's nested Tree nodes. */\nfunction normalizeFernFileTrees(body: string): string {\n return replaceOutsideCode(body, (whole) => whole.replace(/<FileTree\\s*>\\s*\\n([\\s\\S]*?)\\n\\s*<\\/FileTree>/g,\n (original: string, contents: string) => {\n const lines = contents.split(/\\r?\\n/).filter((line) => line.trim())\n if (!lines.length) return original\n const nodes: Array<{ indent: number; name: string; isFolder: boolean }> = []\n for (const line of lines) {\n const match = /^( *)(?:-|\\*|\\+)\\s+(.+?)\\s*$/.exec(line)\n if (!match || match[1].length % 2 !== 0 || match[2].includes('\\u0000')) return original\n nodes.push({ indent: match[1].length / 2, name: match[2], isFolder: match[2].endsWith('/') })\n }\n const output = ['<Tree>']\n const folders: Array<number> = []\n for (const node of nodes) {\n while (folders.length > node.indent) {\n output.push('</Folder>')\n folders.pop()\n }\n // A child without a parent folder is not a reliable tree. Retain the\n // Fern source so migration validation can report it explicitly.\n if (node.indent > folders.length || (node.indent > 0 && folders.length === 0)) return original\n const name = `{${JSON.stringify(node.name)}}`\n if (node.isFolder) {\n output.push(`<Folder name=${name} defaultOpen>`)\n folders.push(node.indent)\n } else {\n output.push(`<File name=${name} />`)\n }\n }\n while (folders.length) {\n output.push('</Folder>')\n folders.pop()\n }\n output.push('</Tree>')\n return output.join('\\n')\n }))\n}\n\n/**\n * Converts an HTML comment (`<!-- text -->`) to MDX's own comment syntax\n * (`{/* text *\\/}`), skipping fenced/inline code. remark-mdx does not parse\n * `<!-- -->` at all — a real Markdown/Docusaurus source commonly has one\n * (`<!-- prettier-ignore -->` ahead of a snippet import is a common\n * Docusaurus pattern) and it otherwise throws \"Unexpected character `!`\"\n * wherever something needs a real MDX parse of the page — not just this\n * module's `normalizeMdx`, but `components.ts`'s independent component\n * analysis too, before that pass ever gets a chance to run.\n */\nexport function normalizeHtmlComments(body: string): string {\n return replaceOutsideCode(body, (whole) => whole.replace(/<!--([\\s\\S]*?)-->/g, (_match, comment: string) => mdxComment(comment)))\n}\n\n/**\n * Rewrite Fern's callout intents to Thally's fixed callout tags without\n * touching fenced code. Delimiters are tracked with a stack so nested and\n * sibling callouts each close with the tag their own opening intent chose.\n * A single scan is required: collecting every opener before visiting any\n * closer incorrectly pairs sibling and nested callouts.\n */\nfunction normalizeFernCallouts(body: string): string {\n return replaceOutsideCode(body, (whole) => {\n const openTags: Array<string | null> = []\n const output: string[] = []\n let cursor = 0\n while (cursor < whole.length) {\n const opening = whole.indexOf('<Callout', cursor)\n const closing = whole.indexOf('</Callout>', cursor)\n const next = opening < 0 ? closing : closing < 0 ? opening : Math.min(opening, closing)\n if (next < 0) break\n output.push(whole.slice(cursor, next))\n if (next === closing) {\n const tag = openTags.pop()\n output.push(tag ? `</${tag}>` : '</Callout>')\n cursor = next + '</Callout>'.length\n continue\n }\n const attributeStart = next + '<Callout'.length\n if (!/[\\s/>]/.test(whole[attributeStart] ?? '')) {\n output.push('<Callout')\n cursor = attributeStart\n continue\n }\n // JSX attributes may contain quoted `>` or expression comparisons.\n // Locate the delimiter in one pass instead of using a backtracking\n // regex over arbitrary source content.\n let end = attributeStart\n let quote: string | null = null\n let expressionDepth = 0\n for (; end < whole.length; end++) {\n const character = whole[end]\n if (quote) {\n if (character === quote && whole[end - 1] !== '\\\\') quote = null\n } else if (character === '\"' || character === \"'\") {\n quote = character\n } else if (character === '{') {\n expressionDepth++\n } else if (character === '}' && expressionDepth > 0) {\n expressionDepth--\n } else if (character === '>' && expressionDepth === 0) {\n break\n }\n }\n if (end === whole.length) {\n output.push(whole.slice(next))\n cursor = whole.length\n break\n }\n const attributes = whole.slice(attributeStart, end)\n const intent = findStaticCalloutIntent(attributes)\n const isSelfClosing = attributes.trimEnd().endsWith('/')\n if (!intent) {\n output.push(whole.slice(next, end + 1))\n if (!isSelfClosing) openTags.push(null)\n } else {\n const value = intent.value.toLowerCase()\n const tag = value === 'warning' ? 'Warning'\n : value === 'success' || value === 'tip' ? 'Tip'\n : value === 'error' || value === 'danger' ? 'Error' : 'Note'\n output.push(`<${tag}${attributes.slice(0, intent.start)}${attributes.slice(intent.end)}>`)\n if (!isSelfClosing) openTags.push(tag)\n }\n cursor = end + 1\n }\n output.push(whole.slice(cursor))\n return output.join('')\n })\n}\n\n/** Find only a static, quoted intent attribute; preserve every other prop. */\nfunction findStaticCalloutIntent(attributes: string): { start: number; end: number; value: string } | null {\n let cursor = 0\n while (cursor < attributes.length) {\n const start = cursor\n while (/\\s/.test(attributes[cursor] ?? '')) cursor++\n if (cursor === start) { cursor++; continue }\n const nameStart = cursor\n while (/[A-Za-z0-9_-]/.test(attributes[cursor] ?? '')) cursor++\n const name = attributes.slice(nameStart, cursor)\n while (/\\s/.test(attributes[cursor] ?? '')) cursor++\n if (attributes[cursor] !== '=') continue\n cursor++\n while (/\\s/.test(attributes[cursor] ?? '')) cursor++\n const quote = attributes[cursor]\n if (quote !== '\"' && quote !== \"'\") {\n // Other props may be JSX expressions. Skip balanced braces so a\n // string inside an expression cannot masquerade as an attribute.\n if (quote === '{') {\n let depth = 0\n do {\n if (attributes[cursor] === '{') depth++\n if (attributes[cursor] === '}') depth--\n cursor++\n } while (cursor < attributes.length && depth > 0)\n } else {\n while (cursor < attributes.length && !/\\s/.test(attributes[cursor])) cursor++\n }\n continue\n }\n const valueStart = ++cursor\n while (cursor < attributes.length && attributes[cursor] !== quote) cursor++\n const value = attributes.slice(valueStart, cursor)\n if (cursor < attributes.length) cursor++\n if (name === 'intent') return { start, end: cursor, value }\n }\n return null\n}\n\n/**\n * Unwrap Fern's per-tab `<CodeBlock>` only while inside a `<CodeGroup>`\n * (renamed from Fern's `<CodeBlocks>` earlier in the pipeline). Mintlify also\n * ships a genuine, globally available `<CodeBlock>` component for\n * programmatic rendering inside custom React components — that usage never\n * nests inside a `<CodeGroup>`, so it survives untouched.\n */\nfunction unwrapFernCodeBlockTabs(body: string): string {\n return replaceOutsideCode(body, (whole) => {\n let groupDepth = 0\n return whole.replace(/<CodeGroup\\b[^>]*>|<\\/CodeGroup>|<CodeBlock\\b[^>]*>|<\\/CodeBlock>/g, (match) => {\n if (match.startsWith('<CodeGroup')) {\n groupDepth++\n return match\n }\n if (match === '</CodeGroup>') {\n groupDepth = Math.max(0, groupDepth - 1)\n return match\n }\n return groupDepth > 0 ? '' : match\n })\n })\n}\n\n// TODO: a plain <pre>/<code> shim, not Thally's styled Pre/CodeGroup —\n// upgrade to a registered `CodeBlock` MDX built-in (mirroring Code/CodeGroup)\n// once the scaffold template ships one.\nconst STANDALONE_CODE_BLOCK_SHIM = [\n 'export const CodeBlock = ({ children, filename, ...rest }) => (',\n ' <pre {...rest}>',\n ' {filename ? <div>{filename}</div> : null}',\n ' <code>{children}</code>',\n ' </pre>',\n ');',\n].join('\\n')\n\n/** Extracts the source text of each top-level `export const X = (...) => (…|{…` body. */\nfunction exportDeclarationBodies(body: string): Array<string> {\n const bodies: Array<string> = []\n const opener = /export const \\w+ = [^\\n]*=> ([({])/g\n for (const match of body.matchAll(opener)) {\n const open = match[1]\n const close = open === '(' ? ')' : '}'\n let depth = 1\n let i = (match.index ?? 0) + match[0].length\n while (i < body.length && depth > 0) {\n if (body[i] === open) depth++\n else if (body[i] === close) depth--\n i++\n }\n bodies.push(body.slice((match.index ?? 0) + match[0].length, i))\n }\n return bodies\n}\n\n/**\n * True when `export const NAME = ` (ending right at `sourceIndex`) assigns a\n * real function expression — `function` or an arrow function — rather than a\n * plain value whose initializer merely contains `=>` further in, the way\n * `list.map((x) => x)` does. Only a genuine function reference crosses the\n * server/client boundary as a prop; a value like `items` above does not.\n */\nexport function isFunctionInitializer(source: string, sourceIndex: number): boolean {\n let index = sourceIndex\n const skipSpace = () => { while (/\\s/.test(source[index] ?? '')) index++ }\n // Keywords must end at a word boundary: `functionList`, `asyncMode`, and\n // `classNames` are ordinary identifiers, not function expressions.\n const keyword = (word: string) => new RegExp(`^${word}(?![\\\\w$])`).test(source.slice(index))\n skipSpace()\n if (keyword('async')) {\n index += 'async'.length\n skipSpace()\n // `async => x` is an arrow whose parameter is named `async`.\n if (source.startsWith('=>', index)) return true\n }\n if (keyword('function') || keyword('class')) return true\n if (source[index] === '(') {\n let depth = 0\n for (; index < source.length; index++) {\n if (source[index] === '(') depth++\n else if (source[index] === ')') {\n depth--\n if (depth === 0) {\n index++\n break\n }\n }\n }\n skipSpace()\n return source.startsWith('=>', index)\n }\n const identifier = /^[A-Za-z_$][\\w$]*/.exec(source.slice(index))\n if (!identifier) return false\n index += identifier[0].length\n skipSpace()\n return source.startsWith('=>', index)\n}\n\n/**\n * Detects a page-authored function (`export const Name = () => ...`) passed\n * as a bare JSX prop value (`someProp={Name}`) elsewhere on the same page.\n * Next renders an MDX page as a Server Component by default; an extracted\n * interactive snippet it imports is `'use client'` (see `components.ts`).\n * Passing a plain function across that boundary as a prop throws at render\n * (\"Functions cannot be passed directly to Client Components\") even though\n * the MDX itself compiles cleanly — Mintlify's own renderer has no such\n * server/client split, so this content is valid there. Thally has no import\n * mechanism that moves the declaration into the client module instead, so\n * the page is reported and excluded rather than shipped broken.\n *\n * Two things keep this from over-firing: fenced/inline code (a doc example\n * showing this exact shape) never counts, via `maskCode`; and only a real\n * function initializer counts as \"a function\" — `export const items =\n * list.map((x) => x)` is a plain value, not one, even though its own\n * initializer happens to contain `=>`.\n */\nexport function hasClientBoundaryFunctionProp(body: string): boolean {\n const { masked } = maskCode(body)\n const declared = [...functionDeclaredNames(body)]\n return declared.some((name) => new RegExp(`\\\\w+=\\\\{${name}\\\\}`).test(masked))\n}\n\n/**\n * A page's top-level `export` identifiers that are actually functions — a\n * `function`/`async function` declaration, or an `export const NAME = `\n * whose initializer is a real function expression (see\n * `isFunctionInitializer`). Excludes plain values (`export const diagram =\n * 'graph TD'`), even when their initializer text happens to contain `=>`\n * further in (`export const items = list.map((x) => x)`). Callers that need\n * to know whether a prop value crosses the server/client boundary as a\n * function must check against this set, not every `export const` name —\n * confusing the two would exclude pages that merely pass a string or object\n * to a client component.\n */\nexport function functionDeclaredNames(body: string): Set<string> {\n const { masked } = maskCode(body)\n const names = new Set<string>()\n for (const match of masked.matchAll(/export const (\\w+)\\s*=\\s*/g)) {\n if (isFunctionInitializer(masked, (match.index ?? 0) + match[0].length)) names.add(match[1])\n }\n // Function declarations (incl. `default`, `async`, generators) and classes:\n // a class is a constructor function and is just as unserializable.\n for (const match of masked.matchAll(/export\\s+(?:default\\s+)?(?:async\\s+)?(?:function\\s*\\*?\\s*|class\\s+)(\\w+)/g)) {\n names.add(match[1])\n }\n // One level of aliasing: `export const Alias = Demo` where `Demo` is one.\n for (const match of masked.matchAll(/^export const (\\w+)\\s*=\\s*(\\w+)\\s*;?\\s*$/gm)) {\n if (names.has(match[2])) names.add(match[1])\n }\n return names\n}\n\n/**\n * A page's own `export const` declarations sometimes reference a Mintlify\n * global built-in by bare JS identifier (e.g. `<CodeBlock>` or `<Icon>`\n * inside a custom render function) instead of through prose, where Thally's\n * MDX registry wires it in automatically via `_components`. A bare reference\n * with nothing importing or declaring it throws `ReferenceError` at render.\n * Provide it the same way the source page would have gotten it implicitly:\n * a real import where Thally ships an equivalent, otherwise a minimal\n * same-file shim (see `STANDALONE_CODE_BLOCK_SHIM`).\n */\nfunction provideScopedGlobalReferences(body: string): string {\n const alreadyBound = (name: string): boolean => new RegExp(`\\\\b${name}\\\\s*[=:]|\\\\bimport\\\\s+.*\\\\b${name}\\\\b`).test(body)\n const referencedIn = exportDeclarationBodies(body).join('\\n')\n\n let result = body\n if (/<Icon\\b/.test(referencedIn) && !alreadyBound('Icon')) {\n result = `import { Icon } from '@/components/mdx/content-icon';\\n\\n${result}`\n }\n if (/<CodeBlock\\b/.test(referencedIn) && !alreadyBound('CodeBlock')) {\n result = `${STANDALONE_CODE_BLOCK_SHIM}\\n\\n${result}`\n }\n return result\n}\n\n/**\n * Docusaurus authors a `<Tabs>` block's human labels once, in its own\n * `values={[{ label, value }, ...]}` prop, and each `<TabItem value=\"...\">`\n * repeats only the machine `value`. Thally's `<Tab title=\"...\">` has no\n * separate values table, so each tab's title must be resolved from the\n * parent's `values` entry (a `TabItem`'s own `label` still wins when given).\n * Docusaurus-only props that Thally's `<Tabs>` does not read are dropped so\n * they don't linger as dead JSX attributes on the migrated component.\n */\nfunction normalizeDocusaurusTabs(body: string): string {\n let result = body\n for (let depth = 0; depth < 20; depth += 1) {\n const updated = result.replace(/<Tabs\\b([^>]*)>((?:(?!<Tabs\\b)[\\s\\S])*?)<\\/Tabs>/g, (_match, attributes: string, inner: string) => {\n const labelByValue = new Map<string, string>()\n const valuesSource = attributes.match(/\\bvalues=\\{(\\[[\\s\\S]*?\\])\\s*\\}/)?.[1]\n if (valuesSource) {\n for (const entry of valuesSource.matchAll(/\\{([^{}]*)\\}/g)) {\n const label = entry[1].match(/\\blabel:\\s*(?:\"([^\"]*)\"|'([^']*)')/)?.slice(1).find(Boolean)\n const value = entry[1].match(/\\bvalue:\\s*(?:\"([^\"]*)\"|'([^']*)')/)?.slice(1).find(Boolean)\n if (value) labelByValue.set(value, label ?? value)\n }\n }\n const cleanedAttributes = attributes\n .replace(/\\s*defaultValue=(?:\\{[^}]*\\}|\"[^\"]*\"|'[^']*')/g, '')\n .replace(/\\s*values=\\{[\\s\\S]*?\\]\\s*\\}/g, '')\n .replace(/\\s*values=\\{[^{}]*\\}/g, '')\n .replace(/\\s*groupId=(?:\"[^\"]*\"|'[^']*')/g, '')\n .replace(/\\s*queryString(?:=(?:\\{[^}]*\\}|\"[^\"]*\"|'[^']*'))?/g, '')\n const rewrittenInner = inner\n .replace(/<TabItem\\b([^>]*)>/g, (_itemMatch, itemAttributes: string) => {\n const label = itemAttributes.match(/\\blabel=(?:\"([^\"]*)\"|'([^']*)')/)?.slice(1).find(Boolean)\n const value = itemAttributes.match(/\\bvalue=(?:\"([^\"]*)\"|'([^']*)')/)?.slice(1).find(Boolean)\n const title = label ?? (value && labelByValue.get(value)) ?? value ?? 'Tab'\n return `<Tab title=\"${title.replace(/\"/g, '"')}\">`\n })\n .replace(/<\\/TabItem>/g, '</Tab>')\n return `<ThallyTabs${cleanedAttributes}>${rewrittenInner}</ThallyTabs>`\n })\n if (updated === result) break\n result = updated\n }\n return result.replace(/<ThallyTabs\\b/g, '<Tabs').replace(/<\\/ThallyTabs>/g, '</Tabs>')\n}\n\nconst TAB_FENCE_OPEN = /^(`{3,}|~{3,})(\\S*)\\s+tab\\b(.*)$/\n\n/**\n * `docusaurus-remark-plugin-tab-blocks` marks each fence in a tab group with\n * a `tab` meta keyword (```` ```js tab title=\"npm\" ````). Thally has no\n * matching remark plugin, but its `<CodeGroup>` already reads the same\n * `title=` fence attribute for tab labels, so consecutive tab-marked fences\n * are wrapped in one and the now-meaningless `tab` keyword is dropped. Only\n * fences that opt in via that keyword are touched — this never fires\n * speculatively on an ordinary fence.\n */\nfunction normalizeDocusaurusTabBlocks(body: string): string {\n const lines = body.split('\\n')\n const output: Array<string> = []\n let index = 0\n while (index < lines.length) {\n const open = lines[index].match(TAB_FENCE_OPEN)\n if (!open) {\n output.push(lines[index])\n index++\n continue\n }\n const blocks: Array<Array<string>> = []\n while (index < lines.length) {\n const start = lines[index].match(TAB_FENCE_OPEN)\n if (!start) break\n const marker = start[1]\n const block = [`${start[1]}${start[2]}${start[3]}`]\n index++\n while (index < lines.length && lines[index].trim() !== marker) {\n block.push(lines[index])\n index++\n }\n block.push(lines[index] ?? marker)\n index++\n blocks.push(block)\n let lookahead = index\n while (lookahead < lines.length && lines[lookahead].trim() === '') lookahead++\n if (!lines[lookahead] || !TAB_FENCE_OPEN.test(lines[lookahead])) break\n index = lookahead\n }\n if (blocks.length > 1) {\n output.push('<CodeGroup>', '')\n for (const block of blocks) output.push(...block, '')\n output.pop()\n output.push('</CodeGroup>')\n } else {\n output.push(...blocks[0])\n }\n }\n return output.join('\\n')\n}\n\n/**\n * Split a CSS declaration list on top-level `;` only — a `;` inside\n * `url(...)` (a data URI commonly contains one) must not end the\n * declaration early.\n */\nfunction splitCssDeclarations(value: string): Array<string> {\n const parts: Array<string> = []\n let depth = 0\n let current = ''\n for (const char of value) {\n if (char === '(') depth++\n else if (char === ')') depth = Math.max(0, depth - 1)\n if (char === ';' && depth === 0) {\n parts.push(current)\n current = ''\n } else {\n current += char\n }\n }\n if (current.trim()) parts.push(current)\n return parts\n}\n\n/**\n * Convert a CSS property name to the key a JSX style object expects:\n * camelCase, vendor prefixes capitalized (`-webkit-transform` ->\n * `WebkitTransform`) except React's own `-ms-` quirk (`msTransform`, not\n * `MsTransform`), and a custom property (`--x`) left as a literal string key.\n */\nfunction cssPropertyToJsKey(property: string): string {\n if (property.startsWith('--')) return JSON.stringify(property)\n const camel = property.replace(/-([a-z])/g, (_match, letter: string) => letter.toUpperCase())\n return camel.startsWith('Ms') ? camel.slice(0, 1).toLowerCase() + camel.slice(1) : camel\n}\n\n/**\n * Convert an HTML `style=\"...\"` attribute's raw CSS text into the object\n * literal a JSX `style={{...}}` prop requires.\n *\n * TODO: `!important` cannot be expressed in a React style object at all\n * (there is no per-declaration escape hatch), so it is dropped rather than\n * left in a value string where it would do nothing; upgrade to a `!` layer\n * of inline `<style>` injection if a real page ever needs it.\n */\nfunction cssTextToStyleObjectLiteral(cssText: string): string {\n const entries = splitCssDeclarations(cssText).flatMap((declaration) => {\n const colon = declaration.indexOf(':')\n if (colon === -1) return []\n const property = declaration.slice(0, colon).trim()\n const value = declaration.slice(colon + 1).replace(/\\s*!\\s*important\\s*$/i, '').trim()\n if (!property || !value || !/^-{0,2}[a-zA-Z][a-zA-Z0-9-]*$/.test(property)) return []\n return [`${cssPropertyToJsKey(property)}: ${JSON.stringify(value)}`]\n })\n return `{${entries.join(', ')}}`\n}\n\n/**\n * Rewrite a string `style=\"...\"`/`style='...'` attribute on a lowercase\n * (intrinsic HTML) JSX element into `style={{...}}`. JSX (unlike HTML)\n * requires `style` to be a mapping from property to value; a raw string —\n * common in Markdown/MDX pasted from HTML, e.g. a copied DataFrame table —\n * throws at render instead of just being ignored, so this must run\n * unconditionally, not only for a known source platform. An already-correct\n * `style={...}` expression is untouched (the match requires a quote right\n * after `=`), as is any component tag (uppercase-first is never matched).\n */\nfunction convertHtmlStyleAttributes(body: string): string {\n return body.replace(\n // The separator before `style=` must be `\\s+`, not a single `\\s`: a\n // multi-line tag indents its attributes, so more than one whitespace\n // character (a newline plus spaces) commonly precedes it.\n /<([a-z][a-zA-Z0-9]*)((?:\\s+[^\\s\"'=<>/]+(?:=(?:\"[^\"]*\"|'[^']*'))?)*)\\s+style=([\"'])([\\s\\S]*?)\\3((?:\\s+[^\\s\"'=<>/]+(?:=(?:\"[^\"]*\"|'[^']*'))?)*)\\s*(\\/?)>/g,\n (_match, tag: string, before: string, _quote: string, styleText: string, after: string, selfClose: string) => (\n `<${tag}${before} style={${cssTextToStyleObjectLiteral(styleText)}}${after}${selfClose ? ' /' : ''}>`\n ),\n )\n}\n\n/** Mintlify allows presentational JSX in Update labels; Thally uses the label as an anchor id. */\nfunction normalizeMintlifyUpdateLabels(body: string): string {\n return body.replace(/(<Update\\b[^>]*\\blabel=)\\{(<\\>[\\s\\S]*?<\\/\\>)\\}/g, (match, prefix: string, fragment: string) => {\n let root: DescriptionNode\n try {\n root = descriptionParser.parse(fragment) as DescriptionNode\n } catch {\n return match\n }\n const plainText = (node: DescriptionNode): string | null => {\n // Preserve only parsed text nodes. Dropping markup with a regex can\n // leave a partial tag in the output when an attribute contains `>`.\n if (node.type === 'text' || node.type === 'inlineCode') return node.value ?? ''\n if (node.type === 'mdxTextExpression' || node.type === 'mdxFlowExpression' || node.type === 'mdxjsEsm') return null\n const parts = (node.children ?? []).map(plainText)\n return parts.some((part) => part === null) ? null : parts.join('')\n }\n const label = plainText(root)?.replace(/\\s+/g, ' ').trim()\n // Dynamic labels cannot be flattened safely; preserve their source form.\n return label && !/[{}]/.test(label) ? `${prefix}{${JSON.stringify(label)}}` : match\n })\n}\n\n/** Preserve Mintlify's generated `param-*` links on authored settings fields. */\nfunction normalizeMintlifyParameterAnchors(body: string): string {\n const seen = new Set<string>()\n const anchorFor = (name: string): string => {\n const id = `param-${name.toLowerCase().replace(/[._]/g, '-')}`\n if (seen.has(id)) return ''\n seen.add(id)\n return `<a id=\"${id}\"></a>\\n`\n }\n // `replaceOutsideCode` masks inline backticks as well as fenced code, so\n // scan heading lines separately to retain the authored setting name.\n let fence: string | undefined\n const withHeadings = body.split('\\n').map((line) => {\n const marker = line.match(/^ {0,3}(`{3,}|~{3,})/)\n if (marker) {\n if (!fence) fence = marker[1]\n else if (marker[1][0] === fence[0] && marker[1].length >= fence.length\n && /^\\s*$/.test(line.slice(marker[0].length))) fence = undefined\n return line\n }\n if (fence) return line\n const heading = line.match(/^ {0,3}#{1,6}\\s+`([A-Za-z][A-Za-z0-9._-]*)`(?:\\s|$)/)\n return heading ? `${anchorFor(heading[1])}${line}` : line\n }).join('\\n')\n return replaceOutsideCode(withHeadings, (segment) => segment\n .replace(/<(ResponseField|ParamField)\\b([^>]*?)\\bname=(?:\"([A-Za-z][A-Za-z0-9._-]*)\"|'([A-Za-z][A-Za-z0-9._-]*)')([^>]*)>/g,\n (match: string, _tag: string, _before: string, doubleQuoted: string, singleQuoted: string) =>\n `${anchorFor(doubleQuoted ?? singleQuoted)}${match}`))\n}\n\ninterface FenceLine { index: number; indent: string; char: string; length: number; info: string; rest: string }\n\nfunction matchFenceLine(line: string): FenceLine | null {\n const match = line.match(/^(\\s*)(`{3,}|~{3,})(.*)$/)\n if (!match) return null\n return { index: -1, indent: match[1], char: match[2][0], length: match[2].length, info: match[3].trim(), rest: match[3] }\n}\n\n/**\n * Widen an outer fence (for example a ```mdx block demonstrating authored\n * Markdown) whose content contains a same-or-greater-length nested fence.\n * CommonMark has no concept of fence nesting, so an inner fence of matching\n * length closes the outer one early and strands the rest as raw JSX/markdown.\n * A nested fence is identified by carrying an info string; a bare fence line\n * always closes the innermost open block, matching authoring convention.\n */\nfunction normalizeNestedCodeFences(body: string): string {\n const lines = body.split('\\n')\n const fences = lines\n .map((line, index) => {\n const fence = matchFenceLine(line)\n return fence ? { ...fence, index } : null\n })\n .filter((fence): fence is FenceLine => fence !== null)\n if (fences.length === 0) return body\n\n interface Block { open: FenceLine; close: FenceLine; children: Array<Block> }\n const stack: Array<{ open: FenceLine; children: Array<Block> }> = []\n const roots: Array<Block> = []\n for (const fence of fences) {\n const top = stack.at(-1)\n if (top && fence.info === '' && fence.char === top.open.char && fence.length >= top.open.length) {\n stack.pop()\n const block: Block = { open: top.open, close: fence, children: top.children }\n const parent = stack.at(-1)\n if (parent) parent.children.push(block)\n else roots.push(block)\n } else {\n stack.push({ open: fence, children: [] })\n }\n }\n // An unresolved stack means the fences do not actually nest; leave the\n // source untouched rather than guess at intent.\n if (stack.length > 0) return body\n\n const widenedLengths = new Map<number, number>()\n function requiredLength(block: Block): number {\n const deepestChild = Math.max(0, ...block.children.map(requiredLength))\n const length = Math.max(block.open.length, deepestChild > 0 ? deepestChild + 1 : 0)\n widenedLengths.set(block.open.index, length)\n widenedLengths.set(block.close.index, length)\n return length\n }\n roots.forEach(requiredLength)\n\n return lines\n .map((line, index) => {\n const targetLength = widenedLengths.get(index)\n const fence = targetLength ? matchFenceLine(line) : null\n if (!targetLength || !fence || targetLength <= fence.length) return line\n return `${fence.indent}${fence.char.repeat(targetLength)}${fence.rest}`\n })\n .join('\\n')\n}\n\n/** Replace fenced code blocks and inline code spans with same-length filler. */\nfunction maskCodeRegions(body: string): string {\n let inFence = false\n return body.split('\\n').map((line) => {\n if (/^\\s*(`{3,}|~{3,})/.test(line)) {\n inFence = !inFence\n return ' '.repeat(line.length)\n }\n if (inFence) return ' '.repeat(line.length)\n return line.replace(/`[^`]*`/g, (span) => ' '.repeat(span.length))\n }).join('\\n')\n}\n\n/**\n * Read a JSX tag's attributes from `from` (just after the tag name) to its\n * closing `>`, ignoring any `>` inside quotes or `{...}` expressions. Linear.\n * Returns the index after `>` and whether the tag self-closes, or null when it\n * is not a well-formed tag (a bare `<`, or no end). Returns UNTERMINATED when\n * an open brace/quote runs to end of input: callers stop scanning, since every\n * later opener would rescan the same tail (quadratic on `'<a {'.repeat(n)`).\n */\nconst UNTERMINATED = 'unterminated'\nfunction readTagEnd(text: string, from: number): { end: number; selfClosing: boolean; attributes: string } | null | typeof UNTERMINATED {\n if (from < text.length && !/[\\s/>]/.test(text[from])) return null\n let depth = 0\n let quote = ''\n for (let i = from; i < text.length; i++) {\n const char = text[i]\n if (quote) {\n if (char === '\\\\' && depth > 0) i++\n else if (char === quote) quote = ''\n } else if (char === '\"' || char === \"'\" || (char === '`' && depth > 0)) {\n quote = char\n } else if (char === '{') {\n depth++\n } else if (char === '}') {\n depth = Math.max(0, depth - 1)\n } else if (depth === 0) {\n if (char === '<') return null\n if (char === '>') {\n const selfClosing = text[i - 1] === '/' && i > from\n return { end: i + 1, selfClosing, attributes: text.slice(from, selfClosing ? i - 1 : i) }\n }\n }\n }\n return quote || depth > 0 ? UNTERMINATED : null\n}\n\n/** Add `titleSize=\"p\"` to every `<Steps>` opener (also `<Steps />`) that lacks one. */\nfunction pinStepsTitleSize(body: string): string {\n let result = ''\n let cursor = 0\n for (const match of body.matchAll(/<Steps(?=[\\s/>])/g)) {\n if (match.index < cursor) continue\n const tag = readTagEnd(body, match.index + match[0].length)\n if (tag === UNTERMINATED) break\n if (!tag) continue\n const attributesEnd = tag.end - (tag.selfClosing ? 2 : 1)\n if (/\\btitleSize\\s*=/.test(tag.attributes)) continue\n result += `${body.slice(cursor, attributesEnd).trimEnd()} titleSize=\"p\"${tag.selfClosing ? ' /' : ''}>`\n cursor = tag.end\n }\n return result + body.slice(cursor)\n}\n\n/**\n * Escape closing tags that have no matching real opener (outside code), e.g.\n * a generator that wrote `\\<Warning>` ... `</Warning>`: the escaped opener is\n * literal text, so the bare closer is a compile error. Escaping the closer the\n * same way keeps the page. Linear: one regex pass with a per-name open count.\n * Returns the 1-based line numbers repaired; `body` is returned unchanged when\n * there are none.\n */\nexport function escapeUnmatchedClosingTags(body: string): { body: string; lines: Array<number> } {\n const masked = maskCodeRegions(body)\n const tagPattern = /(\\\\?)<(\\/?)([A-Za-z][\\w.:-]*)/g\n const open = new Map<string, number>()\n const edits: Array<number> = []\n for (let match = tagPattern.exec(masked); match; match = tagPattern.exec(masked)) {\n const [whole, escaped, closing, name] = match\n const tag = readTagEnd(masked, match.index + whole.length)\n if (tag === UNTERMINATED) break\n if (!tag) continue\n tagPattern.lastIndex = tag.end\n if (escaped || tag.selfClosing) continue\n if (!closing) {\n open.set(name, (open.get(name) ?? 0) + 1)\n } else if (open.get(name)) {\n open.set(name, open.get(name)! - 1)\n } else {\n edits.push(match.index)\n }\n }\n if (edits.length === 0) return { body, lines: [] }\n let result = ''\n let cursor = 0\n const lines: Array<number> = []\n let line = 1\n for (const start of edits) {\n result += `${body.slice(cursor, start)}\\\\`\n for (let i = cursor; i < start; i++) if (body.charCodeAt(i) === 10) line++\n lines.push(line)\n cursor = start\n }\n return { body: result + body.slice(cursor), lines }\n}\n\nfunction isKnownComponentName(name: string, body: string): boolean {\n if (isThallyBuiltinComponent(name)) return true\n if (new RegExp(`^\\\\s*export\\\\s+(?:const|function|default\\\\s+function)\\\\s+${name}\\\\b`, 'm').test(body)) return true\n if (new RegExp(`^\\\\s*import\\\\s+(?:\\\\{[^}]*\\\\b${name}\\\\b[^}]*\\\\}|${name})\\\\s+from\\\\s+['\"][^'\"]+['\"]`, 'm').test(body)) return true\n return false\n}\n\n/**\n * Mintlify content occasionally uses a bare capitalized word in angle\n * brackets as a prose placeholder — for example \"<Feature> requires a Pro\n * plan\" — which is invalid JSX: a tag must self-close or have a matching\n * close. Left alone, this crashes MDX compilation for the whole page. A tag\n * name that never closes or self-closes anywhere in the document, and isn't\n * a known built-in, imported, or locally declared component, is almost\n * certainly such a placeholder — escape it to literal text rather than\n * losing the page.\n */\nfunction escapeOrphanCapitalizedTags(body: string): string {\n const masked = maskCodeRegions(body)\n // The attribute group is lazy so a trailing self-close slash is captured by\n // its own group instead of being swallowed as the last attribute character.\n const tagPattern = /<(\\/?)([A-Z][A-Za-z0-9]*)(?:\\s+[^<>]*?)?(\\/?)>/g\n const opens = new Map<string, Array<{ start: number; end: number }>>()\n const closedOrSelfClosed = new Set<string>()\n for (const match of masked.matchAll(tagPattern)) {\n const [full, closing, name, selfClosing] = match\n if (closing || selfClosing) {\n closedOrSelfClosed.add(name)\n continue\n }\n const start = match.index!\n const list = opens.get(name) ?? []\n list.push({ start, end: start + full.length })\n opens.set(name, list)\n }\n const edits: Array<{ start: number; end: number }> = []\n for (const [name, ranges] of opens) {\n if (closedOrSelfClosed.has(name) || isKnownComponentName(name, body)) continue\n edits.push(...ranges)\n }\n if (edits.length === 0) return body\n // The scan above is textual, so it cannot tell prose from a string inside an\n // expression or export. A page that already compiles needs no escaping;\n // never rewrite one.\n try {\n compileSync(body, { outputFormat: 'program' })\n return body\n } catch {\n // Fails to compile: escape the placeholders below.\n }\n edits.sort((a, b) => a.start - b.start)\n let result = ''\n let cursor = 0\n for (const edit of edits) {\n result += body.slice(cursor, edit.start)\n result += body.slice(edit.start, edit.end).replace(/^</, '<').replace(/>$/, '>')\n cursor = edit.end\n }\n return result + body.slice(cursor)\n}\n\nconst REACT_HOOK_NAMES = ['useState', 'useEffect', 'useRef', 'useCallback', 'useMemo', 'useContext', 'useReducer']\n\n/**\n * Mintlify documents these seven hooks as pre-injected globals for a page's\n * inline `export const Widget = () => {...}` components. The migrator's\n * component extraction (components.ts) moves a component that really calls\n * one of these hooks into its own client module, so this is only a fallback\n * for a hook call the extractor doesn't own (for example directly in the\n * page body). Detection is fence-aware: merely showing `useState(...)` in a\n * documentation code sample must not import it — that import alone, unused\n * or not, marks the compiled page a Client Component and breaks the build.\n */\nfunction injectReactHookImports(body: string): string {\n const masked = maskCodeRegions(body)\n const used = REACT_HOOK_NAMES.filter((hook) => (\n new RegExp(`\\\\b${hook}\\\\s*\\\\(`).test(masked)\n && !new RegExp(`import\\\\s*\\\\{[^}]*\\\\b${hook}\\\\b[^}]*\\\\}\\\\s*from\\\\s*['\"]react['\"]`).test(body)\n ))\n return used.length > 0 ? `import { ${used.join(', ')} } from 'react'\\n\\n${body}` : body\n}\n\n/**\n * An MDX expression that names something the page never defines throws a\n * ReferenceError when the page renders and fails the build, while Mintlify\n * renders it as nothing (`exactly one of {backfill, MV}.`). Match Mintlify:\n * drop the expression, or the JSX attribute that carries it, and say so once.\n */\nexport function removeUndefinedExpressions(body: string, warn?: (message: string) => void): string {\n if (!body.includes('{')) return body\n interface ExpressionNode extends MdxOffsetNode {\n attributes?: Array<{ type: string; value?: unknown; position?: MdxOffsetNode['position'] }>\n }\n let tree: ExpressionNode\n try {\n tree = descriptionParser.parse(body) as ExpressionNode\n } catch {\n return body\n }\n const esm: Array<string> = []\n const collect = (node: ExpressionNode): void => {\n if (node.type === 'mdxjsEsm' && node.value) esm.push(node.value)\n for (const child of node.children ?? []) collect(child)\n }\n collect(tree)\n const scope = pageScopeNames(esm)\n const edits: Array<{ start: number; end: number }> = []\n const removed = new Set<string>()\n const check = (expression: string, position: MdxOffsetNode['position']): void => {\n const names = unresolvedExpressionNames(expression, scope)\n const start = position?.start.offset\n const end = position?.end.offset\n if (names.length === 0 || start === undefined || end === undefined) return\n names.forEach((name) => removed.add(name))\n edits.push({ start, end })\n }\n const visit = (node: ExpressionNode): void => {\n if ((node.type === 'mdxFlowExpression' || node.type === 'mdxTextExpression') && node.value !== undefined) {\n check(node.value, node.position)\n }\n for (const attribute of node.attributes ?? []) {\n const value = attribute.value\n if (attribute.type === 'mdxJsxExpressionAttribute' && typeof value === 'string') check(`{${value}}`, attribute.position)\n else if (value && typeof value === 'object' && typeof (value as { value?: unknown }).value === 'string') {\n check((value as { value: string }).value, attribute.position)\n }\n }\n for (const child of node.children ?? []) visit(child)\n }\n visit(tree)\n if (edits.length === 0) return body\n warn?.(`Expressions that use ${[...removed].map((name) => `\"${name}\"`).join(', ')} were removed so the page builds: ${removed.size === 1 ? 'the name is' : 'the names are'} not defined on this page. Define ${removed.size === 1 ? 'the name' : 'the names'} or escape the braces as \\\\{ \\\\} to show the text.`)\n return edits.sort((a, b) => b.start - a.start)\n .reduce((text, edit) => text.slice(0, edit.start) + text.slice(edit.end), body)\n}\n\n/** Keep authored paragraph styling without emitting an invalid <p><p> tree. */\nfunction normalizeFlowParagraphContainers(body: string): string {\n if (!body.includes('<p')) return body\n interface FlowNode {\n type: string\n name?: string | null\n children?: Array<FlowNode>\n position?: { start: { offset?: number }; end: { offset?: number } }\n }\n let tree: FlowNode\n try {\n tree = descriptionParser.parse(body) as FlowNode\n } catch {\n return body\n }\n const edits: Array<{ offset: number; value: string }> = []\n function visit(node: FlowNode): void {\n if (node.type === 'mdxJsxFlowElement' && node.name === 'p'\n && node.children?.some((child) => child.type === 'paragraph' || child.type === 'list' || child.type === 'mdxJsxFlowElement')) {\n const start = node.position?.start.offset\n const end = node.position?.end.offset\n if (start !== undefined && end !== undefined && body.slice(start, start + 2) === '<p') {\n const closing = body.slice(start, end).match(/<\\/p\\s*>\\s*$/i)\n if (closing?.index !== undefined) {\n edits.push({ offset: start + 1, value: 'div' })\n edits.push({ offset: start + closing.index + 2, value: 'div' })\n }\n }\n }\n for (const child of node.children ?? []) visit(child)\n }\n visit(tree)\n // Replace only the tag names; every class, inline style, and child stays\n // authored. Descending offsets keep nested paragraph wrappers stable.\n return edits.sort((a, b) => b.offset - a.offset)\n .reduce((text, edit) => text.slice(0, edit.offset) + edit.value + text.slice(edit.offset + 1), body)\n}\n\n/** Normalize only syntax Thally cannot render; supported source JSX stays intact. */\nexport function normalizeMdx(body: string, platform?: MigrationPlatform, unwrapMdxCodeBlocks = true): string {\n // A caller that doesn't know the source platform (the URL crawler, when it\n // can't identify one) must not guess — applying every platform's textual\n // renames to a page of unknown origin corrupts one that happens to use the\n // same component name for something else, e.g. a Mintlify page's own\n // `<Success>` becoming `<Tip>`, a Fern-only alias. An omitted platform\n // therefore runs no platform-specific renames; a caller exercising one\n // must pass it explicitly.\n const runFern = platform === 'fern'\n const runMintlify = platform === 'mintlify'\n const runDocusaurus = platform === 'docusaurus'\n\n // A heading's explicit `{#custom-id}` anchor (see `normalizeExplicitHeadingIds`)\n // means the same thing on every platform, so — unlike the platform-specific\n // renames below — this always runs, even when `platform` is omitted.\n // `normalizeExplicitHeadingIds` also covers Docusaurus' `{/* #id */}`\n // comment form.\n const sourceBody = runDocusaurus && unwrapMdxCodeBlocks ? unwrapDocusaurusMdxCodeBlocks(body) : body\n let rewritten = normalizeExplicitHeadingIds(\n runFern ? normalizeFernFileTrees(normalizeFernCallouts(normalizeNestedCodeFences(sourceBody))) : normalizeNestedCodeFences(sourceBody),\n undefined,\n { headingMarkers: runMintlify },\n )\n if (runDocusaurus) rewritten = normalizeDocusaurusAdmonitions(rewritten)\n if (runMintlify) rewritten = markMintlifyHeadings(rewritten)\n if (runDocusaurus) {\n // Docusaurus resolves GitHub emoji names in Markdown text. Leaving the\n // shortcodes literal makes comparison tables unreadable after import; the\n // code masker keeps examples and inline code byte-for-byte intact.\n rewritten = replaceOutsideCode(rewritten, (segment) => segment.replace(\n /:([+\\w-]+):/g,\n (shortcode: string, name: string) => nameToEmoji[name] ?? shortcode,\n ))\n // Some authored JSX uses Docusaurus' runtime helper through `require()`\n // directly in a link attribute. That package is absent from a generated\n // Thally site; a static, confined path can be projected without running\n // source code or leaving a build-breaking module reference.\n rewritten = replaceOutsideCode(rewritten, (segment) => segment.replace(\n /\\b(href|src)=\\{\\s*require\\(\\s*(['\"])@docusaurus\\/useBaseUrl\\2\\s*\\)\\.default\\(\\s*(['\"])([^'\"]+)\\3\\s*\\)\\s*\\}/g,\n (original: string, attribute: string, _moduleQuote: string, _pathQuote: string, path: string) => {\n if (!/^[\\w./~%-]+$/.test(path) || path.split('/').includes('..')) return original\n return `${attribute}=\"/${path.replace(/^\\.\\//, '').replace(/^\\/+/, '')}\"`\n },\n ))\n rewritten = replaceOutsideCode(rewritten, (segment) => normalizeDocusaurusThemedImages(normalizeDocusaurusAdmonitionTags(segment)))\n // Docusaurus injects these theme components globally. Thally also\n // exposes its equivalents globally, so source-only imports must not survive.\n rewritten = normalizeDocusaurusTabBlocks(normalizeDocusaurusTabs(removeGlobalDocusaurusImports(rewritten)))\n // Data-only @site imports are often used solely by Docusaurus' tab\n // metadata. Once those props are projected to <Tab> labels, remove only\n // imports whose binding has no remaining use on the page.\n rewritten = replaceOutsideCode(rewritten, (segment) => segment.replace(\n /\\bimport\\s+([A-Za-z_$][\\w$]*)\\s+from\\s+(['\"])(@site\\/[^'\"]+)\\2\\s*;?/g,\n (statement: string, binding: string, _quote: string, _source: string, offset: number) => {\n const withoutStatement = segment.slice(0, offset) + segment.slice(offset + statement.length)\n return new RegExp(`\\\\b${binding}\\\\s*(?:\\\\.|\\\\[|\\\\})`).test(withoutStatement) ? statement : ''\n },\n ))\n rewritten = rewritten.replace(/^\\n{2,}/, '\\n')\n }\n if (runMintlify) rewritten = normalizeMintlifyParameterAnchors(rewritten)\n rewritten = replaceOutsideCode(rewritten, (segment) => {\n let result = convertHtmlStyleAttributes(segment)\n .replace(/<!--([\\s\\S]*?)-->/g, (_match, content: string) => mdxComment(content))\n .replace(/<Danger(\\s[^>]*)?>/g, '<Error$1>')\n .replace(/<\\/Danger>/g, '</Error>')\n .replace(/<Warn(\\s[^>]*)?>/g, '<Warning$1>')\n .replace(/<\\/Warn>/g, '</Warning>')\n .replace(/<Check(\\s[^>]*)?>/g, '<Note$1>')\n .replace(/<\\/Check>/g, '</Note>')\n .replace(/<Tree\\.Folder/g, '<Folder')\n .replace(/<\\/Tree\\.Folder>/g, '</Folder>')\n .replace(/<Tree\\.File/g, '<File')\n .replace(/<\\/Tree\\.File>/g, '</File>')\n if (runDocusaurus) {\n result = result\n // A TabItem outside any <Tabs>...</Tabs> pair (malformed source)\n // never reaches normalizeDocusaurusTabs' block match above; fall\n // back to its own label/value so it still renders as a Tab.\n .replace(/<TabItem\\b([^<>]*)>/g, (_match, attributes: string) => {\n const title = attributes.match(/\\blabel=(?:\"([^\"]*)\"|'([^']*)')/)?.slice(1).find(Boolean)\n ?? attributes.match(/\\bvalue=(?:\"([^\"]*)\"|'([^']*)')/)?.slice(1).find(Boolean)\n ?? 'Tab'\n return `<Tab title=\"${title.replace(/\"/g, '"')}\">`\n })\n .replace(/<\\/TabItem>/g, '</Tab>')\n result = normalizeDocusaurusLinkTags(result)\n // The repository adapter expands DocCardList from the resolved\n // sidebar after page discovery. Its source tag has no runtime peer.\n .replace(/<(?:DocCardList|TOCInline)\\b[^<>]*\\/>/g, '')\n }\n if (runMintlify) {\n result = normalizeMintlifyUpdateLabels(result)\n // Mintlify documents `<FileTree>` as a plain alias of `<Tree>`; Thally\n // only registers the latter.\n .replace(/<FileTree(\\s[^>]*)?>/g, '<Tree$1>')\n .replace(/<\\/FileTree>/g, '</Tree>')\n // Mintlify's `<GitHub.Repo repo=\"owner/name\" />` is the same repository\n // card Thally already registers as `<GitHub repo=\"owner/name\" />`.\n .replace(/<GitHub\\.Repo/g, '<GitHub')\n .replace(/<\\/GitHub\\.Repo>/g, '</GitHub>')\n // `<Column>` is an unstyled wrapper for arbitrary content inside\n // `<Columns>`; Thally's grid already treats any direct child as a\n // column, so a plain `<div>` (built into MDX, no registry entry\n // needed) matches.\n .replace(/<Column(\\s[^>]*)?>/g, '<div$1>')\n .replace(/<\\/Column>/g, '</div>')\n // Mintlify renders step titles as plain text unless `titleSize` says\n // otherwise; Thally's default is an `<h3>`, so pin the source's default.\n result = pinStepsTitleSize(result)\n }\n if (runFern) {\n result = result\n // Fern-only wrappers with a direct Thally equivalent.\n .replace(/<CodeBlocks(\\s[^>]*)?>/g, '<CodeGroup$1>')\n .replace(/<\\/CodeBlocks>/g, '</CodeGroup>')\n .replace(/<ParameterField\\b/g, '<ParamField')\n .replace(/<\\/ParameterField>/g, '</ParamField>')\n .replace(/<Cards(\\s[^>]*)?>/g, '<CardGroup$1>')\n .replace(/<\\/Cards>/g, '</CardGroup>')\n .replace(/<Success(\\s[^>]*)?>/g, '<Tip$1>')\n .replace(/<\\/Success>/g, '</Tip>')\n .replace(/<Launch(\\s[^>]*)?>/g, '<Note$1>')\n .replace(/<\\/Launch>/g, '</Note>')\n // Fern's <Files> wraps <File>/<Folder> nodes Thally already renders\n // directly (also reached via the Docusaurus <Tree.Folder> rename above).\n .replace(/<Files(\\s[^>]*)?>\\n?/g, '')\n .replace(/<\\/Files>/g, '')\n }\n return result\n })\n if (runFern) rewritten = unwrapFernCodeBlockTabs(rewritten)\n const normalized = escapeOrphanCapitalizedTags(provideScopedGlobalReferences(rewritten))\n if (runDocusaurus && sourceBody !== body) {\n try {\n compileSync(normalized, { outputFormat: 'program' })\n } catch {\n // Some Docusaurus sites use this fence for imports or JSX fragments\n // whose surrounding source requires their private remark transforms.\n // Retain the original fenced page instead of dropping it entirely.\n return normalizeMdx(body, platform, false)\n }\n }\n return normalizeFlowParagraphContainers(normalized)\n}\n\n/** Parse source Markdown or MDX into the canonical page representation. */\nexport function parseMarkdownPage(input: {\n id: string\n navigationId?: string\n locale?: string\n raw: string\n source: string\n /** Gates platform-specific textual renames in `normalizeMdx`; omitted runs them all. */\n platform?: MigrationPlatform\n /** Resolve platform-specific routes from the already-parsed frontmatter. */\n resolveIdentity?: (\n frontmatter: Record<string, unknown>,\n fallback: MarkdownPageIdentity,\n ) => MarkdownPageIdentity\n /** Receives a message when frontmatter is dropped or altered. */\n warn?: (message: string) => void\n}): MigrationPage | null {\n const parsed = parseFrontmatter(input.raw)\n const fallbackIdentity: MarkdownPageIdentity = {\n id: input.id,\n navigationId: input.navigationId ?? input.id,\n ...(input.locale ? { locale: input.locale } : {}),\n }\n const identity = input.resolveIdentity?.(parsed.data, fallbackIdentity) ?? fallbackIdentity\n const body = removeUndefinedExpressions(injectReactHookImports(normalizeMdx(parsed.content, input.platform)), input.warn).trim()\n const keywords = Array.isArray(parsed.data.keywords)\n ? parsed.data.keywords.filter((value): value is string => typeof value === 'string')\n : []\n const heading = leadingPageHeading(body)\n const title = typeof parsed.data.title === 'string' && parsed.data.title.trim()\n ? parsed.data.title.trim()\n : heading?.title ?? titleFromId(identity.navigationId)\n const sidebarTitle = input.platform === 'docusaurus' ? parsed.data.sidebar_label : parsed.data.sidebarTitle\n const navTitle = typeof sidebarTitle === 'string' && sidebarTitle.trim()\n ? sidebarTitle.trim()\n : typeof parsed.data.navTitle === 'string' && parsed.data.navTitle.trim()\n ? parsed.data.navTitle.trim()\n : undefined\n const icon = typeof parsed.data.icon === 'string' && parsed.data.icon.trim() ? parsed.data.icon.trim() : undefined\n const iconType = icon && typeof parsed.data.iconType === 'string'\n && ['regular', 'solid', 'outline', 'brands'].includes(parsed.data.iconType)\n ? parsed.data.iconType as MigrationPage['iconType']\n : undefined\n const meta: NonNullable<MigrationPage['meta']> = {}\n for (const [source, field] of PAGE_META_FIELDS) {\n const value = parsed.data[source]\n if (typeof value === 'string' && value.trim()) meta[field] = value.trim()\n }\n const badge = typeof parsed.data.tag === 'string' && parsed.data.tag.trim()\n ? parsed.data.tag.trim()\n : typeof parsed.data.badge === 'string' && parsed.data.badge.trim()\n ? parsed.data.badge.trim()\n : undefined\n const sourceMode = typeof parsed.data.mode === 'string' ? parsed.data.mode : undefined\n // Mintlify's frame mode and Thally's wide mode both retain the sidebar while\n // removing the on-page table of contents. Other shared modes map directly.\n const mode = sourceMode === 'frame'\n ? 'wide'\n : ['default', 'wide', 'custom', 'center', 'home'].includes(sourceMode ?? '')\n ? sourceMode as MigrationPage['mode']\n : undefined\n const description = typeof parsed.data.description === 'string' && parsed.data.description.trim()\n ? parsed.data.description.trim()\n // An API reference page is described by its operation, not by its prose.\n : typeof parsed.data.openapi === 'string' && parsed.data.openapi.trim()\n ? ''\n : firstParagraph(body)\n const page: MigrationPage = {\n id: identity.id,\n navigationId: identity.navigationId,\n locale: identity.locale,\n title,\n navTitle,\n icon,\n iconType,\n description,\n ...(input.platform === 'docusaurus' || input.platform === 'fern'\n ? { descriptionPlacement: 'body' as const }\n : {}),\n badge,\n keywords,\n mode,\n hidden: parsed.data.hidden === true ? true : undefined,\n noindex: parsed.data.noindex === true || parsed.data.noindex === 'true' ? true : undefined,\n ...(Object.keys(meta).length > 0 ? { meta } : {}),\n openapi: typeof parsed.data.openapi === 'string' ? parsed.data.openapi.trim() : undefined,\n ...apiFrontmatter(parsed.data, input.warn),\n body,\n source: input.source,\n ...(parsed.error ? { frontmatterError: parsed.error } : {}),\n }\n resolvePageDisplayHeading(page, input.platform)\n return page\n}\n\nconst PAGE_META_FIELDS = [\n ['og:title', 'ogTitle'],\n ['og:description', 'ogDescription'],\n ['og:image', 'ogImage'],\n ['twitter:title', 'twitterTitle'],\n ['twitter:description', 'twitterDescription'],\n ['twitter:image', 'twitterImage'],\n] as const\n\nconst AUTH_METHODS = new Set(['bearer', 'basic', 'key', 'none'])\n\n/** Carry manual-API frontmatter (`api`, `authMethod`) through; report what cannot be kept. */\nfunction apiFrontmatter(\n data: Record<string, unknown>,\n warn?: (message: string) => void,\n): Pick<MigrationPage, 'api' | 'authMethod' | 'playground'> {\n const result: Pick<MigrationPage, 'api' | 'authMethod' | 'playground'> = {}\n if (data.playground !== undefined && data.playground !== null) {\n const display = playgroundDisplay(data.playground, 'The page\\'s \"playground\" frontmatter', warn)\n if (display) result.playground = display\n }\n if (data.api !== undefined && data.api !== null) {\n if (typeof data.api === 'string' && data.api.trim()) result.api = data.api.trim()\n else warn?.('The page\\'s \"api\" frontmatter is not a \"METHOD url-or-path\" string and was dropped.')\n }\n if (data.authMethod !== undefined && data.authMethod !== null) {\n const method = typeof data.authMethod === 'string' ? data.authMethod.trim().toLowerCase() : ''\n if (AUTH_METHODS.has(method)) result.authMethod = method\n else warn?.('The page\\'s \"authMethod\" frontmatter is not one of bearer, basic, key, none and was dropped.')\n }\n return result\n}\n","/**\n * Thally's registered MDX built-in component tag names, kept in one place so\n * the unknown-component fallback (`mdx.ts`) knows what already renders\n * without guessing. Mirrors the registry in the Thally runtime's\n * `src/components/mdx/builtin-components.tsx` (read-only from this package,\n * which ships standalone and cannot import app source at runtime).\n *\n * A drift guard (`src/components/mdx/client-registry.test.ts`, in the app\n * package) recomputes this set straight from that registry's AST and fails\n * CI if the two disagree, so a renderer change that adds or removes a\n * built-in fails loudly here instead of silently going stale in the\n * migrator.\n */\nexport const THALLY_BUILTIN_COMPONENTS: ReadonlySet<string> = new Set([\n 'CodeGroup', 'Info', 'Warning', 'Check', 'Danger', 'Error', 'Note', 'Tip',\n 'Callout', 'AccordionGroup', 'Latex', 'Hero', 'Card', 'CardGroup',\n 'Columns', 'Frame', 'Accordion', 'Tooltip', 'Icon', 'Steps', 'Step',\n 'Tabs', 'Tab', 'Badge', 'Update', 'RequestExample', 'ResponseExample',\n 'Panel', 'ContentPanel', 'InlinePanel', 'InlineRequestExample',\n 'InlineResponseExample', 'Tile', 'TileGroup', 'Prompt', 'PromptUser',\n 'PromptAssistant', 'Terminal', 'TerminalInput', 'TerminalOutput',\n 'AgentPrompt', 'Color', 'Tree', 'Folder', 'File', 'ResponseField',\n 'ParamField', 'Expandable', 'Mermaid', 'View', 'Embed', 'LegacyView',\n 'GitHub', 'Github', 'Visibility', 'Human', 'Agent', 'BannerPreview', 'Banner',\n 'CodeBlock', 'Column', 'FileTree', 'LiteYouTubeEmbed', 'MDX', 'Video', 'YouTube',\n])\n\n/** True for a builtin tag, or a member-expression tag on one (`Color.Item` -> `Color`). */\nexport function isThallyBuiltinComponent(name: string): boolean {\n return THALLY_BUILTIN_COMPONENTS.has(name) || THALLY_BUILTIN_COMPONENTS.has(name.split('.')[0])\n}\n","/**\n * Analysis behind the migrator's extraction of a page's own inline\n * components into a `'use client'` module (see `components.ts`).\n *\n * A page component that calls a React hook cannot stay in the page: Next\n * compiles an MDX page as a Server Component. Moving only that component is\n * not enough, because it may use other declarations from the same page (a\n * sibling component, a helper, a data array) that the client module cannot\n * reach. This module computes what has to travel with it, what the page\n * still needs a copy of, and refuses (with a reason) when a dependency cannot\n * be made available in the client module, so a page is never left half moved.\n */\n\nimport ts from 'typescript'\n\nimport { isThallyBuiltinComponent } from './builtin-components.js'\n\nexport interface InlineDeclaration { start: number; end: number; source: string }\n\ntype RefKind = 'tag' | 'member' | 'prop' | 'typeof' | 'other'\ninterface Ref { name: string; kind: RefKind }\n\ninterface Unit {\n index: number\n source: string\n names: Array<string>\n refs: Array<Ref>\n /** Page-level names this declaration reassigns or mutates. */\n assigned: Array<string>\n hooks: boolean\n /** Handlers, browser globals: cannot render in a Server Component. */\n clientOnly: boolean\n componentLike: boolean\n blockers: Array<string>\n}\n\n/** Minimal MDX syntax-tree shape needed to find the page's own references. */\nexport interface InlineTree {\n type: string\n name?: string | null\n value?: string\n attributes?: Array<{ type: string; name?: string; value?: string | { type?: string; value?: string } | null }>\n children?: Array<InlineTree>\n}\n\nexport interface InlinePlan {\n /** Blocked roots, each with the reason it was left in the page. */\n blocked: Array<{ name: string; reason: string }>\n /** Declarations deleted from the page (declaration indexes). */\n moved: Array<number>\n /** Declarations the page keeps and the client module also receives. */\n copied: Array<number>\n /** Names of every moved declaration. */\n movedNames: Array<string>\n /** Import statements the client module needs, in source order. */\n moduleImports: Array<string>\n /** Server-side helper declarations (page-level text) the module also needs. */\n moduleHelpers: Array<string>\n /** Moved names used as a JSX tag in the page body (registered, then renamed). */\n registryNames: Array<string>\n /** Moved names the page reaches by their real name (import statement needed). */\n pageImportNames: Array<string>\n /** Local names every module statement may reference without a built-in lookup. */\n moduleNames: Set<string>\n /** Names still referenced by the page after the move (drives import cleanup). */\n pageReferences: Set<string>\n}\n\nconst SERVER_ONLY_IDENTIFIERS = new Set(['process', 'require', 'module', 'exports', '__dirname', '__filename', 'Buffer'])\nconst BROWSER_IDENTIFIERS = new Set([\n 'window', 'document', 'localStorage', 'sessionStorage', 'navigator', 'location', 'history',\n 'IntersectionObserver', 'ResizeObserver', 'MutationObserver', 'matchMedia', 'requestAnimationFrame',\n 'cancelAnimationFrame', 'alert', 'confirm', 'prompt', 'HTMLElement', 'Element', 'Node', 'Event',\n 'KeyboardEvent', 'MouseEvent', 'CustomEvent', 'FormData', 'File', 'FileReader', 'Image', 'Audio',\n 'XMLHttpRequest', 'WebSocket', 'getComputedStyle', 'innerWidth', 'innerHeight', 'scrollTo',\n])\n/** Bare names any module may reference without declaring them. */\n// An explicit list: the set of globals a Node process happens to have varies by\n// version, and the answer must not depend on which one runs the migration.\nconst JS_GLOBALS = new Set([\n 'undefined', 'NaN', 'Infinity', 'globalThis', 'arguments',\n 'Object', 'Function', 'Array', 'Number', 'Boolean', 'String', 'Symbol', 'BigInt', 'Date', 'RegExp',\n 'Error', 'AggregateError', 'EvalError', 'RangeError', 'ReferenceError', 'SyntaxError', 'TypeError', 'URIError',\n 'JSON', 'Math', 'Intl', 'Reflect', 'Proxy', 'Promise', 'Map', 'Set', 'WeakMap', 'WeakSet', 'WeakRef',\n 'ArrayBuffer', 'SharedArrayBuffer', 'DataView', 'Atomics', 'Int8Array', 'Uint8Array', 'Uint8ClampedArray',\n 'Int16Array', 'Uint16Array', 'Int32Array', 'Uint32Array', 'Float32Array', 'Float64Array', 'BigInt64Array', 'BigUint64Array',\n 'parseInt', 'parseFloat', 'isNaN', 'isFinite', 'encodeURI', 'encodeURIComponent', 'decodeURI', 'decodeURIComponent',\n 'console', 'fetch', 'URL', 'URLSearchParams', 'Headers', 'Request', 'Response', 'AbortController', 'AbortSignal',\n 'TextEncoder', 'TextDecoder', 'atob', 'btoa', 'structuredClone', 'queueMicrotask', 'performance',\n 'setTimeout', 'clearTimeout', 'setInterval', 'clearInterval',\n 'crypto', 'Blob', 'EventTarget', 'escape', 'unescape', 'eval', 'WebAssembly', 'Iterator', 'self', 'screen',\n 'FinalizationRegistry', 'DOMException', 'ReadableStream', 'WritableStream', 'TransformStream', 'MessageChannel', 'BroadcastChannel',\n])\nconst IMPLICIT_MODULE_NAMES = new Set(['React', 'MintlifyComponents'])\n\nfunction parse(source: string): ts.SourceFile {\n return ts.createSourceFile('inline.tsx', source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX)\n}\n\nfunction bindingNames(name: ts.BindingName, into: Set<string>): void {\n if (ts.isIdentifier(name)) into.add(name.text)\n else for (const element of name.elements) if (ts.isBindingElement(element)) bindingNames(element.name, into)\n}\n\nfunction isFunctionLike(node: ts.Node): node is ts.FunctionLikeDeclaration {\n return ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isArrowFunction(node)\n || ts.isMethodDeclaration(node) || ts.isConstructorDeclaration(node)\n || ts.isGetAccessorDeclaration(node) || ts.isSetAccessorDeclaration(node)\n}\n\n/** Every name a function scope binds: parameters, and any declaration in its body outside nested functions. */\nfunction functionScope(fn: ts.FunctionLikeDeclaration): Set<string> {\n const scope = new Set<string>()\n if ((ts.isFunctionExpression(fn) || ts.isFunctionDeclaration(fn)) && fn.name) scope.add(fn.name.text)\n for (const parameter of fn.parameters) bindingNames(parameter.name, scope)\n function collect(node: ts.Node): void {\n if (ts.isVariableDeclaration(node)) bindingNames(node.name, scope)\n if ((ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node)) && node.name) scope.add(node.name.text)\n if (isFunctionLike(node)) return\n ts.forEachChild(node, collect)\n }\n if (fn.body) ts.forEachChild(fn.body, collect)\n if (fn.body && !ts.isBlock(fn.body)) collect(fn.body)\n return scope\n}\n\n/** Free (unbound) identifier references in one declaration, with how each is used. */\nfunction collectRefs(root: ts.Node): { refs: Array<Ref>; assigned: Array<string>; awaitOutsideAsync: boolean; dynamicImport: boolean } {\n const refs: Array<Ref> = []\n const assigned: Array<string> = []\n let awaitOutsideAsync = false\n let dynamicImport = false\n function bound(name: string, scopes: Array<Set<string>>): boolean {\n return scopes.some((scope) => scope.has(name))\n }\n function push(name: string, kind: RefKind, scopes: Array<Set<string>>): void {\n if (!bound(name, scopes)) refs.push({ name, kind })\n }\n function walkNode(node: ts.Node, scopes: Array<Set<string>>, inAsync: boolean): void {\n if (ts.isTypeNode(node)) return\n if (isFunctionLike(node)) {\n const nextScopes = [...scopes, functionScope(node)]\n const async = !!node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword)\n for (const parameter of node.parameters) {\n if (!ts.isIdentifier(parameter.name)) walkNode(parameter.name, nextScopes, async)\n if (parameter.initializer) walkNode(parameter.initializer, nextScopes, async)\n }\n if (node.body) walkNode(node.body, nextScopes, async)\n return\n }\n if (ts.isAwaitExpression(node) && !inAsync) awaitOutsideAsync = true\n const target = ts.isBinaryExpression(node) && node.operatorToken.kind >= ts.SyntaxKind.FirstAssignment && node.operatorToken.kind <= ts.SyntaxKind.LastAssignment\n ? node.left\n : (ts.isPrefixUnaryExpression(node) || ts.isPostfixUnaryExpression(node))\n && (node.operator === ts.SyntaxKind.PlusPlusToken || node.operator === ts.SyntaxKind.MinusMinusToken)\n ? node.operand\n : undefined\n if (target) {\n let base: ts.Node = target\n while (ts.isPropertyAccessExpression(base) || ts.isElementAccessExpression(base)) base = base.expression\n if (ts.isIdentifier(base) && !bound(base.text, scopes)) assigned.push(base.text)\n }\n if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword) dynamicImport = true\n if (ts.isIdentifier(node)) {\n const parent = node.parent\n let operandOf = parent\n while (ts.isParenthesizedExpression(operandOf)) operandOf = operandOf.parent\n const kind: RefKind = ts.isTypeOfExpression(operandOf)\n ? 'typeof'\n : ts.isJsxExpression(parent) && parent.parent && ts.isJsxAttribute(parent.parent) ? 'prop' : 'other'\n push(node.text, kind, scopes)\n return\n }\n if (ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) {\n let tag: ts.Node = node.tagName\n while (ts.isPropertyAccessExpression(tag)) tag = tag.expression\n if (ts.isIdentifier(tag)) {\n // A lowercase, non-member tag is an intrinsic element, not a reference.\n if (node.tagName !== tag || /^[A-Z]/.test(tag.text)) push(tag.text, node.tagName === tag ? 'tag' : 'member', scopes)\n }\n walkNode(node.attributes, scopes, inAsync)\n return\n }\n if (ts.isJsxClosingElement(node)) return\n if (ts.isJsxAttribute(node)) {\n if (node.initializer) walkNode(node.initializer, scopes, inAsync)\n return\n }\n if (ts.isPropertyAccessExpression(node)) {\n walkNode(node.expression, scopes, inAsync)\n return\n }\n if (ts.isQualifiedName(node)) return\n if (ts.isPropertyAssignment(node)) {\n if (ts.isComputedPropertyName(node.name)) walkNode(node.name.expression, scopes, inAsync)\n walkNode(node.initializer, scopes, inAsync)\n return\n }\n if (ts.isShorthandPropertyAssignment(node)) {\n push(node.name.text, 'other', scopes)\n if (node.objectAssignmentInitializer) walkNode(node.objectAssignmentInitializer, scopes, inAsync)\n return\n }\n if (ts.isBindingElement(node)) {\n if (node.propertyName && ts.isComputedPropertyName(node.propertyName)) walkNode(node.propertyName.expression, scopes, inAsync)\n if (node.initializer) walkNode(node.initializer, scopes, inAsync)\n if (!ts.isIdentifier(node.name)) walkNode(node.name, scopes, inAsync)\n return\n }\n if (ts.isVariableDeclaration(node)) {\n if (!ts.isIdentifier(node.name)) walkNode(node.name, scopes, inAsync)\n if (node.initializer) walkNode(node.initializer, scopes, inAsync)\n return\n }\n if (ts.isClassDeclaration(node) || ts.isClassExpression(node)) {\n // A class expression's own name is bound inside the class only.\n const inner = ts.isClassExpression(node) && node.name ? [...scopes, new Set([node.name.text])] : scopes\n node.members.forEach((member) => walkNode(member, inner, inAsync))\n for (const clause of node.heritageClauses ?? []) for (const type of clause.types) walkNode(type.expression, scopes, inAsync)\n return\n }\n // `import.meta` / `new.target` carry a keyword-like name, not a reference.\n if (ts.isMetaProperty(node)) return\n if (ts.isPropertyDeclaration(node)) {\n if (node.initializer) walkNode(node.initializer, scopes, inAsync)\n return\n }\n if (ts.isLabeledStatement(node)) {\n walkNode(node.statement, scopes, inAsync)\n return\n }\n if (ts.isBreakOrContinueStatement(node)) return\n ts.forEachChild(node, (child) => walkNode(child, scopes, inAsync))\n }\n // A top-level declaration's own name is module-level, so a self-reference\n // (recursion) is reported like any other page-level name.\n walkNode(root, [], false)\n return { refs, assigned, awaitOutsideAsync, dynamicImport }\n}\n\nfunction declaredNames(statement: ts.Statement): { names: Array<string>; componentLike: boolean; defaultExport: boolean } {\n const isDefault = !!(ts.canHaveModifiers(statement) ? ts.getModifiers(statement) : undefined)?.some((modifier) => modifier.kind === ts.SyntaxKind.DefaultKeyword)\n || ts.isExportAssignment(statement)\n const names = new Set<string>()\n let componentLike = false\n if (ts.isVariableStatement(statement)) {\n for (const declaration of statement.declarationList.declarations) {\n bindingNames(declaration.name, names)\n const initializer = declaration.initializer && unwrap(declaration.initializer)\n if (ts.isIdentifier(declaration.name) && /^[A-Z]/.test(declaration.name.text) && initializer\n && (ts.isArrowFunction(initializer) || ts.isFunctionExpression(initializer) || ts.isClassExpression(initializer))) {\n componentLike = true\n }\n }\n } else if ((ts.isFunctionDeclaration(statement) || ts.isClassDeclaration(statement)) && statement.name) {\n names.add(statement.name.text)\n componentLike = /^[A-Z]/.test(statement.name.text)\n }\n return { names: [...names], componentLike, defaultExport: isDefault }\n}\n\nfunction unwrap(expression: ts.Expression): ts.Expression {\n let current = expression\n while (ts.isParenthesizedExpression(current) || ts.isAsExpression(current) || ts.isSatisfiesExpression(current)) current = current.expression\n return current\n}\n\nfunction analyzeUnit(index: number, source: string, reactGlobals: ReadonlySet<string>): Unit {\n const sf = parse(source)\n const statement = sf.statements[0]\n const blockers: Array<string> = []\n if (!statement || sf.statements.length !== 1) {\n return { index, source, names: [], refs: [], assigned: [], hooks: false, clientOnly: false, componentLike: false, blockers: ['is not a single declaration'] }\n }\n const { names, componentLike, defaultExport } = declaredNames(statement)\n if (defaultExport) blockers.push('is a default export')\n const { refs, assigned, awaitOutsideAsync, dynamicImport } = collectRefs(statement)\n if (awaitOutsideAsync) blockers.push('uses await outside an async function')\n if (dynamicImport) blockers.push('uses a dynamic import()')\n // An async component is a Server Component; it has no client equivalent.\n const initializer = ts.isVariableStatement(statement) && statement.declarationList.declarations.length === 1\n ? statement.declarationList.declarations[0].initializer && unwrap(statement.declarationList.declarations[0].initializer)\n : statement\n if (initializer && isFunctionLike(initializer) && initializer.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword)) {\n blockers.push('is an async (server) component')\n }\n for (const { name } of refs) {\n if (SERVER_ONLY_IDENTIFIERS.has(name)) blockers.push(`uses the server-only \\`${name}\\``)\n }\n let handlers = false\n function scanHandlers(node: ts.Node): void {\n if (ts.isJsxAttribute(node) && ts.isIdentifier(node.name) && /^on[A-Z]/.test(node.name.text)) handlers = true\n ts.forEachChild(node, scanHandlers)\n }\n scanHandlers(statement)\n return {\n index,\n source,\n names,\n refs,\n assigned,\n hooks: refs.some(({ name }) => reactGlobals.has(name)) || /\\bReact\\.(?:use[A-Z]\\w*|createContext|forwardRef|memo)\\b/.test(source),\n clientOnly: handlers || refs.some(({ name }) => BROWSER_IDENTIFIERS.has(name)),\n componentLike,\n blockers: [...new Set(blockers)],\n }\n}\n\nfunction helperNames(source: string): Array<string> {\n const statement = parse(source).statements[0]\n return statement ? declaredNames(statement).names : []\n}\n\nfunction importLocals(statementText: string): Array<string> {\n const sf = parse(statementText)\n const locals: Array<string> = []\n for (const statement of sf.statements) {\n if (!ts.isImportDeclaration(statement) || !statement.importClause) continue\n const clause = statement.importClause\n if (clause.name) locals.push(clause.name.text)\n const named = clause.namedBindings\n if (named && ts.isNamespaceImport(named)) locals.push(named.name.text)\n if (named && ts.isNamedImports(named)) for (const element of named.elements) locals.push(element.name.text)\n }\n return locals\n}\n\n/** Free identifier references in an MDX expression string (`{...}` body or attribute value). */\nfunction expressionRefs(expression: string): Array<Ref> {\n const sf = parse(`const __expression = (${expression})`)\n const statement = sf.statements[0]\n return statement ? collectRefs(statement).refs : []\n}\n\nfunction bodyReferences(tree: InlineTree): { mdastTags: Set<string>; refs: Array<Ref> } {\n const mdastTags = new Set<string>()\n const refs: Array<Ref> = []\n function visit(node: InlineTree): void {\n if (node.type === 'mdxjsEsm') return\n if (['mdxJsxFlowElement', 'mdxJsxTextElement'].includes(node.type) && node.name) {\n const root = node.name.split('.')[0]\n if (node.name.includes('.')) refs.push({ name: root, kind: 'member' })\n else mdastTags.add(root)\n }\n if (['mdxFlowExpression', 'mdxTextExpression'].includes(node.type) && node.value) refs.push(...expressionRefs(node.value))\n for (const attribute of node.attributes ?? []) {\n const value = attribute.value\n if (attribute.type === 'mdxJsxExpressionAttribute' && typeof value === 'string') {\n refs.push(...expressionRefs(`{${value}}`))\n } else if (value && typeof value === 'object' && typeof value.value === 'string') {\n refs.push(...expressionRefs(value.value).map((ref, _index, all) => (\n all.length === 1 && value.value!.trim() === ref.name ? { name: ref.name, kind: 'prop' as const } : ref\n )))\n }\n }\n for (const child of node.children ?? []) visit(child)\n }\n visit(tree)\n return { mdastTags, refs }\n}\n\nexport function planInlineExtraction(input: {\n declarations: Array<InlineDeclaration>\n tree: InlineTree\n /** Import statements the client module may copy verbatim (already valid from the module's own directory). */\n copyableImports: Array<string>\n /** Page-level declarations synthesized by the migrator (static helpers), by source text. */\n helperDeclarations: Array<string>\n /** Local names of imports the migration removed, with their package. */\n unavailableImports: Map<string, string>\n reactGlobals: ReadonlySet<string>\n}): InlinePlan {\n const units = input.declarations.map((declaration, index) => analyzeUnit(index, declaration.source, input.reactGlobals))\n const owner = new Map<string, number>()\n for (const unit of units) for (const name of unit.names) owner.set(name, unit.index)\n // A page-level custom hook (`useTheme`) makes whatever calls it a hook user too.\n for (let changed = true; changed;) {\n changed = false\n for (const unit of units) {\n if (unit.hooks) continue\n if (unit.refs.some(({ name }) => /^use[A-Z]/.test(name) && units[owner.get(name) ?? -1]?.hooks)) { unit.hooks = true; changed = true }\n }\n }\n\n const importByLocal = new Map<string, string>()\n for (const statement of input.copyableImports) for (const local of importLocals(statement)) importByLocal.set(local, statement)\n const helperByName = new Map<string, string>()\n for (const helper of input.helperDeclarations) {\n for (const name of helperNames(helper)) helperByName.set(name, helper)\n }\n\n const body = bodyReferences(input.tree)\n\n const dependencies = (unit: Unit): Set<number> => {\n const found = new Set<number>()\n for (const { name } of unit.refs) {\n const target = owner.get(name)\n if (target !== undefined && target !== unit.index) found.add(target)\n }\n return found\n }\n const closureOf = (rootIndex: number): Set<number> => {\n const seen = new Set<number>([rootIndex])\n const queue = [rootIndex]\n while (queue.length) for (const next of dependencies(units[queue.pop()!])) {\n if (!seen.has(next)) { seen.add(next); queue.push(next) }\n }\n return seen\n }\n\n /** Why one declaration can never live in the client module, whatever the page does. */\n const intrinsicBlocker = (unit: Unit): string | undefined => {\n if (unit.blockers.length > 0) return `\\`${unit.names[0] ?? 'a dependency'}\\` ${unit.blockers[0]}`\n for (const { name, kind } of unit.refs) {\n if (owner.has(name) || importByLocal.has(name) || helperByName.has(name)) continue\n if (input.unavailableImports.has(name)) {\n return `\\`${unit.names[0]}\\` uses \\`${name}\\`, imported from ${input.unavailableImports.get(name)}, which is not available in the migrated project`\n }\n if (input.reactGlobals.has(name) || IMPLICIT_MODULE_NAMES.has(name) || JS_GLOBALS.has(name) || BROWSER_IDENTIFIERS.has(name)) continue\n if ((kind === 'tag' || kind === 'member') && isThallyBuiltinComponent(name)) continue\n return `\\`${unit.names[0]}\\` uses \\`${name}\\`, which is not declared or imported on the page`\n }\n return undefined\n }\n\n const roots = units\n .filter((unit) => unit.hooks && unit.names.some((name) => /^[A-Z]/.test(name)))\n .map((unit) => unit.index)\n const blocked: Array<{ name: string; reason: string }> = []\n const active = new Set(roots)\n\n const block = (rootIndex: number, reason: string): void => {\n active.delete(rootIndex)\n blocked.push({ name: units[rootIndex].names.find((name) => /^[A-Z]/.test(name)) ?? units[rootIndex].names[0], reason })\n }\n for (const rootIndex of roots) {\n for (const member of closureOf(rootIndex)) {\n const reason = intrinsicBlocker(units[member])\n if (reason) { block(rootIndex, reason); break }\n }\n }\n\n for (let round = 0; round <= units.length + 1; round += 1) {\n const inModule = new Set<number>()\n for (const rootIndex of active) for (const member of closureOf(rootIndex)) inModule.add(member)\n\n // A declaration that cannot render on the server, or that calls one that\n // cannot, must be moved; everything else the page still uses stays too.\n const clientSide = new Set<number>()\n for (const index of inModule) if (units[index].hooks || units[index].clientOnly) clientSide.add(index)\n for (let changed = true; changed;) {\n changed = false\n for (const index of inModule) {\n if (clientSide.has(index)) continue\n const unit = units[index]\n const callsClient = unit.refs.some(({ name, kind }) => {\n const target = owner.get(name)\n return target !== undefined && clientSide.has(target) && kind !== 'tag' && kind !== 'prop'\n })\n if (callsClient) { clientSide.add(index); changed = true }\n }\n }\n for (const rootIndex of active) clientSide.add(rootIndex)\n\n const nonModuleRefs: Array<Ref> = [...body.refs]\n for (const unit of units) if (!inModule.has(unit.index)) nonModuleRefs.push(...unit.refs)\n const referencedByPage = (index: number): boolean => units[index].names.some((name) => (\n body.mdastTags.has(name) || nonModuleRefs.some((ref) => ref.name === name)\n ))\n\n const kept = new Set<number>()\n for (const index of inModule) {\n if (clientSide.has(index)) continue\n if (!units[index].componentLike || referencedByPage(index)) kept.add(index)\n }\n for (let changed = true; changed;) {\n changed = false\n for (const index of kept) for (const dependency of dependencies(units[index])) {\n if (inModule.has(dependency) && !clientSide.has(dependency) && !kept.has(dependency)) { kept.add(dependency); changed = true }\n }\n }\n const moved = new Set<number>([...inModule].filter((index) => !kept.has(index)))\n\n // Page code that stays behind may only render a moved declaration, or hand\n // it to a client component; calling or reading it would run it on the server.\n const pageRefs: Array<Ref> = [...nonModuleRefs]\n for (const index of kept) pageRefs.push(...units[index].refs)\n const violation = [...moved].find((index) => clientSide.has(index) && units[index].names.some((name) => (\n pageRefs.some((ref) => ref.name === name && ref.kind !== 'tag' && ref.kind !== 'prop')\n )))\n if (violation !== undefined) {\n const names = units[violation].names\n const readsIt = (ref: Ref): boolean => names.includes(ref.name) && ref.kind !== 'tag' && ref.kind !== 'prop'\n // A component that only reads the moved value (a context provider next\n // to its consumer) can travel with it, so the value keeps one identity.\n const readers = units.filter((unit) => !moved.has(unit.index) && unit.refs.some(readsIt))\n const promotable = !body.refs.some(readsIt) && readers.every((unit) => (\n unit.componentLike && !active.has(unit.index)\n && [...closureOf(unit.index)].every((member) => !intrinsicBlocker(units[member]))\n ))\n if (promotable && readers.length > 0) {\n for (const unit of readers) active.add(unit.index)\n continue\n }\n for (const rootIndex of [...active]) {\n if (closureOf(rootIndex).has(violation)) {\n block(rootIndex, `\\`${names[0]}\\` is also read or called by page code that stays on the server`)\n }\n }\n continue\n }\n\n // A copy of mutable state would diverge: the page and the client module\n // would each count on their own. Refuse when page code reaches it.\n const reachable = new Set<number>([...kept].filter(referencedByPage))\n for (let changed = true; changed;) {\n changed = false\n for (const index of [...reachable]) for (const dependency of dependencies(units[index])) {\n if (kept.has(dependency) && !reachable.has(dependency)) { reachable.add(dependency); changed = true }\n }\n }\n const mutated = [...reachable].find((index) => units.some((unit) => unit.assigned.some((name) => units[index].names.includes(name))))\n if (mutated !== undefined) {\n for (const rootIndex of [...active]) {\n if (closureOf(rootIndex).has(mutated)) {\n block(rootIndex, `\\`${units[mutated].names[0]}\\` is mutable state that page code also uses, so a copy in the client module would diverge`)\n }\n }\n continue\n }\n\n const movedNames = [...moved].flatMap((index) => units[index].names)\n const registryNames = movedNames.filter((name) => body.mdastTags.has(name))\n const importNames = movedNames.filter((name) => pageRefs.some((ref) => ref.name === name))\n const moduleMembers = [...inModule].sort((a, b) => a - b)\n const moduleNames = new Set<string>(moduleMembers.flatMap((index) => units[index].names))\n const usedImports: Array<string> = []\n const usedHelpers: Array<string> = []\n for (const index of moduleMembers) {\n for (const { name } of units[index].refs) {\n const statement = importByLocal.get(name)\n if (statement && !usedImports.includes(statement)) usedImports.push(statement)\n const helper = helperByName.get(name)\n if (helper && !usedHelpers.includes(helper)) usedHelpers.push(helper)\n }\n }\n for (const statement of usedImports) for (const local of importLocals(statement)) moduleNames.add(local)\n for (const helper of usedHelpers) for (const name of helperNames(helper)) moduleNames.add(name)\n const pageReferences = new Set<string>(pageRefs.map((ref) => ref.name))\n for (const tag of body.mdastTags) pageReferences.add(tag)\n return {\n blocked,\n moved: [...moved].sort((a, b) => a - b),\n copied: [...kept].sort((a, b) => a - b),\n movedNames,\n moduleImports: usedImports,\n moduleHelpers: usedHelpers,\n registryNames: [...new Set(registryNames)],\n pageImportNames: [...new Set(importNames)],\n moduleNames,\n pageReferences,\n }\n }\n return { blocked, moved: [], copied: [], movedNames: [], moduleImports: [], moduleHelpers: [], registryNames: [], pageImportNames: [], moduleNames: new Set(), pageReferences: new Set() }\n}\n\n/** JSX tag names (capitalized, unbound) a module's source uses, for built-in lookup. */\nexport function unboundTags(moduleSource: string, boundNames: ReadonlySet<string>): Array<string> {\n const sf = parse(moduleSource)\n const tags = new Set<string>()\n for (const statement of sf.statements) {\n if (ts.isImportDeclaration(statement)) continue\n for (const ref of collectRefs(statement).refs) {\n if ((ref.kind === 'tag' || ref.kind === 'member') && /^[A-Z]/.test(ref.name) && !boundNames.has(ref.name)) tags.add(ref.name)\n }\n }\n return [...tags].sort()\n}\n\n/**\n * Globals a function may use when its source is copied into another module.\n * Deliberately narrower than JS_GLOBALS: `Function`, `globalThis`, `Reflect`,\n * `Proxy`, `fetch`, `console` and timers reach code execution or the network.\n */\nconst INLINE_SAFE_GLOBALS = new Set([\n 'undefined', 'NaN', 'Infinity', 'Math', 'JSON', 'String', 'Number', 'Boolean', 'Array', 'Date', 'Intl',\n 'parseInt', 'parseFloat', 'isNaN', 'isFinite', 'encodeURIComponent', 'decodeURIComponent',\n])\nconst FORBIDDEN_MEMBERS = new Set(['constructor', 'prototype'])\n\n/** True when a node sits where an object literal acts as a destructuring target (`({ a: x } = y)`, `[{ a: x }] = y`, `for ({ a: x } of y)`). */\nfunction isAssignmentTarget(node: ts.Node): boolean {\n let child = node\n for (let parent = node.parent; parent; child = parent, parent = parent.parent) {\n if (ts.isBinaryExpression(parent) && parent.operatorToken.kind === ts.SyntaxKind.EqualsToken) return parent.left === child\n if ((ts.isForOfStatement(parent) || ts.isForInStatement(parent)) && parent.initializer === child) return true\n if (!(ts.isPropertyAssignment(parent) || ts.isShorthandPropertyAssignment(parent) || ts.isArrayLiteralExpression(parent)\n || ts.isSpreadAssignment(parent) || ts.isSpreadElement(parent) || ts.isParenthesizedExpression(parent) || ts.isObjectLiteralExpression(parent))) return false\n }\n return false\n}\n\n/**\n * True when no member access in the node can walk to `Function`: `x.constructor`, `x['constructor']`, `x[key]`,\n * `__proto__`, and the same keys read through destructuring (`const { constructor: C } = String`, `({ [key]: C } = x)`).\n */\nfunction hasOnlySafeMemberAccess(root: ts.Node): boolean {\n const unsafeName = (name: string) => FORBIDDEN_MEMBERS.has(name) || name.startsWith('__')\n /** A property key is safe when it is a plain name or a static string/number that is not forbidden; any other computed key is not. */\n const safeKey = (name: ts.PropertyName | undefined, computedAllowed: boolean): boolean => {\n if (!name) return true\n if (ts.isComputedPropertyName(name)) {\n const expression = name.expression\n if (ts.isStringLiteralLike(expression)) return !unsafeName(expression.text)\n return ts.isNumericLiteral(expression) || computedAllowed\n }\n return !unsafeName(name.text)\n }\n let safe = true\n function visit(node: ts.Node): void {\n if (!safe) return\n if (ts.isPropertyAccessExpression(node) && unsafeName(node.name.text)) safe = false\n else if (ts.isElementAccessExpression(node)) {\n const argument = node.argumentExpression\n const literal = ts.isNumericLiteral(argument) || (ts.isStringLiteralLike(argument) && !unsafeName(argument.text))\n if (!literal) safe = false\n } else if (ts.isBindingElement(node)) {\n // `{ constructor }` has no propertyName; the bound identifier is the key.\n if (ts.isObjectBindingPattern(node.parent) && !safeKey(node.propertyName ?? (ts.isIdentifier(node.name) ? node.name : undefined), false)) safe = false\n } else if (ts.isObjectLiteralExpression(node)) {\n const target = isAssignmentTarget(node)\n for (const property of node.properties) {\n if ((ts.isPropertyAssignment(property) || ts.isShorthandPropertyAssignment(property)) && !safeKey(property.name, !target)) safe = false\n }\n }\n ts.forEachChild(node, visit)\n }\n visit(root)\n return safe\n}\n\n/** True when a function expression only reaches its own parameters, locals and a small set of pure built-ins, so its source can be copied verbatim to another module. */\nexport function isSelfContainedFunction(node: ts.Node): boolean {\n const { refs, assigned, awaitOutsideAsync, dynamicImport } = collectRefs(node)\n return assigned.length === 0 && !awaitOutsideAsync && !dynamicImport && refs.every((ref) => INLINE_SAFE_GLOBALS.has(ref.name))\n && hasOnlySafeMemberAccess(node)\n}\n\n/** Names a page's own ESM source (imports and top-level declarations) makes available to its expressions. */\nexport function pageScopeNames(esmSources: ReadonlyArray<string>): Set<string> {\n const names = new Set<string>()\n for (const source of esmSources) {\n for (const statement of parse(source).statements) {\n for (const local of importLocals(statement.getText())) names.add(local)\n if (ts.isImportDeclaration(statement)) continue\n const target = ts.isExportDeclaration(statement) || ts.isExportAssignment(statement) ? undefined : statement\n if (target) for (const name of declaredNames(target).names) names.add(name)\n }\n }\n return names\n}\n\n/**\n * Free identifiers in an MDX expression that neither the page, the expression\n * itself, a JS built-in nor the compiled-MDX scope (`props`) defines. Evaluating\n * one throws a ReferenceError at render time.\n */\nexport function unresolvedExpressionNames(expression: string, pageNames: ReadonlySet<string>): Array<string> {\n const names = new Set<string>()\n for (const { name, kind } of expressionRefs(expression)) {\n // `typeof missing` is safe at runtime; only a bare reference throws.\n if (kind === 'typeof') continue\n if (!name || pageNames.has(name) || JS_GLOBALS.has(name) || BROWSER_IDENTIFIERS.has(name) || SERVER_ONLY_IDENTIFIERS.has(name)\n || name === 'props' || name === 'React') continue\n if ((kind === 'tag' || kind === 'member') && isThallyBuiltinComponent(name)) continue\n names.add(name)\n }\n return [...names]\n}\n","/**\n * The one frontmatter parser for authored content in this package.\n *\n * Only YAML is supported. Parsing delimiters locally keeps legacy JavaScript\n * engines out of migration processes and treats language-tagged blocks as\n * opaque metadata rather than executable third-party input.\n *\n * Internal module — deliberately not part of the package's public API. The\n * app-side original is `src/lib/frontmatter.ts`; the packages keep their own\n * copies because they cannot import from the app.\n */\n\nimport { parse as parseYaml } from 'yaml'\n\ninterface ParsedFrontmatter {\n content: string\n data: Record<string, unknown>\n /** Set when the frontmatter block was invalid YAML; `data` is a best-effort salvage. */\n error?: string\n}\n\n/**\n * Best-effort recovery for frontmatter that failed to parse as a whole block:\n * try each top-level `key: value` line on its own. A line that is itself\n * invalid YAML (e.g. an unescaped reserved character) is skipped rather than\n * aborting the whole page.\n */\nfunction salvageFrontmatterLines(matter: string): Record<string, unknown> {\n const data: Record<string, unknown> = {}\n for (const line of matter.split(/\\r?\\n/)) {\n if (!/^[A-Za-z_][\\w-]*:\\s?/.test(line)) continue\n try {\n const parsedLine = parseYaml(line)\n if (parsedLine && typeof parsedLine === 'object' && !Array.isArray(parsedLine)) {\n Object.assign(data, parsedLine)\n }\n } catch {\n // Line is unrecoverable on its own; drop just that field.\n }\n }\n return data\n}\n\n/** Parse frontmatter without any path that can execute the content. */\nexport function parseFrontmatter(raw: string): ParsedFrontmatter {\n const source = raw.charCodeAt(0) === 0xfeff ? raw.slice(1) : raw\n const opening = /^---([^\\r\\n]*)\\r?\\n/.exec(source)\n if (!opening || opening[1].startsWith('-')) return { content: source, data: {} }\n\n const language = opening[1].trim().toLowerCase()\n const remainder = source.slice(opening[0].length)\n const closing = /^---[ \\t]*\\r?$/m.exec(remainder)\n const matter = closing ? remainder.slice(0, closing.index) : remainder\n let content = closing ? remainder.slice(closing.index + closing[0].length) : ''\n if (content.startsWith('\\r\\n')) content = content.slice(2)\n else if (content.startsWith('\\n')) content = content.slice(1)\n\n if (matter.trim() === '' || !['', 'yaml', 'yml'].includes(language)) {\n return { content, data: {} }\n }\n try {\n const parsed = parseYaml(matter)\n return {\n content,\n data: parsed && typeof parsed === 'object' && !Array.isArray(parsed)\n ? parsed as Record<string, unknown>\n : {},\n }\n } catch (err) {\n return {\n content,\n data: salvageFrontmatterLines(matter),\n error: err instanceof Error ? err.message : String(err),\n }\n }\n}\n","/**\n * Move repository-owned MDX components into the customer component registry.\n * Source is parsed, never evaluated. Every dependency must remain inside the\n * documentation root and traverse only ordinary files (including its parents).\n */\n\nimport { createHash } from 'node:crypto'\nimport { existsSync, lstatSync, readFileSync } from 'node:fs'\nimport { basename, dirname, extname, posix, relative, resolve } from 'node:path'\nimport postcss from 'postcss'\nimport selectorParser from 'postcss-selector-parser'\nimport remarkMdx from 'remark-mdx'\nimport remarkParse from 'remark-parse'\nimport ts from 'typescript'\nimport { unified } from 'unified'\n\nimport { parseFrontmatter } from './frontmatter.js'\nimport { functionDeclaredNames, isFunctionInitializer, mdxComment, normalizeHtmlComments, normalizeExplicitHeadingIds, replaceOutsideCodeAndComments } from './mdx.js'\nimport { resolveWithin } from './path.js'\nimport { planInlineExtraction, unboundTags } from './inline-extraction.js'\nimport type { MigrationWarning, RenderedMigrationFile } from './types.js'\n\ninterface MdxNode {\n type: string\n name?: string | null\n value?: string\n attributes?: Array<{\n name?: string\n type: string\n value?: string | { type?: string; value?: string } | null\n position?: { start: { offset?: number }; end: { offset?: number } }\n }>\n children?: Array<MdxNode>\n position?: { start: { offset?: number }; end: { offset?: number } }\n}\n\ninterface Replacement { start: number; end: number; value: string }\ninterface Binding { local: string; imported: string; source: string }\n\n/** Make JSX-indented fences parse as code before MDX import analysis runs. */\nexport function normalizeIndentedFences(content: string): string {\n const lines = content.split('\\n')\n let active: { indent: string; marker: string } | undefined\n let ordinaryFence: string | undefined\n let indentedJsx: { tag: string; indent: string } | undefined\n let listIndent: number | undefined\n return lines.map((line) => {\n const trimmed = line.trimStart()\n // A fenced example inside list-indented JSX must move together with its\n // container. Unindenting only the fence leaves the tag unclosed; keeping\n // everything indented lets textual import passes mistake example imports\n // for live MDX imports.\n if (indentedJsx) {\n const normalized = line.startsWith(indentedJsx.indent) ? line.slice(indentedJsx.indent.length) : line\n if (normalized.trimStart().startsWith(`</${indentedJsx.tag}>`)) indentedJsx = undefined\n return normalized\n }\n if (ordinaryFence) {\n const closing = trimmed.match(/^(`{3,}|~{3,})\\s*$/)?.[1]\n if (closing && closing[0] === ordinaryFence[0] && closing.length >= ordinaryFence.length) ordinaryFence = undefined\n return line\n }\n if (!active) {\n const listItem = line.match(/^([ \\t]*)(?:[-*+]|\\d+[.)])\\s/)\n const indentation = line.length - trimmed.length\n if (listItem) listIndent = listItem[1].length\n else if (trimmed && listIndent !== undefined && indentation <= listIndent) listIndent = undefined\n const jsx = line.match(/^([ \\t]{2,})<([A-Z][\\w.]*)\\b[^>]*>\\s*$/)\n if (jsx && listIndent !== undefined && jsx[1].length > listIndent && !trimmed.endsWith('/>')) {\n indentedJsx = { tag: jsx[2], indent: jsx[1] }\n return line.slice(jsx[1].length)\n }\n const match = line.match(/^([ \\t]{4,})(`{3,}|~{3,})/)\n if (match) {\n // A fence under a Markdown list is already valid. Moving it left\n // ends the list item and detaches its example from the numbered step.\n if (listIndent !== undefined && match[1].length > listIndent) {\n ordinaryFence = match[2]\n return line\n }\n active = { indent: match[1], marker: match[2] }\n return line.slice(match[1].length)\n }\n const ordinary = line.match(/^[ \\t]{0,3}(`{3,}|~{3,})/)\n if (ordinary) ordinaryFence = ordinary[1]\n return line\n }\n const normalized = line.startsWith(active.indent) ? line.slice(active.indent.length) : line\n const closing = normalized.trimStart().match(/^(`{3,}|~{3,})\\s*$/)?.[1]\n if (closing && closing[0] === active.marker[0] && closing.length >= active.marker.length) active = undefined\n return normalized\n }).join('\\n')\n}\n\n/** Lift a self-contained data helper out of a copied client module for server MDX expressions. */\nfunction staticServerHelper(path: string, imported: string, local: string): string | null {\n if (imported === 'default') return null\n const parsed = ts.createSourceFile(path, readFileSync(path, 'utf8'), ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX)\n const isLiteral = (node: ts.Expression): boolean => {\n if (ts.isParenthesizedExpression(node)) return isLiteral(node.expression)\n if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node) || ts.isNumericLiteral(node)) return true\n if ([ts.SyntaxKind.TrueKeyword, ts.SyntaxKind.FalseKeyword, ts.SyntaxKind.NullKeyword].includes(node.kind)) return true\n if (ts.isPrefixUnaryExpression(node)) return [ts.SyntaxKind.PlusToken, ts.SyntaxKind.MinusToken].includes(node.operator) && ts.isNumericLiteral(node.operand)\n if (ts.isArrayLiteralExpression(node)) return node.elements.every((element) => ts.isExpression(element) && isLiteral(element))\n if (ts.isObjectLiteralExpression(node)) return node.properties.every((property) => ts.isPropertyAssignment(property)\n && (ts.isIdentifier(property.name) || ts.isStringLiteral(property.name) || ts.isNumericLiteral(property.name))\n && isLiteral(property.initializer))\n return false\n }\n for (const statement of parsed.statements) {\n if (!ts.isVariableStatement(statement) || !statement.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)) continue\n for (const declaration of statement.declarationList.declarations) {\n if (!ts.isIdentifier(declaration.name) || declaration.name.text !== imported || !declaration.initializer || !ts.isArrowFunction(declaration.initializer)) continue\n const arrow = declaration.initializer\n if (arrow.parameters.length > 0) continue\n const result = ts.isBlock(arrow.body)\n ? arrow.body.statements.length === 1 && ts.isReturnStatement(arrow.body.statements[0]) ? arrow.body.statements[0].expression : undefined\n : arrow.body\n if (result && isLiteral(result)) return `export const ${local} = () => (${result.getText(parsed)});`\n }\n }\n return null\n}\n\n/** A bare `id`/`videoId`-style attribute value is safe to embed in an `iframe src` only when it looks like a real YouTube video id. */\nconst YOUTUBE_VIDEO_ID = /^[\\w-]{1,64}$/\n/** Packages known to take a literal YouTube video id prop (as opposed to a URL, or a non-YouTube player). */\nconst YOUTUBE_ID_PACKAGES = new Set(['react-lite-youtube-embed', 'react-youtube', '@justinribeiro/lite-youtube'])\nconst YOUTUBE_URL = /(?:youtube\\.com\\/(?:watch\\?v=|embed\\/|shorts\\/)|youtu\\.be\\/)([\\w-]{1,64})/i\n\n/**\n * Only a small, known set of YouTube embed packages get a working `<iframe>`\n * replacement; every other removed component (Vimeo, Loom, a bespoke\n * in-house player, or any package this doesn't recognize) falls back to the\n * plain comment stub, since guessing at an unfamiliar player's embed URL\n * shape would silently render the wrong video or nothing at all.\n */\nfunction youtubeEmbedVideoId(node: MdxNode, specifier: string): string | undefined {\n const attribute = (attributeName: string): string | undefined => {\n const raw = node.attributes?.find((entry) => entry.name === attributeName)?.value\n return typeof raw === 'string' ? raw : undefined\n }\n if (YOUTUBE_ID_PACKAGES.has(specifier)) {\n const id = attribute('id') ?? attribute('videoId')\n return id && YOUTUBE_VIDEO_ID.test(id) ? id : undefined\n }\n if (specifier === 'react-player') {\n const url = attribute('url')\n const match = url ? YOUTUBE_URL.exec(url) : null\n return match ? match[1] : undefined\n }\n return undefined\n}\n\nconst parser = unified().use(remarkParse).use(remarkMdx)\nconst CODE_EXTENSIONS = new Set(['.js', '.jsx', '.ts', '.tsx', '.mjs'])\nconst DATA_EXTENSIONS = new Set(['.json', '.css', '.svg', '.png', '.jpg', '.jpeg', '.webp', '.gif', '.avif', '.woff', '.woff2'])\n/**\n * Binary asset types a failed component/module import can still be rescued\n * as: copied verbatim to `public/` and bound to its URL string, rather than\n * losing the reference outright. Includes types `DATA_EXTENSIONS` already\n * lets `copyGraph` copy as a JS-module asset (an image can still fail that\n * path for other reasons — budget, a symlink) and types it never would\n * (`.docx`, `.pdf`: not importable as a JS module, but a real static file).\n */\nconst RESCUABLE_ASSET_EXTENSIONS = new Set(['.svg', '.png', '.jpg', '.jpeg', '.webp', '.gif', '.avif', '.ico', '.docx', '.pdf'])\n\n/**\n * Tiny equivalents for the handful of Docusaurus theme/runtime imports a\n * copied component commonly uses, so the component still copies instead of\n * failing outright with \"external package requires manual installation\" —\n * the same reasoning as `SCAFFOLD_PROVIDED_IMPORTS`, just for names that\n * only exist inside a Docusaurus build. Deliberately small: anything else\n * Docusaurus-specific (`@docusaurus/Translate`, a theme-common hook, ...)\n * still fails copyGraph and is handled by the dead-import-removal fallback\n * (the catch block below) instead of growing this into a Docusaurus shim\n * layer.\n */\nconst DOCUSAURUS_THEME_SHIMS: Record<string, { filename: string; content: string }> = {\n '@theme/CodeBlock': {\n filename: 'docusaurus-code-block.tsx',\n content: [\n \"import type { ReactNode } from 'react'\",\n '',\n 'export default function CodeBlock({ children, className }: { children?: ReactNode; className?: string }) {',\n ' return (',\n ' <pre className={className}>',\n ' <code>{children}</code>',\n ' </pre>',\n ' )',\n '}',\n '',\n ].join('\\n'),\n },\n '@docusaurus/BrowserOnly': {\n filename: 'docusaurus-browser-only.tsx',\n content: [\n \"'use client'\",\n '',\n \"import { useEffect, useState, type ReactNode } from 'react'\",\n '',\n 'export default function BrowserOnly({ children, fallback = null }: { children: () => ReactNode; fallback?: ReactNode }) {',\n ' const [mounted, setMounted] = useState(false)',\n ' useEffect(() => { setMounted(true) }, [])',\n ' return mounted ? <>{children()}</> : <>{fallback}</>',\n '}',\n '',\n ].join('\\n'),\n },\n}\nconst SHARED_IMPORTS = new Set(['react', 'react/jsx-runtime', 'react/jsx-dev-runtime'])\n/**\n * Packages the standalone starter already installs as its own runtime\n * dependencies. An MDX page's import of one of these (or of a subpath such\n * as `next/navigation` or `lucide-react/dynamic`), even outside JSX, is not\n * \"unavailable\": the package is really there, so the page keeps the import\n * and any extracted client module gets a copy of it.\n *\n * `@/…` path aliases are deliberately absent: in a source repository they\n * point at that site's own code, not at Thally's.\n *\n * Drift-guarded by the \"SCAFFOLD_PROVIDED_IMPORTS drift guard\" test in\n * `packages/migrate/src/__tests__/components.test.ts`.\n */\nexport const SCAFFOLD_PROVIDED_IMPORTS: ReadonlySet<string> = new Set(['next', 'react-dom', 'clsx', 'lucide-react', 'tailwind-merge'])\nfunction isScaffoldProvidedImport(specifier: string): boolean {\n // Next aliases react-dom/server so renderToString/renderToStaticMarkup throw\n // at render; treat it like an unavailable package rather than keep the page.\n if (/^react-dom\\/server(?:[./]|$)/.test(specifier)) return false\n return [...SCAFFOLD_PROVIDED_IMPORTS].some((name) => specifier === name || specifier.startsWith(`${name}/`))\n}\n/** How a warning names an import that is not available in the migrated project. */\nfunction unavailableImportLabel(specifier: string): string {\n return specifier.startsWith('@/')\n ? `the path alias '${specifier}' (the source site's own code, which the migration does not copy)`\n : `the npm package '${specifier}'`\n}\nconst REACT_GLOBALS = new Set([\n 'useState', 'useEffect', 'useLayoutEffect', 'useMemo', 'useCallback', 'useRef',\n 'useReducer', 'useContext', 'useId', 'useTransition', 'useDeferredValue',\n 'useImperativeHandle', 'useSyncExternalStore', 'useInsertionEffect',\n 'useOptimistic', 'useActionState', 'use', 'createContext', 'forwardRef', 'memo',\n])\nconst MAX_COMPONENT_FILES = 300\nconst MAX_COMPONENT_BYTES = 20_000_000\nconst MAX_FILE_BYTES = 2_000_000\n\n/**\n * Where each Thally built-in MDX component actually lives, for building the\n * `MintlifyComponents` shim (below). This must import each leaf module\n * directly rather than the app's `mdx-components.tsx` registry: that\n * registry pulls in `src/mdx/custom-components.tsx`, which re-exports every\n * migrated component — including this very generated file — so importing\n * it here would be circular (`ReferenceError: Cannot access 'X' before\n * initialization` at build time). Kept in sync with\n * `src/components/mdx/builtin-components.tsx`'s own imports by hand; a drift\n * here only means `MintlifyComponents.<Name>` is `undefined` for a name\n * added there, not a crash.\n */\nconst MINTLIFY_COMPONENT_MODULES: ReadonlyArray<{ module: string; names: ReadonlyArray<string> }> = [\n { module: '@/components/mdx/note', names: ['Note'] },\n { module: '@/components/mdx/agent-prompt', names: ['AgentPrompt'] },\n { module: '@/components/mdx/code-blocks', names: ['CodeGroup'] },\n { module: '@/components/mdx/rich-content', names: ['Columns', 'Frame', 'Hero'] },\n { module: '@/components/mdx/accordion', names: ['Accordion', 'AccordionGroup'] },\n { module: '@/components/mdx/content-cards', names: ['Card', 'CardGroup', 'Tile', 'TileGroup'] },\n { module: '@/components/mdx/content-icon', names: ['Icon'] },\n { module: '@/components/mdx/content-inline', names: ['Badge', 'Tooltip'] },\n { module: '@/components/mdx/color', names: ['Color'] },\n { module: '@/components/mdx/content-metadata', names: ['Update'] },\n { module: '@/components/mdx/panel', names: ['Panel', 'ContentPanel', 'InlinePanel'] },\n { module: '@/components/mdx/examples', names: ['RequestExample', 'ResponseExample', 'InlineRequestExample', 'InlineResponseExample'] },\n { module: '@/components/mdx/prompt', names: ['Prompt', 'PromptAssistant', 'PromptUser', 'Terminal', 'TerminalInput', 'TerminalOutput'] },\n { module: '@/components/mdx/file-tree', names: ['Tree', 'Folder', 'File'] },\n { module: '@/components/mdx/api-fields', names: ['ResponseField', 'ParamField', 'Expandable'] },\n { module: '@/components/mdx/mermaid', names: ['Mermaid'] },\n { module: '@/components/mdx/view', names: ['Embed', 'LegacyView', 'View'] },\n { module: '@/components/mdx/github-card', names: ['GitHub'] },\n { module: '@/components/mdx/visibility', names: ['Agent', 'Human', 'Visibility'] },\n { module: '@/components/mdx/steps', names: ['Steps', 'Step'] },\n { module: '@/components/mdx/content-tabs', names: ['Tabs', 'Tab'] },\n]\n\n/** Builtins mapped straight through to their same-named import (aliased, see below). */\nconst MINTLIFY_DIRECT_COMPONENTS = [\n 'AccordionGroup', 'Hero', 'Card', 'CardGroup', 'Columns', 'Frame', 'Accordion', 'Tooltip',\n 'Icon', 'Steps', 'Step', 'Tabs', 'Tab', 'Badge', 'Update', 'RequestExample', 'ResponseExample',\n 'Panel', 'ContentPanel', 'InlinePanel', 'InlineRequestExample', 'InlineResponseExample',\n 'Tile', 'TileGroup', 'Prompt', 'PromptUser', 'PromptAssistant', 'Terminal', 'TerminalInput',\n 'TerminalOutput', 'AgentPrompt', 'Color', 'Tree', 'Folder', 'File', 'ResponseField',\n 'ParamField', 'Expandable', 'Mermaid', 'View', 'Embed', 'LegacyView', 'GitHub', 'Visibility',\n 'Human', 'Agent', 'CodeGroup',\n]\n\n/**\n * Mintlify's implicit `MintlifyComponents` global for a copied `.jsx`\n * snippet (`const { Card } = MintlifyComponents;`, no import) — an object\n * of Thally's built-in equivalents. Every import is aliased (`Card as\n * __mintlify_Card`) so none of ~50 bare names land in the snippet's own top\n * level scope and risk colliding with something it already declares; only\n * the single `MintlifyComponents` identifier is introduced unaliased.\n */\nfunction mintlifyComponentsShim(): string {\n const alias = (name: string) => `__mintlify_${name}`\n const imports = MINTLIFY_COMPONENT_MODULES\n .map(({ module, names }) => `import { ${names.map((name) => `${name} as ${alias(name)}`).join(', ')} } from '${module}';`)\n .join('\\n')\n const entries = [\n // The callout family all render through Note, whose `type` mdx-components.tsx\n // sets for each name; Callout and Latex mirror that file's own handling.\n `Info: (props) => <${alias('Note')} type=\"info\" {...props} />`,\n `Warning: (props) => <${alias('Note')} type=\"warning\" {...props} />`,\n `Check: (props) => <${alias('Note')} type=\"check\" {...props} />`,\n `Danger: (props) => <${alias('Note')} type=\"danger\" {...props} />`,\n `Error: (props) => <${alias('Note')} type=\"danger\" {...props} />`,\n `Note: (props) => <${alias('Note')} type=\"note\" {...props} />`,\n `Tip: (props) => <${alias('Note')} type=\"tip\" {...props} />`,\n `Callout: (props) => <${alias('Note')} {...props} />`,\n `Latex: ({ children }) => <code>{children}</code>`,\n ...MINTLIFY_DIRECT_COMPONENTS.map((name) => `${name}: ${alias(name)}`),\n `Github: ${alias('GitHub')}`,\n ]\n return `${imports}\\nconst MintlifyComponents = {\\n ${entries.join(',\\n ')},\\n};`\n}\n\nfunction applyReplacements(source: string, replacements: Array<Replacement>): string {\n return replacements.sort((a, b) => b.start - a.start).reduce(\n (text, edit) => text.slice(0, edit.start) + edit.value + text.slice(edit.end), source,\n )\n}\n\nfunction hash(value: string): string {\n return createHash('sha256').update(value).digest('hex').slice(0, 12)\n}\n\nfunction portableSpecifier(path: string): string {\n // Customer tsconfigs do not opt into allowImportingTsExtensions. Next's\n // bundler resolves these extensionless source imports without that flag.\n return path.replace(/\\.tsx?$/, '')\n}\n\n/**\n * CSS Modules in the Thally scaffold require a local class or ID in each\n * selector branch. A global-only branch cannot be made local by wrapping it\n * in :global(...); Turbopack still rejects it. Omit those branches while\n * preserving local rules, and report the omission for manual review.\n */\nfunction sanitizeCssModuleSelectors(css: string): { css: string; omitted: number } {\n const root = postcss.parse(css)\n let omitted = 0\n root.walkRules((rule) => {\n // Keyframe selectors and CSS Modules' value exports are not DOM\n // selectors; removing them breaks otherwise portable local styles.\n if (rule.parent?.type === 'atrule' && /keyframes$/i.test(rule.parent.name)) return\n if (/^:export$|^:import\\(/.test(rule.selector.trim())) return\n const selectors = selectorParser().astSync(rule.selector)\n selectors.each((selector) => {\n let hasLocalClassOrId = false\n selector.walk((node) => {\n if (node.type !== 'class' && node.type !== 'id') return\n let parent = node.parent\n while (parent) {\n if (parent.type === 'pseudo' && parent.value === ':global') return\n parent = parent.parent\n }\n hasLocalClassOrId = true\n })\n if (!hasLocalClassOrId) {\n selector.remove()\n omitted++\n }\n })\n if (selectors.nodes.length === 0) rule.remove()\n else rule.selector = selectors.toString()\n })\n return { css: root.toString(), omitted }\n}\n\nfunction sourceFile(source: string, filename: string): ts.SourceFile {\n return ts.createSourceFile(filename, source, ts.ScriptTarget.Latest, true,\n /\\.[cm]?ts$/.test(filename) ? ts.ScriptKind.TS : ts.ScriptKind.TSX)\n}\n\nfunction walk(node: MdxNode, visitor: (node: MdxNode) => void): void {\n visitor(node)\n for (const child of node.children ?? []) walk(child, visitor)\n}\n\nconst BROWSER_GLOBALS = new Set(['window', 'document', 'navigator', 'localStorage', 'sessionStorage', 'location', 'history', 'matchMedia',\n 'requestAnimationFrame', 'cancelAnimationFrame', 'requestIdleCallback', 'IntersectionObserver', 'ResizeObserver', 'MutationObserver', 'alert', 'confirm', 'prompt', 'self',\n 'setTimeout', 'setInterval', 'clearTimeout', 'clearInterval', 'addEventListener', 'removeEventListener', 'getComputedStyle', 'XMLHttpRequest', 'WebSocket'])\n\n/**\n * Whether a copied module must run on the client: it declares `'use client'`,\n * calls a hook, attaches an event handler, touches a browser global, builds a\n * context, extends a class, or imports anything besides React (a package or\n * scaffold module may itself need the client). Relative imports are judged on\n * their own copy. Anything else is a pure server module.\n */\nfunction needsClientBoundary(source: ts.SourceFile): boolean {\n let needed = false\n const isHook = (name: string): boolean => /^use(?:[A-Z0-9]|$)/.test(name) || name === 'createContext'\n function visitNode(node: ts.Node): void {\n if (needed) return\n if (ts.isExpressionStatement(node) && ts.isStringLiteral(node.expression) && node.expression.text === 'use client') needed = true\n else if ((ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) {\n const specifier = node.moduleSpecifier.text\n if (!specifier.startsWith('.') && !SHARED_IMPORTS.has(specifier)) needed = true\n } else if (ts.isCallExpression(node)) {\n const callee = ts.isPropertyAccessExpression(node.expression) ? node.expression.name.text : ts.isIdentifier(node.expression) ? node.expression.text : ''\n if (isHook(callee) || callee === 'forwardRef' || callee === 'addEventListener' || callee === 'removeEventListener' || node.expression.kind === ts.SyntaxKind.ImportKeyword) needed = true\n } else if (ts.isJsxAttribute(node) && ts.isIdentifier(node.name) && (/^on[A-Z]/.test(node.name.text) || node.name.text === 'ref')) needed = true\n // Spread props can carry handlers into a DOM element; fail closed.\n else if (ts.isJsxSpreadAttribute(node) && (ts.isJsxOpeningLikeElement(node.parent.parent)) && ts.isIdentifier(node.parent.parent.tagName) && /^[a-z]/.test(node.parent.parent.tagName.text)) needed = true\n // A handler prop read or destructured, or a hook referenced without being called (`const h = React.useState`).\n else if (ts.isIdentifier(node) && (isHook(node.text) || /^on[A-Z]/.test(node.text)) && !ts.isJsxAttribute(node.parent)) needed = true\n else if (ts.isIdentifier(node) && BROWSER_GLOBALS.has(node.text) && !(ts.isPropertyAccessExpression(node.parent) && node.parent.name === node)\n && !(ts.isPropertyAssignment(node.parent) && node.parent.name === node)) needed = true\n else if (ts.isClassLike(node) && node.heritageClauses?.some((clause) => clause.token === ts.SyntaxKind.ExtendsKeyword)) needed = true\n ts.forEachChild(node, visitNode)\n }\n visitNode(source)\n return needed\n}\n\nfunction implicitReactImports(source: ts.SourceFile): string {\n const bindings = new Set<string>()\n const references = new Set<string>()\n function bind(name: ts.BindingName): void {\n if (ts.isIdentifier(name)) bindings.add(name.text)\n else for (const element of name.elements) if (ts.isBindingElement(element)) bind(element.name)\n }\n for (const statement of source.statements) {\n if (ts.isImportDeclaration(statement) && statement.importClause) {\n const clause = statement.importClause\n if (clause.name) bindings.add(clause.name.text)\n if (clause.namedBindings) {\n if (ts.isNamespaceImport(clause.namedBindings)) bindings.add(clause.namedBindings.name.text)\n else for (const element of clause.namedBindings.elements) bindings.add(element.name.text)\n }\n }\n if (ts.isVariableStatement(statement)) for (const declaration of statement.declarationList.declarations) bind(declaration.name)\n if ((ts.isFunctionDeclaration(statement) || ts.isClassDeclaration(statement)) && statement.name) bindings.add(statement.name.text)\n }\n function visit(node: ts.Node): void {\n if (ts.isIdentifier(node) && !(ts.isPropertyAccessExpression(node.parent) && node.parent.name === node)\n && !(ts.isPropertyAssignment(node.parent) && node.parent.name === node)) references.add(node.text)\n ts.forEachChild(node, visit)\n }\n visit(source)\n const hooks = [...REACT_GLOBALS].filter((name) => references.has(name) && !bindings.has(name)).sort()\n // Mintlify's own snippet convention exposes its built-in MDX components as\n // an implicit global too (`const { Card } = MintlifyComponents;`, no\n // import).\n const usesMintlifyComponents = references.has('MintlifyComponents') && !bindings.has('MintlifyComponents')\n // Mintlify also makes built-ins available as bare JSX names inside snippet\n // modules. A copied module runs outside Mintlify's MDX scope, so explicitly\n // import every unbound name it uses before the destination build renders it.\n const implicitComponents = MINTLIFY_COMPONENT_MODULES.map(({ module, names }) => {\n const needed = names.filter((name) => references.has(name) && !bindings.has(name))\n return needed.length ? `import { ${needed.join(', ')} } from '${module}';` : ''\n }).filter(Boolean)\n return [\n references.has('React') && !bindings.has('React') ? \"import * as React from 'react';\" : '',\n hooks.length ? `import { ${hooks.join(', ')} } from 'react';` : '',\n ...implicitComponents,\n usesMintlifyComponents ? mintlifyComponentsShim() : '',\n ].filter(Boolean).join('\\n')\n}\n\n/**\n * A component extracted into its own client module (see `createComponentMigrator`'s\n * inline-hook extraction below) loses the page's implicit access to Thally's\n * built-in MDX components: unlike a component that stays inline in the page,\n * this module never receives a `components` prop to read a name such as\n * `CodeBlock` from. Any JSX tag it uses that isn't locally declared or\n * imported is resolved instead from `builtinMdxComponents`, the same\n * built-in registry every MDX page reads from — imported directly rather\n * than through `useMDXComponents`, which in turn imports the customer\n * registry that imports every extracted module, closing an import cycle\n * that throws at runtime once bundled.\n */\nfunction resolveInlineBuiltinReferences(source: string, moduleNames: ReadonlySet<string>): string {\n const unresolved = unboundTags(source, moduleNames)\n if (unresolved.length === 0) return source\n return [\n \"import { builtinMdxComponents } from '@/components/mdx/builtin-components';\",\n `const { ${unresolved.join(', ')} } = builtinMdxComponents;`,\n source,\n ].join('\\n')\n}\n\nfunction imports(statement: ts.ImportDeclaration): Array<Binding> {\n if (!ts.isStringLiteral(statement.moduleSpecifier) || !statement.importClause) return []\n const clause = statement.importClause\n if (clause.isTypeOnly) return []\n const source = statement.moduleSpecifier.text\n const bindings: Array<Binding> = clause.name ? [{ local: clause.name.text, imported: 'default', source }] : []\n if (clause.namedBindings && ts.isNamedImports(clause.namedBindings)) {\n for (const element of clause.namedBindings.elements) {\n if (!element.isTypeOnly) bindings.push({ local: element.name.text, imported: element.propertyName?.text ?? element.name.text, source })\n }\n }\n return bindings\n}\n\n/**\n * Every component this migrator actually renders in a client boundary — a\n * copied import (`register`, always prefixed 'use client' by `copyGraph`) or\n * an inline-extracted interactive block (`Inline<n>`) — is renamed to one of\n * these two shapes in the transformed body. No other JSX tag (a Thally\n * built-in, an unregistered/removed component) ever matches.\n */\nconst EXTRACTED_CLIENT_COMPONENT_TAG = /^(?:Migrated[0-9a-f]+|Inline\\d+)$/\n\n/**\n * Thally's own runtime `src/components/mdx/builtin-components.tsx` registry names\n * whose backing module is a 'use client' file under `src/components/mdx/`.\n * Passing a page-authored function as a prop into any of these also throws\n * \"Functions cannot be passed directly to Client Components\" at render, the\n * same as an extracted component — so they are confirmed exclusion targets\n * too, not just unconfirmed-and-warned.\n *\n * This list is a snapshot, not a live read of the app (a published migrate\n * package cannot import from the app's `src/`). It is drift-guarded by\n * `src/components/mdx/client-registry.test.ts`, which recomputes the same\n * set from `src/components/mdx/*.tsx` and `builtin-components.tsx` and fails CI\n * if this snapshot goes stale.\n *\n * Source file per name (all under `src/components/mdx/`):\n * Accordion, AccordionGroup -> accordion.tsx\n * AgentPrompt -> agent-prompt.tsx\n * ResponseField, ParamField,\n * Expandable -> api-fields.tsx\n * CodeGroup -> code-blocks.tsx\n * Badge, Tooltip -> content-inline.tsx\n * Tabs, Tab -> content-tabs.tsx\n * RequestExample, ResponseExample,\n * InlineRequestExample,\n * InlineResponseExample -> examples.tsx\n * Tree, Folder, File -> file-tree.tsx\n * Mermaid -> mermaid.tsx\n * Panel, ContentPanel,\n * InlinePanel -> panel.tsx\n * Prompt, PromptUser,\n * PromptAssistant, Terminal,\n * TerminalInput, TerminalOutput -> prompt.tsx\n * View, Embed, LegacyView -> view.tsx\n * Color, Color.Item -> color.tsx (server) spreads props into\n * color-item.tsx's 'use client'\n * ColorItemClient — a re-export, not a\n * direct 'use client' file, so it is\n * listed by hand (see the override map\n * in client-registry.test.ts)\n */\nexport const CLIENT_BUILTIN_COMPONENT_TAGS: ReadonlySet<string> = new Set([\n 'Accordion', 'AccordionGroup',\n 'AgentPrompt',\n 'ResponseField', 'ParamField', 'Expandable',\n 'CodeGroup', 'CodeBlock',\n 'Badge', 'Tooltip',\n 'Tabs', 'Tab',\n 'Steps', 'Step',\n 'RequestExample', 'ResponseExample', 'InlineRequestExample', 'InlineResponseExample',\n 'Tree', 'FileTree', 'Folder', 'File',\n 'Mermaid',\n 'Panel', 'ContentPanel', 'InlinePanel',\n 'Prompt', 'PromptUser', 'PromptAssistant', 'Terminal', 'TerminalInput', 'TerminalOutput',\n 'View', 'Embed', 'LegacyView',\n 'Color', 'Color.Item',\n])\n\nfunction isConfirmedClientBoundaryTag(name: string): boolean {\n return EXTRACTED_CLIENT_COMPONENT_TAG.test(name) || CLIENT_BUILTIN_COMPONENT_TAGS.has(name)\n}\n\n/**\n * A JSX attribute's raw expression source (`prop={<this>}`), for the two\n * shapes that end up passing a function value: a bare reference to a\n * page-declared function (`declaredNames`), or a function written inline\n * right there (`() => ...`, `function () { ... }`). Only `mdxJsxAttribute`\n * nodes with an expression value carry a source string here; a string\n * literal attribute (`title=\"x\"`) or a spread (`mdxJsxExpressionAttribute`)\n * never does, so this returns `undefined` for those.\n */\nfunction functionValuedAttribute(node: MdxNode, declaredNames: ReadonlySet<string>): string | undefined {\n const isFunctionValue = (value: string) => declaredNames.has(value.trim()) || isFunctionInitializer(value.trim(), 0)\n for (const attribute of node.attributes ?? []) {\n const raw = attribute.value\n const value = raw && typeof raw === 'object' ? raw.value : undefined\n if (typeof value !== 'string') continue\n if (isFunctionValue(value)) return value\n }\n // Function-as-children (`<Accordion>{() => 'x'}</Accordion>`) is passed as\n // the `children` prop, so it crosses the same boundary. Inline children sit\n // inside a paragraph node; look one level into it.\n const children = (node.children ?? []).flatMap((child) => child.type === 'paragraph' ? child.children ?? [] : [child])\n for (const child of children) {\n if (['mdxFlowExpression', 'mdxTextExpression'].includes(child.type) && child.value && isFunctionValue(child.value)) return child.value\n }\n return undefined\n}\n\n/**\n * True when a name in `declaredNames` (typically a page's `export const`/\n * `export function` identifiers) is passed as a bare JSX prop\n * (`prop={Name}`), or a function is written inline as a prop value\n * (`prop={() => ...}`, `prop={function () { ... }}`), into a component\n * confirmed to cross the server/client boundary: one this migrator extracted\n * as a 'use client' module, or a Thally runtime built-in already backed by\n * one (`CLIENT_BUILTIN_COMPONENT_TAGS`). This is the only shape that really\n * throws \"Functions cannot be passed directly to Client Components\" at\n * render — the same check against an unconfirmed tag would over-fire and\n * drop pages that render just fine.\n *\n * JSX written inside a page's own `export const Name = () => <...>` (bound\n * for client extraction itself) is never visited here: `remark-mdx` parses\n * that whole declaration as a single `mdxjsEsm` text node, not as MDX JSX\n * element nodes, so `walk`'s `.children` traversal never reaches it — the\n * same reason fenced/inline code samples (parsed as `code`/`inlineCode` text\n * nodes) never reach it either.\n */\nexport function propsTargetExtractedClientComponent(body: string, declaredNames: ReadonlySet<string>): boolean {\n const tree = parser.parse(body) as MdxNode\n let found = false\n walk(tree, (node) => {\n if (found || !node.name || !isConfirmedClientBoundaryTag(node.name)) return\n if (functionValuedAttribute(node, declaredNames) !== undefined) found = true\n })\n return found\n}\n\n/**\n * Broader, unconfirmed signal: true when *any* JSX tag on the page (not\n * just a confirmed client boundary) receives a function-valued prop, the\n * same shapes `propsTargetExtractedClientComponent` looks for. Callers use\n * this to decide whether the page is worth a \"might throw at render, review\n * manually\" warning even when the receiving tag's own client/server status\n * cannot be confirmed.\n */\nexport function hasAnyFunctionValuedProp(body: string, declaredNames: ReadonlySet<string>): boolean {\n const tree = parser.parse(body) as MdxNode\n let found = false\n walk(tree, (node) => {\n if (found || !node.name) return\n if (functionValuedAttribute(node, declaredNames) !== undefined) found = true\n })\n return found\n}\n\n/**\n * True when a page's own inline `export const`/`export function` declaration\n * (kept in place, not extracted into a 'use client' module — see the\n * `mdxjsEsm` note above) references `document` or `window` as an identifier.\n * Next renders an MDX page as a Server Component by default, where neither\n * global exists; callers use this to warn rather than let the page fail at\n * render. Identifiers are found via the TS AST, so a string, comment, or\n * fenced code sample using the same words never counts.\n */\nexport function declarationsReferenceBrowserGlobal(body: string): boolean {\n const tree = parser.parse(body) as MdxNode\n let found = false\n function inspect(node: ts.Node): void {\n if (ts.isIdentifier(node) && (node.text === 'document' || node.text === 'window')) found = true\n ts.forEachChild(node, inspect)\n }\n for (const node of tree.children ?? []) {\n if (found || node.type !== 'mdxjsEsm' || node.value === undefined) continue\n for (const statement of sourceFile(node.value, 'inline.tsx').statements) {\n if (ts.isVariableStatement(statement) || ts.isFunctionDeclaration(statement)) inspect(statement)\n }\n }\n return found\n}\n\n/**\n * Relative code-module specifiers (`./metadata`, `../lib/x.js`) that a page's\n * own top-level `import`/`export ... from` still names after migration. The\n * migrator rewrites every component or data import it copies to an `@/mdx/\n * migrated/...` path, so a relative one that survives points at a file that is\n * never shipped beside the page (for example a Next.js app's `metadata.ts`)\n * and fails the site build with \"Module not found\". `.md`/`.mdx` specifiers\n * are left out: snippet imports are inlined or rewritten elsewhere.\n */\nexport function unresolvedRelativeModuleSpecifiers(body: string): Array<string> {\n const tree = parser.parse(body) as MdxNode\n const specifiers = new Set<string>()\n const add = (specifier: string): void => {\n if (/^\\.\\.?\\//.test(specifier) && !/\\.mdx?$/i.test(specifier)) specifiers.add(specifier)\n }\n walk(tree, (node) => {\n if (node.type !== 'mdxjsEsm' || node.value === undefined) return\n for (const statement of sourceFile(node.value, 'inline.tsx').statements) {\n if (!(ts.isImportDeclaration(statement) || ts.isExportDeclaration(statement))) continue\n const specifier = statement.moduleSpecifier\n if (specifier && ts.isStringLiteral(specifier)) add(specifier.text)\n }\n })\n for (const { specifier } of dynamicImportLiterals(body, tree)) add(specifier)\n return [...specifiers]\n}\n\n/**\n * Every dynamic `import('<literal>')` in a page's ESM blocks, expressions and\n * JSX expression attributes. `start`/`end` locate the string literal in `body`\n * (so it can be rewritten in place) and are omitted when that location cannot\n * be confirmed against the source text.\n */\nfunction dynamicImportLiterals(body: string, tree: MdxNode): Array<{ specifier: string; start?: number; end?: number }> {\n const found: Array<{ specifier: string; start?: number; end?: number }> = []\n // `origin` is where `code` starts in `body`; `prefix` is any wrapper length\n // added in front of it when it was parsed.\n const scan = (code: string, origin: number | undefined, prefix: number): void => {\n const source = sourceFile(prefix ? `(${code})` : code, 'inline.tsx')\n const visit = (node: ts.Node): void => {\n if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword) {\n const argument = node.arguments[0]\n if (argument && ts.isStringLiteralLike(argument)) {\n const start = origin === undefined ? undefined : origin + argument.getStart(source) - prefix\n const end = start === undefined ? undefined : start + argument.getWidth(source)\n const confirmed = start !== undefined && end !== undefined && body.slice(start, end) === argument.getText(source)\n found.push(confirmed ? { specifier: argument.text, start, end } : { specifier: argument.text })\n }\n }\n ts.forEachChild(node, visit)\n }\n visit(source)\n }\n walk(tree, (node) => {\n if (node.type === 'mdxjsEsm' && node.value !== undefined) {\n scan(node.value, node.position?.start.offset, 0)\n return\n }\n if ((node.type === 'mdxFlowExpression' || node.type === 'mdxTextExpression') && node.value !== undefined) {\n scan(node.value, node.position?.start.offset === undefined ? undefined : node.position.start.offset + 1, 1)\n }\n for (const attribute of node.attributes ?? []) {\n // A plain string attribute is text; only expression values are code.\n if (attribute.type === 'mdxJsxExpressionAttribute' && typeof attribute.value === 'string') {\n scan(attribute.value, attribute.position?.start.offset === undefined ? undefined : attribute.position.start.offset + 1, 1)\n } else if (typeof attribute.value === 'object' && attribute.value?.value !== undefined) {\n scan(attribute.value.value, attribute.position?.end.offset === undefined ? undefined : attribute.position.end.offset - 1 - attribute.value.value.length, 1)\n }\n }\n })\n return found\n}\n\n/** Create one bounded component graph and registry for a repository migration. */\n/**\n * `siteRoot` (a Mintlify/Docusaurus project root, when one was detected —\n * `repositoryDir` otherwise) is what `@site/...`/root-relative (`/...`)\n * specifiers resolve against, matching Docusaurus' own alias semantics; a\n * warning's reported `source` and the generated destination paths stay\n * relative to it too, unchanged from before this parameter split. A plain\n * relative import (`../../components/X`), though, is written relative to\n * the importing *page*, which — in a monorepo where docs/ is a sibling of\n * website/ rather than nested under it (Redux) — can resolve outside\n * `siteRoot` even for a component the repository legitimately owns.\n * `confinementRoot` (always `repositoryDir`) is the actual security\n * boundary for that resolution and for `checkedFile`'s symlink safety walk,\n * so such an import still copies instead of throwing \"path escapes its\n * root\" for a component this migration should have imported.\n */\n\n/**\n * Text-only safety net for a page whose MDX can't be parsed at all (see the\n * catch below `transform`'s own `parser.parse` call): with no AST to drive a\n * real copy/removal, this only guarantees the page still builds by dropping\n * a `@site/...`/relative component import and blanking its JSX usage,\n * narrowly matching the single-binding default/named-as-default shape\n * `SNIPPET_IMPORT_PATTERN` (repository.ts) already uses for the same reason.\n * A bare npm-package import is left alone — that failure mode (\"module not\n * installed\") is unrelated to why this page's MDX didn't parse, and this\n * pass has no way to tell whether it is actually used anywhere.\n */\nfunction neutralizeUnresolvableImportsWithoutAst(content: string, currentFile: string, warn: (message: string, source: string) => void): string {\n const importPattern = /^import\\s+(?:\\{\\s*(?:default\\s+as\\s+)?([A-Z][A-Za-z0-9_]*)\\s*\\}|([A-Z][A-Za-z0-9_]*))\\s+from\\s+(['\"])((?:@site\\/|\\.\\.?\\/|\\/)[^'\"]+)\\3\\s*;?[ \\t]*$/gm\n const dropped: Array<{ name: string; specifier: string }> = []\n let result = content.replace(importPattern, (_match, namedComponent: string | undefined, defaultComponent: string | undefined, _quote: string, specifier: string) => {\n dropped.push({ name: (namedComponent ?? defaultComponent) as string, specifier })\n return ''\n })\n for (const { name, specifier } of dropped) {\n warn(`Component import ${JSON.stringify(specifier)} could not be resolved because this page has invalid MDX (see the previous warning), so the import and its usage were removed to keep the site building.`, currentFile)\n const removed = mdxComment(` Removed <${name}>: unresolved during fallback migration `)\n result = replaceOutsideCodeAndComments(result, (segment) => segment\n .replace(new RegExp(`<${name}(?:\\\\s[^>]*)?/>`, 'g'), removed)\n .replace(new RegExp(`<${name}(?:\\\\s[^>]*)?>[\\\\s\\\\S]*?<\\\\/${name}>`, 'g'), removed))\n }\n return result\n}\n\nexport function createComponentMigrator(siteRoot: string, confinementRoot: string, warnings: Array<MigrationWarning>, sourceIdentity: string): {\n transform: (raw: string, currentFile: string) => string\n files: () => Array<RenderedMigrationFile>\n /** Merge the component files of another migrated bundle (a sourceRef sub-site) into this one. */\n adopt: (incoming: ReadonlyArray<RenderedMigrationFile>) => void\n} {\n const root = resolve(siteRoot)\n const confined = resolve(confinementRoot)\n // A destination can contain imports from several repositories with identical\n // snippet names. Stable source identity isolates their graphs without tying\n // registry names to a temporary checkout path or changing repeat imports.\n const destinationRoot = `src/mdx/migrated/${hash(sourceIdentity)}`\n const copied = new Map<string, RenderedMigrationFile>()\n const registrations = new Map<string, { path: string; imported: string }>()\n let copiedBytes = 0\n\n function warn(message: string, source: string): void {\n warnings.push({ code: 'unsupported-config', message, source: relative(root, source).replace(/\\\\/g, '/') })\n }\n\n function checkedFile(path: string): string {\n const local = relative(confined, path)\n resolveWithin(confined, local)\n let current = confined\n if (lstatSync(current).isSymbolicLink()) throw new Error('symbolic links are not imported')\n for (const segment of local.split(/[\\\\/]/)) {\n current = resolveWithin(current, segment)\n if (lstatSync(current).isSymbolicLink()) throw new Error('symbolic links are not imported')\n }\n if (!lstatSync(path).isFile()) throw new Error('dependency is not a regular file')\n return path\n }\n\n function expandCandidates(candidate: string): Array<string> {\n const candidates = [candidate]\n if (!extname(candidate)) {\n candidates.push(...['.tsx', '.jsx', '.ts', '.js', '.mjs', '.json', '.d.ts'].map((extension) => candidate + extension))\n candidates.push(...['index.tsx', 'index.jsx', 'index.ts', 'index.js'].map((name) => resolve(candidate, name)))\n } else if (extname(candidate) === '.js') {\n candidates.push(candidate.slice(0, -3) + '.ts', candidate.slice(0, -3) + '.tsx')\n }\n return candidates\n }\n\n /**\n * `allowFlattenedMirror` gates the repository-root retry for a *plain\n * relative* specifier (`./x`, `../x`) below. It must stay narrow: a\n * relative import resolves relative to the importing file by standard\n * Node semantics, full stop — rebasing that resolution onto the\n * repository root for every project with a nested docs directory (the\n * ordinary case, not a monorepo) let a missing/broken import like\n * `./bogus` silently resolve to an unrelated same-named file that\n * happens to live at the mirrored repository-root path, instead of\n * being reported as unresolvable. The one real need for a mirror is\n * Playwright's own build (`cp -r nodejs/* .` flattens the `nodejs/`\n * project root's contents, including its shared `images/`, up into the\n * repository root before the site is generated), reached only through\n * the `require('../asset.ext')` idiom below — so only that call site\n * passes `true`. Every ordinary component/`import` resolution passes\n * `false` (the default) and never rebases.\n */\n function resolveDependency(specifier: string, importer: string, allowFlattenedMirror = false): string {\n if (specifier.includes('\\\\') || specifier.includes('\\0') || /[?#]/.test(specifier)) throw new Error('unsupported component dependency path')\n const candidates = specifier.startsWith('/')\n ? expandCandidates(resolveWithin(root, specifier.slice(1)))\n : expandCandidates(resolveWithin(confined, relative(confined, resolve(dirname(importer), specifier))))\n // A root-relative `@site/...`/`/...` specifier is Docusaurus' own alias\n // for its *project* root; when the shared component it names isn't\n // nested under the narrower detected project root, it can still be a\n // real, unambiguous repository-root path (Playwright's shared\n // `src/components` tree is a sibling of the `nodejs/` project, not\n // nested under it) — tried only after the direct, project-rooted path\n // fails. This is safe even outside a flattened-build layout: the\n // specifier itself names an absolute-from-some-root path, so the retry\n // can't be fooled into matching an unrelated file the way a *relative*\n // import's rebase (below) can.\n if (root !== confined && specifier.startsWith('/')) {\n candidates.push(...expandCandidates(resolveWithin(confined, specifier.slice(1))))\n }\n if (root !== confined && !specifier.startsWith('/') && allowFlattenedMirror) {\n try {\n const rebasedImporter = resolveWithin(confined, relative(root, importer))\n candidates.push(...expandCandidates(resolveWithin(confined, relative(confined, resolve(dirname(rebasedImporter), specifier)))))\n } catch {\n // Importer itself is outside the project root; nothing to rebase.\n }\n }\n for (const path of candidates) {\n if (existsSync(path) && lstatSync(path).isFile()) return checkedFile(path)\n }\n throw new Error(`component dependency ${specifier} was not found`)\n }\n\n function outputPath(path: string): string {\n // Must be `confined`, not `root`: a component reached via a relative\n // import from outside `root` (see `resolveDependency`) is still inside\n // `confined`, and a `../`-containing destination path would risk\n // writing outside `destinationRoot`.\n return `${destinationRoot}/source/${relative(confined, path).replace(/\\\\/g, '/')}`\n }\n\n function copyGraph(entry: string): string {\n // Stage the entire graph in memory. A missing leaf must not leave a partly\n // registered component or consume the successful-copy budget.\n const staged = new Map<string, RenderedMigrationFile>()\n const stagedWarnings: Array<{ path: string; omitted: number }> = []\n /** Code modules awaiting their final prologue: client-ness is settled over the whole graph (see below). */\n const modules = new Map<string, { local: boolean; dependencies: Array<string>; render: (client: boolean) => string }>()\n let stagedBytes = 0\n /** Stage a small fixed-content shim module (see `DOCUSAURUS_THEME_SHIMS`) once, reused across every component that imports it. */\n function stageShim(filename: string, content: string): string {\n const shimDestination = `${destinationRoot}/shims/${filename}`\n if (!copied.has(shimDestination) && !staged.has(shimDestination)) staged.set(shimDestination, { path: shimDestination, content })\n return shimDestination\n }\n function visit(path: string): void {\n const destination = outputPath(path)\n if (copied.has(destination) || staged.has(destination)) return\n checkedFile(path)\n const size = lstatSync(path).size\n if (size > MAX_FILE_BYTES || copiedBytes + stagedBytes + size > MAX_COMPONENT_BYTES\n || copied.size + staged.size >= MAX_COMPONENT_FILES) throw new Error('component migration budget exceeded')\n const extension = extname(path).toLowerCase()\n if (!CODE_EXTENSIONS.has(extension) && !DATA_EXTENSIONS.has(extension)) throw new Error(`unsupported dependency file type ${extension}`)\n stagedBytes += size\n const content = readFileSync(path)\n staged.set(destination, { path: destination, content })\n if (!CODE_EXTENSIONS.has(extension)) {\n if (extension === '.css' && /@import\\b|url\\s*\\(/i.test(content.toString('utf8'))) throw new Error('CSS resource dependencies require manual migration')\n if (extension === '.css' && path.toLowerCase().endsWith('.module.css')) {\n // A parse failure here (malformed CSS, an SCSS-only construct that\n // isn't valid CSS, ...) is treated the same as any other copy\n // failure: throw, so the dead-import-removal fallback in the\n // caller can neutralize its usage instead of shipping CSS\n // Turbopack would reject anyway.\n let sanitized: { css: string; omitted: number }\n try {\n sanitized = sanitizeCssModuleSelectors(content.toString('utf8'))\n } catch {\n throw new Error('CSS module could not be parsed to check for Turbopack-safe selectors')\n }\n if (sanitized.omitted > 0) stagedWarnings.push({ path, omitted: sanitized.omitted })\n staged.set(destination, { path: destination, content: sanitized.css })\n }\n return\n }\n const text = content.toString('utf8')\n // transpileModule cannot emit declaration files; parse their syntax as TS.\n const syntax = ts.transpileModule(text, { fileName: path.replace(/\\.d\\.ts$/, '.ts'), reportDiagnostics: true,\n compilerOptions: { jsx: ts.JsxEmit.Preserve, target: ts.ScriptTarget.ESNext, module: ts.ModuleKind.ESNext } })\n if (syntax.diagnostics?.some((diagnostic) => diagnostic.category === ts.DiagnosticCategory.Error)) throw new Error('component source contains unsupported JavaScript or TypeScript syntax')\n const ast = sourceFile(text, path)\n if (ast.statements.some((statement) => ts.isExpressionStatement(statement)\n && ts.isStringLiteral(statement.expression) && statement.expression.text === 'use server')) throw new Error('server-only component modules require manual migration')\n const edits: Array<Replacement> = []\n const dependencies: Array<string> = []\n // Our own prologue re-adds the directive, so drop the source's to avoid a duplicate.\n for (const statement of ast.statements) {\n if (ts.isExpressionStatement(statement) && ts.isStringLiteral(statement.expression) && statement.expression.text === 'use client') {\n edits.push({ start: statement.getStart(ast), end: statement.end, value: '' })\n }\n }\n function dependency(literal: ts.StringLiteralLike, importDeclaration?: ts.ImportDeclaration): void {\n const specifier = literal.text\n if (SHARED_IMPORTS.has(specifier) || isScaffoldProvidedImport(specifier)) return\n // `@docusaurus/Link` maps onto `next/link` (already scaffold-provided)\n // rather than a shim module: same default export, only its `to` prop\n // is renamed to `href` wherever the copied file itself uses the tag.\n if (specifier === '@docusaurus/Link') {\n edits.push({ start: literal.getStart(ast), end: literal.end, value: '\"next/link\"' })\n const localName = importDeclaration?.importClause?.name?.text\n if (localName) {\n function renameToProp(node: ts.Node): void {\n if (ts.isJsxAttribute(node) && node.name.getText(ast) === 'to'\n && ts.isJsxOpeningLikeElement(node.parent.parent)\n && node.parent.parent.tagName.getText(ast) === localName) {\n edits.push({ start: node.name.getStart(ast), end: node.name.end, value: 'href' })\n }\n ts.forEachChild(node, renameToProp)\n }\n renameToProp(ast)\n }\n return\n }\n const shim = DOCUSAURUS_THEME_SHIMS[specifier]\n if (shim) {\n const shimPath = stageShim(shim.filename, shim.content)\n dependencies.push(shimPath)\n const nextPath = relative(dirname(destination), shimPath).replace(/\\\\/g, '/')\n edits.push({ start: literal.getStart(ast), end: literal.end, value: JSON.stringify(portableSpecifier(nextPath.startsWith('.') ? nextPath : `./${nextPath}`)) })\n return\n }\n if (!specifier.startsWith('.') && !specifier.startsWith('/')) {\n throw new Error(specifier.startsWith('@/')\n ? `path alias ${specifier} points to source-site code the migration does not copy`\n : `external package ${specifier} requires manual installation and review`)\n }\n const target = resolveDependency(specifier, path)\n visit(target)\n dependencies.push(outputPath(target))\n const nextPath = relative(dirname(destination), outputPath(target)).replace(/\\\\/g, '/')\n edits.push({ start: literal.getStart(ast), end: literal.end, value: JSON.stringify(portableSpecifier(nextPath.startsWith('.') ? nextPath : `./${nextPath}`)) })\n }\n function inspect(node: ts.Node): void {\n if ((ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) {\n dependency(node.moduleSpecifier, ts.isImportDeclaration(node) ? node : undefined)\n }\n if (ts.isCallExpression(node) && (node.expression.kind === ts.SyntaxKind.ImportKeyword\n || (ts.isIdentifier(node.expression) && node.expression.text === 'require'))) {\n const argument = node.arguments[0]\n if (node.arguments.length !== 1 || !argument || !ts.isStringLiteralLike(argument)) throw new Error('computed component imports require manual migration')\n dependency(argument)\n }\n ts.forEachChild(node, inspect)\n }\n inspect(ast)\n // Mark copied code as client-owned when it needs the browser (see\n // `needsClientBoundary`); browser hooks must never run in MDX's server\n // scope. A pure component stays a server module: as 'use client' it\n // would only see its server-rendered children as opaque references,\n // so a site component that walks `children` (a `{{KEY}}` template)\n // could not read the code it wraps.\n // The source site never ran `next build`'s full `tsc --noEmit` type\n // check (Docusaurus/Mintlify/Fern don't gate their own build on it),\n // so loose types, `window.ethereum`-style ambient globals, and\n // implicit `any` parameters that were fine there fail it here.\n // `@ts-nocheck` is valid in both `.ts(x)` and `.js(x)` files (a no-op\n // unless `checkJs` is on) and is the least invasive fix: it preserves\n // the component's real behavior instead of stripping or rewriting it.\n const replaced = applyReplacements(text, edits)\n // A `#!/...` shebang is only special on line 1 of the file (Node\n // strips it before parsing only there); inserting the `@ts-nocheck`\n // prologue above it would both break that and, since `// @ts-nocheck`\n // isn't itself a shebang, leave a dead shebang-looking comment mid\n // file. If the copied source starts with one, keep it first and\n // insert the prologue right after instead.\n const shebangMatch = /^#!.*\\r?\\n/.exec(replaced)\n modules.set(destination, {\n local: needsClientBoundary(ast),\n dependencies,\n render: (client) => {\n const prologue = `// @ts-nocheck\\n${client ? \"'use client';\\n\" : ''}\\n`\n return shebangMatch\n ? shebangMatch[0] + prologue + implicitReactImports(ast) + '\\n' + replaced.slice(shebangMatch[0].length)\n : prologue + implicitReactImports(ast) + '\\n' + replaced\n },\n })\n }\n visit(entry)\n // A client module's importers must be client modules too: a server module\n // calling a function exported by a client module fails at render (\"Attempted\n // to call fmt() from the server\"). Settle that to a fixpoint over the graph;\n // a module imported from an earlier graph is judged by its emitted directive.\n const clientModules = new Set([...modules].filter(([, module]) => module.local).map(([path]) => path))\n const isClient = (path: string): boolean => clientModules.has(path)\n || (!modules.has(path) && /^\\s*(?:\\/\\/[^\\n]*\\n\\s*)*['\"]use client['\"]/.test(String((copied.get(path) ?? staged.get(path))?.content ?? '')))\n for (let changed = true; changed;) {\n changed = false\n for (const [path, module] of modules) {\n if (!clientModules.has(path) && module.dependencies.some(isClient)) {\n clientModules.add(path)\n changed = true\n }\n }\n }\n for (const [path, module] of modules) staged.set(path, { path, content: module.render(clientModules.has(path)) })\n for (const [path, file] of staged) copied.set(path, file)\n for (const { path, omitted } of stagedWarnings) {\n warn(`Site-authored CSS module ${relative(root, path).replace(/\\\\/g, '/')} has ${omitted} global-only selector branch(es) that cannot be copied into Thally CSS Modules; those styles were omitted.`, path)\n }\n copiedBytes += stagedBytes\n return outputPath(entry)\n }\n\n /**\n * Copy a binary asset verbatim to a stable `public/` path (bypassing\n * `copyGraph`'s JS-module handling entirely — the asset is bound to its\n * URL string, never imported as a module) and return that URL. Used when\n * an import couldn't be copied as a component but names a real file the\n * page only ever references by URL (`src={Logo}`), never as a JSX tag.\n */\n function copyPublicAsset(path: string): string {\n checkedFile(path)\n const size = lstatSync(path).size\n if (size > MAX_FILE_BYTES || copiedBytes + size > MAX_COMPONENT_BYTES || copied.size >= MAX_COMPONENT_FILES) {\n throw new Error('component migration budget exceeded')\n }\n const publicName = `migrated-${hash(relative(confined, path))}${extname(path).toLowerCase()}`\n const destination = `public/${publicName}`\n if (!copied.has(destination)) {\n copied.set(destination, { path: destination, content: readFileSync(path) })\n copiedBytes += size\n }\n return `/${publicName}`\n }\n\n /**\n * Docusaurus ships SVGR out of the box, so `import Foo from './foo.svg';\n * <Foo />` renders the SVG as a real component there. The migrated\n * project has no SVGR loader, so importing an `.svg` file the ordinary\n * way (`copyGraph`'s ordinary path, used below) yields whatever the\n * bundler's default asset handling returns for it — a URL string or a\n * `{ src }` object, either way not a valid React element type — and\n * `<Foo />` throws \"Element type is invalid\" at render. Used only when\n * the import is actually rendered as a JSX tag (an attribute-only usage,\n * `src={Foo}`, needs no wrapper and is left as the plain import): stage a\n * tiny companion module next to the copied SVG that imports it as a URL\n * either way and renders a plain `<img>`, and register that instead of\n * the raw file.\n */\n function wrapSvgAsComponent(svgOutputPath: string): string {\n const wrapperPath = `${svgOutputPath}.component.tsx`\n if (!copied.has(wrapperPath)) {\n const content = [\n \"'use client'\",\n '',\n `import svgSource from ${JSON.stringify(`./${basename(svgOutputPath)}`)}`,\n '',\n 'export default function SvgImage(props: Record<string, unknown>) {',\n ' const src = typeof svgSource === \"string\" ? svgSource : (svgSource as { src: string }).src',\n ' // eslint-disable-next-line @next/next/no-img-element -- no SVGR loader is configured; this renders the raw file.',\n ' return <img src={src} alt=\"\" {...props} />',\n '}',\n '',\n ].join('\\n')\n copied.set(wrapperPath, { path: wrapperPath, content })\n }\n return wrapperPath\n }\n\n function register(path: string, imported: string): string {\n const name = `Migrated${hash(`${path}:${imported}`)}`\n registrations.set(name, { path, imported })\n return name\n }\n\n function transform(raw: string, currentFile: string): string {\n const parsedFrontmatter = parseFrontmatter(raw).content\n const frontmatter = raw.slice(0, raw.length - parsedFrontmatter.length)\n // remark-mdx does not parse a raw HTML comment (`<!-- ... -->`) at all —\n // a real source page commonly has one (Docusaurus' own\n // `<!-- prettier-ignore -->` ahead of a snippet import) and it\n // otherwise throws here before this pass ever runs, leaving whatever\n // import that comment sits near completely untouched. `normalizeMdx`\n // converts the same syntax later in the pipeline anyway, so doing it\n // here too (on this function's own working copy) is never wasted work.\n // Likewise a heading's explicit `{#custom-id}` suffix is never valid MDX\n // (a bare `{...}` in prose is always parsed as a JS expression, and no\n // JS expression starts with `#`) regardless of platform, so converting\n // it up front is always safe — and it must happen before this\n // function's own parse below, or that parse throws first and this\n // whole page's import analysis is skipped instead of just this one\n // page's expression.\n // A `{/* #id */}` comment already on a heading is a Mintlify heading id and\n // is valid MDX, so it is kept rather than rewritten to an anchor.\n const content = normalizeExplicitHeadingIds(normalizeIndentedFences(normalizeHtmlComments(parsedFrontmatter)), undefined, { keepIdComments: true })\n let tree: MdxNode\n try {\n tree = parser.parse(content) as MdxNode\n } catch {\n warn('Custom component analysis could not parse this MDX; its source was preserved for manual migration.', currentFile)\n // The AST walk below is how every `@site/...`/relative component\n // import normally gets copied or removed; none of that runs on a page\n // that can't even be parsed. Left alone, an import like that survives\n // verbatim and breaks `next build` with \"Module not found\" for the\n // whole site, not just this page — the same failure mode the AST path\n // already guards against everywhere else. This is a text-only,\n // best-effort safety net (no AST here to drive a real copy), narrow by\n // design: only the single-binding default/named-as-default import\n // shape actually seen in practice, mirroring `SNIPPET_IMPORT_PATTERN`.\n return frontmatter + neutralizeUnresolvableImportsWithoutAst(content, currentFile, warn)\n }\n const aliases = new Map<string, string>()\n const unsupportedImports = new Map<string, string>()\n // Local names dropped by the lowercase-named `.mdx?` import case below\n // (Docusaurus' auto-generated per-file `toc` export, merged into\n // another page's own `toc` via `...viewsToc`) — collected here so the\n // spread-removal pass after the main walk can find every one of them,\n // regardless of which esmNode declared the import vs. used it.\n const droppedMdxDataBindings = new Set<string>()\n const edits: Array<Replacement> = []\n const declarations: Array<{ start: number; end: number; source: string }> = []\n const moduleImports: Array<string> = []\n const realPageImports: Array<string> = []\n const serverPageDeclarations: Array<string> = []\n const sharedImportEdits: Array<Replacement> = []\n let hasUnsupportedImports = false\n function hasExpressionReference(names: Set<string>, options: {\n excludedNodes?: Array<MdxNode>\n includeDeclarations?: boolean\n includeDirectTags?: boolean\n } = {}): boolean {\n let found = false\n function inspect(node: ts.Node): void {\n if (ts.isIdentifier(node) && names.has(node.text)) found = true\n ts.forEachChild(node, inspect)\n }\n walk(tree, (node) => {\n if (options.excludedNodes?.some((excluded) => node.position?.start.offset !== undefined\n && node.position.end.offset !== undefined\n && node.position.start.offset >= excluded.position!.start.offset!\n && node.position.end.offset <= excluded.position!.end.offset!)) return\n if (node.name && [...names].some((name) => node.name!.startsWith(`${name}.`)\n || (options.includeDirectTags && node.name === name))) found = true\n if (options.includeDeclarations !== false && node.type === 'mdxjsEsm' && node.value) {\n for (const statement of sourceFile(node.value, 'expression.tsx').statements) {\n if (!ts.isImportDeclaration(statement)) inspect(statement)\n }\n }\n const expressions = [\n ...(['mdxFlowExpression', 'mdxTextExpression'].includes(node.type) ? [node.value ?? ''] : []),\n ...(node.attributes ?? []).flatMap((attribute) => {\n if (attribute.type === 'mdxJsxExpressionAttribute' && typeof attribute.value === 'string') return [attribute.value]\n return attribute.value && typeof attribute.value === 'object' ? [attribute.value.value ?? ''] : []\n }),\n ]\n for (const expression of expressions) inspect(sourceFile(expression, 'expression.tsx'))\n })\n return found\n }\n /** True when `name` is ever a JSX tag's own root name (`<name ...>`), regardless of any other usage — narrower than `hasExpressionReference`'s combined signal, needed where a string binding (an asset's URL) is fine for any *other* usage but not this one. */\n function usedAsJsxTagName(name: string): boolean {\n let found = false\n walk(tree, (node) => { if (node.name === name) found = true })\n return found\n }\n // Docusaurus allows a real `import`/`export` ESM block anywhere in the\n // body, not just at the top of the file — a documentation page\n // demonstrating a live code example commonly nests one inside a JSX\n // wrapper (`<BrowserWindow>\\nimport Foo from './foo.svg'\\n\\n<Foo\n // /></BrowserWindow>`, straight from Docusaurus' own docs). remark-mdx\n // still parses each as its own `mdxjsEsm` node, just nested under that\n // wrapper's `children` instead of `tree.children` directly, so this\n // must walk the whole tree to find every one of them; a scan of only\n // `tree.children` silently skips a nested import, leaving the page\n // referencing a path the migrated project never has (\"Module not\n // found\") without any warning at all.\n const esmNodes: Array<MdxNode> = []\n walk(tree, (node) => { if (node.type === 'mdxjsEsm') esmNodes.push(node) })\n for (const node of esmNodes) {\n if (node.value === undefined || node.position?.start.offset === undefined) continue\n const ast = sourceFile(node.value, 'inline.tsx')\n for (const statement of ast.statements) {\n if (!ts.isImportDeclaration(statement)) {\n // `export ... from './x'` of a file the migration copies points at\n // the copied module; one that cannot be copied falls through and the\n // page is excluded afterwards (repository.ts).\n const reexport = ts.isExportDeclaration(statement) && statement.moduleSpecifier && ts.isStringLiteral(statement.moduleSpecifier)\n && /^\\.\\.?\\//.test(statement.moduleSpecifier.text) && !/\\.mdx?$/i.test(statement.moduleSpecifier.text)\n ? statement.moduleSpecifier : undefined\n if (reexport) {\n try {\n const path = copyGraph(resolveDependency(reexport.text, currentFile))\n edits.push({\n start: node.position.start.offset + reexport.getStart(ast),\n end: node.position.start.offset + reexport.end,\n value: JSON.stringify(portableSpecifier(`@/${path.replace(/^src\\//, '').replace(/\\\\/g, '/')}`)),\n })\n continue\n } catch {\n // Not shipped: handled below.\n }\n }\n let unsupportedDependency = ts.isExportDeclaration(statement) && !!statement.moduleSpecifier\n function inspect(node: ts.Node): void {\n if (ts.isCallExpression(node) && (node.expression.kind === ts.SyntaxKind.ImportKeyword\n || (ts.isIdentifier(node.expression) && node.expression.text === 'require'))) unsupportedDependency = true\n ts.forEachChild(node, inspect)\n }\n inspect(statement)\n if (unsupportedDependency) {\n hasUnsupportedImports = true\n warn('Inline MDX module dependencies require manual extraction; source was preserved.', currentFile)\n }\n declarations.push({\n start: node.position.start.offset + statement.getStart(ast),\n end: node.position.start.offset + statement.end,\n source: statement.getText(ast),\n })\n continue\n }\n const bindings = imports(statement)\n const rawSpecifier = ts.isStringLiteral(statement.moduleSpecifier) ? statement.moduleSpecifier.text : ''\n // `@site/...` is Docusaurus' and Mintlify's shared alias for the\n // project root; treat it exactly like the root-relative `/...` form\n // `resolveDependency` already understands, so a locally-owned\n // component copies the same way a relative import would.\n const specifier = rawSpecifier.startsWith('@site/') ? `/${rawSpecifier.slice('@site/'.length)}` : rawSpecifier\n if (isScaffoldProvidedImport(specifier)) {\n // Some translated Mintlify pages place a React hook import above a\n // fenced example. Keeping that unused import turns server MDX into\n // a module that illegally imports a client-only hook.\n if (specifier === 'react' && bindings.length > 0\n && bindings.every((binding) => REACT_GLOBALS.has(binding.imported))\n && !hasExpressionReference(new Set(bindings.map((binding) => binding.local)))) {\n edits.push({ start: node.position.start.offset + statement.getStart(ast), end: node.position.start.offset + statement.end, value: '' })\n continue\n }\n // Installed in the migrated project: keep it on the page (content\n // outside an extracted block may use it) and copy it into any\n // extracted client module, whose moved declarations may use it too.\n moduleImports.push(statement.getText(ast))\n continue\n }\n if (!specifier.startsWith('.') && !specifier.startsWith('/')) {\n if (SHARED_IMPORTS.has(specifier)) {\n const isUnusedReactHookImport = specifier === 'react' && bindings.length > 0\n && bindings.every((binding) => REACT_GLOBALS.has(binding.imported))\n && !hasExpressionReference(new Set(bindings.map((binding) => binding.local)))\n if (!isUnusedReactHookImport) moduleImports.push(statement.getText(ast))\n // A shared React import is normally retained in server MDX. An\n // unused hook import belongs only to a fenced example and would\n // cause Next's server-component build to reject the whole page.\n if (isUnusedReactHookImport) edits.push({ start: node.position.start.offset + statement.getStart(ast), end: node.position.start.offset + statement.end, value: '' })\n else sharedImportEdits.push({ start: node.position.start.offset + statement.getStart(ast), end: node.position.start.offset + statement.end, value: '' })\n } else if (specifier.startsWith('@theme/') || specifier.startsWith('@docusaurus/')) {\n // Docusaurus' own theme/runtime components (Tabs, TabItem, Link,\n // useBaseUrl, ...) are converted to Thally's equivalents by\n // normalizeMdx right after this pass; just drop the now-redundant\n // import and leave JSX usage untouched for that pass to rewrite.\n edits.push({ start: node.position.start.offset + statement.getStart(ast), end: node.position.start.offset + statement.end, value: '' })\n } else if (hasExpressionReference(new Set(bindings.map((binding) => binding.local)))) {\n // Only JSX usage (`<Widget />`) is rewritten below; a reference\n // in `{pkg.fn()}`, a prop, or an inline `export const` cannot be\n // rewritten the same way. Dropping the import would leave an\n // undefined identifier; keeping it imports a package that is not\n // installed in the migrated project, which fails `next build`\n // for the *whole* site with \"Module not found\" — not just this\n // page. Excluding the page is the only option that keeps the\n // rest of the site building; `pruneMissingNavigationPages` (see\n // repository.ts) then drops it from navigation too.\n warnings.push({\n code: 'skipped-file',\n message: `This page was excluded because it uses '${bindings.map((binding) => binding.local).join(', ')}' from ${unavailableImportLabel(rawSpecifier)} outside JSX (in an expression, prop, or inline declaration), and that import isn't available in the migrated project. Make '${rawSpecifier}' available and add the import back manually, or rewrite the page to avoid it.`,\n source: relative(root, currentFile).replace(/\\\\/g, '/'),\n })\n hasUnsupportedImports = true\n } else {\n // An npm package Thally's runtime does not ship is not installed\n // in the migrated project; leaving the import in place breaks\n // `next build` with \"Module not found\". Drop the import and\n // replace its JSX usage with a safe fallback instead (see the\n // `unsupportedImports` walk below) rather than shipping a page\n // that cannot compile.\n warn(`MDX import of ${unavailableImportLabel(rawSpecifier)} is not available in the migrated project; the import was removed and its usage replaced.`, currentFile)\n edits.push({ start: node.position.start.offset + statement.getStart(ast), end: node.position.start.offset + statement.end, value: '' })\n for (const binding of bindings) unsupportedImports.set(binding.local, rawSpecifier)\n }\n continue\n }\n if (/\\.mdx?$/.test(specifier)) {\n // A capitalized default/named import here is a JSX-usable\n // partial (`import Partial from './x.mdx'`, `<Partial />`) — the\n // earlier text-level snippet-inlining pass (`inlineMdxSnippets`,\n // repository.ts) already resolved and removed those before this\n // AST walk ever runs, so reaching this point with only lowercase\n // bindings means every one is a plain named value that pass\n // can't touch: Docusaurus' own convention of auto-generating a\n // `toc` export per `.mdx` file, commonly re-exported by spreading\n // it into another page's `toc` (`export const toc = [...viewsToc,\n // ...]`). The migrated project has no such module to import from\n // (\"Module not found\"), and no way to compute the value, so the\n // import and that one safe, common usage shape are both removed;\n // anything shaped differently is left for the npm-style\n // expression-reference check below to exclude the page instead of\n // guessing at a replacement.\n if (bindings.length > 0 && bindings.every((binding) => !/^[A-Z]/.test(binding.local))) {\n warn(`MDX named import of ${JSON.stringify(rawSpecifier)} isn't a component and has no equivalent in the migrated site (for example Docusaurus's generated 'toc' export), so it was removed, along with any '...${bindings.map((binding) => binding.local).join(\", '...\")}' spread of it.`, currentFile)\n edits.push({ start: node.position.start.offset + statement.getStart(ast), end: node.position.start.offset + statement.end, value: '' })\n for (const binding of bindings) droppedMdxDataBindings.add(binding.local)\n }\n continue\n }\n // A binary asset (`docusaurusLogo.svg`, a `.docx` handout, ...) is\n // never really \"a component\" — it's fine bound to any identifier\n // shape and referenced from an expression (`src={docusaurusLogo}`),\n // unlike the component-alias mechanism below, which can only\n // rewrite a literal JSX tag name, not an arbitrary expression\n // reference. Handle it before the component-shaped checks reject it\n // for the wrong reason (a lowercase local name, expression usage)\n // and leave a dead import behind. Only bail out of this path (to\n // the ordinary handling below, which removes the import either way)\n // when the same local name is *also* used as a JSX tag — a string\n // URL bound there would throw just as hard as the missing import.\n if (bindings.length && RESCUABLE_ASSET_EXTENSIONS.has(extname(specifier).toLowerCase())\n && !bindings.some((binding) => usedAsJsxTagName(binding.local))) {\n try {\n const href = copyPublicAsset(resolveDependency(specifier, currentFile))\n edits.push({\n start: node.position.start.offset + statement.getStart(ast),\n end: node.position.start.offset + statement.end,\n // `export`, not a bare `const`: this replaces an `import`\n // statement in place, and the final MDX compile (a fresh\n // parse of this text, not a reuse of this pass's own AST)\n // only recognizes a top-level JS block as ESM — evaluated and\n // its bindings put in scope — when it starts with `import` or\n // `export`. A bare `const` there is just prose to that parser\n // and renders as literal paragraph text instead, leaving\n // every reference to this binding a `ReferenceError` at\n // render (reproduced against hasura/graphql-engine's\n // databases/overview.mdx, which binds asset URLs this way).\n value: bindings.map((binding) => `export const ${binding.local} = ${JSON.stringify(href)};`).join('\\n'),\n })\n warn(`Asset import ${JSON.stringify(rawSpecifier)} was copied to ${href} and bound to that URL instead of importing it as a component.`, currentFile)\n continue\n } catch {\n // Not actually resolvable/copyable (missing file, budget) —\n // fall through to the ordinary handling below.\n }\n }\n if (!statement.importClause) {\n // Same treatment as a side-effect import of an npm package above:\n // stylesheets and setup scripts have no effect on the migrated\n // page, and leaving the import would reference a file that is not\n // shipped beside it.\n warn(`MDX side-effect import of ${JSON.stringify(rawSpecifier)} has no effect in the migrated site (stylesheets and setup scripts are not copied); the import was removed.`, currentFile)\n edits.push({ start: node.position.start.offset + statement.getStart(ast), end: node.position.start.offset + statement.end, value: '' })\n continue\n }\n if (statement.importClause.namedBindings && ts.isNamespaceImport(statement.importClause.namedBindings)) {\n // A namespace import cannot be registered as a JSX tag, but it can\n // be imported for real from the copied file, like an expression-used\n // named import. When the target cannot be copied the import is left\n // alone and the page is excluded afterwards (repository.ts).\n try {\n const path = copyGraph(resolveDependency(specifier, currentFile))\n const aliasSpecifier = portableSpecifier(`@/${path.replace(/^src\\//, '').replace(/\\\\/g, '/')}`)\n realPageImports.push(`import ${statement.importClause.getText(ast)} from ${JSON.stringify(aliasSpecifier)};`)\n edits.push({ start: node.position.start.offset + statement.getStart(ast), end: node.position.start.offset + statement.end, value: '' })\n } catch {\n hasUnsupportedImports = true\n }\n continue\n }\n // Unlike the unavailable-npm-package case above, a local/relative\n // import is fully owned by the migration: `copyGraph` below moves\n // the whole file (and its own local dependency graph) into the\n // project, and the statement it emits keeps every binding's original\n // local name (`as ${binding.local}`) — only the module specifier\n // changes. Nothing in the page body needs renaming, so it makes no\n // difference whether a binding is used as a bare JSX tag or only\n // referenced elsewhere (an enum member on a sibling prop, a member\n // tag, a wrapping declaration, ...): the copy is attempted either\n // way, and the page is excluded only if that copy genuinely fails\n // (an unsupported dependency inside the local graph — see the catch\n // below), not merely because of how the binding is used.\n try {\n const sourcePath = resolveDependency(specifier, currentFile)\n const path = copyGraph(sourcePath)\n const isSvgUsedAsTag = extname(path).toLowerCase() === '.svg' && bindings.some((binding) => usedAsJsxTagName(binding.local))\n for (const binding of bindings) {\n const registerPath = isSvgUsedAsTag ? wrapSvgAsComponent(path) : path\n const registerImported = isSvgUsedAsTag ? 'default' : binding.imported\n // MDX's own component-injection pass (recma-jsx-rewrite) only\n // rewrites a JSX tag it finds as a genuine, direct mdast JSX\n // node (`<Foo>` as its own markdown/JSX child) to pull from the\n // shared `_components` scope (the customComponents registry\n // below) — the alias-rename walk further down can likewise only\n // text-replace that same direct form. Any other shape —\n // `Capability.ToolUse` in a sibling prop's object literal,\n // `<Widget />` nested inside a `{...}` expression or an\n // `export const` declaration, a member tag `<Widget.Item />`,\n // a plain value passed as a prop (`as={Widget}`) — is invisible\n // to both, and registering it under an aliased key would leave\n // the literal reference an unresolved free variable\n // (`ReferenceError` at render; reproduced against a real\n // Cohere build for both an enum-in-a-prop and a component\n // nested in a `.map()` callback). Give any such binding a real\n // import instead, keeping its original local name and using the\n // project's `@/` alias so the specifier resolves regardless of\n // where the generated runtime module that embeds this page\n // ends up on disk.\n if (!isSvgUsedAsTag && hasExpressionReference(new Set([binding.local]))) {\n const helper = staticServerHelper(sourcePath, binding.imported, binding.local)\n if (helper) {\n serverPageDeclarations.push(helper)\n continue\n }\n const aliasSpecifier = portableSpecifier(`@/${registerPath.replace(/^src\\//, '').replace(/\\\\/g, '/')}`)\n realPageImports.push(`import { ${registerImported} as ${binding.local} } from ${JSON.stringify(aliasSpecifier)};`)\n continue\n }\n const name = register(registerPath, registerImported)\n aliases.set(binding.local, name)\n moduleImports.push(`import { ${registerImported} as ${binding.local} } from ${JSON.stringify(portableSpecifier(`./${relative(destinationRoot, registerPath).replace(/\\\\/g, '/')}`))};`)\n }\n edits.push({ start: node.position.start.offset + statement.getStart(ast), end: node.position.start.offset + statement.end, value: '' })\n } catch (error) {\n if (hasExpressionReference(new Set(bindings.map((binding) => binding.local)))) {\n warnings.push({\n code: 'skipped-file',\n message: `This page was excluded because local import ${JSON.stringify(rawSpecifier)} could not be copied and is used in an expression: ${error instanceof Error ? error.message : 'unsupported dependency'}.`,\n source: relative(root, currentFile).replace(/\\\\/g, '/'),\n })\n hasUnsupportedImports = true\n continue\n }\n const failedPackage = error instanceof Error\n ? error.message.match(/^external package (\\S+) requires manual installation and review$/)?.[1]\n : undefined\n if (failedPackage && YOUTUBE_ID_PACKAGES.has(failedPackage)) {\n // The local wrapper's own body just re-exports a known\n // YouTube-embed package one level down (Playwright's\n // `LiteYouTube` wraps `react-lite-youtube-embed` this way); the\n // wrapper itself can't be copied, but its usage is id-prop-shaped\n // exactly like a direct import of that package, so the same\n // working `<iframe>` fallback below applies instead of losing\n // the embed to the generic unknown-component neutralization.\n for (const binding of bindings) unsupportedImports.set(binding.local, failedPackage)\n edits.push({ start: node.position.start.offset + statement.getStart(ast), end: node.position.start.offset + statement.end, value: '' })\n continue\n }\n hasUnsupportedImports = true\n const importStart = node.position.start.offset + statement.getStart(ast)\n const importEnd = node.position.start.offset + statement.end\n // The component itself couldn't be copied (unsupported syntax, a\n // budget limit, a missing file, ...). Leaving the import in place\n // referenced a module that will never exist in the migrated\n // project, which fails `next build` for the *whole* site with\n // \"Module not found\" — not just this page (the same reasoning as\n // the unsupported-npm-import case above). Remove it. Its bindings\n // are known to be used only as JSX tags here (any expression/prop\n // usage already excluded this import from reaching this try, via\n // the `hasExpressionReference` check above), so a plain-page\n // rescan (`replaceUnknownComponents`, mdx.ts, run by repository.ts\n // after this transform) will find each now-undeclared tag and\n // neutralize it (a paired tag keeps its children in a `<div>`; a\n // self-closing one is removed) rather than leave a dangling\n // reference.\n let rescuedAssetHref: string | undefined\n if (RESCUABLE_ASSET_EXTENSIONS.has(extname(specifier).toLowerCase())) {\n // Only worth rescuing as a URL string when nothing ever renders\n // it as a JSX tag (`<Logo />`) — a string bound to a component\n // reference would throw just as hard as the missing import did.\n const usedAsJsxTag = bindings.some((binding) => hasExpressionReference(new Set([binding.local]), { includeDirectTags: true }))\n if (!usedAsJsxTag) {\n try {\n rescuedAssetHref = copyPublicAsset(resolveDependency(specifier, currentFile))\n } catch {\n rescuedAssetHref = undefined\n }\n }\n }\n if (rescuedAssetHref !== undefined) {\n edits.push({\n start: importStart,\n end: importEnd,\n // See the identical `export const` note on the other asset\n // rescue above — same reason.\n value: bindings.map((binding) => `export const ${binding.local} = ${JSON.stringify(rescuedAssetHref)};`).join('\\n'),\n })\n warn(`Asset import ${JSON.stringify(rawSpecifier)} could not be copied as a component; it was copied to ${rescuedAssetHref} and bound to that URL instead.`, currentFile)\n } else {\n edits.push({ start: importStart, end: importEnd, value: '' })\n warn(`Site-authored component import ${JSON.stringify(rawSpecifier)} could not be copied and was removed: ${error instanceof Error ? error.message : 'unsupported dependency'}. Its usage on this page was neutralized; review this custom implementation manually.`, currentFile)\n }\n }\n }\n }\n\n // Extract a page's own inline component that calls a React hook (Mintlify\n // treats hooks as pre-injected globals for such components, but Thally's\n // page module is a Server Component and cannot import a hook itself) into\n // a client module, together with every page-local declaration it uses.\n // `planInlineExtraction` decides what moves, what the page keeps a copy\n // of, and whether the move is safe at all; an unsafe one is left in place\n // and reported. Whatever the page still renders is wired back as a\n // registered tag, or a real import when it is reached by name.\n const refusedInlineComponents = new Set<string>()\n {\n const plan = planInlineExtraction({\n declarations,\n tree,\n copyableImports: [...moduleImports, ...realPageImports],\n helperDeclarations: serverPageDeclarations,\n unavailableImports: unsupportedImports,\n reactGlobals: REACT_GLOBALS,\n })\n for (const { name, reason } of plan.blocked) {\n refusedInlineComponents.add(name)\n warn(`Inline component \"${name}\" calls a React hook but was left in the page: ${reason}. Move it and everything it uses into a client component manually.`, currentFile)\n }\n const moduleSource = plan.moved.length === 0 ? '' : [\n ...plan.moduleImports, ...plan.moduleHelpers,\n ...[...plan.moved, ...plan.copied].sort((a, b) => a - b).map((index) => declarations[index].source),\n ].join('\\n\\n')\n if (plan.moved.length > 0\n && (copied.size >= MAX_COMPONENT_FILES || Buffer.byteLength(moduleSource) > MAX_FILE_BYTES\n || copiedBytes + Buffer.byteLength(moduleSource) > MAX_COMPONENT_BYTES)) {\n warn(`Inline component \"${plan.movedNames[0]}\" exceeded the component migration budget; source was preserved.`, currentFile)\n } else if (plan.moved.length > 0) {\n const path = `${destinationRoot}/inline-${hash(`${relative(root, currentFile)}:components`)}.jsx`\n const inlineSource = `${resolveInlineBuiltinReferences(moduleSource, plan.moduleNames)}\\n`\n copied.set(path, { path, content: `'use client';\\n${implicitReactImports(sourceFile(inlineSource, 'inline.jsx'))}\\n${inlineSource}` })\n copiedBytes += Buffer.byteLength(inlineSource)\n const movedIndexes = new Set(plan.moved)\n for (const index of plan.moved) edits.push({ start: declarations[index].start, end: declarations[index].end, value: '' })\n for (const name of plan.registryNames) {\n const registered = register(path, name)\n aliases.set(name, registered)\n // A root the page still extracts as interactive JSX (below) imports\n // the moved component from this module by its real name.\n moduleImports.push(`import { ${name} } from ${JSON.stringify(portableSpecifier(`./${relative(destinationRoot, path).replace(/\\\\/g, '/')}`))};`)\n }\n if (plan.pageImportNames.length > 0) {\n realPageImports.push(`import { ${plan.pageImportNames.join(', ')} } from ${JSON.stringify(portableSpecifier(`@/${path.replace(/^src\\//, '').replace(/\\\\/g, '/')}`))};`)\n }\n // A React import the page only kept for the code that just moved would\n // make the page itself import a hook; drop it once nothing else uses it.\n for (const entry of [...sharedImportEdits]) {\n const locals = [...sourceFile(content.slice(entry.start, entry.end), 'shared.tsx').statements]\n .flatMap((statement) => ts.isImportDeclaration(statement) ? imports(statement).map((binding) => binding.local) : [])\n if (locals.length === 0 || locals.some((local) => plan.pageReferences.has(local))) continue\n edits.push(entry)\n sharedImportEdits.splice(sharedImportEdits.indexOf(entry), 1)\n }\n for (let index = declarations.length - 1; index >= 0; index -= 1) {\n if (movedIndexes.has(index)) declarations.splice(index, 1)\n }\n }\n }\n\n // Remove every `...name` spread of a dropped MDX data import (see the\n // lowercase-binding `.mdx?` case above) from any array literal it\n // appears in, so the export that merges it (`export const toc =\n // [...viewsToc, ...]`) stays valid instead of throwing on the now-\n // undefined identifier. Only the spread element itself is touched\n // (plus one adjacent comma, so the array literal it lived in stays\n // syntactically valid); a bare reference to the same name elsewhere is\n // intentionally left alone for `hasExpressionReference`'s excluded-page\n // path elsewhere in this function to catch instead of guessing at it.\n if (droppedMdxDataBindings.size > 0) {\n for (const node of esmNodes) {\n if (node.value === undefined || node.position?.start.offset === undefined) continue\n const nodeStart = node.position.start.offset\n const nodeText = node.value\n const ast = sourceFile(nodeText, 'inline.tsx')\n function stripDroppedSpreads(inner: ts.Node): void {\n if (ts.isSpreadElement(inner) && ts.isIdentifier(inner.expression) && droppedMdxDataBindings.has(inner.expression.text)) {\n const start = nodeStart + inner.getStart(ast)\n let end = nodeStart + inner.end\n const following = nodeText.slice(inner.end).match(/^\\s*,/)\n if (following) end += following[0].length\n edits.push({ start, end, value: '' })\n return\n }\n ts.forEachChild(inner, stripDroppedSpreads)\n }\n for (const statement of ast.statements) stripDroppedSpreads(statement)\n }\n }\n\n // Docusaurus' own docs teach `require('./relative/asset.ext').default`\n // as the idiom for linking a JSX attribute (`href={...}`) straight to a\n // static asset — real, working MDX on the source site, but a dangling\n // `require()` of a path the migrated project never has once copied\n // verbatim (\"Module not found\" at build time). Only a JSX *attribute*\n // expression is handled here (`mdxFlowExpression`/`mdxTextExpression`\n // bodies never hit this shape in practice and the ESM-import loop above\n // already covers a bare top-level `require()`); each attribute's own\n // `position` bounds the replacement to just that attribute's text, so\n // this can never match the identical-looking call inside a *fenced code\n // example* documenting the very same idiom (a real risk here, since\n // Docusaurus' own assets.mdx page shows both side by side).\n walk(tree, (node) => {\n for (const attribute of node.attributes ?? []) {\n if (attribute.type !== 'mdxJsxAttribute' || !attribute.value || typeof attribute.value !== 'object') continue\n const start = attribute.position?.start.offset\n const end = attribute.position?.end.offset\n if (start === undefined || end === undefined) continue\n const attributeText = content.slice(start, end)\n const match = attributeText.match(/require\\(\\s*(['\"])((?:\\.\\.?\\/|\\/|@site\\/)[^'\"]+?\\.(?:svg|png|jpe?g|webp|gif|avif|ico|docx|pdf))\\1\\s*\\)(?:\\.default|\\.src)?/)\n if (!match || match.index === undefined) continue\n try {\n const specifier = match[2]\n const normalized = specifier.startsWith('@site/') ? `/${specifier.slice('@site/'.length)}` : specifier\n // `true`: this is exactly the Playwright `require('../images/...')`\n // idiom (a page under a flattened project root reaching a shared\n // asset that only exists at the mirrored repository-root path) —\n // see `resolveDependency`'s `allowFlattenedMirror` doc.\n const resolvedPath = normalized.startsWith('/') ? resolveWithin(root, normalized.slice(1)) : resolveDependency(normalized, currentFile, true)\n const href = copyPublicAsset(resolvedPath)\n const replaced = attributeText.slice(0, match.index) + JSON.stringify(href) + attributeText.slice(match.index + match[0].length)\n edits.push({ start, end, value: replaced })\n } catch {\n // Not actually resolvable/copyable, even after the repository-root\n // fallback above. Leaving the dangling `require()` in place would\n // break the whole site's build (\"Module not found\"), so replace it\n // with an empty string (a broken image is degraded, not fatal) and\n // warn instead.\n const replaced = attributeText.slice(0, match.index) + JSON.stringify('') + attributeText.slice(match.index + match[0].length)\n edits.push({ start, end, value: replaced })\n warn(`Asset require(${JSON.stringify(match[2])}) could not be resolved and was removed; the reference was replaced with an empty string.`, currentFile)\n }\n }\n })\n\n // A page-local declaration (not imported) that uses a hook or an event\n // handler must move to the client module along with its invocation: left\n // inline, it compiles into the page's own server-rendered module, where\n // `useState` and friends are never in scope (see `implicitReactImports`,\n // only applied to extracted client files).\n // A component the extraction above refused (and warned about) stays where\n // it is; this pass must not move it, and the dependency that blocked it,\n // behind that decision.\n const statefulDeclarationNames = new Set(\n declarations\n .filter(({ source }) => /\\bon[A-Z]\\w*\\s*=|\\buse[A-Z]\\w*\\s*\\(/.test(source))\n .map(({ source }) => source.match(/^export const (\\w+)/)?.[1])\n .filter((name): name is string => !!name && !refusedInlineComponents.has(name)),\n )\n\n // Extract whole HTML JSX roots with event handlers. Markdown and global\n // built-ins remain server-rendered; React functions stay inside the client\n // module, so no function is serialized across a server/client boundary.\n const extracted: Array<{ node: MdxNode; name: string; jsx: string }> = []\n for (const candidate of tree.children ?? []) {\n const node = candidate.type === 'paragraph' && candidate.children?.length === 1 ? candidate.children[0] : candidate\n if (!['mdxJsxFlowElement', 'mdxJsxTextElement'].includes(node.type) || node.position?.start.offset === undefined || node.position.end.offset === undefined) continue\n let interactive = statefulDeclarationNames.has(node.name ?? '')\n let supported = true\n walk(node, (child) => {\n if (child.attributes?.some((attribute) => /^on[A-Z]/.test(attribute.name ?? ''))) interactive = true\n if (child.name && /^[A-Z]/.test(child.name) && !aliases.has(child.name) && !statefulDeclarationNames.has(child.name)) supported = false\n if (!['mdxJsxFlowElement', 'mdxJsxTextElement', 'mdxFlowExpression', 'mdxTextExpression', 'text', 'paragraph'].includes(child.type)) supported = false\n })\n if (!interactive) continue\n if (!supported || hasUnsupportedImports) {\n warn('Interactive MDX containing Markdown or unregistered components requires manual extraction; source was preserved.', currentFile)\n continue\n }\n const jsx = content.slice(node.position.start.offset, node.position.end.offset)\n // The JSX compiler is the final syntax gate: Markdown embedded in JSX\n // must not accidentally become JavaScript in the generated module.\n const result = ts.transpileModule(`export default function Inline() { return (${jsx}); }`, {\n fileName: 'inline.jsx', compilerOptions: { jsx: ts.JsxEmit.Preserve }, reportDiagnostics: true,\n })\n if (result.diagnostics?.some((diagnostic) => diagnostic.category === ts.DiagnosticCategory.Error)) {\n warn('Interactive MDX could not be safely extracted as JSX; source was preserved.', currentFile)\n continue\n }\n extracted.push({ node, name: `Inline${extracted.length}`, jsx })\n }\n if (extracted.length && declarations.length) {\n const declared = new Set<string>()\n function bind(name: ts.BindingName): void {\n if (ts.isIdentifier(name)) declared.add(name.text)\n else for (const element of name.elements) if (ts.isBindingElement(element)) bind(element.name)\n }\n for (const declaration of declarations) {\n for (const statement of sourceFile(declaration.source, 'declaration.tsx').statements) {\n if ((ts.isFunctionDeclaration(statement) || ts.isClassDeclaration(statement)) && statement.name) declared.add(statement.name.text)\n if (ts.isVariableStatement(statement)) for (const variable of statement.declarationList.declarations) bind(variable.name)\n }\n }\n const hasSharedDeclarations = hasExpressionReference(declared, {\n excludedNodes: extracted.map((entry) => entry.node),\n includeDeclarations: false,\n includeDirectTags: true,\n })\n if (hasSharedDeclarations) {\n warn('Interactive MDX shares declarations with remaining page content; source was preserved for manual extraction.', currentFile)\n extracted.length = 0\n }\n }\n if (extracted.length && (copied.size >= MAX_COMPONENT_FILES\n || Buffer.byteLength(content) > MAX_FILE_BYTES\n || copiedBytes + Buffer.byteLength(content) > MAX_COMPONENT_BYTES)) {\n warn('Interactive MDX exceeded the component migration budget; source was preserved.', currentFile)\n extracted.length = 0\n }\n if (extracted.length) {\n const path = `${destinationRoot}/inline-${hash(relative(root, currentFile))}.jsx`\n let declarationSource = [...new Set(declarations.map((entry) => entry.source))].join('\\n\\n')\n // Mintlify exposes this DOM id as its documented search trigger. Thally\n // opens search through the same keyboard event handled by CommandSearch.\n declarationSource = declarationSource.replace(/document\\.getElementById\\(\\s*(['\"])search-bar-entry\\1\\s*\\)\\.click\\(\\s*\\)/g,\n `document.dispatchEvent(new KeyboardEvent('keydown', { key: 'k', ctrlKey: true, bubbles: true }))`)\n const inlineSource = [\n ...moduleImports, '', declarationSource, '',\n ...extracted.map(({ name, jsx }) => `export function ${name}() {\\n return (${jsx});\\n}`), '',\n ].join('\\n')\n // A single leading directive: `inlineSource` no longer carries its own,\n // or Next rejects the file (\"use client\" must be the first statement).\n copied.set(path, { path, content: `'use client';\\n${implicitReactImports(sourceFile(inlineSource, 'inline.jsx'))}\\n${inlineSource}` })\n copiedBytes += Buffer.byteLength(inlineSource)\n for (const { node, name } of extracted) {\n edits.push({ start: node.position!.start.offset!, end: node.position!.end.offset!, value: `<${register(path, name)} />` })\n }\n edits.push(...declarations.map(({ start, end }) => ({ start, end, value: '' })), ...sharedImportEdits)\n }\n // A page-local callback cannot cross into a copied client component. Move\n // a self-contained JSX callback into a thin client adapter while keeping\n // the MDX children on the page. More complex callbacks remain passive.\n const pageFunctions = functionDeclaredNames(content)\n const passiveRoots: Array<MdxNode> = []\n const wrappedRoots = new Set<MdxNode>()\n walk(tree, (node) => {\n if (!node.name || !aliases.has(node.name) || !functionValuedAttribute(node, pageFunctions)\n || node.position?.start.offset === undefined || node.position.end.offset === undefined) return\n if (passiveRoots.some((root) => node.position!.start.offset! >= root.position!.start.offset!\n && node.position!.end.offset! <= root.position!.end.offset!)) return\n const functionAttributes = (node.attributes ?? []).filter((attribute) => {\n const value = attribute.value && typeof attribute.value === 'object' ? attribute.value.value?.trim() : undefined\n return !!value && pageFunctions.has(value)\n })\n if (functionAttributes.length === 1 && functionAttributes[0].name\n && functionAttributes[0].position?.start.offset !== undefined\n && functionAttributes[0].position.end.offset !== undefined) {\n const functionName = (functionAttributes[0].value as { value: string }).value.trim()\n const declaration = declarations.find((entry) => new RegExp(`^export const ${functionName}\\\\s*=`).test(entry.source))\n const safeDeclaration = declaration?.source.match(/^export const \\w+\\s*=\\s*\\(\\s*\\{\\s*children(?:\\s*,\\s*\\.\\.\\.props)?\\s*\\}\\s*\\)\\s*=>\\s*\\([\\s\\S]*\\)\\s*;?$/)\n && !/\\b(?:window|document|process|fetch|use[A-Z]\\w*)\\b/.test(declaration.source)\n && [...declaration.source.matchAll(/<([A-Z]\\w*)\\b/g)].every((match) => match[1] === 'CodeBlock')\n && [...declaration.source.matchAll(/\\{([^{}]*)\\}/g)].every((match) =>\n ['children', '...props', 'children, ...props'].includes(match[1].trim()))\n const binding = registrations.get(aliases.get(node.name)!)\n if (safeDeclaration && binding) {\n const fallbackDeclaration = declaration!.source.replace(/<CodeBlock\\b[^>]*>/g, '<pre><code>').replace(/<\\/CodeBlock>/g, '</code></pre>')\n const wrapperPath = `${destinationRoot}/callback-${hash(`${currentFile}:${node.position.start.offset}`)}.jsx`\n const imported = binding.imported === 'default' ? 'default' : binding.imported\n const importClause = imported === 'default' ? 'import Original' : `import { ${imported} as Original }`\n const specifier = portableSpecifier(`./${relative(destinationRoot, binding.path).replace(/\\\\/g, '/')}`)\n const wrapperName = register(wrapperPath, 'CallbackWrapper')\n copied.set(wrapperPath, { path: wrapperPath, content: [\n \"'use client';\", `${importClause} from ${JSON.stringify(specifier)};`,\n fallbackDeclaration,\n `export function CallbackWrapper({ children, ...props }) { return <Original {...props} ${functionAttributes[0].name}={${functionName}}>{children}</Original>; }`,\n ].join('\\n') })\n const start = node.position.start.offset\n const end = node.position.end.offset\n const source = content.slice(start, end)\n const opening = source.indexOf(`<${node.name}`)\n const closing = source.lastIndexOf(`</${node.name}`)\n if (opening >= 0) edits.push({ start: start + opening + 1, end: start + opening + 1 + node.name.length, value: wrapperName })\n if (closing >= 0) edits.push({ start: start + closing + 2, end: start + closing + 2 + node.name.length, value: wrapperName })\n edits.push({ start: functionAttributes[0].position!.start.offset!, end: functionAttributes[0].position!.end.offset!, value: '' })\n wrappedRoots.add(node)\n if (declaration!.source.includes('<CodeBlock')) warn(`Interactive <${node.name}> was retained with a basic code block in place of Mintlify's CodeBlock.`, currentFile)\n return\n }\n }\n const source = content.slice(node.position.start.offset, node.position.end.offset)\n const openingEnd = source.indexOf('>')\n const closingStart = source.lastIndexOf(`</${node.name}`)\n const children = openingEnd >= 0 && closingStart > openingEnd ? source.slice(openingEnd + 1, closingStart) : ''\n edits.push({ start: node.position.start.offset, end: node.position.end.offset,\n value: `<div data-migration-interactive-fallback=\"${node.name}\">${children}</div>` })\n passiveRoots.push(node)\n warn(`Interactive <${node.name}> passes a page-local function into a client component; its child content was retained without the interactive control. Move the callback into a client component to restore it.`, currentFile)\n })\n walk(tree, (node) => {\n const replacement = node.name ? aliases.get(node.name) : undefined\n if (!replacement || node.position?.start.offset === undefined || node.position.end.offset === undefined) return\n if (wrappedRoots.has(node)) return\n if (passiveRoots.some((root) => node.position!.start.offset! >= root.position!.start.offset!\n && node.position!.end.offset! <= root.position!.end.offset!)) return\n const start = node.position.start.offset\n const end = node.position.end.offset\n if (extracted.some((item) => start >= item.node.position!.start.offset! && end <= item.node.position!.end.offset!)) return\n const text = content.slice(start, end)\n const opening = text.indexOf(`<${node.name}`)\n if (opening >= 0) edits.push({ start: start + opening + 1, end: start + opening + 1 + node.name!.length, value: replacement })\n const closing = text.lastIndexOf(`</${node.name}`)\n if (closing >= 0) edits.push({ start: start + closing + 2, end: start + closing + 2 + node.name!.length, value: replacement })\n })\n // Every usage of a removed npm-package import is replaced whole (tag,\n // props, and children) rather than just its name: nothing in the\n // migrated project can render it. A video-embed-shaped usage (an `id`\n // prop, a name suggesting an embedded player) gets a trivial working\n // `<iframe>`; anything else becomes a visible, greppable MDX comment.\n walk(tree, (node) => {\n const specifier = node.name ? unsupportedImports.get(node.name) : undefined\n if (!specifier || node.position?.start.offset === undefined || node.position.end.offset === undefined) return\n const start = node.position.start.offset\n const end = node.position.end.offset\n const videoId = youtubeEmbedVideoId(node, specifier)\n const value = videoId\n ? `<iframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/${videoId}\" title=\"Embedded video\" allowFullScreen />`\n : mdxComment(` Removed <${node.name}>: unsupported import '${specifier}' `)\n edits.push({ start, end, value })\n })\n // Dynamic `import('./x')` of a file the migration copies points at the\n // copied module instead. Located on the edited text, so the offsets\n // never overlap the edits above; one that cannot be copied is left as\n // written and the page is excluded afterwards (repository.ts).\n const edited = applyReplacements(content, edits)\n const dynamicEdits: Array<Replacement> = []\n try {\n for (const { specifier: rawDynamic, start, end } of dynamicImportLiterals(edited, parser.parse(edited) as MdxNode)) {\n if (start === undefined || end === undefined || !/^\\.\\.?\\//.test(rawDynamic) || /\\.mdx?$/i.test(rawDynamic)) continue\n try {\n const path = copyGraph(resolveDependency(rawDynamic, currentFile))\n dynamicEdits.push({ start, end, value: JSON.stringify(portableSpecifier(`@/${path.replace(/^src\\//, '').replace(/\\\\/g, '/')}`)) })\n } catch {\n // Not shipped: reported by the caller's exclusion check.\n }\n }\n } catch {\n // The edited text does not parse; the caller reports it.\n }\n const rendered = applyReplacements(edited, dynamicEdits)\n // Inserted after all offset-based edits (it has no position in the\n // original source) so it lands once, at the very top of the body.\n const pageStatements = [...new Set([...realPageImports, ...serverPageDeclarations])]\n const realImportsBlock = pageStatements.length ? `${pageStatements.join('\\n')}\\n\\n` : ''\n return frontmatter + realImportsBlock + rendered\n }\n\n function files(): Array<RenderedMigrationFile> {\n // A rescued asset (`copyPublicAsset`) lands in `copied` without ever\n // registering a component; it still needs emitting even when nothing\n // was registered.\n if (!copied.size) return []\n if (!registrations.size) return [...copied.values()]\n const entries = [...registrations.entries()].sort(([a], [b]) => a.localeCompare(b))\n return [...copied.values(), {\n path: 'src/mdx/custom-components.tsx',\n content: [\n '/** Repository components preserved by migration; this registry is customer-owned. */',\n \"import type { MDXComponents } from 'mdx/types'\", ...entries.map(([name, binding]) => {\n const path = portableSpecifier(`./${relative('src/mdx', binding.path).replace(/\\\\/g, '/')}`)\n return `import { ${binding.imported} as ${name} } from ${JSON.stringify(path)}`\n }), '',\n // Not every registered local binding is JSX-taggable — a companion\n // value from the same import (an enum used only as `Foo.Bar` inside\n // another component's prop, e.g.) is registered too, so the whole\n // MDX scope resolves it. `MDXComponents`' index signature expects a\n // component at every key, which such a value structurally is not;\n // cast rather than exclude it, since excluding it would leave a\n // dangling reference in the page that DOES need it.\n 'export const customComponents = {',\n ...entries.map(([name]) => ` ${name},`), '} as unknown as MDXComponents', '',\n ].join('\\n'),\n }]\n }\n // Rendered registry lines look like `import { Imported as MigratedAbc } from \"./migrated/<id>/x\"`.\n const REGISTRY_IMPORT = /^import \\{ (\\w+) as (Migrated[0-9a-f]+) \\} from \"(\\.\\/[^\"]+)\"$/gm\n function adopt(incoming: ReadonlyArray<RenderedMigrationFile>): void {\n for (const file of incoming) {\n if (file.path !== 'src/mdx/custom-components.tsx') {\n if (!copied.has(file.path)) copied.set(file.path, file)\n continue\n }\n if (typeof file.content !== 'string') continue\n for (const [, imported, name, specifier] of file.content.matchAll(REGISTRY_IMPORT)) {\n const base = posix.join('src/mdx', specifier)\n const path = [...copied.keys()].find((candidate) => candidate.replace(/\\.tsx?$/, '') === base) ?? base\n registrations.set(name, { path, imported })\n }\n }\n }\n return { transform, files, adopt }\n}\n\n/** Preserve an authored component registry while adding an isolated import map. */\nexport function mergeComponentRegistry(existing: string, incoming: string): Array<RenderedMigrationFile> {\n const fingerprint = hash(incoming)\n const name = `MigratedRegistry${fingerprint}`\n const registryPath = `./migrated-components-${fingerprint}`\n const source = sourceFile(existing, 'custom-components.tsx')\n const alreadyImported = source.statements.some((statement) => ts.isImportDeclaration(statement)\n && ts.isStringLiteral(statement.moduleSpecifier) && statement.moduleSpecifier.text === registryPath)\n let initializer: ts.Expression | undefined\n for (const statement of source.statements) {\n if (!ts.isVariableStatement(statement) || !statement.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)) continue\n for (const declaration of statement.declarationList.declarations) {\n if (ts.isIdentifier(declaration.name) && declaration.name.text === 'customComponents') initializer = declaration.initializer\n }\n }\n while (initializer && (ts.isAsExpression(initializer) || ts.isSatisfiesExpression(initializer) || ts.isParenthesizedExpression(initializer))) initializer = initializer.expression\n if (!initializer || !ts.isObjectLiteralExpression(initializer)) {\n throw new Error('Cannot safely merge src/mdx/custom-components.tsx: export customComponents as an object literal before importing components. The existing registry was preserved.')\n }\n const content = alreadyImported ? existing : applyReplacements(existing, [\n { start: source.statements.filter((statement) => ts.isExpressionStatement(statement) && ts.isStringLiteral(statement.expression)).at(-1)?.end ?? 0,\n end: source.statements.filter((statement) => ts.isExpressionStatement(statement) && ts.isStringLiteral(statement.expression)).at(-1)?.end ?? 0,\n value: `\\nimport { customComponents as ${name} } from ${JSON.stringify(registryPath)}\\n` },\n { start: initializer.getStart(source) + 1, end: initializer.getStart(source) + 1, value: `\\n ...${name},` },\n ])\n return [\n { path: `src/mdx/migrated-components-${fingerprint}.tsx`, content: incoming },\n { path: 'src/mdx/custom-components.tsx', content },\n ]\n}\n","/** Render canonical migration bundles into a portable Thally repository tree. */\n\nimport { mergeComponentRegistry } from './components.js'\n\nimport type {\n MigrationBundle,\n MigrationDocsConfig,\n MigrationNavigationGroup,\n RenderedMigrationFile,\n} from './types.js'\n\nfunction yamlString(value: string): string {\n return JSON.stringify(value.replace(/\\s+/g, ' ').trim())\n}\n\nfunction renderPage(bundle: MigrationBundle, page: MigrationBundle['pages'][number]): string {\n return [\n '---',\n `title: ${yamlString(page.title)}`,\n page.headingTitle ? `headingTitle: ${yamlString(page.headingTitle)}` : null,\n page.navTitle ? `navTitle: ${yamlString(page.navTitle)}` : null,\n page.icon ? `icon: ${yamlString(page.icon)}` : null,\n page.iconType ? `iconType: ${yamlString(page.iconType)}` : null,\n `description: ${yamlString(page.description)}`,\n page.descriptionPlacement === 'body' ? 'descriptionPlacement: body' : null,\n page.badge ? `badge: ${yamlString(page.badge)}` : null,\n page.keywords.length > 0 ? `keywords: [${page.keywords.map(yamlString).join(', ')}]` : null,\n page.mode ? `mode: ${yamlString(page.mode)}` : null,\n page.hidden ? 'hidden: true' : null,\n page.noindex ? 'noindex: true' : null,\n // Group names must match identity-provider claims exactly, so they are\n // quoted as-is rather than whitespace-collapsed like display strings.\n page.groups?.length ? `groups: [${page.groups.map((group) => JSON.stringify(group)).join(', ')}]` : null,\n page.public !== undefined ? `public: ${page.public ? 'true' : 'false'}` : null,\n ...Object.entries(page.meta ?? {}).map(([field, value]) => `${field}: ${yamlString(value)}`),\n page.openapi ? `openapi: ${yamlString(page.openapi)}` : null,\n page.api ? `api: ${yamlString(page.api)}` : null,\n page.authMethod ? `authMethod: ${yamlString(page.authMethod)}` : null,\n page.playground ? `playground: ${yamlString(page.playground)}` : null,\n bundle.sourceKind === 'url' ? `source: ${yamlString(page.source)}` : null,\n '---',\n '',\n page.body,\n '',\n ].filter((line) => line !== null).join('\\n')\n}\n\nfunction isIntegrationsObject(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === 'object' && !Array.isArray(value)\n}\n\n/** Merge imported tabs into an existing site without duplicating changelog tabs. */\nexport function mergeMigrationConfig(\n existing: MigrationDocsConfig,\n incoming: MigrationDocsConfig,\n): MigrationDocsConfig {\n const tabs = existing.tabs.filter((tab) => tab.tab.toLowerCase() !== 'changelog')\n const names = new Set(tabs.map((tab) => tab.tab.toLowerCase()))\n const seenPages = new Set<string>()\n function recordPages(pages: Array<string | MigrationNavigationGroup> = []): void {\n for (const page of pages) {\n if (typeof page === 'string') seenPages.add(page)\n else recordPages(page.pages)\n }\n }\n for (const tab of tabs) {\n recordPages(tab.pages)\n recordPages(tab.groups)\n }\n function uniquePages(\n pages: Array<string | MigrationNavigationGroup> = [],\n ): Array<string | MigrationNavigationGroup> {\n return pages.flatMap<string | MigrationNavigationGroup>((page) => {\n if (typeof page === 'string') {\n if (seenPages.has(page)) return []\n seenPages.add(page)\n return [page]\n }\n const children = uniquePages(page.pages)\n return children.length > 0 ? [{ ...page, pages: children }] : []\n })\n }\n function uniqueGroups(\n groups: Array<MigrationNavigationGroup> | undefined,\n ): Array<MigrationNavigationGroup> {\n return uniquePages(groups ?? []) as Array<MigrationNavigationGroup>\n }\n for (const tab of incoming.tabs) {\n if (tab.tab.toLowerCase() === 'changelog') continue\n const uniqueTab = {\n ...tab,\n ...(tab.pages ? { pages: uniquePages(tab.pages) } : {}),\n ...(tab.groups ? { groups: uniqueGroups(tab.groups) } : {}),\n }\n if ((tab.pages || tab.groups)\n && !uniqueTab.pages?.length\n && !uniqueTab.groups?.length\n && !tab.href\n && !tab.api) continue\n if (!names.has(tab.tab.toLowerCase())) {\n tabs.push(uniqueTab)\n names.add(tab.tab.toLowerCase())\n continue\n }\n const target = tabs.find((item) => item.tab.toLowerCase() === tab.tab.toLowerCase())\n if (!target) continue\n const incomingRootNodes = [\n ...(uniqueTab.pages ?? []),\n ...(uniqueTab.groups ?? []),\n ]\n if (uniqueTab.pages?.length) {\n target.pages = [...(target.pages ?? target.groups ?? []), ...incomingRootNodes]\n delete target.groups\n } else if (uniqueTab.groups?.length) {\n if (target.pages) target.pages = [...target.pages, ...uniqueTab.groups]\n else target.groups = [...(target.groups ?? []), ...uniqueTab.groups]\n }\n }\n const changelog = existing.tabs.find((tab) => tab.tab.toLowerCase() === 'changelog')\n ?? incoming.tabs.find((tab) => tab.tab.toLowerCase() === 'changelog')\n if (changelog) tabs.push(changelog)\n const i18n = existing.i18n || incoming.i18n\n ? {\n defaultLocale: existing.i18n?.defaultLocale ?? incoming.i18n?.defaultLocale ?? 'en',\n locales: [...new Map(\n [...(existing.i18n?.locales ?? []), ...(incoming.i18n?.locales ?? [])]\n .map((locale) => [locale.code, locale]),\n ).values()],\n }\n : undefined\n return {\n ...incoming,\n ...existing,\n tabs,\n // Collection presentation belongs to the imported information\n // architecture; starter defaults must not coerce source dropdowns to tabs.\n navigation: incoming.navigation ?? existing.navigation,\n ...((existing.stylesheets?.length || incoming.stylesheets?.length)\n ? { stylesheets: [...new Set([...(existing.stylesheets ?? []), ...(incoming.stylesheets ?? [])])] }\n : {}),\n ...(i18n ? { i18n } : {}),\n // Existing providers win per provider; imported ones fill the gaps.\n // A malformed (string/array) existing value must not spread into index keys.\n ...(isIntegrationsObject(existing.integrations) || isIntegrationsObject(incoming.integrations)\n ? {\n integrations: {\n ...(isIntegrationsObject(incoming.integrations) ? incoming.integrations : {}),\n ...(isIntegrationsObject(existing.integrations) ? existing.integrations : {}),\n },\n }\n : {}),\n }\n}\n\n/** Materialize every canonical page, asset, and config as repository file changes. */\nexport function renderMigrationFiles(\n bundle: MigrationBundle,\n options: { existingConfig?: MigrationDocsConfig; existingComponentRegistry?: string } = {},\n): Array<RenderedMigrationFile> {\n const config = options.existingConfig\n ? mergeMigrationConfig(options.existingConfig, bundle.docsConfig)\n : bundle.docsConfig\n return [\n ...bundle.pages.map((page) => ({\n path: `src/content/${page.id}.mdx`,\n content: renderPage(bundle, page),\n })),\n ...bundle.assets.map((asset) => ({ path: asset.projectRelative ? asset.path : `public/${asset.path}`, content: asset.content })),\n ...(bundle.componentFiles ?? []).flatMap((file) => file.path === 'src/mdx/custom-components.tsx'\n && options.existingComponentRegistry !== undefined && typeof file.content === 'string'\n ? mergeComponentRegistry(options.existingComponentRegistry, file.content)\n : [file]),\n ...(bundle.quarantinedFiles ?? []),\n { path: 'docs.json', content: `${JSON.stringify(config, null, 2)}\\n` },\n ]\n}\n"],"mappings":"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","names":["load","remarkParse","remarkMdx","unified","realpathSync","extname","relative","relative","sourceNavigation","parser","resolve","remarkMdx","remarkParse","unified","parseYaml","plainText","unified","remarkParse","remarkMdx","parseYaml","content","title","unified","remarkParse","remarkMdx","canonical","load","resolve","relative","createHash","existsSync","lstatSync","readFileSync","dirname","extname","posix","relative","resolve","remarkMdx","remarkParse","ts","unified","parser","unified","remarkParse","remarkMdx","hash","createHash","ts","hash","ts"]}
|