@farming-labs/theme 0.2.75 → 0.2.78

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.
Files changed (2) hide show
  1. package/dist/docs-api.mjs +98 -30
  2. package/package.json +2 -2
package/dist/docs-api.mjs CHANGED
@@ -4,8 +4,8 @@ import { BoundedRouteCache } from "./bounded-route-cache.mjs";
4
4
  import fs from "node:fs";
5
5
  import path from "node:path";
6
6
  import matter from "gray-matter";
7
- import { DEFAULT_A2A_AGENT_CARD_ROUTE, DEFAULT_AGENT_SKILLS_ARCHIVE_ROUTE_PATTERN, DEFAULT_AGENT_SKILLS_INDEX_ROUTE, DEFAULT_AGENT_SKILLS_ROUTE_PREFIX, DEFAULT_API_CATALOG_ROUTE, DEFAULT_LEGACY_SKILLS_INDEX_ROUTE, DEFAULT_SITEMAP_MD_DOCS_ROUTE, DOCS_AGENT_MANIFEST_FORMAT, DOCS_AGENT_MANIFEST_SCHEMA_URI, DOCS_AGENT_MANIFEST_VERSION, DOCS_MARKDOWN_SECTION_INDEX_FORMAT, acceptsDocsMarkdown, buildDocsAskAIContext, buildDocsConfigMap, buildDocsDiagnostics, createDocsAgentTraceContext, createDocsAgentTraceId, createDocsMarkdownResponse, createDocsRobotsResponse, createDocsSitemapResponse, createDocsStandardsDiscoveryResponse, detectDocsMarkdownAgentRequest, emitDocsAgentTraceEvent, emitDocsAnalyticsEvent, emitDocsTelemetryAgentSurfaceEvent, emitDocsTelemetryProjectEvent, formatDocsAskAIPackageHints, getDocsAgentManifestLinkHeader, getDocsDiscoveryLinkHeader, getDocsLlmsTxtMaxCharsIssue, getDocsMcpProtectedResourceMetadataRoutes, hasDocsMarkdownSignatureAgent, inferDocsTelemetryAgentSurface, isDocsConfigRequest, isDocsDiagnosticsRequest, normalizeDocsRelated, normalizePageAgentFrontmatter, performDocsSearch, performDocsSearchWithMetadata, renderDocsLlmsTxt, renderDocsMarkdownDocument, resolveAskAISearchRequestConfig, resolveChangelogConfig, resolveDocsAgentContractMcpTools, resolveDocsAudienceMdxContent, resolveDocsI18n, resolveDocsLlmsTxtRequest, resolveDocsLlmsTxtSections, resolveDocsLocale, resolveDocsMetadataBaseUrl, resolveDocsPublishedAgentSkill, resolveDocsRequestApiRoute, resolveDocsRetrievalLastModified, resolveDocsSearchAudience, resolveDocsSearchFilters, resolveDocsSitemapConfig, resolveDocsStandardsDiscoveryRequest, resolvePageSidebarFolderIndexBehavior, resolveSearchRequestConfig, selectDocsLlmsTxtContent, stripGeneratedAgentProvenance } from "@farming-labs/docs";
8
- import { buildApiReferenceOpenApiDocumentAsync, createDocsMcpHttpHandler, createFilesystemDocsMcpSource, readDocsSitemapManifest, resolveApiReferenceConfig, resolveConfiguredAgentSkills, resolveDocsMcpConfig } from "@farming-labs/docs/server";
7
+ import { DEFAULT_A2A_AGENT_CARD_ROUTE, DEFAULT_AGENT_SKILLS_ARCHIVE_ROUTE_PATTERN, DEFAULT_AGENT_SKILLS_INDEX_ROUTE, DEFAULT_AGENT_SKILLS_ROUTE_PREFIX, DEFAULT_API_CATALOG_ROUTE, DEFAULT_LEGACY_SKILLS_INDEX_ROUTE, DEFAULT_SITEMAP_MD_DOCS_ROUTE, DOCS_AGENT_MANIFEST_FORMAT, DOCS_AGENT_MANIFEST_SCHEMA_URI, DOCS_AGENT_MANIFEST_VERSION, DOCS_CONTENT_CHANGES_FORMAT, DOCS_CONTENT_CHANGES_RESPONSE_VALUE, DOCS_MARKDOWN_SECTION_INDEX_FORMAT, acceptsDocsMarkdown, buildDocsAskAIContext, buildDocsConfigMap, buildDocsDiagnostics, buildDocsSearchFacets, createDocsAgentTraceContext, createDocsAgentTraceId, createDocsContentChangeFeed, createDocsContentChangesHttpResponse, createDocsMarkdownResponse, createDocsRobotsResponse, createDocsSitemapResponse, createDocsStandardsDiscoveryResponse, detectDocsMarkdownAgentRequest, emitDocsAgentTraceEvent, emitDocsAnalyticsEvent, emitDocsTelemetryAgentSurfaceEvent, emitDocsTelemetryProjectEvent, formatDocsAskAIPackageHints, getDocsAgentManifestLinkHeader, getDocsDiscoveryLinkHeader, getDocsLlmsTxtMaxCharsIssue, getDocsMcpProtectedResourceMetadataRoutes, hasDocsMarkdownSignatureAgent, inferDocsTelemetryAgentSurface, isDocsConfigRequest, isDocsContentChangesRequest, isDocsDiagnosticsRequest, normalizeDocsRelated, normalizePageAgentFrontmatter, performDocsSearch, performDocsSearchWithMetadata, renderDocsLlmsTxt, renderDocsMarkdownDocument, resolveAskAISearchRequestConfig, resolveChangelogConfig, resolveDocsAgentContractMcpTools, resolveDocsAudienceMdxContent, resolveDocsContentChangesConfig, resolveDocsI18n, resolveDocsLlmsTxtRequest, resolveDocsLlmsTxtSections, resolveDocsLocale, resolveDocsMetadataBaseUrl, resolveDocsPublishedAgentSkill, resolveDocsRequestApiRoute, resolveDocsRetrievalLastModified, resolveDocsSearchAudience, resolveDocsSearchError, resolveDocsSearchRequest, resolveDocsSitemapConfig, resolveDocsStandardsDiscoveryRequest, resolvePageSidebarFolderIndexBehavior, resolveSearchRequestConfig, selectDocsLlmsTxtContent, stripGeneratedAgentProvenance } from "@farming-labs/docs";
8
+ import { buildApiReferenceOpenApiDocumentAsync, createDocsMcpHttpHandler, createFilesystemDocsMcpSource, readDocsSitemapManifest, resolveApiReferenceOpenApiDiscovery, resolveConfiguredAgentSkills, resolveDocsMcpConfig } from "@farming-labs/docs/server";
9
9
 
10
10
  //#region src/docs-api.ts
11
11
  /**
@@ -197,22 +197,11 @@ function isRobotsDiscoveryEnabled(robots) {
197
197
  function isApiReferenceOpenApiRequest(url) {
198
198
  return url.searchParams.get("format")?.trim() === "openapi";
199
199
  }
200
- function resolveApiReferenceOpenApiDiscovery(value) {
201
- const apiReference = resolveApiReferenceConfig(value);
202
- if (!apiReference.enabled) return { enabled: false };
203
- return {
204
- enabled: true,
205
- url: `${DEFAULT_DOCS_API_ROUTE}?format=openapi`,
206
- urlSource: "default",
207
- source: apiReference.specUrl ? "configured" : "generated",
208
- specUrl: apiReference.specUrl,
209
- apiReferencePath: `/${apiReference.path}`
210
- };
211
- }
212
- function buildAgentSpec({ origin, entry, apiRoute, apiCatalog, i18n, search, mcp, feedback, llms, sitemap, robots, openapi, publishedSkills = [], agentCard }) {
200
+ function buildAgentSpec({ origin, entry, apiRoute, apiCatalog, i18n, search, contentChanges, mcp, feedback, llms, sitemap, robots, openapi, publishedSkills = [], agentCard }) {
213
201
  const normalizedEntry = normalizePathSegment(entry) || "docs";
214
202
  const localesEnabled = i18n !== null;
215
203
  const searchEnabled = isSearchEnabled(search);
204
+ const contentChangesEnabled = resolveDocsContentChangesConfig(contentChanges).enabled;
216
205
  const sitemapConfig = resolveDocsSitemapConfig(sitemap, { baseUrl: llms.baseUrl });
217
206
  const robotsEnabled = isRobotsDiscoveryEnabled(robots);
218
207
  const llmsSections = resolveDocsLlmsTxtSections(llms);
@@ -250,6 +239,7 @@ function buildAgentSpec({ origin, entry, apiRoute, apiCatalog, i18n, search, mcp
250
239
  skills: true,
251
240
  mcp: mcp.enabled,
252
241
  search: searchEnabled,
242
+ contentChanges: contentChangesEnabled,
253
243
  sitemap: sitemapConfig.enabled,
254
244
  robots: robotsEnabled,
255
245
  structuredData: true,
@@ -272,6 +262,7 @@ function buildAgentSpec({ origin, entry, apiRoute, apiCatalog, i18n, search, mcp
272
262
  agentSpecQuery: `${apiRoute}?agent=spec`,
273
263
  agents: `${apiRoute}?format=agents`,
274
264
  openapi: `${apiRoute}?format=openapi`,
265
+ ...contentChangesEnabled ? { contentChanges: `${apiRoute}?audience=agent&response=${DOCS_CONTENT_CHANGES_RESPONSE_VALUE}` } : {},
275
266
  ...apiCatalog ? {
276
267
  apiCatalog: DEFAULT_API_CATALOG_ROUTE,
277
268
  apiCatalogQuery: `${apiRoute}?format=api-catalog`
@@ -406,13 +397,16 @@ function buildAgentSpec({ origin, entry, apiRoute, apiCatalog, i18n, search, mcp
406
397
  endpoint: `${apiRoute}?query={query}`,
407
398
  agentEndpoint: `${apiRoute}?query={query}&audience=agent`,
408
399
  structuredAgentEndpoint: `${apiRoute}?query={query}&audience=agent&response=structured`,
400
+ facetsEndpoint: `${apiRoute}?audience=agent&response=facets`,
409
401
  method: "GET",
410
402
  queryParam: "query",
411
403
  localeParam: "lang",
412
404
  audienceParam: "audience",
413
405
  responseParam: "response",
414
406
  structuredResponseValue: "structured",
407
+ facetsResponseValue: "facets",
415
408
  responseFormat: "docs-search.v1",
409
+ facetsResponseFormat: "docs-search-facets.v1",
416
410
  filterParams: {
417
411
  framework: "framework",
418
412
  version: "version",
@@ -426,9 +420,36 @@ function buildAgentSpec({ origin, entry, apiRoute, apiCatalog, i18n, search, mcp
426
420
  "tags"
427
421
  ],
428
422
  warningsField: "warnings",
423
+ facetParam: "facet",
424
+ limitParam: "limit",
425
+ cursorParam: "cursor",
426
+ nextCursorField: "nextCursor",
427
+ hasMoreField: "hasMore",
428
+ totalField: "total",
429
429
  defaultAudience: "human",
430
430
  supportedAudiences: ["human", "agent"]
431
431
  },
432
+ contentChanges: {
433
+ enabled: contentChangesEnabled,
434
+ endpoint: contentChangesEnabled ? `${apiRoute}?audience=agent&response=${DOCS_CONTENT_CHANGES_RESPONSE_VALUE}` : null,
435
+ method: "GET",
436
+ audienceParam: "audience",
437
+ defaultAudience: "agent",
438
+ supportedAudiences: ["human", "agent"],
439
+ responseParam: "response",
440
+ responseValue: DOCS_CONTENT_CHANGES_RESPONSE_VALUE,
441
+ sinceParam: "since",
442
+ format: DOCS_CONTENT_CHANGES_FORMAT,
443
+ generationField: "indexGeneration",
444
+ resetRequiredField: "resetRequired",
445
+ modes: [
446
+ "snapshot",
447
+ "delta",
448
+ "reset"
449
+ ],
450
+ bodyFree: true,
451
+ etag: true
452
+ },
432
453
  agents: {
433
454
  enabled: true,
434
455
  file: "AGENTS.md",
@@ -1302,7 +1323,7 @@ function renderMarkdownDocument(page, options = {}) {
1302
1323
  sitemap: options.sitemap
1303
1324
  });
1304
1325
  }
1305
- function renderSkillDocument({ origin, entry, apiRoute, apiCatalog, search, mcp, feedback, llms, sitemap, robots, openapi }) {
1326
+ function renderSkillDocument({ origin, entry, apiRoute, apiCatalog, search, contentChanges, mcp, feedback, llms, sitemap, robots, openapi }) {
1306
1327
  const normalizedEntry = normalizePathSegment(entry) || "docs";
1307
1328
  const siteTitle = compactSkillText(llms.siteTitle ?? "Documentation");
1308
1329
  const siteDescription = llms.siteDescription ? compactSkillText(llms.siteDescription) : void 0;
@@ -1326,7 +1347,8 @@ function renderSkillDocument({ origin, entry, apiRoute, apiCatalog, search, mcp,
1326
1347
  lines.push("", "## When To Use", "Use this skill when you need to read or implement against this documentation site.", "", "## Start Here", `- Fetch ${DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE}; fall back to ${DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE} or ${agentSpecRoute}.`);
1327
1348
  if (apiCatalog) lines.push(`- Fetch ${DEFAULT_API_CATALOG_ROUTE} for the standards-based API catalog.`);
1328
1349
  lines.push(`- Fetch ${DEFAULT_AGENT_SKILLS_INDEX_ROUTE} to discover the published site skill and verify its SHA-256 digest.`, `- Fetch /${normalizedEntry}.md for the root docs page.`, `- Fetch /${normalizedEntry}/{slug}.md for page-specific context.`, `- Fetch /${normalizedEntry}/{slug}.md?sections to list section IDs and size estimates before requesting ?section={id}.`, "- You can also request text/markdown from normal page URLs.");
1329
- if (searchEnabled) lines.push(`- Search with ${apiRoute}?query={query}&audience=agent&response=structured when you do not know the page; add framework, version, package, or repeated tags filters when scope matters.`);
1350
+ if (searchEnabled) lines.push(`- Discover valid search filters with ${apiRoute}?audience=agent&response=facets before choosing framework, version, package, or tag values; continue a large field with facet={field}, limit={limit}, and its nextCursor.`, `- Search with ${apiRoute}?query={query}&audience=agent&response=structured when you do not know the page; add framework, version, package, or repeated tags filters when scope matters.`);
1351
+ if (resolveDocsContentChangesConfig(contentChanges).enabled) lines.push(`- Synchronize changed documents with ${apiRoute}?audience=agent&response=${DOCS_CONTENT_CHANGES_RESPONSE_VALUE}; pass indexGeneration as since on the next request and clear stale state when resetRequired is true.`);
1330
1352
  if (openapi.enabled && openapiUrl) lines.push(`- Fetch ${openapiUrl} for the machine-readable OpenAPI schema before scraping API reference pages.`);
1331
1353
  if (llms.enabled) {
1332
1354
  lines.push(`- Use ${DEFAULT_LLMS_TXT_ROUTE} for a compact docs index.`, `- Use ${DEFAULT_LLMS_FULL_TXT_ROUTE} for full markdown context.`);
@@ -1362,7 +1384,7 @@ function renderSkillDocument({ origin, entry, apiRoute, apiCatalog, search, mcp,
1362
1384
  lines.push("", "## Reusable Framework Skills", "For framework setup, CLI, page actions, Ask AI, or configuration work, install the reusable Farming Labs skills:", "", "```sh", "npx skills add farming-labs/docs", "```");
1363
1385
  return lines.join("\n");
1364
1386
  }
1365
- function renderAgentsDocument({ origin, entry, apiRoute, apiCatalog, search, mcp, feedback, llms, sitemap, robots, openapi }) {
1387
+ function renderAgentsDocument({ origin, entry, apiRoute, apiCatalog, search, contentChanges, mcp, feedback, llms, sitemap, robots, openapi }) {
1366
1388
  const normalizedEntry = normalizePathSegment(entry) || "docs";
1367
1389
  const siteTitle = compactSkillText(llms.siteTitle ?? "Documentation");
1368
1390
  const siteDescription = llms.siteDescription ? compactSkillText(llms.siteDescription) : void 0;
@@ -1391,7 +1413,8 @@ function renderAgentsDocument({ origin, entry, apiRoute, apiCatalog, search, mcp
1391
1413
  if (sitemapConfig.xml.enabled) lines.push(`- Use ${sitemapConfig.xml.route} for canonical URLs and freshness metadata.`);
1392
1414
  }
1393
1415
  if (robotsEnabled) lines.push(`- Check ${DEFAULT_ROBOTS_TXT_ROUTE} before crawling broadly.`);
1394
- if (searchEnabled) lines.push(`- Search with ${apiRoute}?query={query}&audience=agent&response=structured when the route is unknown; add framework, version, package, or repeated tags filters when scope matters.`);
1416
+ if (searchEnabled) lines.push(`- Discover valid search filters with ${apiRoute}?audience=agent&response=facets before choosing framework, version, package, or tag values; continue a large field with facet={field}, limit={limit}, and its nextCursor.`, `- Search with ${apiRoute}?query={query}&audience=agent&response=structured when the route is unknown; add framework, version, package, or repeated tags filters when scope matters.`);
1417
+ if (resolveDocsContentChangesConfig(contentChanges).enabled) lines.push(`- Synchronize changed documents with ${apiRoute}?audience=agent&response=${DOCS_CONTENT_CHANGES_RESPONSE_VALUE}; pass indexGeneration as since on the next request and clear stale state when resetRequired is true.`);
1395
1418
  if (openapi.enabled && openapiUrl) lines.push(`- Fetch ${openapiUrl} before scraping API reference pages; prefer schemas over prose.`);
1396
1419
  if (mcp.enabled) lines.push(`- Use MCP at ${DEFAULT_MCP_PUBLIC_ROUTE} or ${DEFAULT_MCP_WELL_KNOWN_ROUTE} when your environment supports MCP tools.`);
1397
1420
  if (feedback.enabled) lines.push(`- Read ${feedback.schemaRoute} before posting feedback to ${feedback.route}.`);
@@ -2462,8 +2485,10 @@ function readApiReferenceConfig(root) {
2462
2485
  const pathMatch = block.match(/path\s*:\s*["']([^"']+)["']/);
2463
2486
  const routeRootMatch = block.match(/routeRoot\s*:\s*["']([^"']+)["']/);
2464
2487
  const specUrlMatch = block.match(/specUrl\s*:\s*["']([^"']+)["']/);
2488
+ const catalogTargetsMatch = block.match(/catalogTargets\s*:\s*\[([\s\S]*?)\]/);
2465
2489
  const rendererMatch = block.match(/renderer\s*:\s*["'](fumadocs|scalar)["']/);
2466
2490
  const excludeMatch = block.match(/exclude\s*:\s*\[([\s\S]*?)\]/);
2491
+ const catalogTargets = catalogTargetsMatch?.[1] === void 0 ? void 0 : Array.from(catalogTargetsMatch[1].matchAll(/["']([^"']+)["']/g)).map((match) => match[1]).filter(Boolean);
2467
2492
  const exclude = excludeMatch?.[1] === void 0 ? void 0 : Array.from(excludeMatch[1].matchAll(/["']([^"']+)["']/g)).map((match) => match[1]).filter(Boolean);
2468
2493
  const renderer = rendererMatch?.[1] === "fumadocs" || rendererMatch?.[1] === "scalar" ? rendererMatch[1] : void 0;
2469
2494
  return {
@@ -2471,6 +2496,7 @@ function readApiReferenceConfig(root) {
2471
2496
  path: pathMatch?.[1],
2472
2497
  routeRoot: routeRootMatch?.[1],
2473
2498
  specUrl: specUrlMatch?.[1],
2499
+ catalogTargets,
2474
2500
  renderer,
2475
2501
  exclude
2476
2502
  };
@@ -2532,6 +2558,8 @@ function createDocsAPI(options) {
2532
2558
  apiRoute: configuredApiRoute
2533
2559
  } : cloudConfig;
2534
2560
  const searchConfig = options?.search;
2561
+ const contentChangesConfig = resolveDocsContentChangesConfig(options?.agent?.contentChanges, { staticExport: options?.staticExport === true });
2562
+ const contentChangeFeed = createDocsContentChangeFeed(options?.agent?.contentChanges);
2535
2563
  const llmsConfig = resolveLlmsTxtConfig(options?.llmsTxt, readLlmsTxtConfig(root));
2536
2564
  const apiCatalogEnabled = options?.apiCatalog ?? llmsConfig.apiCatalog ?? true;
2537
2565
  const sitemapConfig = options?.sitemap ?? readSitemapConfig(root);
@@ -2564,6 +2592,7 @@ function createDocsAPI(options) {
2564
2592
  contentDir,
2565
2593
  telemetry: options?.telemetry,
2566
2594
  search: searchConfig,
2595
+ agent: options?.agent,
2567
2596
  ai: aiConfig,
2568
2597
  cloud: effectiveCloudConfig,
2569
2598
  i18n: i18nConfig ?? void 0,
@@ -2585,6 +2614,7 @@ function createDocsAPI(options) {
2585
2614
  setConfigMapFallback("entry", entry);
2586
2615
  setConfigMapFallback("ai", Object.keys(aiConfig).length > 0 ? aiConfig : void 0);
2587
2616
  setConfigMapFallback("search", searchConfig);
2617
+ setConfigMapFallback("agent", options?.agent);
2588
2618
  setConfigMapFallback("i18n", i18nConfig ?? void 0);
2589
2619
  setConfigMapFallback("mcp", rawMcpConfig);
2590
2620
  setConfigMapFallback("apiReference", apiReferenceConfig);
@@ -2782,6 +2812,7 @@ function createDocsAPI(options) {
2782
2812
  apiCatalog: apiCatalogEnabled,
2783
2813
  i18n,
2784
2814
  search: searchConfig,
2815
+ contentChanges: contentChangesConfig.enabled,
2785
2816
  mcp: mcpConfig,
2786
2817
  feedback: agentFeedbackConfig,
2787
2818
  llms: llmsConfig,
@@ -2802,6 +2833,7 @@ function createDocsAPI(options) {
2802
2833
  apiRoute,
2803
2834
  i18n,
2804
2835
  search: searchConfig,
2836
+ contentChanges: contentChangesConfig.enabled,
2805
2837
  mcp: mcpConfig,
2806
2838
  feedback: agentFeedbackConfig,
2807
2839
  llms: llmsConfig,
@@ -2867,6 +2899,7 @@ function createDocsAPI(options) {
2867
2899
  apiCatalog: apiCatalogEnabled,
2868
2900
  i18n,
2869
2901
  search: searchConfig,
2902
+ contentChanges: contentChangesConfig.enabled,
2870
2903
  mcp: mcpConfig,
2871
2904
  feedback: agentFeedbackConfig,
2872
2905
  llms: llmsConfig,
@@ -2882,6 +2915,7 @@ function createDocsAPI(options) {
2882
2915
  apiCatalog: apiCatalogEnabled,
2883
2916
  i18n,
2884
2917
  search: searchConfig,
2918
+ contentChanges: contentChangesConfig.enabled,
2885
2919
  mcp: mcpConfig,
2886
2920
  feedback: agentFeedbackConfig,
2887
2921
  llms: llmsConfig,
@@ -2897,6 +2931,23 @@ function createDocsAPI(options) {
2897
2931
  "X-Robots-Tag": "noindex"
2898
2932
  } });
2899
2933
  }
2934
+ if (isDocsContentChangesRequest(url)) {
2935
+ if (!contentChangesConfig.enabled) return new Response("Not Found", {
2936
+ status: 404,
2937
+ headers: {
2938
+ "Content-Type": "text/plain; charset=utf-8",
2939
+ "X-Robots-Tag": "noindex"
2940
+ }
2941
+ });
2942
+ return createDocsContentChangesHttpResponse({
2943
+ request,
2944
+ feed: contentChangeFeed,
2945
+ pages: getIndexes(ctx),
2946
+ search: searchConfig,
2947
+ locale: ctx.locale,
2948
+ baseUrl: markdownMetadataBaseUrl || url.origin
2949
+ });
2950
+ }
2900
2951
  if (isApiReferenceOpenApiRequest(url)) {
2901
2952
  if (!openapiDiscovery.enabled) return new Response("Not Found", {
2902
2953
  status: 404,
@@ -2962,6 +3013,7 @@ function createDocsAPI(options) {
2962
3013
  apiRoute: requestApiRoute,
2963
3014
  apiCatalog: apiCatalogEnabled,
2964
3015
  search: searchConfig,
3016
+ contentChanges: contentChangesConfig.enabled,
2965
3017
  mcp: mcpConfig,
2966
3018
  feedback: agentFeedbackConfig,
2967
3019
  llms: llmsConfig,
@@ -2994,6 +3046,7 @@ function createDocsAPI(options) {
2994
3046
  apiCatalog: apiCatalogEnabled,
2995
3047
  i18n,
2996
3048
  search: searchConfig,
3049
+ contentChanges: contentChangesConfig.enabled,
2997
3050
  mcp: mcpConfig,
2998
3051
  feedback: agentFeedbackConfig,
2999
3052
  llms: llmsConfig,
@@ -3166,9 +3219,16 @@ function createDocsAPI(options) {
3166
3219
  }
3167
3220
  const query = url.searchParams.get("query")?.trim();
3168
3221
  const audience = resolveDocsSearchAudience(url.searchParams.get("audience"));
3169
- const filters = resolveDocsSearchFilters(url.searchParams);
3170
- const structured = url.searchParams.get("response") === "structured";
3171
- if (!query && !structured) return new Response("[]", { headers: { "Content-Type": "application/json" } });
3222
+ let searchRequest;
3223
+ try {
3224
+ searchRequest = resolveDocsSearchRequest(url.searchParams);
3225
+ } catch (error) {
3226
+ const searchError = resolveDocsSearchError(error);
3227
+ if (!searchError) throw error;
3228
+ return Response.json({ error: searchError }, { status: 400 });
3229
+ }
3230
+ const { filters, structured, facets, facet, cursor, limit } = searchRequest;
3231
+ if (!query && !structured && !facets) return new Response("[]", { headers: { "Content-Type": "application/json" } });
3172
3232
  const searchOptions = {
3173
3233
  pages: getIndexes(ctx),
3174
3234
  query: query ?? "",
@@ -3179,19 +3239,27 @@ function createDocsAPI(options) {
3179
3239
  pathname: url.searchParams.get("pathname") ?? void 0,
3180
3240
  siteTitle: llmsConfig.siteTitle ?? "Documentation",
3181
3241
  baseUrl: markdownMetadataBaseUrl || url.origin,
3182
- syncBaseUrl: markdownMetadataBaseUrl ?? null
3242
+ syncBaseUrl: markdownMetadataBaseUrl ?? null,
3243
+ facet,
3244
+ cursor,
3245
+ limit
3183
3246
  };
3184
- if (!query) {
3185
- const searchResponse = structured ? await performDocsSearchWithMetadata(searchOptions) : [];
3186
- return new Response(JSON.stringify(searchResponse), { headers: { "Content-Type": "application/json" } });
3187
- }
3188
3247
  const searchStartedAt = Date.now();
3189
- const searchResponse = structured ? await performDocsSearchWithMetadata(searchOptions) : await performDocsSearch(searchOptions);
3190
- const resultCount = Array.isArray(searchResponse) ? searchResponse.length : searchResponse.resultCount;
3248
+ let searchResponse;
3249
+ try {
3250
+ searchResponse = facets ? await buildDocsSearchFacets(searchOptions) : structured ? await performDocsSearchWithMetadata(searchOptions) : query ? await performDocsSearch(searchOptions) : [];
3251
+ } catch (error) {
3252
+ const searchError = resolveDocsSearchError(error);
3253
+ if (!searchError) throw error;
3254
+ return Response.json({ error: searchError }, { status: 400 });
3255
+ }
3256
+ if (facets) return Response.json(searchResponse, { headers: { "Cache-Control": "public, max-age=60, stale-while-revalidate=300" } });
3257
+ if (!query) return Response.json(searchResponse);
3258
+ const resultCount = Array.isArray(searchResponse) ? searchResponse.length : "resultCount" in searchResponse ? searchResponse.resultCount : searchResponse.matchedPageCount;
3191
3259
  await emitDocsAnalyticsEvent(analytics, {
3192
3260
  type: "api_search",
3193
3261
  source: "server",
3194
- url: request.url,
3262
+ url: `${url.origin}${url.pathname}`,
3195
3263
  path: url.pathname,
3196
3264
  locale: ctx.locale,
3197
3265
  input: { query },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farming-labs/theme",
3
- "version": "0.2.75",
3
+ "version": "0.2.78",
4
4
  "description": "Theme package for @farming-labs/docs — layout, provider, MDX components, and styles",
5
5
  "keywords": [
6
6
  "docs",
@@ -148,7 +148,7 @@
148
148
  "tsdown": "^0.20.3",
149
149
  "typescript": "^5.9.3",
150
150
  "vitest": "^4.1.8",
151
- "@farming-labs/docs": "0.2.75"
151
+ "@farming-labs/docs": "0.2.78"
152
152
  },
153
153
  "peerDependencies": {
154
154
  "@farming-labs/docs": ">=0.0.1",