@farming-labs/next 0.2.114 → 0.2.116

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/api.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import path from "node:path";
2
2
  import { fileURLToPath, pathToFileURL } from "node:url";
3
3
  import { createDocsAPI as createDocsAPI$1, createDocsMCPAPI as createDocsMCPAPI$1 } from "@farming-labs/theme/api";
4
- import { bundledAgentSkills } from "@farming-labs/docs/agent-skills-bundle";
4
+ import { bundledAgentSkills, bundledRootDocuments } from "@farming-labs/docs/agent-skills-bundle";
5
5
  import { createDocsCloudRouteHandler } from "@farming-labs/docs/cloud/server";
6
6
 
7
7
  //#region src/api.ts
@@ -117,7 +117,8 @@ function createDocsAPI(options = {}, cloudIntegration) {
117
117
  const handlers = createDocsAPI$1({
118
118
  ...options,
119
119
  ...rootDir ? { rootDir } : {},
120
- _preloadedAgentSkills: options._preloadedAgentSkills ?? bundledAgentSkills
120
+ _preloadedAgentSkills: options._preloadedAgentSkills ?? bundledAgentSkills,
121
+ _preloadedRootDocuments: options._preloadedRootDocuments ?? bundledRootDocuments
121
122
  });
122
123
  const integration = resolveDocsCloudIntegration(cloudIntegration);
123
124
  if (!integration) return handlers;
@@ -146,7 +147,8 @@ function createDocsMCPAPI(options = {}) {
146
147
  return createDocsMCPAPI$1({
147
148
  ...options,
148
149
  ...rootDir ? { rootDir } : {},
149
- _preloadedAgentSkills: options._preloadedAgentSkills ?? bundledAgentSkills
150
+ _preloadedAgentSkills: options._preloadedAgentSkills ?? bundledAgentSkills,
151
+ _preloadedRootDocuments: options._preloadedRootDocuments ?? bundledRootDocuments
150
152
  });
151
153
  }
152
154
 
package/dist/config.mjs CHANGED
@@ -669,11 +669,33 @@ function readAgentSkillPaths(root, configPath) {
669
669
  if (!configured) throw new Error("withDocs could not statically bundle agent.skills. Use a literal path, a literal path array, or { paths: [...] } so configured skills are included in production.");
670
670
  return configured;
671
671
  }
672
+ const ROOT_DOCUMENT_FILES = [
673
+ "skill.md",
674
+ "AGENTS.md",
675
+ "AGENT.md"
676
+ ];
677
+ /**
678
+ * Snapshot the project-root agent documents into the skills bundle. Tracing
679
+ * them by bare filename is not an option: Turbopack matches such globs at any
680
+ * depth, which drags `node_modules/next/AGENTS.md` into the trace through the
681
+ * pnpm workspace symlink and breaks Vercel output materialization.
682
+ */
683
+ function readRootDocumentsSnapshot(root) {
684
+ const documents = {};
685
+ for (const fileName of ROOT_DOCUMENT_FILES) {
686
+ const filePath = join(root, fileName);
687
+ if (!existsSync(filePath)) continue;
688
+ try {
689
+ documents[fileName] = readFileSync(filePath, "utf8");
690
+ } catch {}
691
+ }
692
+ return Object.keys(documents).length > 0 ? documents : void 0;
693
+ }
672
694
  function writeAgentSkillsBundle(root, configuredSkills) {
673
695
  const bundlePath = join(root, AGENT_SKILLS_BUNDLE_PATH);
674
- const source = `${GENERATED_BANNER}${renderDocsAgentSkillsBundle(resolveConfiguredAgentSkillsSync(configuredSkills, { rootDir: root }))}`;
675
- mkdirSync(dirname(bundlePath), { recursive: true });
676
- if (!existsSync(bundlePath) || readFileSync(bundlePath, "utf8") !== source) writeFileSync(bundlePath, source, "utf8");
696
+ const source = `${GENERATED_BANNER}${renderDocsAgentSkillsBundle(resolveConfiguredAgentSkillsSync(configuredSkills, { rootDir: root }), readRootDocumentsSnapshot(root))}`;
697
+ makeGeneratedDir(dirname(bundlePath));
698
+ if (!existsSync(bundlePath) || readFileSync(bundlePath, "utf8") !== source) writeGeneratedFile(bundlePath, source);
677
699
  return bundlePath;
678
700
  }
679
701
  /** Read the OG endpoint from docs.config.ts[x] (returns undefined if not set). */
@@ -695,32 +717,32 @@ function readApiReferenceConfig(root) {
695
717
  if (content.match(/apiReference\s*:\s*false/)) return {
696
718
  enabled: false,
697
719
  path: "api-reference",
698
- renderer: "fumadocs",
720
+ renderer: "farming-labs",
699
721
  routeRoot: "api"
700
722
  };
701
723
  if (content.match(/apiReference\s*:\s*true/)) return {
702
724
  enabled: true,
703
725
  path: "api-reference",
704
- renderer: "fumadocs",
726
+ renderer: "farming-labs",
705
727
  routeRoot: "api"
706
728
  };
707
729
  const block = extractObjectLiteral(content, "apiReference");
708
730
  if (!block) continue;
709
731
  const enabledMatch = block.match(/enabled\s*:\s*(true|false)/);
710
732
  const pathMatch = block.match(/path\s*:\s*["']([^"']+)["']/);
711
- const rendererMatch = block.match(/renderer\s*:\s*["'](fumadocs|scalar)["']/);
733
+ const rendererMatch = block.match(/renderer\s*:\s*["'](farming-labs|fumadocs|scalar)["']/);
712
734
  const routeRootMatch = block.match(/routeRoot\s*:\s*["']([^"']+)["']/);
713
735
  return {
714
736
  enabled: enabledMatch ? enabledMatch[1] !== "false" : true,
715
737
  path: pathMatch?.[1]?.replace(/^\/+|\/+$/g, "") || "api-reference",
716
- renderer: rendererMatch?.[1] ?? "fumadocs",
738
+ renderer: rendererMatch?.[1] ?? "farming-labs",
717
739
  routeRoot: routeRootMatch?.[1]?.replace(/^\/+|\/+$/g, "") || "api"
718
740
  };
719
741
  } catch {
720
742
  return {
721
743
  enabled: false,
722
744
  path: "api-reference",
723
- renderer: "fumadocs",
745
+ renderer: "farming-labs",
724
746
  routeRoot: "api"
725
747
  };
726
748
  }
@@ -728,7 +750,7 @@ function readApiReferenceConfig(root) {
728
750
  return {
729
751
  enabled: false,
730
752
  path: "api-reference",
731
- renderer: "fumadocs",
753
+ renderer: "farming-labs",
732
754
  routeRoot: "api"
733
755
  };
734
756
  }
@@ -1155,7 +1177,29 @@ function resolveManagedChangelogSourceLayoutPath(root, appDir, entry, changelogC
1155
1177
  return join(contentRoot, "layout.tsx");
1156
1178
  }
1157
1179
  function removeManagedFile(filePath) {
1158
- if (isManagedGeneratedFile(filePath)) rmSync(filePath, { force: true });
1180
+ if (isManagedGeneratedFile(filePath)) withReadOnlyFsGuard(filePath, () => rmSync(filePath, { force: true }));
1181
+ }
1182
+ const READ_ONLY_FS_CODES = new Set([
1183
+ "EROFS",
1184
+ "EACCES",
1185
+ "EPERM"
1186
+ ]);
1187
+ /** Generation must not take down config evaluation on read-only filesystems; the build already shipped these files. */
1188
+ function withReadOnlyFsGuard(target, mutate) {
1189
+ try {
1190
+ return mutate();
1191
+ } catch (error) {
1192
+ const code = error?.code;
1193
+ if (!code || !READ_ONLY_FS_CODES.has(code)) throw error;
1194
+ console.warn(`[docs] Skipped generating ${target} (${code}): filesystem is read-only; using existing files.`);
1195
+ return;
1196
+ }
1197
+ }
1198
+ function makeGeneratedDir(dirPath) {
1199
+ withReadOnlyFsGuard(dirPath, () => mkdirSync(dirPath, { recursive: true }));
1200
+ }
1201
+ function writeGeneratedFile(filePath, content) {
1202
+ withReadOnlyFsGuard(filePath, () => writeFileSync(filePath, content, "utf8"));
1159
1203
  }
1160
1204
  function resolveMcpConfig(mcp) {
1161
1205
  if (mcp === false) return {
@@ -1557,6 +1601,72 @@ function buildDocsMarkdownRewrites(entry, docsPath) {
1557
1601
  }
1558
1602
  ];
1559
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
+ }
1560
1670
  function buildAgentSpecRewrites() {
1561
1671
  return [
1562
1672
  {
@@ -1863,7 +1973,7 @@ function buildHiddenFolderRedirects(docsDir, docsPath) {
1863
1973
  scan(docsDir, []);
1864
1974
  return dedupeRedirects(redirects);
1865
1975
  }
1866
- function mergeDocsMarkdownRewrites(entry, docsPath, mcp, sitemap, agentFeedback, robots, result) {
1976
+ function mergeDocsMarkdownRewrites(entry, docsPath, apiReference, mcp, sitemap, agentFeedback, robots, result) {
1867
1977
  const autoBeforeFilesRewrites = [
1868
1978
  ...buildAgentSpecRewrites(),
1869
1979
  ...buildStandardsDiscoveryRewrites(),
@@ -1873,6 +1983,7 @@ function mergeDocsMarkdownRewrites(entry, docsPath, mcp, sitemap, agentFeedback,
1873
1983
  ...buildSitemapRewrites(sitemap),
1874
1984
  ...buildRobotsRewrites(robots),
1875
1985
  ...buildDocsMarkdownRewrites(entry, docsPath),
1986
+ ...buildApiReferenceMarkdownRewrites(apiReference),
1876
1987
  ...buildDocsPathRewrites(entry, docsPath),
1877
1988
  ...buildAgentFeedbackRewrites(agentFeedback)
1878
1989
  ];
@@ -1910,36 +2021,36 @@ function withDocs(nextConfig = {}, docsConfig) {
1910
2021
  const agentSkillsBundlePath = writeAgentSkillsBundle(root, docsConfig === void 0 || !Object.hasOwn(docsConfig, "agent") ? readAgentSkillPaths(root, docsConfigPath) : docsConfig.agent?.skills);
1911
2022
  const agentFeedback = readAgentFeedbackConfig(root);
1912
2023
  const docsConfigRelativeAlias = docsConfigPath.startsWith("./") || docsConfigPath.startsWith("../") ? docsConfigPath : `./${docsConfigPath}`;
1913
- ensureDocsReviewWorkflow({
2024
+ withReadOnlyFsGuard(".github/workflows/docs-review.yml", () => ensureDocsReviewWorkflow({
1914
2025
  rootDir: root,
1915
2026
  configPath: docsConfigPath,
1916
2027
  configContent: existsSync(docsConfigAbsolutePath) ? readFileSync(docsConfigAbsolutePath, "utf-8") : void 0,
1917
2028
  log: process.env.NODE_ENV === "test" ? void 0 : (message) => console.log(message)
1918
- });
1919
- if (!hasFile(root, "mdx-components")) writeFileSync(join(root, "mdx-components.tsx"), MDX_COMPONENTS_TEMPLATE);
2029
+ }));
2030
+ if (!hasFile(root, "mdx-components")) writeGeneratedFile(join(root, "mdx-components.tsx"), MDX_COMPONENTS_TEMPLATE);
1920
2031
  const entry = readDocsEntry(root);
1921
2032
  const docsPath = normalizeDocsPath(readDocsPath(root), entry);
1922
2033
  const configuredContentDir = readDocsContentDir(root);
1923
2034
  const appDir = getNextAppDir(root);
1924
2035
  const docsContentDir = configuredContentDir ?? join(appDir, entry);
1925
2036
  const layoutDir = join(root, appDir, entry);
1926
- if (!existsSync(layoutDir)) mkdirSync(layoutDir, { recursive: true });
2037
+ if (!existsSync(layoutDir)) makeGeneratedDir(layoutDir);
1927
2038
  const docsLayoutPath = join(layoutDir, "layout.tsx");
1928
- if (!hasFile(layoutDir, "layout") || isManagedGeneratedFile(docsLayoutPath)) writeFileSync(join(layoutDir, "layout.tsx"), DOCS_LAYOUT_TEMPLATE);
2039
+ if (!hasFile(layoutDir, "layout") || isManagedGeneratedFile(docsLayoutPath)) writeGeneratedFile(join(layoutDir, "layout.tsx"), DOCS_LAYOUT_TEMPLATE);
1929
2040
  const isStaticExport = nextConfig.output === "export";
1930
2041
  const docsApiRouteDir = join(root, appDir, "api", "docs");
1931
2042
  const existingDocsApiRoutePath = FILE_EXTS.map((ext) => join(docsApiRouteDir, `route.${ext}`)).find((filePath) => existsSync(filePath));
1932
2043
  const docsApiRoutePath = existingDocsApiRoutePath ?? join(docsApiRouteDir, "route.ts");
1933
2044
  if (!isStaticExport && (!existingDocsApiRoutePath || isManagedGeneratedFile(existingDocsApiRoutePath))) {
1934
- mkdirSync(docsApiRouteDir, { recursive: true });
1935
- writeFileSync(docsApiRoutePath, DOCS_API_ROUTE_TEMPLATE);
2045
+ makeGeneratedDir(docsApiRouteDir);
2046
+ writeGeneratedFile(docsApiRoutePath, DOCS_API_ROUTE_TEMPLATE);
1936
2047
  }
1937
2048
  const sitemap = readSitemapConfig(root);
1938
2049
  const robots = readRobotsConfig(root);
1939
2050
  const docsMcpRouteDir = join(root, appDir, "api", "docs", "mcp");
1940
2051
  if (mcp.enabled && mcp.route === DEFAULT_MCP_ROUTE && !isStaticExport && !hasFile(docsMcpRouteDir, "route")) {
1941
- mkdirSync(docsMcpRouteDir, { recursive: true });
1942
- writeFileSync(join(docsMcpRouteDir, "route.ts"), DOCS_MCP_ROUTE_TEMPLATE);
2052
+ makeGeneratedDir(docsMcpRouteDir);
2053
+ writeGeneratedFile(join(docsMcpRouteDir, "route.ts"), DOCS_MCP_ROUTE_TEMPLATE);
1943
2054
  }
1944
2055
  const apiReference = readApiReferenceConfig(root);
1945
2056
  if (apiReference.enabled && !isStaticExport) {
@@ -1954,19 +2065,19 @@ function withDocs(nextConfig = {}, docsConfig) {
1954
2065
  removeManagedFile(apiReferenceRoutePath);
1955
2066
  removeManagedFile(legacyApiReferencePagePath);
1956
2067
  if (!hasFile(apiReferenceBaseDir, "layout") || isManagedGeneratedFile(apiReferenceLayoutPath)) {
1957
- mkdirSync(apiReferenceBaseDir, { recursive: true });
1958
- writeFileSync(apiReferenceLayoutPath, API_REFERENCE_LAYOUT_TEMPLATE);
2068
+ makeGeneratedDir(apiReferenceBaseDir);
2069
+ writeGeneratedFile(apiReferenceLayoutPath, API_REFERENCE_LAYOUT_TEMPLATE);
1959
2070
  }
1960
2071
  if (!hasFile(apiReferencePageDir, "page") || isManagedGeneratedFile(apiReferencePagePath)) {
1961
- mkdirSync(apiReferencePageDir, { recursive: true });
1962
- writeFileSync(apiReferencePagePath, API_REFERENCE_PAGE_TEMPLATE);
2072
+ makeGeneratedDir(apiReferencePageDir);
2073
+ writeGeneratedFile(apiReferencePagePath, API_REFERENCE_PAGE_TEMPLATE);
1963
2074
  }
1964
2075
  } else {
1965
2076
  removeManagedFile(apiReferenceLayoutPath);
1966
2077
  removeManagedFile(apiReferencePagePath);
1967
2078
  if (!hasFile(apiReferenceRouteDir, "route") || isManagedGeneratedFile(apiReferenceRoutePath)) {
1968
- mkdirSync(apiReferenceRouteDir, { recursive: true });
1969
- writeFileSync(apiReferenceRoutePath, API_REFERENCE_ROUTE_TEMPLATE);
2079
+ makeGeneratedDir(apiReferenceRouteDir);
2080
+ writeGeneratedFile(apiReferenceRoutePath, API_REFERENCE_ROUTE_TEMPLATE);
1970
2081
  }
1971
2082
  }
1972
2083
  }
@@ -1987,22 +2098,22 @@ function withDocs(nextConfig = {}, docsConfig) {
1987
2098
  removeManagedFile(join(legacyChangelogBaseDir, "page.tsx"));
1988
2099
  removeManagedFile(join(legacyChangelogBaseDir, "__changelog.generated.tsx"));
1989
2100
  }
1990
- mkdirSync(changelogBaseDir, { recursive: true });
2101
+ makeGeneratedDir(changelogBaseDir);
1991
2102
  removeManagedFile(join(changelogBaseDir, "__changelog.generated.tsx"));
1992
- if (!hasFile(changelogBaseDir, "page") || isManagedGeneratedFile(changelogIndexPath)) writeFileSync(changelogIndexPath, buildChangelogIndexPageSource(root, changelogIndexPath, entry, changelog.path, changelogEntries));
2103
+ if (!hasFile(changelogBaseDir, "page") || isManagedGeneratedFile(changelogIndexPath)) writeGeneratedFile(changelogIndexPath, buildChangelogIndexPageSource(root, changelogIndexPath, entry, changelog.path, changelogEntries));
1993
2104
  if (changelogUsesSourcePagesAsDetails) {
1994
2105
  removeManagedFile(changelogEntryPath);
1995
2106
  if (changelogSourceLayoutPath) removeManagedFile(changelogSourceLayoutPath);
1996
2107
  } else {
1997
2108
  if (!hasFile(changelogEntryDir, "page") || isManagedGeneratedFile(changelogEntryPath)) {
1998
- mkdirSync(changelogEntryDir, { recursive: true });
1999
- writeFileSync(changelogEntryPath, buildChangelogEntryPageSource(root, changelogEntryPath, entry, changelog.path, changelogEntries));
2109
+ makeGeneratedDir(changelogEntryDir);
2110
+ writeGeneratedFile(changelogEntryPath, buildChangelogEntryPageSource(root, changelogEntryPath, entry, changelog.path, changelogEntries));
2000
2111
  }
2001
2112
  if (changelogSourceLayoutPath) {
2002
2113
  const changelogSourceLayoutDir = dirname(changelogSourceLayoutPath);
2003
2114
  if (!hasFile(changelogSourceLayoutDir, "layout") || isManagedGeneratedFile(changelogSourceLayoutPath)) {
2004
- mkdirSync(changelogSourceLayoutDir, { recursive: true });
2005
- writeFileSync(changelogSourceLayoutPath, CHANGELOG_SOURCE_LAYOUT_TEMPLATE);
2115
+ makeGeneratedDir(changelogSourceLayoutDir);
2116
+ writeGeneratedFile(changelogSourceLayoutPath, CHANGELOG_SOURCE_LAYOUT_TEMPLATE);
2006
2117
  }
2007
2118
  }
2008
2119
  }
@@ -2098,15 +2209,12 @@ function withDocs(nextConfig = {}, docsConfig) {
2098
2209
  };
2099
2210
  const existingTracingIncludes = nextConfig.outputFileTracingIncludes ?? {};
2100
2211
  const docsTraceGlob = docsContentDir.replace(/\\/g, "/").replace(/^\.?\//, "") + "/**/*";
2101
- const skillTraceFile = "skill.md";
2102
- const agentsTraceFile = "AGENTS.md";
2103
- const agentTraceFile = "AGENT.md";
2104
2212
  const sitemapManifestTraceFile = DEFAULT_SITEMAP_MANIFEST_PATH;
2105
2213
  const docsContentRoot = isAbsolute(docsContentDir) ? docsContentDir : join(root, docsContentDir);
2106
2214
  if (!isStaticExport) {
2107
2215
  const existingRewrites = nextConfig.rewrites;
2108
2216
  nextConfig.rewrites = async () => {
2109
- 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);
2110
2218
  };
2111
2219
  const autoRedirects = buildHiddenFolderRedirects(docsContentRoot, docsPath);
2112
2220
  if (autoRedirects.length > 0) {
@@ -2121,16 +2229,9 @@ function withDocs(nextConfig = {}, docsConfig) {
2121
2229
  "/api/docs": [...new Set([
2122
2230
  ...existingTracingIncludes["/api/docs"] ?? [],
2123
2231
  docsTraceGlob,
2124
- skillTraceFile,
2125
- agentsTraceFile,
2126
- agentTraceFile,
2127
2232
  sitemapManifestTraceFile
2128
2233
  ])],
2129
- [DEFAULT_MCP_ROUTE]: [...new Set([
2130
- ...existingTracingIncludes[DEFAULT_MCP_ROUTE] ?? [],
2131
- docsTraceGlob,
2132
- skillTraceFile
2133
- ])]
2234
+ [DEFAULT_MCP_ROUTE]: [...new Set([...existingTracingIncludes[DEFAULT_MCP_ROUTE] ?? [], docsTraceGlob])]
2134
2235
  };
2135
2236
  return withMDX(nextConfig);
2136
2237
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farming-labs/next",
3
- "version": "0.2.114",
3
+ "version": "0.2.116",
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.114",
113
- "@farming-labs/theme": "0.2.114"
112
+ "@farming-labs/docs": "0.2.116",
113
+ "@farming-labs/theme": "0.2.116"
114
114
  },
115
115
  "peerDependencies": {
116
116
  "@farming-labs/docs": ">=0.2.73",