@farming-labs/next 0.1.96 → 0.1.98

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/config.mjs +160 -55
  2. package/package.json +3 -3
package/dist/config.mjs CHANGED
@@ -78,20 +78,7 @@ ${GENERATED_BANNER}
78
78
  import docsConfig from "@/docs.config";
79
79
  import { createDocsAPI } from "@farming-labs/next/api";
80
80
 
81
- export const { GET, POST } = createDocsAPI({
82
- entry: docsConfig.entry,
83
- contentDir: docsConfig.contentDir,
84
- i18n: docsConfig.i18n,
85
- changelog: docsConfig.changelog,
86
- feedback: docsConfig.feedback,
87
- mcp: docsConfig.mcp,
88
- llmsTxt: docsConfig.llmsTxt,
89
- sitemap: docsConfig.sitemap,
90
- search: docsConfig.search,
91
- analytics: docsConfig.analytics,
92
- observability: docsConfig.observability,
93
- ai: docsConfig.ai,
94
- });
81
+ export const { GET, POST } = createDocsAPI(docsConfig);
95
82
 
96
83
  export const revalidate = false;
97
84
  `;
@@ -100,16 +87,7 @@ ${GENERATED_BANNER}
100
87
  import docsConfig from "@/docs.config";
101
88
  import { createDocsMCPAPI } from "@farming-labs/next/api";
102
89
 
103
- export const { GET, POST, DELETE } = createDocsMCPAPI({
104
- entry: docsConfig.entry,
105
- contentDir: docsConfig.contentDir,
106
- nav: docsConfig.nav,
107
- ordering: docsConfig.ordering,
108
- search: docsConfig.search,
109
- analytics: docsConfig.analytics,
110
- observability: docsConfig.observability,
111
- mcp: docsConfig.mcp,
112
- });
90
+ export const { GET, POST, DELETE } = createDocsMCPAPI(docsConfig);
113
91
 
114
92
  export const revalidate = false;
115
93
  `;
@@ -173,6 +151,7 @@ const DEFAULT_LLMS_TXT_WELL_KNOWN_ROUTE = "/.well-known/llms.txt";
173
151
  const DEFAULT_LLMS_FULL_TXT_WELL_KNOWN_ROUTE = "/.well-known/llms-full.txt";
174
152
  const DEFAULT_SKILL_MD_ROUTE = "/skill.md";
175
153
  const DEFAULT_SKILL_MD_WELL_KNOWN_ROUTE = "/.well-known/skill.md";
154
+ const DEFAULT_ROBOTS_TXT_ROUTE = "/robots.txt";
176
155
  const DEFAULT_SITEMAP_XML_ROUTE = "/sitemap.xml";
177
156
  const DEFAULT_SITEMAP_MD_ROUTE = "/sitemap.md";
178
157
  const DEFAULT_SITEMAP_MD_WELL_KNOWN_ROUTE = "/.well-known/sitemap.md";
@@ -451,9 +430,7 @@ function extractRootObjectLiteral(content) {
451
430
  }
452
431
  }
453
432
  }
454
- function readTopLevelStringProperty(content, key) {
455
- const block = extractRootObjectLiteral(content);
456
- if (!block) return void 0;
433
+ function findTopLevelPropertyValueIndex(block, key) {
457
434
  let objectDepth = 0;
458
435
  let arrayDepth = 0;
459
436
  let parenDepth = 0;
@@ -535,26 +512,102 @@ function readTopLevelStringProperty(content, key) {
535
512
  if (block[cursor] !== ":") continue;
536
513
  cursor += 1;
537
514
  while (/\s/.test(block[cursor] ?? "")) cursor += 1;
538
- const quote = block[cursor];
539
- if (quote !== "\"" && quote !== "'") continue;
540
- cursor += 1;
541
- let value = "";
542
- for (; cursor < block.length; cursor += 1) {
543
- const valueChar = block[cursor];
544
- if (valueChar === "\\") {
545
- const escapedChar = block[cursor + 1];
546
- if (escapedChar) {
547
- value += escapedChar;
548
- cursor += 1;
549
- }
515
+ return cursor;
516
+ }
517
+ }
518
+ function findMatchingObjectEnd(block, start) {
519
+ let depth = 0;
520
+ let inString = null;
521
+ let inLineComment = false;
522
+ let inBlockComment = false;
523
+ let escaped = false;
524
+ for (let index = start; index < block.length; index += 1) {
525
+ const char = block[index];
526
+ const next = block[index + 1];
527
+ if (inLineComment) {
528
+ if (char === "\n") inLineComment = false;
529
+ continue;
530
+ }
531
+ if (inBlockComment) {
532
+ if (char === "*" && next === "/") {
533
+ inBlockComment = false;
534
+ index += 1;
535
+ }
536
+ continue;
537
+ }
538
+ if (inString) {
539
+ if (escaped) {
540
+ escaped = false;
550
541
  continue;
551
542
  }
552
- if (valueChar === quote) return value;
553
- value += valueChar;
543
+ if (char === "\\") {
544
+ escaped = true;
545
+ continue;
546
+ }
547
+ if (char === inString) inString = null;
548
+ continue;
549
+ }
550
+ if (char === "/" && next === "/") {
551
+ inLineComment = true;
552
+ index += 1;
553
+ continue;
554
+ }
555
+ if (char === "/" && next === "*") {
556
+ inBlockComment = true;
557
+ index += 1;
558
+ continue;
559
+ }
560
+ if (char === "\"" || char === "'" || char === "`") {
561
+ inString = char;
562
+ continue;
563
+ }
564
+ if (char === "{") {
565
+ depth += 1;
566
+ continue;
567
+ }
568
+ if (char !== "}") continue;
569
+ depth -= 1;
570
+ if (depth === 0) return index;
571
+ }
572
+ }
573
+ function readTopLevelStringProperty(content, key) {
574
+ const block = extractRootObjectLiteral(content);
575
+ if (!block) return void 0;
576
+ const cursor = findTopLevelPropertyValueIndex(block, key);
577
+ if (cursor === void 0) return void 0;
578
+ const quote = block[cursor];
579
+ if (quote !== "\"" && quote !== "'") return void 0;
580
+ let value = "";
581
+ for (let index = cursor + 1; index < block.length; index += 1) {
582
+ const valueChar = block[index];
583
+ if (valueChar === "\\") {
584
+ const escapedChar = block[index + 1];
585
+ if (escapedChar) {
586
+ value += escapedChar;
587
+ index += 1;
588
+ }
589
+ continue;
554
590
  }
555
- return;
591
+ if (valueChar === quote) return value;
592
+ value += valueChar;
556
593
  }
557
594
  }
595
+ function readTopLevelBooleanProperty(content, key) {
596
+ const block = extractRootObjectLiteral(content);
597
+ if (!block) return void 0;
598
+ const cursor = findTopLevelPropertyValueIndex(block, key);
599
+ if (cursor === void 0) return void 0;
600
+ if (block.startsWith("true", cursor)) return true;
601
+ if (block.startsWith("false", cursor)) return false;
602
+ }
603
+ function extractTopLevelObjectLiteral(content, key) {
604
+ const block = extractRootObjectLiteral(content);
605
+ if (!block) return void 0;
606
+ const cursor = findTopLevelPropertyValueIndex(block, key);
607
+ if (cursor === void 0 || block[cursor] !== "{") return void 0;
608
+ const end = findMatchingObjectEnd(block, cursor);
609
+ return end === void 0 ? void 0 : block.slice(cursor + 1, end);
610
+ }
558
611
  function extractObjectLiteral(content, key) {
559
612
  const keyIndex = content.search(new RegExp(`${key}\\s*:\\s*\\{`));
560
613
  if (keyIndex === -1) return void 0;
@@ -759,16 +812,54 @@ function readSitemapConfig(root) {
759
812
  };
760
813
  } catch {
761
814
  return {
762
- enabled: false,
815
+ enabled: true,
763
816
  routePrefix: ""
764
817
  };
765
818
  }
766
819
  }
767
820
  return {
768
- enabled: false,
821
+ enabled: true,
769
822
  routePrefix: ""
770
823
  };
771
824
  }
825
+ function readRobotsConfig(root) {
826
+ const publicStaticPath = join(root, "public", "robots.txt");
827
+ const appDir = getNextAppDir(root);
828
+ const appStaticPath = join(root, appDir, "robots.txt");
829
+ const appRouteExists = FILE_EXTS.some((ext) => existsSync(join(root, appDir, `robots.${ext}`)));
830
+ const hasStaticFile = existsSync(publicStaticPath) || existsSync(appStaticPath) || appRouteExists;
831
+ for (const ext of FILE_EXTS) {
832
+ const configPath = join(root, `docs.config.${ext}`);
833
+ if (!existsSync(configPath)) continue;
834
+ try {
835
+ const content = readFileSync(configPath, "utf-8");
836
+ if (content.match(/robots\s*:\s*false/)) return {
837
+ enabled: false,
838
+ hasStaticFile
839
+ };
840
+ if (content.match(/robots\s*:\s*true/)) return {
841
+ enabled: true,
842
+ hasStaticFile
843
+ };
844
+ const block = extractObjectLiteral(content, "robots");
845
+ if (!block) continue;
846
+ const enabledMatch = block.match(/enabled\s*:\s*(true|false)/);
847
+ return {
848
+ enabled: enabledMatch ? enabledMatch[1] !== "false" : true,
849
+ hasStaticFile
850
+ };
851
+ } catch {
852
+ return {
853
+ enabled: true,
854
+ hasStaticFile
855
+ };
856
+ }
857
+ }
858
+ return {
859
+ enabled: true,
860
+ hasStaticFile
861
+ };
862
+ }
772
863
  function normalizeAgentFeedbackRoute(route, fallback = DEFAULT_AGENT_FEEDBACK_ROUTE) {
773
864
  if (!route || route.trim().length === 0) return fallback;
774
865
  const normalized = `/${route}`.replace(/\/+/g, "/");
@@ -776,6 +867,11 @@ function normalizeAgentFeedbackRoute(route, fallback = DEFAULT_AGENT_FEEDBACK_RO
776
867
  }
777
868
  function readAgentFeedbackConfig(root) {
778
869
  const defaultRoute = normalizeAgentFeedbackRoute();
870
+ const enabled = {
871
+ enabled: true,
872
+ route: defaultRoute,
873
+ schemaRoute: `${defaultRoute}/schema`
874
+ };
779
875
  const disabled = {
780
876
  enabled: false,
781
877
  route: defaultRoute,
@@ -785,16 +881,16 @@ function readAgentFeedbackConfig(root) {
785
881
  const configPath = join(root, `docs.config.${ext}`);
786
882
  if (!existsSync(configPath)) continue;
787
883
  try {
788
- const feedbackBlock = extractObjectLiteral(readFileSync(configPath, "utf-8"), "feedback");
789
- if (!feedbackBlock) continue;
884
+ const content = readFileSync(configPath, "utf-8");
885
+ const feedbackBoolean = readTopLevelBooleanProperty(content, "feedback");
886
+ if (feedbackBoolean === false) return disabled;
887
+ if (feedbackBoolean === true) return enabled;
888
+ const feedbackBlock = extractTopLevelObjectLiteral(content, "feedback");
889
+ if (!feedbackBlock) return enabled;
790
890
  if (feedbackBlock.match(/agent\s*:\s*false/)) return disabled;
791
- if (feedbackBlock.match(/agent\s*:\s*true/)) return {
792
- enabled: true,
793
- route: defaultRoute,
794
- schemaRoute: `${defaultRoute}/schema`
795
- };
891
+ if (feedbackBlock.match(/agent\s*:\s*true/)) return enabled;
796
892
  const agentBlock = extractObjectLiteral(feedbackBlock, "agent");
797
- if (!agentBlock) continue;
893
+ if (!agentBlock) return enabled;
798
894
  const enabledMatch = agentBlock.match(/enabled\s*:\s*(true|false)/);
799
895
  const routeMatch = agentBlock.match(/route\s*:\s*["']([^"']+)["']/);
800
896
  const schemaRouteMatch = agentBlock.match(/schemaRoute\s*:\s*["']([^"']+)["']/);
@@ -808,7 +904,7 @@ function readAgentFeedbackConfig(root) {
808
904
  return disabled;
809
905
  }
810
906
  }
811
- return disabled;
907
+ return enabled;
812
908
  }
813
909
  function buildDocsMarkdownRewrites(entry) {
814
910
  const normalizedEntry = entry.replace(/^\/+|\/+$/g, "") || "docs";
@@ -925,6 +1021,13 @@ function buildSitemapRewrites(config) {
925
1021
  }
926
1022
  ];
927
1023
  }
1024
+ function buildRobotsRewrites(config) {
1025
+ if (!config.enabled || config.hasStaticFile) return [];
1026
+ return [{
1027
+ source: DEFAULT_ROBOTS_TXT_ROUTE,
1028
+ destination: "/api/docs?format=robots"
1029
+ }];
1030
+ }
928
1031
  function buildMcpRewrites(config) {
929
1032
  if (!config.enabled) return [];
930
1033
  return [DEFAULT_MCP_PUBLIC_ROUTE, DEFAULT_MCP_WELL_KNOWN_ROUTE].filter((source) => source !== config.route).map((source) => ({
@@ -1058,13 +1161,14 @@ function buildHiddenFolderRedirects(docsDir, entry) {
1058
1161
  scan(docsDir, []);
1059
1162
  return dedupeRedirects(redirects);
1060
1163
  }
1061
- function mergeDocsMarkdownRewrites(entry, mcp, sitemap, agentFeedback, result) {
1164
+ function mergeDocsMarkdownRewrites(entry, mcp, sitemap, agentFeedback, robots, result) {
1062
1165
  const autoRewrites = [
1063
1166
  ...buildAgentSpecRewrites(),
1064
1167
  ...buildMcpRewrites(mcp),
1065
1168
  ...buildLlmsTxtRewrites(entry),
1066
1169
  ...buildSkillMdRewrites(),
1067
1170
  ...buildSitemapRewrites(sitemap),
1171
+ ...buildRobotsRewrites(robots),
1068
1172
  ...buildDocsMarkdownRewrites(entry),
1069
1173
  ...buildAgentFeedbackRewrites(agentFeedback)
1070
1174
  ];
@@ -1106,6 +1210,7 @@ function withDocs(nextConfig = {}) {
1106
1210
  }
1107
1211
  const mcp = readMcpConfig(root);
1108
1212
  const sitemap = readSitemapConfig(root);
1213
+ const robots = readRobotsConfig(root);
1109
1214
  const docsMcpRouteDir = join(root, appDir, "api", "docs", "mcp");
1110
1215
  if (mcp.enabled && mcp.route === DEFAULT_MCP_ROUTE && !isStaticExport && !hasFile(docsMcpRouteDir, "route")) {
1111
1216
  mkdirSync(docsMcpRouteDir, { recursive: true });
@@ -1268,7 +1373,7 @@ function withDocs(nextConfig = {}) {
1268
1373
  if (!isStaticExport) {
1269
1374
  const existingRewrites = nextConfig.rewrites;
1270
1375
  nextConfig.rewrites = async () => {
1271
- return mergeDocsMarkdownRewrites(entry, mcp, sitemap, agentFeedback, typeof existingRewrites === "function" ? await existingRewrites() : existingRewrites);
1376
+ return mergeDocsMarkdownRewrites(entry, mcp, sitemap, agentFeedback, robots, typeof existingRewrites === "function" ? await existingRewrites() : existingRewrites);
1272
1377
  };
1273
1378
  const autoRedirects = buildHiddenFolderRedirects(docsContentRoot, entry);
1274
1379
  if (autoRedirects.length > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farming-labs/next",
3
- "version": "0.1.96",
3
+ "version": "0.1.98",
4
4
  "description": "Next.js adapter for @farming-labs/docs — MDX config wrapper",
5
5
  "keywords": [
6
6
  "docs",
@@ -100,8 +100,8 @@
100
100
  "tsdown": "^0.20.3",
101
101
  "typescript": "^5.9.3",
102
102
  "vitest": "^3.2.4",
103
- "@farming-labs/docs": "0.1.96",
104
- "@farming-labs/theme": "0.1.96"
103
+ "@farming-labs/docs": "0.1.98",
104
+ "@farming-labs/theme": "0.1.98"
105
105
  },
106
106
  "peerDependencies": {
107
107
  "@farming-labs/docs": ">=0.0.1",