- CLI
- 后端
- 云原生
【免费下载链接】vercel
Develop. Preview. Ship.
@vercel/client是 Vercel 官方开源的 Node.js 部署客户端,它把「收集文件 → 计算哈希 → 创建部署 → 上传文件 → 轮询状态 → 别名分配」这一整套部署流程封装成一个异步事件流,让任何 Node.js 程序都能以纯代码方式完成vercel deploy所做的事。本文以 packages/client/CHANGELOG.md 为骨架,结合 packages/client/src 源码与测试用例,为你梳理该包的事件驱动 API 用法、关键配置项、底层实现原理,以及从 12.x 到 18.x 版本演进中的破坏性变更、新功能与安全加固,读完即可在自己的 CI/CD 或工具链中直接使用。
一、@vercel/client 是什么
@vercel/client位于本仓库packages/client目录,是一个专为「以编程方式向 Vercel 部署」设计的 npm 包。它不依赖交互式 CLI,而是通过一个**异步生成器(async generator)**对外输出事件,调用方用for await...of消费事件即可驱动整个部署流程。
从 packages/client/package.json 可以看到其运行时依赖集中在构建与路由相关的工作区包上:@vercel/build-utils、@vercel/error-utils、@vercel/routing-utils、@vercel/microfrontends,并引入async-retry(重试)、async-sema(信号量并发控制)、tar-fs(tgz 归档)等基础设施。当前版本为 18.2.3,engines要求Node.js >= 20。
包对外暴露的入口非常精简(见 src/index.ts):
export { continueDeployment } from './continue'; export { checkDeploymentStatus } from './check-deployment-status'; export { inspectDeploymentFiles } from './inspect-deployment-files'; export { getVercelIgnore, buildFileTree } from './utils/index'; export const createDeployment = buildCreateDeployment(); export * from './errors'; export * from './types';其中createDeployment是最核心的入口,continueDeployment用于手动部署(manual deployment)的续跑,checkDeploymentStatus负责部署状态轮询,inspectDeploymentFiles与getVercelIgnore/buildFileTree则暴露了文件收集与忽略规则解析的能力。
二、快速上手:五分钟跑通一次编程式部署
安装与引入
npm install @vercel/clientconst { createDeployment } = require('@vercel/client');最小可用示例
createDeployment接收两个参数:
<path>:一个目录路径、单个文件路径,或同一层级的多个文件路径组成的数组;<options>:包含token(必填)、可选的teamId,以及任意vercel.json合法的部署字段。
async function deploy() { let deployment; for await (const event of createDeployment({ token: process.env.TOKEN, path: '/Users/me/Code/myproject', })) { if (event.type === 'ready') { deployment = event.payload; break; } } return deployment; }完整事件列表
README 中给出的事件列表是:
[ // File events(文件事件) 'hashes-calculated', // 文件哈希计算完成 'file-count', // 需要上传的文件数量与进度对象 'file-uploaded', // 单个文件上传完成 'all-files-uploaded', // 全部文件上传完成 // Deployment events(部署事件) 'created', // 部署已在服务端创建 'building', // 进入构建阶段 'ready', // 部署就绪 'alias-assigned', // 别名已分配 'warning', // 警告 'error', // 错误 ];而源码 src/utils/index.ts 中维护的EVENTS_ARRAY实际上更完整(README 只是子集),还包括notice、tip、canceled,以及 v1 / v2 Checks 相关事件:
const EVENTS_ARRAY = [ // File events 'hashes-calculated', 'file-count', 'file-uploaded', 'all-files-uploaded', // Deployment events 'created', 'building', 'ready', 'alias-assigned', 'warning', 'error', 'notice', 'tip', 'canceled', // v1 Checks events 'checks-registered', 'checks-completed', 'checks-running', 'checks-conclusion-succeeded', 'checks-conclusion-failed', 'checks-conclusion-skipped', 'checks-conclusion-canceled', // v2 Checks events 'checks-v2-failed', ] as const; export type DeploymentEventType = (typeof EVENTS_ARRAY)[number]; export const EVENTS = new Set(EVENTS_ARRAY);你也可以在代码中直接导入事件集合做校验:
import { EVENTS } from '@vercel/client';三、配置项详解:VercelClientOptions 与 DeploymentOptions
createDeployment的第一个参数类型为VercelClientOptions,定义在 src/types.ts。下表汇总了全部字段及其作用:
| 字段 | 类型 | 说明 |
|---|---|---|
token | string | 必填,Vercel API Token。缺失时抛token_not_provided错误(见 src/create-deployment.ts) |
path | string \| string[] | 目录 / 文件 / 同级文件数组,必须为绝对路径,否则抛invalid_path |
debug | boolean | 开启后向 stderr 输出[client-debug]前缀的调试日志(见 src/utils/index.ts) |
teamId | string | 指定 Team 作用域,会拼入 API 查询参数?teamId= |
apiUrl | string | 自定义 API 地址,默认https://api.vercel.com |
force | boolean | 强制部署 |
prebuilt | boolean | 预构建部署模式,上传.vercel/output产物而非源码 |
vercelOutputDir | string | prebuilt 模式下产物目录,缺省时报错(见 src/utils/index.ts) |
rootDirectory | string \| null | 项目根目录相对路径 |
withCache | boolean | 保留缓存的强制部署 |
userAgent | string | 自定义 UA,默认client-v${pkgVersion} |
defaultName | string | 默认部署名 |
isDirectory | boolean | path 是否为目录,由collectDeploymentFiles自动探测(见 src/collect-deployment-files.ts) |
skipAutoDetectionConfirmation | boolean | 跳过框架自动检测确认 |
archive | 'tgz' | 归档上传格式,目前仅支持tgz(VALID_ARCHIVE_FORMATS,见 src/types.ts) |
dispatcher | FetchDispatcher | undici Dispatcher(如undici.ProxyAgent),用于代理等连接定制 |
projectName | string | 项目名,用于微前端配置推断 |
bulkRedirectsPath | string \| null | 批量重定向文件(相对项目根目录),prebuilt 部署会将其纳入上传 |
manual | boolean | 实验性手动部署模式,要求prebuilt: true(见 src/create-deployment.ts) |
aliasAssignedSignal | AbortSignal | 当已有部署事件流观察到别名分配时,用它中止当前轮询 |
FetchDispatcher是一个极简的 undici Dispatcher 接口抽象(src/types.ts):
export interface FetchDispatcher { dispatch(options: unknown, handler: unknown): boolean; }第二个参数DeploymentOptions(src/types.ts)会被序列化进创建部署的请求体,包括version、regions、routes、cleanUrls、rewrites、redirects、headers、trailingSlash、builds、functions、env、build.env、source、target、name、meta、projectSettings、gitMetadata、actor、autoAssignCustomDomains、customEnvironmentSlugOrId等。其中target的处理逻辑值得注意(src/deploy.ts):
target === 'preview'时直接置为undefined(预览部署本就是默认);target为其他非production值时,会转换为customEnvironmentSlugOrId(自定义环境部署)。
VercelConfig接口(src/types.ts)还完整覆盖了vercel.json支持的所有顶层字段,包括builds、routes、functions、crons、images、proxy、bunVersion、bulkRedirectsPath,以及实验性的experimentalServices/experimentalServiceGroups/experimentalServicesV2和正式的services多服务配置。
四、源码级解析:一次部署请求的完整生命周期
createDeployment的实际执行流程在 src/create-deployment.ts 中一目了然:
const { fileList, filesMap: files } = await collectDeploymentFiles(path, clientOptions, debug); if (fileList.length === 0) { yield { type: 'warning', payload: 'There are no files inside your deployment.' }; } yield { type: 'hashes-calculated', payload: mapToObject(files) }; deploymentOptions.version = 2; for await (const event of upload(files, clientOptions, deploymentOptions)) { yield event; }整个链路可以拆成五个阶段:
1. 文件收集(collectDeploymentFiles)
src/collect-deployment-files.ts 负责:
- 校验 path 为绝对路径(目录、单文件或文件数组);
- 调用
buildFileTree(src/utils/index.ts)递归扫描目录; - 依据
.vercelignore/.nowignore规则过滤文件; - prebuilt 模式下通过
.vc-config.json的filePathMap补充引用文件、纳入.vercel/routes.json与微前端配置、bulkRedirectsPath; - 最后按是否
archive === 'tgz'选择走createTgzFiles归档或hashes逐文件哈希。
2. 哈希计算(hashes)
src/utils/hashes.ts 实现了基于SHA-1的内容寻址:
- 普通文件:
createHash('sha1')计算十六进制摘要; - 符号链接:对链接目标字符串本身做哈希;
- 超大文件:超过
MAX_BUFFER_FILE_SIZE = 2^31 - 1(约 2 GiB)时,fs.readFile会抛ERR_FS_FILE_TOO_LARGE,因此改用hashFile以createReadStream流式计算哈希,且不保留内存中的data,为后续流式上传做准备; - 哈希去重:相同内容的文件合并到同一个
DeploymentFile.names数组中(同名只传一次)。
const MAX_BUFFER_FILE_SIZE = 2 ** 31 - 1;3. 创建部署(deploy → postDeployment)
src/deploy.ts 中deploy()先生成默认部署名(单文件叫file,目录取最后一段路径名),然后调用postDeployment向/v13/deployments发送 POST 请求。请求体会附带files清单——即prepareFiles()生成的PreparedFile[](文件名、sha、size、mode)。
这里有一个值得关注的静态内联快速路径(src/utils/index.ts):当文件集满足「全部是普通文件、全部为.html/.htm/.md后缀、文件数 ≤ 10、总字节 ≤ 5 MB」时,shouldInlineStaticFiles返回 true,文件内容会以 base64 直接内联进创建请求(src/utils/index.ts),服务端可走免构建的即时静态部署路径;不满足条件时自动回退到常规构建流程。
4. 上传缺失文件(uploadFiles)
创建请求若返回missing_files(code 为missing_files),客户端拿到缺失的 SHA 清单后进入上传阶段(src/upload.ts)。上传实现有这些特点:
- 并发控制:
new Sema(50, { capacity: 50 })最多 50 个并发请求; - 重试策略:
async-retry配置retries: 5, factor: 6, minTimeout: 10,对ETIMEDOUT、ECONNREFUSED、ENOTFOUND、ECONNRESET、EAI_FAIL、socket hang up等客户端网络错误重试(isClientNetworkError,还递归检查 native fetch 的cause); - 失败即取消:一旦某个文件遇到非网络错误,会 abort 所有进行中的上传(
abortControllers集合),避免无效请求继续消耗带宽; - 大文件流式上传:无内存
data的大文件用createReadStream(names[0])直接从磁盘流式发送,并通过 Transform 计数 chunk 更新UploadProgress.bytesUploaded(UploadProgress继承自EventEmitter,src/upload.ts); - 上传成功返回
file-uploaded事件,全部完成后 yieldall-files-uploaded,随后再次调用deploy()正式完成部署创建。
5. 状态轮询与收尾
第二次deploy()创建成功后,进入checkDeploymentStatus(见下一节),直到ready/alias-assigned/error/checks-v2-failed等终止事件返回。若部署在首次创建响应里就已READY且别名已分配,则直接短路返回,无需轮询。
五、部署状态轮询与容错机制
src/check-deployment-status.ts 实现了状态轮询逻辑,包含了 CHANGELOG 中多次提到的容错特性:
1. Retry-After 解析与限流退避
parseRetryAfterMs(src/check-deployment-status.ts)对三种情况分别处理:
| 响应状态 | 行为 |
|---|---|
| HTTP 429 / 503 | 读取Retry-After头(秒数或 HTTP 日期),解析失败时回退默认 5000ms |
| HTTP 5xx | 视为可安全重试,等待默认 5000ms |
| 其他 | 不重试,直接继续 |
等待时长被钳制在[RETRY_DELAY_MIN_MS=5000, RETRY_DELAY_MAX_MS=60000]区间,防止极端Retry-After值导致客户端长时间挂起。
2. 随机抖动(Jitter)防惊群
对应 CHANGELOG 17.2.31 的变更:每次限流退避都会叠加0 ~ RETRY_DELAY_SKEW_MS(30s)的随机偏移,避免多个客户端在同一时刻抢一个限流配额(thundering herd):
const randomSkewMs = Math.floor(RETRY_DELAY_SKEW_MS * Math.random());3. 重试上限
CHANGELOG 17.2.14 提到checkDeploymentStatus对 HTTP 429 / 5xx 最多重试 3 次,当前源码中为RETRY_COUNT = 5(后续版本上调),配合getPollingDelay(见 src/utils/get-polling-delay.ts)根据已耗时动态调整轮询间隔。
4. 事件流驱动的别名分配优化
CHANGELOG 17.6.4 引入了「从 alias-assigned 构建流事件完成部署」的能力:当调用方通过aliasAssignedSignal传入一个已收到别名分配事件的 AbortSignal 时,sleepUntilAliasAssigned会在信号中止时提前唤醒,checkDeploymentStatus随即从事件中恢复出READY+alias-assigned状态并直接返回,轮询降级为兜底方案(src/check-deployment-status.ts)。
5. Checks 事件透传
轮询过程中,服务端返回的checksState/checksConclusion会逐一映射为checks-registered、checks-running、checks-completed、checks-conclusion-*事件;当 v2 的deployment-alias检查失败时立即产出checks-v2-failed并终止——这正是 CHANGELOG 17.3.0「Deployment Checks 支持」的落地实现,让deploy --prod在别名提升前就能发现检查失败。
六、版本演进亮点:从 12.x 到 18.x 的关键变更
CHANGELOG 记录了本包自 12.5.1 至 18.2.3 的全部变更。剔除大量「Updated dependencies」条目后,实质性的功能、修复与破坏性变更可按主题归纳如下。
破坏性变更(Major Changes)
| 版本 | 变更 |
|---|---|
| 13.0.0 | 移除 Node.js 14,最低要求提升到 Node.js 16 |
| 15.0.0 | 移除 Node.js 16 支持 |
| 16.0.0 | 移除 Node.js 18,最低要求提升到 Node.js 20(当前engines即>= 20) |
| 17.0.0 | 请求 api-deployments 时在客户端 options 中设置prebuilt |
| 17.5.16 | 移除已废弃的public字段:不再向部署接口发送该字段(含--public标志),测试 fixtures 与辅助函数同步清理 |
| 18.0.0 | 从node-fetch迁移到原生fetch:删除 CLI bundle 中最后一处url.parse()用法,消除了 Node 24 上vercel deploy触发的DEP0169DeprecationWarning;agent?: http.Agent选项被替换为dispatcher?: FetchDispatcher(undici dispatcher,如undici.ProxyAgent)。CLI 会自动透传代理感知的 dispatcher,因此HTTP_PROXY/HTTPS_PROXY行为对 CLI 用户保持不变 |
| 14.0.0 | 默认忽略.yarn/cache;split-tgz成为归档部署的默认方式 |
新功能与能力扩展
- Node.js Routing Middleware(18.2.0):通过
proxy.entrypoint支持 Node.js 路由中间件入口,可用proxy.matcher做可选路径匹配;matcher 只能配置在入口源码或vercel.json其中之一(对应ProxyConfig类型,见 src/types.ts)。 - Rust 项目默认跳过
target/(18.1.0):检测到根目录Cargo.toml时,vercel deploy/vercel dev默认忽略target/(本地构建产物可达数百 MB,服务端会重建并缓存),用户可用!/target加回;同时加固了vercel dev的本地文件扫描器,目录在扫描中途被删除(cargo build频繁刷新target/的常见竞态)时跳过而非崩溃。实现见 src/utils/index.ts。 - 多服务配置
services(17.4.3 → 17.6.0):services成为多服务项目的正式配置字段,experimentalServicesV2保留为废弃的向后兼容别名;17.5.0 支持在services中通过显式env配置注入引用其他服务的环境变量(17.5.6 则移除了service-ref形态下的顶层env)。 - 实验性手动部署(17.2.42,
manual):仅支持 prebuilt 部署,创建后部署保持INITIALIZING,需后续调用continueDeployment继续(src/continue.ts),CLI 侧配套vercel deploy --prebuilt与deploy continue命令;17.2.64 为deploy continue增加了--archive支持。 vercel deploy --dry(17.6.1):不上传、不创建部署,仅检查检测到的框架预设与本地部署文件集,对非 TTY 消费方输出完整 JSON。bulkRedirectsPath支持目录(17.2.58):prebuilt 部署中该路径既可以是单文件也可以是目录(目录内递归纳入),修复了此前目录场景报 "No files found at path" 的问题;路径逃逸(解析到项目根之外)会被拒绝,见 src/utils/index.ts。- 微前端配置(15.2.0 / 15.3.0 / 15.3.1):将
microfrontends.json/microfrontends.jsonc纳入部署(15.2.0);未指定rootDirectory时自动推断其位置(15.3.0);findConfig更名为findMicrofrontendsConfig(15.3.1),实现基于@vercel/microfrontends。 - 框架与运行时支持:17.2.0 新增 TanStack Start 框架预设;17.1.0 通过
vercel.json属性支持 Bun(bunVersion);15.1.0 将 v9 pnpm lockfile 识别为 pnpm 10。 - API 与事件增强:13.2.0 起固定使用 v13 创建部署接口;13.3.0 发送
customEnvironmentSlugOrId;13.4.0 在vc ls [project]展示部署保留策略;17.2.37 将检测到的 agent 名作为actor传入部署请求体;17.2.5 在部署中包含.vercel/routes.json。 - 默认忽略规则扩充(15.1.5 / 14.0.0):加入 Yarn Plug'n'Play 文件(
.pnp*)、.yarn/cache等默认忽略项,完整默认列表见 src/utils/index.ts。
安全加固与健壮性修复
- prebuilt 场景的忽略规则复检(18.2.3):
vercel deploy --prebuilt不再无条件信任.vc-config.json的filePathMap引用。源码路径会重新对照项目的.vercelignore/.nowignore规则,解析出部署根目录之外的引用会被拒绝,从而防止被篡改或低信任度的构建产物把.env等被忽略文件重新塞回上传集。对应实现见 src/utils/index.ts,并有专门 fixtureprebuilt-filepathmap-ignore配套测试。 - 超大文件不再崩溃(17.6.2):大于 Node
fs.readFile上限(约 2 GiB)的文件改为流式哈希与流式上传(ERR_FS_FILE_TOO_LARGE不再出现),CLI 上传进度不再假设所有文件都在内存中;当文件仍超过服务端单请求上传上限(HTTP 413)时,CLI 会提示改用--archive=tgz(分片上传)。哈希侧实现见 src/utils/hashes.ts。 - 上传失败级联取消(13.4.18):任一文件上传失败时取消其他进行中的上传。
- 归档分片(13.5.0 / 14.0.0):支持将归档部署拆分为多个部分上传,并使其成为默认方式(split-tgz)。
- 错误提示优化:13.1.1 为
vc deploy --prebuilt缺失文件提供更友好报错(含 node_modules 依赖未安装提示,见 src/collect-deployment-files.ts);13.4.19 在特定部署失败时建议--archive标志。
类型与内部重构
- 17.4.0:升级到 TypeScript 5.9。
- 17.2.48:内部
fetch重命名为fetchApi。 - 13.4.10:以
node-fetch替换@zeit/fetch;18.0.0 再进一步迁移到原生fetch。 - 13.4.20:引入 vitest 作为测试框架,并重构避免全局设置最大监听数(
max listeners)。
七、测试与验证:如何确认客户端行为
packages/client/tests下提供了完整的单元与集成测试,是对上述行为的直接验证:
- 单元测试:unit.check-deployment-status.test.ts(轮询与 Retry-After 逻辑)、unit.get-polling-delay.test.ts(轮询间隔)、unit.hashes.test.ts(SHA-1 哈希与去重)、unit.utils.test.ts、unit.vercelignore.test.ts(忽略规则)、unit.inspect-deployment-files.test.ts、unit.manual-deployment.test.ts(手动部署模式)。
- 集成测试:integration-create-deployment.test.ts、integration-paths.test.ts。
- 测试 fixtures:覆盖了
nowignore(新旧忽略文件冲突)、rust-target/rust-target-with-ignore(Rust target 跳过)、prebuilt-filepathmap-ignore(filePathMap 安全复检)、bulk-redirects-path/bulk-redirects-dir(批量重定向文件与目录)、microfrontend、symlinks、vercelignore-allow-nodemodules等典型场景。
在包目录内可直接运行:
# 单元测试 pnpm vitest-unit # 集成测试 pnpm vitest-e2e # 类型检查 pnpm type-check八、进一步探索
想要深入理解@vercel/client,建议按以下顺序阅读源码:
- src/create-deployment.ts:事件流编排入口;
- src/upload.ts:并发上传、重试与取消逻辑;
- src/check-deployment-status.ts:状态轮询与限流退避;
- src/utils/index.ts:文件树构建、忽略规则、fetchApi 封装、静态内联判定;
- src/types.ts:全部类型定义(
VercelClientOptions、Deployment、VercelConfig等); - src/continue.ts:手动部署续跑;
- packages/client/CHANGELOG.md:版本演进完整记录;
- packages/client/README.md:官方使用文档。
如果你是 CLI 使用者,该包的行为会以vercel deploy、vercel dev、vercel build等命令的形式透出;如果你是工具链开发者,直接消费@vercel/client的事件流即可把 Vercel 部署能力嵌入自己的发布流水线——从文件收集、哈希去重、限流退避到别名分配,所有工程细节都已在此包中处理好。
- CLI
- 后端
- 云原生
【免费下载链接】vercel
Develop. Preview. Ship.
相关推荐
Huly 服务端客户端库 `@hcengineering/server-client` 深入解析:从版本演进到源码实现
Huly 服务端客户端库 @hcengineering/server client 深入解析:从版本演进到源码实现 @hcengineering/server
后端前端企业应用项目管理即时通讯CRMGradio Python 客户端 gradio_client 演进全解:从 0.1.2 到 2.6.1 的 API 客户端设计与版本变迁
Gradio Python 客户端 gradio_client 演进全解:从 0.1.2 到 2.6.1 的 API 客户端设计与版本变迁 本文以 Gradio
前端后端AI 应用Huly 平台账号服务客户端 @hcengineering/account-client 源码解析:RPC API 能力全景与版本演进
Huly 平台账号服务客户端 @hcengineering/account client 源码解析:RPC API 能力全景与版本演进 @hcengineeri
后端前端企业应用项目管理即时通讯CRM
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考