Better Auth 开发指南:仓库结构、命令规范与运行时无关的工程实践 📅 发布时间:2026/9/11 11:04:36 👁 浏览次数: Better Auth 开发指南仓库结构、命令规范与运行时无关的工程实践【免费下载链接】better-authThe most comprehensive authentication framework项目地址: https://gitcode.com/GitHub_Trending/be/better-authBetter Auth 是一个面向 TypeScript 的综合认证框架设计目标是运行时runtime与框架framework无关可运行于 Node.js、Bun、Deno 与 Cloudflare Workers。本文以仓库根目录的 CLAUDE.md 为骨架结合packages/better-auth、packages/core等源码系统梳理该仓库的开发规范、测试体系与架构约定帮助开发者快速上手贡献代码、编写插件并维护跨运行时兼容性。仓库结构Monorepo 的模块划分Better Auth 采用 pnpm workspace 管理的 monorepo 布局各目录职责清晰。从根目录看核心结构如下packages/better-auth—— 主认证库包含 API 路由src/api/、认证上下文src/auth/、src/context/、Cookie 处理src/cookies/、数据库适配层src/db/、客户端src/client/、插件src/plugins/186 个 TS 文件与 OAuth2 流程src/oauth2/packages/core—— 共享的核心类型与工具例如 URL 工具packages/core/src/utils/url.ts与占位邮箱工具packages/core/src/utils/email.ts供主库与各插件复用packages/cli—— CLI 工具原better-auth/cli已更名为auth在文档与面向用户的输出中统一使用npx authlatestpackages/*—— 数据库适配器drizzle、kysely、prisma、mongo、memory 等、插件与集成包docs/—— 文档站点Next.js Fumadocs内容位于docs/content/docs/test/—— 共享测试工作区e2e/—— 端到端测试细分为e2e/smoke/、e2e/adapter/与e2e/integration/demo/—— 示例应用electron、expo、nextjs、oidc-client、stateless 等。这种布局意味着主认证库只负责认证编排数据库差异由独立 adapter 包屏蔽插件如 admin、organization、two-factor各自独立成目录尽量通过修改插件自身而非改动核心来实现新能力——这是 CLAUDE.md 中明确的开发原则Plugins should be as independent as possible。命令规范为什么永远用 pnpmCLAUDE.md 对命令的使用有硬性约束违反这些约束会破坏锁文件或拖慢 CI命令规范说明pnpm一律使用 pnpm禁止 npm、yarn、bunpnpm test永远不要运行——它会跑遍所有包应改用vitest path/to/test -t pattern精确定位pnpm typecheck提交前必须通过全量类型检查pnpm install --lockfile-only修改package.json或pnpm-workspace.yaml中的依赖版本后在受影响的 workspace 根目录执行避免无关的锁文件更新pnpm install --frozen-lockfile锁文件变更后用于验证值得注意的是嵌套的demo/*workspace 拥有各自独立的锁文件例如 demo/nextjs/pnpm-lock.yaml所以依赖变更要区分主仓库与 demo 目录分别处理。格式化与 lint 由 Lefthook Biome 在提交时自动执行无需手动运行。编写代码的硬性约束跨运行时兼容认证框架最常见的坑是隐式依赖 Node.js 专有 API。Better Auth 要求代码同时工作于 Node.js、Bun、Deno 与 Cloudflare Workers因此避免使用运行时特有的 API用Uint8Array代替Buffer测试除外Node.js 内置模块使用node:协议导入如node:crypto。在 test-instance.ts 中可以看到该约束的实际体现即使测试代码也通过node:async_hooks、node:crypto显式前缀导入内置模块。类型与风格绝不使用any绝不使用 class——框架以函数式与类型推断为核心Biome 配置代码使用 Tab 缩进JSON 使用 2 空格见 biome.jsonzod 统一以import * as z from zod导入仅类型的导入必须使用import type公开 API 必须带 JSDoc 注释。测试中的客户端创建测试场景下禁止用createAuthClient()单独创建客户端必须通过getTestInstance()返回的客户端并通过clientOptions.plugins注入客户端插件。这是为了让服务端与客户端始终共享同一份认证配置避免测试与实际行为脱节。URL 组合appendQueryParams 的用法与边界OAuth 回调与重定向 URL 往往需要追加查询参数如错误信息。CLAUDE.md 规定必须使用appendQueryParams来自better-auth/core/utils/url并把URL 组合与信任校验视为两个独立关注点。const params new URLSearchParams({ error }); const redirectURL appendQueryParams(errorURL, params); throw ctx.redirect(redirectURL);从源码看packages/core/src/utils/url.ts该函数有严格的输入约束只接受绝对 URL 或根相对 URL以/开头拒绝//、/\这类带 authority 前缀的输入否则抛出TypeError相对 URL 以https://better-auth.invalid为基准解析解析后 origin 与基准不一致同样抛错防止相对路径被提升为外部权威地址保留已有查询串并追加新参数追加前检查是否已以结尾避免参数拼接错误追加位置在 URL 的 fragment 之前保证哈希路由不受污染。值得补充的是同文件还提供了 isSafeUrlScheme它只拦截javascript:、data:、vbscript:等可执行 scheme相对路径与myapp://这类移动端 deep link 仍被放行适合守卫window.location.href等导航汇点。这与组合与校验分离的原则互为表里——一个负责拼 URL一个负责判断 URL 是否安全。占位邮箱createPlaceholderEmail 的设计动机当前架构中User.email是必填且唯一的字段这是设计限制而非疏忽。当某个流程如匿名登录、部分社交登录没有真实邮箱时不能直接留空必须用createPlaceholderEmail生成一个稳定、不可路由的地址并要求使用稳定的 identifier 与 namespace占位邮箱保持未验证状态保留把生成逻辑委托给用户代码的流程。实现位于 packages/core/src/utils/email.tsconst PLACEHOLDER_EMAIL_DOMAIN placeholder.invalid; export function createPlaceholderEmail({ identifier, namespace }) { // 拼接为 ${identifier}${namespace}.placeholder.invalid 并用 zod 校验 // 校验失败抛出 TypeError(Invalid placeholder email) }placeholder.invalid取自 RFC 6761 第 6.4 节保留的不可路由域源码注释中给出了 RFC 引用因此生成的地址绝不会被真实投递。namespace 的作用是区分来源不同 namespace 下相同 identifier 生成的邮箱不同。这一点被 packages/core/src/utils/email.test.ts 直接验证相同 identifier 不同 namespace → 邮箱不同identifier 或 namespace 非法 → 抛出TypeError。在 packages/better-auth/src/plugins/anonymous/index.ts 等匿名/占位相关插件中可以看到它的实际消费场景。Issue 分类与 API 契约维护CLAUDE.md 用一整节强调问题分类Issue Triage的纪律核心观点是可复现的错误不一定是 bug。判断标准是行为是否违反了 Better Auth 的文档契约、TypeScript 契约或既定的运行时语义。在修改公开 API 行为之前必须先检查现有文档、生成的/推断的类型、端点元数据、发布历史与相关代码路径的 git 历史。文档中特别点名requireHeaders、requireRequest、端点 method、schema 与中间件等长期存在的元数据属于 API 契约的一部分不得随意改动。对于回归regression声明要对比精确报告的版本或 tag如果该行为在声明版本之前就已存在则应归类为预期行为、文档缺口或集成误用除非有其他契约证明相反。一个更微妙的区分是非法用法与合法的空状态服务端会话检查没有请求头→ 非法用法服务端会话检查有请求头但没有会话 Cookie→ 合法请求返回null。此外不要为了运行时的宽容而削弱 TypeScript 的严格指导——可选输入类型可能会向用户和 Agent 隐藏集成 bug。当当前行为是刻意的但令人困惑时优先改进文档或错误信息而不是改动 API 契约。评审外部 issue PR 时应同时对照契约验证 issue 与修复方案若 PR 改变了长期契约要先明确指出来再推进。测试体系从 getTestInstance 到适配器测试首选工具getTestInstance绝大多数测试基于 Viteste2e/下部分使用 Playwright。规范要求统一使用better-auth/test导出的getTestInstance()它返回{ client, auth, sessionSetter, ... }。从 test-instance.ts 的签名可以看出完整的能力getTestInstanceO, C( options?: O, config?: { clientOptions?: C; port?: number; disableTestUser?: boolean; testUser?: PartialUser; testWith?: sqlite | postgres | mongodb | mysql; transaction?: boolean; }, )其内部做了大量开箱即用的装配test-instance.ts预置 GitHub/Google 的测试社交登录凭据clientId: test与满足校验长度的测试secret默认开启邮箱密码登录、关闭限流logger 级别为debug密码哈希被替换为$test$sha256$前缀的快速哈希见createTestPasswordHash测试不等待真实 bcrypt 计算默认数据库是SQLite 内存库node:sqlite的DatabaseSync(:memory:)通过testWith可切换 Postgres/MySQL/MongoDB自动创建测试用户testtest.com/test123456/test user可用disableTestUser关闭自动注册bearer()插件返回的db是解析后的 adapter通过AsyncLocalStorage提供runWithUser可在指定用户会话上下文中执行断言用afterAll注册清理函数SQLite 直接关闭、MongoDB 删库、Postgres 删 schema、MySQL 清空表并复位外键检查。客户端如何假请求真实 handler测试客户端的fetchOptions.customFetchImpl会把请求直接转交给auth.handler(new Request(...))test-instance.ts即不经过真实 HTTP 端口而是在内存中完成端到端请求处理。登录响应中的set-cookie会被parseSetCookieHeader解析并写回请求头的cookie从而维持会话状态。这正是signInWithTestUser()、sessionSetter与runWithUser能无缝工作的底层机制。secondary-storage.test.ts等测试如 packages/better-auth/src/db/secondary-storage.test.ts展示了典型用法解构client、signInWithTestUser、auth后直接书写行为断言。多数据库与回归测试适配器测试需要 Dockerdocker compose up -d对应根目录 docker-compose.yml因为 Postgres/MySQL/MongoDB 需要真实服务回归测试必须通过see引用相关 issue 或权威来源且不得引用当前 PR 或评审意见多个回归测试共享同一引用时把see放在describe()上方独立的回归测试放在it()上方/** * see https://github.com/better-auth/better-auth/issues/{issue_number} */ it(should handle the previously broken behavior, async () { // ... });提交与发布约定Bug 修复与新增功能必须包含测试对 bug 修复先确认可复现行为违反契约再先写失败的测试后实现修复修改公开 API 时必须同步更新docs/content/docs/下的文档完成前必须通过pnpm typecheck除非用户明确要求不要提交代码提交信息遵循 Conventional Commitsfeat(scope):、fix(scope):、docs:、chore:破坏性变更加!如feat(auth)!:PR 目标分支为main。小结CLAUDE.md 表面上是一份给 AI 助手的开发指南实际上浓缩了 Better Auth 工程化的全部关键决策以 pnpm 为唯一包管理器、以 Vitest 为测试基座、以getTestInstance统一测试装配、以appendQueryParams收敛 URL 拼接、以createPlaceholderEmail解决必填邮箱约束并以契约优先的态度对待每一个 issue 与 PR。对想要为该框架贡献插件、修复 bug 或深入理解其内部机制的开发者而言遵循这些约定意味着更低的沟通成本与更可靠的跨运行时行为。【免费下载链接】better-authThe most comprehensive authentication framework项目地址: https://gitcode.com/GitHub_Trending/be/better-auth创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考