{"_id":"@codeleap/perf","_rev":"6-c013d0de1f8118fdea2b68ad4086e1b2","name":"@codeleap/perf","dist-tags":{"latest":"7.7.1"},"versions":{"7.6.2":{"name":"@codeleap/perf","version":"7.6.2","_id":"@codeleap/perf@7.6.2","maintainers":[{"name":"psouza10","email":"paulosouza300272@gmail.com"},{"name":"codeleap-dev","email":"dev@codeleap.co.uk"}],"bin":{"codeleap-perf":"dist/cli.js"},"dist":{"shasum":"0c956abfd359349e76d6a3f73330ca2931eeefd9","tarball":"https://registry.npmjs.org/@codeleap/perf/-/perf-7.6.2.tgz","fileCount":47,"integrity":"sha512-l6Zdwlnk29eHwM/AmyUeNymrADbm8qksAPTxvR4wkIoSteLoHgBHBKWXGORmwbwWnM+HqnkDRs47UiB+tvVEUA==","signatures":[{"sig":"MEQCIHWkOJ0jIDd77A7FPjKhwyAHaolVkrkKkEtRWsd0PBAEAiAxIm2BtBaAPBFKqD7g8UnXhLqpoKnvqUGWvA0PxTtMrw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":118588},"main":"dist/index.js","types":"./dist/index.d.ts","exports":{".":{"bun":"./src/index.ts","types":"./dist/index.d.ts","import":"./dist/index.js","source":"./src/index.ts","default":"./dist/index.js"}},"gitHead":"c221a706d45f3c24287f7c1f5cd80e647e50429f","scripts":{"test":"bun test","build":"tsc --build tsconfig.build.json"},"_npmUser":{"name":"codeleap-dev","email":"dev@codeleap.co.uk"},"_npmVersion":"10.9.3","description":"Performance analysis tooling for CodeLeap template apps — boot-graph, imports, chunks, measure.","directories":{},"_nodeVersion":"22.19.0","dependencies":{"ws":"^6.2.3","lighthouse":"^13.4.0","chrome-launcher":"^0.15.2"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/perf_7.6.2_1786639662219_0.3953988103960362","host":"s3://npm-registry-packages-npm-production"}},"7.6.3":{"name":"@codeleap/perf","version":"7.6.3","_id":"@codeleap/perf@7.6.3","maintainers":[{"name":"psouza10","email":"paulosouza300272@gmail.com"},{"name":"codeleap-dev","email":"dev@codeleap.co.uk"}],"bin":{"codeleap-perf":"dist/cli.js"},"dist":{"shasum":"1bfbc98f81fca1771d31d390260142fbdbf3bf98","tarball":"https://registry.npmjs.org/@codeleap/perf/-/perf-7.6.3.tgz","fileCount":47,"integrity":"sha512-X4cnuFmg6m6h3VdfTDDIBTdR/2014msVlG7oINAbEpinnv5/on/zc+dDm+bVeB7MLM2HDXnbPcO+brOjlVAmjw==","signatures":[{"sig":"MEQCIBAAKY/oV09aiDrFn8jEj4zyAlCV/XeDqoPoTa2Ly/VvAiAsFwY9r1h6y+cJAUh06oS4Q0KpWPMUUunRPLQyDyq14A==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":118588},"main":"dist/index.js","_from":"file:codeleap-perf-7.6.3.tgz","types":"./dist/index.d.ts","exports":{".":{"bun":"./src/index.ts","types":"./dist/index.d.ts","import":"./dist/index.js","source":"./src/index.ts","default":"./dist/index.js"}},"scripts":{"test":"bun test","build":"tsc --build tsconfig.build.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:6eafe928-c8ad-4c40-a4fc-4877109a3947"}},"_resolved":"/home/runner/work/internal-libs-monorepo/internal-libs-monorepo/packages/perf/codeleap-perf-7.6.3.tgz","_integrity":"sha512-X4cnuFmg6m6h3VdfTDDIBTdR/2014msVlG7oINAbEpinnv5/on/zc+dDm+bVeB7MLM2HDXnbPcO+brOjlVAmjw==","_npmVersion":"11.16.0","description":"Performance analysis tooling for CodeLeap template apps — boot-graph, imports, chunks, measure.","directories":{},"_nodeVersion":"24.18.0","dependencies":{"ws":"^6.2.3","lighthouse":"^13.4.0","chrome-launcher":"^0.15.2"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/perf_7.6.3_1786640504328_0.1773221176499471","host":"s3://npm-registry-packages-npm-production"}},"7.6.4":{"name":"@codeleap/perf","version":"7.6.4","_id":"@codeleap/perf@7.6.4","maintainers":[{"name":"psouza10","email":"paulosouza300272@gmail.com"},{"name":"codeleap-dev","email":"dev@codeleap.co.uk"}],"bin":{"codeleap-perf":"dist/cli.js"},"dist":{"shasum":"be6b0add04377ad55042b8c3776fdf579c1dcb05","tarball":"https://registry.npmjs.org/@codeleap/perf/-/perf-7.6.4.tgz","fileCount":47,"integrity":"sha512-YlcOa/uhSnjXlypg0qTr93JksTjmtcCOtmGndK/lsN7JVEnnnTDDQd86Xa2qHz8YPSjsinV+OJYTUiuawGwjnQ==","signatures":[{"sig":"MEYCIQDaH8C31NaEjB4i1ZU95RaiAPJBm8aZYEVXEKJmNQ9mCAIhAL4BNZGXvTev546Z1DtkOH9REMc9eIXgq6dpv7AmMHNs","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":118588},"main":"dist/index.js","_from":"file:codeleap-perf-7.6.4.tgz","types":"./dist/index.d.ts","exports":{".":{"bun":"./src/index.ts","types":"./dist/index.d.ts","import":"./dist/index.js","source":"./src/index.ts","default":"./dist/index.js"}},"scripts":{"test":"bun test","build":"tsc --build tsconfig.build.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:6eafe928-c8ad-4c40-a4fc-4877109a3947"}},"_resolved":"/home/runner/work/internal-libs-monorepo/internal-libs-monorepo/packages/perf/codeleap-perf-7.6.4.tgz","_integrity":"sha512-YlcOa/uhSnjXlypg0qTr93JksTjmtcCOtmGndK/lsN7JVEnnnTDDQd86Xa2qHz8YPSjsinV+OJYTUiuawGwjnQ==","_npmVersion":"11.17.0","description":"Performance analysis tooling for CodeLeap template apps — boot-graph, imports, chunks, measure.","directories":{},"_nodeVersion":"24.19.0","dependencies":{"ws":"^6.2.3","lighthouse":"^13.4.0","chrome-launcher":"^0.15.2"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/perf_7.6.4_1786645522540_0.0527851097132781","host":"s3://npm-registry-packages-npm-production"}},"7.6.5":{"name":"@codeleap/perf","version":"7.6.5","_id":"@codeleap/perf@7.6.5","maintainers":[{"name":"psouza10","email":"paulosouza300272@gmail.com"},{"name":"codeleap-dev","email":"dev@codeleap.co.uk"}],"bin":{"codeleap-perf":"dist/cli.js"},"dist":{"shasum":"3095f17d3413a2a50240a5cd080c09a1d197fca7","tarball":"https://registry.npmjs.org/@codeleap/perf/-/perf-7.6.5.tgz","fileCount":47,"integrity":"sha512-8FtBK/3b3wonk8qb5A3/DemPnXrRDqutUQMAb+tTU7dBiubYxQYKYeCE0aaFIzOqRdvf/d7naKCc+OW0Wawd0A==","signatures":[{"sig":"MEQCIEDTht+TS951r2P6afKvy8N+h6RPQC/KMaUd9AAX8douAiADqY+Bc0KNVUN3zvc2TM1syf9MhQGCOjJP3lAWF1o7FQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":118588},"main":"dist/index.js","_from":"file:codeleap-perf-7.6.5.tgz","types":"./dist/index.d.ts","exports":{".":{"bun":"./src/index.ts","types":"./dist/index.d.ts","import":"./dist/index.js","source":"./src/index.ts","default":"./dist/index.js"}},"scripts":{"test":"bun test","build":"tsc --build tsconfig.build.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:6eafe928-c8ad-4c40-a4fc-4877109a3947"}},"_resolved":"/home/runner/work/internal-libs-monorepo/internal-libs-monorepo/packages/perf/codeleap-perf-7.6.5.tgz","_integrity":"sha512-8FtBK/3b3wonk8qb5A3/DemPnXrRDqutUQMAb+tTU7dBiubYxQYKYeCE0aaFIzOqRdvf/d7naKCc+OW0Wawd0A==","_npmVersion":"11.17.0","description":"Performance analysis tooling for CodeLeap template apps — boot-graph, imports, chunks, measure.","directories":{},"_nodeVersion":"24.19.0","dependencies":{"ws":"^6.2.3","lighthouse":"^13.4.0","chrome-launcher":"^0.15.2"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/perf_7.6.5_1786675001839_0.13434888813916968","host":"s3://npm-registry-packages-npm-production"}},"7.7.0":{"name":"@codeleap/perf","version":"7.7.0","_id":"@codeleap/perf@7.7.0","maintainers":[{"name":"psouza10","email":"paulosouza300272@gmail.com"},{"name":"codeleap-dev","email":"dev@codeleap.co.uk"}],"bin":{"codeleap-perf":"dist/cli.js"},"dist":{"shasum":"dafd15800f5c2b78db105b7e2da30da3c440297f","tarball":"https://registry.npmjs.org/@codeleap/perf/-/perf-7.7.0.tgz","fileCount":47,"integrity":"sha512-1vb9YqaLcCvp30RvhQY3uO8PMXJ+V3YKr8Eej9BAMTEw+2ZEZu+uyuAu16QXCTS+JM+mYx326o8s26ZyTSsAlQ==","signatures":[{"sig":"MEQCIG5VFHlajze3aMmGXN1/kUlGk1ICdUZRs4tKhEh+AClTAiAG8y9sYq1dNNT+UtD2HtBJCGuZTxU/6JsElO6FN+LQ7w==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEYCIQCgWQdiwihrwBRGZ0rnFmnCILY1HRjMbEwDeTMc0aUVlgIhAJ+tuq1YBG3ibCG6Hgs4krkscs2O+DmCK0k2GXHYP9RP","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":118588},"main":"dist/index.js","_from":"file:codeleap-perf-7.7.0.tgz","types":"./dist/index.d.ts","exports":{".":{"bun":"./src/index.ts","types":"./dist/index.d.ts","import":"./dist/index.js","source":"./src/index.ts","default":"./dist/index.js"}},"scripts":{"test":"bun test","build":"tsc --build tsconfig.build.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:6eafe928-c8ad-4c40-a4fc-4877109a3947"}},"_resolved":"/home/runner/work/internal-libs-monorepo/internal-libs-monorepo/packages/perf/codeleap-perf-7.7.0.tgz","_integrity":"sha512-1vb9YqaLcCvp30RvhQY3uO8PMXJ+V3YKr8Eej9BAMTEw+2ZEZu+uyuAu16QXCTS+JM+mYx326o8s26ZyTSsAlQ==","_npmVersion":"11.17.0","description":"Performance analysis tooling for CodeLeap template apps — boot-graph, imports, chunks, measure.","directories":{},"_nodeVersion":"24.19.0","dependencies":{"ws":"^6.2.3","lighthouse":"^13.4.0","chrome-launcher":"^0.15.2"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/perf_7.7.0_1788215150221_0.806983305487277","host":"s3://npm-registry-packages-npm-production"}},"7.7.1":{"_id":"@codeleap/perf@7.7.1","bin":{"codeleap-perf":"dist/cli.js"},"dist":{"shasum":"3f8294e7b403f68e1d10676e2ff313bdb1d11a39","tarball":"https://registry.npmjs.org/@codeleap/perf/-/perf-7.7.1.tgz","fileCount":47,"integrity":"sha512-pGqSBJbXjzzyNjy61UO3hltlI0J1xV8SUPyiHolR7TN8B6/CJ4pxxULpsJYSsz+h4+il1gfEqLeZEF4/jvLN5A==","signatures":[{"sig":"MEUCIQCQg1YnNNX+bGjCeRPRHFNfX/b//7HEQ2weqQXSY+trDAIgVq/AdNHgTQcJ6u+OAX6tlr0UhEiM2/08Jx3wigmRyt8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDQ7nN06QFuwKE41a0qOy7pDoXGpDORfqBJcjbVlnsixQIhAKC7XZwP3Oxs8olpai7lItZmAzvZUdf3i3TbUx6amafz"}],"unpackedSize":118588},"main":"dist/index.js","name":"@codeleap/perf","_from":"file:codeleap-perf-7.7.1.tgz","types":"./dist/index.d.ts","exports":{".":{"bun":"./src/index.ts","types":"./dist/index.d.ts","import":"./dist/index.js","source":"./src/index.ts","default":"./dist/index.js"}},"scripts":{"test":"bun test","build":"tsc --build tsconfig.build.json"},"version":"7.7.1","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:6eafe928-c8ad-4c40-a4fc-4877109a3947"}},"_resolved":"/home/runner/work/internal-libs-monorepo/internal-libs-monorepo/packages/perf/codeleap-perf-7.7.1.tgz","_integrity":"sha512-pGqSBJbXjzzyNjy61UO3hltlI0J1xV8SUPyiHolR7TN8B6/CJ4pxxULpsJYSsz+h4+il1gfEqLeZEF4/jvLN5A==","_npmVersion":"11.19.0","description":"Performance analysis tooling for CodeLeap template apps — boot-graph, imports, chunks, measure.","directories":{},"maintainers":[{"name":"psouza10","email":"paulosouza300272@gmail.com"},{"name":"codeleap-dev","email":"dev@codeleap.co.uk"}],"_nodeVersion":"24.20.0","dependencies":{"ws":"^6.2.3","lighthouse":"^13.4.0","chrome-launcher":"^0.15.2"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/perf_7.7.1_1789585167030_0.6454742720714146"}}},"time":{"created":"2026-08-13T16:47:41.977Z","modified":"2026-09-16T18:59:27.306Z","7.6.2":"2026-08-13T16:47:42.352Z","7.6.3":"2026-08-13T17:01:44.515Z","7.6.4":"2026-08-13T18:25:22.703Z","7.6.5":"2026-08-14T02:36:42.044Z","7.7.0":"2026-08-31T22:25:50.327Z","7.7.1":"2026-09-16T18:59:27.116Z"},"description":"Performance analysis tooling for CodeLeap template apps — boot-graph, imports, chunks, measure.","maintainers":[{"name":"psouza10","email":"paulosouza300272@gmail.com"},{"name":"codeleap-dev","email":"dev@codeleap.co.uk"}],"readme":"# @codeleap/perf\n\nCatch web performance regressions **before they ship** — and measure TBT/LCP/FCP reliably when\nyou do. A build/dev-time CLI for CodeLeap template apps that analyzes the boot bundle, traces bad\nimports, runs Lighthouse, and gives you a single **PASS/FAIL verdict** — designed to gate CI, though\nnot currently wired into this repo's CI pipeline; today it's run manually (`bun run perf`).\n\n```bash\nbun run perf        # = codeleap-perf analyze — the one command you run\n```\n\n---\n\n## Why\n\nBoot performance is **easy to break and hard to see**. A single stray static import of a heavy SDK\nsilently lands its whole module on the boot bundle:\n\n- `import * as Sentry from '@sentry/nextjs'` on a boot-reachable file → real boot weight, even if\n  you only call `init()` later (exact KB varies by SDK version — deferring the *call* doesn't\n  defer the *parse*).\n- A root-barrel import (`@/components`) drags **Zod / react-aria** onto the homepage.\n- Replacing a `dynamic import()` with a static one removes a code-split boundary → the whole graph\n  behind it lands on boot instead.\n\nNone of these throw an error. You find out from users, or a Lighthouse score that crept down. And\nwhen you *do* measure, it's easy to draw the wrong conclusion: machine load swings TBT by 2× between\nruns, and grepping a chunk for `\"@sentry/\"` false-positives on lazy-import call sites.\n\n`@codeleap/perf` exists to make this **fast, repeatable, and honest**:\n\n| You want to… | Command | Needs a server? |\n|---|---|---|\n| Know if a heavy lib is on boot (seconds) | `boot-graph` | no |\n| See which third-party packages the boot graph pulls | `imports` | no |\n| See chunk sizes + duplication of configured heavy libs across chunks | `chunks` | no |\n| Get real TBT/LCP/FCP (by **floor**, not noisy median) | `measure` | yes |\n| Explore boot CPU self-time by library (secondary signal — see below) | `cpu` | yes |\n| Run everything + a PASS/FAIL verdict for CI | `analyze` | optional |\n\n`chunks`'s duplication check runs on `config.markers` — the same list `boot-graph` uses, but reports\nany marker found in ≥2 boot chunks as a bundler dedup failure. Two things to know before adding a\nmarker purely for duplication tracking:\n- It only catches what you thought to name in advance.\n- The same list feeds `boot-graph`, which treats **any** single occurrence as a hit. A marker added\n  only for duplication will also make `boot-graph` report a false FAIL for the first (correct)\n  occurrence — treat that as expected noise, not a regression.\n\n`cpu` is real (it drives an actual CDP profile and fingerprints chunks by source) but it's a\nsecondary, exploratory signal, not a decision-making one — cpu self-time ≠ TBT, since a function can\ncost real self-time while running entirely outside the >50ms blocking window. Use it to compare two\nbuilds' *composition*; use `measure`'s floor (the subtract method) to confirm any actual TBT impact.\nSee [Measuring Correctly](https://docs.codeleap.co.uk/docs/perf/measuring) §Attribution gotchas.\n\nIt bakes in the methodology that keeps the numbers trustworthy — SDK-internal markers (not\npackage-name substrings), comparison by floor, recorded machine load — documented in full in\n[Measuring Correctly](https://docs.codeleap.co.uk/docs/perf/measuring).\n\n---\n\n## Install\n\nIn this monorepo it's a workspace package — already a devDependency of `apps/web`. In a forked\nproduct app:\n\n```bash\nbun add -d @codeleap/perf\n```\n\nThen add the script (already present in the template):\n\n```json\n{ \"scripts\": { \"perf\": \"codeleap-perf analyze\" } }\n```\n\nThe `codeleap-perf` bin resolves from the installed package — it works in the monorepo and in a\nforked app, with no relative `../../packages` paths.\n\n---\n\n## Quick start\n\n```bash\n# 1. build the app (the analyzers read .next/)\nbun run build\n\n# 2a. static-only — fast, no server, great for a pre-commit / quick check\nbun run perf -- --no-server          # or: codeleap-perf analyze --no-server\n\n# 2b. full — start a prod server first, then measure too\nbun run start &                      # serves :3000\nbun run perf                         # static + Lighthouse floors + verdict\n```\n\n`analyze` runs the static checks always, adds `measure` (and `--cpu` if asked) **when a server is\nreachable** at `config.serverUrl`, and **exits 1 on FAIL** so CI can gate on it.\n\n---\n\n## Commands\n\n```\ncodeleap-perf boot-graph                  # heavy libraries on the boot bundle (expect CLEAN) — static\ncodeleap-perf imports                     # third-party packages on the static boot import graph — static\ncodeleap-perf chunks                      # per-chunk sizes + cross-chunk duplication — static\ncodeleap-perf measure [runs] [--headless] # TBT/LCP/FCP, floor of N runs (default 6) — needs server; headed by default\ncodeleap-perf cpu                         # boot CPU self-time by library — needs server\ncodeleap-perf analyze [options]           # the lot + a verdict; exits 1 on FAIL\n    --no-server      skip measure/cpu even if a server is up (pure static run)\n    --cpu            also run the CPU profile (slower, fuzzier than measure)\n    --headless       drive the internal measure call headless (default is headed)\n    --runs N         Lighthouse runs per route for measure (default config.measureRuns)\n    --json [file]    write the full report to disk (default perf-report.json)\n```\n\n`measure` launches a real, visible Chrome window by default — headless LCP has been observed to read\nartificially low against the same build measured headed, so trust the headed number. Pass `--headless`\nonly when no display is available (e.g. a CI runner without Xvfb); never mix the two modes within\none comparison.\n\n### Reading the output\n\n```\n═══ perf report · 2026-06-20T15:05:45Z · load 1.98/2.22/2.28 ═══\nboot-graph  ✓ CLEAN — 19 chunks, 1463KB\nchunks      1463KB total, 19 chunks, largest 233KB\nimports     21 third-party on boot (72 files)\nmeasure /   score 97 · TBT 135⚠ · LCP 2262⚠ · FCP 1062 · CLS 0.003  (floor of 6)\n─── VERDICT: ✗ FAIL (2 issue(s)) ───\n  ⚠ /: TBT floor 135 > 100\n  ⚠ /: LCP floor 2262 > 2200\n```\n\n- The report header records **machine load** — a number without its conditions is meaningless\n  (see [Measuring Correctly](https://docs.codeleap.co.uk/docs/perf/measuring) §Avoiding context loss). Compare runs at similar load.\n- `⚠` marks a metric over its configured threshold; the **verdict** fails on any boot-graph hit or\n  over-threshold metric.\n- `--json` writes the same data structured — your baseline anchor on disk.\n\n---\n\n## Config\n\nDefaults target a Next.js **pages-router** app and need no config. To override, drop\n`codeleap-perf.config.json` at the app root:\n\n```jsonc\n{\n  \"buildDir\": \".next\",\n  \"srcDir\": \"src\",\n  \"bootEntry\": [\"src/pages/_app.tsx\", \"src/pages/_document.tsx\"],\n  \"aliases\": { \"@/\": \"src/\" },\n  \"routes\": [\"/\", \"/auth\"],\n  \"serverUrl\": \"http://localhost:3000\",\n  \"measureRuns\": 6,\n  \"cpuSettleMs\": 4000,\n  \"thresholds\": { \"tbt\": 100, \"lcp\": 2200, \"fcp\": 1100 },\n  \"markers\": [\n    { \"name\": \"Zod\", \"pattern\": \"safeParse|ZodError\" },\n    { \"name\": \"Sentry\", \"pattern\": \"BrowserClient|makeFetchTransport\" }\n  ],\n  \"sigs\": [\n    { \"name\": \"react-aria\", \"pattern\": \"react-aria|useFocusRing|VisuallyHidden\" },\n    { \"name\": \"Sentry\", \"pattern\": \"sentry\", \"flags\": \"gi\" }\n  ]\n}\n```\n\nYou only need the fields you're overriding — anything omitted keeps its default. **`markers` and\n`sigs` replace the whole default list rather than merging** — copy the defaults from `DEFAULT_CONFIG`\nand extend them if you want to keep the built-in entries.\n\n> **`markers` must use SDK-internal symbol regexes, never package-name substrings.** Package names\n> appear in lazy `import('...')` call-site strings and false-positive. This is the single most\n> common way to misread the boot graph — see [Measuring Correctly](https://docs.codeleap.co.uk/docs/perf/measuring) §Attribution gotchas.\n\n`markers` feeds both `boot-graph` (any hit anywhere is a fail) and `chunks` (a hit in ≥2 chunks is\nduplication). `sigs` is only used by `cpu` — it controls how the CPU profiler labels each chunk's\nsource (library attribution, not leak detection); `pattern` is a RegExp source string, `flags`\ndefaults to `'g'`.\n\n---\n\n## Docs\n\n- **[Measuring Correctly](https://docs.codeleap.co.uk/docs/perf/measuring)** — the metrics, the gotchas, and the discipline that keeps a perf number trustworthy. Read before trusting any conclusion.\n- **[Improving Boot Performance](https://docs.codeleap.co.uk/docs/perf/improving)** — change → why → verify playbook for TBT/LCP on a React/Next.js project.\n- **[Command & Config Reference](https://docs.codeleap.co.uk/docs/perf/reference)** — full command reference, every config field, and the programmatic API.\n- **`apps/web/docs/PERFORMANCE.md`** — the boot-path invariants in apps/web that this tool protects.\n\n---\n\n## Programmatic API\n\nEvery analyzer is exported for use in scripts or CI beyond the CLI:\n\n```ts\nimport {\n  analyze, bootGraph, traceImports, analyzeChunks, measure, cpuProfile,\n  loadConfig, DEFAULT_CONFIG,\n} from '@codeleap/perf'\n\nconst config = loadConfig()\nconst report = await analyze(config, { cpu: true })\nif (!report.verdict.pass) {\n  console.error(report.verdict.issues.join('\\n'))\n  process.exit(1)\n}\n```\n\n| Function | Returns |\n|---|---|\n| `loadConfig(cwd?)` | Merged config — `codeleap-perf.config.json` over `DEFAULT_CONFIG`, or defaults if no file. |\n| `bootGraph(config)` | `{ bootChunks, totalBootKb, hits, clean }` — static |\n| `traceImports(config)` | `{ files, thirdParty: { pkg, files }[] }` — static |\n| `analyzeChunks(config)` | `{ totalKb, count, chunks, duplicated }` — static |\n| `measure(config, runs?, opts?)` | `Promise<RouteMeasure[]>` — one entry per route with `tbt/lcp/fcp` floor+median |\n| `cpuProfile(config)` | `Promise<CpuResult>` — `{ scriptedMs, idleMs, byLibrary, topChunks }` |\n| `analyze(config, opts?)` | `Promise<AnalyzeReport>` — all of the above plus `verdict: { pass, issues }` |\n\n`opts` for `analyze`: `{ noServer?, cpu?, runs?, headed? }` — `headed` defaults to `true`.  \n`opts` for `measure`: `{ headed? }` — same default.\n\n`bootGraph`, `traceImports`, and `analyzeChunks` are synchronous (read only the build output, no server). `measure`, `cpuProfile`, and `analyze` are async; `analyze` never throws for \"no server\" — it sets `serverUp: false` and leaves `measure`/`cpu` as `null`.\n\n---\n\n## How it works\n\nThe static commands read Next's `build-manifest.json` to find the `/_app` + `routes[0]` chunks (the\n\"boot bundle\" — pages router only), then scan them for marker regexes (`boot-graph`), walk the\nstatic `import` graph from the boot entries (`imports`), and size the chunks (`chunks`) — no app\ncode runs. `measure` drives Lighthouse via its Node API + `chrome-launcher` (no `npx`/PATH\ndependency) using mobile form factor + Slow-4G + 4× CPU throttle, launches Chrome once, and reuses\nit across all N runs and routes (Lighthouse resets cache/storage per run). `cpu` drives headless\nChrome over CDP, profiles boot at 4× throttle, and attributes self-time per chunk via source\nfingerprinting against `config.sigs` (no source maps required). `analyze` composes them and renders\na verdict.\n\n---\n\n## Tests\n\n```bash\nbun test\n```\n\nThe static analyzers (`boot-graph`, `imports`, `chunks`), `loadConfig`, and `analyze`'s verdict are\nunit-tested against fixtures in `test/fixtures` (a fake `.next` build + a fake source tree) — fast\nand deterministic, no server needed. `measure` and `cpu` are I/O-bound (Lighthouse / headless Chrome)\nand validated by running them against a live build.\n\n---\n\n## Status\n\nPhases 1–4 complete: static analyzers, measurement, unified `analyze` + JSON + CI exit code, and\ndocs. Possible follow-ups: an HTML report from the JSON, App Router support.\n","readmeFilename":"README.md"}