@farming-labs/docs 0.1.35 → 0.1.37

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.
@@ -69,13 +69,13 @@ async function main() {
69
69
  searchApiKey: typeof flags["search-api-key"] === "string" ? flags["search-api-key"] : void 0
70
70
  };
71
71
  if (!parsedCommand.command || parsedCommand.command === "init") {
72
- const { init } = await import("../init-DwUjo2Kv.mjs");
72
+ const { init } = await import("../init-i5ySLGa2.mjs");
73
73
  await init(initOptions);
74
74
  } else if (parsedCommand.command === "mcp") {
75
75
  const { runMcp } = await import("../mcp-DLP94P1H.mjs");
76
76
  await runMcp(mcpOptions);
77
77
  } else if (parsedCommand.command === "search" && subcommand === "sync") {
78
- const { syncSearch } = await import("../search-CLaNOUrd.mjs");
78
+ const { syncSearch } = await import("../search-CNesqs89.mjs");
79
79
  await syncSearch(searchSyncOptions);
80
80
  } else if (parsedCommand.command === "search") {
81
81
  console.error(pc.red(`Unknown search subcommand: ${subcommand ?? "(missing)"}`));
package/dist/index.d.mts CHANGED
@@ -1,5 +1,6 @@
1
- import { $ as SidebarPageNode, A as DocsSearchResult, B as OGConfig, C as DocsSearchAdapterContext, D as DocsSearchDocument, E as DocsSearchConfig, F as FontStyle, G as PageActionsConfig, H as OpenDocsProvider, I as GithubConfig, J as PageTwitter, K as PageFrontmatter, L as LastUpdatedConfig, M as DocsSearchSourcePage, N as DocsTheme, O as DocsSearchEmbeddingsConfig, P as FeedbackConfig, Q as SidebarNode, R as LlmsTxtConfig, S as DocsSearchAdapter, T as DocsSearchChunkingConfig, U as OpenGraphImage, V as OpenDocsConfig, W as OrderingItem, X as SidebarConfig, Y as SidebarComponentProps, Z as SidebarFolderNode, _ as DocsI18nConfig, a as ApiReferenceRenderer, at as UIConfig, b as DocsMetadata, c as ChangelogFrontmatter, d as CustomDocsSearchConfig, et as SidebarTree, f as DocsAgentFeedbackContext, g as DocsFeedbackValue, h as DocsFeedbackData, i as ApiReferenceConfig, it as TypographyConfig, j as DocsSearchResultType, k as DocsSearchQuery, l as CodeBlockCopyData, m as DocsConfig, n as AgentFeedbackConfig, nt as ThemeToggleConfig, o as BreadcrumbConfig, p as DocsAgentFeedbackData, q as PageOpenGraph, r as AlgoliaDocsSearchConfig, rt as TypesenseDocsSearchConfig, s as ChangelogConfig, t as AIConfig, tt as SimpleDocsSearchConfig, u as CopyMarkdownConfig, v as DocsMcpConfig, w as DocsSearchAdapterFactory, x as DocsNav, y as DocsMcpToolsConfig, z as McpDocsSearchConfig } from "./types-CP8OcVgL.mjs";
2
- import { a as createSimpleSearchAdapter, c as resolveSearchRequestConfig, i as createMcpSearchAdapter, n as createAlgoliaSearchAdapter, o as createTypesenseSearchAdapter, r as createCustomSearchAdapter, s as performDocsSearch, t as buildDocsSearchDocuments } from "./search-zClNrbqL.mjs";
1
+ import { $ as SidebarFolderNode, A as DocsSearchQuery, B as McpDocsSearchConfig, C as DocsSearchAdapter, D as DocsSearchConfig, E as DocsSearchChunkingConfig, F as FeedbackConfig, G as OrderingItem, H as OpenDocsConfig, I as FontStyle, J as PageOpenGraph, K as PageActionsConfig, L as GithubConfig, M as DocsSearchResultType, N as DocsSearchSourcePage, O as DocsSearchDocument, P as DocsTheme, Q as SidebarConfig, R as LastUpdatedConfig, S as DocsRelatedItem, T as DocsSearchAdapterFactory, U as OpenDocsProvider, V as OGConfig, W as OpenGraphImage, X as ResolvedDocsRelatedLink, Y as PageTwitter, Z as SidebarComponentProps, _ as DocsI18nConfig, a as ApiReferenceRenderer, at as TypesenseDocsSearchConfig, b as DocsMetadata, c as ChangelogFrontmatter, d as CustomDocsSearchConfig, et as SidebarNode, f as DocsAgentFeedbackContext, g as DocsFeedbackValue, h as DocsFeedbackData, i as ApiReferenceConfig, it as ThemeToggleConfig, j as DocsSearchResult, k as DocsSearchEmbeddingsConfig, l as CodeBlockCopyData, m as DocsConfig, n as AgentFeedbackConfig, nt as SidebarTree, o as BreadcrumbConfig, ot as TypographyConfig, p as DocsAgentFeedbackData, q as PageFrontmatter, r as AlgoliaDocsSearchConfig, rt as SimpleDocsSearchConfig, s as ChangelogConfig, st as UIConfig, t as AIConfig, tt as SidebarPageNode, u as CopyMarkdownConfig, v as DocsMcpConfig, w as DocsSearchAdapterContext, x as DocsNav, y as DocsMcpToolsConfig, z as LlmsTxtConfig } from "./types-CP2NmW5c.mjs";
2
+ import { DocsMcpPage, DocsMcpResolvedConfig } from "./mcp.mjs";
3
+ import { a as createSimpleSearchAdapter, c as resolveSearchRequestConfig, i as createMcpSearchAdapter, n as createAlgoliaSearchAdapter, o as createTypesenseSearchAdapter, r as createCustomSearchAdapter, s as performDocsSearch, t as buildDocsSearchDocuments } from "./search-BWqFW9rt.mjs";
3
4
 
4
5
  //#region src/define-docs.d.ts
5
6
  /**
@@ -115,4 +116,183 @@ declare function buildPageOpenGraph(page: Pick<PageFrontmatter, "title" | "descr
115
116
  */
116
117
  declare function buildPageTwitter(page: Pick<PageFrontmatter, "title" | "description" | "ogImage" | "openGraph" | "twitter">, ogConfig?: OGConfig, baseUrl?: string): PageTwitter | undefined;
117
118
  //#endregion
118
- export { type AIConfig, type AgentFeedbackConfig, type AlgoliaDocsSearchConfig, type ApiReferenceConfig, type ApiReferenceRenderer, type BreadcrumbConfig, type ChangelogConfig, type ChangelogEntrySummary, type ChangelogFrontmatter, type CodeBlockCopyData, type CopyMarkdownConfig, type CustomDocsSearchConfig, type DocsAgentFeedbackContext, type DocsAgentFeedbackData, type DocsConfig, type DocsFeedbackData, type DocsFeedbackValue, type DocsI18nConfig, type DocsMcpConfig, type DocsMcpToolsConfig, type DocsMetadata, type DocsNav, type DocsPathMatch, type DocsSearchAdapter, type DocsSearchAdapterContext, type DocsSearchAdapterFactory, type DocsSearchChunkingConfig, type DocsSearchConfig, type DocsSearchDocument, type DocsSearchEmbeddingsConfig, type DocsSearchQuery, type DocsSearchResult, type DocsSearchResultType, type DocsSearchSourcePage, type DocsTheme, type FeedbackConfig, type FontStyle, type GithubConfig, type LastUpdatedConfig, type LlmsTxtConfig, type McpDocsSearchConfig, type OGConfig, type OpenDocsConfig, type OpenDocsProvider, type OpenGraphImage, type OrderingItem, type PageActionsConfig, type PageFrontmatter, type PageOpenGraph, type PageTwitter, type ResolvedChangelogConfig, type ResolvedDocsI18n, type SidebarComponentProps, type SidebarConfig, type SidebarFolderNode, type SidebarNode, type SidebarPageNode, type SidebarTree, type SimpleDocsSearchConfig, type ThemeToggleConfig, type TypesenseDocsSearchConfig, type TypographyConfig, type UIConfig, buildDocsSearchDocuments, buildPageOpenGraph, buildPageTwitter, createAlgoliaSearchAdapter, createCustomSearchAdapter, createMcpSearchAdapter, createSimpleSearchAdapter, createTheme, createTypesenseSearchAdapter, deepMerge, defineDocs, extendTheme, performDocsSearch, resolveChangelogConfig, resolveDocsI18n, resolveDocsLocale, resolveDocsPath, resolveOGImage, resolveSearchRequestConfig, resolveTitle };
119
+ //#region src/related.d.ts
120
+ declare function normalizeDocsRelated(value: unknown): ResolvedDocsRelatedLink[];
121
+ declare function renderDocsRelatedMarkdownLines(related: ResolvedDocsRelatedLink[] | undefined): string[];
122
+ //#endregion
123
+ //#region src/agent.d.ts
124
+ declare const DEFAULT_DOCS_API_ROUTE = "/api/docs";
125
+ declare const DEFAULT_AGENT_SPEC_ROUTE = "/api/docs/agent/spec";
126
+ declare const DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE = "/.well-known/agent";
127
+ declare const DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE = "/.well-known/agent.json";
128
+ declare const DEFAULT_MCP_ROUTE = "/api/docs/mcp";
129
+ declare const DEFAULT_MCP_PUBLIC_ROUTE = "/mcp";
130
+ declare const DEFAULT_MCP_WELL_KNOWN_ROUTE = "/.well-known/mcp";
131
+ declare const DEFAULT_LLMS_TXT_ROUTE = "/llms.txt";
132
+ declare const DEFAULT_LLMS_FULL_TXT_ROUTE = "/llms-full.txt";
133
+ declare const DEFAULT_LLMS_TXT_WELL_KNOWN_ROUTE = "/.well-known/llms.txt";
134
+ declare const DEFAULT_LLMS_FULL_TXT_WELL_KNOWN_ROUTE = "/.well-known/llms-full.txt";
135
+ declare const DEFAULT_AGENT_FEEDBACK_ROUTE = "/api/docs/agent/feedback";
136
+ interface DocsAgentFeedbackDiscoveryConfig {
137
+ enabled?: boolean;
138
+ route?: string;
139
+ schemaRoute?: string;
140
+ }
141
+ interface DocsLlmsDiscoveryConfig {
142
+ enabled?: boolean;
143
+ baseUrl?: string;
144
+ siteTitle?: string;
145
+ siteDescription?: string;
146
+ }
147
+ interface DocsAgentDiscoverySpecOptions {
148
+ origin: string;
149
+ entry?: string;
150
+ i18n?: ResolvedDocsI18n | null;
151
+ search?: boolean | DocsSearchConfig;
152
+ mcp: DocsMcpResolvedConfig;
153
+ feedback?: DocsAgentFeedbackDiscoveryConfig;
154
+ llms?: DocsLlmsDiscoveryConfig;
155
+ markdown?: {
156
+ acceptHeader?: boolean;
157
+ };
158
+ }
159
+ interface DocsMarkdownPage {
160
+ slug?: string;
161
+ url: string;
162
+ title: string;
163
+ description?: string;
164
+ related?: ResolvedDocsRelatedLink[];
165
+ content: string;
166
+ rawContent?: string;
167
+ agentContent?: string;
168
+ agentRawContent?: string;
169
+ agentFallbackContent?: string;
170
+ agentFallbackRawContent?: string;
171
+ }
172
+ declare function normalizeDocsPathSegment(value: string): string;
173
+ declare function normalizeDocsUrlPath(value: string): string;
174
+ declare function isDocsAgentDiscoveryRequest(url: URL): boolean;
175
+ declare function isDocsMcpRequest(url: URL): boolean;
176
+ declare function isDocsPublicGetRequest(entry: string, url: URL, request: Request): boolean;
177
+ declare function resolveDocsLlmsTxtFormat(url: URL): "llms" | "llms-full" | null;
178
+ declare function resolveDocsMarkdownRequest(entry: string, url: URL, request: Request): {
179
+ requestedPath: string;
180
+ } | null;
181
+ declare function findDocsMarkdownPage<T extends DocsMarkdownPage>(entry: string, pages: T[], requestedPath: string): T | null;
182
+ declare function renderDocsMarkdownDocument(page: DocsMcpPage | DocsSearchSourcePage): string;
183
+ declare function renderDocsMarkdownDocument(page: DocsMarkdownPage): string;
184
+ declare function resolveDocsAgentMdxContent(content: string, audience: "human" | "agent"): string;
185
+ declare function buildDocsAgentDiscoverySpec({
186
+ origin,
187
+ entry,
188
+ i18n,
189
+ search,
190
+ mcp,
191
+ feedback,
192
+ llms,
193
+ markdown
194
+ }: DocsAgentDiscoverySpecOptions): {
195
+ version: string;
196
+ name: string;
197
+ baseUrl: string;
198
+ site: {
199
+ title: string;
200
+ description: string | undefined;
201
+ entry: string;
202
+ baseUrl: string;
203
+ };
204
+ locales: {
205
+ enabled: boolean;
206
+ available: string[];
207
+ default: string | null;
208
+ queryParam: string;
209
+ fallbackQueryParam: string;
210
+ };
211
+ capabilities: {
212
+ markdownRoutes: boolean;
213
+ agentMdOverrides: boolean;
214
+ agentBlocks: boolean;
215
+ llms: boolean;
216
+ skills: boolean;
217
+ mcp: boolean;
218
+ search: boolean;
219
+ agentFeedback: boolean;
220
+ locales: boolean;
221
+ };
222
+ api: {
223
+ docs: string;
224
+ agentSpec: string;
225
+ agentSpecDefault: string;
226
+ agentSpecFallback: string;
227
+ agentSpecWellKnown: string;
228
+ agentSpecWellKnownJson: string;
229
+ agentSpecQuery: string;
230
+ };
231
+ markdown: {
232
+ enabled: boolean;
233
+ acceptHeader: string | null;
234
+ pagePattern: string;
235
+ rootPage: string;
236
+ apiPattern: string;
237
+ resolutionOrder: string[];
238
+ };
239
+ llms: {
240
+ enabled: boolean;
241
+ defaultTxt: string;
242
+ defaultFull: string;
243
+ txt: string;
244
+ full: string;
245
+ publicTxt: string;
246
+ publicFull: string;
247
+ wellKnownTxt: string;
248
+ wellKnownFull: string;
249
+ };
250
+ search: {
251
+ enabled: boolean;
252
+ endpoint: string;
253
+ method: string;
254
+ queryParam: string;
255
+ localeParam: string;
256
+ };
257
+ skills: {
258
+ enabled: boolean;
259
+ registry: string;
260
+ install: string;
261
+ recommended: {
262
+ name: string;
263
+ description: string;
264
+ }[];
265
+ };
266
+ mcp: {
267
+ enabled: boolean;
268
+ endpoint: string;
269
+ defaultEndpoint: string;
270
+ publicEndpoint: string;
271
+ wellKnownEndpoint: string;
272
+ publicEndpoints: string[];
273
+ canonicalEndpoint: string;
274
+ name: string;
275
+ version: string;
276
+ tools: {
277
+ listPages: boolean;
278
+ readPage: boolean;
279
+ searchDocs: boolean;
280
+ getNavigation: boolean;
281
+ };
282
+ };
283
+ feedback: {
284
+ enabled: boolean;
285
+ schema: string;
286
+ submit: string;
287
+ schemaQuery: string;
288
+ submitQuery: string;
289
+ };
290
+ instructions: {
291
+ preferMarkdownRoutes: boolean;
292
+ useMcpWhenAvailable: boolean;
293
+ readFeedbackSchemaBeforeSubmitting: boolean;
294
+ doNotAssumeFeedbackPayloadShape: boolean;
295
+ };
296
+ };
297
+ //#endregion
298
+ export { type AIConfig, type AgentFeedbackConfig, type AlgoliaDocsSearchConfig, type ApiReferenceConfig, type ApiReferenceRenderer, type BreadcrumbConfig, type ChangelogConfig, type ChangelogEntrySummary, type ChangelogFrontmatter, type CodeBlockCopyData, type CopyMarkdownConfig, type CustomDocsSearchConfig, DEFAULT_AGENT_FEEDBACK_ROUTE, DEFAULT_AGENT_SPEC_ROUTE, DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE, DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE, DEFAULT_DOCS_API_ROUTE, DEFAULT_LLMS_FULL_TXT_ROUTE, DEFAULT_LLMS_FULL_TXT_WELL_KNOWN_ROUTE, DEFAULT_LLMS_TXT_ROUTE, DEFAULT_LLMS_TXT_WELL_KNOWN_ROUTE, DEFAULT_MCP_PUBLIC_ROUTE, DEFAULT_MCP_ROUTE, DEFAULT_MCP_WELL_KNOWN_ROUTE, type DocsAgentDiscoverySpecOptions, type DocsAgentFeedbackContext, type DocsAgentFeedbackData, type DocsAgentFeedbackDiscoveryConfig, type DocsConfig, type DocsFeedbackData, type DocsFeedbackValue, type DocsI18nConfig, type DocsLlmsDiscoveryConfig, type DocsMarkdownPage, type DocsMcpConfig, type DocsMcpToolsConfig, type DocsMetadata, type DocsNav, type DocsPathMatch, type DocsRelatedItem, type DocsSearchAdapter, type DocsSearchAdapterContext, type DocsSearchAdapterFactory, type DocsSearchChunkingConfig, type DocsSearchConfig, type DocsSearchDocument, type DocsSearchEmbeddingsConfig, type DocsSearchQuery, type DocsSearchResult, type DocsSearchResultType, type DocsSearchSourcePage, type DocsTheme, type FeedbackConfig, type FontStyle, type GithubConfig, type LastUpdatedConfig, type LlmsTxtConfig, type McpDocsSearchConfig, type OGConfig, type OpenDocsConfig, type OpenDocsProvider, type OpenGraphImage, type OrderingItem, type PageActionsConfig, type PageFrontmatter, type PageOpenGraph, type PageTwitter, type ResolvedChangelogConfig, type ResolvedDocsI18n, type ResolvedDocsRelatedLink, type SidebarComponentProps, type SidebarConfig, type SidebarFolderNode, type SidebarNode, type SidebarPageNode, type SidebarTree, type SimpleDocsSearchConfig, type ThemeToggleConfig, type TypesenseDocsSearchConfig, type TypographyConfig, type UIConfig, buildDocsAgentDiscoverySpec, buildDocsSearchDocuments, buildPageOpenGraph, buildPageTwitter, createAlgoliaSearchAdapter, createCustomSearchAdapter, createMcpSearchAdapter, createSimpleSearchAdapter, createTheme, createTypesenseSearchAdapter, deepMerge, defineDocs, extendTheme, findDocsMarkdownPage, isDocsAgentDiscoveryRequest, isDocsMcpRequest, isDocsPublicGetRequest, normalizeDocsPathSegment, normalizeDocsRelated, normalizeDocsUrlPath, performDocsSearch, renderDocsMarkdownDocument, renderDocsRelatedMarkdownLines, resolveChangelogConfig, resolveDocsAgentMdxContent, resolveDocsI18n, resolveDocsLlmsTxtFormat, resolveDocsLocale, resolveDocsMarkdownRequest, resolveDocsPath, resolveOGImage, resolveSearchRequestConfig, resolveTitle };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as createSimpleSearchAdapter, c as resolveSearchRequestConfig, i as createMcpSearchAdapter, n as createAlgoliaSearchAdapter, o as createTypesenseSearchAdapter, r as createCustomSearchAdapter, s as performDocsSearch, t as buildDocsSearchDocuments } from "./search-BS6C5N1i.mjs";
1
+ import { a as createSimpleSearchAdapter, c as resolveSearchRequestConfig, i as createMcpSearchAdapter, l as normalizeDocsRelated, n as createAlgoliaSearchAdapter, o as createTypesenseSearchAdapter, r as createCustomSearchAdapter, s as performDocsSearch, t as buildDocsSearchDocuments, u as renderDocsRelatedMarkdownLines } from "./search-CfnJVeh-.mjs";
2
2
 
3
3
  //#region src/define-docs.ts
4
4
  /**
@@ -266,4 +266,262 @@ function buildPageTwitter(page, ogConfig, baseUrl) {
266
266
  }
267
267
 
268
268
  //#endregion
269
- export { buildDocsSearchDocuments, buildPageOpenGraph, buildPageTwitter, createAlgoliaSearchAdapter, createCustomSearchAdapter, createMcpSearchAdapter, createSimpleSearchAdapter, createTheme, createTypesenseSearchAdapter, deepMerge, defineDocs, extendTheme, performDocsSearch, resolveChangelogConfig, resolveDocsI18n, resolveDocsLocale, resolveDocsPath, resolveOGImage, resolveSearchRequestConfig, resolveTitle };
269
+ //#region src/agent.ts
270
+ const DEFAULT_DOCS_API_ROUTE = "/api/docs";
271
+ const DEFAULT_AGENT_SPEC_ROUTE = "/api/docs/agent/spec";
272
+ const DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE = "/.well-known/agent";
273
+ const DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE = "/.well-known/agent.json";
274
+ const DEFAULT_MCP_ROUTE = "/api/docs/mcp";
275
+ const DEFAULT_MCP_PUBLIC_ROUTE = "/mcp";
276
+ const DEFAULT_MCP_WELL_KNOWN_ROUTE = "/.well-known/mcp";
277
+ const DEFAULT_LLMS_TXT_ROUTE = "/llms.txt";
278
+ const DEFAULT_LLMS_FULL_TXT_ROUTE = "/llms-full.txt";
279
+ const DEFAULT_LLMS_TXT_WELL_KNOWN_ROUTE = "/.well-known/llms.txt";
280
+ const DEFAULT_LLMS_FULL_TXT_WELL_KNOWN_ROUTE = "/.well-known/llms-full.txt";
281
+ const DEFAULT_AGENT_FEEDBACK_ROUTE = "/api/docs/agent/feedback";
282
+ function normalizeDocsPathSegment(value) {
283
+ return value.replace(/^\/+|\/+$/g, "");
284
+ }
285
+ function normalizeDocsUrlPath(value) {
286
+ const normalized = value.replace(/\/+/g, "/");
287
+ if (normalized === "/") return normalized;
288
+ return normalized.replace(/\/+$/, "");
289
+ }
290
+ function isDocsAgentDiscoveryRequest(url) {
291
+ const pathname = normalizeDocsUrlPath(url.pathname);
292
+ if (pathname === DEFAULT_DOCS_API_ROUTE && url.searchParams.get("agent")?.trim() === "spec") return true;
293
+ return pathname === DEFAULT_AGENT_SPEC_ROUTE || pathname === DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE || pathname === DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE;
294
+ }
295
+ function isDocsMcpRequest(url) {
296
+ const pathname = normalizeDocsUrlPath(url.pathname);
297
+ return pathname === DEFAULT_MCP_ROUTE || pathname === DEFAULT_MCP_PUBLIC_ROUTE || pathname === DEFAULT_MCP_WELL_KNOWN_ROUTE;
298
+ }
299
+ function isDocsPublicGetRequest(entry, url, request) {
300
+ const pathname = normalizeDocsUrlPath(url.pathname);
301
+ if (pathname === DEFAULT_DOCS_API_ROUTE || pathname === DEFAULT_MCP_ROUTE) return false;
302
+ return isDocsAgentDiscoveryRequest(url) || resolveDocsLlmsTxtFormat(url) !== null || resolveDocsMarkdownRequest(entry, url, request) !== null;
303
+ }
304
+ function resolveDocsLlmsTxtFormat(url) {
305
+ const pathname = normalizeDocsUrlPath(url.pathname);
306
+ if (pathname === DEFAULT_LLMS_TXT_ROUTE || pathname === DEFAULT_LLMS_TXT_WELL_KNOWN_ROUTE) return "llms";
307
+ if (pathname === DEFAULT_LLMS_FULL_TXT_ROUTE || pathname === DEFAULT_LLMS_FULL_TXT_WELL_KNOWN_ROUTE) return "llms-full";
308
+ const format = url.searchParams.get("format");
309
+ return pathname === DEFAULT_DOCS_API_ROUTE && (format === "llms" || format === "llms-full") ? format : null;
310
+ }
311
+ function resolveDocsMarkdownRequest(entry, url, request) {
312
+ const pathname = normalizeDocsUrlPath(url.pathname);
313
+ const format = url.searchParams.get("format")?.trim();
314
+ if (pathname === DEFAULT_DOCS_API_ROUTE && format === "markdown") return { requestedPath: url.searchParams.get("path")?.trim() ?? "" };
315
+ const normalizedEntry = `/${normalizeDocsPathSegment(entry) || "docs"}`;
316
+ if (pathname === `${normalizedEntry}.md`) return { requestedPath: "" };
317
+ const slugPrefix = `${normalizedEntry}/`;
318
+ if (pathname.startsWith(slugPrefix) && pathname.endsWith(".md")) return { requestedPath: pathname.slice(slugPrefix.length, -3) };
319
+ if (acceptsMarkdown(request)) {
320
+ if (pathname === normalizedEntry) return { requestedPath: "" };
321
+ if (pathname.startsWith(slugPrefix)) return { requestedPath: pathname.slice(slugPrefix.length) };
322
+ }
323
+ return null;
324
+ }
325
+ function findDocsMarkdownPage(entry, pages, requestedPath) {
326
+ const normalizedRequest = normalizeRequestedMarkdownPath(entry, requestedPath);
327
+ for (const page of pages) if (normalizeDocsUrlPath(page.url) === normalizedRequest) return page;
328
+ const normalizedSlug = normalizeDocsPathSegment(requestedPath.replace(/^\//, "").replace(/\.md$/i, ""));
329
+ for (const page of pages) if (page.slug !== void 0 && normalizeDocsPathSegment(page.slug) === normalizedSlug) return page;
330
+ return null;
331
+ }
332
+ function renderDocsMarkdownDocument(page) {
333
+ if (page.agentRawContent !== void 0) return page.agentRawContent;
334
+ const relatedLines = renderDocsRelatedMarkdownLines(page.related);
335
+ const lines = [`# ${page.title}`, `URL: ${page.url}`];
336
+ if (page.description) lines.push(`Description: ${page.description}`);
337
+ lines.push(...relatedLines);
338
+ lines.push("", page.agentFallbackRawContent ?? page.rawContent ?? page.content);
339
+ return lines.join("\n");
340
+ }
341
+ function resolveDocsAgentMdxContent(content, audience) {
342
+ const lines = content.split("\n");
343
+ const output = [];
344
+ let fenceMarker = null;
345
+ let agentDepth = 0;
346
+ for (const line of lines) {
347
+ const trimmed = line.trim();
348
+ const fenceMatch = trimmed.match(/^(`{3,}|~{3,})/);
349
+ if (fenceMatch) {
350
+ if (!fenceMarker) fenceMarker = fenceMatch[1];
351
+ else if (trimmed.startsWith(fenceMarker)) fenceMarker = null;
352
+ if (audience === "agent" || agentDepth === 0) output.push(line);
353
+ continue;
354
+ }
355
+ if (!fenceMarker) {
356
+ if (/^<Agent(?:\s[^>]*)?\/>$/.test(trimmed)) continue;
357
+ const singleLineMatch = line.match(/^(\s*)<Agent(?:\s[^>]*)?>([\s\S]*?)<\/Agent>\s*$/);
358
+ if (singleLineMatch) {
359
+ if (audience === "agent" && singleLineMatch[2]) output.push(`${singleLineMatch[1]}${singleLineMatch[2]}`);
360
+ continue;
361
+ }
362
+ if (line.match(/^(\s*)<Agent(?:\s[^>]*)?>\s*$/)) {
363
+ agentDepth += 1;
364
+ continue;
365
+ }
366
+ const openWithContentMatch = line.match(/^(\s*)<Agent(?:\s[^>]*)?>(.*)$/);
367
+ if (openWithContentMatch) {
368
+ agentDepth += 1;
369
+ if (audience === "agent" && openWithContentMatch[2]) output.push(`${openWithContentMatch[1]}${openWithContentMatch[2]}`);
370
+ continue;
371
+ }
372
+ const closeWithContentMatch = line.match(/^(.*)<\/Agent>\s*$/);
373
+ if (closeWithContentMatch && agentDepth > 0) {
374
+ if (audience === "agent" && closeWithContentMatch[1].trim()) output.push(closeWithContentMatch[1]);
375
+ agentDepth = Math.max(0, agentDepth - 1);
376
+ continue;
377
+ }
378
+ if (/^<\/Agent>\s*$/.test(trimmed) && agentDepth > 0) {
379
+ agentDepth = Math.max(0, agentDepth - 1);
380
+ continue;
381
+ }
382
+ }
383
+ if (agentDepth > 0 && audience === "human") continue;
384
+ output.push(line);
385
+ }
386
+ return output.join("\n").replace(/\n{3,}/g, "\n\n").trim();
387
+ }
388
+ function buildDocsAgentDiscoverySpec({ origin, entry = "docs", i18n = null, search, mcp, feedback, llms, markdown }) {
389
+ const normalizedEntry = normalizeDocsPathSegment(entry) || "docs";
390
+ const localesEnabled = i18n !== null;
391
+ const searchEnabled = isSearchEnabled(search);
392
+ const feedbackRoute = feedback?.route ?? DEFAULT_AGENT_FEEDBACK_ROUTE;
393
+ const feedbackSchemaRoute = feedback?.schemaRoute ?? `${feedbackRoute}/schema`;
394
+ const llmsEnabled = llms?.enabled ?? true;
395
+ return {
396
+ version: "1",
397
+ name: "@farming-labs/docs",
398
+ baseUrl: origin,
399
+ site: {
400
+ title: llms?.siteTitle ?? "Documentation",
401
+ description: llms?.siteDescription,
402
+ entry: normalizedEntry,
403
+ baseUrl: llms?.baseUrl ?? origin
404
+ },
405
+ locales: {
406
+ enabled: localesEnabled,
407
+ available: i18n?.locales ?? [],
408
+ default: i18n?.defaultLocale ?? null,
409
+ queryParam: "lang",
410
+ fallbackQueryParam: "locale"
411
+ },
412
+ capabilities: {
413
+ markdownRoutes: true,
414
+ agentMdOverrides: true,
415
+ agentBlocks: true,
416
+ llms: llmsEnabled,
417
+ skills: true,
418
+ mcp: mcp.enabled,
419
+ search: searchEnabled,
420
+ agentFeedback: feedback?.enabled ?? false,
421
+ locales: localesEnabled
422
+ },
423
+ api: {
424
+ docs: DEFAULT_DOCS_API_ROUTE,
425
+ agentSpec: DEFAULT_AGENT_SPEC_ROUTE,
426
+ agentSpecDefault: DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE,
427
+ agentSpecFallback: DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE,
428
+ agentSpecWellKnown: DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE,
429
+ agentSpecWellKnownJson: DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE,
430
+ agentSpecQuery: `${DEFAULT_DOCS_API_ROUTE}?agent=spec`
431
+ },
432
+ markdown: {
433
+ enabled: true,
434
+ acceptHeader: markdown?.acceptHeader === false ? null : "text/markdown",
435
+ pagePattern: `/${normalizedEntry}/{slug}.md`,
436
+ rootPage: `/${normalizedEntry}.md`,
437
+ apiPattern: `${DEFAULT_DOCS_API_ROUTE}?format=markdown&path={slug}`,
438
+ resolutionOrder: [
439
+ "agent.md",
440
+ "Agent blocks",
441
+ "page markdown"
442
+ ]
443
+ },
444
+ llms: {
445
+ enabled: llmsEnabled,
446
+ defaultTxt: DEFAULT_LLMS_TXT_ROUTE,
447
+ defaultFull: DEFAULT_LLMS_FULL_TXT_ROUTE,
448
+ txt: `${DEFAULT_DOCS_API_ROUTE}?format=llms`,
449
+ full: `${DEFAULT_DOCS_API_ROUTE}?format=llms-full`,
450
+ publicTxt: DEFAULT_LLMS_TXT_ROUTE,
451
+ publicFull: DEFAULT_LLMS_FULL_TXT_ROUTE,
452
+ wellKnownTxt: DEFAULT_LLMS_TXT_WELL_KNOWN_ROUTE,
453
+ wellKnownFull: DEFAULT_LLMS_FULL_TXT_WELL_KNOWN_ROUTE
454
+ },
455
+ search: {
456
+ enabled: searchEnabled,
457
+ endpoint: `${DEFAULT_DOCS_API_ROUTE}?query={query}`,
458
+ method: "GET",
459
+ queryParam: "query",
460
+ localeParam: "lang"
461
+ },
462
+ skills: {
463
+ enabled: true,
464
+ registry: "skills.sh",
465
+ install: "npx skills add farming-labs/docs",
466
+ recommended: [{
467
+ name: "getting-started",
468
+ description: "Use for installation, init, framework setup, theme CSS, and first docs.config wiring."
469
+ }]
470
+ },
471
+ mcp: {
472
+ enabled: mcp.enabled,
473
+ endpoint: mcp.route,
474
+ defaultEndpoint: DEFAULT_MCP_PUBLIC_ROUTE,
475
+ publicEndpoint: DEFAULT_MCP_PUBLIC_ROUTE,
476
+ wellKnownEndpoint: DEFAULT_MCP_WELL_KNOWN_ROUTE,
477
+ publicEndpoints: [DEFAULT_MCP_PUBLIC_ROUTE, DEFAULT_MCP_WELL_KNOWN_ROUTE],
478
+ canonicalEndpoint: DEFAULT_MCP_ROUTE,
479
+ name: mcp.name,
480
+ version: mcp.version,
481
+ tools: mcp.tools
482
+ },
483
+ feedback: {
484
+ enabled: feedback?.enabled ?? false,
485
+ schema: feedbackSchemaRoute,
486
+ submit: feedbackRoute,
487
+ schemaQuery: `${DEFAULT_DOCS_API_ROUTE}?feedback=agent&schema=1`,
488
+ submitQuery: `${DEFAULT_DOCS_API_ROUTE}?feedback=agent`
489
+ },
490
+ instructions: {
491
+ preferMarkdownRoutes: true,
492
+ useMcpWhenAvailable: true,
493
+ readFeedbackSchemaBeforeSubmitting: true,
494
+ doNotAssumeFeedbackPayloadShape: true
495
+ }
496
+ };
497
+ }
498
+ function acceptsMarkdown(request) {
499
+ const accept = request.headers.get("accept");
500
+ if (!accept) return false;
501
+ return accept.split(",").map((value) => value.trim()).some((value) => {
502
+ const [mediaType, ...params] = value.split(";").map((part) => part.trim().toLowerCase());
503
+ if (mediaType !== "text/markdown") return false;
504
+ const qualityParam = params.find((param) => param.split("=", 1)[0]?.trim() === "q");
505
+ if (!qualityParam) return true;
506
+ const qualityValue = qualityParam.slice(qualityParam.indexOf("=") + 1).trim();
507
+ const quality = Number.parseFloat(qualityValue);
508
+ return Number.isFinite(quality) ? quality > 0 : true;
509
+ });
510
+ }
511
+ function normalizeRequestedMarkdownPath(entry, requestedPath) {
512
+ const normalizedEntry = `/${normalizeDocsPathSegment(entry) || "docs"}`;
513
+ const trimmed = requestedPath.trim().replace(/\.md$/i, "");
514
+ if (!trimmed) return normalizedEntry;
515
+ const normalized = normalizeDocsUrlPath(trimmed.startsWith("/") ? trimmed : `/${trimmed}`);
516
+ if (normalized === normalizedEntry || normalized.startsWith(`${normalizedEntry}/`)) return normalized;
517
+ const slug = normalizeDocsPathSegment(trimmed);
518
+ return slug ? normalizeDocsUrlPath(`${normalizedEntry}/${slug}`) : normalizedEntry;
519
+ }
520
+ function isSearchEnabled(search) {
521
+ if (search === false) return false;
522
+ if (search && typeof search === "object" && search.enabled === false) return false;
523
+ return true;
524
+ }
525
+
526
+ //#endregion
527
+ export { DEFAULT_AGENT_FEEDBACK_ROUTE, DEFAULT_AGENT_SPEC_ROUTE, DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE, DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE, DEFAULT_DOCS_API_ROUTE, DEFAULT_LLMS_FULL_TXT_ROUTE, DEFAULT_LLMS_FULL_TXT_WELL_KNOWN_ROUTE, DEFAULT_LLMS_TXT_ROUTE, DEFAULT_LLMS_TXT_WELL_KNOWN_ROUTE, DEFAULT_MCP_PUBLIC_ROUTE, DEFAULT_MCP_ROUTE, DEFAULT_MCP_WELL_KNOWN_ROUTE, buildDocsAgentDiscoverySpec, buildDocsSearchDocuments, buildPageOpenGraph, buildPageTwitter, createAlgoliaSearchAdapter, createCustomSearchAdapter, createMcpSearchAdapter, createSimpleSearchAdapter, createTheme, createTypesenseSearchAdapter, deepMerge, defineDocs, extendTheme, findDocsMarkdownPage, isDocsAgentDiscoveryRequest, isDocsMcpRequest, isDocsPublicGetRequest, normalizeDocsPathSegment, normalizeDocsRelated, normalizeDocsUrlPath, performDocsSearch, renderDocsMarkdownDocument, renderDocsRelatedMarkdownLines, resolveChangelogConfig, resolveDocsAgentMdxContent, resolveDocsI18n, resolveDocsLlmsTxtFormat, resolveDocsLocale, resolveDocsMarkdownRequest, resolveDocsPath, resolveOGImage, resolveSearchRequestConfig, resolveTitle };
@@ -247,6 +247,14 @@ function sveltePageConfigImport(useAlias) {
247
247
  function svelteLayoutServerImport(useAlias) {
248
248
  return useAlias ? "$lib/docs.server" : "../../lib/docs.server";
249
249
  }
250
+ function svelteRouteServerImport(filePath, useAlias) {
251
+ if (useAlias) return "$lib/docs.server";
252
+ return relativeImport(filePath, "src/lib/docs.server.ts");
253
+ }
254
+ function svelteRouteConfigImport(filePath, useAlias) {
255
+ if (useAlias) return "$lib/docs.config";
256
+ return relativeImport(filePath, "src/lib/docs.config.ts");
257
+ }
250
258
  function astroServerConfigImport(useAlias) {
251
259
  return useAlias ? "@/lib/docs.config" : "./docs.config";
252
260
  }
@@ -258,6 +266,14 @@ function astroPageServerImport(useAlias, depth) {
258
266
  if (useAlias) return "@/lib/docs.server";
259
267
  return `${"../".repeat(depth)}lib/docs.server`;
260
268
  }
269
+ function astroRouteServerImport(filePath, useAlias) {
270
+ if (useAlias) return "@/lib/docs.server";
271
+ return relativeImport(filePath, "src/lib/docs.server.ts");
272
+ }
273
+ function astroRouteConfigImport(filePath, useAlias) {
274
+ if (useAlias) return "@/lib/docs.config";
275
+ return relativeImport(filePath, "src/lib/docs.config.ts");
276
+ }
261
277
  function docsConfigTemplate(cfg) {
262
278
  if (cfg.theme === "custom" && cfg.customThemeName) {
263
279
  const exportName = getThemeExportName(cfg.customThemeName);
@@ -811,13 +827,27 @@ function DocsIndexPage() {
811
827
  }
812
828
  function tanstackDocsCatchAllRouteTemplate(opts) {
813
829
  const entryUrl = `/${opts.entry.replace(/^\/+|\/+$/g, "")}`;
830
+ const serverImport = opts.useAlias ? "@/lib/docs.server" : relativeImport(opts.filePath, "src/lib/docs.server.ts");
814
831
  return `\
815
832
  import { createFileRoute, notFound } from "@tanstack/react-router";
833
+ import { isDocsPublicGetRequest } from "@farming-labs/docs";
816
834
  import { TanstackDocsPage } from "@farming-labs/tanstack-start/react";
817
835
  import { loadDocPage } from "${tanstackDocsFunctionsImport(opts)}";
836
+ import { docsServer } from "${serverImport}";
818
837
  import docsConfig from "${tanstackDocsConfigImport(opts.filePath)}";
819
838
 
820
839
  export const Route = createFileRoute("${entryUrl}/$")({
840
+ server: {
841
+ handlers: {
842
+ GET: async ({ request }) => {
843
+ const url = new URL(request.url);
844
+ if (isDocsPublicGetRequest(${JSON.stringify(opts.entry)}, url, request)) {
845
+ return docsServer.GET({ request });
846
+ }
847
+ return undefined;
848
+ },
849
+ },
850
+ },
821
851
  loader: async ({ location }) => {
822
852
  try {
823
853
  return await loadDocPage({ data: { pathname: location.pathname } });
@@ -865,6 +895,46 @@ export const Route = createFileRoute("/api/docs")({
865
895
  });
866
896
  `;
867
897
  }
898
+ function tanstackDocsPublicRouteTemplate(useAlias, filePath, entry) {
899
+ return `\
900
+ import { createFileRoute } from "@tanstack/react-router";
901
+ import { isDocsMcpRequest, isDocsPublicGetRequest } from "@farming-labs/docs";
902
+ import { docsServer } from "${useAlias ? "@/lib/docs.server" : relativeImport(filePath, "src/lib/docs.server.ts")}";
903
+
904
+ const docsEntry = ${JSON.stringify(entry)};
905
+
906
+ async function handlePublicDocsRequest(request: Request) {
907
+ const url = new URL(request.url);
908
+ const method = request.method.toUpperCase();
909
+
910
+ if (isDocsMcpRequest(url)) {
911
+ if (method === "POST") return docsServer.MCP.POST({ request });
912
+ if (method === "DELETE") return docsServer.MCP.DELETE({ request });
913
+ if (method === "GET" || method === "HEAD") return docsServer.MCP.GET({ request });
914
+ return new Response("Method Not Allowed", {
915
+ status: 405,
916
+ headers: { Allow: "GET, HEAD, POST, DELETE" },
917
+ });
918
+ }
919
+
920
+ if ((method === "GET" || method === "HEAD") && isDocsPublicGetRequest(docsEntry, url, request)) {
921
+ return docsServer.GET({ request });
922
+ }
923
+
924
+ return new Response("Not Found", { status: 404 });
925
+ }
926
+
927
+ export const Route = createFileRoute("/$")({
928
+ server: {
929
+ handlers: {
930
+ GET: async ({ request }) => handlePublicDocsRequest(request),
931
+ POST: async ({ request }) => handlePublicDocsRequest(request),
932
+ DELETE: async ({ request }) => handlePublicDocsRequest(request),
933
+ },
934
+ },
935
+ });
936
+ `;
937
+ }
868
938
  function tanstackApiReferenceRouteTemplate(opts) {
869
939
  const routePath = `/${opts.apiReferencePath}${opts.catchAll ? "/$" : "/"}`;
870
940
  return `\
@@ -1038,6 +1108,7 @@ src/lib/docs.functions.ts
1038
1108
  src/routes/${cfg.entry}/index.tsx
1039
1109
  src/routes/${cfg.entry}/$.tsx
1040
1110
  src/routes/api/docs.ts
1111
+ src/routes/$.ts
1041
1112
  \`\`\`
1042
1113
  `;
1043
1114
  }
@@ -1192,7 +1263,7 @@ const contentFiles = import.meta.glob("/${cfg.entry ?? "docs"}/**/*.{md,mdx,svx}
1192
1263
  eager: true,
1193
1264
  }) as Record<string, string>;
1194
1265
 
1195
- export const { load, GET, POST } = createDocsServer({
1266
+ export const { load, GET, POST, MCP } = createDocsServer({
1196
1267
  ...config,
1197
1268
  _preloadedContent: contentFiles,
1198
1269
  });
@@ -1237,6 +1308,91 @@ import config from "${useAlias ? "$lib/docs.config" : relativeImport(filePath, "
1237
1308
  export const GET = createSvelteApiReference(config);
1238
1309
  `;
1239
1310
  }
1311
+ function svelteDocsApiRouteTemplate(filePath, useAlias) {
1312
+ return `\
1313
+ export { GET, POST } from "${svelteRouteServerImport(filePath, useAlias)}";
1314
+ `;
1315
+ }
1316
+ function svelteDocsPublicHookTemplate(filePath, useAlias) {
1317
+ const serverImport = svelteRouteServerImport(filePath, useAlias);
1318
+ return `\
1319
+ import { isDocsMcpRequest, isDocsPublicGetRequest } from "@farming-labs/docs";
1320
+ import type { Handle } from "@sveltejs/kit";
1321
+ import config from "${svelteRouteConfigImport(filePath, useAlias)}";
1322
+ import { GET, MCP } from "${serverImport}";
1323
+
1324
+ const docsEntry = config.entry ?? "docs";
1325
+
1326
+ export const handle: Handle = async ({ event, resolve }) => {
1327
+ const method = event.request.method.toUpperCase();
1328
+
1329
+ if (isDocsMcpRequest(event.url)) {
1330
+ if (method === "POST") return MCP.POST({ request: event.request });
1331
+ if (method === "DELETE") return MCP.DELETE({ request: event.request });
1332
+ if (method === "GET" || method === "HEAD") return MCP.GET({ request: event.request });
1333
+ return new Response("Method Not Allowed", {
1334
+ status: 405,
1335
+ headers: { Allow: "GET, HEAD, POST, DELETE" },
1336
+ });
1337
+ }
1338
+
1339
+ if ((method === "GET" || method === "HEAD") && isDocsPublicGetRequest(docsEntry, event.url, event.request)) {
1340
+ return GET({ url: event.url, request: event.request });
1341
+ }
1342
+
1343
+ return resolve(event);
1344
+ };
1345
+ `;
1346
+ }
1347
+ function injectSvelteDocsPublicHook(content, filePath, useAlias) {
1348
+ if (content.includes("isDocsPublicGetRequest") || content.includes("docsPublicHandle")) return null;
1349
+ if (/export\s*{\s*handle\b/.test(content)) return null;
1350
+ const serverImport = svelteRouteServerImport(filePath, useAlias);
1351
+ const configImport = svelteRouteConfigImport(filePath, useAlias);
1352
+ let next = content.trimEnd();
1353
+ let hasExistingHandle = false;
1354
+ for (const [pattern, replacement] of [
1355
+ [/\bexport\s+const\s+handle(\s*:\s*[^=]+)?\s*=/, "const existingHandle$1 ="],
1356
+ [/\bexport\s+async\s+function\s+handle\b/, "async function existingHandle"],
1357
+ [/\bexport\s+function\s+handle\b/, "function existingHandle"]
1358
+ ]) if (pattern.test(next)) {
1359
+ next = next.replace(pattern, replacement);
1360
+ next = next.replace(/const existingHandle(:[^=\n]*?)\s+=/, "const existingHandle$1 =");
1361
+ hasExistingHandle = true;
1362
+ break;
1363
+ }
1364
+ const imports = [
1365
+ "import { isDocsMcpRequest, isDocsPublicGetRequest } from \"@farming-labs/docs\";",
1366
+ ...next.includes("Handle") ? [] : ["import type { Handle } from \"@sveltejs/kit\";"],
1367
+ ...hasExistingHandle && !next.includes("sequence") ? ["import { sequence } from \"@sveltejs/kit/hooks\";"] : [],
1368
+ `import docsConfig from "${configImport}";`,
1369
+ `import { GET as docsGET, MCP as docsMCP } from "${serverImport}";`
1370
+ ];
1371
+ const docsHandle = `\
1372
+ const docsEntry = docsConfig.entry ?? "docs";
1373
+
1374
+ const docsPublicHandle: Handle = async ({ event, resolve }) => {
1375
+ const method = event.request.method.toUpperCase();
1376
+
1377
+ if (isDocsMcpRequest(event.url)) {
1378
+ if (method === "POST") return docsMCP.POST({ request: event.request });
1379
+ if (method === "DELETE") return docsMCP.DELETE({ request: event.request });
1380
+ if (method === "GET" || method === "HEAD") return docsMCP.GET({ request: event.request });
1381
+ return new Response("Method Not Allowed", {
1382
+ status: 405,
1383
+ headers: { Allow: "GET, HEAD, POST, DELETE" },
1384
+ });
1385
+ }
1386
+
1387
+ if ((method === "GET" || method === "HEAD") && isDocsPublicGetRequest(docsEntry, event.url, event.request)) {
1388
+ return docsGET({ url: event.url, request: event.request });
1389
+ }
1390
+
1391
+ return resolve(event);
1392
+ };`;
1393
+ const exportLine = hasExistingHandle ? "export const handle = sequence(docsPublicHandle, existingHandle);" : "export const handle = docsPublicHandle;";
1394
+ return `${imports.join("\n")}\n${next}\n\n${docsHandle}\n\n${exportLine}\n`;
1395
+ }
1240
1396
  function svelteRootLayoutTemplate(globalCssRelPath) {
1241
1397
  let cssImport;
1242
1398
  if (globalCssRelPath.startsWith("src/")) cssImport = "./" + globalCssRelPath.slice(4);
@@ -1354,10 +1510,12 @@ ${cfg.entry}/ # Markdown content
1354
1510
  quickstart/
1355
1511
  page.md # /${cfg.entry}/quickstart
1356
1512
  src/
1513
+ hooks.server.ts # Public docs aliases
1357
1514
  lib/
1358
1515
  docs.config.ts # Docs configuration
1359
1516
  docs.server.ts # Server-side docs loader
1360
1517
  routes/
1518
+ api/docs/+server.ts # Search/AI API route
1361
1519
  ${cfg.entry}/
1362
1520
  +layout.svelte # Docs layout
1363
1521
  +layout.server.js # Layout data loader
@@ -1509,7 +1667,7 @@ const contentFiles = import.meta.glob("/${cfg.entry ?? "docs"}/**/*.{md,mdx}", {
1509
1667
  eager: true,
1510
1668
  }) as Record<string, string>;
1511
1669
 
1512
- export const { load, GET, POST } = createDocsServer({
1670
+ export const { load, GET, POST, MCP } = createDocsServer({
1513
1671
  ...config,
1514
1672
  _preloadedContent: contentFiles,
1515
1673
  });
@@ -1619,6 +1777,86 @@ export const POST: APIRoute = async ({ request }) => {
1619
1777
  };
1620
1778
  `;
1621
1779
  }
1780
+ function astroDocsMiddlewareTemplate(filePath, useAlias) {
1781
+ const serverImport = astroRouteServerImport(filePath, useAlias);
1782
+ return `\
1783
+ import { isDocsMcpRequest, isDocsPublicGetRequest } from "@farming-labs/docs";
1784
+ import type { MiddlewareHandler } from "astro";
1785
+ import config from "${astroRouteConfigImport(filePath, useAlias)}";
1786
+ import { GET, MCP } from "${serverImport}";
1787
+
1788
+ const docsEntry = config.entry ?? "docs";
1789
+
1790
+ export const onRequest: MiddlewareHandler = async (context, next) => {
1791
+ const method = context.request.method.toUpperCase();
1792
+
1793
+ if (isDocsMcpRequest(context.url)) {
1794
+ if (method === "POST") return MCP.POST({ request: context.request });
1795
+ if (method === "DELETE") return MCP.DELETE({ request: context.request });
1796
+ if (method === "GET" || method === "HEAD") return MCP.GET({ request: context.request });
1797
+ return new Response("Method Not Allowed", {
1798
+ status: 405,
1799
+ headers: { Allow: "GET, HEAD, POST, DELETE" },
1800
+ });
1801
+ }
1802
+
1803
+ if ((method === "GET" || method === "HEAD") && isDocsPublicGetRequest(docsEntry, context.url, context.request)) {
1804
+ return GET({ request: context.request });
1805
+ }
1806
+
1807
+ return next();
1808
+ };
1809
+ `;
1810
+ }
1811
+ function injectAstroDocsMiddleware(content, filePath, useAlias) {
1812
+ if (content.includes("isDocsPublicGetRequest") || content.includes("docsPublicMiddleware")) return null;
1813
+ if (/export\s*{\s*onRequest\b/.test(content)) return null;
1814
+ const serverImport = astroRouteServerImport(filePath, useAlias);
1815
+ const configImport = astroRouteConfigImport(filePath, useAlias);
1816
+ let next = content.trimEnd();
1817
+ let hasExistingMiddleware = false;
1818
+ for (const [pattern, replacement] of [
1819
+ [/\bexport\s+const\s+onRequest(\s*:\s*[^=]+)?\s*=/, "const existingOnRequest$1 ="],
1820
+ [/\bexport\s+async\s+function\s+onRequest\b/, "async function existingOnRequest"],
1821
+ [/\bexport\s+function\s+onRequest\b/, "function existingOnRequest"]
1822
+ ]) if (pattern.test(next)) {
1823
+ next = next.replace(pattern, replacement);
1824
+ next = next.replace(/const existingOnRequest(:[^=\n]*?)\s+=/, "const existingOnRequest$1 =");
1825
+ hasExistingMiddleware = true;
1826
+ break;
1827
+ }
1828
+ const imports = [
1829
+ "import { isDocsMcpRequest, isDocsPublicGetRequest } from \"@farming-labs/docs\";",
1830
+ ...next.includes("MiddlewareHandler") ? [] : ["import type { MiddlewareHandler } from \"astro\";"],
1831
+ ...hasExistingMiddleware && !next.includes("sequence") ? ["import { sequence } from \"astro:middleware\";"] : [],
1832
+ `import docsConfig from "${configImport}";`,
1833
+ `import { GET as docsGET, MCP as docsMCP } from "${serverImport}";`
1834
+ ];
1835
+ const docsMiddleware = `\
1836
+ const docsEntry = docsConfig.entry ?? "docs";
1837
+
1838
+ const docsPublicMiddleware: MiddlewareHandler = async (context, next) => {
1839
+ const method = context.request.method.toUpperCase();
1840
+
1841
+ if (isDocsMcpRequest(context.url)) {
1842
+ if (method === "POST") return docsMCP.POST({ request: context.request });
1843
+ if (method === "DELETE") return docsMCP.DELETE({ request: context.request });
1844
+ if (method === "GET" || method === "HEAD") return docsMCP.GET({ request: context.request });
1845
+ return new Response("Method Not Allowed", {
1846
+ status: 405,
1847
+ headers: { Allow: "GET, HEAD, POST, DELETE" },
1848
+ });
1849
+ }
1850
+
1851
+ if ((method === "GET" || method === "HEAD") && isDocsPublicGetRequest(docsEntry, context.url, context.request)) {
1852
+ return docsGET({ request: context.request });
1853
+ }
1854
+
1855
+ return next();
1856
+ };`;
1857
+ const exportLine = hasExistingMiddleware ? "export const onRequest = sequence(docsPublicMiddleware, existingOnRequest);" : "export const onRequest = docsPublicMiddleware;";
1858
+ return `${imports.join("\n")}\n${next}\n\n${docsMiddleware}\n\n${exportLine}\n`;
1859
+ }
1622
1860
  function astroApiReferenceRouteTemplate(filePath) {
1623
1861
  return `\
1624
1862
  import { createAstroApiReference } from "@farming-labs/astro/api-reference";
@@ -1730,6 +1968,7 @@ ${cfg.entry}/ # Markdown content
1730
1968
  quickstart/
1731
1969
  page.md # /${cfg.entry}/quickstart
1732
1970
  src/
1971
+ middleware.ts # Public docs aliases
1733
1972
  lib/
1734
1973
  docs.config.ts # Docs configuration
1735
1974
  docs.server.ts # Server-side docs loader
@@ -1862,66 +2101,20 @@ export default defineDocs({
1862
2101
  });
1863
2102
  `;
1864
2103
  }
1865
- function nuxtDocsServerTemplate(cfg) {
1866
- const contentDirName = cfg.entry ?? "docs";
2104
+ function nuxtServerApiDocsRouteTemplate(cfg) {
1867
2105
  return `\
1868
- import { createDocsServer } from "@farming-labs/nuxt/server";
2106
+ import { defineDocsHandler } from "@farming-labs/nuxt/server";
1869
2107
  import config from "${cfg.useAlias ? "~/docs.config" : "../../docs.config"}";
1870
2108
 
1871
- const contentFiles = import.meta.glob("/${contentDirName}/**/*.{md,mdx}", {
1872
- query: "?raw",
1873
- import: "default",
1874
- eager: true,
1875
- }) as Record<string, string>;
1876
-
1877
- export const docsServer = createDocsServer({
1878
- ...config,
1879
- _preloadedContent: contentFiles,
1880
- });
1881
- `;
1882
- }
1883
- function nuxtServerApiDocsGetTemplate() {
1884
- return `\
1885
- import { getRequestURL } from "h3";
1886
- import { docsServer } from "../utils/docs-server";
1887
-
1888
- export default defineEventHandler((event) => {
1889
- const url = getRequestURL(event);
1890
- const request = new Request(url.href, {
1891
- method: event.method,
1892
- headers: event.headers,
1893
- });
1894
- return docsServer.GET({ request });
1895
- });
1896
- `;
1897
- }
1898
- function nuxtServerApiDocsPostTemplate() {
1899
- return `\
1900
- import { getRequestURL, readRawBody } from "h3";
1901
- import { docsServer } from "../utils/docs-server";
1902
-
1903
- export default defineEventHandler(async (event) => {
1904
- const url = getRequestURL(event);
1905
- const body = await readRawBody(event);
1906
- const request = new Request(url.href, {
1907
- method: "POST",
1908
- headers: event.headers,
1909
- body: body ?? undefined,
1910
- });
1911
- return docsServer.POST({ request });
1912
- });
2109
+ export default defineDocsHandler(config, useStorage);
1913
2110
  `;
1914
2111
  }
1915
- function nuxtServerApiDocsLoadTemplate() {
2112
+ function nuxtServerDocsPublicMiddlewareTemplate(cfg) {
1916
2113
  return `\
1917
- import { getQuery } from "h3";
1918
- import { docsServer } from "../../utils/docs-server";
2114
+ import { defineDocsPublicHandler } from "@farming-labs/nuxt/server";
2115
+ import config from "${cfg.useAlias ? "~/docs.config" : "../../docs.config"}";
1919
2116
 
1920
- export default defineEventHandler(async (event) => {
1921
- const query = getQuery(event);
1922
- const pathname = (query.pathname as string) ?? "/docs";
1923
- return docsServer.load(pathname);
1924
- });
2117
+ export default defineDocsPublicHandler(config, useStorage);
1925
2118
  `;
1926
2119
  }
1927
2120
  function nuxtServerApiReferenceRouteTemplate(filePath, useAlias) {
@@ -1942,7 +2135,7 @@ const route = useRoute();
1942
2135
  const pathname = computed(() => route.path);
1943
2136
 
1944
2137
  const { data, error } = await useAsyncData(\`docs-\${pathname.value}\`, () =>
1945
- $fetch("/api/docs/load", {
2138
+ $fetch("/api/docs", {
1946
2139
  query: { pathname: pathname.value },
1947
2140
  })
1948
2141
  );
@@ -1979,6 +2172,7 @@ export default defineNuxtConfig({
1979
2172
 
1980
2173
  nitro: {
1981
2174
  moduleSideEffects: ["@farming-labs/nuxt/server"],
2175
+ serverAssets: [{ baseName: "${cfg.entry}", dir: "${cfg.entry}" }],
1982
2176
  },
1983
2177
  });
1984
2178
  `;
@@ -2065,11 +2259,8 @@ ${cfg.entry}/ # Markdown content
2065
2259
  installation/page.md
2066
2260
  quickstart/page.md
2067
2261
  server/
2068
- utils/docs-server.ts # createDocsServer + preloaded content
2069
- api/docs/
2070
- load.get.ts # Page data API
2071
- docs.get.ts # Search API
2072
- docs.post.ts # AI chat API
2262
+ api/docs.ts # Page data, search, and AI chat API
2263
+ middleware/docs-public.ts # llms.txt, .well-known, .md, and MCP aliases
2073
2264
  pages/
2074
2265
  ${cfg.entry}/[[...slug]].vue # Docs catch-all page
2075
2266
  docs.config.ts
@@ -3091,6 +3282,7 @@ function scaffoldTanstackStart(cwd, cfg, globalCssRelPath, write, skipped, writt
3091
3282
  const docsIndexRoute = `${routeDir}/index.tsx`;
3092
3283
  const docsCatchAllRoute = `${routeDir}/$.tsx`;
3093
3284
  const apiRoute = "src/routes/api/docs.ts";
3285
+ const publicRoute = "src/routes/$.ts";
3094
3286
  write(docsIndexRoute, tanstackDocsIndexRouteTemplate({
3095
3287
  entry: cfg.entry,
3096
3288
  filePath: docsIndexRoute,
@@ -3104,6 +3296,7 @@ function scaffoldTanstackStart(cwd, cfg, globalCssRelPath, write, skipped, writt
3104
3296
  projectName: cfg.projectName
3105
3297
  }));
3106
3298
  write(apiRoute, tanstackApiDocsRouteTemplate(cfg.useAlias, apiRoute));
3299
+ write(publicRoute, tanstackDocsPublicRouteTemplate(cfg.useAlias, publicRoute, cfg.entry));
3107
3300
  if (cfg.apiReference) {
3108
3301
  const apiReferenceIndexRoute = `src/routes/${cfg.apiReference.path}.index.ts`;
3109
3302
  const apiReferenceCatchAllRoute = `src/routes/${cfg.apiReference.path}.$.ts`;
@@ -3168,6 +3361,18 @@ function scaffoldSvelteKit(cwd, cfg, globalCssRelPath, write, skipped, written)
3168
3361
  write(`src/routes/${cfg.entry}/+layout.server.js`, svelteDocsLayoutServerTemplate(cfg));
3169
3362
  write(`src/routes/${cfg.entry}/[...slug]/+page.svelte`, svelteDocsPageTemplate(cfg));
3170
3363
  if (cfg.i18n?.locales.length) write(`src/routes/${cfg.entry}/+page.svelte`, svelteDocsPageTemplate(cfg));
3364
+ const apiDocsRoute = "src/routes/api/docs/+server.ts";
3365
+ const publicHook = "src/hooks.server.ts";
3366
+ write(apiDocsRoute, svelteDocsApiRouteTemplate(apiDocsRoute, cfg.useAlias));
3367
+ const publicHookPath = path.join(cwd, publicHook);
3368
+ const existingPublicHook = readFileSafe(publicHookPath);
3369
+ if (existingPublicHook) {
3370
+ const injected = injectSvelteDocsPublicHook(existingPublicHook, publicHook, cfg.useAlias);
3371
+ if (injected) {
3372
+ writeFileSafe(publicHookPath, injected, true);
3373
+ written.push(`${publicHook} (composed docs public hook)`);
3374
+ } else skipped.push(`${publicHook} (already configured or could not compose docs public hook)`);
3375
+ } else write(publicHook, svelteDocsPublicHookTemplate(publicHook, cfg.useAlias));
3171
3376
  if (cfg.apiReference) {
3172
3377
  const apiReferenceIndexRoute = `src/routes/${cfg.apiReference.path}/+server.ts`;
3173
3378
  const apiReferenceCatchAllRoute = `src/routes/${cfg.apiReference.path}/[...slug]/+server.ts`;
@@ -3215,6 +3420,16 @@ function scaffoldAstro(cwd, cfg, globalCssRelPath, write, skipped, written) {
3215
3420
  write(`src/pages/${cfg.entry}/index.astro`, astroDocsIndexTemplate(cfg));
3216
3421
  write(`src/pages/${cfg.entry}/[...slug].astro`, astroDocsPageTemplate(cfg));
3217
3422
  write(`src/pages/api/${cfg.entry}.ts`, astroApiRouteTemplate(cfg));
3423
+ const middleware = "src/middleware.ts";
3424
+ const middlewarePath = path.join(cwd, middleware);
3425
+ const existingMiddleware = readFileSafe(middlewarePath);
3426
+ if (existingMiddleware) {
3427
+ const injected = injectAstroDocsMiddleware(existingMiddleware, middleware, cfg.useAlias);
3428
+ if (injected) {
3429
+ writeFileSafe(middlewarePath, injected, true);
3430
+ written.push(`${middleware} (composed docs public middleware)`);
3431
+ } else skipped.push(`${middleware} (already configured or could not compose docs public middleware)`);
3432
+ } else write(middleware, astroDocsMiddlewareTemplate(middleware, cfg.useAlias));
3218
3433
  if (cfg.apiReference) {
3219
3434
  const apiReferenceIndexRoute = `src/pages/${cfg.apiReference.path}/index.ts`;
3220
3435
  const apiReferenceCatchAllRoute = `src/pages/${cfg.apiReference.path}/[...slug].ts`;
@@ -3256,10 +3471,8 @@ function scaffoldNuxt(cwd, cfg, globalCssRelPath, write, skipped, written) {
3256
3471
  write(`themes/${baseName}.css`, customThemeCssTemplate(baseName));
3257
3472
  }
3258
3473
  write("docs.config.ts", nuxtDocsConfigTemplate(cfg));
3259
- write("server/utils/docs-server.ts", nuxtDocsServerTemplate(cfg));
3260
- write("server/api/docs.get.ts", nuxtServerApiDocsGetTemplate());
3261
- write("server/api/docs.post.ts", nuxtServerApiDocsPostTemplate());
3262
- write("server/api/docs/load.get.ts", nuxtServerApiDocsLoadTemplate());
3474
+ write("server/api/docs.ts", nuxtServerApiDocsRouteTemplate(cfg));
3475
+ write("server/middleware/docs-public.ts", nuxtServerDocsPublicMiddlewareTemplate(cfg));
3263
3476
  write(`pages/${cfg.entry}/[[...slug]].vue`, nuxtDocsPageTemplate(cfg));
3264
3477
  if (cfg.apiReference) {
3265
3478
  const apiReferenceIndexRoute = `server/routes/${cfg.apiReference.path}/index.ts`;
package/dist/mcp.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { E as DocsSearchConfig, W as OrderingItem, v as DocsMcpConfig } from "./types-CP8OcVgL.mjs";
1
+ import { D as DocsSearchConfig, G as OrderingItem, N as DocsSearchSourcePage, v as DocsMcpConfig } from "./types-CP2NmW5c.mjs";
2
2
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
 
4
4
  //#region src/mcp.d.ts
@@ -7,6 +7,7 @@ interface DocsMcpPage {
7
7
  url: string;
8
8
  title: string;
9
9
  description?: string;
10
+ related?: DocsSearchSourcePage["related"];
10
11
  icon?: string;
11
12
  content: string;
12
13
  rawContent?: string;
package/dist/mcp.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { s as performDocsSearch } from "./search-BS6C5N1i.mjs";
1
+ import { l as normalizeDocsRelated, s as performDocsSearch, u as renderDocsRelatedMarkdownLines } from "./search-CfnJVeh-.mjs";
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
4
  import { randomUUID } from "node:crypto";
@@ -330,6 +330,7 @@ function scanFilesystemDocsPages(contentDirAbs, entry) {
330
330
  url,
331
331
  title,
332
332
  description: data.description,
333
+ relatedInput: data.related,
333
334
  icon: data.icon,
334
335
  content: stripMarkdownForMcp(humanRawContent),
335
336
  rawContent: humanRawContent,
@@ -341,7 +342,7 @@ function scanFilesystemDocsPages(contentDirAbs, entry) {
341
342
  }
342
343
  }
343
344
  scan(contentDirAbs, []);
344
- return pages;
345
+ return resolveRelatedForMcpPages(pages);
345
346
  }
346
347
  function readFilesystemAgentDoc(dir) {
347
348
  const agentPath = path.join(dir, "agent.md");
@@ -352,6 +353,15 @@ function readFilesystemAgentDoc(dir) {
352
353
  agentRawContent: content
353
354
  };
354
355
  }
356
+ function resolveRelatedForMcpPages(pages) {
357
+ return pages.map(({ relatedInput, ...page }) => {
358
+ const related = normalizeDocsRelated(relatedInput);
359
+ return related.length > 0 ? {
360
+ ...page,
361
+ related
362
+ } : page;
363
+ });
364
+ }
355
365
  function buildNavigationTreeFromPages(pages, siteTitle, ordering) {
356
366
  const bySlug = new Map(pages.map((page) => [page.slug, page]));
357
367
  const rootPage = bySlug.get("");
@@ -465,7 +475,8 @@ function toSearchSourcePages(pages) {
465
475
  agentRawContent: page.agentRawContent,
466
476
  agentFallbackContent: page.agentFallbackContent,
467
477
  agentFallbackRawContent: page.agentFallbackRawContent,
468
- description: page.description
478
+ description: page.description,
479
+ related: page.related
469
480
  }));
470
481
  }
471
482
  function isSelfMcpSearchEndpoint(search, route) {
@@ -522,8 +533,10 @@ function normalizeUrlPath(value) {
522
533
  }
523
534
  function renderPageDocument(page) {
524
535
  if (page.agentRawContent !== void 0) return page.agentRawContent;
536
+ const relatedLines = renderDocsRelatedMarkdownLines(page.related);
525
537
  const lines = [`# ${page.title}`, `URL: ${page.url}`];
526
538
  if (page.description) lines.push(`Description: ${page.description}`);
539
+ lines.push(...relatedLines);
527
540
  lines.push("", page.agentFallbackRawContent ?? page.rawContent ?? page.content);
528
541
  return lines.join("\n");
529
542
  }
@@ -1,4 +1,4 @@
1
- import { A as DocsSearchResult, D as DocsSearchDocument, E as DocsSearchConfig, M as DocsSearchSourcePage, S as DocsSearchAdapter, T as DocsSearchChunkingConfig, d as CustomDocsSearchConfig, r as AlgoliaDocsSearchConfig, rt as TypesenseDocsSearchConfig, w as DocsSearchAdapterFactory, z as McpDocsSearchConfig } from "./types-CP8OcVgL.mjs";
1
+ import { B as McpDocsSearchConfig, C as DocsSearchAdapter, D as DocsSearchConfig, E as DocsSearchChunkingConfig, N as DocsSearchSourcePage, O as DocsSearchDocument, T as DocsSearchAdapterFactory, at as TypesenseDocsSearchConfig, d as CustomDocsSearchConfig, j as DocsSearchResult, r as AlgoliaDocsSearchConfig } from "./types-CP2NmW5c.mjs";
2
2
 
3
3
  //#region src/search.d.ts
4
4
  declare function buildDocsSearchDocuments(pages: DocsSearchSourcePage[], chunking?: DocsSearchChunkingConfig): DocsSearchDocument[];
@@ -1,4 +1,4 @@
1
- import { n as createAlgoliaSearchAdapter, o as createTypesenseSearchAdapter, t as buildDocsSearchDocuments } from "./search-BS6C5N1i.mjs";
1
+ import { n as createAlgoliaSearchAdapter, o as createTypesenseSearchAdapter, t as buildDocsSearchDocuments } from "./search-CfnJVeh-.mjs";
2
2
  import "./api-reference-BQ16eRPP.mjs";
3
3
  import { createFilesystemDocsMcpSource } from "./mcp.mjs";
4
4
  import "./server.mjs";
@@ -1,3 +1,52 @@
1
+ //#region src/related.ts
2
+ function normalizeDocsRelated(value) {
3
+ if (!Array.isArray(value)) return [];
4
+ const seen = /* @__PURE__ */ new Set();
5
+ const links = [];
6
+ for (const item of value) {
7
+ const parsed = parseRelatedInput(item);
8
+ if (!parsed) continue;
9
+ const dedupeKey = normalizeRelatedLookupPath(parsed.href) ?? parsed.href;
10
+ if (seen.has(dedupeKey)) continue;
11
+ seen.add(dedupeKey);
12
+ links.push({ href: parsed.href });
13
+ }
14
+ return links;
15
+ }
16
+ function renderDocsRelatedMarkdownLines(related) {
17
+ if (!related?.length) return [];
18
+ return [`Related: ${related.map((link) => normalizeInlineText(link.href)).join(", ")}`];
19
+ }
20
+ function parseRelatedInput(value) {
21
+ if (typeof value === "string") {
22
+ const href = cleanString(value);
23
+ return href ? { href } : null;
24
+ }
25
+ return null;
26
+ }
27
+ function cleanString(value) {
28
+ if (typeof value !== "string") return void 0;
29
+ return value.trim() || void 0;
30
+ }
31
+ function normalizeRelatedLookupPath(value) {
32
+ const trimmed = value.trim();
33
+ if (!trimmed) return null;
34
+ let pathname = trimmed;
35
+ try {
36
+ pathname = new URL(trimmed, "https://docs.local").pathname;
37
+ } catch {
38
+ pathname = trimmed.split(/[?#]/, 1)[0] ?? trimmed;
39
+ }
40
+ pathname = pathname.replace(/\/+/g, "/").replace(/\.md$/i, "");
41
+ if (!pathname.startsWith("/")) pathname = `/${pathname}`;
42
+ if (pathname !== "/") pathname = pathname.replace(/\/+$/, "");
43
+ return pathname;
44
+ }
45
+ function normalizeInlineText(value) {
46
+ return value.replace(/\s+/g, " ").trim();
47
+ }
48
+
49
+ //#endregion
1
50
  //#region src/search.ts
2
51
  const DEFAULT_SEARCH_LIMIT = 10;
3
52
  const DEFAULT_MCP_PROTOCOL_VERSION = "2025-11-25";
@@ -668,4 +717,4 @@ function createCustomSearchAdapter(adapter) {
668
717
  }
669
718
 
670
719
  //#endregion
671
- export { createSimpleSearchAdapter as a, resolveSearchRequestConfig as c, createMcpSearchAdapter as i, createAlgoliaSearchAdapter as n, createTypesenseSearchAdapter as o, createCustomSearchAdapter as r, performDocsSearch as s, buildDocsSearchDocuments as t };
720
+ export { createSimpleSearchAdapter as a, resolveSearchRequestConfig as c, createMcpSearchAdapter as i, normalizeDocsRelated as l, createAlgoliaSearchAdapter as n, createTypesenseSearchAdapter as o, createCustomSearchAdapter as r, performDocsSearch as s, buildDocsSearchDocuments as t, renderDocsRelatedMarkdownLines as u };
package/dist/server.d.mts CHANGED
@@ -1,6 +1,6 @@
1
- import { A as DocsSearchResult, C as DocsSearchAdapterContext, D as DocsSearchDocument, E as DocsSearchConfig, M as DocsSearchSourcePage, S as DocsSearchAdapter, a as ApiReferenceRenderer, k as DocsSearchQuery, m as DocsConfig, w as DocsSearchAdapterFactory, z as McpDocsSearchConfig } from "./types-CP8OcVgL.mjs";
2
- import { a as createSimpleSearchAdapter, c as resolveSearchRequestConfig, i as createMcpSearchAdapter, n as createAlgoliaSearchAdapter, o as createTypesenseSearchAdapter, r as createCustomSearchAdapter, s as performDocsSearch, t as buildDocsSearchDocuments } from "./search-zClNrbqL.mjs";
1
+ import { A as DocsSearchQuery, B as McpDocsSearchConfig, C as DocsSearchAdapter, D as DocsSearchConfig, N as DocsSearchSourcePage, O as DocsSearchDocument, T as DocsSearchAdapterFactory, a as ApiReferenceRenderer, j as DocsSearchResult, m as DocsConfig, w as DocsSearchAdapterContext } from "./types-CP2NmW5c.mjs";
3
2
  import { DocsMcpHttpHandlers, DocsMcpNavigationNode, DocsMcpNavigationTree, DocsMcpPage, DocsMcpResolvedConfig, DocsMcpSource, createDocsMcpHttpHandler, createDocsMcpServer, createFilesystemDocsMcpSource, normalizeDocsMcpRoute, resolveDocsMcpConfig, runDocsMcpStdio } from "./mcp.mjs";
3
+ import { a as createSimpleSearchAdapter, c as resolveSearchRequestConfig, i as createMcpSearchAdapter, n as createAlgoliaSearchAdapter, o as createTypesenseSearchAdapter, r as createCustomSearchAdapter, s as performDocsSearch, t as buildDocsSearchDocuments } from "./search-BWqFW9rt.mjs";
4
4
 
5
5
  //#region src/api-reference.d.ts
6
6
  type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS" | "HEAD";
package/dist/server.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as createSimpleSearchAdapter, c as resolveSearchRequestConfig, i as createMcpSearchAdapter, n as createAlgoliaSearchAdapter, o as createTypesenseSearchAdapter, r as createCustomSearchAdapter, s as performDocsSearch, t as buildDocsSearchDocuments } from "./search-BS6C5N1i.mjs";
1
+ import { a as createSimpleSearchAdapter, c as resolveSearchRequestConfig, i as createMcpSearchAdapter, n as createAlgoliaSearchAdapter, o as createTypesenseSearchAdapter, r as createCustomSearchAdapter, s as performDocsSearch, t as buildDocsSearchDocuments } from "./search-CfnJVeh-.mjs";
2
2
  import { a as buildApiReferencePageTitle, c as resolveApiReferenceRenderer, i as buildApiReferenceOpenApiDocumentAsync, n as buildApiReferenceHtmlDocumentAsync, o as buildApiReferenceScalarCss, r as buildApiReferenceOpenApiDocument, s as resolveApiReferenceConfig, t as buildApiReferenceHtmlDocument } from "./api-reference-BQ16eRPP.mjs";
3
3
  import { createDocsMcpHttpHandler, createDocsMcpServer, createFilesystemDocsMcpSource, normalizeDocsMcpRoute, resolveDocsMcpConfig, runDocsMcpStdio } from "./mcp.mjs";
4
4
 
@@ -242,9 +242,15 @@ interface PageTwitter {
242
242
  title?: string;
243
243
  description?: string;
244
244
  }
245
+ type DocsRelatedItem = string;
246
+ interface ResolvedDocsRelatedLink {
247
+ href: string;
248
+ }
245
249
  interface PageFrontmatter {
246
250
  title: string;
247
251
  description?: string;
252
+ /** Related doc URLs rendered into machine-readable markdown routes and MCP page output. */
253
+ related?: DocsRelatedItem[];
248
254
  tags?: string[];
249
255
  icon?: string;
250
256
  /** Path to custom OG image for this page (shorthand). Ignored if `openGraph` is set. */
@@ -590,7 +596,7 @@ interface DocsMcpToolsConfig {
590
596
  * Built-in MCP server configuration.
591
597
  *
592
598
  * When enabled, adapters can expose a Streamable HTTP endpoint for your docs
593
- * at `/mcp` and `/.well-known/mcp` in Next.js, backed by the canonical `/api/docs/mcp` route.
599
+ * at `/mcp` and `/.well-known/mcp`, backed by the canonical `/api/docs/mcp` route.
594
600
  * The same config is also reused by the local `docs mcp` stdio command.
595
601
  */
596
602
  interface DocsMcpConfig {
@@ -598,7 +604,7 @@ interface DocsMcpConfig {
598
604
  enabled?: boolean;
599
605
  /**
600
606
  * Streamable HTTP route for the MCP endpoint.
601
- * Defaults to `/api/docs/mcp`; Next.js also exposes it publicly at `/mcp` and `/.well-known/mcp`.
607
+ * Defaults to `/api/docs/mcp`; generated projects can also expose it publicly at `/mcp` and `/.well-known/mcp`.
602
608
  */
603
609
  route?: string;
604
610
  /**
@@ -620,6 +626,7 @@ interface DocsSearchSourcePage {
620
626
  url: string;
621
627
  content: string;
622
628
  description?: string;
629
+ related?: ResolvedDocsRelatedLink[];
623
630
  rawContent?: string;
624
631
  agentContent?: string;
625
632
  agentRawContent?: string;
@@ -1592,8 +1599,8 @@ interface DocsConfig {
1592
1599
  /**
1593
1600
  * Built-in MCP server for agent/assistant access to your docs content.
1594
1601
  *
1595
- * - omitted → enable the default MCP surface at `/mcp` and `/.well-known/mcp` in Next.js, backed by `/api/docs/mcp`
1596
- * - `true` → enable the default MCP surface at `/mcp` and `/.well-known/mcp` in Next.js, backed by `/api/docs/mcp`
1602
+ * - omitted → enable the default MCP surface at `/mcp` and `/.well-known/mcp`, backed by `/api/docs/mcp`
1603
+ * - `true` → enable the default MCP surface at `/mcp` and `/.well-known/mcp`, backed by `/api/docs/mcp`
1597
1604
  * - `{ route: "/api/docs/mcp" }` → enable with explicit route/config
1598
1605
  * - `false` or `{ enabled: false }` → disable MCP explicitly
1599
1606
  */
@@ -1811,4 +1818,4 @@ interface DocsConfig {
1811
1818
  og?: OGConfig;
1812
1819
  }
1813
1820
  //#endregion
1814
- export { SidebarPageNode as $, DocsSearchResult as A, OGConfig as B, DocsSearchAdapterContext as C, DocsSearchDocument as D, DocsSearchConfig as E, FontStyle as F, PageActionsConfig as G, OpenDocsProvider as H, GithubConfig as I, PageTwitter as J, PageFrontmatter as K, LastUpdatedConfig as L, DocsSearchSourcePage as M, DocsTheme as N, DocsSearchEmbeddingsConfig as O, FeedbackConfig as P, SidebarNode as Q, LlmsTxtConfig as R, DocsSearchAdapter as S, DocsSearchChunkingConfig as T, OpenGraphImage as U, OpenDocsConfig as V, OrderingItem as W, SidebarConfig as X, SidebarComponentProps as Y, SidebarFolderNode as Z, DocsI18nConfig as _, ApiReferenceRenderer as a, UIConfig as at, DocsMetadata as b, ChangelogFrontmatter as c, CustomDocsSearchConfig as d, SidebarTree as et, DocsAgentFeedbackContext as f, DocsFeedbackValue as g, DocsFeedbackData as h, ApiReferenceConfig as i, TypographyConfig as it, DocsSearchResultType as j, DocsSearchQuery as k, CodeBlockCopyData as l, DocsConfig as m, AgentFeedbackConfig as n, ThemeToggleConfig as nt, BreadcrumbConfig as o, DocsAgentFeedbackData as p, PageOpenGraph as q, AlgoliaDocsSearchConfig as r, TypesenseDocsSearchConfig as rt, ChangelogConfig as s, AIConfig as t, SimpleDocsSearchConfig as tt, CopyMarkdownConfig as u, DocsMcpConfig as v, DocsSearchAdapterFactory as w, DocsNav as x, DocsMcpToolsConfig as y, McpDocsSearchConfig as z };
1821
+ export { SidebarFolderNode as $, DocsSearchQuery as A, McpDocsSearchConfig as B, DocsSearchAdapter as C, DocsSearchConfig as D, DocsSearchChunkingConfig as E, FeedbackConfig as F, OrderingItem as G, OpenDocsConfig as H, FontStyle as I, PageOpenGraph as J, PageActionsConfig as K, GithubConfig as L, DocsSearchResultType as M, DocsSearchSourcePage as N, DocsSearchDocument as O, DocsTheme as P, SidebarConfig as Q, LastUpdatedConfig as R, DocsRelatedItem as S, DocsSearchAdapterFactory as T, OpenDocsProvider as U, OGConfig as V, OpenGraphImage as W, ResolvedDocsRelatedLink as X, PageTwitter as Y, SidebarComponentProps as Z, DocsI18nConfig as _, ApiReferenceRenderer as a, TypesenseDocsSearchConfig as at, DocsMetadata as b, ChangelogFrontmatter as c, CustomDocsSearchConfig as d, SidebarNode as et, DocsAgentFeedbackContext as f, DocsFeedbackValue as g, DocsFeedbackData as h, ApiReferenceConfig as i, ThemeToggleConfig as it, DocsSearchResult as j, DocsSearchEmbeddingsConfig as k, CodeBlockCopyData as l, DocsConfig as m, AgentFeedbackConfig as n, SidebarTree as nt, BreadcrumbConfig as o, TypographyConfig as ot, DocsAgentFeedbackData as p, PageFrontmatter as q, AlgoliaDocsSearchConfig as r, SimpleDocsSearchConfig as rt, ChangelogConfig as s, UIConfig as st, AIConfig as t, SidebarPageNode as tt, CopyMarkdownConfig as u, DocsMcpConfig as v, DocsSearchAdapterContext as w, DocsNav as x, DocsMcpToolsConfig as y, LlmsTxtConfig as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farming-labs/docs",
3
- "version": "0.1.35",
3
+ "version": "0.1.37",
4
4
  "description": "Modern, flexible MDX-based docs framework — core types, config, and CLI",
5
5
  "keywords": [
6
6
  "docs",