@farming-labs/next 0.2.59 → 0.2.61
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.
- package/dist/config.d.mts +2 -1
- package/dist/config.mjs +130 -13
- package/package.json +3 -3
package/dist/config.d.mts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { DocsConfig } from "@farming-labs/docs";
|
|
1
2
|
import { NextConfig } from "next";
|
|
2
3
|
|
|
3
4
|
//#region src/config.d.ts
|
|
4
|
-
declare function withDocs(nextConfig?: NextConfig): NextConfig;
|
|
5
|
+
declare function withDocs(nextConfig?: NextConfig, docsConfig?: Pick<DocsConfig, "mcp">): NextConfig;
|
|
5
6
|
//#endregion
|
|
6
7
|
export { withDocs };
|
package/dist/config.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { dirname, isAbsolute, join, relative } from "node:path";
|
|
2
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { DEFAULT_AGENT_SKILLS_INDEX_ROUTE, DEFAULT_AGENT_SKILLS_ROUTE_PREFIX, DEFAULT_API_CATALOG_ROUTE, DOCS_AI_AGENT_USER_AGENT_HEADER_PATTERN, DOCS_BOT_LIKE_USER_AGENT_HEADER_PATTERN, DOCS_TRADITIONAL_BOT_USER_AGENT_HEADER_PATTERN } from "@farming-labs/docs";
|
|
4
|
+
import { DEFAULT_A2A_AGENT_CARD_ROUTE, DEFAULT_AGENT_SKILLS_INDEX_ROUTE, DEFAULT_AGENT_SKILLS_ROUTE_PREFIX, DEFAULT_AGENT_SKILL_ARCHIVE_FORMAT, DEFAULT_API_CATALOG_ROUTE, DEFAULT_LEGACY_SKILLS_INDEX_ROUTE, DEFAULT_LEGACY_SKILLS_ROUTE_PREFIX, DOCS_AI_AGENT_USER_AGENT_HEADER_PATTERN, DOCS_BOT_LIKE_USER_AGENT_HEADER_PATTERN, DOCS_TRADITIONAL_BOT_USER_AGENT_HEADER_PATTERN } from "@farming-labs/docs";
|
|
5
5
|
import { ensureDocsReviewWorkflow } from "@farming-labs/docs/server";
|
|
6
6
|
import matter from "gray-matter";
|
|
7
7
|
import createMDX from "@next/mdx";
|
|
@@ -152,6 +152,7 @@ const DEFAULT_AGENT_FEEDBACK_ROUTE = "/api/docs/agent/feedback";
|
|
|
152
152
|
const DEFAULT_MCP_ROUTE = "/api/docs/mcp";
|
|
153
153
|
const DEFAULT_MCP_PUBLIC_ROUTE = "/mcp";
|
|
154
154
|
const DEFAULT_MCP_WELL_KNOWN_ROUTE = "/.well-known/mcp";
|
|
155
|
+
const DEFAULT_MCP_PROTECTED_RESOURCE_METADATA_ROUTE = "/.well-known/oauth-protected-resource";
|
|
155
156
|
const DEFAULT_LLMS_TXT_ROUTE = "/llms.txt";
|
|
156
157
|
const DEFAULT_LLMS_FULL_TXT_ROUTE = "/llms-full.txt";
|
|
157
158
|
const DEFAULT_LLMS_TXT_WELL_KNOWN_ROUTE = "/.well-known/llms.txt";
|
|
@@ -544,6 +545,44 @@ function readDocsConfigPath(root) {
|
|
|
544
545
|
}
|
|
545
546
|
return "docs.config.ts";
|
|
546
547
|
}
|
|
548
|
+
function readLiteralStringList(block, key) {
|
|
549
|
+
const cursor = findTopLevelPropertyValueIndex(block, key);
|
|
550
|
+
if (cursor === void 0) return null;
|
|
551
|
+
const value = block.slice(cursor);
|
|
552
|
+
const quoted = value.match(/^(["'])(.*?)\1/s);
|
|
553
|
+
if (quoted) return [quoted[2]];
|
|
554
|
+
if (!value.startsWith("[")) return null;
|
|
555
|
+
const end = value.indexOf("]");
|
|
556
|
+
if (end === -1) return null;
|
|
557
|
+
return [...value.slice(1, end).matchAll(/(["'])(.*?)\1/g)].map((match) => match[2]);
|
|
558
|
+
}
|
|
559
|
+
function readAgentSkillTraceGlobs(root, configPath) {
|
|
560
|
+
const absoluteConfigPath = join(root, configPath);
|
|
561
|
+
if (!existsSync(absoluteConfigPath)) return [];
|
|
562
|
+
const agent = extractTopLevelObjectLiteral(readFileSync(absoluteConfigPath, "utf8"), "agent");
|
|
563
|
+
if (!agent) return [];
|
|
564
|
+
const skillsCursor = findTopLevelPropertyValueIndex(agent, "skills");
|
|
565
|
+
if (skillsCursor === void 0) return [];
|
|
566
|
+
const skillsObject = agent[skillsCursor] === "{" ? extractObjectLiteral(agent, "skills") : void 0;
|
|
567
|
+
const configured = skillsObject ? readLiteralStringList(skillsObject, "paths") : readLiteralStringList(agent, "skills");
|
|
568
|
+
if (!configured) throw new Error("withDocs could not statically trace agent.skills. Use a literal path, a literal path array, or { paths: [...] } so configured skills are included in production.");
|
|
569
|
+
return configured.map((configuredPath) => {
|
|
570
|
+
const normalized = configuredPath.replace(/\\/g, "/").replace(/\/$/, "");
|
|
571
|
+
return normalized.endsWith("/SKILL.md") || normalized === "SKILL.md" ? normalized : `${normalized}/**/*`;
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
function commonTracingRoot(root, globs) {
|
|
575
|
+
let common = resolve(root);
|
|
576
|
+
for (const glob of globs) {
|
|
577
|
+
const candidate = resolve(root, glob.replace(/\/\*\*\/\*$/, ""));
|
|
578
|
+
while (relative(common, candidate) === ".." || relative(common, candidate).startsWith(`..${sep}`)) {
|
|
579
|
+
const parent = dirname(common);
|
|
580
|
+
if (parent === common) break;
|
|
581
|
+
common = parent;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
return common;
|
|
585
|
+
}
|
|
547
586
|
/** Read the OG endpoint from docs.config.ts[x] (returns undefined if not set). */
|
|
548
587
|
function readOgEndpoint(root) {
|
|
549
588
|
for (const ext of FILE_EXTS) {
|
|
@@ -1025,6 +1064,23 @@ function resolveManagedChangelogSourceLayoutPath(root, appDir, entry, changelogC
|
|
|
1025
1064
|
function removeManagedFile(filePath) {
|
|
1026
1065
|
if (isManagedGeneratedFile(filePath)) rmSync(filePath, { force: true });
|
|
1027
1066
|
}
|
|
1067
|
+
function resolveMcpConfig(mcp) {
|
|
1068
|
+
if (mcp === false) return {
|
|
1069
|
+
enabled: false,
|
|
1070
|
+
route: DEFAULT_MCP_ROUTE,
|
|
1071
|
+
protectedResource: false
|
|
1072
|
+
};
|
|
1073
|
+
if (!mcp || mcp === true) return {
|
|
1074
|
+
enabled: true,
|
|
1075
|
+
route: DEFAULT_MCP_ROUTE,
|
|
1076
|
+
protectedResource: false
|
|
1077
|
+
};
|
|
1078
|
+
return {
|
|
1079
|
+
enabled: mcp.enabled ?? true,
|
|
1080
|
+
route: normalizeRoutePath(mcp.route ?? DEFAULT_MCP_ROUTE),
|
|
1081
|
+
protectedResource: Boolean(mcp.security?.authenticate && mcp.security.protectedResource)
|
|
1082
|
+
};
|
|
1083
|
+
}
|
|
1028
1084
|
function readMcpConfig(root) {
|
|
1029
1085
|
for (const ext of FILE_EXTS) {
|
|
1030
1086
|
const configPath = join(root, `docs.config.${ext}`);
|
|
@@ -1033,36 +1089,50 @@ function readMcpConfig(root) {
|
|
|
1033
1089
|
const content = readFileSync(configPath, "utf-8");
|
|
1034
1090
|
if (content.match(/mcp\s*:\s*false/)) return {
|
|
1035
1091
|
enabled: false,
|
|
1036
|
-
route: DEFAULT_MCP_ROUTE
|
|
1092
|
+
route: DEFAULT_MCP_ROUTE,
|
|
1093
|
+
protectedResource: false
|
|
1037
1094
|
};
|
|
1038
1095
|
if (content.match(/mcp\s*:\s*true/)) return {
|
|
1039
1096
|
enabled: true,
|
|
1040
|
-
route: DEFAULT_MCP_ROUTE
|
|
1097
|
+
route: DEFAULT_MCP_ROUTE,
|
|
1098
|
+
protectedResource: false
|
|
1041
1099
|
};
|
|
1042
1100
|
const block = extractObjectLiteral(content, "mcp");
|
|
1043
1101
|
if (!block) continue;
|
|
1044
1102
|
const enabledMatch = block.match(/enabled\s*:\s*(true|false)/);
|
|
1045
1103
|
const routeMatch = block.match(/route\s*:\s*["']([^"']+)["']/);
|
|
1104
|
+
const securityBlock = extractObjectLiteral(block, "security");
|
|
1105
|
+
const protectedResource = Boolean(securityBlock && /\bauthenticate\b/u.test(securityBlock) && extractObjectLiteral(securityBlock, "protectedResource"));
|
|
1046
1106
|
return {
|
|
1047
1107
|
enabled: enabledMatch ? enabledMatch[1] !== "false" : true,
|
|
1048
|
-
route: normalizeRoutePath(routeMatch?.[1] ?? DEFAULT_MCP_ROUTE)
|
|
1108
|
+
route: normalizeRoutePath(routeMatch?.[1] ?? DEFAULT_MCP_ROUTE),
|
|
1109
|
+
protectedResource
|
|
1049
1110
|
};
|
|
1050
1111
|
} catch {
|
|
1051
1112
|
return {
|
|
1052
1113
|
enabled: true,
|
|
1053
|
-
route: DEFAULT_MCP_ROUTE
|
|
1114
|
+
route: DEFAULT_MCP_ROUTE,
|
|
1115
|
+
protectedResource: false
|
|
1054
1116
|
};
|
|
1055
1117
|
}
|
|
1056
1118
|
}
|
|
1057
1119
|
return {
|
|
1058
1120
|
enabled: true,
|
|
1059
|
-
route: DEFAULT_MCP_ROUTE
|
|
1121
|
+
route: DEFAULT_MCP_ROUTE,
|
|
1122
|
+
protectedResource: false
|
|
1060
1123
|
};
|
|
1061
1124
|
}
|
|
1062
1125
|
function normalizeRoutePath(route) {
|
|
1063
1126
|
const normalized = `/${route}`.replace(/\/+/g, "/");
|
|
1064
1127
|
return normalized !== "/" ? normalized.replace(/\/+$/, "") : DEFAULT_MCP_ROUTE;
|
|
1065
1128
|
}
|
|
1129
|
+
function normalizeMcpResourcePath(route) {
|
|
1130
|
+
return route.trim() === "/" ? "/" : normalizeRoutePath(route);
|
|
1131
|
+
}
|
|
1132
|
+
function buildMcpProtectedResourceMetadataRoute(resourcePath) {
|
|
1133
|
+
const normalized = normalizeMcpResourcePath(resourcePath);
|
|
1134
|
+
return normalized === "/" ? DEFAULT_MCP_PROTECTED_RESOURCE_METADATA_ROUTE : `${DEFAULT_MCP_PROTECTED_RESOURCE_METADATA_ROUTE}${normalized}`;
|
|
1135
|
+
}
|
|
1066
1136
|
function normalizeRoutePrefix(prefix) {
|
|
1067
1137
|
if (!prefix) return "";
|
|
1068
1138
|
const normalized = `/${prefix.replace(/^\/+|\/+$/g, "")}`.replace(/\/+/g, "/");
|
|
@@ -1422,7 +1492,35 @@ function buildStandardsDiscoveryRewrites() {
|
|
|
1422
1492
|
},
|
|
1423
1493
|
{
|
|
1424
1494
|
source: `${DEFAULT_AGENT_SKILLS_ROUTE_PREFIX}/:name/SKILL.md`,
|
|
1425
|
-
destination: "/api/docs?format=agent-skill&name=:name"
|
|
1495
|
+
destination: "/api/docs?format=agent-skill-file&name=:name&path=SKILL.md"
|
|
1496
|
+
},
|
|
1497
|
+
{
|
|
1498
|
+
source: `${DEFAULT_AGENT_SKILLS_ROUTE_PREFIX}/:name/skill.md`,
|
|
1499
|
+
destination: "/api/docs?format=agent-skill-file&name=:name&path=SKILL.md"
|
|
1500
|
+
},
|
|
1501
|
+
{
|
|
1502
|
+
source: `${DEFAULT_AGENT_SKILLS_ROUTE_PREFIX}/:name.tar.gz`,
|
|
1503
|
+
destination: `/api/docs?format=${DEFAULT_AGENT_SKILL_ARCHIVE_FORMAT}&name=:name`
|
|
1504
|
+
},
|
|
1505
|
+
{
|
|
1506
|
+
source: `${DEFAULT_AGENT_SKILLS_ROUTE_PREFIX}/:name/:path*`,
|
|
1507
|
+
destination: "/api/docs?format=agent-skill-file&name=:name&path=:path*"
|
|
1508
|
+
},
|
|
1509
|
+
{
|
|
1510
|
+
source: DEFAULT_LEGACY_SKILLS_INDEX_ROUTE,
|
|
1511
|
+
destination: "/api/docs?format=legacy-skills"
|
|
1512
|
+
},
|
|
1513
|
+
{
|
|
1514
|
+
source: `${DEFAULT_LEGACY_SKILLS_ROUTE_PREFIX}/:name/skill.md`,
|
|
1515
|
+
destination: "/api/docs?format=agent-skill-file&name=:name&path=SKILL.md"
|
|
1516
|
+
},
|
|
1517
|
+
{
|
|
1518
|
+
source: `${DEFAULT_LEGACY_SKILLS_ROUTE_PREFIX}/:name/:path*`,
|
|
1519
|
+
destination: "/api/docs?format=agent-skill-file&name=:name&path=:path*"
|
|
1520
|
+
},
|
|
1521
|
+
{
|
|
1522
|
+
source: DEFAULT_A2A_AGENT_CARD_ROUTE,
|
|
1523
|
+
destination: "/api/docs?format=agent-card"
|
|
1426
1524
|
}
|
|
1427
1525
|
];
|
|
1428
1526
|
}
|
|
@@ -1527,10 +1625,19 @@ function buildRobotsRewrites(config) {
|
|
|
1527
1625
|
}
|
|
1528
1626
|
function buildMcpRewrites(config) {
|
|
1529
1627
|
if (!config.enabled) return [];
|
|
1530
|
-
|
|
1628
|
+
const endpointRewrites = [DEFAULT_MCP_PUBLIC_ROUTE, DEFAULT_MCP_WELL_KNOWN_ROUTE].filter((source) => source !== config.route).map((source) => ({
|
|
1531
1629
|
source,
|
|
1532
1630
|
destination: config.route
|
|
1533
1631
|
}));
|
|
1632
|
+
const metadataRewrites = config.protectedResource ? Array.from(new Set([
|
|
1633
|
+
config.route,
|
|
1634
|
+
DEFAULT_MCP_PUBLIC_ROUTE,
|
|
1635
|
+
DEFAULT_MCP_WELL_KNOWN_ROUTE
|
|
1636
|
+
])).map(buildMcpProtectedResourceMetadataRoute).map((source) => ({
|
|
1637
|
+
source,
|
|
1638
|
+
destination: config.route
|
|
1639
|
+
})) : [];
|
|
1640
|
+
return [...endpointRewrites, ...metadataRewrites];
|
|
1534
1641
|
}
|
|
1535
1642
|
function buildAgentFeedbackRewrites(config) {
|
|
1536
1643
|
if (!config.enabled) return [];
|
|
@@ -1694,11 +1801,16 @@ function mergeDocsMarkdownRewrites(entry, docsPath, mcp, sitemap, agentFeedback,
|
|
|
1694
1801
|
function mergeDocsRedirects(autoRedirects, redirects) {
|
|
1695
1802
|
return dedupeRedirects([...redirects ?? [], ...autoRedirects]);
|
|
1696
1803
|
}
|
|
1697
|
-
function withDocs(nextConfig = {}) {
|
|
1804
|
+
function withDocs(nextConfig = {}, docsConfig) {
|
|
1698
1805
|
const root = process.cwd();
|
|
1699
1806
|
const workspaceRoot = findDocsWorkspaceRoot(root);
|
|
1700
1807
|
const docsConfigPath = readDocsConfigPath(root);
|
|
1701
1808
|
const docsConfigAbsolutePath = join(root, docsConfigPath);
|
|
1809
|
+
const mcp = docsConfig ? resolveMcpConfig(docsConfig.mcp) : readMcpConfig(root);
|
|
1810
|
+
const nextBasePath = normalizeRoutePrefix(nextConfig.basePath);
|
|
1811
|
+
if (mcp.enabled && mcp.protectedResource && nextBasePath) throw new Error(`@farming-labs/next: mcp.security.protectedResource cannot be combined with Next.js basePath ${JSON.stringify(nextBasePath)} because RFC 9728 metadata must be hosted at origin-root well-known URLs. Host the protected docs app at the origin root or publish the metadata and MCP proxy at the edge.`);
|
|
1812
|
+
const agentSkillTraceGlobs = readAgentSkillTraceGlobs(root, docsConfigPath);
|
|
1813
|
+
if (agentSkillTraceGlobs.some((glob) => glob.startsWith("../"))) nextConfig.outputFileTracingRoot ??= workspaceRoot ?? commonTracingRoot(root, agentSkillTraceGlobs);
|
|
1702
1814
|
const agentFeedback = readAgentFeedbackConfig(root);
|
|
1703
1815
|
const docsConfigRelativeAlias = docsConfigPath.startsWith("./") || docsConfigPath.startsWith("../") ? docsConfigPath : `./${docsConfigPath}`;
|
|
1704
1816
|
ensureDocsReviewWorkflow({
|
|
@@ -1725,7 +1837,6 @@ function withDocs(nextConfig = {}) {
|
|
|
1725
1837
|
mkdirSync(docsApiRouteDir, { recursive: true });
|
|
1726
1838
|
writeFileSync(docsApiRoutePath, DOCS_API_ROUTE_TEMPLATE);
|
|
1727
1839
|
}
|
|
1728
|
-
const mcp = readMcpConfig(root);
|
|
1729
1840
|
const sitemap = readSitemapConfig(root);
|
|
1730
1841
|
const robots = readRobotsConfig(root);
|
|
1731
1842
|
const docsMcpRouteDir = join(root, appDir, "api", "docs", "mcp");
|
|
@@ -1917,9 +2028,15 @@ function withDocs(nextConfig = {}) {
|
|
|
1917
2028
|
skillTraceFile,
|
|
1918
2029
|
agentsTraceFile,
|
|
1919
2030
|
agentTraceFile,
|
|
1920
|
-
sitemapManifestTraceFile
|
|
2031
|
+
sitemapManifestTraceFile,
|
|
2032
|
+
...agentSkillTraceGlobs
|
|
1921
2033
|
])],
|
|
1922
|
-
[DEFAULT_MCP_ROUTE]: [...new Set([
|
|
2034
|
+
[DEFAULT_MCP_ROUTE]: [...new Set([
|
|
2035
|
+
...existingTracingIncludes[DEFAULT_MCP_ROUTE] ?? [],
|
|
2036
|
+
docsTraceGlob,
|
|
2037
|
+
skillTraceFile,
|
|
2038
|
+
...agentSkillTraceGlobs
|
|
2039
|
+
])]
|
|
1923
2040
|
};
|
|
1924
2041
|
return withMDX(nextConfig);
|
|
1925
2042
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@farming-labs/next",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.61",
|
|
4
4
|
"description": "Next.js adapter for @farming-labs/docs — MDX config wrapper",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"docs",
|
|
@@ -106,8 +106,8 @@
|
|
|
106
106
|
"tsdown": "^0.20.3",
|
|
107
107
|
"typescript": "^5.9.3",
|
|
108
108
|
"vitest": "^4.1.8",
|
|
109
|
-
"@farming-labs/
|
|
110
|
-
"@farming-labs/
|
|
109
|
+
"@farming-labs/theme": "0.2.61",
|
|
110
|
+
"@farming-labs/docs": "0.2.61"
|
|
111
111
|
},
|
|
112
112
|
"peerDependencies": {
|
|
113
113
|
"@farming-labs/docs": ">=0.0.1",
|