@paulirish/trace_engine 0.0.42 → 0.0.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.tmp/tsbuildinfo/tsconfig.tsbuildinfo +1 -1
- package/locales/en-US.json +0 -12
- package/locales/en-XL.json +0 -12
- package/models/cpu_profile/CPUProfileDataModel.js +2 -2
- package/models/cpu_profile/CPUProfileDataModel.js.map +1 -1
- package/models/trace/LanternComputationData.js +1 -1
- package/models/trace/LanternComputationData.js.map +1 -1
- package/models/trace/extras/TraceTree.d.ts +15 -2
- package/models/trace/extras/TraceTree.js +2 -2
- package/models/trace/extras/TraceTree.js.map +1 -1
- package/models/trace/extras/extras.d.ts +0 -1
- package/models/trace/extras/extras.js +0 -1
- package/models/trace/handlers/MetaHandler.js +3 -3
- package/models/trace/handlers/MetaHandler.js.map +1 -1
- package/models/trace/handlers/PageLoadMetricsHandler.js.map +1 -1
- package/models/trace/handlers/RendererHandler.d.ts +1 -1
- package/models/trace/handlers/RendererHandler.js +1 -1
- package/models/trace/handlers/RendererHandler.js.map +1 -1
- package/models/trace/handlers/ScreenshotsHandler.js +1 -1
- package/models/trace/handlers/ScreenshotsHandler.js.map +1 -1
- package/models/trace/helpers/SamplesIntegrator.d.ts +1 -1
- package/models/trace/helpers/SamplesIntegrator.js +2 -2
- package/models/trace/helpers/SamplesIntegrator.js.map +1 -1
- package/models/trace/helpers/Trace.d.ts +1 -1
- package/models/trace/helpers/Trace.js +1 -1
- package/models/trace/helpers/Trace.js.map +1 -1
- package/models/trace/helpers/TreeHelpers.js +1 -1
- package/models/trace/helpers/TreeHelpers.js.map +1 -1
- package/models/trace/insights/CLSCulprits.d.ts +2 -0
- package/models/trace/insights/CLSCulprits.js +41 -8
- package/models/trace/insights/CLSCulprits.js.map +1 -1
- package/models/trace/insights/DocumentLatency.d.ts +2 -12
- package/models/trace/insights/DocumentLatency.js +23 -14
- package/models/trace/insights/DocumentLatency.js.map +1 -1
- package/models/trace/insights/LCPDiscovery.d.ts +2 -14
- package/models/trace/insights/LCPDiscovery.js +8 -16
- package/models/trace/insights/LCPDiscovery.js.map +1 -1
- package/models/trace/insights/types.d.ts +4 -0
- package/models/trace/insights/types.js.map +1 -1
- package/models/trace/lantern/graph/BaseNode.d.ts +1 -1
- package/models/trace/lantern/graph/BaseNode.js +1 -1
- package/models/trace/lantern/graph/BaseNode.js.map +1 -1
- package/models/trace/lantern/metrics/FirstContentfulPaint.d.ts +1 -1
- package/models/trace/lantern/metrics/FirstContentfulPaint.js.map +1 -1
- package/models/trace/lantern/simulation/ConnectionPool.js +1 -1
- package/models/trace/lantern/simulation/ConnectionPool.js.map +1 -1
- package/models/trace/lantern/simulation/Simulator.d.ts +2 -2
- package/models/trace/lantern/simulation/Simulator.js +3 -3
- package/models/trace/lantern/simulation/Simulator.js.map +1 -1
- package/models/trace/lantern/types/Lantern.d.ts +2 -2
- package/models/trace/lantern/types/Lantern.js.map +1 -1
- package/models/trace/root-causes/LayoutShift.d.ts +1 -1
- package/models/trace/root-causes/LayoutShift.js +4 -4
- package/models/trace/root-causes/LayoutShift.js.map +1 -1
- package/models/trace/types/TraceEvents.d.ts +2 -2
- package/models/trace/types/TraceEvents.js.map +1 -1
- package/package.json +1 -1
- package/core/platform/PromiseUtilities.d.ts +0 -10
- package/core/platform/PromiseUtilities.js +0 -18
- package/core/platform/PromiseUtilities.js.map +0 -1
- package/core/platform/SetUtilities.d.ts +0 -2
- package/core/platform/SetUtilities.js +0 -23
- package/core/platform/SetUtilities.js.map +0 -1
- package/models/trace/EntriesFilter.d.ts +0 -72
- package/models/trace/EntriesFilter.js +0 -296
- package/models/trace/EntriesFilter.js.map +0 -1
- package/models/trace/LegacyTracingModel.js.map +0 -1
- package/models/trace/extras/URLForEntry.d.ts +0 -13
- package/models/trace/extras/URLForEntry.js +0 -44
- package/models/trace/extras/URLForEntry.js.map +0 -1
- package/models/trace/handlers/EnhancedTracesHandler.d.ts +0 -48
- package/models/trace/handlers/EnhancedTracesHandler.js +0 -165
- package/models/trace/handlers/EnhancedTracesHandler.js.map +0 -1
- package/models/trace/insights/CumulativeLayoutShift.d.ts +0 -34
- package/models/trace/insights/CumulativeLayoutShift.js +0 -209
- package/models/trace/insights/CumulativeLayoutShift.js.map +0 -1
- package/models/trace/insights/InsightRunners.d.ts +0 -6
- package/models/trace/insights/InsightRunners.js +0 -10
- package/models/trace/insights/InsightRunners.js.map +0 -1
- package/models/trace/insights/LargestContentfulPaint.d.ts +0 -25
- package/models/trace/insights/LargestContentfulPaint.js +0 -93
- package/models/trace/insights/LargestContentfulPaint.js.map +0 -1
- package/models/trace/lantern/BaseNode.d.ts +0 -91
- package/models/trace/lantern/BaseNode.js +0 -268
- package/models/trace/lantern/BaseNode.js.map +0 -1
- package/models/trace/lantern/CPUNode.d.ts +0 -24
- package/models/trace/lantern/CPUNode.js +0 -64
- package/models/trace/lantern/CPUNode.js.map +0 -1
- package/models/trace/lantern/LanternError.d.ts +0 -3
- package/models/trace/lantern/LanternError.js +0 -7
- package/models/trace/lantern/LanternError.js.map +0 -1
- package/models/trace/lantern/MetricsModule.d.ts +0 -11
- package/models/trace/lantern/MetricsModule.js +0 -14
- package/models/trace/lantern/MetricsModule.js.map +0 -1
- package/models/trace/lantern/NetworkNode.d.ts +0 -22
- package/models/trace/lantern/NetworkNode.js +0 -83
- package/models/trace/lantern/NetworkNode.js.map +0 -1
- package/models/trace/lantern/PageDependencyGraph.d.ts +0 -43
- package/models/trace/lantern/PageDependencyGraph.js +0 -509
- package/models/trace/lantern/PageDependencyGraph.js.map +0 -1
- package/models/trace/lantern/SimulationModule.d.ts +0 -17
- package/models/trace/lantern/SimulationModule.js +0 -13
- package/models/trace/lantern/SimulationModule.js.map +0 -1
- package/models/trace/lantern/simulation/NetworkAnalyzer.d.ts +0 -112
- package/models/trace/lantern/simulation/NetworkAnalyzer.js +0 -486
- package/models/trace/lantern/simulation/NetworkAnalyzer.js.map +0 -1
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
// Copyright 2024 The Chromium Authors. All rights reserved.
|
|
2
|
-
// Use of this source code is governed by a BSD-style license that can be
|
|
3
|
-
// found in the LICENSE file.
|
|
4
|
-
import * as Handlers from '../handlers/handlers.js';
|
|
5
|
-
import * as Helpers from '../helpers/helpers.js';
|
|
6
|
-
import * as Types from '../types/types.js';
|
|
7
|
-
import { findLCPRequest } from './Common.js';
|
|
8
|
-
import { InsightWarning } from './types.js';
|
|
9
|
-
export function deps() {
|
|
10
|
-
return ['NetworkRequests', 'PageLoadMetrics', 'LargestImagePaint', 'Meta'];
|
|
11
|
-
}
|
|
12
|
-
function breakdownPhases(nav, mainRequest, lcpMs, lcpRequest) {
|
|
13
|
-
const mainReqTiming = mainRequest.args.data.timing;
|
|
14
|
-
if (!mainReqTiming) {
|
|
15
|
-
throw new Error('no timing for main resource');
|
|
16
|
-
}
|
|
17
|
-
const firstDocByteTs = Helpers.Timing.secondsToMicroseconds(mainReqTiming.requestTime) +
|
|
18
|
-
Helpers.Timing.millisecondsToMicroseconds(mainReqTiming.receiveHeadersStart);
|
|
19
|
-
const firstDocByteTiming = Types.Timing.MicroSeconds(firstDocByteTs - nav.ts);
|
|
20
|
-
const ttfb = Helpers.Timing.microSecondsToMilliseconds(firstDocByteTiming);
|
|
21
|
-
let renderDelay = Types.Timing.MilliSeconds(lcpMs - ttfb);
|
|
22
|
-
if (!lcpRequest) {
|
|
23
|
-
return { ttfb, renderDelay };
|
|
24
|
-
}
|
|
25
|
-
const lcpStartTs = Types.Timing.MicroSeconds(lcpRequest.ts - nav.ts);
|
|
26
|
-
const resourceStart = Helpers.Timing.microSecondsToMilliseconds(lcpStartTs);
|
|
27
|
-
const lcpReqEndTs = Types.Timing.MicroSeconds(lcpRequest.args.data.syntheticData.finishTime - nav.ts);
|
|
28
|
-
const resourceEnd = Helpers.Timing.microSecondsToMilliseconds(lcpReqEndTs);
|
|
29
|
-
const loadDelay = Types.Timing.MilliSeconds(resourceStart - ttfb);
|
|
30
|
-
const loadTime = Types.Timing.MilliSeconds(resourceEnd - resourceStart);
|
|
31
|
-
renderDelay = Types.Timing.MilliSeconds(lcpMs - resourceEnd);
|
|
32
|
-
return {
|
|
33
|
-
ttfb,
|
|
34
|
-
loadDelay,
|
|
35
|
-
loadTime,
|
|
36
|
-
renderDelay,
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
export function generateInsight(traceParsedData, context) {
|
|
40
|
-
const networkRequests = traceParsedData.NetworkRequests;
|
|
41
|
-
const nav = traceParsedData.Meta.navigationsByNavigationId.get(context.navigationId);
|
|
42
|
-
if (!nav) {
|
|
43
|
-
throw new Error('no trace navigation');
|
|
44
|
-
}
|
|
45
|
-
const frameMetrics = traceParsedData.PageLoadMetrics.metricScoresByFrameId.get(context.frameId);
|
|
46
|
-
if (!frameMetrics) {
|
|
47
|
-
throw new Error('no frame metrics');
|
|
48
|
-
}
|
|
49
|
-
const navMetrics = frameMetrics.get(context.navigationId);
|
|
50
|
-
if (!navMetrics) {
|
|
51
|
-
throw new Error('no navigation metrics');
|
|
52
|
-
}
|
|
53
|
-
const metricScore = navMetrics.get("LCP" /* Handlers.ModelHandlers.PageLoadMetrics.MetricName.LCP */);
|
|
54
|
-
const lcpEvent = metricScore?.event;
|
|
55
|
-
if (!lcpEvent || !Types.TraceEvents.isTraceEventLargestContentfulPaintCandidate(lcpEvent)) {
|
|
56
|
-
return { warnings: [InsightWarning.NO_LCP] };
|
|
57
|
-
}
|
|
58
|
-
// This helps calculate the phases.
|
|
59
|
-
const lcpMs = Helpers.Timing.microSecondsToMilliseconds(metricScore.timing);
|
|
60
|
-
// This helps position things on the timeline's UI accurately for a trace.
|
|
61
|
-
const lcpTs = metricScore.event?.ts ? Helpers.Timing.microSecondsToMilliseconds(metricScore.event?.ts) : undefined;
|
|
62
|
-
const lcpResource = findLCPRequest(traceParsedData, context, lcpEvent);
|
|
63
|
-
const mainReq = networkRequests.byTime.find(req => req.args.data.requestId === context.navigationId);
|
|
64
|
-
if (!mainReq) {
|
|
65
|
-
return { lcpMs, lcpTs, warnings: [InsightWarning.NO_DOCUMENT_REQUEST] };
|
|
66
|
-
}
|
|
67
|
-
if (!lcpResource) {
|
|
68
|
-
return {
|
|
69
|
-
lcpMs: lcpMs,
|
|
70
|
-
lcpTs: lcpTs,
|
|
71
|
-
phases: breakdownPhases(nav, mainReq, lcpMs, lcpResource),
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
const imageLoadingAttr = lcpEvent.args.data?.loadingAttr;
|
|
75
|
-
const imagePreloaded = lcpResource?.args.data.isLinkPreload || lcpResource?.args.data.initiator?.type === 'preload';
|
|
76
|
-
const imageFetchPriorityHint = lcpResource?.args.data.fetchPriorityHint;
|
|
77
|
-
// This is the earliest discovery time an LCP resource could have - it's TTFB.
|
|
78
|
-
const earliestDiscoveryTime = mainReq && mainReq.args.data.timing ?
|
|
79
|
-
Helpers.Timing.secondsToMicroseconds(mainReq.args.data.timing.requestTime) +
|
|
80
|
-
Helpers.Timing.millisecondsToMicroseconds(mainReq.args.data.timing.receiveHeadersStart) :
|
|
81
|
-
undefined;
|
|
82
|
-
return {
|
|
83
|
-
lcpMs: lcpMs,
|
|
84
|
-
lcpTs: lcpTs,
|
|
85
|
-
phases: breakdownPhases(nav, mainReq, lcpMs, lcpResource),
|
|
86
|
-
shouldRemoveLazyLoading: imageLoadingAttr === 'lazy',
|
|
87
|
-
shouldIncreasePriorityHint: imageFetchPriorityHint !== 'high',
|
|
88
|
-
shouldPreloadImage: !imagePreloaded,
|
|
89
|
-
lcpResource,
|
|
90
|
-
earliestDiscoveryTimeTs: earliestDiscoveryTime ? Types.Timing.MicroSeconds(earliestDiscoveryTime) : undefined,
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
//# sourceMappingURL=LargestContentfulPaint.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"LargestContentfulPaint.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/LargestContentfulPaint.ts"],"names":[],"mappings":"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","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 * as Handlers from '../handlers/handlers.js';\nimport * as Helpers from '../helpers/helpers.js';\nimport * as Types from '../types/types.js';\n\nimport {findLCPRequest} from './Common.js';\nimport {InsightWarning, type LCPInsightResult, type NavigationInsightContext, type RequiredData} from './types.js';\n\nexport function deps(): ['NetworkRequests', 'PageLoadMetrics', 'LargestImagePaint', 'Meta'] {\n return ['NetworkRequests', 'PageLoadMetrics', 'LargestImagePaint', 'Meta'];\n}\n\nexport interface LCPPhases {\n /**\n * The time between when the user initiates loading the page until when\n * the browser receives the first byte of the html response.\n */\n ttfb: Types.Timing.MilliSeconds;\n /**\n * The time between ttfb and the LCP resource request being started.\n * For a text LCP, this is undefined given no resource is loaded.\n */\n loadDelay?: Types.Timing.MilliSeconds;\n /**\n * The time it takes to load the LCP resource.\n */\n loadTime?: Types.Timing.MilliSeconds;\n /**\n * The time between when the LCP resource finishes loading and when\n * the LCP element is rendered.\n */\n renderDelay: Types.Timing.MilliSeconds;\n}\n\nfunction breakdownPhases(\n nav: Types.TraceEvents.TraceEventNavigationStart, mainRequest: Types.TraceEvents.SyntheticNetworkRequest,\n lcpMs: Types.Timing.MilliSeconds, lcpRequest: Types.TraceEvents.SyntheticNetworkRequest|null): LCPPhases {\n const mainReqTiming = mainRequest.args.data.timing;\n if (!mainReqTiming) {\n throw new Error('no timing for main resource');\n }\n const firstDocByteTs = Helpers.Timing.secondsToMicroseconds(mainReqTiming.requestTime) +\n Helpers.Timing.millisecondsToMicroseconds(mainReqTiming.receiveHeadersStart);\n\n const firstDocByteTiming = Types.Timing.MicroSeconds(firstDocByteTs - nav.ts);\n const ttfb = Helpers.Timing.microSecondsToMilliseconds(firstDocByteTiming);\n let renderDelay = Types.Timing.MilliSeconds(lcpMs - ttfb);\n\n if (!lcpRequest) {\n return {ttfb, renderDelay};\n }\n\n const lcpStartTs = Types.Timing.MicroSeconds(lcpRequest.ts - nav.ts);\n const resourceStart = Helpers.Timing.microSecondsToMilliseconds(lcpStartTs);\n\n const lcpReqEndTs = Types.Timing.MicroSeconds(lcpRequest.args.data.syntheticData.finishTime - nav.ts);\n const resourceEnd = Helpers.Timing.microSecondsToMilliseconds(lcpReqEndTs);\n\n const loadDelay = Types.Timing.MilliSeconds(resourceStart - ttfb);\n const loadTime = Types.Timing.MilliSeconds(resourceEnd - resourceStart);\n renderDelay = Types.Timing.MilliSeconds(lcpMs - resourceEnd);\n\n return {\n ttfb,\n loadDelay,\n loadTime,\n renderDelay,\n };\n}\n\nexport function generateInsight(\n traceParsedData: RequiredData<typeof deps>, context: NavigationInsightContext): LCPInsightResult {\n const networkRequests = traceParsedData.NetworkRequests;\n\n const nav = traceParsedData.Meta.navigationsByNavigationId.get(context.navigationId);\n if (!nav) {\n throw new Error('no trace navigation');\n }\n\n const frameMetrics = traceParsedData.PageLoadMetrics.metricScoresByFrameId.get(context.frameId);\n if (!frameMetrics) {\n throw new Error('no frame metrics');\n }\n\n const navMetrics = frameMetrics.get(context.navigationId);\n if (!navMetrics) {\n throw new Error('no navigation metrics');\n }\n const metricScore = navMetrics.get(Handlers.ModelHandlers.PageLoadMetrics.MetricName.LCP);\n const lcpEvent = metricScore?.event;\n if (!lcpEvent || !Types.TraceEvents.isTraceEventLargestContentfulPaintCandidate(lcpEvent)) {\n return {warnings: [InsightWarning.NO_LCP]};\n }\n\n // This helps calculate the phases.\n const lcpMs = Helpers.Timing.microSecondsToMilliseconds(metricScore.timing);\n // This helps position things on the timeline's UI accurately for a trace.\n const lcpTs = metricScore.event?.ts ? Helpers.Timing.microSecondsToMilliseconds(metricScore.event?.ts) : undefined;\n const lcpResource = findLCPRequest(traceParsedData, context, lcpEvent);\n const mainReq = networkRequests.byTime.find(req => req.args.data.requestId === context.navigationId);\n if (!mainReq) {\n return {lcpMs, lcpTs, warnings: [InsightWarning.NO_DOCUMENT_REQUEST]};\n }\n\n if (!lcpResource) {\n return {\n lcpMs: lcpMs,\n lcpTs: lcpTs,\n phases: breakdownPhases(nav, mainReq, lcpMs, lcpResource),\n };\n }\n\n const imageLoadingAttr = lcpEvent.args.data?.loadingAttr;\n const imagePreloaded = lcpResource?.args.data.isLinkPreload || lcpResource?.args.data.initiator?.type === 'preload';\n const imageFetchPriorityHint = lcpResource?.args.data.fetchPriorityHint;\n\n // This is the earliest discovery time an LCP resource could have - it's TTFB.\n const earliestDiscoveryTime = mainReq && mainReq.args.data.timing ?\n Helpers.Timing.secondsToMicroseconds(mainReq.args.data.timing.requestTime) +\n Helpers.Timing.millisecondsToMicroseconds(mainReq.args.data.timing.receiveHeadersStart) :\n undefined;\n\n return {\n lcpMs: lcpMs,\n lcpTs: lcpTs,\n phases: breakdownPhases(nav, mainReq, lcpMs, lcpResource),\n shouldRemoveLazyLoading: imageLoadingAttr === 'lazy',\n shouldIncreasePriorityHint: imageFetchPriorityHint !== 'high',\n shouldPreloadImage: !imagePreloaded,\n lcpResource,\n earliestDiscoveryTimeTs: earliestDiscoveryTime ? Types.Timing.MicroSeconds(earliestDiscoveryTime) : undefined,\n };\n}\n"]}
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import { type CPUNode } from './CPUNode.js';
|
|
2
|
-
import { type NetworkNode } from './NetworkNode.js';
|
|
3
|
-
import type * as Lantern from './types/lantern.js';
|
|
4
|
-
/**
|
|
5
|
-
* A union of all types derived from BaseNode, allowing type check discrimination
|
|
6
|
-
* based on `node.type`. If a new node type is created, it should be added here.
|
|
7
|
-
*/
|
|
8
|
-
export type Node<T = Lantern.AnyNetworkObject> = CPUNode<T> | NetworkNode<T>;
|
|
9
|
-
/**
|
|
10
|
-
* @fileoverview This class encapsulates logic for handling resources and tasks used to model the
|
|
11
|
-
* execution dependency graph of the page. A node has a unique identifier and can depend on other
|
|
12
|
-
* nodes/be depended on. The construction of the graph maintains some important invariants that are
|
|
13
|
-
* inherent to the model:
|
|
14
|
-
*
|
|
15
|
-
* 1. The graph is a DAG, there are no cycles.
|
|
16
|
-
* 2. There is always a root node upon which all other nodes eventually depend.
|
|
17
|
-
*
|
|
18
|
-
* This allows particular optimizations in this class so that we do no need to check for cycles as
|
|
19
|
-
* these methods are called and we can always start traversal at the root node.
|
|
20
|
-
*/
|
|
21
|
-
declare class BaseNode<T = Lantern.AnyNetworkObject> {
|
|
22
|
-
static types: {
|
|
23
|
-
readonly NETWORK: "network";
|
|
24
|
-
readonly CPU: "cpu";
|
|
25
|
-
};
|
|
26
|
-
_id: string;
|
|
27
|
-
_isMainDocument: boolean;
|
|
28
|
-
_dependents: Node[];
|
|
29
|
-
_dependencies: Node[];
|
|
30
|
-
constructor(id: string);
|
|
31
|
-
get id(): string;
|
|
32
|
-
get type(): 'network' | 'cpu';
|
|
33
|
-
/**
|
|
34
|
-
* In microseconds
|
|
35
|
-
*/
|
|
36
|
-
get startTime(): number;
|
|
37
|
-
/**
|
|
38
|
-
* In microseconds
|
|
39
|
-
*/
|
|
40
|
-
get endTime(): number;
|
|
41
|
-
setIsMainDocument(value: boolean): void;
|
|
42
|
-
isMainDocument(): boolean;
|
|
43
|
-
getDependents(): Node[];
|
|
44
|
-
getNumberOfDependents(): number;
|
|
45
|
-
getDependencies(): Node[];
|
|
46
|
-
getNumberOfDependencies(): number;
|
|
47
|
-
getRootNode(): Node<T>;
|
|
48
|
-
addDependent(node: Node): void;
|
|
49
|
-
addDependency(node: Node): void;
|
|
50
|
-
removeDependent(node: Node): void;
|
|
51
|
-
removeDependency(node: Node): void;
|
|
52
|
-
removeAllDependencies(): void;
|
|
53
|
-
/**
|
|
54
|
-
* Computes whether the given node is anywhere in the dependency graph of this node.
|
|
55
|
-
* While this method can prevent cycles, it walks the graph and should be used sparingly.
|
|
56
|
-
* Nodes are always considered dependent on themselves for the purposes of cycle detection.
|
|
57
|
-
*/
|
|
58
|
-
isDependentOn(node: BaseNode<T>): boolean;
|
|
59
|
-
/**
|
|
60
|
-
* Clones the node's information without adding any dependencies/dependents.
|
|
61
|
-
*/
|
|
62
|
-
cloneWithoutRelationships(): Node<T>;
|
|
63
|
-
/**
|
|
64
|
-
* Clones the entire graph connected to this node filtered by the optional predicate. If a node is
|
|
65
|
-
* included by the predicate, all nodes along the paths between the node and the root will be included. If the
|
|
66
|
-
* node this was called on is not included in the resulting filtered graph, the method will throw.
|
|
67
|
-
*/
|
|
68
|
-
cloneWithRelationships(predicate?: (arg0: Node) => boolean): Node;
|
|
69
|
-
/**
|
|
70
|
-
* Traverses all connected nodes in BFS order, calling `callback` exactly once
|
|
71
|
-
* on each. `traversalPath` is the shortest (though not necessarily unique)
|
|
72
|
-
* path from `node` to the root of the iteration.
|
|
73
|
-
*
|
|
74
|
-
* The `getNextNodes` function takes a visited node and returns which nodes to
|
|
75
|
-
* visit next. It defaults to returning the node's dependents.
|
|
76
|
-
*/
|
|
77
|
-
traverse(callback: (node: Node<T>, traversalPath: Node<T>[]) => void, getNextNodes?: (arg0: Node<T>) => Node<T>[]): void;
|
|
78
|
-
/**
|
|
79
|
-
* @see BaseNode.traverse
|
|
80
|
-
*/
|
|
81
|
-
traverseGenerator(getNextNodes?: (arg0: Node) => Node[]): Generator<{
|
|
82
|
-
node: Node;
|
|
83
|
-
traversalPath: Node[];
|
|
84
|
-
}, void, unknown>;
|
|
85
|
-
/**
|
|
86
|
-
* Returns whether the given node has a cycle in its dependent graph by performing a DFS.
|
|
87
|
-
*/
|
|
88
|
-
static hasCycle(node: Node, direction?: 'dependents' | 'dependencies' | 'both'): boolean;
|
|
89
|
-
canDependOn(node: Node): boolean;
|
|
90
|
-
}
|
|
91
|
-
export { BaseNode };
|
|
@@ -1,268 +0,0 @@
|
|
|
1
|
-
// Copyright 2024 The Chromium Authors. All rights reserved.
|
|
2
|
-
// Use of this source code is governed by a BSD-style license that can be
|
|
3
|
-
// found in the LICENSE file.
|
|
4
|
-
/**
|
|
5
|
-
* @fileoverview This class encapsulates logic for handling resources and tasks used to model the
|
|
6
|
-
* execution dependency graph of the page. A node has a unique identifier and can depend on other
|
|
7
|
-
* nodes/be depended on. The construction of the graph maintains some important invariants that are
|
|
8
|
-
* inherent to the model:
|
|
9
|
-
*
|
|
10
|
-
* 1. The graph is a DAG, there are no cycles.
|
|
11
|
-
* 2. There is always a root node upon which all other nodes eventually depend.
|
|
12
|
-
*
|
|
13
|
-
* This allows particular optimizations in this class so that we do no need to check for cycles as
|
|
14
|
-
* these methods are called and we can always start traversal at the root node.
|
|
15
|
-
*/
|
|
16
|
-
class BaseNode {
|
|
17
|
-
static types = {
|
|
18
|
-
NETWORK: 'network',
|
|
19
|
-
CPU: 'cpu',
|
|
20
|
-
};
|
|
21
|
-
_id;
|
|
22
|
-
_isMainDocument;
|
|
23
|
-
_dependents;
|
|
24
|
-
_dependencies;
|
|
25
|
-
constructor(id) {
|
|
26
|
-
this._id = id;
|
|
27
|
-
this._isMainDocument = false;
|
|
28
|
-
this._dependents = [];
|
|
29
|
-
this._dependencies = [];
|
|
30
|
-
}
|
|
31
|
-
get id() {
|
|
32
|
-
return this._id;
|
|
33
|
-
}
|
|
34
|
-
get type() {
|
|
35
|
-
throw new Error('Unimplemented');
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* In microseconds
|
|
39
|
-
*/
|
|
40
|
-
get startTime() {
|
|
41
|
-
throw new Error('Unimplemented');
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* In microseconds
|
|
45
|
-
*/
|
|
46
|
-
get endTime() {
|
|
47
|
-
throw new Error('Unimplemented');
|
|
48
|
-
}
|
|
49
|
-
setIsMainDocument(value) {
|
|
50
|
-
this._isMainDocument = value;
|
|
51
|
-
}
|
|
52
|
-
isMainDocument() {
|
|
53
|
-
return this._isMainDocument;
|
|
54
|
-
}
|
|
55
|
-
getDependents() {
|
|
56
|
-
return this._dependents.slice();
|
|
57
|
-
}
|
|
58
|
-
getNumberOfDependents() {
|
|
59
|
-
return this._dependents.length;
|
|
60
|
-
}
|
|
61
|
-
getDependencies() {
|
|
62
|
-
return this._dependencies.slice();
|
|
63
|
-
}
|
|
64
|
-
getNumberOfDependencies() {
|
|
65
|
-
return this._dependencies.length;
|
|
66
|
-
}
|
|
67
|
-
getRootNode() {
|
|
68
|
-
let rootNode = this;
|
|
69
|
-
while (rootNode._dependencies.length) {
|
|
70
|
-
rootNode = rootNode._dependencies[0];
|
|
71
|
-
}
|
|
72
|
-
return rootNode;
|
|
73
|
-
}
|
|
74
|
-
addDependent(node) {
|
|
75
|
-
node.addDependency(this);
|
|
76
|
-
}
|
|
77
|
-
addDependency(node) {
|
|
78
|
-
// @ts-expect-error - in checkJs, ts doesn't know that CPUNode and NetworkNode *are* BaseNodes.
|
|
79
|
-
if (node === this) {
|
|
80
|
-
throw new Error('Cannot add dependency on itself');
|
|
81
|
-
}
|
|
82
|
-
if (this._dependencies.includes(node)) {
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
node._dependents.push(this);
|
|
86
|
-
this._dependencies.push(node);
|
|
87
|
-
}
|
|
88
|
-
removeDependent(node) {
|
|
89
|
-
node.removeDependency(this);
|
|
90
|
-
}
|
|
91
|
-
removeDependency(node) {
|
|
92
|
-
if (!this._dependencies.includes(node)) {
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
const thisIndex = node._dependents.indexOf(this);
|
|
96
|
-
node._dependents.splice(thisIndex, 1);
|
|
97
|
-
this._dependencies.splice(this._dependencies.indexOf(node), 1);
|
|
98
|
-
}
|
|
99
|
-
removeAllDependencies() {
|
|
100
|
-
for (const node of this._dependencies.slice()) {
|
|
101
|
-
this.removeDependency(node);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* Computes whether the given node is anywhere in the dependency graph of this node.
|
|
106
|
-
* While this method can prevent cycles, it walks the graph and should be used sparingly.
|
|
107
|
-
* Nodes are always considered dependent on themselves for the purposes of cycle detection.
|
|
108
|
-
*/
|
|
109
|
-
isDependentOn(node) {
|
|
110
|
-
let isDependentOnNode = false;
|
|
111
|
-
this.traverse(currentNode => {
|
|
112
|
-
if (isDependentOnNode) {
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
isDependentOnNode = currentNode === node;
|
|
116
|
-
}, currentNode => {
|
|
117
|
-
// If we've already found the dependency, don't traverse further.
|
|
118
|
-
if (isDependentOnNode) {
|
|
119
|
-
return [];
|
|
120
|
-
}
|
|
121
|
-
// Otherwise, traverse the dependencies.
|
|
122
|
-
return currentNode.getDependencies();
|
|
123
|
-
});
|
|
124
|
-
return isDependentOnNode;
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Clones the node's information without adding any dependencies/dependents.
|
|
128
|
-
*/
|
|
129
|
-
cloneWithoutRelationships() {
|
|
130
|
-
const node = new BaseNode(this.id);
|
|
131
|
-
node.setIsMainDocument(this._isMainDocument);
|
|
132
|
-
return node;
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* Clones the entire graph connected to this node filtered by the optional predicate. If a node is
|
|
136
|
-
* included by the predicate, all nodes along the paths between the node and the root will be included. If the
|
|
137
|
-
* node this was called on is not included in the resulting filtered graph, the method will throw.
|
|
138
|
-
*/
|
|
139
|
-
cloneWithRelationships(predicate) {
|
|
140
|
-
const rootNode = this.getRootNode();
|
|
141
|
-
const idsToIncludedClones = new Map();
|
|
142
|
-
// Walk down dependents.
|
|
143
|
-
rootNode.traverse(node => {
|
|
144
|
-
if (idsToIncludedClones.has(node.id)) {
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
if (predicate === undefined) {
|
|
148
|
-
// No condition for entry, so clone every node.
|
|
149
|
-
idsToIncludedClones.set(node.id, node.cloneWithoutRelationships());
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
if (predicate(node)) {
|
|
153
|
-
// Node included, so walk back up dependencies, cloning nodes from here back to the root.
|
|
154
|
-
node.traverse(node => idsToIncludedClones.set(node.id, node.cloneWithoutRelationships()),
|
|
155
|
-
// Dependencies already cloned have already cloned ancestors, so no need to visit again.
|
|
156
|
-
node => node._dependencies.filter(parent => !idsToIncludedClones.has(parent.id)));
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
// Copy dependencies between nodes.
|
|
160
|
-
rootNode.traverse(originalNode => {
|
|
161
|
-
const clonedNode = idsToIncludedClones.get(originalNode.id);
|
|
162
|
-
if (!clonedNode) {
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
for (const dependency of originalNode._dependencies) {
|
|
166
|
-
const clonedDependency = idsToIncludedClones.get(dependency.id);
|
|
167
|
-
if (!clonedDependency) {
|
|
168
|
-
throw new Error('Dependency somehow not cloned');
|
|
169
|
-
}
|
|
170
|
-
clonedNode.addDependency(clonedDependency);
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
const clonedThisNode = idsToIncludedClones.get(this.id);
|
|
174
|
-
if (!clonedThisNode) {
|
|
175
|
-
throw new Error('Cloned graph missing node');
|
|
176
|
-
}
|
|
177
|
-
return clonedThisNode;
|
|
178
|
-
}
|
|
179
|
-
/**
|
|
180
|
-
* Traverses all connected nodes in BFS order, calling `callback` exactly once
|
|
181
|
-
* on each. `traversalPath` is the shortest (though not necessarily unique)
|
|
182
|
-
* path from `node` to the root of the iteration.
|
|
183
|
-
*
|
|
184
|
-
* The `getNextNodes` function takes a visited node and returns which nodes to
|
|
185
|
-
* visit next. It defaults to returning the node's dependents.
|
|
186
|
-
*/
|
|
187
|
-
traverse(callback, getNextNodes) {
|
|
188
|
-
for (const { node, traversalPath } of this.traverseGenerator(getNextNodes)) {
|
|
189
|
-
callback(node, traversalPath);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
/**
|
|
193
|
-
* @see BaseNode.traverse
|
|
194
|
-
*/
|
|
195
|
-
// clang-format off
|
|
196
|
-
*traverseGenerator(getNextNodes) {
|
|
197
|
-
// clang-format on
|
|
198
|
-
if (!getNextNodes) {
|
|
199
|
-
getNextNodes = node => node.getDependents();
|
|
200
|
-
}
|
|
201
|
-
// @ts-expect-error - only traverses graphs of Node, so force tsc to treat `this` as one
|
|
202
|
-
const queue = [[this]];
|
|
203
|
-
const visited = new Set([this.id]);
|
|
204
|
-
while (queue.length) {
|
|
205
|
-
// @ts-expect-error - queue has length so it's guaranteed to have an item
|
|
206
|
-
const traversalPath = queue.shift();
|
|
207
|
-
const node = traversalPath[0];
|
|
208
|
-
yield { node, traversalPath };
|
|
209
|
-
for (const nextNode of getNextNodes(node)) {
|
|
210
|
-
if (visited.has(nextNode.id)) {
|
|
211
|
-
continue;
|
|
212
|
-
}
|
|
213
|
-
visited.add(nextNode.id);
|
|
214
|
-
queue.push([nextNode, ...traversalPath]);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
/**
|
|
219
|
-
* Returns whether the given node has a cycle in its dependent graph by performing a DFS.
|
|
220
|
-
*/
|
|
221
|
-
static hasCycle(node, direction = 'both') {
|
|
222
|
-
// Checking 'both' is the default entrypoint to recursively check both directions
|
|
223
|
-
if (direction === 'both') {
|
|
224
|
-
return BaseNode.hasCycle(node, 'dependents') || BaseNode.hasCycle(node, 'dependencies');
|
|
225
|
-
}
|
|
226
|
-
const visited = new Set();
|
|
227
|
-
const currentPath = [];
|
|
228
|
-
const toVisit = [node];
|
|
229
|
-
const depthAdded = new Map([[node, 0]]);
|
|
230
|
-
// Keep going while we have nodes to visit in the stack
|
|
231
|
-
while (toVisit.length) {
|
|
232
|
-
// Get the last node in the stack (DFS uses stack, not queue)
|
|
233
|
-
// @ts-expect-error - toVisit has length so it's guaranteed to have an item
|
|
234
|
-
const currentNode = toVisit.pop();
|
|
235
|
-
// We've hit a cycle if the node we're visiting is in our current dependency path
|
|
236
|
-
if (currentPath.includes(currentNode)) {
|
|
237
|
-
return true;
|
|
238
|
-
}
|
|
239
|
-
// If we've already visited the node, no need to revisit it
|
|
240
|
-
if (visited.has(currentNode)) {
|
|
241
|
-
continue;
|
|
242
|
-
}
|
|
243
|
-
// Since we're visiting this node, clear out any nodes in our path that we had to backtrack
|
|
244
|
-
// @ts-expect-error
|
|
245
|
-
while (currentPath.length > depthAdded.get(currentNode)) {
|
|
246
|
-
currentPath.pop();
|
|
247
|
-
}
|
|
248
|
-
// Update our data structures to reflect that we're adding this node to our path
|
|
249
|
-
visited.add(currentNode);
|
|
250
|
-
currentPath.push(currentNode);
|
|
251
|
-
// Add all of its dependents to our toVisit stack
|
|
252
|
-
const nodesToExplore = direction === 'dependents' ? currentNode._dependents : currentNode._dependencies;
|
|
253
|
-
for (const nextNode of nodesToExplore) {
|
|
254
|
-
if (toVisit.includes(nextNode)) {
|
|
255
|
-
continue;
|
|
256
|
-
}
|
|
257
|
-
toVisit.push(nextNode);
|
|
258
|
-
depthAdded.set(nextNode, currentPath.length);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
return false;
|
|
262
|
-
}
|
|
263
|
-
canDependOn(node) {
|
|
264
|
-
return node.startTime <= this.startTime;
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
export { BaseNode };
|
|
268
|
-
//# sourceMappingURL=BaseNode.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"BaseNode.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/lantern/BaseNode.ts"],"names":[],"mappings":"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","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 CPUNode} from './CPUNode.js';\nimport {type NetworkNode} from './NetworkNode.js';\nimport type * as Lantern from './types/lantern.js';\n\n/**\n * A union of all types derived from BaseNode, allowing type check discrimination\n * based on `node.type`. If a new node type is created, it should be added here.\n */\nexport type Node<T = Lantern.AnyNetworkObject> = CPUNode<T>|NetworkNode<T>;\n\n/**\n * @fileoverview This class encapsulates logic for handling resources and tasks used to model the\n * execution dependency graph of the page. A node has a unique identifier and can depend on other\n * nodes/be depended on. The construction of the graph maintains some important invariants that are\n * inherent to the model:\n *\n * 1. The graph is a DAG, there are no cycles.\n * 2. There is always a root node upon which all other nodes eventually depend.\n *\n * This allows particular optimizations in this class so that we do no need to check for cycles as\n * these methods are called and we can always start traversal at the root node.\n */\n\nclass BaseNode<T = Lantern.AnyNetworkObject> {\n static types = {\n NETWORK: 'network',\n CPU: 'cpu',\n } as const;\n\n _id: string;\n _isMainDocument: boolean;\n _dependents: Node[];\n _dependencies: Node[];\n\n constructor(id: string) {\n this._id = id;\n this._isMainDocument = false;\n this._dependents = [];\n this._dependencies = [];\n }\n\n get id(): string {\n return this._id;\n }\n\n get type(): 'network'|'cpu' {\n throw new Error('Unimplemented');\n }\n\n /**\n * In microseconds\n */\n get startTime(): number {\n throw new Error('Unimplemented');\n }\n\n /**\n * In microseconds\n */\n get endTime(): number {\n throw new Error('Unimplemented');\n }\n\n setIsMainDocument(value: boolean): void {\n this._isMainDocument = value;\n }\n\n isMainDocument(): boolean {\n return this._isMainDocument;\n }\n\n getDependents(): Node[] {\n return this._dependents.slice();\n }\n\n getNumberOfDependents(): number {\n return this._dependents.length;\n }\n\n getDependencies(): Node[] {\n return this._dependencies.slice();\n }\n\n getNumberOfDependencies(): number {\n return this._dependencies.length;\n }\n\n getRootNode(): Node<T> {\n let rootNode = this as BaseNode as Node;\n while (rootNode._dependencies.length) {\n rootNode = rootNode._dependencies[0];\n }\n\n return rootNode;\n }\n\n addDependent(node: Node): void {\n node.addDependency(this as BaseNode as Node);\n }\n\n addDependency(node: Node): void {\n // @ts-expect-error - in checkJs, ts doesn't know that CPUNode and NetworkNode *are* BaseNodes.\n if (node === this) {\n throw new Error('Cannot add dependency on itself');\n }\n\n if (this._dependencies.includes(node)) {\n return;\n }\n\n node._dependents.push(this as BaseNode as Node);\n this._dependencies.push(node);\n }\n\n removeDependent(node: Node): void {\n node.removeDependency(this as BaseNode as Node);\n }\n\n removeDependency(node: Node): void {\n if (!this._dependencies.includes(node)) {\n return;\n }\n\n const thisIndex = node._dependents.indexOf(this as BaseNode as Node);\n node._dependents.splice(thisIndex, 1);\n this._dependencies.splice(this._dependencies.indexOf(node), 1);\n }\n\n removeAllDependencies(): void {\n for (const node of this._dependencies.slice()) {\n this.removeDependency(node);\n }\n }\n\n /**\n * Computes whether the given node is anywhere in the dependency graph of this node.\n * While this method can prevent cycles, it walks the graph and should be used sparingly.\n * Nodes are always considered dependent on themselves for the purposes of cycle detection.\n */\n isDependentOn(node: BaseNode<T>): boolean {\n let isDependentOnNode = false;\n this.traverse(\n currentNode => {\n if (isDependentOnNode) {\n return;\n }\n isDependentOnNode = currentNode === node;\n },\n currentNode => {\n // If we've already found the dependency, don't traverse further.\n if (isDependentOnNode) {\n return [];\n }\n // Otherwise, traverse the dependencies.\n return currentNode.getDependencies();\n });\n\n return isDependentOnNode;\n }\n\n /**\n * Clones the node's information without adding any dependencies/dependents.\n */\n cloneWithoutRelationships(): Node<T> {\n const node = new BaseNode(this.id) as Node<T>;\n node.setIsMainDocument(this._isMainDocument);\n return node;\n }\n\n /**\n * Clones the entire graph connected to this node filtered by the optional predicate. If a node is\n * included by the predicate, all nodes along the paths between the node and the root will be included. If the\n * node this was called on is not included in the resulting filtered graph, the method will throw.\n */\n cloneWithRelationships(predicate?: (arg0: Node) => boolean): Node {\n const rootNode = this.getRootNode();\n\n const idsToIncludedClones = new Map<string, Node>();\n\n // Walk down dependents.\n rootNode.traverse(node => {\n if (idsToIncludedClones.has(node.id)) {\n return;\n }\n\n if (predicate === undefined) {\n // No condition for entry, so clone every node.\n idsToIncludedClones.set(node.id, node.cloneWithoutRelationships());\n return;\n }\n\n if (predicate(node)) {\n // Node included, so walk back up dependencies, cloning nodes from here back to the root.\n node.traverse(\n node => idsToIncludedClones.set(node.id, node.cloneWithoutRelationships()),\n // Dependencies already cloned have already cloned ancestors, so no need to visit again.\n node => node._dependencies.filter(parent => !idsToIncludedClones.has(parent.id)),\n );\n }\n });\n\n // Copy dependencies between nodes.\n rootNode.traverse(originalNode => {\n const clonedNode = idsToIncludedClones.get(originalNode.id);\n if (!clonedNode) {\n return;\n }\n\n for (const dependency of originalNode._dependencies) {\n const clonedDependency = idsToIncludedClones.get(dependency.id);\n if (!clonedDependency) {\n throw new Error('Dependency somehow not cloned');\n }\n clonedNode.addDependency(clonedDependency);\n }\n });\n\n const clonedThisNode = idsToIncludedClones.get(this.id);\n if (!clonedThisNode) {\n throw new Error('Cloned graph missing node');\n }\n return clonedThisNode;\n }\n\n /**\n * Traverses all connected nodes in BFS order, calling `callback` exactly once\n * on each. `traversalPath` is the shortest (though not necessarily unique)\n * path from `node` to the root of the iteration.\n *\n * The `getNextNodes` function takes a visited node and returns which nodes to\n * visit next. It defaults to returning the node's dependents.\n */\n traverse(callback: (node: Node<T>, traversalPath: Node<T>[]) => void, getNextNodes?: (arg0: Node<T>) => Node<T>[]):\n void {\n for (const {node, traversalPath} of this.traverseGenerator(getNextNodes)) {\n callback(node, traversalPath);\n }\n }\n\n /**\n * @see BaseNode.traverse\n */\n // clang-format off\n *traverseGenerator(getNextNodes?: (arg0: Node) => Node[]):\n Generator<{node: Node, traversalPath: Node[]}, void, unknown> {\n // clang-format on\n if (!getNextNodes) {\n getNextNodes = node => node.getDependents();\n }\n\n // @ts-expect-error - only traverses graphs of Node, so force tsc to treat `this` as one\n const queue: Node[][] = [[this]];\n const visited = new Set([this.id]);\n\n while (queue.length) {\n // @ts-expect-error - queue has length so it's guaranteed to have an item\n const traversalPath: Node[] = queue.shift();\n const node = traversalPath[0];\n yield {node, traversalPath};\n\n for (const nextNode of getNextNodes(node)) {\n if (visited.has(nextNode.id)) {\n continue;\n }\n visited.add(nextNode.id);\n\n queue.push([nextNode, ...traversalPath]);\n }\n }\n }\n\n /**\n * Returns whether the given node has a cycle in its dependent graph by performing a DFS.\n */\n static hasCycle(node: Node, direction: 'dependents'|'dependencies'|'both' = 'both'): boolean {\n // Checking 'both' is the default entrypoint to recursively check both directions\n if (direction === 'both') {\n return BaseNode.hasCycle(node, 'dependents') || BaseNode.hasCycle(node, 'dependencies');\n }\n\n const visited = new Set();\n const currentPath: BaseNode[] = [];\n const toVisit = [node];\n const depthAdded = new Map([[node, 0]]);\n\n // Keep going while we have nodes to visit in the stack\n while (toVisit.length) {\n // Get the last node in the stack (DFS uses stack, not queue)\n // @ts-expect-error - toVisit has length so it's guaranteed to have an item\n const currentNode: BaseNode = toVisit.pop();\n\n // We've hit a cycle if the node we're visiting is in our current dependency path\n if (currentPath.includes(currentNode)) {\n return true;\n }\n // If we've already visited the node, no need to revisit it\n if (visited.has(currentNode)) {\n continue;\n }\n\n // Since we're visiting this node, clear out any nodes in our path that we had to backtrack\n // @ts-expect-error\n while (currentPath.length > depthAdded.get(currentNode)) {\n currentPath.pop();\n }\n\n // Update our data structures to reflect that we're adding this node to our path\n visited.add(currentNode);\n currentPath.push(currentNode);\n\n // Add all of its dependents to our toVisit stack\n const nodesToExplore = direction === 'dependents' ? currentNode._dependents : currentNode._dependencies;\n for (const nextNode of nodesToExplore) {\n if (toVisit.includes(nextNode)) {\n continue;\n }\n toVisit.push(nextNode);\n depthAdded.set(nextNode, currentPath.length);\n }\n }\n\n return false;\n }\n\n canDependOn(node: Node): boolean {\n return node.startTime <= this.startTime;\n }\n}\n\nexport {BaseNode};\n"]}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { BaseNode } from './BaseNode.js';
|
|
2
|
-
import type * as Lantern from './types/lantern.js';
|
|
3
|
-
declare class CPUNode<T = Lantern.AnyNetworkObject> extends BaseNode<T> {
|
|
4
|
-
_event: Lantern.TraceEvent;
|
|
5
|
-
_childEvents: Lantern.TraceEvent[];
|
|
6
|
-
_correctedEndTs: number | undefined;
|
|
7
|
-
constructor(parentEvent: Lantern.TraceEvent, childEvents?: Lantern.TraceEvent[], correctedEndTs?: number);
|
|
8
|
-
get type(): 'cpu';
|
|
9
|
-
get startTime(): number;
|
|
10
|
-
get endTime(): number;
|
|
11
|
-
get duration(): number;
|
|
12
|
-
get event(): Lantern.TraceEvent;
|
|
13
|
-
get childEvents(): Lantern.TraceEvent[];
|
|
14
|
-
/**
|
|
15
|
-
* Returns true if this node contains a Layout task.
|
|
16
|
-
*/
|
|
17
|
-
didPerformLayout(): boolean;
|
|
18
|
-
/**
|
|
19
|
-
* Returns the script URLs that had their EvaluateScript events occur in this task.
|
|
20
|
-
*/
|
|
21
|
-
getEvaluateScriptURLs(): Set<string>;
|
|
22
|
-
cloneWithoutRelationships(): CPUNode;
|
|
23
|
-
}
|
|
24
|
-
export { CPUNode };
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
// Copyright 2024 The Chromium Authors. All rights reserved.
|
|
2
|
-
// Use of this source code is governed by a BSD-style license that can be
|
|
3
|
-
// found in the LICENSE file.
|
|
4
|
-
import { BaseNode } from './BaseNode.js';
|
|
5
|
-
class CPUNode extends BaseNode {
|
|
6
|
-
_event;
|
|
7
|
-
_childEvents;
|
|
8
|
-
_correctedEndTs;
|
|
9
|
-
constructor(parentEvent, childEvents = [], correctedEndTs) {
|
|
10
|
-
const nodeId = `${parentEvent.tid}.${parentEvent.ts}`;
|
|
11
|
-
super(nodeId);
|
|
12
|
-
this._event = parentEvent;
|
|
13
|
-
this._childEvents = childEvents;
|
|
14
|
-
this._correctedEndTs = correctedEndTs;
|
|
15
|
-
}
|
|
16
|
-
get type() {
|
|
17
|
-
return BaseNode.types.CPU;
|
|
18
|
-
}
|
|
19
|
-
get startTime() {
|
|
20
|
-
return this._event.ts;
|
|
21
|
-
}
|
|
22
|
-
get endTime() {
|
|
23
|
-
if (this._correctedEndTs) {
|
|
24
|
-
return this._correctedEndTs;
|
|
25
|
-
}
|
|
26
|
-
return this._event.ts + this._event.dur;
|
|
27
|
-
}
|
|
28
|
-
get duration() {
|
|
29
|
-
return this.endTime - this.startTime;
|
|
30
|
-
}
|
|
31
|
-
get event() {
|
|
32
|
-
return this._event;
|
|
33
|
-
}
|
|
34
|
-
get childEvents() {
|
|
35
|
-
return this._childEvents;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Returns true if this node contains a Layout task.
|
|
39
|
-
*/
|
|
40
|
-
didPerformLayout() {
|
|
41
|
-
return this._childEvents.some(evt => evt.name === 'Layout');
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Returns the script URLs that had their EvaluateScript events occur in this task.
|
|
45
|
-
*/
|
|
46
|
-
getEvaluateScriptURLs() {
|
|
47
|
-
const urls = new Set();
|
|
48
|
-
for (const event of this._childEvents) {
|
|
49
|
-
if (event.name !== 'EvaluateScript') {
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
52
|
-
if (!event.args.data || !event.args.data.url) {
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
urls.add(event.args.data.url);
|
|
56
|
-
}
|
|
57
|
-
return urls;
|
|
58
|
-
}
|
|
59
|
-
cloneWithoutRelationships() {
|
|
60
|
-
return new CPUNode(this._event, this._childEvents, this._correctedEndTs);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
export { CPUNode };
|
|
64
|
-
//# sourceMappingURL=CPUNode.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"CPUNode.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/lantern/CPUNode.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,yEAAyE;AACzE,6BAA6B;AAE7B,OAAO,EAAC,QAAQ,EAAC,MAAM,eAAe,CAAC;AAGvC,MAAM,OAAsC,SAAQ,QAAW;IAC7D,MAAM,CAAqB;IAC3B,YAAY,CAAuB;IACnC,eAAe,CAAmB;IAElC,YAAY,WAA+B,EAAE,cAAoC,EAAE,EAAE,cAAuB;QAC1G,MAAM,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,IAAI,WAAW,CAAC,EAAE,EAAE,CAAC;QACtD,KAAK,CAAC,MAAM,CAAC,CAAC;QAEd,IAAI,CAAC,MAAM,GAAG,WAAW,CAAC;QAC1B,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC;QAChC,IAAI,CAAC,eAAe,GAAG,cAAc,CAAC;IACxC,CAAC;IAED,IAAa,IAAI;QACf,OAAO,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC;IAC5B,CAAC;IAED,IAAa,SAAS;QACpB,OAAO,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;IACxB,CAAC;IAED,IAAa,OAAO;QAClB,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC,eAAe,CAAC;QAC9B,CAAC;QACD,OAAO,IAAI,CAAC,MAAM,CAAC,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC;IAC1C,CAAC;IAED,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC;IACvC,CAAC;IAED,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED;;OAEG;IACH,gBAAgB;QACd,OAAO,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;IAC9D,CAAC;IAED;;OAEG;IACH,qBAAqB;QACnB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;QAC/B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtC,IAAI,KAAK,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;gBACpC,SAAS;YACX,CAAC;YACD,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;gBAC7C,SAAS;YACX,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAChC,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAEQ,yBAAyB;QAChC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;IAC3E,CAAC;CACF;AAED,OAAO,EAAC,OAAO,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 {BaseNode} from './BaseNode.js';\nimport type * as Lantern from './types/lantern.js';\n\nclass CPUNode<T = Lantern.AnyNetworkObject> extends BaseNode<T> {\n _event: Lantern.TraceEvent;\n _childEvents: Lantern.TraceEvent[];\n _correctedEndTs: number|undefined;\n\n constructor(parentEvent: Lantern.TraceEvent, childEvents: Lantern.TraceEvent[] = [], correctedEndTs?: number) {\n const nodeId = `${parentEvent.tid}.${parentEvent.ts}`;\n super(nodeId);\n\n this._event = parentEvent;\n this._childEvents = childEvents;\n this._correctedEndTs = correctedEndTs;\n }\n\n override get type(): 'cpu' {\n return BaseNode.types.CPU;\n }\n\n override get startTime(): number {\n return this._event.ts;\n }\n\n override get endTime(): number {\n if (this._correctedEndTs) {\n return this._correctedEndTs;\n }\n return this._event.ts + this._event.dur;\n }\n\n get duration(): number {\n return this.endTime - this.startTime;\n }\n\n get event(): Lantern.TraceEvent {\n return this._event;\n }\n\n get childEvents(): Lantern.TraceEvent[] {\n return this._childEvents;\n }\n\n /**\n * Returns true if this node contains a Layout task.\n */\n didPerformLayout(): boolean {\n return this._childEvents.some(evt => evt.name === 'Layout');\n }\n\n /**\n * Returns the script URLs that had their EvaluateScript events occur in this task.\n */\n getEvaluateScriptURLs(): Set<string> {\n const urls = new Set<string>();\n for (const event of this._childEvents) {\n if (event.name !== 'EvaluateScript') {\n continue;\n }\n if (!event.args.data || !event.args.data.url) {\n continue;\n }\n urls.add(event.args.data.url);\n }\n\n return urls;\n }\n\n override cloneWithoutRelationships(): CPUNode {\n return new CPUNode(this._event, this._childEvents, this._correctedEndTs);\n }\n}\n\nexport {CPUNode};\n"]}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
// Copyright 2024 The Chromium Authors. All rights reserved.
|
|
2
|
-
// Use of this source code is governed by a BSD-style license that can be
|
|
3
|
-
// found in the LICENSE file.
|
|
4
|
-
class LanternError extends Error {
|
|
5
|
-
}
|
|
6
|
-
export { LanternError };
|
|
7
|
-
//# sourceMappingURL=LanternError.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"LanternError.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/lantern/LanternError.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,yEAAyE;AACzE,6BAA6B;AAE7B,MAAM,YAAa,SAAQ,KAAK;CAAG;AAEnC,OAAO,EAAC,YAAY,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\nclass LanternError extends Error {}\n\nexport {LanternError};\n"]}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { FirstContentfulPaint } from './metrics/FirstContentfulPaint.js';
|
|
2
|
-
import { Interactive } from './metrics/Interactive.js';
|
|
3
|
-
import { LargestContentfulPaint } from './metrics/LargestContentfulPaint.js';
|
|
4
|
-
import { MaxPotentialFID } from './metrics/MaxPotentialFID.js';
|
|
5
|
-
import { type Extras, Metric } from './metrics/Metric.js';
|
|
6
|
-
import { SpeedIndex } from './metrics/SpeedIndex.js';
|
|
7
|
-
import { TotalBlockingTime } from './metrics/TotalBlockingTime.js';
|
|
8
|
-
import type * as Lantern from './types/lantern.js';
|
|
9
|
-
export type Result<T = Lantern.AnyNetworkObject> = Lantern.Metrics.Result<T>;
|
|
10
|
-
export { FirstContentfulPaint, Interactive, LargestContentfulPaint, MaxPotentialFID, Extras, Metric, SpeedIndex, TotalBlockingTime, };
|
|
11
|
-
export * as TBTUtils from './metrics/TBTUtils.js';
|