@farming-labs/docs 0.1.36 → 0.1.38

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,7 +69,7 @@ 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");
package/dist/index.d.mts CHANGED
@@ -1,5 +1,6 @@
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-ByCOEOnE.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-HA-ofcyh.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
  /**
@@ -119,4 +120,179 @@ declare function buildPageTwitter(page: Pick<PageFrontmatter, "title" | "descrip
119
120
  declare function normalizeDocsRelated(value: unknown): ResolvedDocsRelatedLink[];
120
121
  declare function renderDocsRelatedMarkdownLines(related: ResolvedDocsRelatedLink[] | undefined): string[];
121
122
  //#endregion
122
- 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 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, buildDocsSearchDocuments, buildPageOpenGraph, buildPageTwitter, createAlgoliaSearchAdapter, createCustomSearchAdapter, createMcpSearchAdapter, createSimpleSearchAdapter, createTheme, createTypesenseSearchAdapter, deepMerge, defineDocs, extendTheme, normalizeDocsRelated, performDocsSearch, renderDocsRelatedMarkdownLines, resolveChangelogConfig, resolveDocsI18n, resolveDocsLocale, resolveDocsPath, resolveOGImage, resolveSearchRequestConfig, resolveTitle };
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
@@ -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, normalizeDocsRelated, performDocsSearch, renderDocsRelatedMarkdownLines, 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 { D as DocsSearchConfig, G as OrderingItem, N as DocsSearchSourcePage, v as DocsMcpConfig } from "./types-ByCOEOnE.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
@@ -1,4 +1,4 @@
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-ByCOEOnE.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[];
package/dist/server.d.mts CHANGED
@@ -1,6 +1,6 @@
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-ByCOEOnE.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-HA-ofcyh.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";
@@ -596,7 +596,7 @@ interface DocsMcpToolsConfig {
596
596
  * Built-in MCP server configuration.
597
597
  *
598
598
  * When enabled, adapters can expose a Streamable HTTP endpoint for your docs
599
- * 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.
600
600
  * The same config is also reused by the local `docs mcp` stdio command.
601
601
  */
602
602
  interface DocsMcpConfig {
@@ -604,7 +604,7 @@ interface DocsMcpConfig {
604
604
  enabled?: boolean;
605
605
  /**
606
606
  * Streamable HTTP route for the MCP endpoint.
607
- * 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`.
608
608
  */
609
609
  route?: string;
610
610
  /**
@@ -1599,8 +1599,8 @@ interface DocsConfig {
1599
1599
  /**
1600
1600
  * Built-in MCP server for agent/assistant access to your docs content.
1601
1601
  *
1602
- * - omitted → enable the default MCP surface at `/mcp` and `/.well-known/mcp` in Next.js, backed by `/api/docs/mcp`
1603
- * - `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`
1604
1604
  * - `{ route: "/api/docs/mcp" }` → enable with explicit route/config
1605
1605
  * - `false` or `{ enabled: false }` → disable MCP explicitly
1606
1606
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farming-labs/docs",
3
- "version": "0.1.36",
3
+ "version": "0.1.38",
4
4
  "description": "Modern, flexible MDX-based docs framework — core types, config, and CLI",
5
5
  "keywords": [
6
6
  "docs",