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

@molecule/api-git-provider

v1.0.3

Published

Abstract interface for a git hosting provider — OAuth endpoints, token auth shape, repository listing and lookup — so an app can support GitHub, GitLab, Gitea or any other host without naming one.

Readme

@molecule/api-git-provider

Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit src/index.ts JSDoc, not this file.

@molecule/api-git-provider — the abstract interface for a git hosting provider: OAuth endpoints, the token auth shape a push expects, and repository listing/lookup.

This exists because "which git hosts do we support?" kept being answered by a closed union inside the application:

const GIT_PROVIDERS = ['github', 'gitlab'] as const
const PROVIDER_DEFAULTS: Record<GitProvider, …>      // OAuth URLs + scopes
const TOKEN_USERNAME:    Record<string, string>      // x-access-token | oauth2
const DEFAULT_PROVIDER_HOSTS: Record<GitProvider, …> // github.com | gitlab.com
provider === 'github' ? `${base}/repos/${path}` : `${base}/projects/${…}`

Four parallel tables and a scatter of ternaries, all keyed off that union, so adding a host meant editing the app. Behind this interface the supported set becomes whichever bonds are wired, and a consumer never names a vendor.

Quick Start

import {
  listGitProviders,
  registerGitProvider,
  requireGitProvider,
} from '@molecule/api-git-provider'
import { provider as github } from '@molecule/api-git-provider-github'
import { provider as gitea } from '@molecule/api-git-provider-gitea'

registerGitProvider(github)
registerGitProvider(gitea)

// What a "connect your repo" picker offers — not a hardcoded list.
listGitProviders().map((p) => ({ id: p.id, label: p.label }))

const p = requireGitProvider('github')
const repos = await p.listRepositories({
  host: p.defaultHost,
  token: '<oauth token>',
  page: 1,
  perPage: 30,
})

Type

core

Installation

npm install @molecule/api-git-provider

API

Interfaces

GetRepositoryInput

Input for a single repository lookup.

interface GetRepositoryInput {
  /** Host to query. */
  host: string
  /** OAuth access token, or null for an unauthenticated (public) lookup. */
  token: string | null
  /** `owner/name` path. */
  path: string
}

GitProvider

A git hosting provider.

Everything here was a per-vendor branch or lookup table inside an application before this interface existed: provider === 'github' ? … : … for API bases, headers, list endpoints and response shapes, plus four parallel Record<GitProvider, …> tables keyed off a closed union. Adding a host meant editing the app. A provider bond absorbs all of it, so the set of supported hosts becomes "whichever bonds are wired".

interface GitProvider {
  /** Stable identifier, e.g. `github`. Used as the bond name and in stored credentials. */
  id: string

  /** Human-readable name for pickers, e.g. `GitHub`. */
  label: string

  /**
   * The public host this provider lives on, e.g. `github.com`.
   *
   * Load-bearing for security, not just defaults: an OAuth token that grants
   * repo read/write must never be embedded in a remote URL for an arbitrary
   * user-supplied host, so a consumer binds tokens to this host (plus any
   * configured self-hosted endpoint) and refuses everything else.
   */
  defaultHost: string

  /** How this provider authenticates — OAuth flow, or a user-minted token. */
  auth: GitProviderAuth

  /**
   * HTTPS basic-auth username to pair with the token as the password, or NULL
   * when the provider expects the account's own username.
   *
   * Nullable because it is not always a per-provider constant: GitHub wants the
   * literal `x-access-token` and GitLab `oauth2`, but SmolForge wants the
   * user's Forge username — a per-CREDENTIAL value the provider cannot know.
   * A consumer that finds null must substitute the connected account's
   * username. The wrong username fails as an opaque 401 at push time, nowhere
   * near the code that chose it.
   */
  basicAuthUsername: string | null

  /**
   * REST API base URL for a host.
   *
   * A parameter rather than a constant because the same provider serves a
   * different base for its public host than for a self-hosted instance —
   * `api.github.com` vs `<host>/api/v3`.
   *
   * @param host - The host being addressed.
   * @returns The API base URL, without a trailing slash.
   */
  apiBaseForHost(host: string): string

  /**
   * Headers for an API call, including auth when a token is given.
   *
   * @param token - OAuth access token, or null for unauthenticated requests.
   * @returns Headers to send.
   */
  apiHeaders(token: string | null): Record<string, string>

  /**
   * List repositories the token can see, newest activity first.
   *
   * @param input - Host, token and pagination.
   * @returns Normalized repositories. Empty array when the page is past the end.
   */
  listRepositories(input: ListRepositoriesInput): Promise<GitRepository[]>

  /**
   * Look up one repository.
   *
   * @param input - Host, token and `owner/name` path.
   * @returns The repository, or null when it does not exist or is not visible.
   */
  getRepository(input: GetRepositoryInput): Promise<GitRepository | null>
}

GitProviderOAuth

OAuth endpoints and scope for a provider.

interface GitProviderOAuth {
  /** Authorization endpoint the user is redirected to. */
  authorizeUrl: string
  /** Token exchange endpoint. */
  tokenUrl: string
  /**
   * Space-separated scopes.
   *
   * State what each scope is FOR. GitLab needs `read_api` on top of the
   * `*_repository` scopes because the repository scopes cover only the git
   * protocol, not the REST API a repo picker calls — an omission that fails
   * only at the picker, long after the OAuth flow looks successful.
   */
  scope: string
}

GitRepository

A repository as this app understands it, independent of whose API described it. GitHub calls it full_name/clone_url, GitLab path_with_namespace/ http_url_to_repo; a consumer should never have to know which.

interface GitRepository {
  /** `owner/name`, however the provider spells it. */
  fullName: string
  /** HTTPS clone URL. */
  url: string
  /** Whether the repository is private. Null when the provider does not say. */
  private: boolean | null
  /** Default branch name, or null when the provider does not report one. */
  defaultBranch: string | null
  /** Approximate size in KB, or null when unknown. */
  sizeKb: number | null
  /** ISO 8601 timestamp of the last push/activity, or null. */
  updatedAt: string | null
  /** Short description, or null. */
  description: string | null
}

ListRepositoriesInput

Input for a paginated repository listing.

interface ListRepositoriesInput {
  /** Host to query — the provider's default, or a self-hosted instance. */
  host: string
  /** OAuth access token. */
  token: string
  /** 1-based page number. */
  page: number
  /** Page size. */
  perPage: number
}

Types

GitProviderAuth

How a provider authenticates.

A discriminated union rather than an optional OAuth block, because the first non-OAuth provider proved the difference is structural, not a missing field. SmolForge has no authorize/token endpoints at all — the user mints a personal access token and uses it directly. Modelling that as "OAuth with empty URLs" would let a consumer start an authorize redirect to "".

type GitProviderAuth =
  | ({ kind: 'oauth' } & GitProviderOAuth)
  | {
      kind: 'pat'
      /** Where the user creates a token, for the UI to link to. */
      tokensUrl?: string
    }

Functions

clearGitProviders()

Remove every registered provider. Test teardown only.

function clearGitProviders(): void

getGitProvider(id)

Look up a registered provider.

function getGitProvider(id: string): GitProvider | undefined
  • id — The provider id, e.g. github.

Returns: The provider, or undefined when it is not wired.

hasGitProvider(id)

Whether a provider id is wired.

function hasGitProvider(id: string): boolean
  • id — The provider id.

Returns: True when registered.

listGitProviders()

Every registered provider, sorted by id.

This is what a UI should render as the list of connectable hosts — the set is whatever the deployment wired, never a hardcoded union.

function listGitProviders(): GitProvider[]

Returns: The registered providers.

registerGitProvider(provider)

Register a git provider. Re-registering the same id replaces it.

function registerGitProvider(provider: GitProvider): void
  • provider — The provider to register.

requireGitProvider(id)

Look up a provider, throwing when it is absent.

Use this on a path where a missing provider is a configuration error rather than a branch: the error names what IS wired, because "unknown provider gitlab" is unactionable while "gitlab is not wired; github is" says exactly what to do.

function requireGitProvider(id: string): GitProvider
  • id — The provider id.

Returns: The provider.

Available Providers

| Provider | Package | | --------- | -------------------------------------- | | Gitea | @molecule/api-git-provider-gitea | | GitHub | @molecule/api-git-provider-github | | GitLab | @molecule/api-git-provider-gitlab | | SmolForge | @molecule/api-git-provider-smolforge |

Injection Notes

  • Named multi-provider, like ai — not a singleton. One deployment has several wired at once because different users connect different hosts. Register by id and look up by id; there is no "current" git provider.
  • defaultHost is a security boundary, not a default. An OAuth token here grants repository read/write, so it must never be embedded in a remote URL for an arbitrary user-supplied host. Bind tokens to defaultHost plus any configured self-hosted endpoint, and refuse the rest.
  • tokenUsername is not cosmetic. Pushing over HTTPS with a token as the password needs the username the provider expects (x-access-token for GitHub, oauth2 for GitLab). The wrong one fails as an opaque auth error at push time, nowhere near the OAuth code that chose it.
  • apiBaseForHost takes the host because the same provider serves a different base for its public host than for a self-hosted instance (api.github.com vs <host>/api/v3). A constant cannot express that.
  • listRepositories returns [] past the last page, never an error — a caller paginating until empty is the normal shape, and a throw there turns an ordinary end-of-list into a failed import.