@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.
Files changed (45) hide show
  1. package/dist/esm/index.esm2017.js +164 -112
  2. package/dist/esm/index.esm2017.js.map +1 -1
  3. package/dist/esm/src/constants.d.ts +6 -0
  4. package/dist/esm/src/resources/trace.d.ts +3 -1
  5. package/dist/esm/src/{controllers/perf.test.d.ts → resources/web_vitals.d.ts} +10 -2
  6. package/dist/esm/src/services/api_service.d.ts +4 -0
  7. package/dist/esm/src/services/oob_resources_service.d.ts +5 -0
  8. package/dist/esm/src/services/perf_logger.d.ts +1 -0
  9. package/dist/esm/src/services/transport_service.d.ts +5 -0
  10. package/dist/index.cjs.js +164 -112
  11. package/dist/index.cjs.js.map +1 -1
  12. package/dist/src/constants.d.ts +6 -0
  13. package/dist/src/resources/trace.d.ts +3 -1
  14. package/dist/{esm/src/index.test.d.ts → src/resources/web_vitals.d.ts} +10 -2
  15. package/dist/src/services/api_service.d.ts +4 -0
  16. package/dist/src/services/oob_resources_service.d.ts +5 -0
  17. package/dist/src/services/perf_logger.d.ts +1 -0
  18. package/dist/src/services/transport_service.d.ts +5 -0
  19. package/package.json +8 -7
  20. package/dist/esm/src/resources/network_request.test.d.ts +0 -17
  21. package/dist/esm/src/resources/trace.test.d.ts +0 -17
  22. package/dist/esm/src/services/api_service.test.d.ts +0 -17
  23. package/dist/esm/src/services/iid_service.test.d.ts +0 -17
  24. package/dist/esm/src/services/initialization_service.test.d.ts +0 -17
  25. package/dist/esm/src/services/oob_resources_service.test.d.ts +0 -17
  26. package/dist/esm/src/services/perf_logger.test.d.ts +0 -17
  27. package/dist/esm/src/services/remote_config_service.test.d.ts +0 -17
  28. package/dist/esm/src/services/transport_service.test.d.ts +0 -17
  29. package/dist/esm/src/utils/attribute_utils.test.d.ts +0 -17
  30. package/dist/esm/src/utils/metric_utils.test.d.ts +0 -17
  31. package/dist/esm/src/utils/string_merger.test.d.ts +0 -17
  32. package/dist/src/controllers/perf.test.d.ts +0 -17
  33. package/dist/src/index.test.d.ts +0 -17
  34. package/dist/src/resources/network_request.test.d.ts +0 -17
  35. package/dist/src/resources/trace.test.d.ts +0 -17
  36. package/dist/src/services/api_service.test.d.ts +0 -17
  37. package/dist/src/services/iid_service.test.d.ts +0 -17
  38. package/dist/src/services/initialization_service.test.d.ts +0 -17
  39. package/dist/src/services/oob_resources_service.test.d.ts +0 -17
  40. package/dist/src/services/perf_logger.test.d.ts +0 -17
  41. package/dist/src/services/remote_config_service.test.d.ts +0 -17
  42. package/dist/src/services/transport_service.test.d.ts +0 -17
  43. package/dist/src/utils/attribute_utils.test.d.ts +0 -17
  44. package/dist/src/utils/metric_utils.test.d.ts +0 -17
  45. 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.6.12";
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
- // If there are no events to process, wait for DEFAULT_SEND_INTERVAL_MS and try again.
719
- if (!queue.length) {
720
- return processQueue(DEFAULT_SEND_INTERVAL_MS);
727
+ if (queue.length > 0) {
728
+ dispatchQueueEvents();
721
729
  }
722
- dispatchQueueEvents();
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
- sendEventsToFl(data, staged).catch(() => {
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
- return fetch(flTransportFullUrl, {
787
- method: 'POST',
788
- body: JSON.stringify(data)
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 = transportHandler(serializer);
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
- setTimeout(() => sendLog(trace, 1 /* ResourceType.Trace */), 0);
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
- setTimeout(() => sendLog(networkRequest, 0 /* ResourceType.NetworkRequest */), 0);
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
- function createNetworkRequestEntry(performanceController, entry) {
1269
- const performanceEntry = entry;
1270
- if (!performanceEntry || performanceEntry.responseStart === undefined) {
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 navigation timing
1315
- // object has a duration of 0. The setup should run after all current tasks in js queue.
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
- const navigationTimings = api.getEntriesByType('navigation');
1331
- const paintTimings = api.getEntriesByType('paint');
1332
- // If First Input Delay polyfill is added to the page, report the fid value.
1333
- // https://github.com/GoogleChromeLabs/first-input-delay
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
- if (timeoutId) {
1343
- clearTimeout(timeoutId);
1344
- Trace.createOobTrace(performanceController, navigationTimings, paintTimings, fid);
1345
- }
1365
+ firstInputDelay = fid;
1346
1366
  });
1347
1367
  }
1348
- else {
1349
- Trace.createOobTrace(performanceController, navigationTimings, paintTimings);
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 the sdk itself.
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