Rocky_Mountain_Vending/.pnpm-store/v10/files/2d/aaba87108693cc3735a938b6313514987917e70073699768e9c572af44e9dda3a3ffede03d6a07729b4edb83c2acb2c318a9e20c7448fd641139640465ea6f
DMleadgen 46d973904b
Initial commit: Rocky Mountain Vending website
Next.js website for Rocky Mountain Vending company featuring:
- Product catalog with Stripe integration
- Service areas and parts pages
- Admin dashboard with Clerk authentication
- SEO optimized pages with JSON-LD structured data

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 16:22:15 -07:00

1 line
No EOL
38 KiB
Text

{"version":3,"file":"NetworkAnalyzer.js","sourceRoot":"","sources":["../../../../../../../../front_end/models/trace/lantern/core/NetworkAnalyzer.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,yEAAyE;AACzE,6BAA6B;AAI7B,OAAO,EAAC,YAAY,EAAC,MAAM,mBAAmB,CAAC;AAE/C,MAAM,QAAQ;IACZ;;;;OAIG;IACH,MAAM,CAAC,wBAAwB,CAAC,GAAW;QACzC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;YAClC,OAAO,GAAG,CAAC;QACb,CAAC;QACD,2EAA2E;QAC3E,+EAA+E;QAC/E,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACtB,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAC/B,CAAC;QACD,OAAO,GAAG,CAAC,OAAO,CAAC,sBAAsB,EAAE,WAAW,CAAC,CAAC;IAC1D,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,0BAA0B,CAAC,IAAY,EAAE,IAAY;QAC1D,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;QAC/D,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC;YAC3B,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC;YAEf,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC;YAC3B,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC;YAEf,OAAO,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC;QACjC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CACF;AAmCD,MAAM,WAAW,GAAG,EAAE,GAAG,IAAI,CAAC;AAE9B,gFAAgF;AAChF,MAAM,kCAAkC,GAAG,GAAG,CAAC;AAE/C;;;GAGG;AACH,MAAM,kCAAkC,GAAkD;IACxF,QAAQ,EAAE,GAAG;IACb,GAAG,EAAE,GAAG;IACR,KAAK,EAAE,GAAG;CACX,CAAC;AAEF,MAAM,eAAe;IACnB,MAAM,KAAK,OAAO;QAChB,OAAO,aAAa,CAAC;IACvB,CAAC;IAED,MAAM,CAAC,aAAa,CAAC,OAAiC;QACpD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAE,CAAC;QAC1B,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;YACrB,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC;YAC1C,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;YACrC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjB,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC1B,CAAC,CAAC,CAAC;QACH,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,MAAM,CAAC,UAAU,CAAC,MAAgB;QAChC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAE7B,IAAI,MAAM,CAAC;QACX,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACrB,CAAC;aAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACnC,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACtD,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC1D,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;QACvB,CAAC;aAAM,CAAC;YACN,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACvD,CAAC;QAED,OAAO;YACL,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;YACd,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;YAC9B,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM;YACtD,MAAM;SACP,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,SAAS,CAAC,MAA6B;QAC5C,MAAM,YAAY,GAAG,IAAI,GAAG,EAAE,CAAC;QAC/B,MAAM,YAAY,GAAG,EAAE,CAAC;QACxB,KAAK,MAAM,CAAC,GAAG,EAAE,SAAS,CAAC,IAAI,MAAM,EAAE,CAAC;YACtC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,eAAe,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC;YAC7D,YAAY,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC;QAClC,CAAC;QAED,YAAY,CAAC,GAAG,CAAC,eAAe,CAAC,OAAO,EAAE,eAAe,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC;QACpF,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,MAAM,CAAC,qBAAqB,CACxB,QAAkC,EAClC,QAA2D;QAC7D,MAAM,mBAAmB,GAAG,eAAe,CAAC,6BAA6B,CAAC,QAAQ,CAAC,CAAC;QACpF,MAAM,eAAe,GAAG,eAAe,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAEhE,MAAM,SAAS,GAAG,IAAI,GAAG,EAAE,CAAC;QAC5B,KAAK,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,IAAI,eAAe,CAAC,OAAO,EAAE,EAAE,CAAC;YACjE,IAAI,eAAe,GAAa,EAAE,CAAC;YAEnC,KAAK,MAAM,OAAO,IAAI,cAAc,EAAE,CAAC;gBACrC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;gBAC9B,IAAI,CAAC,MAAM,EAAE,CAAC;oBACZ,SAAS;gBACX,CAAC;gBAED,MAAM,KAAK,GAAG,QAAQ,CAAC;oBACrB,OAAO;oBACP,MAAM;oBACN,gBAAgB,EAAE,mBAAmB,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC;iBAC7D,CAAC,CAAC;gBACH,IAAI,OAAO,KAAK,KAAK,WAAW,EAAE,CAAC;oBACjC,eAAe,GAAG,eAAe,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBAClD,CAAC;YACH,CAAC;YAED,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,CAAC;gBAC5B,SAAS;YACX,CAAC;YACD,SAAS,CAAC,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;QACzC,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC;IAED;;;;;;;OAOG;IACH,MAAM,CAAC,8BAA8B,CAAC,IAAiB;QACrD,MAAM,EAAC,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAC,GAAG,IAAI,CAAC;QACjD,IAAI,gBAAgB,EAAE,CAAC;YACrB,OAAO;QACT,CAAC;QAED,MAAM,EAAC,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAC,GAAG,MAAM,CAAC;QAC5D,IAAI,UAAU,IAAI,CAAC,IAAI,YAAY,IAAI,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9E,wDAAwD;YACxD,OAAO,UAAU,GAAG,YAAY,CAAC;QACnC,CAAC;QACD,IAAI,QAAQ,IAAI,CAAC,IAAI,MAAM,IAAI,CAAC,IAAI,QAAQ,KAAK,YAAY,EAAE,CAAC;YAC9D,kEAAkE;YAClE,OAAO,CAAC,UAAU,GAAG,QAAQ,EAAE,QAAQ,GAAG,YAAY,CAAC,CAAC;QAC1D,CAAC;QACD,IAAI,YAAY,IAAI,CAAC,IAAI,UAAU,IAAI,CAAC,EAAE,CAAC;YACzC,OAAO,UAAU,GAAG,YAAY,CAAC;QACnC,CAAC;QAED,OAAO;IACT,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,4BAA4B,CAAC,IAAiB;QACnD,MAAM,EAAC,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAC,GAAG,IAAI,CAAC;QACjD,IAAI,gBAAgB,EAAE,CAAC;YACrB,OAAO;QACT,CAAC;QAED,sEAAsE;QACtE,IAAI,OAAO,CAAC,YAAY,IAAI,WAAW,EAAE,CAAC;YACxC,OAAO;QACT,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,iBAAiB,CAAC,IAAI,MAAM,CAAC,iBAAiB,GAAG,CAAC,EAAE,CAAC;YAC/E,OAAO;QACT,CAAC;QAED,2FAA2F;QAC3F,MAAM,SAAS,GAAG,OAAO,CAAC,cAAc,GAAG,OAAO,CAAC,kBAAkB,CAAC;QACtE,MAAM,0BAA0B,GAAG,SAAS,GAAG,MAAM,CAAC,iBAAiB,CAAC;QACxE,MAAM,kBAAkB,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,GAAG,WAAW,CAAC,CAAC;QAEzE,4FAA4F;QAC5F,6BAA6B;QAC7B,IAAI,kBAAkB,GAAG,CAAC,EAAE,CAAC;YAC3B,OAAO;QACT,CAAC;QAED,OAAO,0BAA0B,GAAG,kBAAkB,CAAC;IACzD,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,6BAA6B,CAAC,IAAiB;QACpD,MAAM,EAAC,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAC,GAAG,IAAI,CAAC;QACjD,IAAI,gBAAgB,EAAE,CAAC;YACrB,OAAO;QACT,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,MAAM,CAAC,SAAS,GAAG,CAAC,EAAE,CAAC;YAC/D,OAAO;QACT,CAAC;QAED,2EAA2E;QAC3E,mDAAmD;QACnD,IAAI,UAAU,GAAG,CAAC,CAAC;QACnB,MAAM;QACN,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACvC,UAAU,IAAI,CAAC,CAAC;QAClB,CAAC;QACD,IAAI,OAAO,CAAC,SAAS,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;YACzC,UAAU,IAAI,CAAC,CAAC;QAClB,CAAC;QACD,OAAO,MAAM,CAAC,SAAS,GAAG,UAAU,CAAC;IACvC,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,8BAA8B,CAAC,IAAiB;QACrD,MAAM,EAAC,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAC,GAAG,IAAI,CAAC;QACjD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,iBAAiB,CAAC,IAAI,MAAM,CAAC,iBAAiB,GAAG,CAAC,EAAE,CAAC;YAC/E,OAAO;QACT,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC;YAC1B,OAAO;QACT,CAAC;QAED,MAAM,4BAA4B,GAC9B,kCAAkC,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,kCAAkC,CAAC;QACnG,MAAM,2BAA2B,GAAG,MAAM,CAAC,iBAAiB,GAAG,4BAA4B,CAAC;QAE5F,gCAAgC;QAChC,iDAAiD;QACjD,IAAI,UAAU,GAAG,CAAC,CAAC;QAEnB,+BAA+B;QAC/B,gFAAgF;QAChF,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACtB,UAAU,IAAI,CAAC,CAAC,CAAE,MAAM;YACxB,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;gBACvC,UAAU,IAAI,CAAC,CAAC,CAAE,MAAM;YAC1B,CAAC;YACD,IAAI,OAAO,CAAC,SAAS,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;gBACzC,UAAU,IAAI,CAAC,CAAC,CAAE,MAAM;YAC1B,CAAC;QACH,CAAC;QAED,kDAAkD;QAClD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,iBAAiB,GAAG,2BAA2B,CAAC,GAAG,UAAU,EAAE,CAAC,CAAC,CAAC;IAC5F,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,4BAA4B,CAAC,OAAiC,EAAE,WAAgC;QAErG,OAAO,eAAe,CAAC,qBAAqB,CAAC,OAAO,EAAE,CAAC,EAAC,OAAO,EAAE,MAAM,EAAC,EAAE,EAAE;YAC1E,IAAI,OAAO,CAAC,kBAAkB,KAAK,SAAS,EAAE,CAAC;gBAC7C,OAAO,OAAO,CAAC,kBAAkB,CAAC;YACpC,CAAC;YAED,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,iBAAiB,CAAC,IAAI,MAAM,CAAC,iBAAiB,GAAG,CAAC,EAAE,CAAC;gBAC/E,OAAO;YACT,CAAC;YACD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,GAAG,CAAC,EAAE,CAAC;gBAC3D,OAAO;YACT,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,CAAC,iBAAiB,GAAG,MAAM,CAAC,OAAO,CAAC;YACvD,MAAM,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,cAAc,CAAC;YAChD,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,WAAW,CAAC,GAAG,CAAC,eAAe,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACrF,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,CAAC,6BAA6B,CAAC,QAAkC;QACrE,MAAM,sBAAsB,GAAG,IAAI,GAAG,EAAE,CAAC;QACzC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,MAAM,OAAO,GAAG,sBAAsB,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC;YAC9F,sBAAsB,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QAC5D,CAAC;QAED,0FAA0F;QAC1F,IAAI,sBAAsB,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC;YACrC,OAAO,KAAK,CAAC;QACf,CAAC;QACD,wGAAwG;QACxG,OAAO,KAAK,CAAC,IAAI,CAAC,sBAAsB,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;IAC/E,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,6BAA6B,CAAC,OAAiC,EAAE,OAAyC;QAE/G,MAAM,EAAC,oBAAoB,GAAG,KAAK,EAAC,GAAG,OAAO,IAAI,EAAE,CAAC;QAErD,4EAA4E;QAC5E,IAAI,CAAC,oBAAoB,IAAI,eAAe,CAAC,6BAA6B,CAAC,OAAO,CAAC,EAAE,CAAC;YACpF,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;QACjG,CAAC;QAED,qFAAqF;QACrF,iDAAiD;QACjD,gBAAgB;QAChB,yDAAyD;QACzD,MAAM,mBAAmB,GAAG,IAAI,GAAG,EAAE,CAAC;QACtC,MAAM,eAAe,GAAG,eAAe,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAC/D,KAAK,MAAM,aAAa,IAAI,eAAe,CAAC,MAAM,EAAE,EAAE,CAAC;YACrD,MAAM,qBAAqB,GACvB,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;YAEpG,KAAK,MAAM,OAAO,IAAI,aAAa,EAAE,CAAC;gBACpC,mBAAmB,CAAC,GAAG,CACnB,OAAO,CAAC,SAAS,EACjB,OAAO,CAAC,kBAAkB,IAAI,qBAAqB,IAAI,OAAO,CAAC,QAAQ,KAAK,IAAI,CACnF,CAAC;YACJ,CAAC;YAED,MAAM,WAAW,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;gBAChD,OAAO,CAAC,CAAC,kBAAkB,GAAG,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7D,CAAC,CAAC,CAAC;YACH,mBAAmB,CAAC,GAAG,CAAC,WAAW,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QACxD,CAAC;QAED,OAAO,mBAAmB,CAAC;IAC7B,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,mBAAmB,CAAC,OAAiC,EAAE,OAA4B;QACxF,MAAM,EACJ,oBAAoB,GAAG,KAAK;QAC5B,wDAAwD;QACxD,0DAA0D;QAC1D,wBAAwB,GAAG,GAAG,EAC9B,oBAAoB,GAAG,IAAI,EAC3B,qBAAqB,GAAG,IAAI,EAC5B,sBAAsB,GAAG,IAAI,GAC9B,GAAG,OAAO,IAAI,EAAE,CAAC;QAElB,MAAM,mBAAmB,GAAG,eAAe,CAAC,6BAA6B,CAAC,OAAO,CAAC,CAAC;QACnF,MAAM,eAAe,GAAG,eAAe,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAE/D,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAAE,CAAC;QACpC,KAAK,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,IAAI,eAAe,CAAC,OAAO,EAAE,EAAE,CAAC;YACjE,MAAM,eAAe,GAAa,EAAE,CAAC;YAErC,SAAS,gBAAgB,CAAC,SAA4D,EAAE,UAAU,GAAG,CAAC;gBACpG,KAAK,MAAM,OAAO,IAAI,cAAc,EAAE,CAAC;oBACrC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;oBAC9B,IAAI,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC;wBACrC,SAAS;oBACX,CAAC;oBAED,MAAM,SAAS,GAAG,SAAS,CAAC;wBAC1B,OAAO;wBACP,MAAM;wBACN,gBAAgB,EAAE,mBAAmB,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC;qBAC7D,CAAC,CAAC;oBACH,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;wBAC5B,SAAS;oBACX,CAAC;oBAED,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;wBAC9B,eAAe,CAAC,IAAI,CAAC,SAAS,GAAG,UAAU,CAAC,CAAC;oBAC/C,CAAC;yBAAM,CAAC;wBACN,eAAe,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC;oBAC9D,CAAC;gBACH,CAAC;YACH,CAAC;YAED,IAAI,CAAC,oBAAoB,EAAE,CAAC;gBAC1B,gBAAgB,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;YACxD,CAAC;YAED,sDAAsD;YACtD,sEAAsE;YACtE,0GAA0G;YAC1G,2GAA2G;YAC3G,iCAAiC;YACjC,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,CAAC;gBAC5B,IAAI,oBAAoB,EAAE,CAAC;oBACzB,gBAAgB,CAAC,IAAI,CAAC,4BAA4B,EAAE,wBAAwB,CAAC,CAAC;gBAChF,CAAC;gBACD,IAAI,qBAAqB,EAAE,CAAC;oBAC1B,gBAAgB,CAAC,IAAI,CAAC,6BAA6B,EAAE,wBAAwB,CAAC,CAAC;gBACjF,CAAC;gBACD,IAAI,sBAAsB,EAAE,CAAC;oBAC3B,gBAAgB,CAAC,IAAI,CAAC,8BAA8B,EAAE,wBAAwB,CAAC,CAAC;gBAClF,CAAC;YACH,CAAC;YAED,IAAI,eAAe,CAAC,MAAM,EAAE,CAAC;gBAC3B,iBAAiB,CAAC,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;YACjD,CAAC;QACH,CAAC;QAED,IAAI,CAAC,iBAAiB,CAAC,IAAI,EAAE,CAAC;YAC5B,MAAM,IAAI,YAAY,CAAC,iCAAiC,CAAC,CAAC;QAC5D,CAAC;QACD,OAAO,eAAe,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IACtD,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,kCAAkC,CAAC,OAAiC,EAAE,OAE5E;QACC,IAAI,WAAW,GAAG,OAAO,EAAE,WAAW,CAAC;QACvC,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,WAAW,GAAG,IAAI,GAAG,EAAE,CAAC;YAExB,MAAM,kBAAkB,GAAG,eAAe,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YACjF,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,kBAAkB,CAAC,OAAO,EAAE,EAAE,CAAC;gBAC7D,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;YACvC,CAAC;QACH,CAAC;QAED,MAAM,iBAAiB,GAAG,eAAe,CAAC,4BAA4B,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;QAC7F,OAAO,eAAe,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IACtD,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,kBAAkB,CAAC,OAAiC;QACzD,IAAI,UAAU,GAAG,CAAC,CAAC;QAEnB,2FAA2F;QAC3F,8FAA8F;QAC9F,oDAAoD;QACpD,MAAM,cAAc,GAAG,OAAO;aACF,MAAM,CACH,CAAC,UAAU,EAAE,OAAO,EAAE,EAAE;YACtB,MAAM,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC;YACzC,2FAA2F;YAC3F,6CAA6C;YAC7C,IAAI,MAAM,KAAK,MAAM,IAAI,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ;gBACxD,OAAO,CAAC,UAAU,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC;gBACtD,OAAO,UAAU,CAAC;YACpB,CAAC;YAED,4FAA4F;YAC5F,UAAU,IAAI,OAAO,CAAC,YAAY,CAAC;YACnC,UAAU,CAAC,IAAI,CAAC,EAAC,IAAI,EAAE,OAAO,CAAC,sBAAsB,GAAG,IAAI,EAAE,OAAO,EAAE,IAAI,EAAC,CAAC,CAAC;YAC9E,UAAU,CAAC,IAAI,CAAC,EAAC,IAAI,EAAE,OAAO,CAAC,cAAc,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,EAAC,CAAC,CAAC;YACvE,OAAO,UAAU,CAAC;QACpB,CAAC,EACD,EAA6C,CAAC;aACjD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;QAE5D,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,CAAC;YAC3B,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,QAAQ,GAAG,CAAC,CAAC;QACjB,IAAI,YAAY,GAAG,CAAC,CAAC;QACrB,IAAI,aAAa,GAAG,CAAC,CAAC;QAEtB,cAAc,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE;YAChC,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;gBACrB,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;oBACnB,+EAA+E;oBAC/E,YAAY,GAAG,QAAQ,CAAC,IAAI,CAAC;gBAC/B,CAAC;gBACD,QAAQ,EAAE,CAAC;YACb,CAAC;iBAAM,CAAC;gBACN,QAAQ,EAAE,CAAC;gBACX,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;oBACnB,yFAAyF;oBACzF,aAAa,IAAI,QAAQ,CAAC,IAAI,GAAG,YAAY,CAAC;gBAChD,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,OAAO,UAAU,GAAG,CAAC,GAAG,aAAa,CAAC;IACxC,CAAC;IAED,MAAM,CAAC,+BAA+B,CAAC,OAAiC;QAEtE,0EAA0E;QAC1E,MAAM,WAAW,GAAG,IAAI,GAAG,EAAkB,CAAC;QAC9C,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,eAAe,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;YACvF,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;QACvC,CAAC;QAED,kGAAkG;QAClG,gGAAgG;QAChG,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACjE,mFAAmF;QACnF,MAAM,qBAAqB,GAAG,eAAe,CAAC,kCAAkC,CAAC,OAAO,EAAE;YACxF,WAAW;SACZ,CAAC,CAAC;QAEH,MAAM,qBAAqB,GAAG,IAAI,GAAG,EAAkB,CAAC;QACxD,MAAM,0BAA0B,GAAG,IAAI,GAAG,EAAkB,CAAC;QAC7D,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,qBAAqB,CAAC,OAAO,EAAE,EAAE,CAAC;YAChE,yFAAyF;YACzF,MAAM,YAAY,GAAG,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,UAAU,CAAC;YAC3D,qBAAqB,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,GAAG,UAAU,CAAC,CAAC;YAC7D,0BAA0B,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QACzD,CAAC;QAED,OAAO;YACL,GAAG,EAAE,UAAU;YACf,qBAAqB;YACrB,0BAA0B;SAC3B,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,OAAO,CAAC,OAAiC;QAC9C,MAAM,UAAU,GAAG,eAAe,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;QAC/D,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;YACxB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO;YACL,UAAU;YACV,GAAG,eAAe,CAAC,+BAA+B,CAAC,OAAO,CAAC;SAC5D,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,kBAAkB,CAAmC,OAAY,EAAE,WAAmB;QAC3F,2GAA2G;QAC3G,OAAO,OAAO,CAAC,IAAI,CACf,OAAO,CAAC,EAAE,CAAC,WAAW,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,0BAA0B,CAAC,OAAO,CAAC,GAAG,EAAE,WAAW,CAAC,CAClH,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,sBAAsB,CAAmC,OAAY,EAAE,WAAmB;QAC/F,2GAA2G;QAC3G,MAAM,gBAAgB,GAAG,OAAO,CAAC,MAAM,CACnC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,YAAY,KAAK,UAAU,IAAI,CAAC,OAAO,CAAC,MAAM;YAC7D,gGAAgG;YAChG,WAAW,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,0BAA0B,CAAC,OAAO,CAAC,GAAG,EAAE,WAAW,CAAC,CAC3G,CAAC;QACF,OAAO,gBAAgB,CAAC,gBAAgB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACvD,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,gBAAgB,CAAmC,OAAU;QAClE,OAAO,OAAO,CAAC,mBAAmB,EAAE,CAAC;YACnC,OAAO,GAAG,OAAO,CAAC,mBAAwB,CAAC;QAC7C,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;CACF;AAED,OAAO,EAAC,eAAe,EAAC,CAAC","sourcesContent":["// Copyright 2024 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport type * as Lantern from '../types/types.js';\n\nimport {LanternError} from './LanternError.js';\n\nclass UrlUtils {\n /**\n * There is fancy URL rewriting logic for the chrome://settings page that we need to work around.\n * Why? Special handling was added by Chrome team to allow a pushState transition between chrome:// pages.\n * As a result, the network URL (chrome://chrome/settings/) doesn't match the final document URL (chrome://settings/).\n */\n static rewriteChromeInternalUrl(url: string): string {\n if (!url?.startsWith('chrome://')) {\n return url;\n }\n // Chrome adds a trailing slash to `chrome://` URLs, but the spec does not.\n // https://github.com/GoogleChrome/lighthouse/pull/3941#discussion_r154026009\n if (url.endsWith('/')) {\n url = url.replace(/\\/$/, '');\n }\n return url.replace(/^chrome:\\/\\/chrome\\//, 'chrome://');\n }\n\n /**\n * Determine if url1 equals url2, ignoring URL fragments.\n */\n static equalWithExcludedFragments(url1: string, url2: string): boolean {\n [url1, url2] = [url1, url2].map(this.rewriteChromeInternalUrl);\n try {\n const urla = new URL(url1);\n urla.hash = '';\n\n const urlb = new URL(url2);\n urlb.hash = '';\n\n return urla.href === urlb.href;\n } catch {\n return false;\n }\n }\n}\n\ninterface Summary {\n min: number;\n max: number;\n avg: number;\n median: number;\n}\n\ninterface RTTEstimateOptions {\n /**\n * TCP connection handshake information will be used when available, but in\n * some circumstances this data can be unreliable. This flag exposes an\n * option to ignore the handshake data and use the coarse download/TTFB timing data.\n */\n forceCoarseEstimates?: boolean;\n /**\n * Coarse estimates include lots of extra time and noise multiply by some factor\n * to deflate the estimates a bit.\n */\n coarseEstimateMultiplier?: number;\n /** Useful for testing to isolate the different methods of estimation. */\n useDownloadEstimates?: boolean;\n /** Useful for testing to isolate the different methods of estimation. */\n useSendStartEstimates?: boolean;\n /** Useful for testing to isolate the different methods of estimation. */\n useHeadersEndEstimates?: boolean;\n}\n\ninterface RequestInfo {\n request: Lantern.NetworkRequest;\n timing: Lantern.ResourceTiming;\n connectionReused?: boolean;\n}\n\nconst INITIAL_CWD = 14 * 1024;\n\n// Assume that 40% of TTFB was server response time by default for static assets\nconst DEFAULT_SERVER_RESPONSE_PERCENTAGE = 0.4;\n\n/**\n * For certain resource types, server response time takes up a greater percentage of TTFB (dynamic\n * assets like HTML documents, XHR/API calls, etc)\n */\nconst SERVER_RESPONSE_PERCENTAGE_OF_TTFB: Partial<Record<Lantern.ResourceType, number>> = {\n Document: 0.9,\n XHR: 0.9,\n Fetch: 0.9,\n};\n\nclass NetworkAnalyzer {\n static get summary(): string {\n return '__SUMMARY__';\n }\n\n static groupByOrigin(records: Lantern.NetworkRequest[]): Map<string, Lantern.NetworkRequest[]> {\n const grouped = new Map();\n records.forEach(item => {\n const key = item.parsedURL.securityOrigin;\n const group = grouped.get(key) || [];\n group.push(item);\n grouped.set(key, group);\n });\n return grouped;\n }\n\n static getSummary(values: number[]): Summary {\n values.sort((a, b) => a - b);\n\n let median;\n if (values.length === 0) {\n median = values[0];\n } else if (values.length % 2 === 0) {\n const a = values[Math.floor((values.length - 1) / 2)];\n const b = values[Math.floor((values.length - 1) / 2) + 1];\n median = (a + b) / 2;\n } else {\n median = values[Math.floor((values.length - 1) / 2)];\n }\n\n return {\n min: values[0],\n max: values[values.length - 1],\n avg: values.reduce((a, b) => a + b, 0) / values.length,\n median,\n };\n }\n\n static summarize(values: Map<string, number[]>): Map<string, Summary> {\n const summaryByKey = new Map();\n const allEstimates = [];\n for (const [key, estimates] of values) {\n summaryByKey.set(key, NetworkAnalyzer.getSummary(estimates));\n allEstimates.push(...estimates);\n }\n\n summaryByKey.set(NetworkAnalyzer.summary, NetworkAnalyzer.getSummary(allEstimates));\n return summaryByKey;\n }\n\n static estimateValueByOrigin(\n requests: Lantern.NetworkRequest[],\n iteratee: (e: RequestInfo) => number | number[] | undefined): Map<string, number[]> {\n const connectionWasReused = NetworkAnalyzer.estimateIfConnectionWasReused(requests);\n const groupedByOrigin = NetworkAnalyzer.groupByOrigin(requests);\n\n const estimates = new Map();\n for (const [origin, originRequests] of groupedByOrigin.entries()) {\n let originEstimates: number[] = [];\n\n for (const request of originRequests) {\n const timing = request.timing;\n if (!timing) {\n continue;\n }\n\n const value = iteratee({\n request,\n timing,\n connectionReused: connectionWasReused.get(request.requestId),\n });\n if (typeof value !== 'undefined') {\n originEstimates = originEstimates.concat(value);\n }\n }\n\n if (!originEstimates.length) {\n continue;\n }\n estimates.set(origin, originEstimates);\n }\n\n return estimates;\n }\n\n /**\n * Estimates the observed RTT to each origin based on how long the connection setup.\n * For h1 and h2, this could includes two estimates - one for the TCP handshake, another for\n * SSL negotiation.\n * For h3, we get only one estimate since QUIC establishes a secure connection in a\n * single handshake.\n * This is the most accurate and preferred method of measurement when the data is available.\n */\n static estimateRTTViaConnectionTiming(info: RequestInfo): number[]|number|undefined {\n const {timing, connectionReused, request} = info;\n if (connectionReused) {\n return;\n }\n\n const {connectStart, sslStart, sslEnd, connectEnd} = timing;\n if (connectEnd >= 0 && connectStart >= 0 && request.protocol.startsWith('h3')) {\n // These values are equal to sslStart and sslEnd for h3.\n return connectEnd - connectStart;\n }\n if (sslStart >= 0 && sslEnd >= 0 && sslStart !== connectStart) {\n // SSL can also be more than 1 RT but assume False Start was used.\n return [connectEnd - sslStart, sslStart - connectStart];\n }\n if (connectStart >= 0 && connectEnd >= 0) {\n return connectEnd - connectStart;\n }\n\n return;\n }\n\n /**\n * Estimates the observed RTT to each origin based on how long a download took on a fresh connection.\n * NOTE: this will tend to overestimate the actual RTT quite significantly as the download can be\n * slow for other reasons as well such as bandwidth constraints.\n */\n static estimateRTTViaDownloadTiming(info: RequestInfo): number|undefined {\n const {timing, connectionReused, request} = info;\n if (connectionReused) {\n return;\n }\n\n // Only look at downloads that went past the initial congestion window\n if (request.transferSize <= INITIAL_CWD) {\n return;\n }\n if (!Number.isFinite(timing.receiveHeadersEnd) || timing.receiveHeadersEnd < 0) {\n return;\n }\n\n // Compute the amount of time downloading everything after the first congestion window took\n const totalTime = request.networkEndTime - request.networkRequestTime;\n const downloadTimeAfterFirstByte = totalTime - timing.receiveHeadersEnd;\n const numberOfRoundTrips = Math.log2(request.transferSize / INITIAL_CWD);\n\n // Ignore requests that required a high number of round trips since bandwidth starts to play\n // a larger role than latency\n if (numberOfRoundTrips > 5) {\n return;\n }\n\n return downloadTimeAfterFirstByte / numberOfRoundTrips;\n }\n\n /**\n * Estimates the observed RTT to each origin based on how long it took until Chrome could\n * start sending the actual request when a new connection was required.\n * NOTE: this will tend to overestimate the actual RTT as the request can be delayed for other\n * reasons as well such as more SSL handshakes if TLS False Start is not enabled.\n */\n static estimateRTTViaSendStartTiming(info: RequestInfo): number|undefined {\n const {timing, connectionReused, request} = info;\n if (connectionReused) {\n return;\n }\n\n if (!Number.isFinite(timing.sendStart) || timing.sendStart < 0) {\n return;\n }\n\n // Assume everything before sendStart was just DNS + (SSL)? + TCP handshake\n // 1 RT for DNS, 1 RT (maybe) for SSL, 1 RT for TCP\n let roundTrips = 1;\n // TCP\n if (!request.protocol.startsWith('h3')) {\n roundTrips += 1;\n }\n if (request.parsedURL.scheme === 'https') {\n roundTrips += 1;\n }\n return timing.sendStart / roundTrips;\n }\n\n /**\n * Estimates the observed RTT to each origin based on how long it took until Chrome received the\n * headers of the response (~TTFB).\n * NOTE: this is the most inaccurate way to estimate the RTT, but in some environments it's all\n * we have access to :(\n */\n static estimateRTTViaHeadersEndTiming(info: RequestInfo): number|undefined {\n const {timing, connectionReused, request} = info;\n if (!Number.isFinite(timing.receiveHeadersEnd) || timing.receiveHeadersEnd < 0) {\n return;\n }\n if (!request.resourceType) {\n return;\n }\n\n const serverResponseTimePercentage =\n SERVER_RESPONSE_PERCENTAGE_OF_TTFB[request.resourceType] || DEFAULT_SERVER_RESPONSE_PERCENTAGE;\n const estimatedServerResponseTime = timing.receiveHeadersEnd * serverResponseTimePercentage;\n\n // When connection was reused...\n // TTFB = 1 RT for request + server response time\n let roundTrips = 1;\n\n // When connection was fresh...\n // TTFB = DNS + (SSL)? + TCP handshake + 1 RT for request + server response time\n if (!connectionReused) {\n roundTrips += 1; // DNS\n if (!request.protocol.startsWith('h3')) {\n roundTrips += 1; // TCP\n }\n if (request.parsedURL.scheme === 'https') {\n roundTrips += 1; // SSL\n }\n }\n\n // subtract out our estimated server response time\n return Math.max((timing.receiveHeadersEnd - estimatedServerResponseTime) / roundTrips, 3);\n }\n\n /**\n * Given the RTT to each origin, estimates the observed server response times.\n */\n static estimateResponseTimeByOrigin(records: Lantern.NetworkRequest[], rttByOrigin: Map<string, number>):\n Map<string, number[]> {\n return NetworkAnalyzer.estimateValueByOrigin(records, ({request, timing}) => {\n if (request.serverResponseTime !== undefined) {\n return request.serverResponseTime;\n }\n\n if (!Number.isFinite(timing.receiveHeadersEnd) || timing.receiveHeadersEnd < 0) {\n return;\n }\n if (!Number.isFinite(timing.sendEnd) || timing.sendEnd < 0) {\n return;\n }\n\n const ttfb = timing.receiveHeadersEnd - timing.sendEnd;\n const origin = request.parsedURL.securityOrigin;\n const rtt = rttByOrigin.get(origin) || rttByOrigin.get(NetworkAnalyzer.summary) || 0;\n return Math.max(ttfb - rtt, 0);\n });\n }\n\n static canTrustConnectionInformation(requests: Lantern.NetworkRequest[]): boolean {\n const connectionIdWasStarted = new Map();\n for (const request of requests) {\n const started = connectionIdWasStarted.get(request.connectionId) || !request.connectionReused;\n connectionIdWasStarted.set(request.connectionId, started);\n }\n\n // We probably can't trust the network information if all the connection IDs were the same\n if (connectionIdWasStarted.size <= 1) {\n return false;\n }\n // Or if there were connections that were always reused (a connection had to have started at some point)\n return Array.from(connectionIdWasStarted.values()).every(started => started);\n }\n\n /**\n * Returns a map of requestId -> connectionReused, estimating the information if the information\n * available in the records themselves appears untrustworthy.\n */\n static estimateIfConnectionWasReused(records: Lantern.NetworkRequest[], options?: {forceCoarseEstimates: boolean}):\n Map<string, boolean> {\n const {forceCoarseEstimates = false} = options || {};\n\n // Check if we can trust the connection information coming from the protocol\n if (!forceCoarseEstimates && NetworkAnalyzer.canTrustConnectionInformation(records)) {\n return new Map(records.map(request => [request.requestId, Boolean(request.connectionReused)]));\n }\n\n // Otherwise we're on our own, a request may not have needed a fresh connection if...\n // - It was not the first request to the domain\n // - It was H2\n // - It was after the first request to the domain ended\n const connectionWasReused = new Map();\n const groupedByOrigin = NetworkAnalyzer.groupByOrigin(records);\n for (const originRecords of groupedByOrigin.values()) {\n const earliestReusePossible =\n originRecords.map(request => request.networkEndTime).reduce((a, b) => Math.min(a, b), Infinity);\n\n for (const request of originRecords) {\n connectionWasReused.set(\n request.requestId,\n request.networkRequestTime >= earliestReusePossible || request.protocol === 'h2',\n );\n }\n\n const firstRecord = originRecords.reduce((a, b) => {\n return a.networkRequestTime > b.networkRequestTime ? b : a;\n });\n connectionWasReused.set(firstRecord.requestId, false);\n }\n\n return connectionWasReused;\n }\n\n /**\n * Estimates the RTT to each origin by examining observed network timing information.\n * Attempts to use the most accurate information first and falls back to coarser estimates when it\n * is unavailable.\n */\n static estimateRTTByOrigin(records: Lantern.NetworkRequest[], options?: RTTEstimateOptions): Map<string, Summary> {\n const {\n forceCoarseEstimates = false,\n // coarse estimates include lots of extra time and noise\n // multiply by some factor to deflate the estimates a bit.\n coarseEstimateMultiplier = 0.3,\n useDownloadEstimates = true,\n useSendStartEstimates = true,\n useHeadersEndEstimates = true,\n } = options || {};\n\n const connectionWasReused = NetworkAnalyzer.estimateIfConnectionWasReused(records);\n const groupedByOrigin = NetworkAnalyzer.groupByOrigin(records);\n\n const estimatesByOrigin = new Map();\n for (const [origin, originRequests] of groupedByOrigin.entries()) {\n const originEstimates: number[] = [];\n\n function collectEstimates(estimator: (e: RequestInfo) => number[] | number | undefined, multiplier = 1): void {\n for (const request of originRequests) {\n const timing = request.timing;\n if (!timing || !request.transferSize) {\n continue;\n }\n\n const estimates = estimator({\n request,\n timing,\n connectionReused: connectionWasReused.get(request.requestId),\n });\n if (estimates === undefined) {\n continue;\n }\n\n if (!Array.isArray(estimates)) {\n originEstimates.push(estimates * multiplier);\n } else {\n originEstimates.push(...estimates.map(e => e * multiplier));\n }\n }\n }\n\n if (!forceCoarseEstimates) {\n collectEstimates(this.estimateRTTViaConnectionTiming);\n }\n\n // Connection timing can be missing for a few reasons:\n // - Origin was preconnected, which we don't have instrumentation for.\n // - Trace began recording after a connection has already been established (for example, in timespan mode)\n // - Perhaps Chrome established a connection already in the background (service worker? Just guessing here)\n // - Not provided in LR netstack.\n if (!originEstimates.length) {\n if (useDownloadEstimates) {\n collectEstimates(this.estimateRTTViaDownloadTiming, coarseEstimateMultiplier);\n }\n if (useSendStartEstimates) {\n collectEstimates(this.estimateRTTViaSendStartTiming, coarseEstimateMultiplier);\n }\n if (useHeadersEndEstimates) {\n collectEstimates(this.estimateRTTViaHeadersEndTiming, coarseEstimateMultiplier);\n }\n }\n\n if (originEstimates.length) {\n estimatesByOrigin.set(origin, originEstimates);\n }\n }\n\n if (!estimatesByOrigin.size) {\n throw new LanternError('No timing information available');\n }\n return NetworkAnalyzer.summarize(estimatesByOrigin);\n }\n\n /**\n * Estimates the server response time of each origin. RTT times can be passed in or will be\n * estimated automatically if not provided.\n */\n static estimateServerResponseTimeByOrigin(records: Lantern.NetworkRequest[], options?: RTTEstimateOptions&{\n rttByOrigin?: Map<string, number>,\n }): Map<string, Summary> {\n let rttByOrigin = options?.rttByOrigin;\n if (!rttByOrigin) {\n rttByOrigin = new Map();\n\n const rttSummaryByOrigin = NetworkAnalyzer.estimateRTTByOrigin(records, options);\n for (const [origin, summary] of rttSummaryByOrigin.entries()) {\n rttByOrigin.set(origin, summary.min);\n }\n }\n\n const estimatesByOrigin = NetworkAnalyzer.estimateResponseTimeByOrigin(records, rttByOrigin);\n return NetworkAnalyzer.summarize(estimatesByOrigin);\n }\n\n /**\n * Computes the average throughput for the given requests in bits/second.\n * Excludes data URI, failed or otherwise incomplete, and cached requests.\n * Returns null if there were no analyzable network requests.\n */\n static estimateThroughput(records: Lantern.NetworkRequest[]): number|null {\n let totalBytes = 0;\n\n // We will measure throughput by summing the total bytes downloaded by the total time spent\n // downloading those bytes. We slice up all the network requests into start/end boundaries, so\n // it's easier to deal with the gaps in downloading.\n const timeBoundaries = records\n .reduce(\n (boundaries, request) => {\n const scheme = request.parsedURL?.scheme;\n // Requests whose bodies didn't come over the network or didn't completely finish will mess\n // with the computation, just skip over them.\n if (scheme === 'data' || request.failed || !request.finished ||\n request.statusCode > 300 || !request.transferSize) {\n return boundaries;\n }\n\n // If we've made it this far, all the times we need should be valid (i.e. not undefined/-1).\n totalBytes += request.transferSize;\n boundaries.push({time: request.responseHeadersEndTime / 1000, isStart: true});\n boundaries.push({time: request.networkEndTime / 1000, isStart: false});\n return boundaries;\n },\n [] as Array<{time: number, isStart: boolean}>)\n .sort((a, b) => a.time - b.time);\n\n if (!timeBoundaries.length) {\n return null;\n }\n\n let inflight = 0;\n let currentStart = 0;\n let totalDuration = 0;\n\n timeBoundaries.forEach(boundary => {\n if (boundary.isStart) {\n if (inflight === 0) {\n // We just ended a quiet period, keep track of when the download period started\n currentStart = boundary.time;\n }\n inflight++;\n } else {\n inflight--;\n if (inflight === 0) {\n // We just entered a quiet period, update our duration with the time we spent downloading\n totalDuration += boundary.time - currentStart;\n }\n }\n });\n\n return totalBytes * 8 / totalDuration;\n }\n\n static computeRTTAndServerResponseTime(records: Lantern.NetworkRequest[]):\n {rtt: number, additionalRttByOrigin: Map<string, number>, serverResponseTimeByOrigin: Map<string, number>} {\n // First pass compute the estimated observed RTT to each origin's servers.\n const rttByOrigin = new Map<string, number>();\n for (const [origin, summary] of NetworkAnalyzer.estimateRTTByOrigin(records).entries()) {\n rttByOrigin.set(origin, summary.min);\n }\n\n // We'll use the minimum RTT as the assumed connection latency since we care about how much addt'l\n // latency each origin introduces as Lantern will be simulating with its own connection latency.\n const minimumRtt = Math.min(...Array.from(rttByOrigin.values()));\n // We'll use the observed RTT information to help estimate the server response time\n const responseTimeSummaries = NetworkAnalyzer.estimateServerResponseTimeByOrigin(records, {\n rttByOrigin,\n });\n\n const additionalRttByOrigin = new Map<string, number>();\n const serverResponseTimeByOrigin = new Map<string, number>();\n for (const [origin, summary] of responseTimeSummaries.entries()) {\n // Not all origins have usable timing data, we'll default to using no additional latency.\n const rttForOrigin = rttByOrigin.get(origin) || minimumRtt;\n additionalRttByOrigin.set(origin, rttForOrigin - minimumRtt);\n serverResponseTimeByOrigin.set(origin, summary.median);\n }\n\n return {\n rtt: minimumRtt,\n additionalRttByOrigin,\n serverResponseTimeByOrigin,\n };\n }\n\n static analyze(records: Lantern.NetworkRequest[]): Lantern.Simulation.Settings['networkAnalysis']|null {\n const throughput = NetworkAnalyzer.estimateThroughput(records);\n if (throughput === null) {\n return null;\n }\n\n return {\n throughput,\n ...NetworkAnalyzer.computeRTTAndServerResponseTime(records),\n };\n }\n\n static findResourceForUrl<T extends Lantern.NetworkRequest>(records: T[], resourceUrl: string): T|undefined {\n // equalWithExcludedFragments is expensive, so check that the resourceUrl starts with the request url first\n return records.find(\n request => resourceUrl.startsWith(request.url) && UrlUtils.equalWithExcludedFragments(request.url, resourceUrl),\n );\n }\n\n static findLastDocumentForUrl<T extends Lantern.NetworkRequest>(records: T[], resourceUrl: string): T|undefined {\n // equalWithExcludedFragments is expensive, so check that the resourceUrl starts with the request url first\n const matchingRequests = records.filter(\n request => request.resourceType === 'Document' && !request.failed &&\n // Note: `request.url` should never have a fragment, else this optimization gives wrong results.\n resourceUrl.startsWith(request.url) && UrlUtils.equalWithExcludedFragments(request.url, resourceUrl),\n );\n return matchingRequests[matchingRequests.length - 1];\n }\n\n /**\n * Resolves redirect chain given a main document.\n * See: {@link NetworkAnalyzer.findLastDocumentForUrl} for how to retrieve main document.\n */\n static resolveRedirects<T extends Lantern.NetworkRequest>(request: T): T {\n while (request.redirectDestination) {\n request = request.redirectDestination as T;\n }\n return request;\n }\n}\n\nexport {NetworkAnalyzer};\n"]}