@farming-labs/next 0.2.61 → 0.2.63

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/api.mjs CHANGED
@@ -1,6 +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
5
  import { createDocsCloudRouteHandler } from "@farming-labs/docs/cloud/server";
5
6
 
6
7
  //#region src/api.ts
@@ -113,10 +114,11 @@ async function isDocsCloudPostRequest(request) {
113
114
  }
114
115
  function createDocsAPI(options = {}, cloudIntegration) {
115
116
  const rootDir = options.rootDir ?? inferNextProjectRootFromCaller();
116
- const handlers = createDocsAPI$1(rootDir ? {
117
+ const handlers = createDocsAPI$1({
117
118
  ...options,
118
- rootDir
119
- } : options);
119
+ ...rootDir ? { rootDir } : {},
120
+ _preloadedAgentSkills: options._preloadedAgentSkills ?? bundledAgentSkills
121
+ });
120
122
  const integration = resolveDocsCloudIntegration(cloudIntegration);
121
123
  if (!integration) return handlers;
122
124
  return {
@@ -141,10 +143,11 @@ function createDocsAPI(options = {}, cloudIntegration) {
141
143
  }
142
144
  function createDocsMCPAPI(options = {}) {
143
145
  const rootDir = options.rootDir ?? inferNextProjectRootFromCaller();
144
- return createDocsMCPAPI$1(rootDir ? {
146
+ return createDocsMCPAPI$1({
145
147
  ...options,
146
- rootDir
147
- } : options);
148
+ ...rootDir ? { rootDir } : {},
149
+ _preloadedAgentSkills: options._preloadedAgentSkills ?? bundledAgentSkills
150
+ });
148
151
  }
149
152
 
150
153
  //#endregion
package/dist/config.d.mts CHANGED
@@ -2,6 +2,6 @@ import { DocsConfig } from "@farming-labs/docs";
2
2
  import { NextConfig } from "next";
3
3
 
4
4
  //#region src/config.d.ts
5
- declare function withDocs(nextConfig?: NextConfig, docsConfig?: Pick<DocsConfig, "mcp">): NextConfig;
5
+ declare function withDocs(nextConfig?: NextConfig, docsConfig?: Pick<DocsConfig, "agent" | "mcp">): NextConfig;
6
6
  //#endregion
7
7
  export { withDocs };
package/dist/config.mjs CHANGED
@@ -1,9 +1,10 @@
1
1
  import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
2
- import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
2
+ import { dirname, extname, isAbsolute, join, relative } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
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
- import { ensureDocsReviewWorkflow } from "@farming-labs/docs/server";
5
+ import { ensureDocsReviewWorkflow, renderDocsAgentSkillsBundle, resolveConfiguredAgentSkillsSync } from "@farming-labs/docs/server";
6
6
  import matter from "gray-matter";
7
+ import { parse } from "@babel/parser";
7
8
  import createMDX from "@next/mdx";
8
9
 
9
10
  //#region src/config.ts
@@ -144,6 +145,8 @@ const FILE_EXTS = [
144
145
  "js"
145
146
  ];
146
147
  const INTERNAL_DOCS_CONFIG_ALIAS = "@farming-labs/next-internal-docs-config";
148
+ const AGENT_SKILLS_BUNDLE_ALIAS = "@farming-labs/docs/agent-skills-bundle";
149
+ const AGENT_SKILLS_BUNDLE_PATH = ".docs/agent-skills-bundle.mjs";
147
150
  const NEXT_PACKAGE_ROOT = fileURLToPath(new URL("..", import.meta.url));
148
151
  const DEFAULT_AGENT_SPEC_ROUTE = "/api/docs/agent/spec";
149
152
  const DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE = "/.well-known/agent";
@@ -188,8 +191,8 @@ function resolvePackageSubpath(packageDir, relativePath) {
188
191
  }
189
192
  function toTurbopackAliasPath(root, value) {
190
193
  if (!isAbsolute(value)) return value;
191
- const relativePath = relative(root, value);
192
- return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
194
+ const relativePath = relative(root, value).replaceAll("\\", "/");
195
+ return relativePath === ".." || relativePath.startsWith("../") ? relativePath : relativePath.startsWith("./") ? relativePath : `./${relativePath}`;
193
196
  }
194
197
  const FUMADOCS_OPENAPI_PACKAGE_ALIAS = resolvePackageAlias("fumadocs-openapi") ?? "fumadocs-openapi";
195
198
  const FUMADOCS_CORE_PACKAGE_ALIAS = resolvePackageAlias("fumadocs-core") ?? "fumadocs-core";
@@ -545,43 +548,119 @@ function readDocsConfigPath(root) {
545
548
  }
546
549
  return "docs.config.ts";
547
550
  }
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;
551
+ function unwrapStaticExpression(expression) {
552
+ let current = expression;
553
+ while (current.type === "ParenthesizedExpression" || current.type === "TSAsExpression" || current.type === "TSSatisfiesExpression" || current.type === "TSTypeAssertion" || current.type === "TSNonNullExpression" || current.type === "TSInstantiationExpression") current = current.expression;
554
+ return current;
555
+ }
556
+ function readStaticPropertyName(property) {
557
+ const key = unwrapStaticExpression(property.key);
558
+ if (property.computed) return void 0;
559
+ if (key.type === "Identifier") return key.name;
560
+ if (key.type === "StringLiteral") return key.value;
561
+ if (key.type === "NumericLiteral") return String(key.value);
562
+ if (key.type === "BigIntLiteral") return key.value;
563
+ if (key.type === "TemplateLiteral" && key.expressions.length === 0) return key.quasis[0]?.value.cooked ?? void 0;
564
+ }
565
+ function resolveStaticObjectProperty(object, key) {
566
+ let resolution = { status: "absent" };
567
+ for (const entry of object.properties) {
568
+ if (entry.type === "SpreadElement") {
569
+ resolution = { status: "unknown" };
570
+ continue;
571
+ }
572
+ const name = readStaticPropertyName(entry);
573
+ if (name === void 0) {
574
+ if (entry.computed) resolution = { status: "unknown" };
575
+ continue;
576
+ }
577
+ if (name === "__proto__" && !entry.computed && entry.type === "ObjectProperty" && !entry.shorthand) {
578
+ if (resolution.status !== "value") resolution = { status: "unknown" };
579
+ continue;
580
+ }
581
+ if (name !== key) continue;
582
+ if (entry.type !== "ObjectProperty" || entry.shorthand) {
583
+ resolution = { status: "unknown" };
584
+ continue;
582
585
  }
586
+ resolution = {
587
+ status: "value",
588
+ value: entry.value
589
+ };
590
+ }
591
+ return resolution;
592
+ }
593
+ function hasDefineDocsImport(program, localName) {
594
+ return program.body.some((statement) => statement.type === "ImportDeclaration" && statement.importKind !== "type" && statement.source.value === "@farming-labs/docs" && statement.specifiers.some((specifier) => specifier.type === "ImportSpecifier" && specifier.importKind !== "type" && specifier.local.name === localName && specifier.imported.type === "Identifier" && specifier.imported.name === "defineDocs"));
595
+ }
596
+ function readDocsConfigRootObject(configPath, content) {
597
+ let program;
598
+ try {
599
+ const extension = extname(configPath).toLowerCase();
600
+ program = parse(content, {
601
+ sourceType: "module",
602
+ sourceFilename: configPath,
603
+ plugins: extension === ".tsx" ? [
604
+ "typescript",
605
+ "jsx",
606
+ "decorators-legacy"
607
+ ] : extension === ".ts" ? ["typescript", "decorators-legacy"] : extension === ".jsx" ? ["jsx", "decorators-legacy"] : ["decorators-legacy"],
608
+ createParenthesizedExpressions: true
609
+ }).program;
610
+ } catch (error) {
611
+ throw new Error(`withDocs could not parse ${configPath} while bundling Agent Skills: ${error instanceof Error ? error.message : String(error)}`);
583
612
  }
584
- return common;
613
+ const defaults = program.body.filter((statement) => statement.type === "ExportDefaultDeclaration");
614
+ if (defaults.length !== 1) throw new Error("withDocs could not statically determine agent.skills. Pass the live docs config as the second withDocs argument or use a literal agent object.");
615
+ let expression = unwrapStaticExpression(defaults[0].declaration);
616
+ if (expression.type === "CallExpression") {
617
+ const callee = expression.callee.type === "V8IntrinsicIdentifier" ? void 0 : unwrapStaticExpression(expression.callee);
618
+ const argument = expression.arguments[0];
619
+ if (!callee || callee.type !== "Identifier" || !hasDefineDocsImport(program, callee.name) || !argument || argument.type === "SpreadElement" || argument.type === "ArgumentPlaceholder") throw new Error("withDocs could not statically determine agent.skills. Pass the live docs config as the second withDocs argument or use a literal agent object.");
620
+ expression = unwrapStaticExpression(argument);
621
+ }
622
+ if (expression.type !== "ObjectExpression") throw new Error("withDocs could not statically determine agent.skills. Pass the live docs config as the second withDocs argument or use a literal agent object.");
623
+ return expression;
624
+ }
625
+ function readStaticStringPaths(expression) {
626
+ const value = unwrapStaticExpression(expression);
627
+ if (value.type === "StringLiteral") return [value.value];
628
+ if (value.type !== "ArrayExpression") return null;
629
+ const paths = [];
630
+ for (const entry of value.elements) {
631
+ if (!entry || entry.type === "SpreadElement") return null;
632
+ const item = unwrapStaticExpression(entry);
633
+ if (item.type !== "StringLiteral") return null;
634
+ paths.push(item.value);
635
+ }
636
+ return paths;
637
+ }
638
+ function readAgentSkillPaths(root, configPath) {
639
+ const absoluteConfigPath = join(root, configPath);
640
+ if (!existsSync(absoluteConfigPath)) return [];
641
+ const agent = resolveStaticObjectProperty(readDocsConfigRootObject(absoluteConfigPath, readFileSync(absoluteConfigPath, "utf8")), "agent");
642
+ if (agent.status === "absent") return [];
643
+ if (agent.status === "unknown") throw new Error("withDocs could not statically determine agent.skills from a composed agent config. Pass the live docs config as the second withDocs argument.");
644
+ const agentValue = unwrapStaticExpression(agent.value);
645
+ if (agentValue.type !== "ObjectExpression") throw new Error("withDocs could not statically determine agent.skills from a composed agent config. Pass the live docs config as the second withDocs argument.");
646
+ const skills = resolveStaticObjectProperty(agentValue, "skills");
647
+ if (skills.status === "absent") return [];
648
+ if (skills.status === "unknown") throw new Error("withDocs could not statically determine agent.skills from a composed agent config. Pass the live docs config as the second withDocs argument.");
649
+ const skillsValue = unwrapStaticExpression(skills.value);
650
+ let configured;
651
+ if (skillsValue.type === "ObjectExpression") {
652
+ const paths = resolveStaticObjectProperty(skillsValue, "paths");
653
+ configured = paths.status === "value" ? readStaticStringPaths(paths.value) : null;
654
+ } else configured = readStaticStringPaths(skillsValue);
655
+ 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.");
656
+ return configured;
657
+ }
658
+ function writeAgentSkillsBundle(root, configuredSkills) {
659
+ const bundlePath = join(root, AGENT_SKILLS_BUNDLE_PATH);
660
+ const source = `${GENERATED_BANNER}${renderDocsAgentSkillsBundle(resolveConfiguredAgentSkillsSync(configuredSkills, { rootDir: root }))}`;
661
+ mkdirSync(dirname(bundlePath), { recursive: true });
662
+ if (!existsSync(bundlePath) || readFileSync(bundlePath, "utf8") !== source) writeFileSync(bundlePath, source, "utf8");
663
+ return bundlePath;
585
664
  }
586
665
  /** Read the OG endpoint from docs.config.ts[x] (returns undefined if not set). */
587
666
  function readOgEndpoint(root) {
@@ -1809,8 +1888,7 @@ function withDocs(nextConfig = {}, docsConfig) {
1809
1888
  const mcp = docsConfig ? resolveMcpConfig(docsConfig.mcp) : readMcpConfig(root);
1810
1889
  const nextBasePath = normalizeRoutePrefix(nextConfig.basePath);
1811
1890
  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);
1891
+ const agentSkillsBundlePath = writeAgentSkillsBundle(root, docsConfig === void 0 || !Object.hasOwn(docsConfig, "agent") ? readAgentSkillPaths(root, docsConfigPath) : docsConfig.agent?.skills);
1814
1892
  const agentFeedback = readAgentFeedbackConfig(root);
1815
1893
  const docsConfigRelativeAlias = docsConfigPath.startsWith("./") || docsConfigPath.startsWith("../") ? docsConfigPath : `./${docsConfigPath}`;
1816
1894
  ensureDocsReviewWorkflow({
@@ -1957,6 +2035,7 @@ function withDocs(nextConfig = {}, docsConfig) {
1957
2035
  ...workspaceRoot ? createDocsWorkspaceAliases(root, workspaceRoot) : {},
1958
2036
  ...existingResolveAlias,
1959
2037
  [INTERNAL_DOCS_CONFIG_ALIAS]: docsConfigRelativeAlias,
2038
+ [AGENT_SKILLS_BUNDLE_ALIAS]: toTurbopackAliasPath(root, agentSkillsBundlePath),
1960
2039
  "fumadocs-openapi": toTurbopackAliasPath(root, FUMADOCS_OPENAPI_PACKAGE_ALIAS),
1961
2040
  "fumadocs-openapi/ui": toTurbopackAliasPath(root, FUMADOCS_OPENAPI_UI_ALIAS),
1962
2041
  "fumadocs-openapi/server": toTurbopackAliasPath(root, FUMADOCS_OPENAPI_SERVER_ALIAS),
@@ -1988,6 +2067,7 @@ function withDocs(nextConfig = {}, docsConfig) {
1988
2067
  "@farming-labs/theme/api": join(workspaceRoot, "packages", "fumadocs", "dist", "docs-api.mjs")
1989
2068
  });
1990
2069
  resolvedConfig.resolve.alias[INTERNAL_DOCS_CONFIG_ALIAS] = docsConfigAbsolutePath;
2070
+ resolvedConfig.resolve.alias[AGENT_SKILLS_BUNDLE_ALIAS] = agentSkillsBundlePath;
1991
2071
  resolvedConfig.resolve.alias["fumadocs-openapi"] = FUMADOCS_OPENAPI_PACKAGE_ALIAS;
1992
2072
  resolvedConfig.resolve.alias["fumadocs-openapi/ui"] = FUMADOCS_OPENAPI_UI_ALIAS;
1993
2073
  resolvedConfig.resolve.alias["fumadocs-openapi/server"] = FUMADOCS_OPENAPI_SERVER_ALIAS;
@@ -2028,14 +2108,12 @@ function withDocs(nextConfig = {}, docsConfig) {
2028
2108
  skillTraceFile,
2029
2109
  agentsTraceFile,
2030
2110
  agentTraceFile,
2031
- sitemapManifestTraceFile,
2032
- ...agentSkillTraceGlobs
2111
+ sitemapManifestTraceFile
2033
2112
  ])],
2034
2113
  [DEFAULT_MCP_ROUTE]: [...new Set([
2035
2114
  ...existingTracingIncludes[DEFAULT_MCP_ROUTE] ?? [],
2036
2115
  docsTraceGlob,
2037
- skillTraceFile,
2038
- ...agentSkillTraceGlobs
2116
+ skillTraceFile
2039
2117
  ])]
2040
2118
  };
2041
2119
  return withMDX(nextConfig);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farming-labs/next",
3
- "version": "0.2.61",
3
+ "version": "0.2.63",
4
4
  "description": "Next.js adapter for @farming-labs/docs — MDX config wrapper",
5
5
  "keywords": [
6
6
  "docs",
@@ -86,6 +86,7 @@
86
86
  }
87
87
  },
88
88
  "dependencies": {
89
+ "@babel/parser": "7.29.7",
89
90
  "@mdx-js/loader": "^3.1.0",
90
91
  "@mdx-js/react": "^3.1.0",
91
92
  "@next/mdx": "^16.1.6",
@@ -100,17 +101,18 @@
100
101
  "zod": "^4.3.6"
101
102
  },
102
103
  "devDependencies": {
104
+ "@babel/types": "7.29.7",
103
105
  "@types/node": "^22.10.0",
104
106
  "@types/react": "^19.2.2",
105
107
  "@types/react-dom": "^19.2.2",
106
108
  "tsdown": "^0.20.3",
107
109
  "typescript": "^5.9.3",
108
110
  "vitest": "^4.1.8",
109
- "@farming-labs/theme": "0.2.61",
110
- "@farming-labs/docs": "0.2.61"
111
+ "@farming-labs/theme": "0.2.63",
112
+ "@farming-labs/docs": "0.2.63"
111
113
  },
112
114
  "peerDependencies": {
113
- "@farming-labs/docs": ">=0.0.1",
115
+ "@farming-labs/docs": ">=0.2.61",
114
116
  "@farming-labs/theme": ">=0.0.1",
115
117
  "next": ">=16.0.0",
116
118
  "react": ">=19.2.0",