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

@hameddk/bitbucket-cloud-client

v0.1.0

Published

Pure REST API client for Bitbucket Cloud. Pull requests, tags, workspaces, repositories — with pagination and rate-limit handling. Caller supplies the access token. Zero deps. Node 18+.

Readme

@hameddk/bitbucket-cloud-client

Pure REST API client for Bitbucket Cloud.

  • Pull requests, tags, workspaces, repositories, current user
  • Auto-pagination via Bitbucket's next URL chain — both single-page and all-at-once forms
  • nextUrl host validation to prevent bearer-token leaks via tampered links
  • Rate-limit aware with configurable auto-retry (HTTP 429, Retry-After)
  • Provider-agnostic auth — caller supplies an async access-token resolver
  • Returns Bitbucket's raw response shape — no derived fields, no business logic
  • Zero dependencies, ESM, Node ≥ 18

Status: 0.1.0 — early. The documented surface is stable.

Install

npm install @hameddk/bitbucket-cloud-client

Quick start

import { createBitbucketCloudClient } from '@hameddk/bitbucket-cloud-client';

const bb = createBitbucketCloudClient({
  getAccessToken: () => oauth.getValidBitbucketAccessToken(),
  options: {
    pageSize: 50,
    autoRetryRateLimit: true,
    onRateLimit: (sec, attempt) => console.warn(`[bb] rate limited, retry ${attempt} in ${sec}s`),
  },
});

// Pull all merged PRs in a repo
const merged = await bb.listPullRequestsAll({
  workspace: 'your-workspace',
  repo: 'your-repo',
  state: 'MERGED',
});

// Tags / releases
const tags = await bb.listTagsAll({ workspace: 'your-workspace', repo: 'your-repo' });

// Current user (for /2.0/user)
const me = await bb.getCurrentUser();

Public API

const bb = createBitbucketCloudClient({
  getAccessToken,                      // () => Promise<string|null>
  options: {
    baseUrl: 'https://api.bitbucket.org/2.0',
    pageSize: 50,
    autoRetryRateLimit: true,          // false | true | { maxRetries, maxDelayMs }
    onRateLimit: (retryAfterSec, attempt) => {},
    fetch: customFetch,                // testing only
    sleep: customSleep,                // testing only
  },
});

Methods

Each paginated resource exposes a single-page form (returns { values, next }) and an auto-paginate form (returns the full array). Use the auto-paginate form unless you need to handle pagination errors yourself.

| Method | Returns | |---|---| | listPullRequests({ workspace, repo, state, pageLen?, nextUrl? }) | { values, next } | | listPullRequestsAll({ workspace, repo, state, pageLen?, maxTotal? }) | BitbucketPR[] | | listTags({ workspace, repo, pageLen?, nextUrl? }) | { values, next } | | listTagsAll({ workspace, repo, pageLen?, maxTotal? }) | BitbucketTag[] | | listWorkspaces({ role?, pageLen?, nextUrl? }) | { values, next } | | listWorkspacesAll({ role?, pageLen?, maxTotal? }) | Workspace[] | | listRepositories(workspace, { pageLen?, nextUrl? }) | { values, next } | | listRepositoriesAll(workspace, { pageLen?, maxTotal? }) | Repository[] | | getCurrentUser() | raw user object |

state for pull requests must be 'OPEN' | 'MERGED' | 'DECLINED' | 'SUPERSEDED'.

The toolkit returns Bitbucket's raw response shapes under values. No field renaming, no derived fields. Compute things like pr_cycle_time_hours from created_on / closed_on in your business code.

Manual pagination

The single-page methods return { values, next }. The next field is a full URL (or null). Pass it back as nextUrl to fetch the next page:

let page = await bb.listTags({ workspace: 'w', repo: 'r' });
while (page.next) {
  console.log(page.values.length, 'tags');
  page = await bb.listTags({ nextUrl: page.next });
}

This is useful when you want to handle errors mid-pagination (e.g. return the results gathered so far).

Authentication

The toolkit doesn't do OAuth — pass a getAccessToken() resolver that returns a valid bearer token (or null). Typical wiring with @hameddk/oauth-toolkit:

const bb = createBitbucketCloudClient({
  getAccessToken: () => bitbucketOAuth.getValidAccessToken(),
});

The resolver is called on every request — no internal caching.

Security: nextUrl host validation

Bitbucket's pagination uses absolute next URLs returned in each response. The toolkit validates that any URL it follows (whether passed in by the caller as nextUrl or returned mid-pagination) matches the configured baseUrl host. A mismatch throws BitbucketConfigError and the request is never sent.

Why this matters: if your application persists next URLs (e.g. for resumable pagination state) and an attacker can tamper with that storage, they could otherwise redirect a request to their own host — leaking the bearer token in the Authorization header. The toolkit prevents this regardless of where the URL came from.

// This throws BitbucketConfigError before any fetch happens:
bb.listPullRequests({ nextUrl: 'https://attacker.example/steal' });

If you proxy Bitbucket through a custom host, set options.baseUrl to that proxy and the validation will use the proxy host instead.

Rate limiting

⚠️ Behavior change vs. raw fetch. With autoRetryRateLimit: true (the default), this toolkit transparently retries HTTP 429 responses. Most Bitbucket clients fail-fast on 429, so callers migrating from manual fetch calls will see different behavior. The toolkit does not retry 401/403 — token-expiry errors still surface immediately as BitbucketAuthError. Set autoRetryRateLimit: false to preserve fail-fast behavior.

Three forms:

| Value | Behavior | |---|---| | true (default) | Up to 2 retries, max 30s wait per retry. | | false | No retry. First 429 throws BitbucketRateLimitError. | | { maxRetries, maxDelayMs } | Custom limits. |

When 429 is received:

  • Reads Retry-After (seconds or HTTP-date) — falls back to 1s.
  • If the wait would exceed maxDelayMs, throws BitbucketRateLimitError immediately.
  • After maxRetries, throws BitbucketRateLimitError.
  • onRateLimit(retryAfterSec, attempt) callback fires before each retry. Throwing inside the callback is swallowed.

Errors

import {
  BitbucketError,             // base
  BitbucketConfigError,       // bad config / missing required arg / bad nextUrl host
  BitbucketAuthError,         // 401, 403, or getAccessToken returned null
  BitbucketNotFoundError,     // 404
  BitbucketRateLimitError,    // 429 after retries (or first 429 if disabled)
  BitbucketApiError,          // other 4xx/5xx
} from '@hameddk/bitbucket-cloud-client';

All errors extend BitbucketError. The provider's response body is preserved verbatim where available.

What this library does not do

  • Doesn't perform OAuth — bring your own token resolver.
  • Doesn't persist anything. No DB, no filesystem.
  • Doesn't compute PR cycle-time, lead-time, or other metrics.
  • Doesn't normalize Bitbucket's response shapes — values is raw.
  • Doesn't dedupe or join paginated results across multiple states.
  • Doesn't cap result sizes — pass maxTotal if you want a cap.

License

MIT © 2026 Hamed Sattari