Budgets per page
third-party script KB (maxKb, default 150), requests (maxRequests, default 10) and estimated main-thread time (maxMainThreadMs, default 250 ms, at msPerKb 2.5 ms per compressed KB).
GitHub Packages · MIT · Node ≥ 18
How much does each third-party script cost your pages? Estimated bytes, requests and main-thread time per page and per vendor, checked against budgets.
Third-party script weight and load-cost report for static sites: estimated bytes, requests and main-thread time per page and per vendor, with budgets, plus sync-script, duplicate and preconnect checks. SARIF, Action. Zero deps.
1 Point the @mrlouiswright scope at GitHub Packages in .npmrc
@mrlouiswright:registry=https://npm.pkg.github.com2 Install
npm i -D @mrlouiswright/thirdparty-pulsePublished to GitHub Packages, not npmjs.com. GitHub Packages needs a GitHub token with read:packages to install, even for public packages: add //npm.pkg.github.com/:_authToken=${GITHUB_TOKEN} to .npmrc or run npm login --registry=https://npm.pkg.github.com.
$ npx thirdparty-pulse dist --base-url https://example.comthirdparty-pulse — https://example.com1 page(s), 1 with third-party content, 6 vendor(s)/origin(s), heaviest page ~465 KB of third-party script HIGH index.html / loads ~465 KB of third-party script (budget 150 KB): intercom 200 KB, google-tag-manager 90 KB, meta-pixel 75 KB, hotjar 70 KB, cdn.example-widgets.net 30 KB [tp-budget-bytes]HIGH index.html:5 Synchronous third-party script https://www.googletagmanager.com/gtm.js?id=GTM-ABC123 on / in <head> [tp-sync-script]MEDIUM index.html /: estimated ~1163 ms of third-party main-thread work (budget 250 ms) [tp-budget-main-thread]MEDIUM index.html:11 https://static.hotjar.com/c/hotjar-1.js is included 2 times on / [tp-duplicate-script]LOW index.html www.googletagmanager.com is requested from <head> on / without a preconnect [tp-no-preconnect]LOW index.html connect.facebook.net is requested from <head> on / without a preconnect [tp-no-preconnect]LOW index.html static.hotjar.com is requested from <head> on / without a preconnect [tp-no-preconnect]LOW index.html widget.intercom.io is requested from <head> on / without a preconnect [tp-no-preconnect]LOW index.html cdn.example-widgets.net is requested from <head> on / without a preconnect [tp-no-preconnect]LOW index.html:4 preconnect to fonts.unused-cdn.example on /, but nothing on the page loads from it [tp-preconnect-unused]LOW index.html:8 Intercom (~200 KB) on / [tp-heavy-vendor]INFO index.html:9 cdn.example-widgets.net script on / is not in the vendor table; assumed 30 KB [tp-unknown-weight] 2 high, 2 medium, 7 low, 1 info✔ Passing (fail-on: none).
Tag managers, chat widgets, pixels and embeds are usually the heaviest JavaScript on a static site, and nobody owns their weight. thirdparty-pulse reads your built HTML, finds every third-party script, inline vendor loader, iframe and asset, estimates its transfer size and main-thread cost from a table of common vendors (or the sizes you configure), and fails CI when a page goes over budget. It also catches the loading mistakes that make third parties expensive: synchronous scripts in “<head>”, the same script included twice, and missing or unused preconnects.
CLI, library and GitHub Action. Node.js 18+, zero runtime dependencies. Works on any static build output (Astro, Next.js “output: 'export'”, Eleventy, Hugo, Jekyll, Docusaurus, VitePress, plain HTML) and runs offline: point it at the folder your build writes. By Louis Wright.
third-party script KB (maxKb, default 150), requests (maxRequests, default 10) and estimated main-thread time (maxMainThreadMs, default 250 ms, at msPerKb 2.5 ms per compressed KB).
vendorBudgets caps a single vendor (e.g. {"google-tag-manager": 80}).
a built-in table of ~45 vendors (analytics, tag managers, chat, video, maps, payments, CDNs) with typical compressed sizes; override or add hosts with weights (host, URL glob or vendor id). Unknown hosts get unknownKb (30) and an info finding.
synchronous third-party scripts (high in <head>, medium in <body>), duplicate includes, heavy vendors that have a facade (YouTube, Maps, chat), early origins without preconnect, and preconnects that nothing uses.
the JSON report lists every vendor/origin with its weight, requests and the number of pages it is on.
# Example
npx thirdparty-pulse dist# Example
npx thirdparty-pulse _site --max-kb 100 --max-requests 6# Example
npx thirdparty-pulse public -f json > third-party.jsonpermissions:
contents: read
security-events: write # only needed for the SARIF upload
jobs:
thirdparty-pulse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with: { persist-credentials: false }
- run: npm ci && npm run build # produce the static site first
- uses: MrLouisWright/thirdparty-pulse@v1
with:
path: dist
fail-on: high
sarif: thirdparty-pulse.sarif
- uses: github/codeql-action/upload-sarif@v3
if: always()
with: { sarif_file: thirdparty-pulse.sarif }
| Flag | Description |
|---|---|
| Site | |
--base-url <url> | Public URL of the site, e.g. https://example.com (default: inferred from canonical/og:url) |
--include <globs> | Only check these pages (file or URL path globs, comma-separated) |
--exclude <globs> | Skip these pages |
--clean-urls | Pages are served without .html (about.html -> /about) |
| Budgets | |
--max-kb <n> | Third-party script KB per page (default 150) |
--max-requests <n> | Third-party requests per page (default 10) |
--max-main-thread <ms> | Estimated third-party main-thread ms per page (default 250) |
| Common | |
-c, --config <file> | JSON config (default: .thirdparty-pulse.json or "thirdpartyPulse" in package.json) |
-f, --format <fmt> | text (default) | json | sarif | markdown |
--json <file> | Also write a JSON report |
--sarif <file> | Also write SARIF 2.1.0 |
--markdown <file> | Also write Markdown (e.g. for a job summary) |
--fail-on <sev> | critical | high | medium | low | info | none (default: high) |
--list-rules | Print rules and default severities |
--no-fixes | Hide fix suggestions in text output |
--no-color | Disable colours |
Full reference, config file format and API in the README.
Support this project
I build free, open-source tools that help developers ship safer websites. Every coffee helps keep them maintained, documented and free for everyone.
Buy me a coffee (opens Stripe in a new tab)Secure checkout by Stripe