import * as runtime from "react/jsx-runtime";
import { m, useLanguage } from "../i18n.mjs";
import useMDXComponents from "../mdx-components.jsx";
import {
apiReferenceGlobalVersion,
apiReferencePageVersion,
apiReferenceSymbolTable,
renderApiReferencePageMdx,
} from "../lib/api-reference.mjs";
import Link from "./Link.jsx";
// Lazy-load the MDX compiler pipeline via dynamic imports with
// non-literal specifiers — Rolldown only bundles `import("literal")`,
// leaving `import(variable)` as runtime resolution. Without this,
// `@mdx-js/mdx` alone is ~500KB and inflates the Cloudflare edge
// worker bundle to well past what it needs to be.
//
// The worker never actually reaches this code — pre-rendered pages
// are served from the ASSETS binding, and `/api/:slug` paths that
// don't match the matchers fall through to the 404 page without
// invoking `ApiReferencePage`. At SSG build time (Node), the dynamic
// imports resolve normally from `node_modules`.
const mdxModuleId = "@mdx-js/mdx";
const rehypeHighlightId = "rehype-highlight";
const rehypeMdxCodePropsId = "rehype-mdx-code-props";
const remarkGfmId = "remark-gfm";
let _pipeline;
async function getMdxPipeline() {
if (_pipeline) return _pipeline;
const [mdx, rh, rmcp, rg] = await Promise.all([
import(/* @vite-ignore */ mdxModuleId),
import(/* @vite-ignore */ rehypeHighlightId),
import(/* @vite-ignore */ rehypeMdxCodePropsId),
import(/* @vite-ignore */ remarkGfmId),
]);
_pipeline = {
compile: mdx.compile,
run: mdx.run,
rehypeHighlight: rh.default ?? rh,
rehypeMdxCodeProps: rmcp.default ?? rmcp,
remarkGfm: rg.default ?? rg,
};
return _pipeline;
}
// Compiled-component cache keyed by (slug, locale, *global* mtime).
// The global mtime means a JSDoc edit on any page invalidates every
// cached page, so cross-reference targets stay in sync.
const compileCache = new Map();
// ── Cross-reference rehype plugin ──────────────────────────────────────────
// Walks every `<code>` element in the compiled tree, splits text nodes on
// identifier boundaries, and wraps any identifier that matches a known API
// symbol with an `<a>` link. Targets on the same page use `#anchor`;
// everything else uses `/api/<slug>#anchor`. Same-name collisions
// (e.g. `createRoute` on /router *and* /navigation) resolve to the
// current page when possible.
const IDENTIFIER_RE = /[A-Za-z_$][A-Za-z_$0-9]*/g;
// Reserved names that should never be linked even if a symbol shares the
// name — keeps code readable and avoids false positives on common tokens.
const RESERVED = new Set([
"string",
"number",
"boolean",
"void",
"null",
"undefined",
"any",
"never",
"unknown",
"object",
"symbol",
"bigint",
"true",
"false",
"this",
"typeof",
"keyof",
"infer",
"extends",
"readonly",
"in",
"out",
"as",
"is",
"new",
"const",
"let",
"var",
"function",
"class",
"interface",
"type",
"enum",
"import",
"export",
"default",
"return",
"async",
"await",
"yield",
"if",
"else",
"for",
"while",
"do",
"switch",
"case",
"break",
"continue",
"throw",
"try",
"catch",
"finally",
"Promise",
"Array",
"Record",
"Partial",
"Required",
"Readonly",
"Pick",
"Omit",
"Exclude",
"Extract",
"NonNullable",
"Parameters",
"ReturnType",
"InstanceType",
"ConstructorParameters",
"ThisType",
"Map",
"Set",
"WeakMap",
"WeakSet",
"Error",
"Date",
"RegExp",
"JSON",
"Math",
"Object",
"Function",
"Boolean",
"Number",
"String",
"Symbol",
"BigInt",
"React",
"JSX",
"HTMLElement",
"Element",
"Node",
"Request",
"Response",
"Headers",
"URL",
"URLSearchParams",
"FormData",
"AbortSignal",
"AbortController",
"ReadableStream",
"WritableStream",
"Buffer",
"ArrayBuffer",
"Uint8Array",
]);
function resolveSymbol(name, currentSlug, table) {
if (RESERVED.has(name)) return null;
const entries = table[name];
if (!entries || entries.length === 0) return null;
// Prefer a definition on the current page; otherwise first-declared wins.
const local = entries.find((e) => e.slug === currentSlug);
const target = local ?? entries[0];
const href =
target.slug === currentSlug
? `#${target.anchor}`
: `/api/${target.slug}#${target.anchor}`;
return href;
}
function linkifyIdentifiersInText(value, currentSlug, table) {
const pieces = [];
let last = 0;
let m;
let produced = false;
while ((m = IDENTIFIER_RE.exec(value))) {
const name = m[0];
const href = resolveSymbol(name, currentSlug, table);
if (!href) continue;
if (m.index > last) {
pieces.push({ type: "text", value: value.slice(last, m.index) });
}
pieces.push({
type: "element",
tagName: "a",