npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

gixa

v0.1.2

Published

Unified Git Provider - single TypeScript API for GitHub, GitLab, Gitea, and GitBucket

Downloads

314

Readme

gixa

npm version npm downloads license Ask DeepWiki

One API for GitHub, GitLab, Gitea, and GitBucket. Write your code once, swap the provider string, done.

If you've ever had to maintain separate API integrations for different git platforms, you know the pain. They all do the same things but none of them agree on how. GitLab calls them "merge requests", GitHub calls them "pull requests". GitLab paginates with x-next-page headers, GitHub uses Link headers. GitLab authenticates with Private-Token, GitHub with Authorization: token. And so on.

gixa normalizes all of that behind a single interface.

Install

pnpm add gixa
# or: npm install gixa

Usage

If you have gh (GitHub CLI) installed and logged in, this just works:

import { createProvider } from "gixa";

// No token needed - picked up from `gh auth token`
const github = createProvider("github");

const { items: repos } = await github.repos.list("unjs");
const repo = await github.repos.get("unjs", "gixa");
console.log(repo.fullName, repo.defaultBranch);

Same for GitLab with glab, and Gitea with tea or env vars.

You can always pass a token explicitly:

const github = createProvider("github", {
  token: process.env.GITHUB_TOKEN,
});

const gitlab = createProvider("gitlab", {
  token: "glpat-...",
  baseURL: "https://gitlab.example.com",
});

The detection chain is: explicit token > env vars (GITHUB_TOKEN, GITLAB_TOKEN, GITEA_TOKEN) > CLI tools (gh, glab) > CLI config files. First match wins.

Providers

GitHub (+ GitBucket)

GitBucket implements the GitHub API, so the same provider handles both. Just change the baseURL:

// GitHub - token auto-detected from gh CLI
const gh = createProvider("github");

// GitBucket - same provider, different URL
const gb = createProvider("github", {
  token: "...",
  baseURL: "https://my-gitbucket.example.com/api/v3",
});

GitLab

Uses Private-Token auth and the v4 API internally. You don't need to care about that.

// Token auto-detected from glab CLI or GITLAB_TOKEN env var
const gl = createProvider("gitlab");

// Or explicit
const gl2 = createProvider("gitlab", {
  token: "glpat-...",
  baseURL: "https://gitlab.example.com",
});

Gitea / Forgejo

Forgejo is a Gitea fork with the same API, so both work.

const gt = createProvider("gitea", {
  baseURL: "https://codeberg.org", // or any Gitea/Forgejo instance
});

API

Every provider gives you four resources. They all work the same way across platforms.

repos - list(owner, opts?), get(owner, repo)

issues - list(owner, repo, opts?), get(owner, repo, number), create(owner, repo, input)

pullRequests - list(owner, repo, opts?), get(owner, repo, number), create(owner, repo, input)

users - get(username), authenticated()

List operations accept ListOptions: page, perPage, and state ('open' | 'closed' | 'all'). They return PageResult<T> with items, hasNextPage, nextPage, and an optional totalCount.

Caching

GET requests are cached automatically using unstorage with an LRU driver (5 min TTL, 500 entries). Works out of the box, but you can tweak it:

const github = createProvider('github', {
  cache: {
    ttl: 60_000,     // 1 minute
    enabled: false,   // or turn it off entirely
  },
})

Errors

All providers throw the same error types:

import { NotFoundError, AuthenticationError, RateLimitError } from "gixa";

try {
  await provider.repos.get("owner", "nope");
} catch (err) {
  if (err instanceof NotFoundError) {
    // 404
  }
  if (err instanceof RateLimitError) {
    // 429, check err.retryAfter
  }
  // All errors have err.status, err.platform, err.originalError
}

A 404 from GitHub and a 404 from GitLab both become NotFoundError. Same for 401 (AuthenticationError) and 429 (RateLimitError). Everything else is a generic GixaError.

Sub-path exports

If you only need one provider, import it directly. Better for tree-shaking.

import { GitHubProvider } from "gixa/github";
import { GitLabProvider } from "gixa/gitlab";
import { createGiteaProvider } from "gixa/gitea";
import type { Provider, Repository } from "gixa/types";

Utilities

A few lower-level pieces are exported if you need them:

import { resolveToken } from 'gixa'
import { fetchAllPages, paginate } from 'gixa'

resolveToken('github') runs the same auth detection chain without creating a provider. Useful for checking if credentials exist.

fetchAllPages(fetcher, url) collects every page into a single array. paginate(fetcher, url) is the async generator version if you want to process pages as they come.

What this doesn't do

This is an MVP. It covers repos, issues, PRs, and users. It does not handle:

  • File/content operations (reading files, commits, trees)
  • Webhooks
  • Branch/tag management
  • GraphQL (REST only)
  • Admin operations

These might come later. For now the scope is intentionally small.

Development

pnpm install
pnpm test        # vitest in watch mode
pnpm run build   # obuild

License

MIT