Huly Core 深度指南:Huly 平台核心包库的架构、构建与二次开发实践
【免费下载链接】platformHuly — All-in-One Project Management Platform (alternative to Linear, Jira, Slack, Notion, Motion)项目地址: https://gitcode.com/GitHub_Trending/platform80/platform
Huly Core 是从 Huly 全栈项目管理平台中抽取出来的一组核心 TypeScript 包集合,承载着 Huly 生态的底层数据模型、客户端访问层、文本处理引擎与平台运行基础设施。本文以 foundations/core/README.md 为主线,结合仓库中 Rush 配置 与各包源码,系统讲解 Huly Core 的包组成、环境要求、基于 Rush 的安装/构建/测试/发布全流程,并延伸介绍 API Client 的编程式接入方式,帮助你基于这些可复用的核心包构建自定义应用或集成 Huly 能力到现有项目。
Huly Core 是什么
Huly Core 是 Huly 平台核心包的独立集合,位于本仓库的 foundations/core 目录。它是从 Huly Platform 中抽取出的基础构件:包括核心数据模型(core data models)、客户端库(client libraries)、文本处理引擎(text processing engines)以及平台工具集(platform utilities)。
这些包被设计为可复用、模块化、框架无关(reusable, modular, framework-agnostic),其目标场景有两个:
- 在 Huly Platform 之上构建自定义应用;
- 将 Huly 功能集成进已有项目。
从目录结构看,foundations/core/packages 下共维护了 26 个 npm 包,全部以@hcengineering/作用域发布。Rush 清单 rush.json 中每个包都声明了shouldPublish: true(除内部脚本包@hcengineering/scripts外),表明它们都是面向 npm 生态对外发布的正式产物。
包清单详解
Core Packages:数据模型与平台运行时
- @hcengineering/core— 核心数据模型、类型定义与平台基础抽象。该包的 入口文件 对外导出
classes、hierarchy、memdb、operations、operator、query、storage、tx、backup、versioning等模块。其中 classes.ts 定义了贯穿全平台的基石类型:Ref<T>(强类型文档引用)、Doc(所有文档的基接口,包含_id、space、modifiedOn、modifiedBy等统一字段)、Class<T>(类描述符,可携带domain、extends、implements、索引配置)、AttachedDoc(挂载到父文档的附属文档)、Space(空间模型)以及AccountRole、Tx、Sequence、Blob等抽象。 - @hcengineering/platform— 平台运行时、插件系统与依赖注入。核心源码在 platform.ts 中:它定义了PRI(Platform Resource Identifier)机制——几乎平台中的一切都是
Resource,通过形如core.string.ClassLabel的字符串标识来引用翻译文本、SVG 图标等资源;并提供plugin()与mergeIds()用于声明插件 ID 命名空间、Id/Plugin/IntlString/StatusCode等类型化字符串。同包还包含 i18n、metadata、event、resource、status 等基础模块。 - @hcengineering/model— 数据模型定义与 Schema 管理。其 dsl.ts 实现了一套模型 DSL:用 TypeScript 装饰器与
toposort拓扑排序收集ClassTxes,将类定义自动转化为一系列Tx(事务)作为模型元数据,供服务端加载与迁移使用。
Client Libraries:客户端访问与同步层
- @hcengineering/client— 客户端数据访问与同步层,负责前端与后端的实时数据同步。
- @hcengineering/client-resources— 客户端共享资源与工具。
- @hcengineering/api-client— 面向编程式访问的 API 客户端,同时支持WebSocket与REST两种协议,其完整使用文档见 api-client/README.md,后文有专门章节展开。
- @hcengineering/account-client— 账号管理客户端。
- @hcengineering/collaborator-client— 实时协作文档客户端。
- @hcengineering/hulylake-client— HulyLake 数据仓库(datalake)客户端。
- @hcengineering/analytics与@hcengineering/analytics-service— 分析与埋点工具及其服务端实现。
Text Processing:文本处理引擎
文本处理是 Huly 文档/评论体系的重要底座,这组包从底层引擎到上层格式逐层分工:
- @hcengineering/text-core— 核心文本处理引擎;
- @hcengineering/text— 高层文本处理工具,其 src 下包含基于 ProseMirror/TipTap 的
kit、nodes、marks、markup等扩展实现; - @hcengineering/text-html— HTML 文本的渲染与解析;
- @hcengineering/text-markdown— Markdown 支持;
- @hcengineering/text-ydoc— Yjs 文档集成,为协同编辑提供 CRDT 基础。
Utilities:平台工具
- @hcengineering/query— 查询语言与执行引擎;
- @hcengineering/storage与@hcengineering/storage-client— 存储抽象与实现、存储客户端;
- @hcengineering/rank— 基于 LexoRank 的排序工具(
Rank类型定义见 classes.ts); - @hcengineering/retry— 重试逻辑与容错模式;
- @hcengineering/rpc— RPC 通信层;
- @hcengineering/token— Token 管理与认证工具(在 Rush 清单中以
@hcengineering/server-token发布)。
此外,foundations/core/packages 目录中还维护着 README 未单独列出、但同样对外发布的辅助包:measurements、measurements-otlp(可观测性指标)与postgres-base(PostgreSQL 基础封装),均在 rush.json 的projects清单中登记。
环境要求与前置条件
开始构建前,系统需要满足:
| 依赖 | 要求 | 说明 |
|---|---|---|
| Node.js | v20.11.0 或更高(README 要求) | 实际 rush.json 中声明的支持范围更宽:>=18.20.3 <19.0.0 \|\| >=20.14.0 <25.0.0,建议以 v20 LTS 为准 |
| Rush | 微软的可扩展 monorepo 管理工具 | 本仓库锁定引擎版本5.158.1,包管理器使用pnpm 10.15.1 |
Rush 的 "version selector" 机制会保证全局安装的任意版本在仓库内按rushVersion声明表现一致;common/scripts/install-run-rush.js等脚本也会自动使用该版本。
安装与构建全流程
安装
首先全局安装 Rush:
npm install -g @microsoft/rush然后在仓库根目录(即 foundations/core)依次执行:
rush install rush buildrush install会按照 rush.json 中的pnpmVersion安装本地副本的 pnpm,为全部 26 个包建立统一的依赖树与符号链接;rush build按拓扑依赖顺序增量构建所有包。
构建与热更新
常用构建命令对照:
rush build # 增量构建所有包(利用 build cache) rush rebuild # 忽略缓存,全量重新构建 rush build:watch # 开发模式:以 watch 模式持续构建,包含 build 与 validate 阶段其中rush build:watch在开发迭代时最为常用——修改任意包的源码后,依赖它的包会被自动重编译,配合rushx test可以快速完成开发闭环。
项目结构更新
当项目结构发生变化(新增包、调整依赖关系)时,需要重新关联并重建:
rush update rush buildrush update会重新解析依赖树并更新common/temp下的安装产物,通常在修改 rush.json 的projects清单或各包package.json依赖后执行。
故障排查:构建缓存
如果构建失败但代码本身没有问题,常见原因是本地 build cache 损坏。此时删除缓存并全量重建:
rm -rf common/temp/build-cache rush rebuild(Rush 的构建缓存机制详见 Rush 官方 build cache 文档,仓库内该缓存路径位于 common/temp 下。)
测试
执行全部包的测试:
rush test若要单独运行某个包内的测试,进入该包目录后执行:
rushx testrushx是 Rush 项目内脚本的执行入口,等价于在该包上下文内运行npm test。各包根目录均配置了独立的 jest.config.js,例如core、platform包都有对应的src/__tests__目录存放单元测试。
包版本管理与发布
Huly Core 的版本发布使用仓库级脚本bump.js完成。对单个包进行版本号递增:
node ./common/scripts/bump.js -p projectName其中projectName替换为要发布的包名(如@hcengineering/core)。该脚本位于 common/scripts/bump.js,属于本仓库 monorepo 的公共工具链,同目录下还提供sync-versions.js、check-versions.js等版本一致性维护脚本。每个包的变更历史记录在各自的 CHANGELOG.md 中。
通过 API Client 编程式接入 Huly
README 特别指出:若想以编程方式与 Huly 交互,应使用 API Client。它提供覆盖全部 Huly 操作的类型化接口,可用来构建集成与自定义应用。以下要点均出自该文档。
两种客户端:WebSocket 与 REST
- WebSocket 客户端(
connect):与 Huly 平台 API 保持长连接,适合实时同步场景; - REST 客户端(
connectRest):使用标准 HTTP 请求执行操作,适合一次性/低频率调用。
两者使用相同的连接选项:
import { connect } from '@hcengineering/api-client' // 使用邮箱密码连接(WebSocket) const client = await connect('https://huly.app', { email: 'johndoe@example.com', password: 'password', workspace: 'my-workspace' }) // 使用完毕后关闭连接 await client.close()REST 版只需将导入改为connectRest,调用方式一致:
import { connectRest } from '@hcengineering/api-client' const client = await connectRest('https://huly.app', { email: 'johndoe@example.com', password: 'password', workspace: 'my-workspace' })认证方式与连接参数
客户端支持两种认证方式:邮箱 + 密码或Token。认证成功后,客户端拥有与对应用户相同的资源访问权限。
url:Huly 实例地址,如https://huly.app;workspace:目标工作区名称,可在工作区 URLhttps://huly.app/workbench/<workspace-name>中获取;token:可选,认证 Token(与邮箱密码二选一);email/password:可选,账号凭据。
使用 Token 连接:
import { connect } from '@hcengineering/api-client' const client = await connect('https://huly.app', { token: '...', workspace: 'my-workspace' })查询:findOne 与 findAll
两个核心检索方法均接受三个参数:_class(目标类,结果包含其全部子类)、query(查询条件)与options(可含limit、sort、lookup、projection、total)。
import { SortingOrder } from '@hcengineering/core' import contact from '@hcengineering/contact' // 查询单个文档 const person = await client.findOne(contact.class.Person, { _id: 'person-id' }) // 批量查询 + 排序 + 限制 const persons = await client.findAll( contact.class.Person, { city: 'New York' }, { limit: 10, sort: { name: SortingOrder.Ascending } } )文档 CRUD
import contact, { AvatarType } from '@hcengineering/contact' // 创建文档 const personId = await client.createDoc(contact.class.Person, contact.space.Contacts, { name: 'Doe,John', city: 'New York', avatarType: AvatarType.COLOR }) // 更新文档 await client.updateDoc(contact.class.Person, contact.space.Contacts, personId, { city: 'New York' }) // 删除文档 await client.removeDoc(contact.class.Person, contact.space.Contacts, personId)集合(Collections)操作
集合用于管理AttachedDoc——即挂载到父文档上的附属文档,如联系人(Person)下的多个联系方式(Channel):
import contact, { AvatarType } from '@hcengineering/contact' // 向集合中添加附属文档 await client.addCollection( contact.class.Channel, // 附属文档类 contact.space.Contacts, // 空间 personId, // 父文档 id contact.class.Person, // 父文档类 'channels', // 集合名 { provider: contact.channelProvider.Email, value: 'john.doe@example.com' } ) // 更新集合中的附属文档 await client.updateCollection( contact.class.Channel, contact.space.Contacts, channelId, personId, contact.class.Person, 'channels', { city: 'New York' } ) // 从集合中移除附属文档 await client.removeCollection( contact.class.Channel, contact.space.Contacts, channelId, personId, contact.class.Person, 'channels' )Mixins 扩展
Mixin 允许在不改变原类的情况下为已有文档动态附加属性,例如给Person增加员工(Employee)信息:
import contact, { AvatarType } from '@hcengineering/contact' // 创建 mixin await client.createMixin( personId, contact.class.Person, contact.space.Contacts, contact.mixin.Employee, { active: true, position: 'CEO' } ) // 更新 mixin 属性 await client.updateMixin( personId, contact.class.Person, contact.space.Contacts, contact.mixin.Employee, { active: false } )从源码理解核心抽象
Huly Core 的设计可以用四个关键词概括:
- 类型化 ID 字符串:
Ref<T>、Plugin、IntlString、Resource<T>等都是带品牌标记(branded type)的字符串类型,在编译期提供类型安全,在运行期只是普通字符串,便于序列化与网络传输(见 classes.ts 与 platform.ts)。 - 事务(Tx)驱动的数据变更:所有数据修改都以
Tx形式表达(TxCreateDoc、TxMixin、TxApplyIf等,见 tx.ts),客户端与服务端通过事务流实现同步与审计。 - 类层次与 Domain:
Class描述符通过extends/implements构成继承体系,并通过domain字段(如DOMAIN_MODEL、DOMAIN_SPACE、DOMAIN_BLOB等,见 classes.ts)决定数据在底层存储中的归属,配合IndexKind声明索引策略。 - 插件化资源解析:
plugin()与mergeIds()将每个插件声明为一段扁平命名空间,运行时按 PRI 加载翻译、图标等资源,实现模块解耦(platform.ts)。
许可证与生态
Huly Core 以 EPL-2.0(Eclipse Public License 2.0)开源。在本仓库的 monorepo 布局中,foundations/core是纯核心库层,与 foundations/communication、foundations/net、foundations/server 等并列;上层则由 models(数据模型定义)、plugins(业务插件)、pods(服务部署单元)等构成完整的 Huly 平台。
对于开发者而言,最直接的落地路径是:参照上文在 foundations/core 下完成rush install与rush build,随后基于@hcengineering/core的模型抽象定义自己的Class与Space,通过 api-client 的 WebSocket/REST 客户端与 Huly 服务端交互,再利用text-markdown、text-html等文本包处理富内容,即可在 Huly 平台上搭建出具备文档、任务与实时协作能力的自定义应用。
【免费下载链接】platformHuly — All-in-One Project Management Platform (alternative to Linear, Jira, Slack, Notion, Motion)项目地址: https://gitcode.com/GitHub_Trending/platform80/platform
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考