Browser bundles
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:
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:
| Forge | forges (Lite) | Existing client |
|---|---|---|
| GitHub | 79.8 kB, 24.3 kB gzip | @octokit/rest 22: 105.7 kB, 21.1 kB gzip |
| GitLab | 63.1 kB, 19.7 kB gzip | @gitbeaker/rest 43: 233.7 kB, 55.3 kB gzip |
| Gitea / Forgejo | 55.6 kB, 17.2 kB gzip | gitea-js 1.23: 54.9 kB, 6.8 kB gzip |
| Bitbucket | 50.5 kB, 15.9 kB gzip | bitbucket 2.12: 110.4 kB, 15.0 kB gzip |
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. 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:
pnpm build
pnpm test:size
To print the sizes without failing, pass --report:
node scripts/bundle-size.ts --report