@forgrit/deploy-vercel
v0.1.0
Published
Framework-agnostic Vercel deployment adapter implementing @forgrit/deploy-core. Submit, poll, and delete Vercel deployments with optional logger + event-store hooks. Bring your own NestJS/Express/orchestrator.
Maintainers
Readme
@forgrit/deploy-vercel
Framework-agnostic Vercel deployment adapter implementing
IDeploymentProvider
from @forgrit/deploy-core.
Status: early-access (v0.x). v0.1.0 is a thin Vercel REST adapter
for submit / poll / delete. It does NOT upload workspacePath contents
— see Source-upload caveat below.
Install
npm install @forgrit/deploy-vercel @forgrit/deploy-core
# or
pnpm add @forgrit/deploy-vercel @forgrit/deploy-coreQuick example
import { VercelProvider } from '@forgrit/deploy-vercel';
const provider = new VercelProvider({
token: process.env.VERCEL_TOKEN!,
teamId: process.env.VERCEL_TEAM_ID, // optional
});
// Submit a deployment.
const result = await provider.deploy({
jobId: 'job-42',
workspacePath: '/path/to/workspace', // accepted but ignored in v0.1.0
projectName: 'my-next-app',
});
console.log(result.deploymentId, result.deployUrl, result.status);
// Poll until ready.
let status = await provider.getStatus(result.deploymentId);
while (status.status === 'queued' || status.status === 'building') {
await new Promise((r) => setTimeout(r, 2000));
status = await provider.getStatus(result.deploymentId);
}
// Teardown (treats 404 as already-deleted).
await provider.teardown(result.deploymentId);NestJS / orchestrator integration
This package intentionally has no NestJS, Prisma, or app-framework imports. Wire it into your orchestrator by passing config + logger + event-store hooks as constructor options.
import { Injectable } from '@nestjs/common';
import {
VercelProvider as CoreVercelProvider,
type VercelDeployEventStore,
} from '@forgrit/deploy-vercel';
@Injectable()
export class MyVercelProvider {
private readonly delegate: CoreVercelProvider;
constructor(
private readonly logger: MyLoggerService,
private readonly eventService: MyDeployEventService,
) {
const eventStore: VercelDeployEventStore = {
findLatest: (id) => this.eventService.findLatest(id),
recordTransition: (input) => this.eventService.record(input),
};
this.delegate = new CoreVercelProvider({
token: () => process.env.VERCEL_TOKEN, // lazy — re-read per call
teamId: () => process.env.VERCEL_TEAM_ID,
logger: this.logger,
eventStore,
});
}
// Delegate deploy / getStatus / teardown to this.delegate.
}The lazy getter pattern for token / teamId preserves
"read-config-at-call-time" semantics so tests + dynamic env reloads work
without reconstructing the provider.
API surface
| Export | Kind | Purpose |
| ------------------------ | ----- | --------------------------------------------------------------------------- |
| VercelProvider | class | The adapter — implements IDeploymentProvider |
| VercelProviderOptions | type | Constructor argument shape |
| VercelLogger | type | Optional log/warn/error hooks (all 3 methods are optional) |
| VercelDeployEventStore | type | Optional non-blocking event persistence (findLatest + recordTransition) |
| VercelFetch | type | Injectable fetch shape (for testing without monkey-patching globals) |
| VercelDeployEvent | type | Minimal event row shape (just { toState }) |
| VercelDeployStatus | type | Alias for DeployResult['status'] from @forgrit/deploy-core |
| mapVercelStatus | fn | Pure helper — Vercel readyState → status union mapping |
Behavior
| Method | What it does |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| deploy(params) | POST /v13/deployments with { name, target, framework, projectSettings, meta.forgritJobId }. Returns { deploymentId, deployUrl, provider: 'vercel', status }. Optionally records the initial queued transition (deduped if a prior event exists for deploymentId). |
| getStatus(deploymentId) | GET /v13/deployments/:id. Maps readyState/state to the deploy-core status union. Optionally records state-change transitions; same-state polls are skipped. Event-store failures log + swallow. |
| teardown(deploymentId) | DELETE /v13/deployments/:id. 404 is treated as success (already deleted). Non-404 errors throw with status + body. |
Status mapping:
| Vercel readyState | Mapped status |
| ---------------------------- | ------------- |
| QUEUED, INITIALIZING | queued |
| BUILDING, DEPLOYING | building |
| READY | ready |
| CANCELED, ERROR, unknown | error |
Source-upload caveat
v0.1.0 does NOT upload workspace files. The workspacePath field
on DeployParams is accepted for IDeploymentProvider compatibility but
is ignored — Vercel reads source from a Git provider connection on the
project, not from a tarball POSTed at deploy time.
This mirrors the apps/api Vercel provider's current behavior. If you
need workspace upload semantics (Vercel Files API or
gitSource: { type: 'tarball' }), it will land in a future v0.x
release (tentatively @forgrit/[email protected]) under plan #23a.1.
Engines
- Node >= 20 (uses native
globalThis.fetch). Tests can pass an injectedfetchto run on older Node versions.
License
MIT. See LICENSE.
Issues: https://github.com/forgrit-ai/forgrit/issues
Related packages
@forgrit/deploy-core— the provider contract this package implements@forgrit/deploy-neon(plan #23b, deferred) — Neon Postgres provisioner@forgrit/deploy-railway(plan #23c, deferred) — Railway provider adapter
See plan #23a for design rationale.
