首页
/ Supabase 文档 spec 流水线:从 OpenAPI 与 TypeDoc 规格文件生成 API 参考文档

Supabase 文档 spec 流水线:从 OpenAPI 与 TypeDoc 规格文件生成 API 参考文档

2026-09-06 13:33:34作者:乔或婵

Supabase 文档站(apps/docs)的参考文档并不是手写出来的,而是由一组“规格文件”(spec files)自动生成的。本文基于 apps/docs/spec/README.md 的核心描述展开,结合同目录下的 Makefile 与生成脚本源码,完整拆解这条“下载 → 转换 → 生成 → 格式化”的文档生成流水线,帮助读者理解 Supabase 的 Management API、Storage、Auth 等参考页面是如何从上游 OpenAPI / TypeDoc 规格中构建出来的,以及如何本地运行和验证这条流水线。

spec 目录的角色:规格文件是参考文档的唯一来源

apps/docs/spec/README.md 的核心陈述只有一句:这些 spec 文件用于生成参考文档(reference documentation)。也就是说,apps/docs/spec/ 目录是文档站的“数据源层”,里面存放了以下几类物料:

  • OpenAPI 规格:如 api_v1_openapi.json / api_v2_openapi.json(Management API)、storage_v0_openapi.jsonauth_v1_openapi.json 等,以及对应的 *_config.yaml 配置文件;
  • TypeDoc 规格enrichments/tsdoc_v1/ 下存放各 JS SDK 包(supabase-js、auth-js/gotrue、postgrest-js、realtime-js、storage-js、functions-js)的类型文档 JSON;
  • 共享章节树common-api-sections.jsoncommon-client-libs-sections.jsoncommon-cli-sections.jsoncommon-cli.yml 等,定义文档站各产品章节的组织结构;
  • 客户端 SDK 规格supabase_js_v1.ymlsupabase_dart_v2.ymlsupabase_kt_v3.ymlsupabase_py_v2.ymlsupabase_swift_v2.ymlsupabase_csharp_v1.yml 等按语言/版本划分的 YAML 规格;
  • 生成脚本sections/generateMgmtApiSections.ctssections/generateAccessControlPartials.mts,负责把 OpenAPI 规格转换为文档章节和权限表。

从源码结构看,这个目录与文档站的构建脚本紧密耦合:apps/docs/package.json 中的 codegen:references 脚本会同时驱动旧版 YAML 管线(Reference.generated.script.ts)和新版 TypeDoc 管线(scripts/build-reference-content.ts),两者都以 apps/docs/spec/ 为输入。

快速上手:make init 与 make

README 给出的上手步骤只有两步:

make init   # 安装全部依赖

make        # 下载并转换 spec 文件,生成文档内容

需要说明两点适用前提:

  1. 当前仓库根目录的 Makefilehelp 输出中并没有列出 init 目标,可以推断 README 中的 make init 描述来自较早的仓库阶段;在现有仓库中,依赖安装实际由 pnpm workspace(pnpm-workspace.yaml + 根 package.json)承担,即 pnpm install 后执行 Make 命令即可。
  2. README 中的 make 指的是在 apps/docs/spec/ 目录下执行。由于 spec/Makefile 的第一个目标是 run,默认目标即:
run: download transform generate format

因此一次 make 会顺序执行四个阶段。Makefile 顶部还定义了 GENERATOR_DIR=../../../packages/generator,指向仓库内的 packages/generator 工具包,供生成脚本复用。

四阶段流水线拆解

download:从各服务拉取最新规格源

Makefiledownload 目标聚合了当前启用的下载任务:

download: download.api.v1 download.mcp-tools-permissions download.storage.v1 download.tsdoc.v2 download.server.v1 download.middleware.v1

各任务的来源与产物:

目标 来源 产物(写入 spec 目录)
download.api.v1 Management API 的 /api/v1-json/api/v2-json 端点 api_v1_openapi.jsonapi_v2_openapi.json
download.mcp-tools-permissions Management API 的 platform/mcp-tools-permissions 投影端点 mcp_tools_permissions.json(下载后用 Prettier 格式化)
download.storage.v1 Storage 服务的 OpenAPI 文档 storage_v0_openapi.json
download.tsdoc.v2 各 JS SDK 包发布的 TypeDoc JSON(supabase-js、auth-js、postgrest-js、realtime-js、storage-js、functions-js 共 6 个包) spec/reference/javascript/v2/*.json
download.server.v1 @supabase/server 的 TypeDoc 规格 spec/reference/server/v1/server.json
download.middleware.v1 @supabase/middleware 的 TypeDoc 规格 spec/reference/middleware/v1/middleware.json

Makefile 中保留了若干被注释的目标,记录了规格的演化历史,值得注意:

  • download.auth.v1 被注释掉,注释说明 GoTrue(Auth)的 OpenAPI 目前走人工流程:先从 GoTrue 的 swagger.json 获取 Swagger 2.0 定义,再手动转换为 OpenAPI 3.0 后写入 auth_v1_openapi.json
  • download.tsdoc.v1(v1 SDK 的 TypeDoc)标注为“不再更新”,v1 客户端规格改为由 enrichments/tsdoc_v1/ 中已提交的 JSON 承担;
  • download.analytics.v0 从 Logflare 拉取 OpenAPI,配套了独立的 validate.analytics.v0 校验目标(redocly lint --extends=minimal)。

transform:用 Redocly bundle 展开 OpenAPI $ref

transform 目标(Makefile)对三份 OpenAPI 规格做“解引用”(dereference),产物统一落到 transforms/ 目录:

transform: dereference.api.v1 dereference.auth.v1 dereference.storage.v0

具体命令形如:

npx --package=@redocly/cli redocly bundle --dereferenced \
  -o transforms/auth_v1_openapi_deparsed.json auth_v1_openapi.json

这里有一个在 Makefile 中明确注释的工程细节:api_v1 / api_v2 的 bundle 故意不带 --dereferenced,因为 api_v2_openapi.json 中存在循环引用(APIErrorObject.issues -> APIErrorObject),Redocly 无法将其扁平化为 JSON;未解析的 $ref 会在后续的 writeApiReferenceSections 逻辑中手动处理。其余规格(auth、storage)则用 --dereferenced 完全展开。

generate:从 OpenAPI 生成章节导航与权限表

generate 目标(Makefile)包含两个生成任务:

generate: generate.sections.api.v1 generate.partials.access-control

任务一:Management API 章节generateMgmtApiSections.cts 接收 transforms/ 下的 v1 与 v2 deparsed 规格,输出 common-api-sections.json。其核心逻辑(见该文件 extractSectionsFromOpenApi 函数)是:

  1. 将 v1、v2 的 paths 合并,后者覆盖前者Object.assign 按文件顺序合并,v2 优先);
  2. 遍历每个 route × HTTP method,跳过带 x-internal 标记的内部端点;
  3. tags[0] 作为分类(category),按 operationId 生成条目,operationId 必须匹配 ^[a-z0-9]+(?:-[a-z0-9]+)*$,标题则由 slug 反向转换(去掉 v\d+ 前缀、连字符转空格、首字母大写);
  4. 输出 type: 'category' 的嵌套 sections 数组,供文档站渲染侧边栏导航。

任务二:访问控制(Scoped PAT / MCP 工具)权限表generateAccessControlPartials.mts 的输入除了 v1/v2 deparsed 规格外,还有 mcp_tools_permissions.json,以及 Studio 侧维护的共享权限目录 packages/shared-data/scoped-access-token-permissions.tsPERMISSION_CATALOG_BY_CATEGORYPERMISSION_MODE_LABEL)。脚本从每个端点读取 x-fga-permissions 字段(一组 scope 组替代项),按资源/访问级别聚合出权限行,最终写入:

脚本在生成内容顶部写入固定注释 {/* Generated by 'make ... generate.partials.access-control'. Do not hand-edit ... */},并内置了一张 WORD_FIXES 映射表(api→APIsso→SSOpit r→PITRpgbouncer→PgBouncer 等),保证生成的 Markdown 术语大小写与文档站风格一致。生成后再执行 prettier --write 统一格式。

format:Prettier 统一格式,便于 Git 追踪变更

最后一个阶段非常务实(Makefile):

format:
	npx prettier --cache --write .

注释说明了动机:规格文件体量巨大且频繁变动,用 Prettier 规范化后 Git 的 diff 才稳定可读——这也是 download.mcp-tools-permissions 单独再跑一次 Prettier 的原因。

客户端 SDK 规格:legacy YAML 与新版 TypeDoc 管线

spec 目录还承载客户端库参考文档的生成,这里存在新旧两条管线,均以 spec 目录为输入:

旧管线(legacy):各语言 SDK 的 supabase_<lib>_v<ver>.yml(如 supabase_js_v2.ymlsupabase_dart_v2.yml)加上共享章节树 common-client-libs-sections.json,由 apps/docs/features/docs/Reference.generated.script.ts 驱动生成 features/docs/generated/<sdk>.<version>.*.json。对应 apps/docspnpm codegen:references:legacy

新管线(TypeDoc)spec/reference/<lib>/<ver>/ 下存放下载得到的 TypeDoc JSON dump(gitignored 构建产物)与手工维护的 config.jsonpartials/,由 apps/docs/scripts/build-reference-content.ts 在构建期扫描生成 content/reference/<lib>/<ver>/ 下的五个 JSON(bySlugflatsectionsfunctionstypeSpec)。该子管线的完整设计——包括 @category/@subcategory 标签收集规则、partials 的路由语义、config.json 的全部选项(excludeCategoriesexcludeDefinitionscategoryOrderpartialsOrdernavigationPrefixes)——在 apps/docs/spec/reference/README.md 中有详尽说明,此处不展开。

一个具体的配置实例是 apps/docs/spec/reference/javascript/v2/config.json

{
  "excludeCategories": ["Initializing"],
  "excludeDefinitions": ["constructor"],
  "categoryOrder": ["Database", "Auth", "Edge Functions", "Realtime", "Storage"],
  "partialsOrder": ["introduction", "installing", "initializing", "typescript-support"],
  "navigationPrefixes": {
    "Database": false,
    "Realtime": false,
    "Edge Functions": "functions"
  }
}

它控制了 JavaScript SDK v2 参考文档的分类过滤、导航顺序以及 URL slug 前缀(例如 Edge Functions 的分类挂在 /functions 而非 /edge-functions 下)。

两条管线都挂在 apps/docs 的构建链上:pnpm codegen:references = codegen:references:legacy + codegen:references:new,而 codegen:references:new 又先执行 codegen:references:new:ensure——检查 spec/reference/javascript/v2/supabase.json 等 dump 是否存在,缺失时自动回落到 cd spec && make download.tsdoc.v2 补下载,因此干净克隆后即可直接 pnpm dev / pnpm build

校验与同步:快照测试与 CI

这条流水线有两个防漂移机制:

  1. 本地快照测试apps/docs/spec/reference/README.md 说明 CI 通过 scripts/build-reference-content.test.ts 的快照测试把关新管线——任何输出变化(新方法、slug 形状、类型签名、章节顺序)都会以快照 diff 的形式出现在 PR 中,作为“人类可审阅的变更预览”。需要刷新快照时执行 cd apps/docs && npx vitest run --update scripts/build-reference-content.test.ts
  2. CI 自动更新:仓库 .github/workflows/ 下存在 docs-mgmt-api-update.yml(Management API 规格定期刷新)与 docs-js-libs-update.yml(supabase-js 发版后自动拉取新 TypeDoc dump 并提交再生成的快照 PR)等工作流,从源码结构看,上游规格更新不会依赖人工记得跑 make download

关键文件索引

路径 作用
apps/docs/spec/README.md spec 目录使用说明(本文骨架来源)
apps/docs/spec/Makefile 下载/转换/生成/格式化四阶段任务定义
apps/docs/spec/sections/generateMgmtApiSections.cts OpenAPI → 章节导航(common-api-sections.json)
apps/docs/spec/sections/generateAccessControlPartials.mts OpenAPI + 权限目录 → Scoped PAT / MCP 工具权限表
apps/docs/spec/reference/README.md 新版 TypeDoc 参考文档管线设计
apps/docs/scripts/build-reference-content.ts TypeDoc dump → 五个参考文档 JSON 的构建脚本
apps/docs/package.json codegen:references:* 脚本与 predev/prebuild 挂载点
packages/shared-data/scoped-access-token-permissions.ts 权限表生成共享的权限目录

总结来说,apps/docs/spec/ 是 Supabase 文档站参考文档的规格层:README 给出“make init + make”两步入口,而 spec/Makefile 把这条入口落实为 download → transform → generate → format 四个可独立复用的阶段——用 Redocly 展开 OpenAPI 引用,用脚本生成章节导航与权限表,再用 Prettier 固定格式以便 Git 审阅。理解这条链路后,你可以自行定位“某个参考页面的章节结构或权限表为什么长这样”,并通过对应的 download.* / generate.* 目标做局部刷新与验证。

登录后查看全文
热门项目推荐
相关项目推荐