news 2026/9/8 22:08:30

Bruno 单体仓库依赖边界:严格的 @usebruno 依赖 DAG 与五条包所有权守护规则

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Bruno 单体仓库依赖边界:严格的 @usebruno 依赖 DAG 与五条包所有权守护规则

Bruno 单体仓库依赖边界:严格的 @usebruno 依赖 DAG 与五条包所有权守护规则

【免费下载链接】brunoOpensource IDE For Exploring and Testing API's (lightweight alternative to Postman/Insomnia)项目地址: https://gitcode.com/GitHub_Trending/br/bruno

Bruno 是一个以 npm workspaces 组织的 monorepo,其内部包(@usebruno/*)之间的依赖方向被.claude/rules/architecture.md明确规定为一个严格有向无环图(DAG)。本文完整继承该规则文档的核心内容——三级依赖分层、五条守护护栏、声明依赖与真实导入一致性要求——并结合当前仓库中各包package.json的实际声明逐条佐证,帮助你在为 Bruno 新增代码或做跨包改动前,准确判断「这个 import 该放哪个包、这个依赖该不该声明」,避免引入循环依赖或向上依赖这类架构级缺陷。

一、核心规则:内部依赖必须是严格 DAG

规则文档的开篇即给出总纲:内部@usebruno/*依赖(即每个包package.json中的声明)构成一个严格 DAG,新代码必须遵守它——循环依赖或向上依赖是架构 bug,而不是图省事的便利

文档同时指明了一份按需查阅的参考文档:整个 monorepo 的完整地图(构建工具、请求管线、沙箱、文件格式、核心数据模型类型、依赖版本)位于 .claude/reference/architecture.md,并要求在进行任何非平凡的跨包或架构性工作之前先阅读它。也就是说,.claude/rules/architecture.md定义的是「不可破坏的不变量」(invariants),而 reference 文档提供的是「地图」——两者配合使用:规则文档常驻生效,参考文档在跨包工作时按需展开。

当前仓库共有 16 个 workspace 包,见根目录 package.json 的workspaces字段(packages/bruno-appbruno-electronbruno-clibruno-commonbruno-convertersbruno-schemabruno-schema-typesbruno-querybruno-jsbruno-langbruno-testsbruno-tomlbruno-graphql-docsbruno-requestsbruno-filestorebruno-sqlite)。理解依赖方向,正是从这个清单出发。

二、依赖方向与所有权边界:三级分层

规则文档将全部内部包按依赖位置划分为三层。以下完整继承原文档的划分,并在后文用各包package.json的实际声明逐层验证:

1. 叶子库(Leaf libs)——零内部依赖

bruno-commonbruno-langbruno-querybruno-requestsbruno-graphql-docsbruno-schemabruno-schema-typesbruno-toml被列为零内部@usebruno/*依赖的叶子库。

从源码结构看,packages/bruno-common/package.json 的dependencies字段就是一个空对象{},是这一层级的最严格实例。需要指出一个与当前代码的细微出入:packages/bruno-requests/package.json 的dependencies中实际声明了"@usebruno/common": "0.1.0",即 bruno-requests 目前指向基础叶子。可以推断规则文档中「零内部依赖」的枚举对 bruno-requests 的描述偏保守,实际它是仅依赖bruno-common的准叶子——方向依然向下,不破坏 DAG,但新代码不应在这条边之外再增加任何内部依赖。

2. 中间消费者(Mid consumers)

  • bruno-js→ (bruno-common,bruno-query)
  • bruno-converters→ (bruno-common,bruno-schemabruno-schema-types作为 devDependency)
  • bruno-filestore→ (bruno-common,bruno-langbruno-schema-types作为 devDependency)

与当前package.json逐一比对:

规则文档声明的内部依赖当前 package.json 实际声明
bruno-jscommon、query"@usebruno/common": "0.1.0""@usebruno/query": "0.1.0"
bruno-converterscommon、schema(运行时);schema-types(dev)dependencies"@usebruno/common": "^0.1.0""@usebruno/schema": "^0.7.0"devDependencies"@usebruno/schema-types": "0.0.1"
bruno-filestorecommon、lang(运行时);schema-types(dev)dependencies"@usebruno/common": "0.1.0""@usebruno/lang": "0.12.0"devDependencies"@usebruno/schema-types": "0.0.1"

三者的运行时/编译期划分与规则文档完全一致:bruno-schema-types只出现在devDependencies中,这正呼应了后文守护规则第 4 条「types-only」。

3. 顶层消费者(Top consumers)——依赖只进不出

「things flowintothem, never out」:

  • bruno-cli→ (bruno-common,bruno-converters,bruno-filestore,bruno-js,bruno-lang,bruno-requests)
  • bruno-electron→ (bruno-common,bruno-converters,bruno-filestore,bruno-js,bruno-lang,bruno-requests,bruno-schema)
  • bruno-app→ (bruno-common,bruno-converters,bruno-graphql-docs,bruno-schema)

实际package.json验证结果:

当前实际声明的内部运行时依赖
bruno-clicommon、converters、filestore、js、lang、requests —— 与文档完全一致
bruno-electroncommon、converters、filestore、js、lang、requests、schema,另加"@usebruno/sqlite": "0.1.0"
bruno-appcommon、graphql-docs、schema,另加"@usebruno/sqlite": "0.1.0"

两个值得注意的补充事实:其一,bruno-electron的包名不带作用域"name": "bruno"),它是唯一以非@usebruno/*命名对外分发的包,因此「依赖只进不出」对它的约束更为关键;其二,当前 workspace 已新增packages/bruno-sqlite,并被bruno-appbruno-electron@usebruno/sqlite 0.1.0声明,规则文档的顶层枚举尚未覆盖这一新增包——它的依赖方向同样是「流入顶层消费者」,符合 DAG,但做架构判断时应把它一并纳入心智模型。

三、五条守护护栏(Guardrails)

规则文档列出了 DAG 所强制的五条具体护栏,下面逐条完整说明并给出仓库证据。

护栏 1:bruno-common 是「浏览器安全」的基础叶子

它运行在 Web 渲染进程(bruno-app)中,而不仅是 Node 环境,因此必须保持平台中立

  • 禁止使用 Node 内建模块(fspathoscryptochild_processnode:*);
  • 禁止依赖任何自身会拉入 Node 的第三方包;
  • 当前保持零运行时依赖——packages/bruno-common/package.json"dependencies": {}印证了这一点,文档要求「保持这个状态」;
  • 不依赖任何其他@usebruno/*包。需要 Node 能力或需要其他 bruno 包的工具函数,必须放到别的包里。

护栏 2:共享库/库包禁止 import bruno-app 或 bruno-electron

渲染进程专属或 Electron 专属的代码,不允许为了「让上层可导入」而下推(push down)到某个库中。任何在packages/bruno-commonbruno-js等库包里出现require('electron')或从bruno-app源码导入的行为,都是对所有权边界的违反。

护栏 3:bruno-js 必须保持 Electron-free

bruno-js同时在两个宿主中运行:Electron 主进程(bruno-electron → js)与命令行(bruno-cli → js,见 bruno-cli/package.json 中的"@usebruno/js": "0.12.0")。一旦在 bruno-js 中加入electron/IPC 导入,CLI 路径即被破坏。规则文档给出的分工是:沙箱逻辑(sandbox logic)属于 bruno-js,宿主接线(host wiring)属于 bruno-electron。bruno-js 的 package.json 中确实没有任何 electron 相关依赖,其main直接指向src/index.js(无构建产物,从源码直接消费)。

护栏 4:bruno-schema-types 是 types-only 包

它是 converters/filestore 的devDependency,用tsc构建(packages/bruno-schema-types/package.json 的"build": "tsc -p tsconfig.json"devDependencies中仅有typescript,无 rollup 等运行时构建工具)。规则要求:只从它导入类型,永远不要往里面加运行时代码,也不要出现对它的运行时 import。这与第二层级中bruno-convertersbruno-filestore把它放在devDependencies的声明方式互为印证。

护栏 5:bruno-schema(Yup)与 bruno-schema-types(TS 类型)是两个独立且都存活的包

  • bruno-appbruno-converters使用@usebruno/schema运行时校验(两者dependencies中均有"@usebruno/schema": "^0.7.0"/"0.7.0");
  • bruno-filestorebruno-converters使用@usebruno/schema-types编译期类型(两者devDependencies中均有"@usebruno/schema-types": "0.0.1");
  • 数据模型的任何改动通常同时触及两者——这是跨包改动时最容易漏掉的一条。

四、声明的依赖必须匹配真实的导入

规则文档最后一节强调:package.json就是契约,而 workspace 的依赖提升(hoisting)会掩盖对契约的违背。具体执行要点有三:

  1. 每个 import 都必须在导入方自己的 manifest 里声明。某个模块仅因为兄弟包或根目录恰好拉了它才解析成功,属于「潜伏的断裂」——一旦工作区结构变化就会炸掉。
  2. 测试与构建专用包必须放进devDependenciesdependencies里的东西会连同其传递依赖树一起发布给用户;各叶子库prepack脚本(如npm run test && npm run build)配合files字段(distsrcpackage.json)决定了最终发布内容,dependencies的膨胀会直接放大用户安装的体积。
  3. 当某次改动移除了某个声明的最后一个消费者时,要同步删掉这条声明,保持声明与实际导入严格同步。

与契约配套的还有一层根级版本钉死。当前根 package.json 的overrides字段硬钉了"axios": "1.18.0""rollup": "3.30.0"等关键版本(还有tarpbkdf2electron-store/conf等)。这意味着:在某个叶子包里单独升级 axios 或 rollup 是无效的——真正生效的是根级 override,升级必须改根package.json。各叶子包package.json中的"overrides": { "rollup": "3.30.0" }(如 bruno-common、bruno-requests、bruno-filestore)也保持了与根级一致。

五、实践自检清单:新增代码前如何验证合规

综合规则文档与各包package.json的证据,跨包改动前可以按以下清单自检:

  1. 依赖方向:新 import 是否指向 DAG 的下方(叶子 → 中间消费者 → 顶层消费者)?若bruno-app/bruno-electron/bruno-cli之外的包需要引用它们,即为向上依赖,属于架构 bug。
  2. 平台约束:改动落在bruno-common中时,是否引入了 Node 内建模块或会拉入 Node 的依赖?落在bruno-js中时,是否引入了electron或 IPC 调用?
  3. schema 双改:涉及集合/请求数据模型时,是否同时检查了@usebruno/schema(运行时 Yup 校验)与@usebruno/schema-types(编译期 TS 类型)?
  4. manifest 一致性:新增的 import 是否写入了导入方自己的dependencies/devDependencies?测试/构建专用依赖是否误入dependencies?被移除的最后一个 import 是否连带删除了声明?
  5. 版本升级:要升 axios/rollup 时,改的是否是根 package.json 的overrides,而不是叶子包里的版本号?
  6. 架构级工作前置:非平凡的跨包改动,是否已先通读 .claude/reference/architecture.md 获取完整 monorepo 地图(构建工具分布、请求管线、沙箱模式、文件格式、关键依赖主版本)?

小结

.claude/rules/architecture.md用不到 50 行定义了一套可执行的架构不变量:以严格 DAG 约束@usebruno/*内部依赖方向,以五条护栏锁定关键包(bruno-common浏览器安全、bruno-js双宿主运行、bruno-schema-types纯类型)的所有权边界,再以「声明必须匹配真实导入」堵住 workspace hoisting 带来的隐性违约。本文所有佐证均来自当前仓库的实际package.json声明;若未来仓库演进(例如bruno-requests收敛掉对bruno-common的运行时依赖,或bruno-sqlite被补入规则文档的枚举),应以仓库实际内容为准重新核对本文表格。

【免费下载链接】brunoOpensource IDE For Exploring and Testing API's (lightweight alternative to Postman/Insomnia)项目地址: https://gitcode.com/GitHub_Trending/br/bruno

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/8 22:08:15

RAID5阵列瘫痪全程复盘:从盘故障到数据恢复实战

那台机器是浪潮的,8块1.2TB SAS盘,RAID5,跑的是公司的文件服务器加一部分生产数据库的定期备份。接到电话的时候,对方的语气已经慌得不行:“小X,阵列崩了,所有盘都在报错,文件夹打不…

作者头像 李华
网站建设 2026/9/8 22:07:08

opencode实操笔记:从安装避坑到模型选型的终端AI编程Agent指南

现在AI编程Agent的工具迭代快得让人眼花缭乱,今天这个发布新版本,明天那个宣布免费。在这种环境里,opencode能杀出重围,靠的不是又一个"生成代码更快"的噱头,而是把"模型无关、客户端开源、终端原生&qu…

作者头像 李华
网站建设 2026/9/8 22:06:53

Trending复盘指南:每月最后一天,把热搜变成内容选题库

八月最后一天的晚上,我几乎是本能地打开了各平台的Trending页面。做内容久了都会形成这个习惯:每到月底总得找个完整的时间,把热搜、热门、趋势榜从头到尾认真扫一遍。外人看这像在刷手机摸鱼,但真正干过内容的人会明白&#xff0…

作者头像 李华