Skip to content
repo-sdk
Esc
navigateopen⌘Jpreview
Zero dependencies · Edge-compatible · MIT

The unified TypeScript SDK for every git provider.

One normalized API over GitHub, GitLab, Bitbucket Cloud, Azure DevOps, and Gitea — plus any plain git remote.

$ npm install repo-sdk
integration.ts
import { createClient } from 'repo-sdk';
import { github } from 'repo-sdk/github';

const client = createClient({
  // Swap in gitlab(), bitbucket(), azureDevOps(), gitea(), or gitHttp() —
  // every call below keeps the same normalized shape.
  provider: github({ auth: { token: process.env.GITHUB_TOKEN! } }),
});

const head = await client.commits.get({
  repo: 'capawesome-team/repo-sdk',
  ref: 'main',
});

for await (const tag of client.tags.listAll({ repo: 'capawesome-team/repo-sdk' })) {
  console.log(tag.name, head.sha);
}

Works with

GitHubGitLabBitbucketAzure DevOpsGiteaAny git remote
Node.js ≥ 20Cloudflare WorkersWeb-standard runtimes

Including self-hosted: GitHub Enterprise Server, GitLab self-managed, Azure DevOps Server, Gitea & Forgejo

Everything a git integration needs

Auth, pagination, rate limits, errors, and webhooks — repo-sdk normalizes the messy parts of every provider's REST API behind one typed interface.

One unified API

Discovery, commits, tags, branches, refs, downloads, and webhooks — one interface over every provider.

Zero dependencies

Just fetch and Web Crypto. Nothing to audit, nothing to bloat your bundle.

Edge-compatible

Runs on Node.js ≥ 20, Cloudflare Workers, and any Web-standard runtime — no node:* imports anywhere.

Normalized types

Namespace, Repository, Commit, Tag, Webhook — with a raw escape hatch on every object.

Typed errors

Every failure is a RepoError with a stable code union, retry hints, and redacted secrets.

Webhook verify & parse

Standalone helpers that take a Web-standard Request — no client needed.

Capability gating

Providers differ. Unsupported options throw instead of being silently dropped.

Pagination that walks itself

list returns a page and an opaque cursor; the listAll async iterators follow cursors for you.

Resilient by default

Bounded retry on rate limits honoring Retry-After, and an AbortSignal on every request.

GitHub App auth

Installation tokens minted in pure Web Crypto — RS256 JWTs, PKCS#1 or PKCS#8 keys.

Self-hosted friendly

GitHub Enterprise Server, self-managed GitLab, Azure DevOps Server, and Gitea or Forgejo via baseUrl.

In-memory testing provider

Exercise your integration end to end without hitting a real provider — repo-sdk/testing.

// Mirror every repository the token can reach.
for await (const repo of client.repos.listAll({ owned: true })) {
  const archive = await client.repos.downloadArchive({
    repo: repo.path,
    ref: repo.defaultBranch ?? 'main',
    format: 'tar.gz',
  });
  // archive.stream is a Web ReadableStream<Uint8Array>
  await uploadToStorage(repo.path, archive.stream);
}

Pagination that walks itself

list returns one page and an opaque cursor; thelistAll async iterators follow the cursors for you — and every cursor is a tamper-guarded envelope that can't redirect an authenticated request. Pair it with downloadArchive, which hands you a WebReadableStream instead of a buffer, and walking an entire account fits in a single for await loop.

Read the pagination guide

Webhooks without the ceremony

verifyWebhook and parseWebhookEvent are standalone helpers that take a Web-standard Request — no client needed. They drop straight into Next.js route handlers, Hono, and workers, and they know each provider's signature scheme — HMAC-SHA256, shared token, or Basic auth — comparing in constant time so you don't have to.

Read the webhook guide
import { verifyWebhook, parseWebhookEvent } from 'repo-sdk/github';

export async function POST(request: Request): Promise<Response> {
  const valid = await verifyWebhook({ request, secret: process.env.WEBHOOK_SECRET! });
  if (!valid) return new Response('invalid signature', { status: 401 });

  const event = await parseWebhookEvent(request);
  if (event.type === 'push') {
    console.log(`push to ${event.repo} @ ${event.ref}`);
  }
  return new Response(null, { status: 204 });
}

What you can build with repo-sdk

Anywhere your product meets a git repository — and your users get to choose where that repository lives.

Providers differ. repo-sdk tells you how.

Every provider ships a typed capability object, and the client throwsunsupported instead of silently dropping options.

CapabilityGitHubGitLabBitbucketAzure DevOpsGitea
Repository search
Tag dates
Release webhooks
tar.gz archives
See the full capability matrix

Frequently asked questions

Which git providers does repo-sdk support?

repo-sdk ships dedicated providers for GitHub, GitLab, Bitbucket Cloud, Azure DevOps, and Gitea, plus a gitHttp provider that talks to any plain git remote over the smart-HTTP protocol. Self-hosted deployments are covered too: GitHub Enterprise Server, self-managed GitLab, Azure DevOps Server, and Gitea or Forgejo instances via a baseUrl option.

Authentication per provider
Is repo-sdk an alternative to Octokit?

For multi-provider integrations, yes: instead of stitching together Octokit plus one SDK per provider, you write against one normalized API. If you only ever talk to GitHub, a dedicated SDK exposes more endpoints — repo-sdk focuses on the surface integrations actually share.

Octokit alternatives compared
Does repo-sdk run on Cloudflare Workers and other edge runtimes?

Yes. repo-sdk has zero runtime dependencies and no node:* imports — it is built on fetch, Web Crypto, and Web streams only, so it runs on Node.js 20+, Cloudflare Workers, and any Web-standard runtime.

How does repo-sdk handle feature differences between providers?

Every provider declares a typed RepoCapabilities object and the client gates on it: an option a provider cannot honor throws a RepoError with code unsupported instead of being silently dropped. You can also branch on client.capabilities at runtime to decide which features to offer.

How capability gating works
How does webhook signature verification work across providers?

verifyWebhook is a standalone helper that takes a Web-standard Request and knows each provider’s scheme — HMAC-SHA256 signatures for GitHub, Bitbucket, and Gitea, a shared token for GitLab, and Basic auth for Azure DevOps. All comparisons run in constant time, and parseWebhookEvent then normalizes the delivery into one event shape.

Receiving webhooks guide
Does repo-sdk support GitHub App authentication?

Yes. Pass an appId and privateKey and the SDK mints installation tokens itself, signing RS256 JWTs with pure Web Crypto. Both PKCS#1 and PKCS#8 keys are accepted, and getInstallationToken() exposes the current token for use outside the SDK — in a git clone URL, for example.

GitHub authentication
How do I test code that uses repo-sdk?

Import the in-memory provider from repo-sdk/testing: it implements the same contract as the real providers with seedable state, so integration tests run without a network. Every provider factory also accepts an injectable fetch for wire-level tests.

Testing guide

Start here

Write it once. Run it everywhere your users host code.

MIT licensed, zero dependencies, TypeScript all the way down. Your first cross-provider integration is one install away.