@firebase/performance 0.6.12 → 0.7.0-20250205220033
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/dist/esm/index.esm2017.js +164 -112
- package/dist/esm/index.esm2017.js.map +1 -1
- package/dist/esm/src/constants.d.ts +6 -0
- package/dist/esm/src/resources/trace.d.ts +3 -1
- package/dist/esm/src/{controllers/perf.test.d.ts → resources/web_vitals.d.ts} +10 -2
- package/dist/esm/src/services/api_service.d.ts +4 -0
- package/dist/esm/src/services/oob_resources_service.d.ts +5 -0
- package/dist/esm/src/services/perf_logger.d.ts +1 -0
- package/dist/esm/src/services/transport_service.d.ts +5 -0
- package/dist/index.cjs.js +164 -112
- package/dist/index.cjs.js.map +1 -1
- package/dist/src/constants.d.ts +6 -0
- package/dist/src/resources/trace.d.ts +3 -1
- package/dist/{esm/src/index.test.d.ts → src/resources/web_vitals.d.ts} +10 -2
- package/dist/src/services/api_service.d.ts +4 -0
- package/dist/src/services/oob_resources_service.d.ts +5 -0
- package/dist/src/services/perf_logger.d.ts +1 -0
- package/dist/src/services/transport_service.d.ts +5 -0
- package/package.json +8 -7
- package/dist/esm/src/resources/network_request.test.d.ts +0 -17
- package/dist/esm/src/resources/trace.test.d.ts +0 -17
- package/dist/esm/src/services/api_service.test.d.ts +0 -17
- package/dist/esm/src/services/iid_service.test.d.ts +0 -17
- package/dist/esm/src/services/initialization_service.test.d.ts +0 -17
- package/dist/esm/src/services/oob_resources_service.test.d.ts +0 -17
- package/dist/esm/src/services/perf_logger.test.d.ts +0 -17
- package/dist/esm/src/services/remote_config_service.test.d.ts +0 -17
- package/dist/esm/src/services/transport_service.test.d.ts +0 -17
- package/dist/esm/src/utils/attribute_utils.test.d.ts +0 -17
- package/dist/esm/src/utils/metric_utils.test.d.ts +0 -17
- package/dist/esm/src/utils/string_merger.test.d.ts +0 -17
- package/dist/src/controllers/perf.test.d.ts +0 -17
- package/dist/src/index.test.d.ts +0 -17
- package/dist/src/resources/network_request.test.d.ts +0 -17
- package/dist/src/resources/trace.test.d.ts +0 -17
- package/dist/src/services/api_service.test.d.ts +0 -17
- package/dist/src/services/iid_service.test.d.ts +0 -17
- package/dist/src/services/initialization_service.test.d.ts +0 -17
- package/dist/src/services/oob_resources_service.test.d.ts +0 -17
- package/dist/src/services/perf_logger.test.d.ts +0 -17
- package/dist/src/services/remote_config_service.test.d.ts +0 -17
- package/dist/src/services/transport_service.test.d.ts +0 -17
- package/dist/src/utils/attribute_utils.test.d.ts +0 -17
- package/dist/src/utils/metric_utils.test.d.ts +0 -17
- package/dist/src/utils/string_merger.test.d.ts +0 -17
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* See the License for the specific language governing permissions and
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
|
+
import { CLSMetricWithAttribution, INPMetricWithAttribution, LCPMetricWithAttribution } from 'web-vitals/attribution';
|
|
17
18
|
declare global {
|
|
18
19
|
interface Window {
|
|
19
20
|
PerformanceObserver: typeof PerformanceObserver;
|
|
@@ -34,6 +35,9 @@ export declare class Api {
|
|
|
34
35
|
private readonly PerformanceObserver;
|
|
35
36
|
private readonly windowLocation;
|
|
36
37
|
readonly onFirstInputDelay?: (fn: (fid: number) => void) => void;
|
|
38
|
+
readonly onLCP: (fn: (metric: LCPMetricWithAttribution) => void) => void;
|
|
39
|
+
readonly onINP: (fn: (metric: INPMetricWithAttribution) => void) => void;
|
|
40
|
+
readonly onCLS: (fn: (metric: CLSMetricWithAttribution) => void) => void;
|
|
37
41
|
readonly localStorage?: Storage;
|
|
38
42
|
readonly document: Document;
|
|
39
43
|
readonly navigator: Navigator;
|
|
@@ -16,3 +16,8 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { PerformanceController } from '../controllers/perf';
|
|
18
18
|
export declare function setupOobResources(performanceController: PerformanceController): void;
|
|
19
|
+
/**
|
|
20
|
+
* This service will only export the page load trace once. This function allows
|
|
21
|
+
* resetting it for unit tests
|
|
22
|
+
*/
|
|
23
|
+
export declare function resetForUnitTests(): void;
|
|
@@ -17,4 +17,5 @@
|
|
|
17
17
|
import { NetworkRequest } from '../resources/network_request';
|
|
18
18
|
import { Trace } from '../resources/trace';
|
|
19
19
|
export declare function logTrace(trace: Trace): void;
|
|
20
|
+
export declare function flushLogs(): void;
|
|
20
21
|
export declare function logNetworkRequest(networkRequest: NetworkRequest): void;
|
|
@@ -21,3 +21,8 @@ export declare function setupTransportService(): void;
|
|
|
21
21
|
export declare function resetTransportService(): void;
|
|
22
22
|
/** Log handler for cc service to send the performance logs to the server. */
|
|
23
23
|
export declare function transportHandler(serializer: (...args: any[]) => string): (...args: unknown[]) => void;
|
|
24
|
+
/**
|
|
25
|
+
* Force flush the queued events. Useful at page unload time to ensure all
|
|
26
|
+
* events are uploaded.
|
|
27
|
+
*/
|
|
28
|
+
export declare function flushQueuedEvents(): void;
|
package/dist/index.cjs.js
CHANGED
|
@@ -4,12 +4,13 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
4
4
|
|
|
5
5
|
var util = require('@firebase/util');
|
|
6
6
|
var logger$1 = require('@firebase/logger');
|
|
7
|
+
var attribution = require('web-vitals/attribution');
|
|
7
8
|
var app = require('@firebase/app');
|
|
8
9
|
var component = require('@firebase/component');
|
|
9
10
|
require('@firebase/installations');
|
|
10
11
|
|
|
11
12
|
const name = "@firebase/performance";
|
|
12
|
-
const version = "0.
|
|
13
|
+
const version = "0.7.0-20250205220033";
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* @license
|
|
@@ -39,6 +40,12 @@ const OOB_TRACE_PAGE_LOAD_PREFIX = '_wt_';
|
|
|
39
40
|
const FIRST_PAINT_COUNTER_NAME = '_fp';
|
|
40
41
|
const FIRST_CONTENTFUL_PAINT_COUNTER_NAME = '_fcp';
|
|
41
42
|
const FIRST_INPUT_DELAY_COUNTER_NAME = '_fid';
|
|
43
|
+
const LARGEST_CONTENTFUL_PAINT_METRIC_NAME = '_lcp';
|
|
44
|
+
const LARGEST_CONTENTFUL_PAINT_ATTRIBUTE_NAME = 'lcp_element';
|
|
45
|
+
const INTERACTION_TO_NEXT_PAINT_METRIC_NAME = '_inp';
|
|
46
|
+
const INTERACTION_TO_NEXT_PAINT_ATTRIBUTE_NAME = 'inp_interactionTarget';
|
|
47
|
+
const CUMULATIVE_LAYOUT_SHIFT_METRIC_NAME = '_cls';
|
|
48
|
+
const CUMULATIVE_LAYOUT_SHIFT_ATTRIBUTE_NAME = 'cls_largestShiftTarget';
|
|
42
49
|
const CONFIG_LOCAL_STORAGE_KEY = '@firebase/performance/config';
|
|
43
50
|
const CONFIG_EXPIRY_LOCAL_STORAGE_KEY = '@firebase/performance/configexpire';
|
|
44
51
|
const SERVICE = 'performance';
|
|
@@ -143,6 +150,9 @@ class Api {
|
|
|
143
150
|
if (window.perfMetrics && window.perfMetrics.onFirstInputDelay) {
|
|
144
151
|
this.onFirstInputDelay = window.perfMetrics.onFirstInputDelay;
|
|
145
152
|
}
|
|
153
|
+
this.onLCP = attribution.onLCP;
|
|
154
|
+
this.onINP = attribution.onINP;
|
|
155
|
+
this.onCLS = attribution.onCLS;
|
|
146
156
|
}
|
|
147
157
|
getUrl() {
|
|
148
158
|
// Do not capture the string query part of url.
|
|
@@ -696,9 +706,8 @@ function changeInitializationStatus() {
|
|
|
696
706
|
*/
|
|
697
707
|
const DEFAULT_SEND_INTERVAL_MS = 10 * 1000;
|
|
698
708
|
const INITIAL_SEND_TIME_DELAY_MS = 5.5 * 1000;
|
|
699
|
-
// If end point does not work, the call will be tried for these many times.
|
|
700
|
-
const DEFAULT_REMAINING_TRIES = 3;
|
|
701
709
|
const MAX_EVENT_COUNT_PER_REQUEST = 1000;
|
|
710
|
+
const DEFAULT_REMAINING_TRIES = 3;
|
|
702
711
|
let remainingTries = DEFAULT_REMAINING_TRIES;
|
|
703
712
|
/* eslint-enable camelcase */
|
|
704
713
|
let queue = [];
|
|
@@ -715,11 +724,10 @@ function processQueue(timeOffset) {
|
|
|
715
724
|
if (remainingTries === 0) {
|
|
716
725
|
return;
|
|
717
726
|
}
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
return processQueue(DEFAULT_SEND_INTERVAL_MS);
|
|
727
|
+
if (queue.length > 0) {
|
|
728
|
+
dispatchQueueEvents();
|
|
721
729
|
}
|
|
722
|
-
|
|
730
|
+
processQueue(DEFAULT_SEND_INTERVAL_MS);
|
|
723
731
|
}, timeOffset);
|
|
724
732
|
}
|
|
725
733
|
function dispatchQueueEvents() {
|
|
@@ -743,7 +751,11 @@ function dispatchQueueEvents() {
|
|
|
743
751
|
log_event
|
|
744
752
|
};
|
|
745
753
|
/* eslint-enable camelcase */
|
|
746
|
-
|
|
754
|
+
postToFlEndpoint(data)
|
|
755
|
+
.then(() => {
|
|
756
|
+
remainingTries = DEFAULT_REMAINING_TRIES;
|
|
757
|
+
})
|
|
758
|
+
.catch(() => {
|
|
747
759
|
// If the request fails for some reason, add the events that were attempted
|
|
748
760
|
// back to the primary queue to retry later.
|
|
749
761
|
queue = [...staged, ...queue];
|
|
@@ -752,41 +764,16 @@ function dispatchQueueEvents() {
|
|
|
752
764
|
processQueue(DEFAULT_SEND_INTERVAL_MS);
|
|
753
765
|
});
|
|
754
766
|
}
|
|
755
|
-
function sendEventsToFl(data, staged) {
|
|
756
|
-
return postToFlEndpoint(data)
|
|
757
|
-
.then(res => {
|
|
758
|
-
if (!res.ok) {
|
|
759
|
-
consoleLogger.info('Call to Firebase backend failed.');
|
|
760
|
-
}
|
|
761
|
-
return res.json();
|
|
762
|
-
})
|
|
763
|
-
.then(res => {
|
|
764
|
-
// Find the next call wait time from the response.
|
|
765
|
-
const transportWait = Number(res.nextRequestWaitMillis);
|
|
766
|
-
let requestOffset = DEFAULT_SEND_INTERVAL_MS;
|
|
767
|
-
if (!isNaN(transportWait)) {
|
|
768
|
-
requestOffset = Math.max(transportWait, requestOffset);
|
|
769
|
-
}
|
|
770
|
-
// Delete request if response include RESPONSE_ACTION_UNKNOWN or DELETE_REQUEST action.
|
|
771
|
-
// Otherwise, retry request using normal scheduling if response include RETRY_REQUEST_LATER.
|
|
772
|
-
const logResponseDetails = res.logResponseDetails;
|
|
773
|
-
if (Array.isArray(logResponseDetails) &&
|
|
774
|
-
logResponseDetails.length > 0 &&
|
|
775
|
-
logResponseDetails[0].responseAction === 'RETRY_REQUEST_LATER') {
|
|
776
|
-
queue = [...staged, ...queue];
|
|
777
|
-
consoleLogger.info(`Retry transport request later.`);
|
|
778
|
-
}
|
|
779
|
-
remainingTries = DEFAULT_REMAINING_TRIES;
|
|
780
|
-
// Schedule the next process.
|
|
781
|
-
processQueue(requestOffset);
|
|
782
|
-
});
|
|
783
|
-
}
|
|
784
767
|
function postToFlEndpoint(data) {
|
|
785
768
|
const flTransportFullUrl = SettingsService.getInstance().getFlTransportFullUrl();
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
769
|
+
const body = JSON.stringify(data);
|
|
770
|
+
return navigator.sendBeacon && navigator.sendBeacon(flTransportFullUrl, body)
|
|
771
|
+
? Promise.resolve()
|
|
772
|
+
: fetch(flTransportFullUrl, {
|
|
773
|
+
method: 'POST',
|
|
774
|
+
body,
|
|
775
|
+
keepalive: true
|
|
776
|
+
}).then();
|
|
790
777
|
}
|
|
791
778
|
function addToQueue(evt) {
|
|
792
779
|
if (!evt.eventTime || !evt.message) {
|
|
@@ -807,6 +794,15 @@ serializer) {
|
|
|
807
794
|
});
|
|
808
795
|
};
|
|
809
796
|
}
|
|
797
|
+
/**
|
|
798
|
+
* Force flush the queued events. Useful at page unload time to ensure all
|
|
799
|
+
* events are uploaded.
|
|
800
|
+
*/
|
|
801
|
+
function flushQueuedEvents() {
|
|
802
|
+
while (queue.length > 0) {
|
|
803
|
+
dispatchQueueEvents();
|
|
804
|
+
}
|
|
805
|
+
}
|
|
810
806
|
|
|
811
807
|
/**
|
|
812
808
|
* @license
|
|
@@ -825,12 +821,16 @@ serializer) {
|
|
|
825
821
|
* limitations under the License.
|
|
826
822
|
*/
|
|
827
823
|
let logger;
|
|
824
|
+
//
|
|
828
825
|
// This method is not called before initialization.
|
|
829
826
|
function sendLog(resource, resourceType) {
|
|
830
827
|
if (!logger) {
|
|
831
|
-
logger =
|
|
828
|
+
logger = {
|
|
829
|
+
send: transportHandler(serializer),
|
|
830
|
+
flush: flushQueuedEvents
|
|
831
|
+
};
|
|
832
832
|
}
|
|
833
|
-
logger(resource, resourceType);
|
|
833
|
+
logger.send(resource, resourceType);
|
|
834
834
|
}
|
|
835
835
|
function logTrace(trace) {
|
|
836
836
|
const settingsService = SettingsService.getInstance();
|
|
@@ -846,10 +846,6 @@ function logTrace(trace) {
|
|
|
846
846
|
if (!Api.getInstance().requiredApisAvailable()) {
|
|
847
847
|
return;
|
|
848
848
|
}
|
|
849
|
-
// Only log the page load auto traces if page is visible.
|
|
850
|
-
if (trace.isAuto && getVisibilityState() !== VisibilityState.VISIBLE) {
|
|
851
|
-
return;
|
|
852
|
-
}
|
|
853
849
|
if (isPerfInitialized()) {
|
|
854
850
|
sendTraceLog(trace);
|
|
855
851
|
}
|
|
@@ -859,6 +855,11 @@ function logTrace(trace) {
|
|
|
859
855
|
getInitializationPromise(trace.performanceController).then(() => sendTraceLog(trace), () => sendTraceLog(trace));
|
|
860
856
|
}
|
|
861
857
|
}
|
|
858
|
+
function flushLogs() {
|
|
859
|
+
if (logger) {
|
|
860
|
+
logger.flush();
|
|
861
|
+
}
|
|
862
|
+
}
|
|
862
863
|
function sendTraceLog(trace) {
|
|
863
864
|
if (!getIid()) {
|
|
864
865
|
return;
|
|
@@ -868,7 +869,7 @@ function sendTraceLog(trace) {
|
|
|
868
869
|
!settingsService.logTraceAfterSampling) {
|
|
869
870
|
return;
|
|
870
871
|
}
|
|
871
|
-
|
|
872
|
+
sendLog(trace, 1 /* ResourceType.Trace */);
|
|
872
873
|
}
|
|
873
874
|
function logNetworkRequest(networkRequest) {
|
|
874
875
|
const settingsService = SettingsService.getInstance();
|
|
@@ -891,7 +892,7 @@ function logNetworkRequest(networkRequest) {
|
|
|
891
892
|
!settingsService.logNetworkAfterSampling) {
|
|
892
893
|
return;
|
|
893
894
|
}
|
|
894
|
-
|
|
895
|
+
sendLog(networkRequest, 0 /* ResourceType.NetworkRequest */);
|
|
895
896
|
}
|
|
896
897
|
function serializer(resource, resourceType) {
|
|
897
898
|
if (resourceType === 0 /* ResourceType.NetworkRequest */) {
|
|
@@ -951,6 +952,46 @@ function getApplicationInfo(firebaseApp) {
|
|
|
951
952
|
}
|
|
952
953
|
/* eslint-enable camelcase */
|
|
953
954
|
|
|
955
|
+
/**
|
|
956
|
+
* @license
|
|
957
|
+
* Copyright 2020 Google LLC
|
|
958
|
+
*
|
|
959
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
960
|
+
* you may not use this file except in compliance with the License.
|
|
961
|
+
* You may obtain a copy of the License at
|
|
962
|
+
*
|
|
963
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
964
|
+
*
|
|
965
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
966
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
967
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
968
|
+
* See the License for the specific language governing permissions and
|
|
969
|
+
* limitations under the License.
|
|
970
|
+
*/
|
|
971
|
+
function createNetworkRequestEntry(performanceController, entry) {
|
|
972
|
+
const performanceEntry = entry;
|
|
973
|
+
if (!performanceEntry || performanceEntry.responseStart === undefined) {
|
|
974
|
+
return;
|
|
975
|
+
}
|
|
976
|
+
const timeOrigin = Api.getInstance().getTimeOrigin();
|
|
977
|
+
const startTimeUs = Math.floor((performanceEntry.startTime + timeOrigin) * 1000);
|
|
978
|
+
const timeToResponseInitiatedUs = performanceEntry.responseStart
|
|
979
|
+
? Math.floor((performanceEntry.responseStart - performanceEntry.startTime) * 1000)
|
|
980
|
+
: undefined;
|
|
981
|
+
const timeToResponseCompletedUs = Math.floor((performanceEntry.responseEnd - performanceEntry.startTime) * 1000);
|
|
982
|
+
// Remove the query params from logged network request url.
|
|
983
|
+
const url = performanceEntry.name && performanceEntry.name.split('?')[0];
|
|
984
|
+
const networkRequest = {
|
|
985
|
+
performanceController,
|
|
986
|
+
url,
|
|
987
|
+
responsePayloadBytes: performanceEntry.transferSize,
|
|
988
|
+
startTimeUs,
|
|
989
|
+
timeToResponseInitiatedUs,
|
|
990
|
+
timeToResponseCompletedUs
|
|
991
|
+
};
|
|
992
|
+
logNetworkRequest(networkRequest);
|
|
993
|
+
}
|
|
994
|
+
|
|
954
995
|
/**
|
|
955
996
|
* @license
|
|
956
997
|
* Copyright 2020 Google LLC
|
|
@@ -972,7 +1013,10 @@ const RESERVED_AUTO_PREFIX = '_';
|
|
|
972
1013
|
const oobMetrics = [
|
|
973
1014
|
FIRST_PAINT_COUNTER_NAME,
|
|
974
1015
|
FIRST_CONTENTFUL_PAINT_COUNTER_NAME,
|
|
975
|
-
FIRST_INPUT_DELAY_COUNTER_NAME
|
|
1016
|
+
FIRST_INPUT_DELAY_COUNTER_NAME,
|
|
1017
|
+
LARGEST_CONTENTFUL_PAINT_METRIC_NAME,
|
|
1018
|
+
CUMULATIVE_LAYOUT_SHIFT_METRIC_NAME,
|
|
1019
|
+
INTERACTION_TO_NEXT_PAINT_METRIC_NAME
|
|
976
1020
|
];
|
|
977
1021
|
/**
|
|
978
1022
|
* Returns true if the metric is custom and does not start with reserved prefix, or if
|
|
@@ -1211,7 +1255,7 @@ class Trace {
|
|
|
1211
1255
|
* @param paintTimings A array which contains paintTiming object of the page load
|
|
1212
1256
|
* @param firstInputDelay First input delay in millisec
|
|
1213
1257
|
*/
|
|
1214
|
-
static createOobTrace(performanceController, navigationTimings, paintTimings, firstInputDelay) {
|
|
1258
|
+
static createOobTrace(performanceController, navigationTimings, paintTimings, webVitalMetrics, firstInputDelay) {
|
|
1215
1259
|
const route = Api.getInstance().getUrl();
|
|
1216
1260
|
if (!route) {
|
|
1217
1261
|
return;
|
|
@@ -1241,7 +1285,21 @@ class Trace {
|
|
|
1241
1285
|
trace.putMetric(FIRST_INPUT_DELAY_COUNTER_NAME, Math.floor(firstInputDelay * 1000));
|
|
1242
1286
|
}
|
|
1243
1287
|
}
|
|
1288
|
+
this.addWebVitalMetric(trace, LARGEST_CONTENTFUL_PAINT_METRIC_NAME, LARGEST_CONTENTFUL_PAINT_ATTRIBUTE_NAME, webVitalMetrics.lcp);
|
|
1289
|
+
this.addWebVitalMetric(trace, CUMULATIVE_LAYOUT_SHIFT_METRIC_NAME, CUMULATIVE_LAYOUT_SHIFT_ATTRIBUTE_NAME, webVitalMetrics.cls);
|
|
1290
|
+
this.addWebVitalMetric(trace, INTERACTION_TO_NEXT_PAINT_METRIC_NAME, INTERACTION_TO_NEXT_PAINT_ATTRIBUTE_NAME, webVitalMetrics.inp);
|
|
1291
|
+
// Page load logs are sent at unload time and so should be logged and
|
|
1292
|
+
// flushed immediately.
|
|
1244
1293
|
logTrace(trace);
|
|
1294
|
+
flushLogs();
|
|
1295
|
+
}
|
|
1296
|
+
static addWebVitalMetric(trace, metricKey, attributeKey, metric) {
|
|
1297
|
+
if (metric) {
|
|
1298
|
+
trace.putMetric(metricKey, Math.floor(metric.value * 1000));
|
|
1299
|
+
if (metric.elementAttribution) {
|
|
1300
|
+
trace.putAttribute(attributeKey, metric.elementAttribution);
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1245
1303
|
}
|
|
1246
1304
|
static createUserTimingTrace(performanceController, measureName) {
|
|
1247
1305
|
const trace = new Trace(performanceController, measureName, false, measureName);
|
|
@@ -1265,54 +1323,17 @@ class Trace {
|
|
|
1265
1323
|
* See the License for the specific language governing permissions and
|
|
1266
1324
|
* limitations under the License.
|
|
1267
1325
|
*/
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
return;
|
|
1272
|
-
}
|
|
1273
|
-
const timeOrigin = Api.getInstance().getTimeOrigin();
|
|
1274
|
-
const startTimeUs = Math.floor((performanceEntry.startTime + timeOrigin) * 1000);
|
|
1275
|
-
const timeToResponseInitiatedUs = performanceEntry.responseStart
|
|
1276
|
-
? Math.floor((performanceEntry.responseStart - performanceEntry.startTime) * 1000)
|
|
1277
|
-
: undefined;
|
|
1278
|
-
const timeToResponseCompletedUs = Math.floor((performanceEntry.responseEnd - performanceEntry.startTime) * 1000);
|
|
1279
|
-
// Remove the query params from logged network request url.
|
|
1280
|
-
const url = performanceEntry.name && performanceEntry.name.split('?')[0];
|
|
1281
|
-
const networkRequest = {
|
|
1282
|
-
performanceController,
|
|
1283
|
-
url,
|
|
1284
|
-
responsePayloadBytes: performanceEntry.transferSize,
|
|
1285
|
-
startTimeUs,
|
|
1286
|
-
timeToResponseInitiatedUs,
|
|
1287
|
-
timeToResponseCompletedUs
|
|
1288
|
-
};
|
|
1289
|
-
logNetworkRequest(networkRequest);
|
|
1290
|
-
}
|
|
1291
|
-
|
|
1292
|
-
/**
|
|
1293
|
-
* @license
|
|
1294
|
-
* Copyright 2020 Google LLC
|
|
1295
|
-
*
|
|
1296
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1297
|
-
* you may not use this file except in compliance with the License.
|
|
1298
|
-
* You may obtain a copy of the License at
|
|
1299
|
-
*
|
|
1300
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1301
|
-
*
|
|
1302
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
1303
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
1304
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1305
|
-
* See the License for the specific language governing permissions and
|
|
1306
|
-
* limitations under the License.
|
|
1307
|
-
*/
|
|
1308
|
-
const FID_WAIT_TIME_MS = 5000;
|
|
1326
|
+
let webVitalMetrics = {};
|
|
1327
|
+
let sentPageLoadTrace = false;
|
|
1328
|
+
let firstInputDelay;
|
|
1309
1329
|
function setupOobResources(performanceController) {
|
|
1310
1330
|
// Do not initialize unless iid is available.
|
|
1311
1331
|
if (!getIid()) {
|
|
1312
1332
|
return;
|
|
1313
1333
|
}
|
|
1314
|
-
// The load event might not have fired yet, and that means performance
|
|
1315
|
-
// object has a duration of 0. The setup should run after
|
|
1334
|
+
// The load event might not have fired yet, and that means performance
|
|
1335
|
+
// navigation timing object has a duration of 0. The setup should run after
|
|
1336
|
+
// all current tasks in js queue.
|
|
1316
1337
|
setTimeout(() => setupOobTraces(performanceController), 0);
|
|
1317
1338
|
setTimeout(() => setupNetworkRequests(performanceController), 0);
|
|
1318
1339
|
setTimeout(() => setupUserTimingTraces(performanceController), 0);
|
|
@@ -1327,27 +1348,44 @@ function setupNetworkRequests(performanceController) {
|
|
|
1327
1348
|
}
|
|
1328
1349
|
function setupOobTraces(performanceController) {
|
|
1329
1350
|
const api = Api.getInstance();
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1351
|
+
// Better support for Safari
|
|
1352
|
+
if ('onpagehide' in window) {
|
|
1353
|
+
api.document.addEventListener('pagehide', () => sendOobTrace(performanceController));
|
|
1354
|
+
}
|
|
1355
|
+
else {
|
|
1356
|
+
api.document.addEventListener('unload', () => sendOobTrace(performanceController));
|
|
1357
|
+
}
|
|
1358
|
+
api.document.addEventListener('visibilitychange', () => {
|
|
1359
|
+
if (api.document.visibilityState === 'hidden') {
|
|
1360
|
+
sendOobTrace(performanceController);
|
|
1361
|
+
}
|
|
1362
|
+
});
|
|
1334
1363
|
if (api.onFirstInputDelay) {
|
|
1335
|
-
// If the fid call back is not called for certain time, continue without it.
|
|
1336
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1337
|
-
let timeoutId = setTimeout(() => {
|
|
1338
|
-
Trace.createOobTrace(performanceController, navigationTimings, paintTimings);
|
|
1339
|
-
timeoutId = undefined;
|
|
1340
|
-
}, FID_WAIT_TIME_MS);
|
|
1341
1364
|
api.onFirstInputDelay((fid) => {
|
|
1342
|
-
|
|
1343
|
-
clearTimeout(timeoutId);
|
|
1344
|
-
Trace.createOobTrace(performanceController, navigationTimings, paintTimings, fid);
|
|
1345
|
-
}
|
|
1365
|
+
firstInputDelay = fid;
|
|
1346
1366
|
});
|
|
1347
1367
|
}
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1368
|
+
api.onLCP((metric) => {
|
|
1369
|
+
var _a;
|
|
1370
|
+
webVitalMetrics.lcp = {
|
|
1371
|
+
value: metric.value,
|
|
1372
|
+
elementAttribution: (_a = metric.attribution) === null || _a === void 0 ? void 0 : _a.element
|
|
1373
|
+
};
|
|
1374
|
+
});
|
|
1375
|
+
api.onCLS((metric) => {
|
|
1376
|
+
var _a;
|
|
1377
|
+
webVitalMetrics.cls = {
|
|
1378
|
+
value: metric.value,
|
|
1379
|
+
elementAttribution: (_a = metric.attribution) === null || _a === void 0 ? void 0 : _a.largestShiftTarget
|
|
1380
|
+
};
|
|
1381
|
+
});
|
|
1382
|
+
api.onINP((metric) => {
|
|
1383
|
+
var _a;
|
|
1384
|
+
webVitalMetrics.inp = {
|
|
1385
|
+
value: metric.value,
|
|
1386
|
+
elementAttribution: (_a = metric.attribution) === null || _a === void 0 ? void 0 : _a.interactionTarget
|
|
1387
|
+
};
|
|
1388
|
+
});
|
|
1351
1389
|
}
|
|
1352
1390
|
function setupUserTimingTraces(performanceController) {
|
|
1353
1391
|
const api = Api.getInstance();
|
|
@@ -1361,13 +1399,27 @@ function setupUserTimingTraces(performanceController) {
|
|
|
1361
1399
|
}
|
|
1362
1400
|
function createUserTimingTrace(performanceController, measure) {
|
|
1363
1401
|
const measureName = measure.name;
|
|
1364
|
-
// Do not create a trace, if the user timing marks and measures are created by
|
|
1402
|
+
// Do not create a trace, if the user timing marks and measures are created by
|
|
1403
|
+
// the sdk itself.
|
|
1365
1404
|
if (measureName.substring(0, TRACE_MEASURE_PREFIX.length) ===
|
|
1366
1405
|
TRACE_MEASURE_PREFIX) {
|
|
1367
1406
|
return;
|
|
1368
1407
|
}
|
|
1369
1408
|
Trace.createUserTimingTrace(performanceController, measureName);
|
|
1370
1409
|
}
|
|
1410
|
+
function sendOobTrace(performanceController) {
|
|
1411
|
+
if (!sentPageLoadTrace) {
|
|
1412
|
+
sentPageLoadTrace = true;
|
|
1413
|
+
const api = Api.getInstance();
|
|
1414
|
+
const navigationTimings = api.getEntriesByType('navigation');
|
|
1415
|
+
const paintTimings = api.getEntriesByType('paint');
|
|
1416
|
+
// On page unload web vitals may be updated so queue the oob trace creation
|
|
1417
|
+
// so that these updates have time to be included.
|
|
1418
|
+
setTimeout(() => {
|
|
1419
|
+
Trace.createOobTrace(performanceController, navigationTimings, paintTimings, webVitalMetrics, firstInputDelay);
|
|
1420
|
+
}, 0);
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1371
1423
|
|
|
1372
1424
|
/**
|
|
1373
1425
|
* @license
|