fix: preserve Mintlify routes and localized navigation

This commit is contained in:
dadachann
2026-09-04 12:31:27 +00:00
parent 289ac19a22
commit 8b052d9ab0
7 changed files with 1512 additions and 951 deletions
+480
View File
@@ -0,0 +1,480 @@
/en/api-reference/bots/create-a-bot
/en/api-reference/bots/delete-a-bot
/en/api-reference/bots/get-a-bot
/en/api-reference/bots/get-bot-event-logs
/en/api-reference/bots/list-all-bots
/en/api-reference/bots/send-a-message-through-a-bot
/en/api-reference/bots/update-a-bot
/en/api-reference/embedding-models/create-an-embedding-model
/en/api-reference/embedding-models/delete-an-embedding-model
/en/api-reference/embedding-models/get-an-embedding-model
/en/api-reference/embedding-models/list-all-embedding-models
/en/api-reference/embedding-models/test-an-embedding-model
/en/api-reference/embedding-models/update-an-embedding-model
/en/api-reference/files/upload-a-document
/en/api-reference/files/upload-an-image
/en/api-reference/knowledge-bases/add-a-file-to-a-knowledge-base
/en/api-reference/knowledge-bases/create-a-knowledge-base
/en/api-reference/knowledge-bases/delete-a-knowledge-base
/en/api-reference/knowledge-bases/get-a-knowledge-base
/en/api-reference/knowledge-bases/list-all-knowledge-bases
/en/api-reference/knowledge-bases/list-knowledge-base-files
/en/api-reference/knowledge-bases/remove-a-file-from-a-knowledge-base
/en/api-reference/knowledge-bases/retrieve-from-a-knowledge-base
/en/api-reference/knowledge-bases/update-a-knowledge-base
/en/api-reference/knowledge-engines/get-the-knowledge-engine-creation-schema
/en/api-reference/knowledge-engines/get-the-knowledge-engine-retrieval-schema
/en/api-reference/knowledge-engines/list-available-knowledge-engines
/en/api-reference/llm-models/create-an-llm-model
/en/api-reference/llm-models/delete-an-llm-model
/en/api-reference/llm-models/get-an-llm-model
/en/api-reference/llm-models/list-all-llm-models
/en/api-reference/llm-models/test-an-llm-model
/en/api-reference/llm-models/update-an-llm-model
/en/api-reference/model-providers/create-a-model-provider
/en/api-reference/model-providers/delete-a-model-provider
/en/api-reference/model-providers/get-a-model-provider
/en/api-reference/model-providers/list-all-model-providers
/en/api-reference/model-providers/update-a-model-provider
/en/api-reference/parsers/list-available-parsers
/en/api-reference/pipelines/copy-a-pipeline
/en/api-reference/pipelines/create-a-pipeline
/en/api-reference/pipelines/delete-a-pipeline
/en/api-reference/pipelines/get-a-pipeline
/en/api-reference/pipelines/get-pipeline-extension-configuration
/en/api-reference/pipelines/get-pipeline-metadata
/en/api-reference/pipelines/list-all-pipelines
/en/api-reference/pipelines/update-a-pipeline
/en/api-reference/pipelines/update-pipeline-extension-configuration
/en/api-reference/plugins/delete-a-plugin
/en/api-reference/plugins/get-plugin-configuration
/en/api-reference/plugins/get-plugin-details
/en/api-reference/plugins/install-a-plugin-from-github
/en/api-reference/plugins/install-a-plugin-from-marketplace
/en/api-reference/plugins/list-installed-plugins
/en/api-reference/plugins/update-plugin-configuration
/en/api-reference/plugins/upgrade-a-plugin
/en/api-reference/system/get-system-information
/en/api-reference/tools/get-tool-detail
/en/api-reference/tools/list-all-available-tools
/en/articles
/en/articles/connect-deepseek-to-wechat
/en/articles/deploy-ai-bot-in-5-minutes
/en/articles/dify-agent-discord-telegram-slack
/en/articles/dify-langbot-rag-knowledge-base
/en/articles/langbot-cloud-multi-user-workspaces
/en/articles/langbot-plugin-system-deep-dive
/en/articles/langbot-v4100-sandbox-skills
/en/articles/langbot-v490-rag-plugin-architecture
/en/articles/langflow-drag-and-drop-ai-bot
/en/articles/langtars-remote-pc-control-dify-n8n
/en/articles/n8n-multi-platform-ai-chatbot
/en/articles/welcome
/en/deploy/command
/en/deploy/langbot/docker
/en/deploy/langbot/kubernetes
/en/deploy/langbot/manual
/en/deploy/langbot/one-click/1panel
/en/deploy/langbot/one-click/bt
/en/deploy/langbot/package
/en/deploy/settings
/en/deploy/update
/en/develop/agent-context
/en/develop/comp-arch
/en/develop/dev-config
/en/develop/plugin-runtime
/en/insight/community
/en/insight/data-collection-policy
/en/insight/features
/en/insight/guide
/en/insight/platform-features
/en/insight/troubleshooting
/en/plugin/compatibility
/en/plugin/dev/apis/common
/en/plugin/dev/apis/messages
/en/plugin/dev/apis/pipeline-events
/en/plugin/dev/apis/tech-details
/en/plugin/dev/basic-info
/en/plugin/dev/components/add
/en/plugin/dev/components/command
/en/plugin/dev/components/event-listener
/en/plugin/dev/components/knowledge-engine
/en/plugin/dev/components/knowledge-retriever
/en/plugin/dev/components/page
/en/plugin/dev/components/parser
/en/plugin/dev/components/tool
/en/plugin/dev/directory-structure
/en/plugin/dev/migration
/en/plugin/dev/publish/github
/en/plugin/dev/publish/market
/en/plugin/dev/style
/en/plugin/dev/tutor
/en/plugin/plugin-intro
/en/tags/readme
/en/tags/webhook
/en/usage/knowledge/readme
/en/usage/mcp/readme
/en/usage/models/readme
/en/usage/pipelines/coze
/en/usage/pipelines/deerflow
/en/usage/pipelines/dify
/en/usage/pipelines/langflow
/en/usage/pipelines/n8n
/en/usage/pipelines/readme
/en/usage/pipelines/weknora
/en/usage/platforms/dingtalk
/en/usage/platforms/discord
/en/usage/platforms/http-bot
/en/usage/platforms/kook
/en/usage/platforms/lark
/en/usage/platforms/line
/en/usage/platforms/qq/aiocqhttp/lagrange
/en/usage/platforms/qq/aiocqhttp/llonebot
/en/usage/platforms/qq/aiocqhttp/napcat
/en/usage/platforms/qq/official_webhook
/en/usage/platforms/readme
/en/usage/platforms/satori/llonebot
/en/usage/platforms/slack
/en/usage/platforms/telegram
/en/usage/platforms/webpage
/en/usage/platforms/wechat/weixin
/en/usage/platforms/wecom/wecom
/en/usage/platforms/wecom/wecombot
/en/usage/platforms/wecom/wecomcs
/en/usage/platforms/wxoa
/en/usage/sandbox/config
/en/usage/sandbox/readme
/en/usage/sandbox/runtime-relationships
/en/usage/skills/readme
/en/workshop/302ai-integration
/en/workshop/impl-platform-adapter
/en/workshop/mcp-details
/en/workshop/network-details
/en/workshop/newapi-integration
/en/workshop/ppio-integration
/en/workshop/production/proxy-and-ssl
/ja/api-reference/bots/创建机器人
/ja/api-reference/bots/删除机器人
/ja/api-reference/bots/更新机器人
/ja/api-reference/bots/获取单个机器人
/ja/api-reference/bots/获取所有机器人
/ja/api-reference/bots/获取机器人事件日志
/ja/api-reference/bots/通过机器人发送消息
/ja/api-reference/embedding-models/创建向量模型
/ja/api-reference/embedding-models/删除向量模型
/ja/api-reference/embedding-models/更新向量模型
/ja/api-reference/embedding-models/测试向量模型
/ja/api-reference/embedding-models/获取单个向量模型
/ja/api-reference/embedding-models/获取所有向量模型
/ja/api-reference/files/上传图片
/ja/api-reference/files/上传文档
/ja/api-reference/knowledge-bases/从知识库删除文件
/ja/api-reference/knowledge-bases/创建知识库
/ja/api-reference/knowledge-bases/删除知识库
/ja/api-reference/knowledge-bases/将文件存入知识库
/ja/api-reference/knowledge-bases/更新知识库
/ja/api-reference/knowledge-bases/检索知识库
/ja/api-reference/knowledge-bases/获取单个知识库
/ja/api-reference/knowledge-bases/获取所有知识库
/ja/api-reference/knowledge-bases/获取知识库文件列表
/ja/api-reference/knowledge-engines/获取可用知识引擎列表
/ja/api-reference/knowledge-engines/获取知识引擎创建配置模式
/ja/api-reference/knowledge-engines/获取知识引擎检索配置模式
/ja/api-reference/llm-models/创建-llm-模型
/ja/api-reference/llm-models/删除-llm-模型
/ja/api-reference/llm-models/更新-llm-模型
/ja/api-reference/llm-models/测试-llm-模型
/ja/api-reference/llm-models/获取单个-llm-模型
/ja/api-reference/llm-models/获取所有-llm-模型
/ja/api-reference/model-providers/创建供应商
/ja/api-reference/model-providers/删除供应商
/ja/api-reference/model-providers/更新供应商
/ja/api-reference/model-providers/获取单个供应商
/ja/api-reference/model-providers/获取所有供应商
/ja/api-reference/parsers/获取可用解析器列表
/ja/api-reference/pipelines/创建流水线
/ja/api-reference/pipelines/删除流水线
/ja/api-reference/pipelines/复制流水线
/ja/api-reference/pipelines/更新流水线
/ja/api-reference/pipelines/更新流水线扩展配置
/ja/api-reference/pipelines/获取单个流水线
/ja/api-reference/pipelines/获取所有流水线
/ja/api-reference/pipelines/获取流水线元数据
/ja/api-reference/pipelines/获取流水线扩展配置
/ja/api-reference/plugins/从-github-安装插件
/ja/api-reference/plugins/从-marketplace-安装插件
/ja/api-reference/plugins/删除插件
/ja/api-reference/plugins/升级插件
/ja/api-reference/plugins/更新插件配置
/ja/api-reference/plugins/获取已安装插件列表
/ja/api-reference/plugins/获取插件详情
/ja/api-reference/plugins/获取插件配置
/ja/api-reference/system/获取系统信息
/ja/api-reference/ツール/ツール詳細を取得
/ja/api-reference/ツール/利用可能なツール一覧を取得
/ja/articles
/ja/articles/connect-deepseek-to-wechat
/ja/articles/deploy-ai-bot-in-5-minutes
/ja/articles/dify-agent-discord-telegram-slack
/ja/articles/dify-langbot-rag-knowledge-base
/ja/articles/langbot-cloud-multi-user-workspaces
/ja/articles/langbot-plugin-system-deep-dive
/ja/articles/langbot-v4100-sandbox-skills
/ja/articles/langbot-v490-rag-plugin-architecture
/ja/articles/langflow-drag-and-drop-ai-bot
/ja/articles/langtars-remote-pc-control-dify-n8n
/ja/articles/n8n-multi-platform-ai-chatbot
/ja/articles/welcome
/ja/deploy/command
/ja/deploy/langbot/docker
/ja/deploy/langbot/kubernetes
/ja/deploy/langbot/manual
/ja/deploy/langbot/one-click/1panel
/ja/deploy/langbot/one-click/bt
/ja/deploy/langbot/package
/ja/deploy/settings
/ja/deploy/update
/ja/develop/agent-context
/ja/develop/comp-arch
/ja/develop/dev-config
/ja/develop/plugin-runtime
/ja/insight/community
/ja/insight/data-collection-policy
/ja/insight/features
/ja/insight/guide
/ja/insight/platform-features
/ja/insight/troubleshooting
/ja/plugin/compatibility
/ja/plugin/dev/apis/common
/ja/plugin/dev/apis/messages
/ja/plugin/dev/apis/pipeline-events
/ja/plugin/dev/apis/tech-details
/ja/plugin/dev/basic-info
/ja/plugin/dev/components/add
/ja/plugin/dev/components/command
/ja/plugin/dev/components/event-listener
/ja/plugin/dev/components/knowledge-engine
/ja/plugin/dev/components/knowledge-retriever
/ja/plugin/dev/components/page
/ja/plugin/dev/components/parser
/ja/plugin/dev/components/tool
/ja/plugin/dev/directory-structure
/ja/plugin/dev/migration
/ja/plugin/dev/publish/github
/ja/plugin/dev/publish/market
/ja/plugin/dev/style
/ja/plugin/dev/tutor
/ja/plugin/plugin-intro
/ja/tags/readme
/ja/tags/webhook
/ja/usage/knowledge/readme
/ja/usage/mcp/readme
/ja/usage/models/readme
/ja/usage/pipelines/coze
/ja/usage/pipelines/deerflow
/ja/usage/pipelines/dify
/ja/usage/pipelines/langflow
/ja/usage/pipelines/n8n
/ja/usage/pipelines/readme
/ja/usage/pipelines/weknora
/ja/usage/platforms/dingtalk
/ja/usage/platforms/discord
/ja/usage/platforms/http-bot
/ja/usage/platforms/kook
/ja/usage/platforms/lark
/ja/usage/platforms/line
/ja/usage/platforms/qq/aiocqhttp/lagrange
/ja/usage/platforms/qq/aiocqhttp/llonebot
/ja/usage/platforms/qq/aiocqhttp/napcat
/ja/usage/platforms/qq/official_webhook
/ja/usage/platforms/readme
/ja/usage/platforms/satori/llonebot
/ja/usage/platforms/slack
/ja/usage/platforms/telegram
/ja/usage/platforms/webpage
/ja/usage/platforms/wechat/weixin
/ja/usage/platforms/wecom/wecom
/ja/usage/platforms/wecom/wecombot
/ja/usage/platforms/wecom/wecomcs
/ja/usage/platforms/wxoa
/ja/usage/sandbox/config
/ja/usage/sandbox/readme
/ja/usage/sandbox/runtime-relationships
/ja/usage/skills/readme
/ja/workshop/302ai-integration
/ja/workshop/impl-platform-adapter
/ja/workshop/mcp-details
/ja/workshop/network-details
/ja/workshop/newapi-integration
/ja/workshop/ppio-integration
/ja/workshop/production/proxy-and-ssl
/scripts/README-blog-articles
/zh/api-reference/向量模型/创建向量模型
/zh/api-reference/向量模型/删除向量模型
/zh/api-reference/向量模型/更新向量模型
/zh/api-reference/向量模型/测试向量模型
/zh/api-reference/向量模型/获取单个向量模型
/zh/api-reference/向量模型/获取所有向量模型
/zh/api-reference/大语言模型/创建-llm-模型
/zh/api-reference/大语言模型/删除-llm-模型
/zh/api-reference/大语言模型/更新-llm-模型
/zh/api-reference/大语言模型/测试-llm-模型
/zh/api-reference/大语言模型/获取单个-llm-模型
/zh/api-reference/大语言模型/获取所有-llm-模型
/zh/api-reference/工具/获取工具详情
/zh/api-reference/工具/获取所有可用工具
/zh/api-reference/插件/从-github-安装插件
/zh/api-reference/插件/从-marketplace-安装插件
/zh/api-reference/插件/删除插件
/zh/api-reference/插件/升级插件
/zh/api-reference/插件/更新插件配置
/zh/api-reference/插件/获取已安装插件列表
/zh/api-reference/插件/获取插件详情
/zh/api-reference/插件/获取插件配置
/zh/api-reference/文件/上传图片
/zh/api-reference/文件/上传文档
/zh/api-reference/机器人/创建机器人
/zh/api-reference/机器人/删除机器人
/zh/api-reference/机器人/更新机器人
/zh/api-reference/机器人/获取单个机器人
/zh/api-reference/机器人/获取所有机器人
/zh/api-reference/机器人/获取机器人事件日志
/zh/api-reference/机器人/通过机器人发送消息
/zh/api-reference/模型供应商/创建供应商
/zh/api-reference/模型供应商/删除供应商
/zh/api-reference/模型供应商/更新供应商
/zh/api-reference/模型供应商/获取单个供应商
/zh/api-reference/模型供应商/获取所有供应商
/zh/api-reference/流水线/创建流水线
/zh/api-reference/流水线/删除流水线
/zh/api-reference/流水线/复制流水线
/zh/api-reference/流水线/更新流水线
/zh/api-reference/流水线/更新流水线扩展配置
/zh/api-reference/流水线/获取单个流水线
/zh/api-reference/流水线/获取所有流水线
/zh/api-reference/流水线/获取流水线元数据
/zh/api-reference/流水线/获取流水线扩展配置
/zh/api-reference/知识库/从知识库删除文件
/zh/api-reference/知识库/创建知识库
/zh/api-reference/知识库/删除知识库
/zh/api-reference/知识库/将文件存入知识库
/zh/api-reference/知识库/更新知识库
/zh/api-reference/知识库/检索知识库
/zh/api-reference/知识库/获取单个知识库
/zh/api-reference/知识库/获取所有知识库
/zh/api-reference/知识库/获取知识库文件列表
/zh/api-reference/知识引擎/获取可用知识引擎列表
/zh/api-reference/知识引擎/获取知识引擎创建配置模式
/zh/api-reference/知识引擎/获取知识引擎检索配置模式
/zh/api-reference/系统/获取系统信息
/zh/api-reference/解析器/获取可用解析器列表
/zh/articles
/zh/articles/connect-deepseek-to-wechat
/zh/articles/deploy-ai-bot-in-5-minutes
/zh/articles/dify-agent-discord-telegram-slack
/zh/articles/dify-langbot-rag-knowledge-base
/zh/articles/langbot-cloud-multi-user-workspaces
/zh/articles/langbot-plugin-system-deep-dive
/zh/articles/langbot-v4100-sandbox-skills
/zh/articles/langbot-v490-rag-plugin-architecture
/zh/articles/langflow-drag-and-drop-ai-bot
/zh/articles/langtars-remote-pc-control-dify-n8n
/zh/articles/n8n-multi-platform-ai-chatbot
/zh/articles/welcome
/zh/deploy/command
/zh/deploy/langbot/docker
/zh/deploy/langbot/kubernetes
/zh/deploy/langbot/manual
/zh/deploy/langbot/one-click/1panel
/zh/deploy/langbot/one-click/bt
/zh/deploy/langbot/package
/zh/deploy/settings
/zh/deploy/update
/zh/develop/adapter/discord/README
/zh/develop/adapter/discord/api_reference
/zh/develop/adapter/discord/design
/zh/develop/adapter/discord/quick_start
/zh/develop/adapter/discord/troubleshooting
/zh/develop/agent-context
/zh/develop/comp-arch
/zh/develop/dev-config
/zh/develop/plugin-runtime
/zh/insight/community
/zh/insight/data-collection-policy
/zh/insight/features
/zh/insight/guide
/zh/insight/platform-features
/zh/insight/troubleshooting
/zh/plugin/compatibility
/zh/plugin/dev/apis/common
/zh/plugin/dev/apis/messages
/zh/plugin/dev/apis/pipeline-events
/zh/plugin/dev/apis/tech-details
/zh/plugin/dev/basic-info
/zh/plugin/dev/components/add
/zh/plugin/dev/components/command
/zh/plugin/dev/components/event-listener
/zh/plugin/dev/components/knowledge-engine
/zh/plugin/dev/components/knowledge-retriever
/zh/plugin/dev/components/page
/zh/plugin/dev/components/parser
/zh/plugin/dev/components/tool
/zh/plugin/dev/directory-structure
/zh/plugin/dev/migration
/zh/plugin/dev/publish/github
/zh/plugin/dev/publish/market
/zh/plugin/dev/style
/zh/plugin/dev/tutor
/zh/plugin/plugin-intro
/zh/tags/readme
/zh/tags/webhook
/zh/tob
/zh/tob/contact
/zh/tob/platform
/zh/tob/provider
/zh/usage/knowledge/readme
/zh/usage/mcp/readme
/zh/usage/models/readme
/zh/usage/pipelines/coze
/zh/usage/pipelines/deerflow
/zh/usage/pipelines/dify
/zh/usage/pipelines/langflow
/zh/usage/pipelines/n8n
/zh/usage/pipelines/readme
/zh/usage/pipelines/tbox
/zh/usage/pipelines/weknora
/zh/usage/platforms/dingtalk
/zh/usage/platforms/discord
/zh/usage/platforms/http-bot
/zh/usage/platforms/kook
/zh/usage/platforms/lark
/zh/usage/platforms/line
/zh/usage/platforms/qq/aiocqhttp/lagrange
/zh/usage/platforms/qq/aiocqhttp/llonebot
/zh/usage/platforms/qq/aiocqhttp/napcat
/zh/usage/platforms/qq/official
/zh/usage/platforms/qq/official_webhook
/zh/usage/platforms/readme
/zh/usage/platforms/satori/llonebot
/zh/usage/platforms/slack
/zh/usage/platforms/telegram
/zh/usage/platforms/webpage
/zh/usage/platforms/wechat/wechatpad
/zh/usage/platforms/wechat/weixin
/zh/usage/platforms/wecom/wecom
/zh/usage/platforms/wecom/wecombot
/zh/usage/platforms/wecom/wecomcs
/zh/usage/platforms/wxoa
/zh/usage/sandbox/config
/zh/usage/sandbox/readme
/zh/usage/sandbox/runtime-relationships
/zh/usage/skills/readme
/zh/workshop/302ai-integration
/zh/workshop/impl-platform-adapter
/zh/workshop/mcp-details
/zh/workshop/network-details
/zh/workshop/newapi-integration
/zh/workshop/ppio-integration
/zh/workshop/production/proxy-and-ssl
/zh/workshop/shengsuanyun-integration
/zh/workshop/tokenpony-integration
+9 -6
View File
@@ -144,14 +144,15 @@ test("canonical MDX and nested OpenAPI sources are discovered", async () => {
test("Cloudflare redirects preserve all Mintlify routes and add root", async () => {
const docs = JSON.parse(await readFile(path.join(root, "docs.json"), "utf8"));
const spec = JSON.parse(await readFile(path.join(root, "openapi/service-api-en.json"), "utf8"));
const lines = renderCloudflareRedirects(docs, spec).trim().split("\n");
assert.equal(lines.length, docs.redirects.length + 1);
const lines = renderCloudflareRedirects(docs).trim().split("\n");
assert.equal(lines.length, docs.redirects.length + 3);
assert.equal(lines[0], "/ /en/insight/guide 302");
assert.ok(lines.includes("/README_EN /en/insight/guide 308"));
assert.ok(lines.includes("/zh/develop/adapter/discord /zh/develop/adapter/discord/README 308"));
assert.ok(lines.includes("/scripts/README-blog-articles /en/articles 308"));
assert.ok(lines.some((line) => line.startsWith("/en/deploy/platforms/* /en/usage/platforms/:splat ")));
assert.ok(lines.includes("/en/api-reference/system/获取系统信息 /en/api-reference/api/v1/system/info/get 308"));
assert.ok(lines.includes("/en/api-reference/model-providers/创建供应商 /en/api-reference/api/v1/provider/providers/post 308"));
assert.ok(lines.includes("/en/api-reference/system/获取系统信息 /en/api-reference/system/get-system-information 308"));
assert.ok(lines.includes("/en/api-reference/model-providers/创建供应商 /en/api-reference/model-providers/create-a-model-provider 308"));
});
test("Fumapress config wires static localized docs, OpenAPI, and Mintlify support", async () => {
@@ -165,6 +166,8 @@ test("Fumapress config wires static localized docs, OpenAPI, and Mintlify suppor
assert.match(config, new RegExp(`input:[\\s\\S]*openapi/${filename}`));
assert.match(config, new RegExp(`staticSource\\(\\{\\s*baseDir:\\s*["']${locale}/api-reference["']`));
}
assert.match(config, /groupBy:\s*["']tag["']/);
assert.match(config, /legacyOpenApiSlug/);
assert.match(config, /mintlifyPlugin\(/);
assert.match(config, /openapiPlugin\(\{\s*server:\s*\w+OpenAPI\s*\}\)/);
assert.match(config, /fumadocsMdx\(/);
@@ -179,7 +182,7 @@ test("prebuild is deterministic and preserves local assets and SEO files", async
assert.equal(first.documents, 302);
assert.equal(first.fallbackDefaults, 0);
assert.equal(first.localeOnlyDocuments, 14);
assert.equal(first.redirects, 102);
assert.equal(first.redirects, 104);
assert.deepEqual(first.locales, ["en", "zh", "ja"]);
await readFile(path.join(temp, "content/docs/insight/guide.mdx"), "utf8");
await readFile(path.join(temp, "content/docs/insight/guide.zh.mdx"), "utf8");
+38 -17
View File
@@ -67,24 +67,36 @@ test("representative localized guides and canonical host are emitted", async ()
test("all locales emit the OpenAPI surface with endpoint semantics", async () => {
const representatives = {
en: ["Get system information", "GET", "/api/v1/system/info"],
zh: ["获取系统信息", "GET", "/api/v1/system/info"],
ja: ["获取系统信息", "GET", "/api/v1/system/info"],
en: ["system/get-system-information", "Get system information", "GET", "/api/v1/system/info"],
zh: ["系统/获取系统信息", "获取系统信息", "GET", "/api/v1/system/info"],
ja: ["system/获取系统信息", "获取系统信息", "GET", "/api/v1/system/info"],
};
const sitemap = await readFile(path.join(publicRoot, "sitemap.xml"), "utf8");
for (const [locale, semantics] of Object.entries(representatives)) {
for (const [locale, [representativePath, ...semantics]] of Object.entries(representatives)) {
const apiRoot = path.join(publicRoot, locale, "api-reference");
const routes = await collectHtmlRoutes(apiRoot);
assert.ok(routes.length >= 50, `${locale} emitted only ${routes.length} API routes`);
const representative = path.join(apiRoot, "api/v1/system/info/get/index.html");
const encodedRepresentativePath = representativePath.split("/").map((segment) => encodeURIComponent(segment)).join("/");
const representative = path.join(apiRoot, encodedRepresentativePath, "index.html");
const html = await readFile(representative, "utf8");
for (const semantic of semantics) assert.ok(html.includes(semantic), `${locale} page is missing ${semantic}`);
assert.ok(sitemap.includes(`https://docs.langbot.dev/${locale}/api-reference/api/v1/system/info/get`));
assert.ok(sitemap.includes(`https://docs.langbot.dev/${locale}/api-reference/${encodedRepresentativePath}`));
}
});
test("canonical sitemap routes match the legacy Mintlify directory structure", async () => {
const legacy = new Set((await readFile(path.join(root, "tests/fixtures/legacy-sitemap-routes.txt"), "utf8"))
.trim().split("\n").map((route) => route.replace(/\/$/, "")));
// Mintlify accidentally indexed this repository maintenance note; preserve
// its URL as a redirect without treating it as public documentation.
legacy.delete("/scripts/README-blog-articles");
const sitemap = await readFile(path.join(publicRoot, "sitemap.xml"), "utf8");
const actual = new Set([...sitemap.matchAll(/<loc>https:\/\/docs\.langbot\.dev(\/[^<]+)<\/loc>/g)]
.map((match) => decodeURIComponent(match[1]).replace(/\/$/, "")));
assert.deepEqual([...actual].sort(), [...legacy].sort());
});
const locales = ["en", "zh", "ja"];
const hreflangByLocale = { en: "en", zh: "zh-CN", ja: "ja" };
@@ -120,11 +132,11 @@ function extractHeadLinks(html, rel) {
}));
}
test("rendered navbar uses each locale's docs.json labels and hrefs", async () => {
test("rendered navbar and documentation tree use each locale's docs.json labels", async () => {
const expected = {
en: ["Home", "https://langbot.app/en", "Roadmap", "https://langbot.app/en/roadmap"],
zh: ["首页", "https://langbot.app/zh", "路线图", "https://langbot.app/zh/roadmap"],
ja: ["ホーム", "https://langbot.app/ja", "ロードマップ", "https://langbot.app/ja/roadmap"],
en: ["Home", "https://langbot.app/en", "Roadmap", "https://langbot.app/en/roadmap", "Guides", "Quick Start", "Installation", "Developers", "Articles", "API Reference"],
zh: ["首页", "https://langbot.app/zh", "路线图", "https://langbot.app/zh/roadmap", "指南", "快速开始", "安装部署", "开发者", "文章", "API 参考"],
ja: ["ホーム", "https://langbot.app/ja", "ロードマップ", "https://langbot.app/ja/roadmap", "ガイド", "クイックスタート", "インストール", "開発者", "記事", "API リファレンス"],
};
for (const locale of locales) {
const html = await readFile(path.join(publicRoot, locale, "insight/guide/index.html"), "utf8");
@@ -179,18 +191,27 @@ test("every page emits a canonical and reciprocal available hreflangs", async ()
const [, locale, ...suffixParts] = route.split("/");
const suffix = suffixParts.join("/");
const expected = {};
for (const candidate of locales) {
const candidateRoute = `/${candidate}/${suffix}`.replace(/\/$/, "");
if (routes.has(candidateRoute)) expected[hreflangByLocale[candidate]] = `https://docs.langbot.dev${candidateRoute}`;
}
expected["x-default"] = expected.en ?? expected["zh-CN"] ?? expected.ja;
const actual = Object.fromEntries(
extractHeadLinks(html, "alternate")
.filter((item) => item.hreflang)
.map((item) => [item.hreflang, item.href]),
);
assert.deepEqual(actual, expected, route);
if (suffix.startsWith("api-reference/")) {
assert.deepEqual(Object.keys(actual).sort(), ["en", "ja", "x-default", "zh-CN"]);
assert.equal(actual["x-default"], actual.en);
for (const href of Object.values(actual)) {
const target = new URL(href).pathname.replace(/\/$/, "");
assert.ok(routes.has(target), `${route} links to missing alternate ${target}`);
}
} else {
const expected = {};
for (const candidate of locales) {
const candidateRoute = `/${candidate}/${suffix}`.replace(/\/$/, "");
if (routes.has(candidateRoute)) expected[hreflangByLocale[candidate]] = `https://docs.langbot.dev${candidateRoute}`;
}
expected["x-default"] = expected.en ?? expected["zh-CN"] ?? expected.ja;
assert.deepEqual(actual, expected, route);
}
}
});