@forgrit/deploy-core
v0.1.0
Published
Provider-agnostic deployment primitives: result-shape types, failure taxonomy, health-probe classifier, retry-with-backoff helper. Zero runtime dependencies. Used by ForGrit and consumable by any deployment orchestrator.
Maintainers
Readme
@forgrit/deploy-core
Provider-agnostic deployment primitives: result-shape types, failure taxonomy, health-probe classifier, retry-with-backoff helper.
Zero runtime dependencies. Bring your own deployment provider.
Status
Early-access (v0.x). Breaking changes are permitted in minor version bumps until v1.0.0. Subscribe to CHANGELOG.md for migration notes.
What's in the box
Provider interface + result shapes
The three-method contract every deployment provider must implement:
import type { IDeploymentProvider, DeployParams, DeployResult } from '@forgrit/deploy-core';
class MyVercelProvider implements IDeploymentProvider {
async deploy(params: DeployParams): Promise<DeployResult> {
// call Vercel API, return { deploymentId, deployUrl, provider: 'vercel', status }
}
async getStatus(deploymentId: string) {
// poll Vercel, return status snapshot
}
async teardown(deploymentId: string) {
// call Vercel's delete API
}
}5-bucket failure taxonomy
Classify deployment errors into one of 5 buckets (config / code / infra / transient / unclassified). First-match-wins precedence. Battle-tested at ForGrit since Q1-2026.
import { classifyError } from '@forgrit/deploy-core';
classifyError('VERCEL_TOKEN expired'); // → 'config'
classifyError('TypeScript compile failed'); // → 'code'
classifyError('Provider returned 503'); // → 'infra'
classifyError('Rate limit exceeded'); // → 'transient'
classifyError('Some weird error we never saw'); // → 'unclassified'The taxonomy also exports Postgres ILIKE helpers for SQL aggregation
paths (buildIlikeCaseForBucket, buildUnclassifiedCase).
Health-probe failure classifier
Classify network errors caught during health probes:
import { classifyHealthProbeFailure, HealthProbeFailureReason } from '@forgrit/deploy-core';
try {
await fetch(probeUrl, { signal: AbortSignal.timeout(5000) });
} catch (e) {
const reason = classifyHealthProbeFailure(e);
// → HealthProbeFailureReason.TIMEOUT | DNS | TLS | CONNECTION_REFUSED | NETWORK | HTTP_ERROR | UNKNOWN
}Retry with exponential backoff
import { retryWithBackoff } from '@forgrit/deploy-core';
const result = await retryWithBackoff(() => fetch('https://provider-api.example.com'), {
maxAttempts: 5,
baseMs: 200,
maxMs: 5000,
shouldRetry: (err, attempt) => {
// Don't retry caller bugs (4xx); do retry transient (429, 5xx, network)
if (err instanceof Response && err.status >= 400 && err.status < 500 && err.status !== 429) {
return false;
}
return true;
},
onRetry: (err, attempt, delayMs) => {
logger.warn({ attempt, delayMs, err }, 'Retrying provider call');
},
});Why deploy-core?
ForGrit runs prompt-to-deployment generation at scale. The deploy-core package extracts the framework-agnostic primitives so any deployment orchestrator can adopt them — without taking on the apps/api NestJS dep graph.
Provider-specific adapters ship as separate sibling packages:
@forgrit/deploy-vercel— Vercel provider (plan #23a — coming soon)@forgrit/deploy-neon— Neon Postgres database provisioner (plan #23b)@forgrit/deploy-railway— Railway provider (plan #23c)
Each adapter depends on @forgrit/deploy-core for the shared interfaces +
taxonomy. The IDeploymentProvider interface is the contract.
Why proprietary stays apps/api-local
Plan #23's investigation found ~5,324 LOC of deploy-related code in ForGrit's apps/api. Only ~176 LOC of that is genuinely framework-agnostic — the rest is tightly coupled to ForGrit's NestJS module graph, Prisma schema, and audit-log conventions. This package ships ONLY the framework-agnostic 176 LOC. The rest stays proprietary at apps/api-local.
See plan #22's close-via-reframing for the related "what stays proprietary" rationale (codegen orchestration).
Install
npm install @forgrit/deploy-core
# or
pnpm add @forgrit/deploy-core
# or
yarn add @forgrit/deploy-coreEngines
Node 20+.
License
MIT — see LICENSE.
