{"_id":"webperf-snippets","_rev":"2-0328c430c69cea9befb643dce003fd65","name":"webperf-snippets","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"webperf-snippets","version":"0.1.0","keywords":["webperf","performance","core-web-vitals","lcp","cls","inp","playwright","cli","ci"],"author":{"name":"Joan Leon | @nucliweb"},"license":"MIT","_id":"webperf-snippets@0.1.0","maintainers":[{"name":"nucliweb","email":"joan.leon@gmail.com"}],"homepage":"https://webperf-snippets.nucliweb.net","bugs":{"url":"https://github.com/nucliweb/webperf-snippets/issues"},"bin":{"webperf-snippets":"src/bin.js"},"dist":{"shasum":"bf0b3d9b0ebd2bd102233df23ffcb1f776ba5bcc","tarball":"https://registry.npmjs.org/webperf-snippets/-/webperf-snippets-0.1.0.tgz","fileCount":67,"integrity":"sha512-y+MtsoaIDtdP98WeY7tMXY4b/HwSTMdlq1iUKQR5xQhbIUoUJ5mdZEHepucFCa+zcPOt1dkoekOgysPm4NEdGA==","signatures":[{"sig":"MEUCIGkLxvHeQCvNckJ51BKUqFrjKyQ70NfQsnAjbYLIhiFNAiEA3CYXRwVsYp8WuPsIGuCHfe9VnFr3RqRJtnN12E0k/eE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":568919},"type":"module","engines":{"node":">=20.12.0"},"gitHead":"2371ecf008c0beae8319d4302725e5b89512ee78","scripts":{"test":"vitest run","test:e2e":"vitest run tests/e2e","test:unit":"vitest run tests/unit","test:watch":"vitest"},"_npmUser":{"name":"nucliweb","email":"joan.leon@gmail.com"},"repository":{"url":"git+https://github.com/nucliweb/webperf-snippets.git","type":"git","directory":"cli"},"_npmVersion":"10.9.7","description":"Run curated WebPerf Snippets headlessly via Playwright. Diagnose Core Web Vitals beyond what Lighthouse exposes.","directories":{},"_nodeVersion":"22.22.2","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^2.0.0"},"peerDependencies":{"playwright":">=1.40.0"},"peerDependenciesMeta":{"playwright":{"optional":false}},"_npmOperationalInternal":{"tmp":"tmp/webperf-snippets_0.1.0_1777919910371_0.15993748681540465","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"webperf-snippets","version":"0.2.0","description":"Run curated WebPerf Snippets headlessly via Playwright. Diagnose Core Web Vitals beyond what Lighthouse exposes.","type":"module","bin":{"webperf-snippets":"src/bin.js"},"scripts":{"test":"vitest run","test:watch":"vitest","test:unit":"vitest run tests/unit","test:e2e":"vitest run tests/e2e"},"devDependencies":{"vitest":"^2.0.0"},"engines":{"node":">=20.12.0"},"keywords":["webperf","performance","core-web-vitals","lcp","cls","inp","playwright","cli","ci"],"author":{"name":"Joan Leon | @nucliweb"},"license":"MIT","publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/nucliweb/webperf-snippets.git","directory":"cli"},"homepage":"https://webperf-snippets.nucliweb.net","peerDependencies":{"playwright":">=1.40.0"},"peerDependenciesMeta":{"playwright":{"optional":false}},"_id":"webperf-snippets@0.2.0","gitHead":"f4e31a4691134bbd72f4eecab983110d9a39fb50","bugs":{"url":"https://github.com/nucliweb/webperf-snippets/issues"},"_nodeVersion":"22.22.2","_npmVersion":"10.9.7","dist":{"integrity":"sha512-3Tw1fjKrOab6QfLuQEFJLaecZSHBll7oE4ExHidcbmnRqw8oNwLXdKccRyU6v35KHf3P0xIFusBZUN6bnT+ALQ==","shasum":"e42c11c28a35e81fb499f2b5ee0727f3295fd1da","tarball":"https://registry.npmjs.org/webperf-snippets/-/webperf-snippets-0.2.0.tgz","fileCount":70,"unpackedSize":577245,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDFOaoQRpZ0LszOlvcziCPZYopuHdXFEWs5vrHHQk1rdQIhAKM03Rs4Ou1o2LB0GIw1uLFugi/i4K4TTlD6CJXtArZc"}]},"_npmUser":{"name":"nucliweb","email":"joan.leon@gmail.com"},"directories":{},"maintainers":[{"name":"nucliweb","email":"joan.leon@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/webperf-snippets_0.2.0_1778082346117_0.5299969127406412"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-04T18:38:30.265Z","modified":"2026-05-06T15:45:46.429Z","0.1.0":"2026-05-04T18:38:30.542Z","0.2.0":"2026-05-06T15:45:46.314Z"},"bugs":{"url":"https://github.com/nucliweb/webperf-snippets/issues"},"author":{"name":"Joan Leon | @nucliweb"},"license":"MIT","homepage":"https://webperf-snippets.nucliweb.net","keywords":["webperf","performance","core-web-vitals","lcp","cls","inp","playwright","cli","ci"],"repository":{"type":"git","url":"git+https://github.com/nucliweb/webperf-snippets.git","directory":"cli"},"description":"Run curated WebPerf Snippets headlessly via Playwright. Diagnose Core Web Vitals beyond what Lighthouse exposes.","maintainers":[{"name":"nucliweb","email":"joan.leon@gmail.com"}],"readme":"# webperf-snippets CLI\n\nRun curated [WebPerf Snippets](https://webperf-snippets.nucliweb.net) headlessly via Playwright. Diagnose Core Web Vitals beyond what Lighthouse exposes and gate CI on real performance budgets.\n\n<img width=\"1820\" height=\"1442\" alt=\"webperf-snippets-CLI\" src=\"https://github.com/user-attachments/assets/af7e6b02-8877-407e-87a4-db063468b5fb\" />\n\n\n> **Status:** v0.2. Core Web Vitals, loading audit, and structural checks. See [Roadmap](#roadmap) for what's next.\n\n## Why\n\nLighthouse gives you a score. The DevTools snippets give you the *diagnosis* — TTFB / Resource Load Delay / Element Render Delay sub-parts, LoAF script attribution, render-blocking resources, etc. This CLI runs the same curated snippets in a headless browser so you can:\n\n- Diagnose LCP regressions in CI without copy-pasting into DevTools.\n- Gate pull requests on real performance budgets.\n- Automate the snippets you already run by hand.\n\n## Install\n\nPlaywright is a peer dependency. Install both, plus the chromium browser:\n\n```bash\nnpm install --save-dev webperf-snippets playwright\nnpx playwright install chromium\n```\n\n## Usage\n\n```bash\nnpx webperf-snippets <url> [options]\n```\n\n### Examples\n\nRun the default Core Web Vitals workflow (LCP + CLS, plus LCP-Subparts if LCP > 2.5s):\n\n```bash\nnpx webperf-snippets https://web.dev\n```\n\nLoading audit (TTFB, FCP, render-blocking, scripts, fonts):\n\n```bash\nnpx webperf-snippets https://web.dev --workflow loading\n```\n\nStructural checks for CI (render-blocking, fonts, priority hints, resource hints):\n\n```bash\nnpx webperf-snippets https://web.dev --workflow audit\n```\n\nMarkdown output for PR comments:\n\n```bash\nnpx webperf-snippets https://web.dev --markdown\n```\n\nJSON output (for piping into `jq` or CI):\n\n```bash\nnpx webperf-snippets https://web.dev --json\n```\n\nSingle snippet:\n\n```bash\nnpx webperf-snippets https://web.dev --snippet LCP-Subparts\n```\n\nSynthetic INP measurement with an interaction script:\n\n```bash\nnpx webperf-snippets https://web.dev --snippet INP --interact-script interactions.json\n```\n\nCI gating:\n\n```bash\nnpx webperf-snippets https://web.dev --budget-lcp 2500 --budget-cls 0.1\n```\n\n### Options\n\n| Option                       | Description                                                            |\n| ---------------------------- | ---------------------------------------------------------------------- |\n| `--workflow <name>`          | Workflow to run. Default: `core-web-vitals`. Options: `core-web-vitals`, `loading`, `audit`. |\n| `--snippet <name>`           | Run a single snippet by alias or `Category/Name` path.                 |\n| `--json`                     | Output JSON instead of formatted text.                                 |\n| `--markdown`                 | Output GitHub-renderable markdown (for PR comments).                   |\n| `--viewport <preset>`        | Viewport preset: `mobile` (default), `tablet`, `desktop`.             |\n| `--wait <ms>`                | Post-load wait before evaluating snippets. Default: `3000`.            |\n| `--interact-script <path>`   | JSON file with interactions to run before evaluation (for INP).        |\n| `--budget-lcp <ms>`          | Exit `1` if LCP exceeds this value.                                    |\n| `--budget-cls <score>`       | Exit `1` if CLS exceeds this value.                                    |\n| `--verbose`                  | Show all items, including passing checks.                              |\n| `--headed`                   | Show the browser window (debug).                                       |\n| `-h, --help`                 | Show help.                                                             |\n\n### Snippet aliases\n\n| Alias              | Snippet                                        |\n| ------------------ | ---------------------------------------------- |\n| `LCP`              | CoreWebVitals/LCP                              |\n| `CLS`              | CoreWebVitals/CLS                              |\n| `LCP-Subparts`     | CoreWebVitals/LCP-Subparts                     |\n| `fonts`            | Loading/Fonts-Preloaded-Loaded-and-used-above-the-fold |\n| `render-blocking`  | Loading/Find-render-blocking-resources         |\n| `resource-hints`   | Loading/Resource-Hints-Validation              |\n| `preload-scripts`  | Loading/Validate-Preload-Async-Defer-Scripts   |\n| `priority-hints`   | Loading/Priority-Hints-Audit                   |\n| `critical-css`     | Loading/Critical-CSS-Detection                 |\n| `ttfb`             | Loading/TTFB-Sub-Parts                         |\n| `script-parties`   | Loading/First-And-Third-Party-Script-Info      |\n| `script-loading`   | Loading/Script-Loading                         |\n| `lazy-atf`         | Loading/Find-Above-The-Fold-Lazy-Loaded-Images |\n| `lazy-conflict`    | Loading/Find-Images-With-Lazy-and-Fetchpriority |\n| `eager-below-fold` | Loading/Find-non-Lazy-Loaded-Images-outside-of-the-viewport |\n\n### Exit codes\n\n| Code | Meaning                                       |\n| ---- | --------------------------------------------- |\n| `0`  | All checks passed.                            |\n| `1`  | Budget violation, or a snippet errored.       |\n| `2`  | Usage error (missing URL, unknown workflow).  |\n\n## CI example\n\nGitHub Actions, fail the PR if LCP exceeds 2.5s:\n\n```yaml\n- run: |\n    npm install --no-save webperf-snippets playwright\n    npx playwright install --with-deps chromium\n    npx webperf-snippets https://staging.web.dev --budget-lcp 2500 --budget-cls 0.1\n```\n\n## Publishing\n\nThe CLI package is published to npm via a tag-based workflow. Publishing is explicit and intentional — it only happens when a `cli-v*` tag is pushed.\n\n### Release steps\n\n1. Bump the version in `cli/package.json`.\n2. Commit the version change.\n3. Tag and push:\n   ```bash\n   git tag cli-v0.2.0\n   git push origin cli-v0.2.0\n   ```\n4. The `publish-cli` CI job runs, executes the full test suite, and publishes to npm.\n\n### Why tag-based and not path-based\n\nAn alternative is to publish automatically on every push to `main` that touches `cli/`, using a version check to skip republishes. Tag-based publishing was chosen instead because it keeps releases deliberate — a passing CI on `main` does not mean the package is ready to ship, and a tag communicates that intent explicitly.\n\n### Access control\n\nTag protection rules restrict who can push `cli-v*` tags. Configure them under **Settings → Rules → New ruleset** in the repository, targeting the `cli-v*` tag pattern and limiting push access to admins or maintainers. This ensures only authorized collaborators can trigger a publish.\n\n### Required secret\n\nThe `NPM_TOKEN` secret must be set in the repository settings with publish access to the `webperf-snippets` npm package.\n\n## Known limitations\n\n- **CLS in headless is conservative**: layout shifts that only happen on scroll are missed unless you script the scroll.\n- **First navigation only**: each `webperf-snippets` invocation runs one URL. SPAs need the post-route URL passed directly.\n- **Synthetic INP ≠ field INP**: `--interact-script` measures handler latency for a single scripted event. Real INP reflects the worst interaction across all user sessions — use RUM for field data.\n\n## Roadmap\n\n- ~~v0.2: Loading workflow (TTFB, FCP, render-blocking, scripts, fonts), shared page session, synthetic interactions for INP, markdown reporter for PR comments.~~ ✓ Released\n- v0.3: GitHub Action wrapper.\n- v0.4: Auth flows (login + measure logged-in pages), CrUX field-data enrichment.\n\n## How it works\n\n1. Launches headless chromium via Playwright.\n2. Pre-registers `PerformanceObserver`s for LCP and layout-shift before navigation (Chrome doesn't expose these via `getEntriesByType` without a buffered observer, so the runner shims it).\n3. Navigates, waits for the page to settle.\n4. Evaluates each snippet's IIFE in the page context, capturing the returned object.\n5. Applies the workflow's decision tree to enqueue follow-up snippets.\n6. Renders results (human or JSON) and exits with an appropriate code.\n\n## License\n\nMIT — see [LICENSE](../LICENSE).\n","readmeFilename":"README.md"}