@farming-labs/next 0.2.115 → 0.2.117

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.
@@ -34,8 +34,13 @@ declare function createNextApiReferencePage(config: DocsConfig): (props?: {
34
34
  }) => Promise<react_jsx_runtime0.JSX.Element>;
35
35
  declare function createNextApiReferenceLayout(config: DocsConfig): (props: {
36
36
  children: React.ReactNode;
37
+ params?: Promise<{
38
+ slug?: string[];
39
+ }> | {
40
+ slug?: string[];
41
+ };
37
42
  }) => Promise<react_jsx_runtime0.JSX.Element>;
38
- declare function getNextApiReferenceSourceState(config: DocsConfig): Promise<NextApiReferenceSourceState>;
39
- declare function getNextApiReferenceMode(config: DocsConfig): "fumadocs" | "scalar";
43
+ declare function getNextApiReferenceSourceState(config: DocsConfig, versionId?: string): Promise<NextApiReferenceSourceState>;
44
+ declare function getNextApiReferenceMode(config: DocsConfig): "farming-labs" | "fumadocs" | "scalar";
40
45
  //#endregion
41
46
  export { NextApiReferenceSourceState, buildNextOpenApiDocument, createNextApiReference, createNextApiReferenceLayout, createNextApiReferencePage, flattenApiReferencePageTreeForSidebar, getNextApiReferenceMode, getNextApiReferenceSourceState, resolveApiReferenceConfig, withNextApiReferenceBanner };
@@ -1,7 +1,8 @@
1
1
  import DocsClientCallbacks from "./client-callbacks.mjs";
2
2
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
3
3
  import { join, relative } from "node:path";
4
- import { buildApiReferenceOpenApiDocumentAsync, buildApiReferencePageTitle, buildApiReferenceScalarCss, resolveApiReferenceConfig, resolveApiReferenceRenderer } from "@farming-labs/docs/server";
4
+ import { resolveDocsMetadataBaseUrl } from "@farming-labs/docs";
5
+ import { buildApiReferenceHtmlDocumentAsync, buildApiReferenceOpenApiDocumentAsync, buildApiReferencePageTitle, buildApiReferenceScalarCss, createApiReferenceOperationMarkdownResponse, resolveApiReferenceConfig, resolveApiReferenceRenderer, resolveApiReferenceVersion, resolveApiReferenceVersionFromPathname } from "@farming-labs/docs/server";
5
6
  import { ApiReference } from "@scalar/nextjs-api-reference";
6
7
  import { SidebarTabsDropdown } from "fumadocs-ui/components/sidebar/tabs/dropdown";
7
8
  import Link from "next/link";
@@ -235,11 +236,12 @@ function buildOpenApiPaths(routes) {
235
236
  return paths;
236
237
  }
237
238
  function buildNextOpenApiDocument(config) {
238
- if (resolveApiReferenceConfig(config.apiReference).specUrl) return {
239
+ const apiReference = resolveApiReferenceConfig(config.apiReference);
240
+ if (apiReference.specUrl || apiReference.versions.length > 0) return {
239
241
  openapi: "3.1.0",
240
242
  info: {
241
243
  title: "API Reference",
242
- description: "Remote OpenAPI specs are resolved at request time through the Next.js API reference renderer.",
244
+ description: "Configured OpenAPI specs are resolved at request time through the Next.js API reference renderer.",
243
245
  version: "0.0.0"
244
246
  },
245
247
  servers: [{ url: "/" }],
@@ -552,14 +554,39 @@ function createNextApiReference(config) {
552
554
  const apiReference = resolveApiReferenceConfig(config.apiReference);
553
555
  return async (request) => {
554
556
  if (!apiReference.enabled) return new Response("Not Found", { status: 404 });
557
+ const requestUrl = request ? new URL(request.url) : void 0;
558
+ if (request && requestUrl) {
559
+ const markdownResponse = await createApiReferenceOperationMarkdownResponse(config, {
560
+ request,
561
+ framework: "next",
562
+ rootDir: process.cwd(),
563
+ baseUrl: getOriginFromRequest(request),
564
+ origin: resolveDocsMetadataBaseUrl(config) || getOriginFromRequest(request),
565
+ sitemap: config.sitemap,
566
+ okf: config.agent?.okf
567
+ });
568
+ if (markdownResponse) return markdownResponse;
569
+ }
570
+ const version = (requestUrl ? resolveApiReferenceVersionFromPathname(config.apiReference, requestUrl.pathname) : void 0) ?? resolveApiReferenceVersion(apiReference);
571
+ if (resolveApiReferenceRenderer(config.apiReference, "next") === "farming-labs") {
572
+ const html = await buildApiReferenceHtmlDocumentAsync(config, {
573
+ framework: "next",
574
+ rootDir: process.cwd(),
575
+ baseUrl: getOriginFromRequest(request),
576
+ version: version?.id
577
+ });
578
+ return new Response(html, { headers: { "Content-Type": "text/html; charset=utf-8" } });
579
+ }
555
580
  const document = await buildApiReferenceOpenApiDocumentAsync(config, {
556
581
  framework: "next",
557
582
  rootDir: process.cwd(),
558
- baseUrl: getOriginFromRequest(request)
583
+ baseUrl: getOriginFromRequest(request),
584
+ version: version?.id
559
585
  });
586
+ const basePath = `/${apiReference.path}${version ? `/${encodeURIComponent(version.id)}` : ""}`;
560
587
  return ApiReference({
561
- pageTitle: buildApiReferencePageTitle(config, "API Reference"),
562
- title: "API Reference",
588
+ pageTitle: buildApiReferencePageTitle(config, version ? `API Reference – ${version.label}` : "API Reference"),
589
+ title: version ? `API Reference – ${version.label}` : "API Reference",
563
590
  content: document,
564
591
  theme: "deepSpace",
565
592
  layout: "modern",
@@ -567,7 +594,7 @@ function createNextApiReference(config) {
567
594
  forceDarkModeState: getForcedMode(config),
568
595
  hideDarkModeToggle: isThemeToggleHidden(config),
569
596
  customCss: buildApiReferenceScalarCss(config),
570
- pathRouting: { basePath: `/${apiReference.path}` },
597
+ pathRouting: { basePath },
571
598
  showSidebar: true,
572
599
  defaultOpenFirstTag: true,
573
600
  tagsSorter: "alpha",
@@ -591,8 +618,10 @@ function getOpenApiInfo(document) {
591
618
  function createNextApiReferencePage(config) {
592
619
  return async function NextApiReferencePage(props) {
593
620
  const [{ createAPIPage }, { DocsDescription, DocsPage, DocsTitle }] = await Promise.all([import("fumadocs-openapi/ui"), import("fumadocs-ui/layouts/notebook/page")]);
594
- const { info, pages, server, source } = await getNextApiReferenceSourceState(config);
595
621
  const slug = (props?.params ? await props.params : void 0)?.slug ?? [];
622
+ const version = resolveApiReferenceVersion(resolveApiReferenceConfig(config.apiReference), slug[0]);
623
+ const pageSlug = version ? slug.slice(1) : slug;
624
+ const { info, pages, server, source } = await getNextApiReferenceSourceState(config, version?.id);
596
625
  if (pages.length === 0) return /* @__PURE__ */ jsxs(DocsPage, {
597
626
  full: true,
598
627
  children: [
@@ -607,10 +636,10 @@ function createNextApiReferencePage(config) {
607
636
  })
608
637
  ]
609
638
  });
610
- const page = slug.length === 0 ? pages[0] : source.getPage(slug);
639
+ const page = pageSlug.length === 0 ? pages[0] : source.getPage(pageSlug);
611
640
  if (!page || typeof page.data?.getAPIPageProps !== "function") notFound();
612
641
  const APIPage = createAPIPage(server, { content: { renderOperationLayout: renderApiReferenceOperationLayout } });
613
- const currentPageIndex = slug.length === 0 ? 0 : pages.findIndex((entry) => entry.url === page.url);
642
+ const currentPageIndex = pageSlug.length === 0 ? 0 : pages.findIndex((entry) => entry.url === page.url);
614
643
  const previousPage = currentPageIndex > 0 ? pages[currentPageIndex - 1] : void 0;
615
644
  const nextPage = currentPageIndex >= 0 && currentPageIndex < pages.length - 1 ? pages[currentPageIndex + 1] : void 0;
616
645
  return /* @__PURE__ */ jsxs(DocsPage, {
@@ -681,10 +710,12 @@ function createNextApiReferencePage(config) {
681
710
  function createNextApiReferenceLayout(config) {
682
711
  return async function NextApiReferenceLayout(props) {
683
712
  const { DocsLayout } = await import("fumadocs-ui/layouts/notebook");
684
- const { apiReference, primaryServerUrl, source } = await getNextApiReferenceSourceState(config);
713
+ const resolvedParams = props.params ? await props.params : void 0;
714
+ const version = resolveApiReferenceVersion(resolveApiReferenceConfig(config.apiReference), resolvedParams?.slug?.[0]);
715
+ const { apiReference, primaryServerUrl, source } = await getNextApiReferenceSourceState(config, version?.id);
685
716
  const sidebarTree = flattenApiReferencePageTreeForSidebar(source.getPageTree());
686
717
  const docsUrl = getDocsUrl(config);
687
- const apiUrl = `/${apiReference.path}`;
718
+ const apiUrl = `/${apiReference.path}${version ? `/${encodeURIComponent(version.id)}` : ""}`;
688
719
  const themeSwitch = resolveApiReferenceThemeSwitch(config.themeToggle);
689
720
  const themeStyle = resolveApiReferenceThemeStyle(config);
690
721
  const banner = mergeBanner(getExistingSidebarBanner(config), /* @__PURE__ */ jsx(ApiReferenceSwitcher, {
@@ -714,14 +745,16 @@ function createNextApiReferenceLayout(config) {
714
745
  });
715
746
  };
716
747
  }
717
- async function getNextApiReferenceSourceState(config) {
748
+ async function getNextApiReferenceSourceState(config, versionId) {
718
749
  const apiReference = resolveApiReferenceConfig(config.apiReference);
750
+ const version = resolveApiReferenceVersion(apiReference, versionId);
719
751
  const [{ createOpenAPI, openapiPlugin, openapiSource }, { loader }] = await Promise.all([import("fumadocs-openapi/server"), import("fumadocs-core/source")]);
720
752
  const baseUrl = await getOriginFromNextHeaders();
721
753
  const document = await buildApiReferenceOpenApiDocumentAsync(config, {
722
754
  framework: "next",
723
755
  rootDir: process.cwd(),
724
- baseUrl
756
+ baseUrl,
757
+ version: version?.id
725
758
  });
726
759
  const server = createOpenAPI({ input: async () => ({ main: document }) });
727
760
  const info = getOpenApiInfo(document);
@@ -734,7 +767,7 @@ async function getNextApiReferenceSourceState(config) {
734
767
  return slugifyApiReferencePageName(typeof operation?.summary === "string" && operation.summary.trim() ? operation.summary : typeof operation?.operationId === "string" && operation.operationId.trim() ? operation.operationId : `${output.item.method} ${output.item.path}`);
735
768
  }
736
769
  }), {
737
- baseUrl: `/${apiReference.path}`,
770
+ baseUrl: `/${apiReference.path}${version ? `/${encodeURIComponent(version.id)}` : ""}`,
738
771
  plugins: [openapiPlugin()]
739
772
  });
740
773
  return {
package/dist/config.mjs CHANGED
@@ -717,32 +717,32 @@ function readApiReferenceConfig(root) {
717
717
  if (content.match(/apiReference\s*:\s*false/)) return {
718
718
  enabled: false,
719
719
  path: "api-reference",
720
- renderer: "fumadocs",
720
+ renderer: "farming-labs",
721
721
  routeRoot: "api"
722
722
  };
723
723
  if (content.match(/apiReference\s*:\s*true/)) return {
724
724
  enabled: true,
725
725
  path: "api-reference",
726
- renderer: "fumadocs",
726
+ renderer: "farming-labs",
727
727
  routeRoot: "api"
728
728
  };
729
729
  const block = extractObjectLiteral(content, "apiReference");
730
730
  if (!block) continue;
731
731
  const enabledMatch = block.match(/enabled\s*:\s*(true|false)/);
732
732
  const pathMatch = block.match(/path\s*:\s*["']([^"']+)["']/);
733
- const rendererMatch = block.match(/renderer\s*:\s*["'](fumadocs|scalar)["']/);
733
+ const rendererMatch = block.match(/renderer\s*:\s*["'](farming-labs|fumadocs|scalar)["']/);
734
734
  const routeRootMatch = block.match(/routeRoot\s*:\s*["']([^"']+)["']/);
735
735
  return {
736
736
  enabled: enabledMatch ? enabledMatch[1] !== "false" : true,
737
737
  path: pathMatch?.[1]?.replace(/^\/+|\/+$/g, "") || "api-reference",
738
- renderer: rendererMatch?.[1] ?? "fumadocs",
738
+ renderer: rendererMatch?.[1] ?? "farming-labs",
739
739
  routeRoot: routeRootMatch?.[1]?.replace(/^\/+|\/+$/g, "") || "api"
740
740
  };
741
741
  } catch {
742
742
  return {
743
743
  enabled: false,
744
744
  path: "api-reference",
745
- renderer: "fumadocs",
745
+ renderer: "farming-labs",
746
746
  routeRoot: "api"
747
747
  };
748
748
  }
@@ -750,7 +750,7 @@ function readApiReferenceConfig(root) {
750
750
  return {
751
751
  enabled: false,
752
752
  path: "api-reference",
753
- renderer: "fumadocs",
753
+ renderer: "farming-labs",
754
754
  routeRoot: "api"
755
755
  };
756
756
  }
@@ -1601,6 +1601,72 @@ function buildDocsMarkdownRewrites(entry, docsPath) {
1601
1601
  }
1602
1602
  ];
1603
1603
  }
1604
+ function buildApiReferenceMarkdownRewrites(apiReference) {
1605
+ if (!apiReference.enabled) return [];
1606
+ const basePath = `/${apiReference.path}`;
1607
+ const markdownAcceptHeader = {
1608
+ type: "header",
1609
+ key: "accept",
1610
+ value: MARKDOWN_ACCEPT_HEADER_VALUE
1611
+ };
1612
+ const markdownSignatureAgentHeader = {
1613
+ type: "header",
1614
+ key: "signature-agent",
1615
+ value: ".+"
1616
+ };
1617
+ const markdownAgentUserAgentHeader = {
1618
+ type: "header",
1619
+ key: "user-agent",
1620
+ value: DOCS_AI_AGENT_USER_AGENT_HEADER_PATTERN
1621
+ };
1622
+ const markdownBotLikeUserAgentHeader = {
1623
+ type: "header",
1624
+ key: "user-agent",
1625
+ value: DOCS_BOT_LIKE_USER_AGENT_HEADER_PATTERN
1626
+ };
1627
+ const markdownTraditionalBotUserAgentHeader = {
1628
+ type: "header",
1629
+ key: "user-agent",
1630
+ value: DOCS_TRADITIONAL_BOT_USER_AGENT_HEADER_PATTERN
1631
+ };
1632
+ const markdownSecFetchModeHeader = {
1633
+ type: "header",
1634
+ key: "sec-fetch-mode"
1635
+ };
1636
+ return [{
1637
+ source: `${basePath}/operations/:operation`,
1638
+ path: `${apiReference.path}/operations/:operation`
1639
+ }, {
1640
+ source: `${basePath}/:version/operations/:operation`,
1641
+ path: `${apiReference.path}/:version/operations/:operation`
1642
+ }].flatMap((route) => [
1643
+ {
1644
+ source: `${route.source}.md`,
1645
+ destination: `/api/docs?format=markdown&path=${route.path}`
1646
+ },
1647
+ {
1648
+ source: route.source,
1649
+ has: [markdownAcceptHeader],
1650
+ destination: `/api/docs?format=markdown&path=${route.path}`
1651
+ },
1652
+ {
1653
+ source: route.source,
1654
+ has: [markdownSignatureAgentHeader],
1655
+ destination: `/api/docs?format=markdown&path=${route.path}`
1656
+ },
1657
+ {
1658
+ source: route.source,
1659
+ has: [markdownAgentUserAgentHeader],
1660
+ destination: `/api/docs?format=markdown&path=${route.path}`
1661
+ },
1662
+ {
1663
+ source: route.source,
1664
+ has: [markdownBotLikeUserAgentHeader],
1665
+ missing: [markdownTraditionalBotUserAgentHeader, markdownSecFetchModeHeader],
1666
+ destination: `/api/docs?format=markdown&path=${route.path}`
1667
+ }
1668
+ ]);
1669
+ }
1604
1670
  function buildAgentSpecRewrites() {
1605
1671
  return [
1606
1672
  {
@@ -1907,7 +1973,7 @@ function buildHiddenFolderRedirects(docsDir, docsPath) {
1907
1973
  scan(docsDir, []);
1908
1974
  return dedupeRedirects(redirects);
1909
1975
  }
1910
- function mergeDocsMarkdownRewrites(entry, docsPath, mcp, sitemap, agentFeedback, robots, result) {
1976
+ function mergeDocsMarkdownRewrites(entry, docsPath, apiReference, mcp, sitemap, agentFeedback, robots, result) {
1911
1977
  const autoBeforeFilesRewrites = [
1912
1978
  ...buildAgentSpecRewrites(),
1913
1979
  ...buildStandardsDiscoveryRewrites(),
@@ -1917,6 +1983,7 @@ function mergeDocsMarkdownRewrites(entry, docsPath, mcp, sitemap, agentFeedback,
1917
1983
  ...buildSitemapRewrites(sitemap),
1918
1984
  ...buildRobotsRewrites(robots),
1919
1985
  ...buildDocsMarkdownRewrites(entry, docsPath),
1986
+ ...buildApiReferenceMarkdownRewrites(apiReference),
1920
1987
  ...buildDocsPathRewrites(entry, docsPath),
1921
1988
  ...buildAgentFeedbackRewrites(agentFeedback)
1922
1989
  ];
@@ -2147,7 +2214,7 @@ function withDocs(nextConfig = {}, docsConfig) {
2147
2214
  if (!isStaticExport) {
2148
2215
  const existingRewrites = nextConfig.rewrites;
2149
2216
  nextConfig.rewrites = async () => {
2150
- return mergeDocsMarkdownRewrites(entry, docsPath, mcp, sitemap, agentFeedback, robots, typeof existingRewrites === "function" ? await existingRewrites() : existingRewrites);
2217
+ return mergeDocsMarkdownRewrites(entry, docsPath, apiReference, mcp, sitemap, agentFeedback, robots, typeof existingRewrites === "function" ? await existingRewrites() : existingRewrites);
2151
2218
  };
2152
2219
  const autoRedirects = buildHiddenFolderRedirects(docsContentRoot, docsPath);
2153
2220
  if (autoRedirects.length > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farming-labs/next",
3
- "version": "0.2.115",
3
+ "version": "0.2.117",
4
4
  "description": "Next.js adapter for @farming-labs/docs — MDX config wrapper",
5
5
  "keywords": [
6
6
  "docs",
@@ -109,8 +109,8 @@
109
109
  "tsdown": "^0.20.3",
110
110
  "typescript": "^5.9.3",
111
111
  "vitest": "^4.1.8",
112
- "@farming-labs/docs": "0.2.115",
113
- "@farming-labs/theme": "0.2.115"
112
+ "@farming-labs/theme": "0.2.117",
113
+ "@farming-labs/docs": "0.2.117"
114
114
  },
115
115
  "peerDependencies": {
116
116
  "@farming-labs/docs": ">=0.2.73",