{"_id":"120fps","_rev":"13-11c7c39e0216b674971375ee6d2fe09d","name":"120fps","dist-tags":{"latest":"0.7.0"},"versions":{"0.1.0":{"name":"120fps","version":"0.1.0","license":"MIT","_id":"120fps@0.1.0","maintainers":[{"name":"justinschmitz97","email":"justin.schmitz97@gmail.com"}],"homepage":"https://github.com/justinschmitz97/120fps#readme","bugs":{"url":"https://github.com/justinschmitz97/120fps/issues"},"bin":{"120fps":"dist/cli.js"},"dist":{"shasum":"f091b09970d24c441ee07910504351772c338742","tarball":"https://registry.npmjs.org/120fps/-/120fps-0.1.0.tgz","fileCount":31,"integrity":"sha512-yztBOWISq98AWA1sGGnMyvohBUw7ojg7AXOSE0v+fntKA4lvrdHfhag0h3Yxl0+HTwjydWO/wHiIRfrOUXvKww==","signatures":[{"sig":"MEYCIQDD7SqhwEjZOliGYJE1aZGSmtQwXnFgupiNDtjrzVZS1gIhAI+Cw+r/nPZVHV6zuFPGKse5wJ144M/wprsV8TxljqNe","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":201410},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"4fe934a8f2927a11df96679203138798cd244f43","scripts":{"lint":"tsc --noEmit","test":"vitest run","build":"tsc","start":"tsc && node dist/cli.js","test:watch":"vitest","prepublishOnly":"tsc"},"_npmUser":{"name":"justinschmitz97","email":"justin.schmitz97@gmail.com"},"repository":{"url":"git+https://github.com/justinschmitz97/120fps.git","type":"git"},"_npmVersion":"10.9.7","description":"Zero-config UI component performance profiler. Real browser, real metrics.","directories":{},"_nodeVersion":"22.22.2","dependencies":{"vite":"^6.3.0","playwright":"^1.52.0","typescript":"^5.8.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"^19.0.0","vitest":"^3.1.0","react-dom":"^19.0.0","@types/node":"^22.0.0","@types/react":"^19.0.0","@types/react-dom":"^19.0.0"},"_npmOperationalInternal":{"tmp":"tmp/120fps_0.1.0_1777816249992_0.24141634959285896","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"120fps","version":"0.1.1","license":"MIT","_id":"120fps@0.1.1","maintainers":[{"name":"justinschmitz97","email":"justin.schmitz97@gmail.com"}],"homepage":"https://github.com/justinschmitz97/120fps#readme","bugs":{"url":"https://github.com/justinschmitz97/120fps/issues"},"bin":{"120fps":"dist/cli.js"},"dist":{"shasum":"94e2165057857225d0262bccd89284599585326b","tarball":"https://registry.npmjs.org/120fps/-/120fps-0.1.1.tgz","fileCount":31,"integrity":"sha512-s4lQppqE8zw3sEK7crGpinr8tuB2IMficFuEJqt9bL9dSmVl/zpRrdma39Gk+BaT1rYlWw1O3zLlOSM2NsmtMw==","signatures":[{"sig":"MEUCIEQiKkd43fggJOLkNxwjk3L0JnFfa10CeAybs0KR82JcAiEAkUrhbC2gGn0fimIbzQ1F8Fq7OOAJqRbzLmOm4glbpZI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":201398},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"4fe934a8f2927a11df96679203138798cd244f43","scripts":{"lint":"tsc --noEmit","test":"vitest run","build":"tsc","start":"tsc && node dist/cli.js","test:watch":"vitest","prepublishOnly":"tsc"},"_npmUser":{"name":"justinschmitz97","email":"justin.schmitz97@gmail.com"},"repository":{"url":"git+https://github.com/justinschmitz97/120fps.git","type":"git"},"_npmVersion":"10.9.7","description":"Zero-config UI component performance profiler. Real browser, real metrics.","directories":{},"_nodeVersion":"22.22.2","dependencies":{"vite":"^6.3.0","playwright":"^1.52.0","typescript":"^5.8.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"^19.0.0","vitest":"^3.1.0","react-dom":"^19.0.0","@types/node":"^22.0.0","@types/react":"^19.0.0","@types/react-dom":"^19.0.0"},"_npmOperationalInternal":{"tmp":"tmp/120fps_0.1.1_1777818743475_0.5423442150175684","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"120fps","version":"0.1.2","license":"MIT","_id":"120fps@0.1.2","maintainers":[{"name":"justinschmitz97","email":"justin.schmitz97@gmail.com"}],"homepage":"https://github.com/justinschmitz97/120fps#readme","bugs":{"url":"https://github.com/justinschmitz97/120fps/issues"},"bin":{"120fps":"dist/cli.js"},"dist":{"shasum":"20688ce1784c1561bdb373d118e7970089d9cc08","tarball":"https://registry.npmjs.org/120fps/-/120fps-0.1.2.tgz","fileCount":31,"integrity":"sha512-+tYR0BDZTwnBmq6TU7eFhs8m4rW7QnANfRluirR3eCYX14Wk3hEUmS4/RS1p5SUVYxcYXeVgIrHkywmd6XcU5Q==","signatures":[{"sig":"MEQCIC2PqW5FTDq8TzenS64hVbndbSeE5jAZeHPNXfnoXyoVAiBkXjeNB0wsxOvpT3nUu4PzPUmbs88pAHn4UBRL+mnxKg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":202397},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"4fe934a8f2927a11df96679203138798cd244f43","scripts":{"lint":"tsc --noEmit","test":"vitest run","build":"tsc","start":"tsc && node dist/cli.js","test:watch":"vitest","prepublishOnly":"tsc"},"_npmUser":{"name":"justinschmitz97","email":"justin.schmitz97@gmail.com"},"repository":{"url":"git+https://github.com/justinschmitz97/120fps.git","type":"git"},"_npmVersion":"10.9.7","description":"Zero-config UI component performance profiler. Real browser, real metrics.","directories":{},"_nodeVersion":"22.22.2","dependencies":{"vite":"^6.3.0","playwright":"^1.52.0","typescript":"^5.8.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"^19.0.0","vitest":"^3.1.0","react-dom":"^19.0.0","@types/node":"^22.0.0","@types/react":"^19.0.0","@types/react-dom":"^19.0.0"},"_npmOperationalInternal":{"tmp":"tmp/120fps_0.1.2_1777824307584_0.14387248499186267","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"120fps","version":"0.1.3","license":"MIT","_id":"120fps@0.1.3","maintainers":[{"name":"justinschmitz97","email":"justin.schmitz97@gmail.com"}],"homepage":"https://github.com/justinschmitz97/120fps#readme","bugs":{"url":"https://github.com/justinschmitz97/120fps/issues"},"bin":{"120fps":"dist/cli.js"},"dist":{"shasum":"517e9edc975238c92de343c7619cc8df1a1b76a9","tarball":"https://registry.npmjs.org/120fps/-/120fps-0.1.3.tgz","fileCount":31,"integrity":"sha512-UkSfiHF5v4XJ9tkMg3rFcb1fY6swQaZJDYmQEI1Fs9YxkeulOa2D/dxea6i5LyOi3dz3I/0gydHbAJToQ3sYBQ==","signatures":[{"sig":"MEUCIQD+lfEGX72zFKoaW7CfS0j7XFrho7reGSgB+bGZm81ndwIgSUq6epNPgB+6Nhcwkv4XDtevXNuN5mmrNE9bnciQxRI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":205098},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"4fe934a8f2927a11df96679203138798cd244f43","scripts":{"lint":"tsc --noEmit","test":"vitest run","build":"tsc","start":"tsc && node dist/cli.js","test:watch":"vitest","prepublishOnly":"tsc"},"_npmUser":{"name":"justinschmitz97","email":"justin.schmitz97@gmail.com"},"repository":{"url":"git+https://github.com/justinschmitz97/120fps.git","type":"git"},"_npmVersion":"10.9.7","description":"Zero-config UI component performance profiler. Real browser, real metrics.","directories":{},"_nodeVersion":"22.22.2","dependencies":{"vite":"^6.3.0","playwright":"^1.52.0","typescript":"^5.8.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"^19.0.0","vitest":"^3.1.0","react-dom":"^19.0.0","@types/node":"^22.0.0","@types/react":"^19.0.0","@types/react-dom":"^19.0.0"},"_npmOperationalInternal":{"tmp":"tmp/120fps_0.1.3_1777832918527_0.6746558609656121","host":"s3://npm-registry-packages-npm-production"}},"0.1.5":{"name":"120fps","version":"0.1.5","license":"MIT","_id":"120fps@0.1.5","maintainers":[{"name":"justinschmitz97","email":"justin.schmitz97@gmail.com"}],"homepage":"https://github.com/justinschmitz97/120fps#readme","bugs":{"url":"https://github.com/justinschmitz97/120fps/issues"},"bin":{"120fps":"dist/cli.js"},"dist":{"shasum":"b450ec40a45eaa21262633df0823ebbdb81c6a2b","tarball":"https://registry.npmjs.org/120fps/-/120fps-0.1.5.tgz","fileCount":31,"integrity":"sha512-WTIc3cL9xhAuzXrIZnkVLzZ0EzKKVCkCISUObpZd8GZpBjIsCdGcLxXnB9s5aHvODp5w/zTUTF/B82Pivm94Tg==","signatures":[{"sig":"MEYCIQDZeePKxLtRnf8qOhAZOJ4ODqAShdJNLS2cX6fyxHvlQAIhAKeJPjKZw4fZzk8d/mHSjcpdrOHyfzsyjFpXArNVirzG","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":205469},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"4fe934a8f2927a11df96679203138798cd244f43","scripts":{"lint":"tsc --noEmit","test":"vitest run","build":"tsc","start":"tsc && node dist/cli.js","test:watch":"vitest","prepublishOnly":"tsc"},"_npmUser":{"name":"justinschmitz97","email":"justin.schmitz97@gmail.com"},"repository":{"url":"git+https://github.com/justinschmitz97/120fps.git","type":"git"},"_npmVersion":"11.12.1","description":"Zero-config UI component performance profiler. Real browser, real metrics.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"vite":"^6.3.0","playwright":"^1.52.0","typescript":"^5.8.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"^19.0.0","vitest":"^3.1.0","react-dom":"^19.0.0","@types/node":"^22.0.0","@types/react":"^19.0.0","@types/react-dom":"^19.0.0"},"_npmOperationalInternal":{"tmp":"tmp/120fps_0.1.5_1777844642281_0.2590159984923808","host":"s3://npm-registry-packages-npm-production"}},"0.1.6":{"name":"120fps","version":"0.1.6","license":"MIT","_id":"120fps@0.1.6","maintainers":[{"name":"justinschmitz97","email":"justin.schmitz97@gmail.com"}],"homepage":"https://github.com/justinschmitz97/120fps#readme","bugs":{"url":"https://github.com/justinschmitz97/120fps/issues"},"bin":{"120fps":"dist/cli.js"},"dist":{"shasum":"3c28d50a1395eabd524e01740702850c853d4cee","tarball":"https://registry.npmjs.org/120fps/-/120fps-0.1.6.tgz","fileCount":31,"integrity":"sha512-hDBmbkAiaHF68lB7Gm+Pj66Np2UV0VkSzYf7S3nwi5ocinJHvwZ/lVhO/BJZI4iaO9iT8GH2iYqaTy+3KhRmJg==","signatures":[{"sig":"MEUCIAP4A/OFgFMQ/xWfWGrD8vBH+oJuDCDHOBRM74IAW9iYAiEAjrtMxCCCsDnfW/L8uSnp+MH5RvIYW+yRaB3nTR6Felg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":206110},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"4fe934a8f2927a11df96679203138798cd244f43","scripts":{"lint":"tsc --noEmit","test":"vitest run","build":"tsc","start":"tsc && node dist/cli.js","test:watch":"vitest","prepublishOnly":"tsc"},"_npmUser":{"name":"justinschmitz97","email":"justin.schmitz97@gmail.com"},"repository":{"url":"git+https://github.com/justinschmitz97/120fps.git","type":"git"},"_npmVersion":"11.12.1","description":"Zero-config UI component performance profiler. Real browser, real metrics.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"vite":"^6.3.0","playwright":"^1.52.0","typescript":"^5.8.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"^19.0.0","vitest":"^3.1.0","react-dom":"^19.0.0","@types/node":"^22.0.0","@types/react":"^19.0.0","@types/react-dom":"^19.0.0"},"_npmOperationalInternal":{"tmp":"tmp/120fps_0.1.6_1777851165405_0.143810914250035","host":"s3://npm-registry-packages-npm-production"}},"0.1.8":{"name":"120fps","version":"0.1.8","license":"MIT","_id":"120fps@0.1.8","maintainers":[{"name":"justinschmitz97","email":"justin.schmitz97@gmail.com"}],"homepage":"https://github.com/justinschmitz97/120fps#readme","bugs":{"url":"https://github.com/justinschmitz97/120fps/issues"},"bin":{"120fps":"dist/cli.js"},"dist":{"shasum":"e9115fbf382b4dd8d7b774d1676a2017bc294cde","tarball":"https://registry.npmjs.org/120fps/-/120fps-0.1.8.tgz","fileCount":43,"integrity":"sha512-i7WfAX4edw+SOhKI+zN02Uyj2FlTDaHIL8atjXJBJ5ryr0EsRRbBket31L6HidYxlBE28EK033y0vdS+AJk+Sw==","signatures":[{"sig":"MEYCIQDyhufw654As4eugSlv01cxek65JHlZG6ZSPFEEI2a1RAIhAOm1XO8ITOT29OlvySWbFOpijtSH3APGFKboB6yhcb+A","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":216028},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"38c007a8434b6289544ed2e41b60a214551137fb","scripts":{"lint":"tsc --noEmit","test":"vitest run","build":"tsc","start":"tsc && node dist/cli.js","test:watch":"vitest","prepublishOnly":"tsc"},"_npmUser":{"name":"justinschmitz97","email":"justin.schmitz97@gmail.com"},"repository":{"url":"git+https://github.com/justinschmitz97/120fps.git","type":"git"},"_npmVersion":"11.12.1","description":"Zero-config UI component performance profiler. Real browser, real metrics.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"vite":"^6.3.0","playwright":"^1.52.0","typescript":"^5.8.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"^19.0.0","vitest":"^3.1.0","react-dom":"^19.0.0","@types/node":"^22.0.0","@types/react":"^19.0.0","@types/react-dom":"^19.0.0"},"_npmOperationalInternal":{"tmp":"tmp/120fps_0.1.8_1777913632603_0.7678166311506385","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"120fps","version":"0.2.0","license":"MIT","_id":"120fps@0.2.0","maintainers":[{"name":"justinschmitz97","email":"justin.schmitz97@gmail.com"}],"homepage":"https://github.com/justinschmitz97/120fps#readme","bugs":{"url":"https://github.com/justinschmitz97/120fps/issues"},"bin":{"120fps":"dist/cli.js"},"dist":{"shasum":"e89a63be7ed1c7a7eb015478faeea5817ee36dbe","tarball":"https://registry.npmjs.org/120fps/-/120fps-0.2.0.tgz","fileCount":49,"integrity":"sha512-xL72GZIsd0PclPQa+9EVcYAd60ShPS4o/8oyCkGgNN6xkHN38HmycgZMZ97y9kTZ201xuwC0vejfHKJcwjg9Eg==","signatures":[{"sig":"MEYCIQDWQz0hphwdHciQ1qnuh2Yb1+H6Ptduiy7adQJ9w0YNVAIhAMhUzvIw6lThgT6Wo23h/kPRi/pSVMd46yDrF3jfVCFt","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":359895},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"4ee16207cdc415f77dd188d8085d74bf87ae3bbb","scripts":{"lint":"tsc --noEmit","test":"vitest run","build":"tsc","start":"tsc && node dist/cli.js","test:watch":"vitest","prepublishOnly":"tsc"},"_npmUser":{"name":"justinschmitz97","email":"justin.schmitz97@gmail.com"},"repository":{"url":"git+https://github.com/justinschmitz97/120fps.git","type":"git"},"_npmVersion":"11.6.2","description":"Zero-config UI component performance profiler. Real browser, real metrics.","directories":{},"_nodeVersion":"24.11.1","dependencies":{"vite":"^6.3.0","playwright":"^1.52.0","typescript":"^5.8.0","@vitejs/plugin-react":"^4.7.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"^19.0.0","vitest":"^3.1.0","react-dom":"^19.0.0","@types/node":"^22.0.0","tailwindcss":"^4.3.3","@types/react":"^19.0.0","@types/react-dom":"^19.0.0","@tailwindcss/postcss":"^4.3.3"},"_npmOperationalInternal":{"tmp":"tmp/120fps_0.2.0_1785497975993_0.7597918436501294","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"120fps","version":"0.2.1","license":"MIT","_id":"120fps@0.2.1","maintainers":[{"name":"justinschmitz97","email":"justin.schmitz97@gmail.com"}],"homepage":"https://github.com/justinschmitz97/120fps#readme","bugs":{"url":"https://github.com/justinschmitz97/120fps/issues"},"bin":{"120fps":"dist/cli.js"},"dist":{"shasum":"ad2e39029848b2a40f16d211bf0d108ce5c56ca6","tarball":"https://registry.npmjs.org/120fps/-/120fps-0.2.1.tgz","fileCount":49,"integrity":"sha512-cvKcJ/DWg2KsZSfmdDbusldhjw/ZrNYef8LeV2epON0WVnE23LARqPmFlim15lXTu1rZMskoHs2opT1LlDsmgg==","signatures":[{"sig":"MEYCIQDxvzJzUUNX1cBI72+XGrLrVDIoL9P41V/F70eUIJTfwQIhAKjXx1afucEv8pIwR8zsDjT49DtHVEJon4uP1rZjvOB9","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":360932},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"902c69097a006506f59682c4476da4dbc6467528","scripts":{"lint":"tsc --noEmit","test":"vitest run","build":"tsc","start":"tsc && node dist/cli.js","test:watch":"vitest","prepublishOnly":"tsc"},"_npmUser":{"name":"justinschmitz97","email":"justin.schmitz97@gmail.com"},"repository":{"url":"git+https://github.com/justinschmitz97/120fps.git","type":"git"},"_npmVersion":"11.12.1","description":"Zero-config UI component performance profiler. Real browser, real metrics.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"vite":"^6.3.0","playwright":"^1.52.0","typescript":"^5.8.0","@vitejs/plugin-react":"^4.7.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"^19.0.0","vitest":"^3.1.0","react-dom":"^19.0.0","@types/node":"^22.0.0","tailwindcss":"^4.3.3","@types/react":"^19.0.0","@types/react-dom":"^19.0.0","@tailwindcss/postcss":"^4.3.3"},"_npmOperationalInternal":{"tmp":"tmp/120fps_0.2.1_1785534608795_0.3539599442325305","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"120fps","version":"0.4.0","license":"MIT","_id":"120fps@0.4.0","maintainers":[{"name":"justinschmitz97","email":"justin.schmitz97@gmail.com"}],"homepage":"https://github.com/justinschmitz97/120fps#readme","bugs":{"url":"https://github.com/justinschmitz97/120fps/issues"},"bin":{"120fps":"dist/cli.js"},"dist":{"shasum":"4f69aa4e5fd1de45d65e56e0ede769aaea9ea28c","tarball":"https://registry.npmjs.org/120fps/-/120fps-0.4.0.tgz","fileCount":66,"integrity":"sha512-T4WqufSz9BEro4ra0m63EJeGdKds8u1tpPcsZjmDJrEAAW3Dakil/nSdBdIKDWy9VXGXOLKEX4WwsWVXO3O2+Q==","signatures":[{"sig":"MEQCIG3KUexHzNyLAe1NZ+Qo0Me7QYoxnmaiNT99VkmhnT+BAiA8HjPx/+hNFcHXVSnYMZHX4uEbw42uBUCpbqB8EuLCSQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":706877},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=22.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"b1739d0881876d37f6aa7519631fb9f021d6c44f","scripts":{"lint":"tsc --noEmit","test":"vitest run test/unit/","build":"tsc","start":"tsc && node dist/cli.js","test:all":"vitest run","test:e2e":"vitest run test/e2e/","test:unit":"vitest run test/unit/","test:watch":"vitest","prepublishOnly":"tsc"},"_npmUser":{"name":"justinschmitz97","email":"justin.schmitz97@gmail.com"},"repository":{"url":"git+https://github.com/justinschmitz97/120fps.git","type":"git"},"_npmVersion":"11.12.1","description":"Zero-config UI component performance profiler. Real browser, real metrics.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"vite":"^6.3.0","playwright":"^1.52.0","typescript":"^5.8.0","@vitejs/plugin-react":"^4.7.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"^19.0.0","vitest":"^3.1.0","react-dom":"^19.0.0","@types/node":"^22.0.0","tailwindcss":"^4.3.3","@types/react":"^19.0.0","@types/react-dom":"^19.0.0","@tailwindcss/postcss":"^4.3.3"},"peerDependencies":{"react":">=18","react-dom":">=18"},"peerDependenciesMeta":{"react":{"optional":true},"react-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/120fps_0.4.0_1787125498575_0.7717938163769671","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"120fps","version":"0.5.0","license":"MIT","_id":"120fps@0.5.0","maintainers":[{"name":"justinschmitz97","email":"justin.schmitz97@gmail.com"}],"homepage":"https://github.com/justinschmitz97/120fps#readme","bugs":{"url":"https://github.com/justinschmitz97/120fps/issues"},"bin":{"120fps":"dist/cli.js"},"dist":{"shasum":"dbb3ec52d5f57d43d11c421d85d4ac35f9c9ce46","tarball":"https://registry.npmjs.org/120fps/-/120fps-0.5.0.tgz","fileCount":76,"integrity":"sha512-IuQNEwM5Q6D2s0XLcoPRi2Ej8t5S/eEGOdLJ1Nn0HPvHhKYjAhRdOWsoxLXG+6z7b9LeGCPWFbTtxUvdXCdTJg==","signatures":[{"sig":"MEUCIF/EPHx5DbmgUfe1TMZOvKTzLJtU0ulHrKlrItOt942kAiEA0MeGLu8s4a+3JKt+bEcwI8+RAGYFIMteg3nfvYgWYR0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":791659},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=22.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"074f16dffd3ca76a7e831fb056511a6dd3d25d31","scripts":{"lint":"tsc --noEmit","test":"vitest run test/unit/","build":"tsc","start":"tsc && node dist/cli.js","test:all":"vitest run","test:e2e":"vitest run test/e2e/","test:unit":"vitest run test/unit/","test:watch":"vitest","prepublishOnly":"tsc"},"_npmUser":{"name":"justinschmitz97","email":"justin.schmitz97@gmail.com"},"repository":{"url":"git+https://github.com/justinschmitz97/120fps.git","type":"git"},"_npmVersion":"11.12.1","description":"Zero-config UI component performance profiler. Real browser, real metrics.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"vite":"^6.3.0","playwright":"^1.52.0","typescript":"^5.8.0","@vitejs/plugin-react":"^4.7.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"^19.0.0","vitest":"^3.1.0","react-dom":"^19.0.0","@types/node":"^22.0.0","tailwindcss":"^4.3.3","@types/react":"^19.0.0","@types/react-dom":"^19.0.0","@tailwindcss/postcss":"^4.3.3"},"peerDependencies":{"react":">=18","react-dom":">=18"},"peerDependenciesMeta":{"react":{"optional":true},"react-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/120fps_0.5.0_1787178041543_0.7827152380996125","host":"s3://npm-registry-packages-npm-production"}},"0.6.0":{"name":"120fps","version":"0.6.0","license":"MIT","_id":"120fps@0.6.0","maintainers":[{"name":"justinschmitz97","email":"justin.schmitz97@gmail.com"}],"homepage":"https://github.com/justinschmitz97/120fps#readme","bugs":{"url":"https://github.com/justinschmitz97/120fps/issues"},"bin":{"120fps":"dist/cli.js"},"dist":{"shasum":"bd4a20c24035dca68efb05f172208ee6d07451b8","tarball":"https://registry.npmjs.org/120fps/-/120fps-0.6.0.tgz","fileCount":76,"integrity":"sha512-CAbVsb4M0UQJ1DYJxUknf2w7CfHNYfg13uAz95y9O+Wmd8mvkTZhz+WVAR9ubQrzOXlyCiRkyjEFd15eVPTBEA==","signatures":[{"sig":"MEQCIAGIeS2VYegtbxzavdMc3K/y0BC2YMEmAJgeMva8hFrbAiBKPW572IyKdk9TtFnVrR9ALOpy9CPhAceSe2bVAhRgvQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1227313},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=22.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"7177203ddfbe75da1207c675c873743dcd4f1f09","scripts":{"lint":"tsc --noEmit","test":"vitest run test/unit/","build":"tsc","start":"tsc && node dist/cli.js","test:all":"vitest run","test:e2e":"vitest run test/e2e/","test:unit":"vitest run test/unit/","test:watch":"vitest","prepublishOnly":"tsc"},"_npmUser":{"name":"justinschmitz97","email":"justin.schmitz97@gmail.com"},"repository":{"url":"git+https://github.com/justinschmitz97/120fps.git","type":"git"},"_npmVersion":"11.12.1","description":"Zero-config UI component performance profiler. Real browser, real metrics.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"vite":"^6.3.0","playwright":"^1.52.0","typescript":"^5.8.0","@vitejs/plugin-react":"^4.7.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"^19.0.0","vitest":"^3.1.0","react-dom":"^19.0.0","@types/node":"^22.0.0","tailwindcss":"^4.3.3","@types/react":"^19.0.0","@types/react-dom":"^19.0.0","@tailwindcss/postcss":"^4.3.3"},"peerDependencies":{"react":">=18","react-dom":">=18"},"peerDependenciesMeta":{"react":{"optional":true},"react-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/120fps_0.6.0_1787557661536_0.6562764590883201","host":"s3://npm-registry-packages-npm-production"}},"0.7.0":{"_id":"120fps@0.7.0","bin":{"120fps":"dist/cli/main.js"},"bugs":{"url":"https://github.com/justinschmitz97/120fps/issues"},"dist":{"shasum":"0c6cd3c111a3815f68bdf1236d3bcf0a3afa7bc2","tarball":"https://registry.npmjs.org/120fps/-/120fps-0.7.0.tgz","fileCount":246,"integrity":"sha512-CJnmH2LACSfg/ZcBsITm+v6A1KB7Bodk4lsR6WOb/FrNsyoRejJPrl4YkyY6mNFJE6DtxVoEvwytdfGqYYxtcw==","signatures":[{"sig":"MEYCIQDdFcRnXEv9Q36eBFPxgHX3bcHvO6HcrRivtUwRIk0FVwIhAIESgT5z6VAwaCh365yHPn91sS2S2hMQ/JhWAaPMK+1i","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIHNDayfKi331rjQ3DH1KEzn/mvrg2EQzBRkKDWXKZUpoAiBQKApDvz645RWXfqbUx26BjU+4a1O/W4wkFHxRFC+Y4Q=="}],"unpackedSize":1296356},"main":"./dist/index.js","name":"120fps","type":"module","types":"./dist/index.d.ts","engines":{"node":">=22.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"f54be55751a8b70e5da21cffead457f8212a85ed","license":"MIT","scripts":{"lint":"tsc --noEmit","test":"vitest run test/unit/","build":"tsc","start":"tsc && node dist/cli/main.js","test:all":"vitest run","test:e2e":"vitest run test/e2e/","test:unit":"vitest run test/unit/","test:watch":"vitest","prepublishOnly":"tsc"},"version":"0.7.0","_npmUser":{"name":"justinschmitz97","email":"justin.schmitz97@gmail.com"},"homepage":"https://github.com/justinschmitz97/120fps#readme","repository":{"url":"git+https://github.com/justinschmitz97/120fps.git","type":"git"},"_npmVersion":"11.12.1","description":"Zero-config UI component performance profiler. Real browser, real metrics.","directories":{},"maintainers":[{"name":"justinschmitz97","email":"justin.schmitz97@gmail.com"}],"_nodeVersion":"24.15.0","dependencies":{"sass":"^1.90.0","vite":"^6.3.0","playwright":"^1.52.0","typescript":"^5.8.0","@vitejs/plugin-react":"^4.7.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"^19.0.0","vitest":"^3.1.0","react-dom":"^19.0.0","@types/node":"^22.0.0","tailwindcss":"^4.3.3","@types/react":"^19.0.0","@types/react-dom":"^19.0.0","@tailwindcss/postcss":"^4.3.3"},"peerDependencies":{"react":">=18","react-dom":">=18"},"peerDependenciesMeta":{"react":{"optional":true},"react-dom":{"optional":true}},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/120fps_0.7.0_1788693717134_0.5242499051500042"}}},"time":{"created":"2026-05-03T13:50:49.831Z","modified":"2026-09-06T11:21:57.487Z","0.1.0":"2026-05-03T13:50:50.126Z","0.1.1":"2026-05-03T14:32:23.615Z","0.1.2":"2026-05-03T16:05:07.729Z","0.1.3":"2026-05-03T18:28:38.682Z","0.1.5":"2026-05-03T21:44:02.481Z","0.1.6":"2026-05-03T23:32:45.544Z","0.1.8":"2026-05-04T16:53:52.803Z","0.2.0":"2026-07-31T11:39:36.135Z","0.2.1":"2026-07-31T21:50:08.935Z","0.4.0":"2026-08-19T07:44:58.722Z","0.5.0":"2026-08-19T22:20:41.705Z","0.6.0":"2026-08-24T07:47:41.684Z","0.7.0":"2026-09-06T11:21:57.302Z"},"bugs":{"url":"https://github.com/justinschmitz97/120fps/issues"},"license":"MIT","homepage":"https://github.com/justinschmitz97/120fps#readme","repository":{"url":"git+https://github.com/justinschmitz97/120fps.git","type":"git"},"description":"Zero-config UI component performance profiler. Real browser, real metrics.","maintainers":[{"name":"justinschmitz97","email":"justin.schmitz97@gmail.com"}],"readme":"# 120fps\n\nZero-config component performance profiler. Real browser, real metrics.\n\n```bash\nnpx 120fps ./Button.tsx     # React\nnpx 120fps ./Button.vue     # Vue\n```\n\nOne command, one component file. You get:\n\n- mount / rerender / unmount timings across generated prop combinations\n- interaction costs: clicks, drags, scrolls, discovered from the DOM and stress-tested\n- scaling behavior, memory leaks, React optimization findings (memo bailouts, context fan-out, callback identity)\n- a pass / warn / fail verdict per combo, with tiered budgets and a remediation hint per finding\n\nNo config, no stories, no test IDs. Props come from your TypeScript types, the browser is headless Chromium, timings come from CDP traces.\n\n## Which mode answers my question\n\n| question | mode |\n|---|---|\n| Is it fast? | `npx 120fps ./Button.tsx` |\n| Does it scale with its data? | `--curve` (auto-activates when a scaling prop is detected) |\n| Which prop costs the most? | `--matrix` |\n| Is it leaking, or degrading as it churns? | `--isolate memory` / `--isolate rerender` |\n| Did I regress? | `--budget` in CI, against a saved baseline |\n| Did the change I just made help? | `--compare HEAD` |\n\nAll numbers are measured under 4× CPU throttle. Compare them against budgets, baselines, or the other side of a `--compare`: not against production wall-clock.\n\n## Install\n\n```bash\nnpm install -D 120fps    # or just: npx 120fps ./Button.tsx\n```\n\n## CLI\n\n```\nnpx 120fps <component.tsx|.jsx|.vue>[#ExportName] [options]\n\nOptions:\n  --explain-props                Dry run: print the resolved component and prop schema, measure nothing\n  --fixture <path>               Fixture file for composed components\n  --json <path>                  JSON output path (default: 120fps-report.json)\n  --ci                           CI mode: JSON only, exit 1 on fail\n  --samples <n>                  Samples per measurement (default: 10)\n  --max-combos <n>               Prop combos to measure (default: 8)\n  --explore-budget <seconds>     Total interaction exploration budget (default: 300)\n  --init-fixture                 Write a starter fixture when auto-composition is rolled back\n  --scale <n,n,...>              Scale points, ≥2 distinct positive integers, overriding both defaults: combo-mode scale probes (default: 1,5,20,50) and curve-mode points (default: 1,3,5,10,20,50)\n  --threshold-mount <ms>         Mount budget (overrides tier budget)\n  --threshold-rerender <ms>      Rerender budget (overrides tier budget)\n  --threshold-interaction <ms>   Interaction budget (overrides tier budget)\n  --flat-thresholds              Use flat budgets instead of tiered\n  --framework <react|vue|vanilla|auto>  Framework mode (default: auto)\n  --curve [prop:type]            Curve mode: mount/rerender across scale points\n  --no-curve                     Disable auto-activation of curve mode\n  --matrix                       Prop variation matrix mode\n  --no-matrix                    Disable auto-activation of matrix mode\n  --isolate <phases>             Isolated measurement: mount,rerender,unmount,memory,strictmode,all\n  --memory-cycles <n>            Mount/unmount cycles for memory mode (default: 20)\n  --no-isolate                   Disable isolation mode (overrides --isolate)\n  --compare <gitref>             Measure the working tree against <gitref>, samples interleaved\n  --save-baseline                Save current measurements as baseline\n  --check                        Compare against baseline, fail on regression\n  --budget                       Shorthand for --ci --check\n  --no-baseline                  Skip baseline comparison in CI mode\n  --no-cache                     Measure even when an unchanged component could reuse its baseline verdict\n  --baseline-env <mode>          Baseline environment handling: strict|normalize|ignore (default: normalize)\n  --report-md <path>             Write a markdown summary (GitHub step summary / PR comment body)\n  --report-junit <path>          Write JUnit XML, one testcase per component\n  --no-deltas                    Skip pairwise prop delta analysis\n  --no-auto-scale                Skip auto-scaling prop detection\n  --no-attribution               Skip cost attribution\n  --no-auto-compose              Skip auto-composition inference\n  --no-react-analysis            Skip React optimization detection\n  --wrap <path>                  Provider wrapper module (auto: 120fps.setup.tsx at project root)\n  --no-wrap                      Disable the provider wrapper, including auto-detection\n  --css <path,...>               Global stylesheets to inject (auto: app/globals.css and friends)\n  --no-css                       Disable stylesheet injection, including auto-detection\n  --react-compiler               Force the React Compiler transform on (auto: babel-plugin-react-compiler in package.json)\n  --no-react-compiler            Disable the React Compiler transform, including auto-detection\n  --no-shims                     Disable Next.js module shims\n  --no-preflight                 Attempt the run even when the component graph reaches a server boundary\n  --no-transforms                Do not load the project's own Vite transforms (SVGR, vanilla-extract)\n  --help                         Show help\n  --version                      Print version\n```\n\nNotes:\n\n- `--matrix` auto-activates on ≥2 small boolean/union axes; the run announces the cell count first. Above 256 cells it falls back to pairwise cover and says how many cells it measured.\n- Matrix runs don't participate in baselines. Use `--no-matrix` to save/check a baseline for such a component.\n- `--curve`, `--matrix`, `--isolate` are exclusive whole-run modes; combining them is a usage error.\n- Exit codes: 0 pass, 1 verdict fail, 2 setup/usage error.\n- The harness readiness wait is bounded at 90s by default; raise it with the `FPS120_READY_TIMEOUT_MS` environment variable (a positive whole number of milliseconds) on a slow or busy machine.\n- A Nuxt project whose `.nuxt/` directory is missing a file its own tsconfig names is refused before the browser starts, naming the missing file and the `nuxi prepare` remedy, in the dry run and the real run alike.\n- A component whose import graph reaches a Babel macro (`*/macro`, `*.macro`, `babel-plugin-macros`) is refused before the browser, naming the importer, the macro and the compiler the project declares for it; `--no-preflight` bypasses the refusal and runs the rest of the pipeline unchanged.\n\n### Which component gets measured\n\nDefault export → export matching the filename (`hotspot-image.tsx` → `HotspotImage`) → first exported component. Override with `#ExportName`:\n\n```bash\nnpx 120fps ./kbd.tsx#KbdCombo\n```\n\nUnknown names error, listing the file's exports.\n\n`--explain-props` shows what a run *would* measure: resolved component, its `file:line`, every prop with kind, required/default, and value pool, unsynthesizable props, whether curve/matrix would activate: without starting a browser. The `default` column prints only when at least one prop carries one. It also prints a `Composition:` line (`would auto-compose from <Root> (<n> exports)` or `would measure <Name> alone`), a `Matrix mode:` line when applicable, and ends with an estimate: `Estimated real run: ~2m 10s (8 combos x 5 samples; phase timings from 120fps-baseline.json)`. It uses recorded phase timings from a matching `--save-baseline` entry when one exists, documented defaults otherwise.\n\nRuns print one line per phase (`mount: 8 combos x 10 samples`) and end with `Total: 4m 12s`. `--ci` prints JSON only. A monorepo run prints one `Roots:` line ahead of the component's table, naming the resolved member root and the workspace root; the line reads the same from any shell directory the CLI was started from (suppressed under `--ci`).\n\n## Report output\n\n- `fail`: a median broke its budget (mount, rerender, or per-step interaction). Any failing combo fails the run.\n- `warn`: nothing broke a budget, but don't trust the numbers at face value: unstable timing, large cost vs calibration, or an optimization finding. Never affects the exit code.\n- `unstable`: CV > 15% *and* spread > 0.5 ms. Reported, skipped for baseline comparison, downgrades to warn. Machine evidence, not component evidence; raise `--samples`.\n- `warnings`: everything the run did or didn't measure (capped combos, noisy machine, unsettled fonts). In the terminal, JSON, and markdown output. A warning repeated for the same component prints once with a `(×N)` suffix; the JSON `warnings` array holds one entry per distinct text, in first-occurrence order.\n- A noisy or hostile machine gets one terminal line naming only the signals that crossed threshold and the flag that helps: `machine: hostile (probe CV 53%, 100% of metrics unstable); raise --samples to measure through it.` The JSON keeps the full detail (`noise.level`, all four `noise.signals`, the full warning text); verdicts and budget lines read the same either way.\n- Combos that crashed while rendering are `FAIL [render error]` with the page errors printed: see [render errors](#render-errors).\n- `DEBUG=1` prints full stack traces.\n\n## Where the minutes go\n\n`Total:` breaks down by phase: `Total: 3m 12s  (build 41s, mount 58s, explore 1m 20s, analysis 12s)`; a phase at zero is left out. The JSON report carries the same numbers in `phaseTimings`: `preflight`, `build`, `calibration`, `mount`, `rerender`, `explore`, `scale`, `deltas`, `attribution`, `analysis` and `total`, each an integer millisecond count, the ten phase keys summing to `total`. `--report-md` includes the same breakdown per component; a report from before this existed renders `-`, never `0s`.\n\n## Budgets & baselines (CI)\n\n```yaml\n# on main: record the numbers everyone checks against\n- run: npx 120fps \"src/components/**/*.tsx\" --save-baseline\n- run: git add 120fps-baseline.json && git commit -m \"chore: update perf baseline\"\n\n# on pull requests: gate the merge\n- run: npx 120fps \"src/components/**/*.tsx\" --budget\n```\n\n- `--save-baseline` writes `120fps-baseline.json` at the project root; `--check` exits 1 on regression; `--budget` = `--ci --check`.\n- Per-component overrides: `120fps.config.json`, keyed by component path. Invalid numbers throw before measuring.\n- Save and check on the same runner image. Keep local baselines out of your commits with `git update-index --skip-worktree 120fps-baseline.json`.\n\nBaseline entries record their environment (CPU, cores, OS, Chromium, throttle, samples, mode, stylesheets, wrapper, React Compiler). `--check` classifies each pair:\n\n| classification | comparison |\n|---|---|\n| `identical` | raw milliseconds |\n| `normalizable` | calibration-normalized, 0.5 ms floor |\n| `incompatible` | none: mismatch named, run doesn't fail |\n| `unknown` | raw, with a warning |\n\nSame-machine comparison is trustworthy; cross-machine catches large regressions and misses small ones. `--baseline-env strict` fails on anything but `identical`; `ignore` always compares raw.\n\nEntries are stored one slot per environment: laptop and CI runner share the file without overwriting each other. A missing slot compares informationally against the freshest other slot and cannot fail. Slots idle for 90 days are pruned on save.\n\nIn check mode, an unchanged component (same source fingerprint, same machine slot) reuses its stored verdict: `cached: true`, seconds instead of minutes for a sweep. `--no-cache` forces measurement; explicit modes, `--save-baseline`, and env changes always measure. The fingerprint can't see cross-file Tailwind utility changes: `--no-cache` when in doubt.\n\n### CI surfacing\n\n- `--report-md <path>`: verdict line + one row per component, regressions behind a `<details>` fold. A component with warnings gets its own fold listing its deduped `(×N)` warning texts. Use as `$GITHUB_STEP_SUMMARY` or a PR-comment body; GitLab renders it too.\n- `--report-junit <path>`: one testcase per component; every CI renders JUnit.\n- No tokens, no network calls: 120fps writes files, your CI posts them.\n- A written report, baseline, or leftover harness directory inside the run's git repo and not covered by its `.gitignore` gets one tip after the last one, at the end of the process: `Tip: 120fps writes report/baseline files into this repo. Consider adding to .gitignore: <patterns>`.\n\n```yaml\n- name: Measure components\n  run: npx 120fps \"src/components/**/*.tsx\" --budget --report-md perf.md --report-junit perf.xml\n  continue-on-error: true\n\n- name: Publish to the job summary\n  if: always()\n  run: cat perf.md >> $GITHUB_STEP_SUMMARY\n\n- name: Comment on the PR\n  if: always() && github.event_name == 'pull_request'\n  uses: marocchino/sticky-pull-request-comment@v2\n  with:\n    path: perf.md\n```\n\n## Isolated measurement\n\n`--isolate <phases>` replaces the combo sweep with focused micro-benchmarks, one browser pass per phase, single prop combination.\n\n```bash\nnpx 120fps ./Button.tsx --isolate mount,rerender\nnpx 120fps ./Button.tsx --isolate all --memory-cycles 40\n```\n\n| phase | measures |\n|---|---|\n| `mount` | mount cost alone, teardown outside the traced window |\n| `unmount` | teardown cost alone |\n| `rerender` | stable, prop-change, and churn (10 alternating cycles, degradation ratio) |\n| `memory` | mount/unmount cycles between forced GCs, heap growth per cycle, leak verdict |\n| `strictmode` | mount with vs without `React.StrictMode`, interleaved pairs |\n\nFails on: mount over budget, suspected leak (>8 KB/cycle), churn degrading >2×. StrictMode overhead >2× is a warning only. Isolation baselines never compare against standard ones.\n\n## Fixtures\n\nComposed components get a `.fixture.tsx` next to the component (`Accordion.fixture.tsx`, auto-detected) or via `--fixture`:\n\n```tsx\nimport { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from \"./Accordion\";\n\nexport default function Scene() {\n  return (\n    <Accordion>\n      <AccordionItem value=\"1\">\n        <AccordionTrigger>Section 1</AccordionTrigger>\n        <AccordionContent>Content 1</AccordionContent>\n      </AccordionItem>\n    </Accordion>\n  );\n}\n```\n\nExport a `scale` function for parameterized scaling:\n\n```tsx\nexport function scale(n: number) {\n  return <Accordion>{Array.from({ length: n }, (_, i) => <AccordionItem key={i} value={String(i)}>…</AccordionItem>)}</Accordion>;\n}\n```\n\n`--init-fixture` writes a starter fixture when a component never auto-composes but declares siblings: the bound root plus a `TODO` placeholder per sibling, and prints `wrote fixture scaffold <path>; edit it to render the real composition, then re-run`. If the target already exists it prints `--init-fixture skipped: <path> already exists` instead of overwriting it.\n\nProp values can also come from a preset: `<stem>.120fps.props.tsx`, `<stem>.120fps.props.ts`, `<stem>.props.tsx`, `<stem>.props.ts`, in that order, the first one shaped as a default-exported object literal (`export default { prop: [values] }`) wins. A candidate file under one of those names without that shape is reported by path, `exists, not a preset: no default-exported object literal`, and every remedy that would have named it points at `<stem>.120fps.props.tsx` instead. Once a preset loads, cap and collapsed-union warnings are re-checked against it: one the preset resolves stops printing, one that survives names the preset instead of asking you to add a file.\n\n## Provider wrapper\n\nComponents that read context need providers. `120fps.setup.tsx` at the project root is picked up automatically (`--wrap` for a different path, `--no-wrap` to disable):\n\n```tsx\nimport \"../app/globals.css\";\nimport { ThemeProvider } from \"./theme\";\n\nexport default function Setup({ children }: { children: React.ReactNode }) {\n  return <ThemeProvider>{children}</ThemeProvider>;\n}\n```\n\nTop-level side effects run before the component module. Optional `export const viewport = { width: 375, height: 667 }`. The report records the wrapper's own cost (`wrapper.overheadMs`).\n\n## Stylesheets\n\nGlobal stylesheets are injected automatically: first hit wins:\n\n```\napp/globals.css   app/global.css   src/app/globals.css   src/app/global.css\nsrc/styles/globals.css   styles/globals.css   src/index.css   src/global.css\n```\n\n- Your `postcss.config.*` is read and normalized by 120fps itself, not handed to Vite's own config loader: a string plugin name, a `[name, options]` tuple, and an object-map entry all resolve, each plugin's package resolved from the directory that actually declares it, so a config re-exported from a shared tooling package still finds its plugins. Tailwind 4 works with no extra config. Tailwind 3 reads `tailwind.config.{js,cjs,mjs,ts}` from the member's own root first, then each ancestor up to the workspace root, whatever directory the CLI was started from, so the `Stylesheets:` line and warnings read the same from any shell directory.\n- Split or unusual paths: `--css ./reset.css,./tokens.css` (cascade order). `--no-css` disables.\n- Fonts settle before the first sample (`document.fonts.ready`, 5s bound); injected files are named in `css.files` and the baseline fingerprint.\n- A package that generates its styling at runtime (Griffel, Emotion, `styled-components`, `@ant-design/cssinjs`, `antd-style`, `css-render`, PrimeVue) prints `Stylesheets: none — styling is generated at runtime by <engine>; no stylesheet was needed` instead of a fallback warning. A `makeStyles`/`createUseStyles`/`styled` import from an unrecognised package gets its own line naming the package and points at `--css`.\n- A package whose `package.json` declares a stylesheet (`style`, `exports[\"./styles\"]`, `exports[\"./style.css\"]`) that isn't built yet is reported as declared but unbuilt: the field, the missing path and the package's build command, instead of falling back to size-ranked guessing. The report's `css.declaredMissing` carries the missing paths.\n- A conventional-filename candidate whose Tailwind syntax contradicts the installed Tailwind major (a v4 `@import \"tailwindcss\"` sheet in a v3 project, or the reverse) is skipped before injection, naming the file, its dialect and the installed version, unless the project's own entry or manifest is what points at it. An injected stylesheet that throws while compiling, or does not finish within a 20s bound, is dropped and reported (`… did not compile within 20 s`) instead of ending the run, and the component may render unstyled.\n\n## React Compiler\n\nIf `babel-plugin-react-compiler` is in your `package.json`, components are measured compiled: your installed copy, so you measure what you ship. Memo-bailout findings become informational.\n\n```bash\nnpx 120fps ./Button.tsx                       # auto-detected\nnpx 120fps ./Button.tsx --no-react-compiler   # measure uncompiled\nnpx 120fps ./Button.tsx --react-compiler      # force on\n```\n\nThe terminal names the resolved target: `React Compiler: active (v1.0.0, target 18)`. When the installed React major's runtime module isn't resolvable, the transform is skipped instead of failing the run: `React Compiler: skipped (target 18: react-compiler-runtime not installed)`. The JSON `reactCompiler` object carries `target` and, on a skip, the reason.\n\nA component whose import graph reaches `react-native` (React Native Web apps such as Expo/RN-for-web projects) is measured through `react-native-web` when it's installed, the same substitution the app's own bundler makes; without it, the run stops naming React Native as the layer it cannot render and the file that imports it.\n\n## Vue\n\n```bash\nnpx 120fps ./Button.vue\nnpx 120fps \"src/components/**/*.vue\" --budget\n```\n\n- Needs `vue` and `@vitejs/plugin-vue` in *your* project: components compile against the versions they ship with.\n- Props from `defineProps<T>()` / `withDefaults` in `<script setup lang=\"ts\">`; imported types resolve. The runtime object form has no types → no props (still mounts and measures).\n- `rerender()` awaits `nextTick()`: the traced window contains Vue's actual DOM patch.\n- `120fps.setup.vue` wraps via its default slot; `.fixture.vue` for compounds.\n- No `--isolate strictmode` (React-only concept), no Vue optimization pass yet. Framework is part of the baseline fingerprint.\n- A read-of-undefined abort inside an ordinary SFC render frame gets a provide/inject hint pointing at `120fps.setup.vue` only when the component's own `<script setup>` block calls `inject(`; otherwise the abort prints no hint.\n\n## Tier Budgets\n\nAuto-classified from DOM complexity; portals/animation raise the floor to T3.\n\n| Tier | DOM nodes | Mount | Rerender | Interaction |\n|------|-----------|-------|----------|-------------|\n| T1   | ≤ 10      | 14 ms | 10 ms    | 250 ms      |\n| T2   | ≤ 40      | 44 ms | 30 ms    | 300 ms      |\n| T3   | portals/anim | 60 ms | 36 ms | 350 ms     |\n| T4   | > 40      | 80 ms | 48 ms    | 400 ms      |\n\n## Project transforms\n\n- The harness never reads your `vite.config`, but loads supported plugins (`vite-plugin-svgr`, `@vanilla-extract/vite-plugin`, `@vitejs/plugin-vue`) from your `node_modules`, dev-server hooks stripped.\n- Unloadable transforms are named with a stable code (`[transform:svgr]`) instead of failing deep inside Vite.\n- `--no-transforms` measures without them. Active transforms are recorded in the report and fingerprint.\n- The config's other declared plugins get one note per run naming what was dropped, in the config's own order: `vite.config.ts declares plugins the harness cannot honor: tanstackRouter, react — the project's Vite config is never executed.` A plugin whose transform the harness does apply (svgr, `@vitejs/plugin-vue`, vanilla-extract) is left out of the list, and the note is omitted entirely when nothing remains.\n- An import of a file type Vite can't load without a plugin (`.yaml`, `.yml`, `.toml`, `.md`, beside `.graphql`/`.gql`) is flagged before the build fails; the message names the loader plugin your project declares for that extension when it declares one, generic wording otherwise.\n- A `#`-prefixed specifier resolves through the nearest `package.json` `imports` field with Vite's own conditions; one that resolves to a file in your repo is measured as part of the graph, never flagged as missing. Nuxt's `#build`/`#imports`/`#app` diagnosis fires only when `nuxt` is actually declared in the project; otherwise the message names the missing specifier and the map that should declare it.\n- An unbuilt workspace sibling is aliased to its own source entry (`source`, `exports`' `development`/`source`/`import` conditions, `module`/`main`, `types`, or `<pkg>/src`, in that order) instead of failing with a \"needs a build step\" diagnosis; the warning names the manifest field it followed and the path it resolved to. A sibling that declares no runtime entry at all is disclosed as types-only, with no `dist/` claim.\n\n## Environment variables\n\n`process.env` on the harness page is built from `.env` and `.env.local` files at the measured project's own root and, in a workspace, its workspace root too — read, never written. Only `NEXT_PUBLIC_*` and `VITE_*`-prefixed keys are forwarded, matching what a real Next.js or Vite production build actually exposes to the browser. The invoking shell's own environment is never read: a component that reads an unprefixed or shell-only variable, or `process.env` itself outside those files, measures `undefined`, the same as it would in production.\n\n## Remediation\n\nEach finding prints its hint once per run; ids are in the JSON `hints`.\n\n### Memo bailout\n\nA memoized child re-rendered on equal-looking props: something is a new reference each render. Hoist the literal or `useMemo` it.\n\n### Context fan-out\n\nEvery consumer re-renders on provider value identity. `useMemo` the value; split value and setter contexts.\n\n### Callback identity\n\nInline arrow = new function every render = broken `memo` on the child. `useCallback`, or move the handler out.\n\n### Portal orphans\n\nPortalled nodes survived unmount. Clean up the container in the effect that created it: React removes what it rendered, not host nodes you appended.\n\n### Leak suspected\n\nSomething outlives the component. Return cleanups for listeners, timers, observers, subscriptions; abort in-flight requests.\n\n### Churn degradation\n\nLater rerenders cost more than early ones: state accumulates. Check for append-only arrays/maps and unremoved subscriptions.\n\n### Superlinear growth\n\nDoubling input more than doubles time: per-item work touches every other item. Look for `filter`/`find`/`includes` inside a `map`, and layout reads interleaved with writes.\n\n### Scaling curves\n\nScale points measured but DOM count never moved: the curve describes nothing rendered. Check the prop drives rendering, or point `--curve prop:type` at the one that does.\n\n### Render errors\n\nPage errors during a combo are printed under `Page errors`; zero DOM + a throw = `FAIL [render error]`: the timings describe a broken tree, not your component. Usual causes: missing provider (`--wrap`), unpopulatable prop (`<stem>.props.tsx` preset). Rendering nothing *without* throwing is legal and only annotated.\n\n### Harness fault\n\nA combo can crash on a value 120fps chose for you rather than a value your code passed — most often a boolean like `asChild`/`as`/`render` whose `true` branch requires a specific `children` shape the synthesizer could not guarantee, or a placeholder value that turns out to appear verbatim in the thrown error. That combo is marked `[harness fault: <prop>]`, its verdict is never `FAIL`, and `Result: PASS` is unaffected even when it is the only combo that crashed. Add a `<stem>.props.tsx` preset naming the prop if you want that combo measured with a real value instead of excluded.\n\n### Async wrapper setup\n\nA component still fetching when the window closed is measured as its skeleton (and disclosed). Stub requests before first render:\n\n```tsx\n// 120fps.setup.tsx\nexport function setup() {\n  window.fetch = (async () => new Response(JSON.stringify(fixtureData))) as typeof window.fetch;\n}\n\nexport default function Wrapper({ children }: { children: React.ReactNode }) {\n  return <Providers>{children}</Providers>;\n}\n```\n\n`setup` may be async and is awaited before measurement; optional `teardown` runs at session close.\n\n## Requirements\n\n- Node >= 22\n- React `>=18` in the profiled project (React mode); `vue` + `@vitejs/plugin-vue` (Vue mode); vanilla needs neither\n- `tsconfig.json` optional: nearest one wins, sane fallback otherwise. When the nearest config declares no `compilerOptions` and only `references`, the referenced config that covers the file supplies `paths`/`baseUrl`/`jsxImportSource`/`customConditions` instead, disclosed once naming both config paths. `.ts`/`.tsx`/`.js`/`.jsx` always compile with the automatic JSX runtime, whatever the config's own `jsx` setting is. A `paths` key with no non-wildcard prefix (`\"/*\"`, `\"*\"`) builds no alias and is reported once instead of applied.\n- `.scss`/`.sass` compiles with the project's own Sass when it resolves, otherwise with the copy 120fps ships, disclosed once naming the version; `.less` and `.styl`/`.stylus` need the project's own package, since 120fps bundles no implementation for either.\n- Chromium via Playwright: run `npx playwright install chromium` once after installing (the Playwright package downloads no browser on `npm install`); a run without the browser stops with that command as its hint\n\n## License\n\nMIT\n","readmeFilename":"README.md"}