---
title: "Browser bundles"
description: "Keep forges small in a browser bundle, and see how CI checks the sizes."
canonical_url: "https://docs-8831a2g1f-poetry.vercel.app/guides/bundling"
---
# Browser bundles

> Keep forges small in a browser bundle, and see how CI checks the sizes.

This page explains how to keep `forges` small in a browser bundle and how CI checks the bundle sizes.

## Import one forge

Each provider has its own subpath export. Import from the subpath to bundle only the forges you use:

```ts
import { githubLite } from 'forges/github'
```

The root `forges` entry re-exports every provider. The package declares `sideEffects: false`, so bundlers can tree-shake the root entry, but a subpath import makes the dependency explicit.

## Use the Lite factory

Every provider except pushin.eu exports two factories, `<kind>()` and `<kind>Lite()`. Both build the same provider. Only `<kind>()` references the webhook handlers. A browser doesn't receive webhook deliveries, so a bundle that imports only `githubLite` leaves out the signature verification and payload translation code.

On a Lite provider, `capabilities.sources.webhook` is `false`, and `webhooks.verify()` and `webhooks.ingest()` reject with `UnsupportedOperationError`. All other operations work the same way, including webhook management with `webhooks.create()` and `webhooks.list()`.

pushin.eu has no Lite factory because `pushin()` contains no webhook code.

## GraphQL on GitHub

`forges/github` loads its GraphQL documents with a dynamic `import()` the first time it needs them, so your bundler emits them as a separate chunk. Discussions and their reactions, `threads.getMany()`, review threads, subscriptions, transfers and duplicate marking use GraphQL. `threads.list()` also uses it to read the checks of each pull request in a page. Code that calls only REST operations doesn't load the chunk.

An anonymous GitHub provider doesn't use GraphQL, because GitHub requires a credential for its GraphQL API.

## Test modules

`forges/fake` and `forges/testing` are for tests. Leave them out of production bundles.

`loadFixtures()` in `forges/testing` imports `node:fs` when you call it. No other entry imports a Node.js built-in module.

## Sizes

The following table compares the Lite factory of each provider with an existing client for the same forge. Each row is a one-line import, bundled for the browser with rolldown and minified:

<table>
<thead>
  <tr>
    <th>
      Forge
    </th>
    
    <th>
      <code>
        forges
      </code>
      
       (Lite)
    </th>
    
    <th>
      Existing client
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      GitHub
    </td>
    
    <td>
      79.8 kB, 24.3 kB gzip
    </td>
    
    <td>
      <code>
        @octokit/rest
      </code>
      
       22: 105.7 kB, 21.1 kB gzip
    </td>
  </tr>
  
  <tr>
    <td>
      GitLab
    </td>
    
    <td>
      63.1 kB, 19.7 kB gzip
    </td>
    
    <td>
      <code>
        @gitbeaker/rest
      </code>
      
       43: 233.7 kB, 55.3 kB gzip
    </td>
  </tr>
  
  <tr>
    <td>
      Gitea / Forgejo
    </td>
    
    <td>
      55.6 kB, 17.2 kB gzip
    </td>
    
    <td>
      <code>
        gitea-js
      </code>
      
       1.23: 54.9 kB, 6.8 kB gzip
    </td>
  </tr>
  
  <tr>
    <td>
      Bitbucket
    </td>
    
    <td>
      50.5 kB, 15.9 kB gzip
    </td>
    
    <td>
      <code>
        bitbucket
      </code>
      
       2.12: 110.4 kB, 15.0 kB gzip
    </td>
  </tr>
</tbody>
</table>

The existing clients cover every endpoint and return each forge's raw responses. `forges` covers fewer operations and returns the shared model, with capabilities, pagination and warnings. A provider is bundled whole: importing one forge leaves out the other forges, but it keeps the operations you don't call.

## Size budgets

`pnpm test:size` runs [`scripts/bundle-size.ts`](https://github.com/danielroe/forges/blob/main/scripts/bundle-size.ts). For each entry with a budget, the script bundles a one-line browser consumer with rolldown, the bundler that Vite uses. It measures the entry chunk minified and gzipped, and reports lazy chunks separately. Lazy chunks don't count toward the budget.

The script fails in two cases:

- An entry is over its budget.
- A Lite bundle contains a string that only the webhook code uses, such as `x-hub-signature-256`.

The script measures `dist`, so build first:

```sh
pnpm build
pnpm test:size
```

To print the sizes without failing, pass `--report`:

```sh
node scripts/bundle-size.ts --report
```


## Sitemap

See the full [sitemap](https://docs-8831a2g1f-poetry.vercel.app/sitemap.md) for all pages.
