@agent-seo/core 1.0.0

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/edge.ts","../src/detect.ts","../src/tokens.ts","../src/headers.ts","../src/llms-txt.ts"],"sourcesContent":["/**\n * Edge-compatible exports from @agent-seo/core.\n * This entrypoint only includes modules that work in Edge runtimes\n * (no Node.js-only dependencies like jsdom, stream, etc.)\n */\n\n// Types\nexport type {\n BotPurpose,\n BotInfo,\n AIRequestContext,\n TransformOptions,\n TransformResult,\n LlmsTxtRoute,\n LlmsTxtOptions,\n LlmsTxtResult,\n AgentSeoHeaders,\n AgentSeoOptions,\n} from './types.js';\n\n// Bot Detection (pure regex, no Node.js deps)\nexport { detectAgent, shouldServeMarkdown, AI_BOT_REGISTRY } from './detect.js';\n\n// Token Estimation (pure math, no Node.js deps)\nexport { estimateTokens } from './tokens.js';\n\n// Response Headers (pure string manipulation, no Node.js deps)\nexport { buildMarkdownHeaders, buildAlternateLinkHeader } from './headers.js';\n\n// llms.txt Generation (pure string manipulation, no Node.js deps)\nexport { generateLlmsTxt } from './llms-txt.js';\n","import type { AIRequestContext, BotInfo, BotPurpose } from './types.js';\n\ninterface BotEntry {\n /** Regex pattern to match against User-Agent string */\n pattern: RegExp;\n /** Bot metadata */\n info: BotInfo;\n}\n\nconst AI_BOT_REGISTRY: BotEntry[] = [\n // === OpenAI ===\n {\n pattern: /GPTBot/i,\n info: { name: 'GPTBot', operator: 'OpenAI', purpose: 'training', rendersJs: false },\n },\n {\n pattern: /OAI-SearchBot/i,\n info: { name: 'OAI-SearchBot', operator: 'OpenAI', purpose: 'search', rendersJs: false },\n },\n {\n pattern: /ChatGPT-User/i,\n info: { name: 'ChatGPT-User', operator: 'OpenAI', purpose: 'agent-browsing', rendersJs: true },\n },\n\n // === Anthropic ===\n {\n pattern: /ClaudeBot/i,\n info: { name: 'ClaudeBot', operator: 'Anthropic', purpose: 'training', rendersJs: false },\n },\n {\n pattern: /Claude-User/i,\n info: { name: 'Claude-User', operator: 'Anthropic', purpose: 'agent-browsing', rendersJs: true },\n },\n {\n pattern: /Claude-SearchBot/i,\n info: { name: 'Claude-SearchBot', operator: 'Anthropic', purpose: 'search', rendersJs: false },\n },\n {\n pattern: /anthropic-ai/i,\n info: { name: 'anthropic-ai', operator: 'Anthropic', purpose: 'training', rendersJs: false },\n },\n\n // === Perplexity ===\n {\n pattern: /PerplexityBot/i,\n info: { name: 'PerplexityBot', operator: 'Perplexity', purpose: 'search', rendersJs: false },\n },\n {\n pattern: /Perplexity-User/i,\n info: { name: 'Perplexity-User', operator: 'Perplexity', purpose: 'agent-browsing', rendersJs: true },\n },\n\n // === Google ===\n {\n pattern: /Google-Extended/i,\n info: { name: 'Google-Extended', operator: 'Google', purpose: 'training', rendersJs: true },\n },\n\n // === Apple ===\n {\n pattern: /Applebot-Extended/i,\n info: { name: 'Applebot-Extended', operator: 'Apple', purpose: 'training', rendersJs: true },\n },\n\n // === Meta ===\n {\n pattern: /meta-externalagent/i,\n info: { name: 'Meta-ExternalAgent', operator: 'Meta', purpose: 'training', rendersJs: false },\n },\n {\n pattern: /FacebookBot/i,\n info: { name: 'FacebookBot', operator: 'Meta', purpose: 'search', rendersJs: false },\n },\n\n // === Common Crawl ===\n {\n pattern: /CCBot/i,\n info: { name: 'CCBot', operator: 'Common Crawl', purpose: 'training', rendersJs: false },\n },\n\n // === Cohere ===\n {\n pattern: /cohere-ai/i,\n info: { name: 'cohere-ai', operator: 'Cohere', purpose: 'training', rendersJs: false },\n },\n\n // === Amazon ===\n {\n pattern: /Amazonbot/i,\n info: { name: 'Amazonbot', operator: 'Amazon', purpose: 'search', rendersJs: false },\n },\n\n // === Bytedance ===\n {\n pattern: /Bytespider/i,\n info: { name: 'Bytespider', operator: 'ByteDance', purpose: 'training', rendersJs: false },\n },\n\n // === You.com ===\n {\n pattern: /YouBot/i,\n info: { name: 'YouBot', operator: 'You.com', purpose: 'search', rendersJs: false },\n },\n\n // === DeepSeek ===\n {\n pattern: /Deepseek/i,\n info: { name: 'DeepSeekBot', operator: 'DeepSeek', purpose: 'training', rendersJs: false },\n },\n];\n\nconst TOKEN_REGISTRY = AI_BOT_REGISTRY.map((entry) => ({\n entry,\n token: regexToToken(entry.pattern),\n}));\n\n/**\n * Detect if an incoming request is from an AI bot.\n *\n * @param userAgent - The User-Agent header value\n * @param acceptHeader - The Accept header value (optional)\n * @returns AIRequestContext with bot detection results\n */\nexport function detectAgent(\n userAgent: string | null | undefined,\n acceptHeader?: string | null\n): AIRequestContext {\n const wantsMarkdown = acceptHeader\n ? /text\\/markdown/i.test(acceptHeader)\n : false;\n\n if (!userAgent) {\n return { isAIBot: false, bot: null, wantsMarkdown };\n }\n\n const ua = userAgent.toLowerCase();\n\n // Check against our AI-specific registry first (more precise)\n for (const { entry, token } of TOKEN_REGISTRY) {\n if (token) {\n if (ua.includes(token)) {\n return { isAIBot: true, bot: entry.info, wantsMarkdown };\n }\n continue;\n }\n if (entry.pattern.test(userAgent)) {\n return { isAIBot: true, bot: entry.info, wantsMarkdown };\n }\n }\n\n // Fallback: we do NOT serve markdown to generic bots — only AI bots get the special treatment\n return { isAIBot: false, bot: null, wantsMarkdown };\n}\n\n/**\n * Check if a request should receive Markdown.\n * Returns true if: the request is from a known AI bot OR the Accept header requests text/markdown.\n */\nexport function shouldServeMarkdown(\n userAgent: string | null | undefined,\n acceptHeader?: string | null\n): boolean {\n const ctx = detectAgent(userAgent, acceptHeader);\n return ctx.isAIBot || ctx.wantsMarkdown;\n}\n\nexport { AI_BOT_REGISTRY };\n\nfunction regexToToken(pattern: RegExp): string | null {\n const source = pattern.source;\n if (/^[A-Za-z0-9-]+$/.test(source)) return source.toLowerCase();\n return null;\n}\n","/**\n * Estimate token count using the chars/4 heuristic.\n * This is the same heuristic used by Cloudflare's X-Markdown-Tokens header.\n * Accurate to within ~10% for English text across GPT and Claude tokenizers.\n */\nexport function estimateTokens(text: string): number {\n return Math.ceil(text.length / 4);\n}\n","import type { TransformResult, AgentSeoHeaders, AgentSeoOptions } from './types.js';\n\n/**\n * Build the response headers for a Markdown response.\n */\nexport function buildMarkdownHeaders(\n result: TransformResult,\n options: Pick<AgentSeoOptions, 'contentSignal'>,\n originalPath?: string\n): AgentSeoHeaders {\n const headers: AgentSeoHeaders = {\n 'Content-Type': 'text/markdown; charset=utf-8',\n 'Content-Disposition': 'inline',\n 'Vary': 'Accept, User-Agent',\n 'X-Markdown-Tokens': String(result.tokenEstimate),\n };\n\n // Content-Signal header (Cloudflare convention)\n const signal = options.contentSignal ?? { aiTrain: true, search: true, aiInput: true };\n const signalParts: string[] = [];\n if (signal.aiTrain !== false) signalParts.push('ai-train=yes');\n if (signal.search !== false) signalParts.push('search=yes');\n if (signal.aiInput !== false) signalParts.push('ai-input=yes');\n if (signalParts.length > 0) {\n headers['Content-Signal'] = signalParts.join(', ');\n }\n\n // X-Robots-Tag: let all AI bots index\n headers['X-Robots-Tag'] = 'all';\n\n return headers;\n}\n\n/**\n * Build a Link header pointing to the Markdown alternate.\n * This is injected into ALL HTML responses (not just Markdown ones)\n * so crawlers can discover the alternate representation.\n */\nexport function buildAlternateLinkHeader(path: string, ext: string = '.md'): string {\n // /docs/getting-started → /docs/getting-started.md\n const mdPath = path.endsWith('/') ? `${path}index${ext}` : `${path}${ext}`;\n return `<${mdPath}>; rel=\"alternate\"; type=\"text/markdown\"`;\n}\n","import type { LlmsTxtOptions, LlmsTxtRoute, LlmsTxtResult } from './types.js';\n\n/**\n * Generate llms.txt and llms-full.txt content from route data.\n *\n * llms.txt format (per spec at llmstxt.org):\n * # Site Name\n * > Site description blockquote\n *\n * ## Section Name\n * - [Page Title](url): Description\n */\nexport function generateLlmsTxt(\n options: LlmsTxtOptions,\n routes: LlmsTxtRoute[],\n fullTextContents?: Map<string, string>\n): LlmsTxtResult {\n const { siteName, siteDescription, baseUrl, markdownExtension = '.md' } = options;\n\n // Group routes by section\n const sections = new Map<string, LlmsTxtRoute[]>();\n for (const route of routes) {\n const section = route.section || 'Pages';\n if (!sections.has(section)) sections.set(section, []);\n sections.get(section)!.push(route);\n }\n\n // Build llms.txt\n const lines: string[] = [];\n lines.push(`# ${siteName}`);\n lines.push('');\n lines.push(`> ${siteDescription}`);\n lines.push('');\n\n for (const [section, sectionRoutes] of sections) {\n lines.push(`## ${section}`);\n lines.push('');\n for (const route of sectionRoutes) {\n const url = `${baseUrl}${route.path}${markdownExtension}`;\n const desc = route.description ? `: ${route.description}` : '';\n lines.push(`- [${route.title}](${url})${desc}`);\n }\n lines.push('');\n }\n\n const llmsTxt = lines.join('\\n').trim() + '\\n';\n\n // Build llms-full.txt (concatenated content of all routes)\n const fullLines: string[] = [];\n fullLines.push(`# ${siteName}`);\n fullLines.push('');\n fullLines.push(`> ${siteDescription}`);\n fullLines.push('');\n\n if (fullTextContents) {\n for (const route of routes) {\n const content = fullTextContents.get(route.path);\n if (content) {\n fullLines.push(`---`);\n fullLines.push('');\n fullLines.push(`## ${route.title}`);\n fullLines.push(`Source: ${baseUrl}${route.path}`);\n fullLines.push('');\n fullLines.push(content);\n fullLines.push('');\n }\n }\n }\n\n const llmsFullTxt = fullLines.join('\\n').trim() + '\\n';\n\n return {\n llmsTxt,\n llmsFullTxt,\n routeCount: routes.length,\n };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1 @@
1
+ export { A as AIRequestContext, c as AI_BOT_REGISTRY, d as AgentSeoHeaders, e as AgentSeoOptions, B as BotInfo, f as BotPurpose, g as LlmsTxtOptions, h as LlmsTxtResult, L as LlmsTxtRoute, T as TransformOptions, a as TransformResult, i as buildAlternateLinkHeader, j as buildMarkdownHeaders, k as detectAgent, l as estimateTokens, m as generateLlmsTxt, s as shouldServeMarkdown } from './edge-AywqjCEh.cjs';
package/dist/edge.d.ts ADDED
@@ -0,0 +1 @@
1
+ export { A as AIRequestContext, c as AI_BOT_REGISTRY, d as AgentSeoHeaders, e as AgentSeoOptions, B as BotInfo, f as BotPurpose, g as LlmsTxtOptions, h as LlmsTxtResult, L as LlmsTxtRoute, T as TransformOptions, a as TransformResult, i as buildAlternateLinkHeader, j as buildMarkdownHeaders, k as detectAgent, l as estimateTokens, m as generateLlmsTxt, s as shouldServeMarkdown } from './edge-AywqjCEh.js';
package/dist/edge.js ADDED
@@ -0,0 +1,214 @@
1
+ // src/detect.ts
2
+ var AI_BOT_REGISTRY = [
3
+ // === OpenAI ===
4
+ {
5
+ pattern: /GPTBot/i,
6
+ info: { name: "GPTBot", operator: "OpenAI", purpose: "training", rendersJs: false }
7
+ },
8
+ {
9
+ pattern: /OAI-SearchBot/i,
10
+ info: { name: "OAI-SearchBot", operator: "OpenAI", purpose: "search", rendersJs: false }
11
+ },
12
+ {
13
+ pattern: /ChatGPT-User/i,
14
+ info: { name: "ChatGPT-User", operator: "OpenAI", purpose: "agent-browsing", rendersJs: true }
15
+ },
16
+ // === Anthropic ===
17
+ {
18
+ pattern: /ClaudeBot/i,
19
+ info: { name: "ClaudeBot", operator: "Anthropic", purpose: "training", rendersJs: false }
20
+ },
21
+ {
22
+ pattern: /Claude-User/i,
23
+ info: { name: "Claude-User", operator: "Anthropic", purpose: "agent-browsing", rendersJs: true }
24
+ },
25
+ {
26
+ pattern: /Claude-SearchBot/i,
27
+ info: { name: "Claude-SearchBot", operator: "Anthropic", purpose: "search", rendersJs: false }
28
+ },
29
+ {
30
+ pattern: /anthropic-ai/i,
31
+ info: { name: "anthropic-ai", operator: "Anthropic", purpose: "training", rendersJs: false }
32
+ },
33
+ // === Perplexity ===
34
+ {
35
+ pattern: /PerplexityBot/i,
36
+ info: { name: "PerplexityBot", operator: "Perplexity", purpose: "search", rendersJs: false }
37
+ },
38
+ {
39
+ pattern: /Perplexity-User/i,
40
+ info: { name: "Perplexity-User", operator: "Perplexity", purpose: "agent-browsing", rendersJs: true }
41
+ },
42
+ // === Google ===
43
+ {
44
+ pattern: /Google-Extended/i,
45
+ info: { name: "Google-Extended", operator: "Google", purpose: "training", rendersJs: true }
46
+ },
47
+ // === Apple ===
48
+ {
49
+ pattern: /Applebot-Extended/i,
50
+ info: { name: "Applebot-Extended", operator: "Apple", purpose: "training", rendersJs: true }
51
+ },
52
+ // === Meta ===
53
+ {
54
+ pattern: /meta-externalagent/i,
55
+ info: { name: "Meta-ExternalAgent", operator: "Meta", purpose: "training", rendersJs: false }
56
+ },
57
+ {
58
+ pattern: /FacebookBot/i,
59
+ info: { name: "FacebookBot", operator: "Meta", purpose: "search", rendersJs: false }
60
+ },
61
+ // === Common Crawl ===
62
+ {
63
+ pattern: /CCBot/i,
64
+ info: { name: "CCBot", operator: "Common Crawl", purpose: "training", rendersJs: false }
65
+ },
66
+ // === Cohere ===
67
+ {
68
+ pattern: /cohere-ai/i,
69
+ info: { name: "cohere-ai", operator: "Cohere", purpose: "training", rendersJs: false }
70
+ },
71
+ // === Amazon ===
72
+ {
73
+ pattern: /Amazonbot/i,
74
+ info: { name: "Amazonbot", operator: "Amazon", purpose: "search", rendersJs: false }
75
+ },
76
+ // === Bytedance ===
77
+ {
78
+ pattern: /Bytespider/i,
79
+ info: { name: "Bytespider", operator: "ByteDance", purpose: "training", rendersJs: false }
80
+ },
81
+ // === You.com ===
82
+ {
83
+ pattern: /YouBot/i,
84
+ info: { name: "YouBot", operator: "You.com", purpose: "search", rendersJs: false }
85
+ },
86
+ // === DeepSeek ===
87
+ {
88
+ pattern: /Deepseek/i,
89
+ info: { name: "DeepSeekBot", operator: "DeepSeek", purpose: "training", rendersJs: false }
90
+ }
91
+ ];
92
+ var TOKEN_REGISTRY = AI_BOT_REGISTRY.map((entry) => ({
93
+ entry,
94
+ token: regexToToken(entry.pattern)
95
+ }));
96
+ function detectAgent(userAgent, acceptHeader) {
97
+ const wantsMarkdown = acceptHeader ? /text\/markdown/i.test(acceptHeader) : false;
98
+ if (!userAgent) {
99
+ return { isAIBot: false, bot: null, wantsMarkdown };
100
+ }
101
+ const ua = userAgent.toLowerCase();
102
+ for (const { entry, token } of TOKEN_REGISTRY) {
103
+ if (token) {
104
+ if (ua.includes(token)) {
105
+ return { isAIBot: true, bot: entry.info, wantsMarkdown };
106
+ }
107
+ continue;
108
+ }
109
+ if (entry.pattern.test(userAgent)) {
110
+ return { isAIBot: true, bot: entry.info, wantsMarkdown };
111
+ }
112
+ }
113
+ return { isAIBot: false, bot: null, wantsMarkdown };
114
+ }
115
+ function shouldServeMarkdown(userAgent, acceptHeader) {
116
+ const ctx = detectAgent(userAgent, acceptHeader);
117
+ return ctx.isAIBot || ctx.wantsMarkdown;
118
+ }
119
+ function regexToToken(pattern) {
120
+ const source = pattern.source;
121
+ if (/^[A-Za-z0-9-]+$/.test(source)) return source.toLowerCase();
122
+ return null;
123
+ }
124
+
125
+ // src/tokens.ts
126
+ function estimateTokens(text) {
127
+ return Math.ceil(text.length / 4);
128
+ }
129
+
130
+ // src/headers.ts
131
+ function buildMarkdownHeaders(result, options, originalPath) {
132
+ const headers = {
133
+ "Content-Type": "text/markdown; charset=utf-8",
134
+ "Content-Disposition": "inline",
135
+ "Vary": "Accept, User-Agent",
136
+ "X-Markdown-Tokens": String(result.tokenEstimate)
137
+ };
138
+ const signal = options.contentSignal ?? { aiTrain: true, search: true, aiInput: true };
139
+ const signalParts = [];
140
+ if (signal.aiTrain !== false) signalParts.push("ai-train=yes");
141
+ if (signal.search !== false) signalParts.push("search=yes");
142
+ if (signal.aiInput !== false) signalParts.push("ai-input=yes");
143
+ if (signalParts.length > 0) {
144
+ headers["Content-Signal"] = signalParts.join(", ");
145
+ }
146
+ headers["X-Robots-Tag"] = "all";
147
+ return headers;
148
+ }
149
+ function buildAlternateLinkHeader(path, ext = ".md") {
150
+ const mdPath = path.endsWith("/") ? `${path}index${ext}` : `${path}${ext}`;
151
+ return `<${mdPath}>; rel="alternate"; type="text/markdown"`;
152
+ }
153
+
154
+ // src/llms-txt.ts
155
+ function generateLlmsTxt(options, routes, fullTextContents) {
156
+ const { siteName, siteDescription, baseUrl, markdownExtension = ".md" } = options;
157
+ const sections = /* @__PURE__ */ new Map();
158
+ for (const route of routes) {
159
+ const section = route.section || "Pages";
160
+ if (!sections.has(section)) sections.set(section, []);
161
+ sections.get(section).push(route);
162
+ }
163
+ const lines = [];
164
+ lines.push(`# ${siteName}`);
165
+ lines.push("");
166
+ lines.push(`> ${siteDescription}`);
167
+ lines.push("");
168
+ for (const [section, sectionRoutes] of sections) {
169
+ lines.push(`## ${section}`);
170
+ lines.push("");
171
+ for (const route of sectionRoutes) {
172
+ const url = `${baseUrl}${route.path}${markdownExtension}`;
173
+ const desc = route.description ? `: ${route.description}` : "";
174
+ lines.push(`- [${route.title}](${url})${desc}`);
175
+ }
176
+ lines.push("");
177
+ }
178
+ const llmsTxt = lines.join("\n").trim() + "\n";
179
+ const fullLines = [];
180
+ fullLines.push(`# ${siteName}`);
181
+ fullLines.push("");
182
+ fullLines.push(`> ${siteDescription}`);
183
+ fullLines.push("");
184
+ if (fullTextContents) {
185
+ for (const route of routes) {
186
+ const content = fullTextContents.get(route.path);
187
+ if (content) {
188
+ fullLines.push(`---`);
189
+ fullLines.push("");
190
+ fullLines.push(`## ${route.title}`);
191
+ fullLines.push(`Source: ${baseUrl}${route.path}`);
192
+ fullLines.push("");
193
+ fullLines.push(content);
194
+ fullLines.push("");
195
+ }
196
+ }
197
+ }
198
+ const llmsFullTxt = fullLines.join("\n").trim() + "\n";
199
+ return {
200
+ llmsTxt,
201
+ llmsFullTxt,
202
+ routeCount: routes.length
203
+ };
204
+ }
205
+ export {
206
+ AI_BOT_REGISTRY,
207
+ buildAlternateLinkHeader,
208
+ buildMarkdownHeaders,
209
+ detectAgent,
210
+ estimateTokens,
211
+ generateLlmsTxt,
212
+ shouldServeMarkdown
213
+ };
214
+ //# sourceMappingURL=edge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/detect.ts","../src/tokens.ts","../src/headers.ts","../src/llms-txt.ts"],"sourcesContent":["import type { AIRequestContext, BotInfo, BotPurpose } from './types.js';\n\ninterface BotEntry {\n /** Regex pattern to match against User-Agent string */\n pattern: RegExp;\n /** Bot metadata */\n info: BotInfo;\n}\n\nconst AI_BOT_REGISTRY: BotEntry[] = [\n // === OpenAI ===\n {\n pattern: /GPTBot/i,\n info: { name: 'GPTBot', operator: 'OpenAI', purpose: 'training', rendersJs: false },\n },\n {\n pattern: /OAI-SearchBot/i,\n info: { name: 'OAI-SearchBot', operator: 'OpenAI', purpose: 'search', rendersJs: false },\n },\n {\n pattern: /ChatGPT-User/i,\n info: { name: 'ChatGPT-User', operator: 'OpenAI', purpose: 'agent-browsing', rendersJs: true },\n },\n\n // === Anthropic ===\n {\n pattern: /ClaudeBot/i,\n info: { name: 'ClaudeBot', operator: 'Anthropic', purpose: 'training', rendersJs: false },\n },\n {\n pattern: /Claude-User/i,\n info: { name: 'Claude-User', operator: 'Anthropic', purpose: 'agent-browsing', rendersJs: true },\n },\n {\n pattern: /Claude-SearchBot/i,\n info: { name: 'Claude-SearchBot', operator: 'Anthropic', purpose: 'search', rendersJs: false },\n },\n {\n pattern: /anthropic-ai/i,\n info: { name: 'anthropic-ai', operator: 'Anthropic', purpose: 'training', rendersJs: false },\n },\n\n // === Perplexity ===\n {\n pattern: /PerplexityBot/i,\n info: { name: 'PerplexityBot', operator: 'Perplexity', purpose: 'search', rendersJs: false },\n },\n {\n pattern: /Perplexity-User/i,\n info: { name: 'Perplexity-User', operator: 'Perplexity', purpose: 'agent-browsing', rendersJs: true },\n },\n\n // === Google ===\n {\n pattern: /Google-Extended/i,\n info: { name: 'Google-Extended', operator: 'Google', purpose: 'training', rendersJs: true },\n },\n\n // === Apple ===\n {\n pattern: /Applebot-Extended/i,\n info: { name: 'Applebot-Extended', operator: 'Apple', purpose: 'training', rendersJs: true },\n },\n\n // === Meta ===\n {\n pattern: /meta-externalagent/i,\n info: { name: 'Meta-ExternalAgent', operator: 'Meta', purpose: 'training', rendersJs: false },\n },\n {\n pattern: /FacebookBot/i,\n info: { name: 'FacebookBot', operator: 'Meta', purpose: 'search', rendersJs: false },\n },\n\n // === Common Crawl ===\n {\n pattern: /CCBot/i,\n info: { name: 'CCBot', operator: 'Common Crawl', purpose: 'training', rendersJs: false },\n },\n\n // === Cohere ===\n {\n pattern: /cohere-ai/i,\n info: { name: 'cohere-ai', operator: 'Cohere', purpose: 'training', rendersJs: false },\n },\n\n // === Amazon ===\n {\n pattern: /Amazonbot/i,\n info: { name: 'Amazonbot', operator: 'Amazon', purpose: 'search', rendersJs: false },\n },\n\n // === Bytedance ===\n {\n pattern: /Bytespider/i,\n info: { name: 'Bytespider', operator: 'ByteDance', purpose: 'training', rendersJs: false },\n },\n\n // === You.com ===\n {\n pattern: /YouBot/i,\n info: { name: 'YouBot', operator: 'You.com', purpose: 'search', rendersJs: false },\n },\n\n // === DeepSeek ===\n {\n pattern: /Deepseek/i,\n info: { name: 'DeepSeekBot', operator: 'DeepSeek', purpose: 'training', rendersJs: false },\n },\n];\n\nconst TOKEN_REGISTRY = AI_BOT_REGISTRY.map((entry) => ({\n entry,\n token: regexToToken(entry.pattern),\n}));\n\n/**\n * Detect if an incoming request is from an AI bot.\n *\n * @param userAgent - The User-Agent header value\n * @param acceptHeader - The Accept header value (optional)\n * @returns AIRequestContext with bot detection results\n */\nexport function detectAgent(\n userAgent: string | null | undefined,\n acceptHeader?: string | null\n): AIRequestContext {\n const wantsMarkdown = acceptHeader\n ? /text\\/markdown/i.test(acceptHeader)\n : false;\n\n if (!userAgent) {\n return { isAIBot: false, bot: null, wantsMarkdown };\n }\n\n const ua = userAgent.toLowerCase();\n\n // Check against our AI-specific registry first (more precise)\n for (const { entry, token } of TOKEN_REGISTRY) {\n if (token) {\n if (ua.includes(token)) {\n return { isAIBot: true, bot: entry.info, wantsMarkdown };\n }\n continue;\n }\n if (entry.pattern.test(userAgent)) {\n return { isAIBot: true, bot: entry.info, wantsMarkdown };\n }\n }\n\n // Fallback: we do NOT serve markdown to generic bots — only AI bots get the special treatment\n return { isAIBot: false, bot: null, wantsMarkdown };\n}\n\n/**\n * Check if a request should receive Markdown.\n * Returns true if: the request is from a known AI bot OR the Accept header requests text/markdown.\n */\nexport function shouldServeMarkdown(\n userAgent: string | null | undefined,\n acceptHeader?: string | null\n): boolean {\n const ctx = detectAgent(userAgent, acceptHeader);\n return ctx.isAIBot || ctx.wantsMarkdown;\n}\n\nexport { AI_BOT_REGISTRY };\n\nfunction regexToToken(pattern: RegExp): string | null {\n const source = pattern.source;\n if (/^[A-Za-z0-9-]+$/.test(source)) return source.toLowerCase();\n return null;\n}\n","/**\n * Estimate token count using the chars/4 heuristic.\n * This is the same heuristic used by Cloudflare's X-Markdown-Tokens header.\n * Accurate to within ~10% for English text across GPT and Claude tokenizers.\n */\nexport function estimateTokens(text: string): number {\n return Math.ceil(text.length / 4);\n}\n","import type { TransformResult, AgentSeoHeaders, AgentSeoOptions } from './types.js';\n\n/**\n * Build the response headers for a Markdown response.\n */\nexport function buildMarkdownHeaders(\n result: TransformResult,\n options: Pick<AgentSeoOptions, 'contentSignal'>,\n originalPath?: string\n): AgentSeoHeaders {\n const headers: AgentSeoHeaders = {\n 'Content-Type': 'text/markdown; charset=utf-8',\n 'Content-Disposition': 'inline',\n 'Vary': 'Accept, User-Agent',\n 'X-Markdown-Tokens': String(result.tokenEstimate),\n };\n\n // Content-Signal header (Cloudflare convention)\n const signal = options.contentSignal ?? { aiTrain: true, search: true, aiInput: true };\n const signalParts: string[] = [];\n if (signal.aiTrain !== false) signalParts.push('ai-train=yes');\n if (signal.search !== false) signalParts.push('search=yes');\n if (signal.aiInput !== false) signalParts.push('ai-input=yes');\n if (signalParts.length > 0) {\n headers['Content-Signal'] = signalParts.join(', ');\n }\n\n // X-Robots-Tag: let all AI bots index\n headers['X-Robots-Tag'] = 'all';\n\n return headers;\n}\n\n/**\n * Build a Link header pointing to the Markdown alternate.\n * This is injected into ALL HTML responses (not just Markdown ones)\n * so crawlers can discover the alternate representation.\n */\nexport function buildAlternateLinkHeader(path: string, ext: string = '.md'): string {\n // /docs/getting-started → /docs/getting-started.md\n const mdPath = path.endsWith('/') ? `${path}index${ext}` : `${path}${ext}`;\n return `<${mdPath}>; rel=\"alternate\"; type=\"text/markdown\"`;\n}\n","import type { LlmsTxtOptions, LlmsTxtRoute, LlmsTxtResult } from './types.js';\n\n/**\n * Generate llms.txt and llms-full.txt content from route data.\n *\n * llms.txt format (per spec at llmstxt.org):\n * # Site Name\n * > Site description blockquote\n *\n * ## Section Name\n * - [Page Title](url): Description\n */\nexport function generateLlmsTxt(\n options: LlmsTxtOptions,\n routes: LlmsTxtRoute[],\n fullTextContents?: Map<string, string>\n): LlmsTxtResult {\n const { siteName, siteDescription, baseUrl, markdownExtension = '.md' } = options;\n\n // Group routes by section\n const sections = new Map<string, LlmsTxtRoute[]>();\n for (const route of routes) {\n const section = route.section || 'Pages';\n if (!sections.has(section)) sections.set(section, []);\n sections.get(section)!.push(route);\n }\n\n // Build llms.txt\n const lines: string[] = [];\n lines.push(`# ${siteName}`);\n lines.push('');\n lines.push(`> ${siteDescription}`);\n lines.push('');\n\n for (const [section, sectionRoutes] of sections) {\n lines.push(`## ${section}`);\n lines.push('');\n for (const route of sectionRoutes) {\n const url = `${baseUrl}${route.path}${markdownExtension}`;\n const desc = route.description ? `: ${route.description}` : '';\n lines.push(`- [${route.title}](${url})${desc}`);\n }\n lines.push('');\n }\n\n const llmsTxt = lines.join('\\n').trim() + '\\n';\n\n // Build llms-full.txt (concatenated content of all routes)\n const fullLines: string[] = [];\n fullLines.push(`# ${siteName}`);\n fullLines.push('');\n fullLines.push(`> ${siteDescription}`);\n fullLines.push('');\n\n if (fullTextContents) {\n for (const route of routes) {\n const content = fullTextContents.get(route.path);\n if (content) {\n fullLines.push(`---`);\n fullLines.push('');\n fullLines.push(`## ${route.title}`);\n fullLines.push(`Source: ${baseUrl}${route.path}`);\n fullLines.push('');\n fullLines.push(content);\n fullLines.push('');\n }\n }\n }\n\n const llmsFullTxt = fullLines.join('\\n').trim() + '\\n';\n\n return {\n llmsTxt,\n llmsFullTxt,\n routeCount: routes.length,\n };\n}\n"],"mappings":"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","names":[]}