Skip to content
repo-sdk
Esc
navigateopen⌘Jpreview
On this page

Client & providers

How createClient, provider factories, and the thin-adapter/fat-client split fit together.

repo-sdk is built on a clean split: thin provider adapters translate one provider’s REST API into a normalized contract, and a single fat client layers all the cross-provider behavior on top — validation, capability gating, retries, and the listAll iterators.

createClient

createClient takes a provider and returns a RepoClient with grouped namespaces:

import { createClient } from 'repo-sdk';
import { github } from 'repo-sdk/github';

const client = createClient({
  provider: github({ auth: { token: process.env.GITHUB_TOKEN! } }),
});

client.namespaces; // list / listAll
client.repos; // list / listAll / get / downloadArchive / getCloneUrl
client.commits; // list / listAll / get
client.tags; // list / listAll
client.webhooks; // create / list / get / update / delete

The client also exposes two read-only fields derived from the provider:

PropType
providerName?'github' | 'gitlab' | 'bitbucket' | 'azure-devops'

Which provider backs this client.

Type'github' | 'gitlab' | 'bitbucket' | 'azure-devops'
capabilities?RepoCapabilities

The provider capability object — see Capabilities.

TypeRepoCapabilities

Provider factories

Each provider ships a factory on its own subpath. A factory validates its options and returns a RepoProvider — the thin adapter the client drives:

import { github } from 'repo-sdk/github';
import { gitlab } from 'repo-sdk/gitlab';
import { bitbucket } from 'repo-sdk/bitbucket';
import { azureDevOps } from 'repo-sdk/azure-devops';

Every factory takes an auth block; most also accept an optional baseUrl for self-hosted or enterprise instances. See Authentication for the per-provider auth shapes.

Thin adapter, fat client

The RepoProvider contract is deliberately minimal — a flat list of methods like listRepositories, getCommit, and createWebhook. Everything that should behave identically across providers lives in the client, not the adapter:

Parameter validation

Required fields like repo and ref are checked before a request is ever made.

Capability gating

Unsupported options throw unsupported instead of being silently dropped.

Bounded retries

One retry on rate_limited when Retry-After is small (configurable).

Pagination

The listAll async iterators follow cursors for you.

The payoff: a new provider only has to implement the thin adapter, and it instantly inherits all of the client’s behavior.

Injectable fetch

Every factory accepts an optional fetch implementation. In production you omit it and the platform’s global fetch is used; in tests you inject a stub so no real network call is made:

const provider = github({
  auth: { token: 'test-token' },
  fetch: async (input, init) => {
    // return a Response for the request under test
    return new Response(JSON.stringify({ sha: 'abc', commit: { message: 'hi' } }), {
      headers: { 'content-type': 'application/json' },
    });
  },
});

const client = createClient({ provider });

Retry configuration

The client performs one bounded retry on rate_limited when the provider’s Retry-After is small. Tune it via createClient:

const client = createClient({
  provider: github({ auth: { token: process.env.GITHUB_TOKEN! } }),
  retry: {
    rateLimit: true, // default — retry once on rate_limited
    maxRetryAfterSeconds: 10, // default — only if Retry-After ≤ 10s
  },
});

Next: Repositories & namespaces

How repositories are addressed and discovered.

Was this page helpful?