首页
/ cal.diy 平台库开发指南:@calcom/platform-libraries 的本地构建、Dev 调试与 NPM 发布工作流

cal.diy 平台库开发指南:@calcom/platform-libraries 的本地构建、Dev 调试与 NPM 发布工作流

2026-09-09 15:06:18作者:卓艾滢Kingsley

本篇技术指南围绕 cal.diy 仓库中 packages/platform/libraries/README.md 所定义的开发工作流展开,讲解 @calcom/platform-libraries 这个 NPM 包的版本管理机制、本地构建(yarn local / yarn build:dev)、一键发布(yarn publish-npm)以及合并到主分支前的手动发布流程。读完本文,你将掌握如何在开发 API v2 时让平台库指向本地源码、如何把改动同步到 NPM 包、以及什么情况下必须触发一次新版本发布。

一、@calcom/platform-libraries 是什么

@calcom/platform-libraries 是 cal.diy 仓库中的一个独立 NPM 包,位于 packages/platform/libraries,它是平台 API(apps/api/v2)与 cal.diy 核心业务逻辑(packages/featurespackages/libpackages/trpc 等)之间的桥梁:API v2 并不直接 import 仓库内部模块,而是通过这个库获得经过打包的、可独立版本化的业务能力。

index.ts 的导出内容可以清晰看到它的职责范围:

  • 预订(Bookings)getBookingForReschedulegetAllUserBookingsgetBookingInfohandleCancelBookingconfirmBookingHandler 等;
  • 日历与忙碌时间getBusyCalendarTimesupdateEventgetConnectedDestinationCalendarsAndEnsureDefaultsInDbcityTimezonesHandler
  • 事件类型(Event Types)dynamicEventgetUsernameListvalidateCustomEventNameparseBookingLimitparseRecurringEvent
  • 认证与安全symmetricEncrypt / symmetricDecryptverifyCodeUnAuthenticatedsendEmailVerificationByCodecheckAdminOrOwner
  • OAuth 与仓储层OAuthServiceCredentialRepositoryProfileRepositorySelectedCalendarRepositoryBookingReferenceRepository
  • 平台常量与枚举ENABLE_ASYNC_TASKERMINUTES_TO_BOOKSchedulingTypeMembershipRoleWebhookTriggerEvents 等。

需要特别注意的是,当前仓库为社区版(community edition),index.ts 中为部分已被移除的 EE 功能保留了 stub 导出(如 roundRobinManualReassignmentcreateApiKeyHandlerverifyPhoneNumber),它们要么是空操作,要么直接抛出 "not available in community edition" 错误,目的仅在于维持 API v2 的既有 import 不中断。这属于从源码结构可以推断的实现细节,社区版开发者不应依赖这些函数返回有效结果。

二、包结构与构建配置

2.1 package.json 的关键设计

packages/platform/libraries/package.json 中有几个值得注意的设计点:

  • 版本号保持 0.0.0:本地开发时包的版本始终为占位符 0.0.0,真实版本号只在发布流程中临时写入(见下文脚本分析);
  • 多入口 exports:除了默认入口 .(指向 dist/index.js / dist/index.cjs / dist/index.d.ts),还按业务域暴露了 13 个子路径,如 ./schedules./bookings./event-types./app-store./slots./emails./conferencing./repositories./organizations./private-links./errors./calendars./tasker,每个子路径都同时提供 import(ESM)、require(CJS)和 types.d.ts)三种产物;
  • peerDependenciesreact ^18 || ^19react-domstripezod 作为宿主依赖外置,避免打包时重复捆绑;
  • workspace 依赖@calcom/features@calcom/i18n@calcom/lib 均为 workspace:*,构建时会被打包进产物。

2.2 Vite 多入口构建

构建由 vite.config.js 驱动,采用 Vite Library Mode + SSR 构建build.ssr: trueplatform: "node"target: "node18"),入口即上文提到的 14 个 ts 文件(index.ts 及各子域模块)。该配置的核心策略是"尽可能把业务代码打进产物,同时把运行时依赖外置":

  • rollupOptions.external 列出了 100+ 个外部依赖,包括 reactzoddayjsstripe@prisma/clientnext-i18next@sentry/nextjs、各种日历/视频/CRM SDK 等,由宿主环境提供;
  • commonjsOptions 通过 dynamicRequireRoot 指向 apps/web,用于处理如 next-i18next.config.js 这类运行时动态 require;
  • resolve.alias@calcom/lib@calcom/trpc@calcom/prisma/client 等 workspace 路径映射到仓库内部真实位置,其中 tslog 被桥接到 apps/api/v2/src/lib/logger.bridge.ts,保证日志实现与 API v2 一致;
  • vite-plugin-dts 负责生成类型声明文件,@vitejs/plugin-react 处理 JSX。

构建产物输出到 dist/,每个入口同时产出 .js(ESM)与 .cjs(CJS),这就是 package.jsonexports 能同时服务 importrequire 的原因。

三、本地开发工作流:让 API v2 指向本地库

README 定义了一套"第一次构建 / 后续重构建"的两步工作流,对应 package.json 中的三个脚本。

3.1 第一次改动:yarn local

packages/platform/libraries 目录下执行:

yarn local

该命令展开后等价于(见 package.json 中的 local 脚本):

node scripts/local.js && npx rimraf dist && yarn build:dev && cd ../../.. && yarn

它依次完成四件事:

  1. 运行 scripts/local.js:把 packages/platform/libraries/package.json 的版本临时写为 9.9.9,同时把 apps/api/v2/package.json@calcom/platform-libraries 的依赖改写为 npm:@calcom/platform-libraries@9.9.9,用 npm: 协议强制从 npm registry 安装(当前仓库中 API v2 的默认依赖是 workspace:*,见 apps/api/v2/package.json,二者互斥切换);
  2. 清空旧的 dist/ 产物rimraf dist);
  3. 执行 yarn build:dev 重新打包(见下节);
  4. 回到仓库根目录执行 yarn 重新解析 workspace 链接,使 API v2 实际引用本地构建产物。

3.2 后续改动:yarn build:dev

如果你已经执行过一次 yarn local,那么之后每次修改平台库源码只需重新构建:

yarn build:dev

该命令等价于:

yarn vite build && sed -i'' -e 's/const CACHE = new lruCache\.LRUCache({ max: 1e3 });/const CACHE = new lruCache({ max: 1e3 });/g' ./dist/index.cjs

注意其中的 lru-cache 修复:Vite 打包会把 lru-cache 的调用编译成 new lruCache.LRUCache(...),而实际运行时 lru-cache 的导出形态不同,因此构建后用 sedlruCache.LRUCache 替换回 lruCache,避免运行时 TypeError。这也是 package.json 中另有一个 watch-lru-fix 脚本存在的原因——在 watch 模式下无法自动做这一步,需要手动轮询修补。若在 watch 开发中遇到 lruCache 相关报错,可参考该脚本。

四、一键发布:yarn publish-npm

当本地开发完成、功能验证通过后,README 推荐使用一条命令完成全部发布动作:

yarn publish-npm

该命令等价于(见 package.json 中 publish-npm 脚本):

yarn && node scripts/prepublish.js && npx rimraf dist && yarn build && npm publish --access public && node scripts/postpublish.js

4.1 prepublish.js:自动递增版本

scripts/prepublish.js 的逻辑如下:

  1. 通过 https.get("https://registry.npmjs.org/@calcom/platform-libraries") 读取 npm registry 的 dist-tags.latest,拿到当前线上最新版本;
  2. 调用 incrementPatchVersion 对 patch 位 +1(a.b.c -> a.b.c+1);
  3. 把新版本号写入 packages/platform/libraries/package.json

也就是说,每次 publish-npm 都只会递增 patch 版本,不涉及 minor / major。如果你需要 minor 或 major 跳版,需要走下一节描述的手动发布流程。

4.2 构建与发布

版本号写入后,脚本清空 dist/,执行 yarn build(即 yarn vite build,注意这里不会执行 lru-cache 的 sed 修补),然后 npm publish --access public 以公开包形式发布到 npm。

4.3 postpublish.js:收尾与依赖回写

scripts/postpublish.js 负责发布后的收尾工作:

  1. 读取刚写入 package.json 的版本号作为 publishedVersion
  2. 调用 waitForNewestNpmRelease 轮询 npm registry(每 5 秒一次,最多 12 次),确认线上已能查到刚发布的版本;
  3. packages/platform/libraries/package.json 的版本重置回 0.0.0(与 README 中"reset 回 0.0.0"的说明一致);
  4. apps/api/v2/package.json@calcom/platform-libraries 依赖改写为 npm:@calcom/platform-libraries@<publishedVersion>,即从"本地构建"切换回"线上 npm 包";
  5. 在仓库根目录执行 yarn install 重新安装依赖,使 API v2 使用刚发布的 npm 版本。

至此,一次完整的"本地开发 → 构建 → 发布 → API v2 回切 npm 依赖"闭环完成。

五、合并到 main 之前:手动发布流程

README 明确要求:在把改动合并到 main 之前,必须先发布你的平台库版本。这是因为 CI / 其他协作者拉取 main 后,API v2 依赖的是 npm 上的真实包版本,而不是你本地未发布的 0.0.0 构建。

手动发布需要满足三个前提,缺一不可:

  1. 具备发布权限:你必须是 @calcom/platform-libraries 这个 npm 包的贡献者(contributor);
  2. CLI 认证:先在本地通过 npm auth 完成 npm CLI 登录认证;
  3. 合理递增版本号:按照语义化版本规范手动设置新版本(例如需要 minor / major 改动时,不能只依赖 publish-npm 的 patch 自动递增)。

手动发布的步骤为:

# 1. 在 packages/platform/libraries 下按需修改版本号
#    例如将 version 从 0.0.0 改为 0.1.0

# 2. 发布到 npm
npm publish

# 3. 发布成功后,把 packages/platform/libraries/package.json 的 version 改回 0.0.0

# 4. 在仓库根目录重新安装依赖
yarn

# 5. 此时 API v2 应使用 npm 包而非本地构建版本

最后一步"运行 yarn"很关键:README 提醒执行完后"你应该正在使用 npm 包而不是本地构建的版本"——这与 postpublish.js 中"更新 API v2 依赖为 npm:@calcom/platform-libraries@<version> 并执行 yarn install"的行为一致,只是手动流程需要你自己完成依赖回写与重装。

版本判断的实操提示

在执行手动发布前,建议先确认 npm 上的 latest 版本,再决定递增策略:

npm view @calcom/platform-libraries version

如果你的改动只是新增导出或修 bug,递增 patch 即可;如果存在破坏性变更(函数签名变化、删除导出、依赖行为变化),应遵循 semver 规则考虑 minor / major。

六、什么时候必须发布新版本

README 定义了三条触发发布新版本的标准,它们对应三类"API 表面变化":

  1. index.js(即 index.ts)新增导出:任何新函数、新类型通过平台库暴露给 API v2 时,必须发版,否则 API v2 无法引用到新能力;
  2. 已导出函数发生代码变更:修改了某个已导出函数的实现逻辑(例如修复 bug、调整行为),必须发版才能让线上 API v2 获得修复;
  3. Prisma schema 变更破坏了当前已发布版本中函数的实现:例如 packages/prisma/schema.prisma 中模型字段调整,导致旧版本库中依赖旧数据形状的函数(如 getAllUserBookingsgetBookingForReschedule、各 Repository 的查询)在新 schema 下编译或运行失败时,必须同步发布兼容的新版本。

CHANGELOG.md 的历史记录也能印证这一规律:例如 0.0.51 是为支持"预订时由参会者指定地点"(attendee specified location)发布的;0.0.41 是为"handleCancelBooking 向 webhook 传递 oauth client id"发布的;0.0.38 则为 API 新增了 bookerLayoutscolorconfirmationPolicyseats 等事件类型属性的内部/API 双向转换器(translator)。可以看到,几乎所有版本发布都对应一次"API v2 需要新导出或行为修复"的变更。

七、实践建议与常见问题

7.1 开发循环速查

场景 命令 说明
首次改动平台库源码 yarn local 构建 + 让 API v2 指向本地库
后续改动重新构建 yarn build:dev 重新打包并执行 lru-cache 修补
一键发布新版本 yarn publish-npm 自动递增 patch、发布、回写依赖
手动发布(需要 minor/major 或非 contributor 自动流程) npm publish 见第五节
watch 模式下的 lru-cache 修补 yarn watch-lru-fix 轮询修补 dist 产物

7.2 常见问题

  • 改了平台库源码但 API v2 表现没变:先确认是否执行过 yarn build:dev;再检查 apps/api/v2/package.json@calcom/platform-libraries 的依赖是 workspace:*(本地模式)还是 npm:@calcom/platform-libraries@x.y.z(npm 模式);
  • 发布后 API v2 仍引用旧版本:检查 postpublish.js 是否成功执行(它会更新依赖并运行 yarn install),或手动执行 yarn 重新安装;
  • 需要 patch 之外的版本跳跃publish-npm 固定只递增 patch,请改用第五节的手动流程;
  • watch 模式遇到 lruCache.LRUCache is not a constructor 之类的报错:运行 yarn watch-lru-fix 修补产物,或改用 yarn build:dev 的完整构建流程。

八、小结

@calcom/platform-libraries 的版本管理策略可以概括为"本地永远 0.0.0,发布时临时写真实版本,发布后回写 npm 依赖"。这套工作流通过 scripts/local.jsscripts/prepublish.jsscripts/postpublish.js 三个脚本将"本地开发 → dev 构建 → npm 发布 → API v2 回切线上依赖"完全自动化,开发者只需牢记三条规则:

  1. 首次改动用 yarn local,后续改动用 yarn build:dev
  2. 合并 main 之前必须先把你的版本发布到 npm,并把本地版本号重置回 0.0.0、重新 yarn
  3. 新增导出、修改已导出函数、Prisma schema 破坏性变更,三者任一发生都要发布新版本。

理解这套机制后,你在 cal.diy 中为 API v2 贡献平台层能力时,就能始终保证"本地调试不污染线上、线上依赖永远指向已发布的稳定版本"。

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

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.76 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
860
1.35 K
docsdocs
暂无描述
Markdown
899
5.83 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
925
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.84 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
533
601
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.03 K
525
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
395