@vercel/client 编程式部署客户端:事件流 API 设计、源码实现与版本演进全解析
2026/9/23 2:41:10 网站建设 项目流程
  • CLI
  • 后端
  • 云原生

【免费下载链接】vercel

Develop. Preview. Ship.

项目地址:https://gitcode.com/gh_mirrors/ve/vercel
点击查看免费下载

@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负责部署状态轮询,inspectDeploymentFilesgetVercelIgnore/buildFileTree则暴露了文件收集与忽略规则解析的能力。

二、快速上手:五分钟跑通一次编程式部署

安装与引入

npm install @vercel/client
const { 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 只是子集),还包括noticetipcanceled,以及 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。下表汇总了全部字段及其作用:

字段类型说明
tokenstring必填,Vercel API Token。缺失时抛token_not_provided错误(见 src/create-deployment.ts)
pathstring \| string[]目录 / 文件 / 同级文件数组,必须为绝对路径,否则抛invalid_path
debugboolean开启后向 stderr 输出[client-debug]前缀的调试日志(见 src/utils/index.ts)
teamIdstring指定 Team 作用域,会拼入 API 查询参数?teamId=
apiUrlstring自定义 API 地址,默认https://api.vercel.com
forceboolean强制部署
prebuiltboolean预构建部署模式,上传.vercel/output产物而非源码
vercelOutputDirstringprebuilt 模式下产物目录,缺省时报错(见 src/utils/index.ts)
rootDirectorystring \| null项目根目录相对路径
withCacheboolean保留缓存的强制部署
userAgentstring自定义 UA,默认client-v${pkgVersion}
defaultNamestring默认部署名
isDirectorybooleanpath 是否为目录,由collectDeploymentFiles自动探测(见 src/collect-deployment-files.ts)
skipAutoDetectionConfirmationboolean跳过框架自动检测确认
archive'tgz'归档上传格式,目前仅支持tgzVALID_ARCHIVE_FORMATS,见 src/types.ts)
dispatcherFetchDispatcherundici Dispatcher(如undici.ProxyAgent),用于代理等连接定制
projectNamestring项目名,用于微前端配置推断
bulkRedirectsPathstring \| null批量重定向文件(相对项目根目录),prebuilt 部署会将其纳入上传
manualboolean实验性手动部署模式,要求prebuilt: true(见 src/create-deployment.ts)
aliasAssignedSignalAbortSignal当已有部署事件流观察到别名分配时,用它中止当前轮询

FetchDispatcher是一个极简的 undici Dispatcher 接口抽象(src/types.ts):

export interface FetchDispatcher { dispatch(options: unknown, handler: unknown): boolean; }

第二个参数DeploymentOptions(src/types.ts)会被序列化进创建部署的请求体,包括versionregionsroutescleanUrlsrewritesredirectsheaderstrailingSlashbuildsfunctionsenvbuild.envsourcetargetnamemetaprojectSettingsgitMetadataactorautoAssignCustomDomainscustomEnvironmentSlugOrId等。其中target的处理逻辑值得注意(src/deploy.ts):

  • target === 'preview'时直接置为undefined(预览部署本就是默认);
  • target为其他非production值时,会转换为customEnvironmentSlugOrId(自定义环境部署)。

VercelConfig接口(src/types.ts)还完整覆盖了vercel.json支持的所有顶层字段,包括buildsroutesfunctionscronsimagesproxybunVersionbulkRedirectsPath,以及实验性的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.jsonfilePathMap补充引用文件、纳入.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,因此改用hashFilecreateReadStream流式计算哈希,且不保留内存中的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,对ETIMEDOUTECONNREFUSEDENOTFOUNDECONNRESETEAI_FAILsocket hang up等客户端网络错误重试(isClientNetworkError,还递归检查 native fetch 的cause);
  • 失败即取消:一旦某个文件遇到非网络错误,会 abort 所有进行中的上传(abortControllers集合),避免无效请求继续消耗带宽;
  • 大文件流式上传:无内存data的大文件用createReadStream(names[0])直接从磁盘流式发送,并通过 Transform 计数 chunk 更新UploadProgress.bytesUploadedUploadProgress继承自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-registeredchecks-runningchecks-completedchecks-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.0node-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/cachesplit-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 --prebuiltdeploy 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.jsonfilePathMap引用。源码路径会重新对照项目的.vercelignore/.nowignore规则,解析出部署根目录之外的引用会被拒绝,从而防止被篡改或低信任度的构建产物把.env等被忽略文件重新塞回上传集。对应实现见 src/utils/index.ts,并有专门 fixtureprebuilt-filepathmap-ignore配套测试。
  • 超大文件不再崩溃(17.6.2):大于 Nodefs.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(批量重定向文件与目录)、microfrontendsymlinksvercelignore-allow-nodemodules等典型场景。

在包目录内可直接运行:

# 单元测试 pnpm vitest-unit # 集成测试 pnpm vitest-e2e # 类型检查 pnpm type-check

八、进一步探索

想要深入理解@vercel/client,建议按以下顺序阅读源码:

  1. src/create-deployment.ts:事件流编排入口;
  2. src/upload.ts:并发上传、重试与取消逻辑;
  3. src/check-deployment-status.ts:状态轮询与限流退避;
  4. src/utils/index.ts:文件树构建、忽略规则、fetchApi 封装、静态内联判定;
  5. src/types.ts:全部类型定义(VercelClientOptionsDeploymentVercelConfig等);
  6. src/continue.ts:手动部署续跑;
  7. packages/client/CHANGELOG.md:版本演进完整记录;
  8. packages/client/README.md:官方使用文档。

如果你是 CLI 使用者,该包的行为会以vercel deployvercel devvercel build等命令的形式透出;如果你是工具链开发者,直接消费@vercel/client的事件流即可把 Vercel 部署能力嵌入自己的发布流水线——从文件收集、哈希去重、限流退避到别名分配,所有工程细节都已在此包中处理好。

  • CLI
  • 后端
  • 云原生

【免费下载链接】vercel

Develop. Preview. Ship.

项目地址:https://gitcode.com/gh_mirrors/ve/vercel
点击查看免费下载

相关推荐

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询