Supabase Reference Docs 技术解析:DocSpec 文档规范体系与规格驱动的文档生成管线
Supabase 官方文档站(apps/docs)除了常规教程外,还承载了整个 Supabase 生态工具的参考文档(Reference Docs)。本文围绕该应用的 README 展开,结合 spec 目录、generator 源码 与 spec/Makefile,讲清楚其“文档规格(DocSpec)”体系的设计动机、四类规范的定义、从 YAML/JSON 规格到 HTML 文档的完整生成管线,以及工具维护者贡献文档的落地路径。读完本文,你将理解 Supabase 如何用一套严格的模式(schema)同时产出 HTML 文档、manpage 等多种格式,并能独立运行或扩展该生成管线。
Reference Docs 是什么:为生态工具统一供给文档
apps/docs/README.md 对该应用的定位只有两句话:这是 “Supabase Reference Docs”,并且面向 Supabase 生态的工具维护者——如果你维护 Supabase 生态中的任何工具(tools or libraries),可以借助该站点为你提供所维护工具与库的文档。
从仓库结构看,这一承诺体现在 apps/docs 的组成上:
- content/ 存放手工撰写的指南(约 840 个 MDX 文件),对应 supabase.com/docs 上的 Guides;
- spec/ 存放全部机器可读的文档规格(OpenAPI JSON、CLISpec/SDKSpec/ConfigSpec YAML 等),这是参考文档的“单一事实来源”;
- generator/ 是 TypeScript 实现的文档生成器,把规格渲染为 Markdown 片段;
- app/ 与 features/ 是 Next.js 站点本身,负责把生成的参考内容渲染成网页。
README 中“Contributing”部分指出的两条入口文档分别为 开发指南 与 贡献指南,二者分别解决“如何本地跑起文档站”与“仓库组织与写作规范”的问题。
DocSpec 体系:四类文档规格
README 的核心论点是:Supabase 使用“文档规格”(documentation specifications)来生成人类可读的文档,共四类:
| 规格 | 类型 | 用途 | 仓库中的实际载体 |
|---|---|---|---|
| OpenAPI | 行业标准 | 描述 API 端点 | api_v1_openapi.json、api_v2_openapi.json、auth_v1_openapi.json、storage_v0_openapi.json、analytics_v0_config.yaml 等 |
| SDKSpec | Supabase 自定义 | 描述 SDK 与客户端库 | supabase_js_v1.yml、supabase_dart_v2.yml、supabase_py_v2.yml、supabase_kt_v3.yml、supabase_swift_v2.yml、supabase_csharp_v1.yml |
| ConfigSpec | Supabase 自定义 | 描述配置项 | cli_v1_config.yaml、functions_v0_config.yaml |
| CLISpec | Supabase 自定义 | 描述 CLI 命令与用法 | cli_v1_commands.yaml(约 4100 行) |
SDKSpec 示例:以 supabase-js 为例
spec/supabase_js_v1.yml 展示了 SDKSpec 的头部结构:以 openref: 0.1 声明规格引用版本,info 段包含文档 id(reference/supabase-js)、标题、描述,以及关键的 definition: spec/enrichments/tsdoc_v1/combined.json 字段——它把 SDK 的 TypeDoc 元数据(API 符号定义)与这份 YAML 挂钩;functions 数组则按功能点组织条目,每个条目通过 $ref 引用具体的 TypeDoc 符号(如 @supabase/supabase-js.index.SupabaseClient.constructor),并内嵌可运行的代码示例:
functions:
- id: initializing
title: 'Initializing'
$ref: '@supabase/supabase-js.index.SupabaseClient.constructor'
examples:
- id: create-client
name: Create Client
code: |
```js
import { createClient } from '@supabase/supabase-js'
// Create a single supabase client for interacting with your database
const supabase = createClient('https://xyzcompany.supabase.co', 'publishable-or-anon-key')
```
这套“YAML 骨架 + TypeDoc 符号引用 + 内联示例”的组织方式,使文档条目天然跟随库的真实 API 符号,避免手写文档与代码漂移。
CLISpec 示例:CLI 命令与全局标志
spec/cli_v1_commands.yaml 以 clispec: '001' 开头,info 段声明了 CLI 的 id、当前记录版本(version: 2.98.2)、语言(language: sh)以及 tags(quick-start、local-dev、management-api、other-commands),随后定义了全局标志(如 --agent、--create-ticket、--debug、--dns-resolver <[ native | https ]>、--experimental),每个标志都带 description、default_value 和可选的 accepted_values。这种把“默认值 + 取值范围 + 枚举”全部结构化的做法,正是 README 所说“严格 schema”的落地形态:机器可以据此生成参数表、校验取值,而非仅输出一行描述文本。
为什么用自定义规格:一份 schema,多种产物
README 给出的收益论证是:使用自定义规格后,可以从同一份严格 schema 生成许多其他类型的产物(例如 HTML 与 manpages),并且可以随意切换文档系统——本站使用 Next.js,而 Supabase 官网(supabase.com)使用一套自定义 React 站点,并且只暴露每个工具 API 的一个子集。
这一“一源多产物”能力在仓库中有明确证据:
- 生成器产物是 Markdown 文档片段(由 EJS 模板渲染后写盘),供 Next.js 站点消费;
- 同一份 OpenAPI 规格还驱动了额外管线。spec/Makefile 中的
generate.partials.access-control目标会调用 sections/generateAccessControlPartials.mts,从 v1/v2 管理 API 规格加上 mcp_tools_permissions.json,生成content/_partials/access-control/下的 PAT 权限表格 MDX——即同一份 API 规格既是端点参考的来源,也是权限矩阵的来源。
生成管线:从规格文件到文档的三个环节
1. 下载:make download
spec/README.md 说明进入 apps/docs/spec 后先执行 make init 安装依赖,然后 make 即可完成“下载并转换规格为文档”的全过程。Makefile 的 download 目标并行拉取多个来源:
- 管理 API:
curl https://api.supabase.com/api/v1-json与/api/v2-json,落地为api_v1_openapi.json/api_v2_openapi.json; - Storage API:从
supabase.github.io/storage/api.json拉取storage_v0_openapi.json; - 客户端库 TypeDoc 元数据(v2):从
supabase.github.io拉取 supabase-js、auth-js、postgrest-js、realtime-js、storage-js、functions-js 六个包的spec.json,写入reference/javascript/v2/; - 服务端库:
server.json、middleware.json分别写入reference/server/v1/与reference/middleware/v1/; - MCP 工具权限投影:
platform/mcp-tools-permissions。
Makefile 中的注释还保留了历史演进痕迹:download.auth.v1(从 gotrue 的 swagger.json 拉取)因“流程需要更新、当前手工维护”而被注释掉,auth 规格现由人工维护——这提醒读者:仓库中的规格文件既有自动下载件,也有人工维护件,贡献前需先确认对应来源。
2. 转换:redocly bundle 消除 $ref
OpenAPI 文件大量使用 $ref 指向共享 schema,直接渲染会导致文档碎片化。transform 目标使用 Redocly CLI 做 bundle(内联引用):
dereference.api.v1:
npx --package=@redocly/cli redocly bundle -o $(REPO_DIR)/transforms/api_v1_openapi_deparsed.json $(REPO_DIR)/api_v1_openapi.json
npx --package=@redocly/cli redocly bundle -o $(REPO_DIR)/transforms/api_v2_openapi_deparsed.json $(REPO_DIR)/api_v2_openapi.json
dereference.auth.v1:
npx --package=@redocly/cli redocly bundle --dereferenced -o $(REPO_DIR)/transforms/auth_v1_openapi_deparsed.json $(REPO_DIR)/auth_v1_openapi.json
注意一个被显式注释记录的实现细节:api_v1/v2 特意不带 --dereferenced,因为 api_v2_openapi.json 存在循环引用(APIErrorObject.issues -> APIErrorObject),Redocly 无法将其展平为 JSON,这些引用改由 writeApiReferenceSections 在生成阶段手工解析。这类“源码级注释”是理解该管线边界条件的可靠依据。
此外,针对 JS 客户端库的 TypeDoc JSON,@supabase/generator 包(packages/generator)提供了一组 tsdoc:dereference:* 脚本(覆盖 supabase/auth/postgrest/realtime/storage/functions 的 v1 与 v2),把含 $ref 的 TypeDoc 输出解析为 *_dereferenced.json,供后续生成器消费。
3. 生成:DocGenerator 按类型分发
生成阶段的统一入口是 generator/index.ts。它使用 node:util 的 parseArgs 解析命令行参数(--input、--output/-n、--type/-n、--url/-n),对输入、输出与类型做 assert 校验,类型白名单为:
const allowedTypes = ['cli', 'config', 'sdk', 'api', 'legacy'] as const
随后按类型分发到各自的生成函数:ApiGenerator、CliGenerator、ConfigGenerator、SdkGenerator 与 LegacyGenerator(generator/legacy.ts 处理旧版规格)。
三个自定义规格生成器共享同一套实现模式,读起来几乎是对称的:
CLISpec 生成器(generator/cli.ts):yaml.load 读入规格,依据 spec.clispec 版本字段分发(当前实现 001)。v001 逻辑先用 spec.commands 建立 id -> command 的 Map,为每条命令计算标题层级——有子命令的命令渲染为 ## 二级标题,叶子命令渲染为 ### 三级标题(并附带 [#id] 锚点),再经 ejs.render 套用 templates/CliTemplate 渲染为 Markdown,最后 writeToDisk 写盘。
SDKSpec 生成器(generator/sdk.ts):同样按 spec.sdkspec: '001' 分发,v001 把 info、functions、types 注入 SdkTemplate 渲染输出。
ConfigSpec 生成器(generator/config.ts):按 spec.configspec: '001' 分发,v001 的一个关键步骤是“按 tag 分组参数”——以 spec.info.tags 作为章节骨架,对 spec.parameters 按 parameter.tags[0] === section.id 过滤,把扁平的参数列表组织为带标题的章节,再渲染 ConfigTemplate。这解释了 ConfigSpec 规格文件中 tags 字段的用途:它同时是参数的分类键与文档的章节顺序。
所有生成器都通过 generator/helpers.ts 的 writeToDisk 统一落盘,并在控制台打印保存路径,方便 CI 与本地排查。
生成管线全景
综合以上,apps/docs/spec 的完整流水线为:
make download # 拉取 OpenAPI / TypeDoc / 权限投影(部分手工维护)
└─ make transform # redocly bundle 内联 $ref -> transforms/*_deparsed.json
└─ make generate
├─ sections/generateMgmtApiSections.cts # v1/v2 管理 API 章节
└─ sections/generateAccessControlPartials.mts # PAT 权限矩阵 MDX
└─ make format # prettier 统一格式,便于 git 追踪变更
其中 generate.sections.api.v1 同时喂入 v1 与 v2 两份 deparsed 规格加 common-api-sections.json,说明章节生成器具备跨版本对比/合并能力;format 目标的存在动机也写在 Makefile 注释里:“让 git 更容易追踪变更”。
站点侧:Next.js 渲染与配套工程实践
README 提到“本站点使用 Next.js”——从 apps/docs/package.json 与 DEVELOPERS.md 可以还原本地开发的最小闭环:
- 按根目录 DEVELOPERS.md 完成 Turborepo 环境安装;
- 在
apps/docs下创建.env.local,社区贡献者至少需要NEXT_PUBLIC_IS_PLATFORM=false(该变量用于区分本地环境与平台环境); pnpm run dev启动后访问http://localhost:3001/docs(必须带/docs前缀);- 站点外观应与 supabase.com/docs 完全一致,作为本地正确性的验收标准。
DEVELOPERS.md 还记载了两项值得注意的工程实践,它们与“规格驱动”的思路一脉相承:
- 面向 AI 的文档产物:
pnpm build:guides-markdown会为/docs/guides/..下每个路由生成纯 Markdown 文件到public/markdown/guides(git 忽略),生产构建时作为prebuild任务运行,使 LLM/Agent 能通过 middleware 与 edge functions 直接消费 Markdown 版指南——参考文档的“规格 -> 多产物”理念在指南侧的延伸; - 无障碍自动化:文档页面通过 e2e/docs 中的 Playwright + axe-core 套件做 WCAG 2.1 A/AA 扫描,PR 只扫描改动影响的页面(
pnpm e2e:docs:a11y,可用PLAYWRIGHT_BASE_URL指向 PR 预览环境)。
如何贡献:维护者的落地路径
回到 README 的主旨——“如果你是 Supabase 生态工具的维护者”:
- 阅读入口文档:DEVELOPERS.md 覆盖环境搭建,CONTRIBUTING.md 覆盖仓库组织与写作风格;
- 选择正确的规格类型:API 端点走 OpenAPI(JSON),SDK/库走 SDKSpec(YAML),配置项走 ConfigSpec,CLI 命令走 CLISpec;对应生成器分别位于 generator/api.ts、generator/sdk.ts、generator/config.ts、generator/cli.ts,规格文件统一放在 apps/docs/spec/;
- 遵循既有的结构惯例:CLISpec 用
clispec: '001'+info.tags+ 结构化flags/commands;SDKSpec 用functions+$ref指向 TypeDoc 符号并内嵌代码示例;ConfigSpec 用info.tags作为参数分组与章节骨架; - 本地验证:在
apps/docs/spec下用 Makefile(make init初始化依赖后按需执行 download/transform/generate/format 各阶段),再在apps/docs下pnpm run dev于localhost:3001/docs目检渲染结果。
小结
Supabase 文档站的 Reference Docs 本质上是一套“规格优先”的文档基础设施:四类规格(OpenAPI、SDKSpec、ConfigSpec、CLISpec)以严格 schema 描述 API 端点、客户端库、配置项与 CLI 命令,apps/docs/spec 下的 Makefile 负责拉取与 Redocly 转换,apps/docs/generator 的 TypeScript 生成器按 clispec/sdkspec/configspec 版本字段分发展现渲染,同一份规格还可衍生出权限矩阵、manpage 等其他产物。这种设计让文档系统与具体渲染框架解耦——Next.js 只是当前消费端之一,这也是 Supabase 能够同时维护文档站与官网两套前端而共享同一事实来源的原因。对生态工具维护者而言,贡献参考文档的正确姿势不是直接写 HTML,而是编写或更新对应的规格文件,让生成管线完成从 schema 到网页的最后一公里。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00