Rocky_Mountain_Vending/.pnpm-store/v10/files/86/5e3c69e24f1dad354fe2f05468f64f98ebc4dd6bdd2ece73be0d914a6a83c31ea12df35417adef41f740b89bd6f05af2e8c92e23ecf569b51db0f1ee04645d
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

489 lines
No EOL
22 KiB
Text

// Copyright 2024 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import { LanternError } from './LanternError.js';
class UrlUtils {
/**
* There is fancy URL rewriting logic for the chrome://settings page that we need to work around.
* Why? Special handling was added by Chrome team to allow a pushState transition between chrome:// pages.
* As a result, the network URL (chrome://chrome/settings/) doesn't match the final document URL (chrome://settings/).
*/
static rewriteChromeInternalUrl(url) {
if (!url?.startsWith('chrome://')) {
return url;
}
// Chrome adds a trailing slash to `chrome://` URLs, but the spec does not.
// https://github.com/GoogleChrome/lighthouse/pull/3941#discussion_r154026009
if (url.endsWith('/')) {
url = url.replace(/\/$/, '');
}
return url.replace(/^chrome:\/\/chrome\//, 'chrome://');
}
/**
* Determine if url1 equals url2, ignoring URL fragments.
*/
static equalWithExcludedFragments(url1, url2) {
[url1, url2] = [url1, url2].map(this.rewriteChromeInternalUrl);
try {
const urla = new URL(url1);
urla.hash = '';
const urlb = new URL(url2);
urlb.hash = '';
return urla.href === urlb.href;
}
catch {
return false;
}
}
}
const INITIAL_CWD = 14 * 1024;
// Assume that 40% of TTFB was server response time by default for static assets
const DEFAULT_SERVER_RESPONSE_PERCENTAGE = 0.4;
/**
* For certain resource types, server response time takes up a greater percentage of TTFB (dynamic
* assets like HTML documents, XHR/API calls, etc)
*/
const SERVER_RESPONSE_PERCENTAGE_OF_TTFB = {
Document: 0.9,
XHR: 0.9,
Fetch: 0.9,
};
class NetworkAnalyzer {
static get summary() {
return '__SUMMARY__';
}
static groupByOrigin(records) {
const grouped = new Map();
records.forEach(item => {
const key = item.parsedURL.securityOrigin;
const group = grouped.get(key) || [];
group.push(item);
grouped.set(key, group);
});
return grouped;
}
static getSummary(values) {
values.sort((a, b) => a - b);
let median;
if (values.length === 0) {
median = values[0];
}
else if (values.length % 2 === 0) {
const a = values[Math.floor((values.length - 1) / 2)];
const b = values[Math.floor((values.length - 1) / 2) + 1];
median = (a + b) / 2;
}
else {
median = values[Math.floor((values.length - 1) / 2)];
}
return {
min: values[0],
max: values[values.length - 1],
avg: values.reduce((a, b) => a + b, 0) / values.length,
median,
};
}
static summarize(values) {
const summaryByKey = new Map();
const allEstimates = [];
for (const [key, estimates] of values) {
summaryByKey.set(key, NetworkAnalyzer.getSummary(estimates));
allEstimates.push(...estimates);
}
summaryByKey.set(NetworkAnalyzer.summary, NetworkAnalyzer.getSummary(allEstimates));
return summaryByKey;
}
static estimateValueByOrigin(requests, iteratee) {
const connectionWasReused = NetworkAnalyzer.estimateIfConnectionWasReused(requests);
const groupedByOrigin = NetworkAnalyzer.groupByOrigin(requests);
const estimates = new Map();
for (const [origin, originRequests] of groupedByOrigin.entries()) {
let originEstimates = [];
for (const request of originRequests) {
const timing = request.timing;
if (!timing) {
continue;
}
const value = iteratee({
request,
timing,
connectionReused: connectionWasReused.get(request.requestId),
});
if (typeof value !== 'undefined') {
originEstimates = originEstimates.concat(value);
}
}
if (!originEstimates.length) {
continue;
}
estimates.set(origin, originEstimates);
}
return estimates;
}
/**
* Estimates the observed RTT to each origin based on how long the connection setup.
* For h1 and h2, this could includes two estimates - one for the TCP handshake, another for
* SSL negotiation.
* For h3, we get only one estimate since QUIC establishes a secure connection in a
* single handshake.
* This is the most accurate and preferred method of measurement when the data is available.
*/
static estimateRTTViaConnectionTiming(info) {
const { timing, connectionReused, request } = info;
if (connectionReused) {
return;
}
const { connectStart, sslStart, sslEnd, connectEnd } = timing;
if (connectEnd >= 0 && connectStart >= 0 && request.protocol.startsWith('h3')) {
// These values are equal to sslStart and sslEnd for h3.
return connectEnd - connectStart;
}
if (sslStart >= 0 && sslEnd >= 0 && sslStart !== connectStart) {
// SSL can also be more than 1 RT but assume False Start was used.
return [connectEnd - sslStart, sslStart - connectStart];
}
if (connectStart >= 0 && connectEnd >= 0) {
return connectEnd - connectStart;
}
return;
}
/**
* Estimates the observed RTT to each origin based on how long a download took on a fresh connection.
* NOTE: this will tend to overestimate the actual RTT quite significantly as the download can be
* slow for other reasons as well such as bandwidth constraints.
*/
static estimateRTTViaDownloadTiming(info) {
const { timing, connectionReused, request } = info;
if (connectionReused) {
return;
}
// Only look at downloads that went past the initial congestion window
if (request.transferSize <= INITIAL_CWD) {
return;
}
if (!Number.isFinite(timing.receiveHeadersEnd) || timing.receiveHeadersEnd < 0) {
return;
}
// Compute the amount of time downloading everything after the first congestion window took
const totalTime = request.networkEndTime - request.networkRequestTime;
const downloadTimeAfterFirstByte = totalTime - timing.receiveHeadersEnd;
const numberOfRoundTrips = Math.log2(request.transferSize / INITIAL_CWD);
// Ignore requests that required a high number of round trips since bandwidth starts to play
// a larger role than latency
if (numberOfRoundTrips > 5) {
return;
}
return downloadTimeAfterFirstByte / numberOfRoundTrips;
}
/**
* Estimates the observed RTT to each origin based on how long it took until Chrome could
* start sending the actual request when a new connection was required.
* NOTE: this will tend to overestimate the actual RTT as the request can be delayed for other
* reasons as well such as more SSL handshakes if TLS False Start is not enabled.
*/
static estimateRTTViaSendStartTiming(info) {
const { timing, connectionReused, request } = info;
if (connectionReused) {
return;
}
if (!Number.isFinite(timing.sendStart) || timing.sendStart < 0) {
return;
}
// Assume everything before sendStart was just DNS + (SSL)? + TCP handshake
// 1 RT for DNS, 1 RT (maybe) for SSL, 1 RT for TCP
let roundTrips = 1;
// TCP
if (!request.protocol.startsWith('h3')) {
roundTrips += 1;
}
if (request.parsedURL.scheme === 'https') {
roundTrips += 1;
}
return timing.sendStart / roundTrips;
}
/**
* Estimates the observed RTT to each origin based on how long it took until Chrome received the
* headers of the response (~TTFB).
* NOTE: this is the most inaccurate way to estimate the RTT, but in some environments it's all
* we have access to :(
*/
static estimateRTTViaHeadersEndTiming(info) {
const { timing, connectionReused, request } = info;
if (!Number.isFinite(timing.receiveHeadersEnd) || timing.receiveHeadersEnd < 0) {
return;
}
if (!request.resourceType) {
return;
}
const serverResponseTimePercentage = SERVER_RESPONSE_PERCENTAGE_OF_TTFB[request.resourceType] || DEFAULT_SERVER_RESPONSE_PERCENTAGE;
const estimatedServerResponseTime = timing.receiveHeadersEnd * serverResponseTimePercentage;
// When connection was reused...
// TTFB = 1 RT for request + server response time
let roundTrips = 1;
// When connection was fresh...
// TTFB = DNS + (SSL)? + TCP handshake + 1 RT for request + server response time
if (!connectionReused) {
roundTrips += 1; // DNS
if (!request.protocol.startsWith('h3')) {
roundTrips += 1; // TCP
}
if (request.parsedURL.scheme === 'https') {
roundTrips += 1; // SSL
}
}
// subtract out our estimated server response time
return Math.max((timing.receiveHeadersEnd - estimatedServerResponseTime) / roundTrips, 3);
}
/**
* Given the RTT to each origin, estimates the observed server response times.
*/
static estimateResponseTimeByOrigin(records, rttByOrigin) {
return NetworkAnalyzer.estimateValueByOrigin(records, ({ request, timing }) => {
if (request.serverResponseTime !== undefined) {
return request.serverResponseTime;
}
if (!Number.isFinite(timing.receiveHeadersEnd) || timing.receiveHeadersEnd < 0) {
return;
}
if (!Number.isFinite(timing.sendEnd) || timing.sendEnd < 0) {
return;
}
const ttfb = timing.receiveHeadersEnd - timing.sendEnd;
const origin = request.parsedURL.securityOrigin;
const rtt = rttByOrigin.get(origin) || rttByOrigin.get(NetworkAnalyzer.summary) || 0;
return Math.max(ttfb - rtt, 0);
});
}
static canTrustConnectionInformation(requests) {
const connectionIdWasStarted = new Map();
for (const request of requests) {
const started = connectionIdWasStarted.get(request.connectionId) || !request.connectionReused;
connectionIdWasStarted.set(request.connectionId, started);
}
// We probably can't trust the network information if all the connection IDs were the same
if (connectionIdWasStarted.size <= 1) {
return false;
}
// Or if there were connections that were always reused (a connection had to have started at some point)
return Array.from(connectionIdWasStarted.values()).every(started => started);
}
/**
* Returns a map of requestId -> connectionReused, estimating the information if the information
* available in the records themselves appears untrustworthy.
*/
static estimateIfConnectionWasReused(records, options) {
const { forceCoarseEstimates = false } = options || {};
// Check if we can trust the connection information coming from the protocol
if (!forceCoarseEstimates && NetworkAnalyzer.canTrustConnectionInformation(records)) {
return new Map(records.map(request => [request.requestId, Boolean(request.connectionReused)]));
}
// Otherwise we're on our own, a request may not have needed a fresh connection if...
// - It was not the first request to the domain
// - It was H2
// - It was after the first request to the domain ended
const connectionWasReused = new Map();
const groupedByOrigin = NetworkAnalyzer.groupByOrigin(records);
for (const originRecords of groupedByOrigin.values()) {
const earliestReusePossible = originRecords.map(request => request.networkEndTime).reduce((a, b) => Math.min(a, b), Infinity);
for (const request of originRecords) {
connectionWasReused.set(request.requestId, request.networkRequestTime >= earliestReusePossible || request.protocol === 'h2');
}
const firstRecord = originRecords.reduce((a, b) => {
return a.networkRequestTime > b.networkRequestTime ? b : a;
});
connectionWasReused.set(firstRecord.requestId, false);
}
return connectionWasReused;
}
/**
* Estimates the RTT to each origin by examining observed network timing information.
* Attempts to use the most accurate information first and falls back to coarser estimates when it
* is unavailable.
*/
static estimateRTTByOrigin(records, options) {
const { forceCoarseEstimates = false,
// coarse estimates include lots of extra time and noise
// multiply by some factor to deflate the estimates a bit.
coarseEstimateMultiplier = 0.3, useDownloadEstimates = true, useSendStartEstimates = true, useHeadersEndEstimates = true, } = options || {};
const connectionWasReused = NetworkAnalyzer.estimateIfConnectionWasReused(records);
const groupedByOrigin = NetworkAnalyzer.groupByOrigin(records);
const estimatesByOrigin = new Map();
for (const [origin, originRequests] of groupedByOrigin.entries()) {
const originEstimates = [];
function collectEstimates(estimator, multiplier = 1) {
for (const request of originRequests) {
const timing = request.timing;
if (!timing || !request.transferSize) {
continue;
}
const estimates = estimator({
request,
timing,
connectionReused: connectionWasReused.get(request.requestId),
});
if (estimates === undefined) {
continue;
}
if (!Array.isArray(estimates)) {
originEstimates.push(estimates * multiplier);
}
else {
originEstimates.push(...estimates.map(e => e * multiplier));
}
}
}
if (!forceCoarseEstimates) {
collectEstimates(this.estimateRTTViaConnectionTiming);
}
// Connection timing can be missing for a few reasons:
// - Origin was preconnected, which we don't have instrumentation for.
// - Trace began recording after a connection has already been established (for example, in timespan mode)
// - Perhaps Chrome established a connection already in the background (service worker? Just guessing here)
// - Not provided in LR netstack.
if (!originEstimates.length) {
if (useDownloadEstimates) {
collectEstimates(this.estimateRTTViaDownloadTiming, coarseEstimateMultiplier);
}
if (useSendStartEstimates) {
collectEstimates(this.estimateRTTViaSendStartTiming, coarseEstimateMultiplier);
}
if (useHeadersEndEstimates) {
collectEstimates(this.estimateRTTViaHeadersEndTiming, coarseEstimateMultiplier);
}
}
if (originEstimates.length) {
estimatesByOrigin.set(origin, originEstimates);
}
}
if (!estimatesByOrigin.size) {
throw new LanternError('No timing information available');
}
return NetworkAnalyzer.summarize(estimatesByOrigin);
}
/**
* Estimates the server response time of each origin. RTT times can be passed in or will be
* estimated automatically if not provided.
*/
static estimateServerResponseTimeByOrigin(records, options) {
let rttByOrigin = options?.rttByOrigin;
if (!rttByOrigin) {
rttByOrigin = new Map();
const rttSummaryByOrigin = NetworkAnalyzer.estimateRTTByOrigin(records, options);
for (const [origin, summary] of rttSummaryByOrigin.entries()) {
rttByOrigin.set(origin, summary.min);
}
}
const estimatesByOrigin = NetworkAnalyzer.estimateResponseTimeByOrigin(records, rttByOrigin);
return NetworkAnalyzer.summarize(estimatesByOrigin);
}
/**
* Computes the average throughput for the given requests in bits/second.
* Excludes data URI, failed or otherwise incomplete, and cached requests.
* Returns null if there were no analyzable network requests.
*/
static estimateThroughput(records) {
let totalBytes = 0;
// We will measure throughput by summing the total bytes downloaded by the total time spent
// downloading those bytes. We slice up all the network requests into start/end boundaries, so
// it's easier to deal with the gaps in downloading.
const timeBoundaries = records
.reduce((boundaries, request) => {
const scheme = request.parsedURL?.scheme;
// Requests whose bodies didn't come over the network or didn't completely finish will mess
// with the computation, just skip over them.
if (scheme === 'data' || request.failed || !request.finished ||
request.statusCode > 300 || !request.transferSize) {
return boundaries;
}
// If we've made it this far, all the times we need should be valid (i.e. not undefined/-1).
totalBytes += request.transferSize;
boundaries.push({ time: request.responseHeadersEndTime / 1000, isStart: true });
boundaries.push({ time: request.networkEndTime / 1000, isStart: false });
return boundaries;
}, [])
.sort((a, b) => a.time - b.time);
if (!timeBoundaries.length) {
return null;
}
let inflight = 0;
let currentStart = 0;
let totalDuration = 0;
timeBoundaries.forEach(boundary => {
if (boundary.isStart) {
if (inflight === 0) {
// We just ended a quiet period, keep track of when the download period started
currentStart = boundary.time;
}
inflight++;
}
else {
inflight--;
if (inflight === 0) {
// We just entered a quiet period, update our duration with the time we spent downloading
totalDuration += boundary.time - currentStart;
}
}
});
return totalBytes * 8 / totalDuration;
}
static computeRTTAndServerResponseTime(records) {
// First pass compute the estimated observed RTT to each origin's servers.
const rttByOrigin = new Map();
for (const [origin, summary] of NetworkAnalyzer.estimateRTTByOrigin(records).entries()) {
rttByOrigin.set(origin, summary.min);
}
// We'll use the minimum RTT as the assumed connection latency since we care about how much addt'l
// latency each origin introduces as Lantern will be simulating with its own connection latency.
const minimumRtt = Math.min(...Array.from(rttByOrigin.values()));
// We'll use the observed RTT information to help estimate the server response time
const responseTimeSummaries = NetworkAnalyzer.estimateServerResponseTimeByOrigin(records, {
rttByOrigin,
});
const additionalRttByOrigin = new Map();
const serverResponseTimeByOrigin = new Map();
for (const [origin, summary] of responseTimeSummaries.entries()) {
// Not all origins have usable timing data, we'll default to using no additional latency.
const rttForOrigin = rttByOrigin.get(origin) || minimumRtt;
additionalRttByOrigin.set(origin, rttForOrigin - minimumRtt);
serverResponseTimeByOrigin.set(origin, summary.median);
}
return {
rtt: minimumRtt,
additionalRttByOrigin,
serverResponseTimeByOrigin,
};
}
static analyze(records) {
const throughput = NetworkAnalyzer.estimateThroughput(records);
if (throughput === null) {
return null;
}
return {
throughput,
...NetworkAnalyzer.computeRTTAndServerResponseTime(records),
};
}
static findResourceForUrl(records, resourceUrl) {
// equalWithExcludedFragments is expensive, so check that the resourceUrl starts with the request url first
return records.find(request => resourceUrl.startsWith(request.url) && UrlUtils.equalWithExcludedFragments(request.url, resourceUrl));
}
static findLastDocumentForUrl(records, resourceUrl) {
// equalWithExcludedFragments is expensive, so check that the resourceUrl starts with the request url first
const matchingRequests = records.filter(request => request.resourceType === 'Document' && !request.failed &&
// Note: `request.url` should never have a fragment, else this optimization gives wrong results.
resourceUrl.startsWith(request.url) && UrlUtils.equalWithExcludedFragments(request.url, resourceUrl));
return matchingRequests[matchingRequests.length - 1];
}
/**
* Resolves redirect chain given a main document.
* See: {@link NetworkAnalyzer.findLastDocumentForUrl} for how to retrieve main document.
*/
static resolveRedirects(request) {
while (request.redirectDestination) {
request = request.redirectDestination;
}
return request;
}
}
export { NetworkAnalyzer };
//# sourceMappingURL=NetworkAnalyzer.js.map