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

@forge-glance/sdk

v0.5.0

Published

GitHub & GitLab API client — REST, GraphQL, and real-time ActionCable subscriptions

Readme

@forge-glance/sdk

Provider-agnostic SDK for GitHub & GitLab — types, REST/GraphQL clients, real-time subscriptions, and dashboard helpers. Works in any Node/Bun runtime.

Install

npm install @forge-glance/sdk
# or
bun add @forge-glance/sdk

Quick Start

import { GitLabProvider } from '@forge-glance/sdk';

const provider = new GitLabProvider('https://gitlab.com', process.env.GITLAB_TOKEN!);
const user = await provider.validateToken();
const prs = await provider.fetchPullRequests();

Providers

Two built-in providers implement the GitProvider interface:

| Provider | Class | Capabilities | |---|---|---| | GitLab | GitLabProvider | Full: merge, rebase, approve, auto-merge, retry pipeline, re-review | | GitHub | GitHubProvider | Core: merge, approve, rebase |

import { createProvider } from '@forge-glance/sdk';

// Auto-create the right provider by slug
const provider = createProvider('gitlab', 'https://gitlab.com', token);

Provider Methods

| Method | Description | |---|---| | validateToken() | Verify credentials, returns UserRef | | fetchPullRequests() | Fetch all MRs you authored, review, or are assigned to | | fetchSingleMR(path, iid, userId) | Fetch one MR by project path and IID | | fetchPullRequestByBranch(path, branch) | Find an open MR by source branch | | createPullRequest(input) | Create a new MR | | updatePullRequest(path, iid, input) | Update an existing MR | | mergePullRequest(path, iid, input?) | Merge an MR | | rebasePullRequest(path, iid) | Rebase an MR onto its target branch | | approvePullRequest(path, iid) | Approve an MR | | unapprovePullRequest(path, iid) | Remove your approval | | setAutoMerge(path, iid) | Enable auto-merge when pipeline passes | | cancelAutoMerge(path, iid) | Cancel auto-merge | | retryPipeline(path, pipelineId) | Retry a pipeline | | requestReReview(path, iid, usernames?) | Request re-review | | watchMR(path, iid, userId, onUpdate) | Real-time MR subscription (returns dispose()) | | fetchMRDiscussions(repoId, iid) | Fetch MR discussions and notes | | fetchBranchProtectionRules(path) | Fetch branch protection rules | | deleteBranch(path, branch) | Delete a branch |


Dashboard API

createDashboard

High-level factory that bundles real-time MR watching with pre-bound mutation actions. Returns a Dashboard object:

import { createDashboard } from '@forge-glance/sdk';

const { actions, subscribe, dispose } = createDashboard({
  provider,
  projectPath: 'group/project',
  mrIid: 42,
  userId
});

// actions are available immediately — no need to wait for the first update
await actions.merge();
await actions.approve();

// subscribe delivers real-time MRDashboardProps on each update
subscribe((mr) => {
  console.log(mr.status, mr.pipeline?.status);
  renderUI(mr, actions);
});

// When done, stop the subscription
dispose();

| Property | Type | Description | |---|---|---| | actions | MRDashboardActions | Pre-bound mutation methods — available immediately | | subscribe(cb) | (cb: (mr: MRDashboardProps) => void) => void | Register a listener for real-time updates | | dispose() | () => void | Stop the subscription and clean up |

Multi-MR: DashboardGroup

Pass an array of IIDs to watch multiple MRs over a single WebSocket:

const { actionsFor, subscribe, dispose } = createDashboard({
  provider,
  projectPath: 'group/project',
  mrIid: [42, 43, 44, 45],
  userId
});

subscribe((mrs) => {
  for (const [iid, mr] of mrs) {
    renderRow(mr, actionsFor(iid));
  }
});

| Property | Type | Description | |---|---|---| | actionsFor(iid) | (iid: number) => MRDashboardActions | Get actions for a specific MR | | subscribe(cb) | (cb: (mrs: Map<number, MRDashboardProps>) => void) => void | Real-time updates for all MRs | | dispose() | () => void | Stop all subscriptions |

getMRDashboardProps

Lower-level helper that transforms a raw PullRequest into render-ready MRDashboardProps. Useful when you already have the MR data from fetchPullRequests() or fetchSingleMR():

import { getMRDashboardProps } from '@forge-glance/sdk';

const prs = await provider.fetchPullRequests();
const dashboards = prs.map(getMRDashboardProps);
// dashboards[0].status → 'mergeable' | 'blocked' | 'draft' | ...
// dashboards[0].mergeButton.visible, .disabled, .label
// dashboards[0].provider → 'gitlab' | 'github'

MRDashboardProps

Pre-computed, UI-ready props — no conditional logic needed in the component layer:

| Field | Description | |---|---| | provider | 'gitlab' or 'github' — use to pick brand icon | | iid, title, webUrl, state | Identity | | status | 'mergeable' \| 'blocked' \| 'draft' \| 'merged' \| 'closed' | | statusDetail | Specific blocker reason (e.g., 'CI_MUST_PASS', 'NEED_REBASE') | | pipeline | Pipeline status breakdown: passing, failing, running counts | | reviews | Reviewer state: required, given, remaining, approvedBy | | mergeButton | { visible, disabled, loading, label } | | rebaseButton | { visible, loading, label, behindBy } | | autoMergeButton | { visible, isActive, strategy, label, cancelLabel } | | blockers | Boolean flags: isDraft, hasConflicts, needsRebase, pipelineFailing, etc. | | diff | { additions, deletions, filesChanged } |

MRDashboardActions

Pre-bound mutation methods — call without passing project path or IID:

| Action | Description | |---|---| | merge(input?) | Merge the MR | | rebase() | Rebase onto target branch | | approve() | Approve the MR | | unapprove() | Remove approval | | setAutoMerge() | Enable auto-merge | | cancelAutoMerge() | Cancel auto-merge | | retryPipeline(id) | Retry a pipeline | | requestReReview(usernames?) | Request re-review | | can | Provider capability flags |


Real-Time Architecture

Shared WebSocket Connection

All watchMR calls on a single GitLabProvider instance share one WebSocket connection via ActionCable. Each MR subscribes 3 channels (merge status, approval state, reviewers) on the shared connection. The connection is ref-counted:

  • First watchMR call → opens the WebSocket
  • Subsequent calls → subscribe additional channels on the existing connection
  • dispose() a watcher → unsubscribes that MR's channels
  • Last watcher disposed → disconnects the WebSocket

This means watching 30 MRs uses 1 WebSocket with 90 channel subscriptions, not 30 separate connections.

createRealtimeWatcher

Generic self-healing subscription + polling module used internally by watchMR. Also exported for custom use:

  • Adaptive poll rate (fast when push is down, slow when healthy)
  • Full-jitter exponential backoff on fetch failures
  • Push-event debouncing (burst events → one refetch)
  • Immediate refetch on reconnect

Types

All domain types are exported:

import type {
  PullRequest,
  MRDashboardProps,
  MRDashboardActions,
  Dashboard,
  MRStatus,
  Reviewer,
  Pipeline,
  PipelineJob,
  DiffStats,
  GitProvider,
  ProviderCapabilities,
} from '@forge-glance/sdk';

License

MIT