Update app and tooling
This commit is contained in:
parent
3046531bdd
commit
e620ec7349
4950 changed files with 2975120 additions and 10 deletions
26
node_modules/web-vitals/CHANGELOG.md
generated
vendored
Normal file
26
node_modules/web-vitals/CHANGELOG.md
generated
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# Changelog
|
||||
|
||||
### v0.2.4 (2020-07-23)
|
||||
|
||||
- Remove the unload listener ([#68](https://github.com/GoogleChrome/web-vitals/pull/68))
|
||||
|
||||
### v0.2.3 (2020-06-26)
|
||||
|
||||
- Ensure reports only occur if a PO was created ([#58](https://github.com/GoogleChrome/web-vitals/pull/58))
|
||||
|
||||
### v0.2.2 (2020-05-12)
|
||||
|
||||
- Remove package `type` field ([#35](https://github.com/GoogleChrome/web-vitals/pull/35))
|
||||
|
||||
### v0.2.1 (2020-05-06)
|
||||
|
||||
- Ensure all modules are pure modules ([#23](https://github.com/GoogleChrome/web-vitals/pull/23))
|
||||
- Ensure proper TypeScript exports and config ([#22](https://github.com/GoogleChrome/web-vitals/pull/22))
|
||||
|
||||
### v0.2.0 (2020-05-03)
|
||||
|
||||
- Initial public release
|
||||
|
||||
### v0.1.0 (2020-04-24)
|
||||
|
||||
- Initial pre-release
|
||||
202
node_modules/web-vitals/LICENSE
generated
vendored
Normal file
202
node_modules/web-vitals/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2020 Google LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
423
node_modules/web-vitals/README.md
generated
vendored
Normal file
423
node_modules/web-vitals/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,423 @@
|
|||
# `web-vitals`
|
||||
|
||||
- [Overview](#overview)
|
||||
- [Installation](#installation)
|
||||
- [Usage](#usage)
|
||||
- [Log the results to the console](#log-the-results-to-the-console)
|
||||
- [Report the value on every change](#report-the-value-on-every-change)
|
||||
- [Report only the delta of changes](#report-only-the-delta-of-changes)
|
||||
- [Send the results to an analytics endpoint](#send-the-results-to-an-analytics-endpoint)
|
||||
- [Send the results to Google Analytics](#send-the-results-to-google-analytics)
|
||||
- [Send the results to Google Tag Manager](#send-the-results-to-google-tag-manager)
|
||||
- [Load `web-vitals` from a CDN](#load-web-vitals-from-a-cdn)
|
||||
- [API](#api)
|
||||
- [Types](#types)
|
||||
- [Functions](#functions)
|
||||
- [Development](#development)
|
||||
- [Browser Support](#browser-support)
|
||||
|
||||
## Overview
|
||||
|
||||
The `web-vitals` library is a tiny (~1K), modular library for measuring all the [Web Vitals](https://web.dev/vitals/) metrics on real users, in a way that accurately matches how they're measured by Chrome and reported to other Google tools (e.g. [Chrome User Experience Report](https://developers.google.com/web/tools/chrome-user-experience-report), [Page Speed Insights](https://developers.google.com/speed/pagespeed/insights/), [Search Console's Speed Report](https://webmasters.googleblog.com/2019/11/search-console-speed-report.html)).
|
||||
|
||||
The library supports all of the [Core Web Vitals](https://web.dev/vitals/#core-web-vitals) as well as all of the [other Web Vitals](https://web.dev/vitals/#other-web-vitals) that can be measured [in the field](https://web.dev/user-centric-performance-metrics/#how-metrics-are-measured):
|
||||
|
||||
### Core Web Vitals
|
||||
|
||||
- [Cumulative Layout Shift (CLS)](https://web.dev/cls/)
|
||||
- [First Input Delay (FID)](https://web.dev/fid/)
|
||||
- [Largest Contentful Paint (LCP)](https://web.dev/lcp/)
|
||||
|
||||
### Other Web Vitals
|
||||
|
||||
- [First Contentful Paint (FCP)](https://web.dev/fcp/)
|
||||
- [Time to First Byte (TTFB)](https://web.dev/time-to-first-byte/)
|
||||
|
||||
## Installation
|
||||
|
||||
You can install this library from npm by running:
|
||||
|
||||
```sh
|
||||
npm install web-vitals
|
||||
```
|
||||
|
||||
_**Note:** If you're not using npm, you can still load `web-vitals` via `<script>` tags from a CDN like [unpkg.com](https://unpkg.com). See the [load `web-vitals` from a CDN](#load-web-vitals-from-a-cdn) usage example below for details._
|
||||
|
||||
## Usage
|
||||
|
||||
Each of the Web Vitals metrics are exposed as a single function that takes an `onReport` callback. This callback will fire any time either:
|
||||
|
||||
- The final value of the metric has been determined.
|
||||
- The current metric value needs to be [reported right away](https://developers.google.com/web/updates/2018/07/page-lifecycle-api#advice-hidden) (due to the page being unloaded or backgrounded).
|
||||
|
||||
### Log the results to the console
|
||||
|
||||
The following example logs the result of each metric to the console once its value is ready to report.
|
||||
|
||||
```js
|
||||
import {getCLS, getFID, getLCP} from 'web-vitals';
|
||||
|
||||
getCLS(console.log);
|
||||
getFID(console.log);
|
||||
getLCP(console.log);
|
||||
```
|
||||
|
||||
_**Note:** some of these metrics will not report until the user has interacted with the page, switched tabs, or the page starts to unload. If you don't see the values logged to the console immediately, try switching tabs and then switching back._
|
||||
|
||||
### Report the value on every change
|
||||
|
||||
In most cases, you only want to call `onReport` when the metric is ready. However, for metrics like LCP and CLS (where the value may change over time) you can pass an optional, second argument (`reportAllChanges`). If `true` then `onReport` will be called any time the value of the metric changes, or once the final value has been determined.
|
||||
|
||||
This could be useful if, for example, you want to report the current LCP candidate as the page is loading, or you want to report layout shifts (and the current CLS value) as users are interacting with the page. In general, though, using `reportAllChanges` is not needed (or recommended).
|
||||
|
||||
```js
|
||||
import {getCLS, getFID, getLCP} from 'web-vitals';
|
||||
|
||||
getCLS(console.log, true);
|
||||
getFID(console.log); // Does not take a `reportAllChanges` param.
|
||||
getLCP(console.log, true);
|
||||
```
|
||||
|
||||
_**Note:** when using the `reportAllChanges` option, pay attention to the `isFinal` property of the reported metric, which will indicate whether or not the value might change in the future. See the [API](#api) reference below for more details._
|
||||
|
||||
### Report only the delta of changes
|
||||
|
||||
Some analytics providers allow you to update the value of a metric, even after you've already sent it to their servers (overwriting the previously-sent value with the same `id`).
|
||||
|
||||
Other analytics providers, however, do not allow this, so instead of reporting the new value, you need to report only the delta (the difference between the current value and the last-reported value). You can then compute the total value by summing all metric deltas sent with the same ID.
|
||||
|
||||
The following example shows how to use the `id` and `delta` properties:
|
||||
|
||||
```js
|
||||
import {getCLS, getFID, getLCP} from 'web-vitals';
|
||||
|
||||
function logDelta({name, id, delta}) {
|
||||
console.log(`${name} matching ID ${id} changed by ${delta}`);
|
||||
}
|
||||
|
||||
getCLS(logDelta);
|
||||
getFID(logDelta);
|
||||
getLCP(logDelta);
|
||||
```
|
||||
|
||||
_**Note:** the first time the `onReport` function is called, its `value` and `delta` properties will be the same._
|
||||
|
||||
### Send the results to an analytics endpoint
|
||||
|
||||
The following example measures each of the Core Web Vitals metrics and reports them to a hypothetical `/analytics` endpoint, as soon as each is ready to be sent.
|
||||
|
||||
The `sendToAnalytics()` function uses the [`navigator.sendBeacon()`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon) method (if available), but falls back to the [`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) API when not.
|
||||
|
||||
```js
|
||||
import {getCLS, getFID, getLCP} from 'web-vitals';
|
||||
|
||||
function sendToAnalytics(metric) {
|
||||
const body = JSON.stringify(metric);
|
||||
// Use `navigator.sendBeacon()` if available, falling back to `fetch()`.
|
||||
(navigator.sendBeacon && navigator.sendBeacon('/analytics', body)) ||
|
||||
fetch('/analytics', {body, method: 'POST', keepalive: true});
|
||||
}
|
||||
|
||||
getCLS(sendToAnalytics);
|
||||
getFID(sendToAnalytics);
|
||||
getLCP(sendToAnalytics);
|
||||
```
|
||||
|
||||
### Send the results to Google Analytics
|
||||
|
||||
Google Analytics does not support reporting metric distributions in any of its built-in reports; however, if you set a unique dimension value (in this case, the metric `id`) on every metric instance that you send to Google Analytics, including that dimension in a custom report will allow you to construct a distribution manually.
|
||||
|
||||
Using the [Google Analytics Reporting API](https://developers.google.com/analytics/devguides/reporting) and a tool like [Data Studio](https://datastudio.google.com/) (or your own visualization library), you can create dashboards with histograms reporting quantile data (the 75th percentile is recommended) for all of the Web Vitals metrics.
|
||||
|
||||
The following code examples show how to send your metrics to Google Analytics in order to enable reporting quantile data:
|
||||
|
||||
#### Using `analytics.js`
|
||||
|
||||
```js
|
||||
import {getCLS, getFID, getLCP} from 'web-vitals';
|
||||
|
||||
function sendToGoogleAnalytics({name, delta, id}) {
|
||||
// Assumes the global `ga()` function exists, see:
|
||||
// https://developers.google.com/analytics/devguides/collection/analyticsjs
|
||||
ga('send', 'event', {
|
||||
eventCategory: 'Web Vitals',
|
||||
eventAction: name,
|
||||
// Google Analytics metrics must be integers, so the value is rounded.
|
||||
// For CLS the value is first multiplied by 1000 for greater precision
|
||||
// (note: increase the multiplier for greater precision if needed).
|
||||
eventValue: Math.round(name === 'CLS' ? delta * 1000 : delta),
|
||||
// The `id` value will be unique to the current page load. When sending
|
||||
// multiple values from the same page (e.g. for CLS), Google Analytics can
|
||||
// compute a total by grouping on this ID (note: requires `eventLabel` to
|
||||
// be a dimension in your report).
|
||||
eventLabel: id,
|
||||
// Use a non-interaction event to avoid affecting bounce rate.
|
||||
nonInteraction: true,
|
||||
});
|
||||
}
|
||||
|
||||
getCLS(sendToGoogleAnalytics);
|
||||
getFID(sendToGoogleAnalytics);
|
||||
getLCP(sendToGoogleAnalytics);
|
||||
```
|
||||
|
||||
#### Using `gtag.js`
|
||||
|
||||
```js
|
||||
import {getCLS, getFID, getLCP} from 'web-vitals';
|
||||
|
||||
function sendToGoogleAnalytics({name, delta, id}) {
|
||||
// Assumes the global `gtag()` function exists, see:
|
||||
// https://developers.google.com/analytics/devguides/collection/gtagjs
|
||||
gtag('event', name, {
|
||||
event_category: 'Web Vitals',
|
||||
// Google Analytics metrics must be integers, so the value is rounded.
|
||||
// For CLS the value is first multiplied by 1000 for greater precision
|
||||
// (note: increase the multiplier for greater precision if needed).
|
||||
value: Math.round(name === 'CLS' ? delta * 1000 : delta),
|
||||
// The `id` value will be unique to the current page load. When sending
|
||||
// multiple values from the same page (e.g. for CLS), Google Analytics can
|
||||
// compute a total by grouping on this ID (note: requires `eventLabel` to
|
||||
// be a dimension in your report).
|
||||
event_label: id,
|
||||
// Use a non-interaction event to avoid affecting bounce rate.
|
||||
non_interaction: true,
|
||||
});
|
||||
}
|
||||
|
||||
getCLS(sendToGoogleAnalytics);
|
||||
getFID(sendToGoogleAnalytics);
|
||||
getLCP(sendToGoogleAnalytics);
|
||||
```
|
||||
|
||||
### Send the results to Google Tag Manager
|
||||
|
||||
The following example measures each of the Core Web Vitals metrics and sends them as separate `dataLayer-events` to be used by Google Tag Manager. With the `web-vitals` trigger you send the metrics to any tag inside your account (see [this comment](https://github.com/GoogleChrome/web-vitals/pull/28#discussion_r422701126) for implementation details).
|
||||
|
||||
```js
|
||||
import {getCLS, getFID, getLCP} from 'web-vitals';
|
||||
|
||||
function sendToGTM({name, delta, id}) {
|
||||
// Assumes the global `dataLayer` array exists, see:
|
||||
// https://developers.google.com/tag-manager/devguide
|
||||
dataLayer.push({
|
||||
event: 'web-vitals',
|
||||
event_category: 'Web Vitals',
|
||||
event_action: name,
|
||||
// Google Analytics metrics must be integers, so the value is rounded.
|
||||
// For CLS the value is first multiplied by 1000 for greater precision
|
||||
// (note: increase the multiplier for greater precision if needed).
|
||||
event_value: Math.round(name === 'CLS' ? delta * 1000 : delta),
|
||||
// The `id` value will be unique to the current page load. When sending
|
||||
// multiple values from the same page (e.g. for CLS), Google Analytics can
|
||||
// compute a total by grouping on this ID (note: requires `eventLabel` to
|
||||
// be a dimension in your report).
|
||||
event_label: id,
|
||||
});
|
||||
}
|
||||
|
||||
getCLS(sendToGTM);
|
||||
getFID(sendToGTM);
|
||||
getLCP(sendToGTM);
|
||||
```
|
||||
|
||||
### Load `web-vitals` from a CDN
|
||||
|
||||
The recommended way to use the `web-vitals` package is to install it from npm and integrate it into your build process. However, if you're not using npm, it's still possible to use `web-vitals` by requesting it from a CDN that serves npm package files.
|
||||
|
||||
The following examples show how to load `web-vitals` from [unpkg.com](https://unpkg.com) using either classic or module scripts:
|
||||
|
||||
```html
|
||||
<!-- Load `web-vitals` using a classic script that sets the global `webVitals` object. -->
|
||||
<script defer src="https://unpkg.com/web-vitals@0.2.4/dist/web-vitals.es5.umd.min.js"></script>
|
||||
<script>
|
||||
addEventListener('DOMContentLoaded', function() {
|
||||
webVitals.getCLS(console.log);
|
||||
webVitals.getFID(console.log);
|
||||
webVitals.getLCP(console.log);
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
```html
|
||||
<!-- Load `web-vitals` using a module script. -->
|
||||
<script type="module">
|
||||
import {getCLS, getFID, getLCP} from 'https://unpkg.com/web-vitals@0.2.4/dist/web-vitals.es5.min.js?module';
|
||||
|
||||
getCLS(console.log);
|
||||
getFID(console.log);
|
||||
getLCP(console.log);
|
||||
</script>
|
||||
```
|
||||
|
||||
_**Note:** it's safe to use module scripts in legacy browsers because unknown script types are ignored._
|
||||
|
||||
## API
|
||||
|
||||
### Types:
|
||||
|
||||
#### `Metric`
|
||||
|
||||
```ts
|
||||
interface Metric {
|
||||
// The name of the metric (in acronym form).
|
||||
name: 'CLS' | 'FCP' | 'FID' | 'LCP' | 'TTFB';
|
||||
|
||||
// The current value of the metric.
|
||||
value: number;
|
||||
|
||||
// The delta between the current value and the last-reported value.
|
||||
// On the first report, `delta` and `value` will always be the same.
|
||||
delta: number;
|
||||
|
||||
// A unique ID representing this particular metric that's specific to the
|
||||
// current page. This ID can be used by an analytics tool to dedupe
|
||||
// multiple values sent for the same metric, or to group multiple deltas
|
||||
// together and calculate a total.
|
||||
id: string;
|
||||
|
||||
// `false` if the value of the metric may change in the future,
|
||||
// for the current page.
|
||||
isFinal: boolean;
|
||||
|
||||
// Any performance entries used in the metric value calculation.
|
||||
// Note, entries will be added to the array as the value changes.
|
||||
entries: PerformanceEntry[];
|
||||
}
|
||||
```
|
||||
|
||||
#### `ReportHandler`
|
||||
|
||||
```ts
|
||||
interface ReportHandler {
|
||||
(metric: Metric): void;
|
||||
}
|
||||
```
|
||||
|
||||
### Functions:
|
||||
|
||||
#### `getCLS()`
|
||||
|
||||
```ts
|
||||
type getCLS = (onReport: ReportHandler, reportAllChanges?: boolean) => void
|
||||
```
|
||||
|
||||
Calculates the [CLS](https://web.dev/cls/) value for the current page and calls the `onReport` function once the value is ready to be reported, along with all `layout-shift` performance entries that were used in the metric value calculation. The reported value is a [double](https://heycam.github.io/webidl/#idl-double) (corresponding to a [layout shift value](https://wicg.github.io/layout-instability/#layout-shift-value)).
|
||||
|
||||
If the `reportAllChanges` param is `true`, the `onReport` function will be called any time a new `layout-shift` performance entry is dispatched, or once the final value of the metric has been determined.
|
||||
|
||||
_**Important:** unlike other metrics, CLS continues to monitor changes for the entire lifespan of the page—including if the user returns to the page after it's been hidden/backgrounded. However, since browsers often [will not fire additional callbacks once the user has backgrounded a page](https://developers.google.com/web/updates/2018/07/page-lifecycle-api#advice-hidden), `onReport` is always called when the page's visibility state changes to hidden. As a result, the `onReport` function might be called multiple times during the same page load (see [Reporting only the delta of changes](#report-only-the-delta-of-changes) for how to manage this)._
|
||||
|
||||
#### `getFCP()`
|
||||
|
||||
```ts
|
||||
type getFCP = (onReport: ReportHandler) => void
|
||||
```
|
||||
|
||||
Calculates the [FCP](https://web.dev/fcp/) value for the current page and calls the `onReport` function once the value is ready, along with the relevant `paint` performance entry used to determine the value. The reported value is a `DOMHighResTimeStamp`.
|
||||
|
||||
#### `getFID()`
|
||||
|
||||
```ts
|
||||
type getFID = (onReport: ReportHandler) => void
|
||||
```
|
||||
|
||||
Calculates the [FID](https://web.dev/fid/) value for the current page and calls the `onReport` function once the value is ready, along with the relevant `first-input` performance entry used to determine the value (and optionally the input event if using the [FID polyfill](#fid-polyfill)). The reported value is a `DOMHighResTimeStamp`.
|
||||
|
||||
_**Important:** since FID is only reported after the user interacts with the page, it's possible that it will not be reported for some page loads._
|
||||
|
||||
#### `getLCP()`
|
||||
|
||||
```ts
|
||||
type getLCP = (onReport: ReportHandler, reportAllChanges?: boolean) => void
|
||||
```
|
||||
|
||||
Calculates the [LCP](https://web.dev/lcp/) value for the current page and calls the `onReport` function once the value is ready (along with the relevant `largest-contentful-paint` performance entries used to determine the value). The reported value is a `DOMHighResTimeStamp`.
|
||||
|
||||
If the `reportAllChanges` param is `true`, the `onReport` function will be called any time a new `largest-contentful-paint` performance entry is dispatched, or once the final value of the metric has been determined.
|
||||
|
||||
#### `getTTFB()`
|
||||
|
||||
```ts
|
||||
type getTTFB = (onReport: ReportHandler) => void
|
||||
```
|
||||
|
||||
Calculates the [TTFB](https://web.dev/time-to-first-byte/) value for the current page and calls the `onReport` function once the page has loaded, along with the relevant `navigation` performance entry used to determine the value. The reported value is a `DOMHighResTimeStamp`.
|
||||
|
||||
Note, this function waits until after the page is loaded to call `onReport` in order to ensure all properties of the `navigation` entry are populated. This is useful if you want to report on other metrics exposed by the [Navigation Timing API](https://w3c.github.io/navigation-timing/).
|
||||
|
||||
For example, the TTFB metric starts from the page's [time origin](https://www.w3.org/TR/hr-time-2/#sec-time-origin), which means it [includes](https://developers.google.com/web/fundamentals/performance/navigation-and-resource-timing#the_life_and_timings_of_a_network_request) time spent on DNS lookup, connection negotiation, network latency, and unloading the previous document. If, in addition to TTFB, you want a metric that excludes these timings and _just_ captures the time spent making the request and receiving the first byte of the response, you could compute that from data found on the performance entry:
|
||||
|
||||
```js
|
||||
import {getTTFB} from 'web-vitals';
|
||||
|
||||
getTTFB((metric) => {
|
||||
// Calculate the request time by subtracting from TTFB
|
||||
// everything that happened prior to the request starting.
|
||||
const requestTime = metric.value - metric.entries[0].requestStart;
|
||||
console.log('Request time:', requestTime);
|
||||
});
|
||||
```
|
||||
|
||||
_**Note:** browsers that do not support `navigation` entries will fall back to
|
||||
using `performance.timing` (with the timestamps converted from epoch time to `DOMHighResTimeStamp`). This ensures code referencing these values (like in the example above) will work the same in all browsers._
|
||||
|
||||
## Browser Support
|
||||
|
||||
This code has been tested and will run without error in all major browsers as well as Internet Explorer back to version 9 (when transpiled to ES5). However, some of the APIs required to capture these metrics are only available in Chromium-based browsers (e.g. Chrome, Edge, Opera, Samsung Internet).
|
||||
|
||||
Browser support for each function is as follows:
|
||||
|
||||
- `getCLS()`: Chromium
|
||||
- `getFCP()`: Chromium
|
||||
- `getFID()`: Chromium, Firefox, Safari, Internet Explorer (with polyfill, [see below](#fid-polyfill))
|
||||
- `getLCP()`: Chromium
|
||||
- `getTTFB()`: Chromium, Firefox, Safari, Internet Explorer
|
||||
|
||||
### FID Polyfill
|
||||
|
||||
The `getFID()` function will work in all browsers if the page has included the [FID polyfill](https://github.com/GoogleChromeLabs/first-input-delay).
|
||||
|
||||
Browsers that support the native [Event Timing API](https://wicg.github.io/event-timing/) will use that and report the metric value from the `first-input` performance entry.
|
||||
|
||||
Browsers that **do not** support the native Event Timing API will use the value reported by the polyfill, and the `entries` array will contain a plain-object version of the native [`PerformanceEventTiming`](https://wicg.github.io/event-timing/#sec-performance-event-timing) object.
|
||||
|
||||
_**Note:** the `duration` and `processingEnd` properties of the `PerformanceEventTiming` will not be present, as they're not exposed by the polyfill._
|
||||
|
||||
## Development
|
||||
|
||||
### Building the code
|
||||
|
||||
The `web-vitals` source code is written in TypeScript. To transpile the code and build the production bundles, run the following command.
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
||||
|
||||
To build the code and watch for changes, run:
|
||||
|
||||
```sh
|
||||
npm run watch
|
||||
```
|
||||
|
||||
### Running the tests
|
||||
|
||||
The `web-vitals` code is tested in real browsers using [webdriver.io](https://webdriver.io/). Use the following command to run the tests:
|
||||
|
||||
```sh
|
||||
npm test
|
||||
```
|
||||
|
||||
To test any of the APIs manually, you can start the test server
|
||||
|
||||
```sh
|
||||
npm run test:server
|
||||
```
|
||||
|
||||
Then navigate to `http://localhost:9090/test/<view>`, where `<view>` is the basename of one the templates under [/test/views/](/test/views/).
|
||||
|
||||
You'll likely want to combine this with `npm run watch` to ensure any changes you make are transpiled and rebuilt.
|
||||
|
||||
## License
|
||||
|
||||
[Apache 2.0](/LICENSE)
|
||||
2
node_modules/web-vitals/dist/getCLS.d.ts
generated
vendored
Normal file
2
node_modules/web-vitals/dist/getCLS.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
import { ReportHandler } from './types.js';
|
||||
export declare const getCLS: (onReport: ReportHandler, reportAllChanges?: boolean) => void;
|
||||
42
node_modules/web-vitals/dist/getCLS.js
generated
vendored
Normal file
42
node_modules/web-vitals/dist/getCLS.js
generated
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { initMetric } from './lib/initMetric.js';
|
||||
import { observe } from './lib/observe.js';
|
||||
import { onHidden } from './lib/onHidden.js';
|
||||
import { bindReporter } from './lib/bindReporter.js';
|
||||
export const getCLS = (onReport, reportAllChanges = false) => {
|
||||
const metric = initMetric('CLS', 0);
|
||||
let report;
|
||||
const entryHandler = (entry) => {
|
||||
// Only count layout shifts without recent user input.
|
||||
if (!entry.hadRecentInput) {
|
||||
metric.value += entry.value;
|
||||
metric.entries.push(entry);
|
||||
report();
|
||||
}
|
||||
};
|
||||
const po = observe('layout-shift', entryHandler);
|
||||
if (po) {
|
||||
report = bindReporter(onReport, metric, po, reportAllChanges);
|
||||
onHidden(({ isUnloading }) => {
|
||||
po.takeRecords().map(entryHandler);
|
||||
if (isUnloading) {
|
||||
metric.isFinal = true;
|
||||
}
|
||||
report();
|
||||
});
|
||||
}
|
||||
};
|
||||
2
node_modules/web-vitals/dist/getFCP.d.ts
generated
vendored
Normal file
2
node_modules/web-vitals/dist/getFCP.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
import { ReportHandler } from './types.js';
|
||||
export declare const getFCP: (onReport: ReportHandler) => void;
|
||||
39
node_modules/web-vitals/dist/getFCP.js
generated
vendored
Normal file
39
node_modules/web-vitals/dist/getFCP.js
generated
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { bindReporter } from './lib/bindReporter.js';
|
||||
import { getFirstHidden } from './lib/getFirstHidden.js';
|
||||
import { initMetric } from './lib/initMetric.js';
|
||||
import { observe } from './lib/observe.js';
|
||||
export const getFCP = (onReport) => {
|
||||
const metric = initMetric('FCP');
|
||||
const firstHidden = getFirstHidden();
|
||||
let report;
|
||||
const entryHandler = (entry) => {
|
||||
if (entry.name === 'first-contentful-paint') {
|
||||
// Only report if the page wasn't hidden prior to the first paint.
|
||||
if (entry.startTime < firstHidden.timeStamp) {
|
||||
metric.value = entry.startTime;
|
||||
metric.isFinal = true;
|
||||
metric.entries.push(entry);
|
||||
report();
|
||||
}
|
||||
}
|
||||
};
|
||||
const po = observe('paint', entryHandler);
|
||||
if (po) {
|
||||
report = bindReporter(onReport, metric, po);
|
||||
}
|
||||
};
|
||||
14
node_modules/web-vitals/dist/getFID.d.ts
generated
vendored
Normal file
14
node_modules/web-vitals/dist/getFID.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import { ReportHandler } from './types.js';
|
||||
interface FIDPolyfillCallback {
|
||||
(value: number, event: Event): void;
|
||||
}
|
||||
interface FIDPolyfill {
|
||||
onFirstInputDelay: (onReport: FIDPolyfillCallback) => void;
|
||||
}
|
||||
declare global {
|
||||
interface Window {
|
||||
perfMetrics: FIDPolyfill;
|
||||
}
|
||||
}
|
||||
export declare const getFID: (onReport: ReportHandler) => void;
|
||||
export {};
|
||||
61
node_modules/web-vitals/dist/getFID.js
generated
vendored
Normal file
61
node_modules/web-vitals/dist/getFID.js
generated
vendored
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { bindReporter } from './lib/bindReporter.js';
|
||||
import { getFirstHidden } from './lib/getFirstHidden.js';
|
||||
import { initMetric } from './lib/initMetric.js';
|
||||
import { observe } from './lib/observe.js';
|
||||
import { onHidden } from './lib/onHidden.js';
|
||||
export const getFID = (onReport) => {
|
||||
const metric = initMetric('FID');
|
||||
const firstHidden = getFirstHidden();
|
||||
const entryHandler = (entry) => {
|
||||
// Only report if the page wasn't hidden prior to the first input.
|
||||
if (entry.startTime < firstHidden.timeStamp) {
|
||||
metric.value = entry.processingStart - entry.startTime;
|
||||
metric.entries.push(entry);
|
||||
metric.isFinal = true;
|
||||
report();
|
||||
}
|
||||
};
|
||||
const po = observe('first-input', entryHandler);
|
||||
const report = bindReporter(onReport, metric, po);
|
||||
if (po) {
|
||||
onHidden(() => {
|
||||
po.takeRecords().map(entryHandler);
|
||||
po.disconnect();
|
||||
}, true);
|
||||
}
|
||||
else {
|
||||
if (window.perfMetrics && window.perfMetrics.onFirstInputDelay) {
|
||||
window.perfMetrics.onFirstInputDelay((value, event) => {
|
||||
// Only report if the page wasn't hidden prior to the first input.
|
||||
if (event.timeStamp < firstHidden.timeStamp) {
|
||||
metric.value = value;
|
||||
metric.isFinal = true;
|
||||
metric.entries = [{
|
||||
entryType: 'first-input',
|
||||
name: event.type,
|
||||
target: event.target,
|
||||
cancelable: event.cancelable,
|
||||
startTime: event.timeStamp,
|
||||
processingStart: event.timeStamp + value,
|
||||
}];
|
||||
report();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
2
node_modules/web-vitals/dist/getLCP.d.ts
generated
vendored
Normal file
2
node_modules/web-vitals/dist/getLCP.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
import { ReportHandler } from './types.js';
|
||||
export declare const getLCP: (onReport: ReportHandler, reportAllChanges?: boolean) => void;
|
||||
54
node_modules/web-vitals/dist/getLCP.js
generated
vendored
Normal file
54
node_modules/web-vitals/dist/getLCP.js
generated
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { bindReporter } from './lib/bindReporter.js';
|
||||
import { getFirstHidden } from './lib/getFirstHidden.js';
|
||||
import { initMetric } from './lib/initMetric.js';
|
||||
import { observe } from './lib/observe.js';
|
||||
import { onHidden } from './lib/onHidden.js';
|
||||
import { whenInput } from './lib/whenInput.js';
|
||||
export const getLCP = (onReport, reportAllChanges = false) => {
|
||||
const metric = initMetric('LCP');
|
||||
const firstHidden = getFirstHidden();
|
||||
let report;
|
||||
const entryHandler = (entry) => {
|
||||
// The startTime attribute returns the value of the renderTime if it is not 0,
|
||||
// and the value of the loadTime otherwise.
|
||||
const value = entry.startTime;
|
||||
// If the page was hidden prior to paint time of the entry,
|
||||
// ignore it and mark the metric as final, otherwise add the entry.
|
||||
if (value < firstHidden.timeStamp) {
|
||||
metric.value = value;
|
||||
metric.entries.push(entry);
|
||||
}
|
||||
else {
|
||||
metric.isFinal = true;
|
||||
}
|
||||
report();
|
||||
};
|
||||
const po = observe('largest-contentful-paint', entryHandler);
|
||||
if (po) {
|
||||
report = bindReporter(onReport, metric, po, reportAllChanges);
|
||||
const onFinal = () => {
|
||||
if (!metric.isFinal) {
|
||||
po.takeRecords().map(entryHandler);
|
||||
metric.isFinal = true;
|
||||
report();
|
||||
}
|
||||
};
|
||||
whenInput().then(onFinal);
|
||||
onHidden(onFinal, true);
|
||||
}
|
||||
};
|
||||
2
node_modules/web-vitals/dist/getTTFB.d.ts
generated
vendored
Normal file
2
node_modules/web-vitals/dist/getTTFB.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
import { ReportHandler } from './types.js';
|
||||
export declare const getTTFB: (onReport: ReportHandler) => void;
|
||||
58
node_modules/web-vitals/dist/getTTFB.js
generated
vendored
Normal file
58
node_modules/web-vitals/dist/getTTFB.js
generated
vendored
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { initMetric } from './lib/initMetric.js';
|
||||
const afterLoad = (callback) => {
|
||||
if (document.readyState === 'complete') {
|
||||
// Queue a task so the callback runs after `loadEventEnd`.
|
||||
setTimeout(callback, 0);
|
||||
}
|
||||
else {
|
||||
// Use `pageshow` so the callback runs after `loadEventEnd`.
|
||||
addEventListener('pageshow', callback);
|
||||
}
|
||||
};
|
||||
const getNavigationEntryFromPerformanceTiming = () => {
|
||||
// Really annoying that TypeScript errors when using `PerformanceTiming`.
|
||||
const timing = performance.timing;
|
||||
const navigationEntry = {
|
||||
entryType: 'navigation',
|
||||
startTime: 0,
|
||||
};
|
||||
for (const key in timing) {
|
||||
if (key !== 'navigationStart' && key !== 'toJSON') {
|
||||
navigationEntry[key] = Math.max(timing[key] - timing.navigationStart, 0);
|
||||
}
|
||||
}
|
||||
return navigationEntry;
|
||||
};
|
||||
export const getTTFB = (onReport) => {
|
||||
const metric = initMetric('TTFB');
|
||||
afterLoad(() => {
|
||||
try {
|
||||
// Use the NavigationTiming L2 entry if available.
|
||||
const navigationEntry = performance.getEntriesByType('navigation')[0] ||
|
||||
getNavigationEntryFromPerformanceTiming();
|
||||
metric.value = metric.delta =
|
||||
navigationEntry.responseStart;
|
||||
metric.entries = [navigationEntry];
|
||||
metric.isFinal = true;
|
||||
onReport(metric);
|
||||
}
|
||||
catch (error) {
|
||||
// Do nothing.
|
||||
}
|
||||
});
|
||||
};
|
||||
6
node_modules/web-vitals/dist/index.d.ts
generated
vendored
Normal file
6
node_modules/web-vitals/dist/index.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
export { getCLS } from './getCLS.js';
|
||||
export { getFCP } from './getFCP.js';
|
||||
export { getFID } from './getFID.js';
|
||||
export { getLCP } from './getLCP.js';
|
||||
export { getTTFB } from './getTTFB.js';
|
||||
export * from './types.js';
|
||||
21
node_modules/web-vitals/dist/index.js
generated
vendored
Normal file
21
node_modules/web-vitals/dist/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { getCLS } from './getCLS.js';
|
||||
export { getFCP } from './getFCP.js';
|
||||
export { getFID } from './getFID.js';
|
||||
export { getLCP } from './getLCP.js';
|
||||
export { getTTFB } from './getTTFB.js';
|
||||
export * from './types.js';
|
||||
2
node_modules/web-vitals/dist/lib/bindReporter.d.ts
generated
vendored
Normal file
2
node_modules/web-vitals/dist/lib/bindReporter.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
import { Metric, ReportHandler } from '../types.js';
|
||||
export declare const bindReporter: (callback: ReportHandler, metric: Metric, po: PerformanceObserver | undefined, observeAllUpdates?: boolean | undefined) => () => void;
|
||||
38
node_modules/web-vitals/dist/lib/bindReporter.js
generated
vendored
Normal file
38
node_modules/web-vitals/dist/lib/bindReporter.js
generated
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export const bindReporter = (callback, metric, po, observeAllUpdates) => {
|
||||
let prevValue;
|
||||
return () => {
|
||||
if (po && metric.isFinal) {
|
||||
po.disconnect();
|
||||
}
|
||||
if (metric.value >= 0) {
|
||||
if (observeAllUpdates ||
|
||||
metric.isFinal ||
|
||||
document.visibilityState === 'hidden') {
|
||||
metric.delta = metric.value - (prevValue || 0);
|
||||
// Report the metric if there's a non-zero delta, if the metric is
|
||||
// final, or if no previous value exists (which can happen in the case
|
||||
// of the document becoming hidden when the metric value is 0).
|
||||
// See: https://github.com/GoogleChrome/web-vitals/issues/14
|
||||
if (metric.delta || metric.isFinal || prevValue === undefined) {
|
||||
callback(metric);
|
||||
prevValue = metric.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
6
node_modules/web-vitals/dist/lib/generateUniqueID.d.ts
generated
vendored
Normal file
6
node_modules/web-vitals/dist/lib/generateUniqueID.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/**
|
||||
* Performantly generate a unique, 27-char string by combining the current
|
||||
* timestamp with a 13-digit random number.
|
||||
* @return {string}
|
||||
*/
|
||||
export declare const generateUniqueID: () => string;
|
||||
23
node_modules/web-vitals/dist/lib/generateUniqueID.js
generated
vendored
Normal file
23
node_modules/web-vitals/dist/lib/generateUniqueID.js
generated
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* Performantly generate a unique, 27-char string by combining the current
|
||||
* timestamp with a 13-digit random number.
|
||||
* @return {string}
|
||||
*/
|
||||
export const generateUniqueID = () => {
|
||||
return `${Date.now()}-${Math.floor(Math.random() * (9e12 - 1)) + 1e12}`;
|
||||
};
|
||||
3
node_modules/web-vitals/dist/lib/getFirstHidden.d.ts
generated
vendored
Normal file
3
node_modules/web-vitals/dist/lib/getFirstHidden.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export declare const getFirstHidden: () => {
|
||||
readonly timeStamp: number;
|
||||
};
|
||||
33
node_modules/web-vitals/dist/lib/getFirstHidden.js
generated
vendored
Normal file
33
node_modules/web-vitals/dist/lib/getFirstHidden.js
generated
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { onHidden } from './onHidden.js';
|
||||
let firstHiddenTime;
|
||||
export const getFirstHidden = () => {
|
||||
if (firstHiddenTime === undefined) {
|
||||
// If the document is hidden when this code runs, assume it was hidden
|
||||
// since navigation start. This isn't a perfect heuristic, but it's the
|
||||
// best we can do until an API is available to support querying past
|
||||
// visibilityState.
|
||||
firstHiddenTime = document.visibilityState === 'hidden' ? 0 : Infinity;
|
||||
// Update the time if/when the document becomes hidden.
|
||||
onHidden(({ timeStamp }) => firstHiddenTime = timeStamp, true);
|
||||
}
|
||||
return {
|
||||
get timeStamp() {
|
||||
return firstHiddenTime;
|
||||
}
|
||||
};
|
||||
};
|
||||
2
node_modules/web-vitals/dist/lib/initMetric.d.ts
generated
vendored
Normal file
2
node_modules/web-vitals/dist/lib/initMetric.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
import { Metric } from '../types.js';
|
||||
export declare const initMetric: (name: Metric['name'], value?: number) => Metric;
|
||||
26
node_modules/web-vitals/dist/lib/initMetric.js
generated
vendored
Normal file
26
node_modules/web-vitals/dist/lib/initMetric.js
generated
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { generateUniqueID } from './generateUniqueID.js';
|
||||
export const initMetric = (name, value = -1) => {
|
||||
return {
|
||||
name,
|
||||
value,
|
||||
delta: 0,
|
||||
entries: [],
|
||||
id: generateUniqueID(),
|
||||
isFinal: false
|
||||
};
|
||||
};
|
||||
12
node_modules/web-vitals/dist/lib/observe.d.ts
generated
vendored
Normal file
12
node_modules/web-vitals/dist/lib/observe.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
export interface PerformanceEntryHandler {
|
||||
(entry: PerformanceEntry): void;
|
||||
}
|
||||
/**
|
||||
* Takes a performance entry type and a callback function, and creates a
|
||||
* `PerformanceObserver` instance that will observe the specified entry type
|
||||
* with buffering enabled and call the callback _for each entry_.
|
||||
*
|
||||
* This function also feature-detects entry support and wraps the logic in a
|
||||
* try/catch to avoid errors in unsupporting browsers.
|
||||
*/
|
||||
export declare const observe: (type: string, callback: PerformanceEntryHandler) => PerformanceObserver | undefined;
|
||||
36
node_modules/web-vitals/dist/lib/observe.js
generated
vendored
Normal file
36
node_modules/web-vitals/dist/lib/observe.js
generated
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* Takes a performance entry type and a callback function, and creates a
|
||||
* `PerformanceObserver` instance that will observe the specified entry type
|
||||
* with buffering enabled and call the callback _for each entry_.
|
||||
*
|
||||
* This function also feature-detects entry support and wraps the logic in a
|
||||
* try/catch to avoid errors in unsupporting browsers.
|
||||
*/
|
||||
export const observe = (type, callback) => {
|
||||
try {
|
||||
if (PerformanceObserver.supportedEntryTypes.includes(type)) {
|
||||
const po = new PerformanceObserver((l) => l.getEntries().map(callback));
|
||||
po.observe({ type, buffered: true });
|
||||
return po;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
// Do nothing.
|
||||
}
|
||||
return;
|
||||
};
|
||||
7
node_modules/web-vitals/dist/lib/onHidden.d.ts
generated
vendored
Normal file
7
node_modules/web-vitals/dist/lib/onHidden.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
export interface OnHiddenCallback {
|
||||
({ timeStamp, isUnloading }: {
|
||||
timeStamp: number;
|
||||
isUnloading: boolean;
|
||||
}): void;
|
||||
}
|
||||
export declare const onHidden: (cb: OnHiddenCallback, once?: boolean) => void;
|
||||
38
node_modules/web-vitals/dist/lib/onHidden.js
generated
vendored
Normal file
38
node_modules/web-vitals/dist/lib/onHidden.js
generated
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
let isUnloading = false;
|
||||
let listenersAdded = false;
|
||||
const onPageHide = (event) => {
|
||||
isUnloading = !event.persisted;
|
||||
};
|
||||
const addListeners = () => {
|
||||
addEventListener('pagehide', onPageHide);
|
||||
// `beforeunload` is needed to fix this bug:
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=987409
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
addEventListener('beforeunload', () => { });
|
||||
};
|
||||
export const onHidden = (cb, once = false) => {
|
||||
if (!listenersAdded) {
|
||||
addListeners();
|
||||
listenersAdded = true;
|
||||
}
|
||||
addEventListener('visibilitychange', ({ timeStamp }) => {
|
||||
if (document.visibilityState === 'hidden') {
|
||||
cb({ timeStamp, isUnloading });
|
||||
}
|
||||
}, { capture: true, once });
|
||||
};
|
||||
1
node_modules/web-vitals/dist/lib/whenInput.d.ts
generated
vendored
Normal file
1
node_modules/web-vitals/dist/lib/whenInput.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
export declare const whenInput: () => Promise<Event>;
|
||||
30
node_modules/web-vitals/dist/lib/whenInput.js
generated
vendored
Normal file
30
node_modules/web-vitals/dist/lib/whenInput.js
generated
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
let inputPromise;
|
||||
export const whenInput = () => {
|
||||
if (!inputPromise) {
|
||||
inputPromise = new Promise((r) => {
|
||||
return ['scroll', 'keydown', 'pointerdown'].map((type) => {
|
||||
addEventListener(type, r, {
|
||||
once: true,
|
||||
passive: true,
|
||||
capture: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
return inputPromise;
|
||||
};
|
||||
11
node_modules/web-vitals/dist/types.d.ts
generated
vendored
Normal file
11
node_modules/web-vitals/dist/types.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
export interface Metric {
|
||||
name: 'CLS' | 'FCP' | 'FID' | 'LCP' | 'TTFB';
|
||||
value: number;
|
||||
delta: number;
|
||||
id: string;
|
||||
isFinal: boolean;
|
||||
entries: PerformanceEntry[];
|
||||
}
|
||||
export interface ReportHandler {
|
||||
(metric: Metric): void;
|
||||
}
|
||||
15
node_modules/web-vitals/dist/types.js
generated
vendored
Normal file
15
node_modules/web-vitals/dist/types.js
generated
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
1
node_modules/web-vitals/dist/web-vitals.es5.min.js
generated
vendored
Normal file
1
node_modules/web-vitals/dist/web-vitals.es5.min.js
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
var t,n,e=function(){return"".concat(Date.now(),"-").concat(Math.floor(8999999999999*Math.random())+1e12)},i=function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:-1;return{name:t,value:n,delta:0,entries:[],id:e(),isFinal:!1}},a=function(t,n){try{if(PerformanceObserver.supportedEntryTypes.includes(t)){var e=new PerformanceObserver((function(t){return t.getEntries().map(n)}));return e.observe({type:t,buffered:!0}),e}}catch(t){}},r=!1,o=!1,s=function(t){r=!t.persisted},u=function(){addEventListener("pagehide",s),addEventListener("beforeunload",(function(){}))},c=function(t){var n=arguments.length>1&&void 0!==arguments[1]&&arguments[1];o||(u(),o=!0),addEventListener("visibilitychange",(function(n){var e=n.timeStamp;"hidden"===document.visibilityState&&t({timeStamp:e,isUnloading:r})}),{capture:!0,once:n})},l=function(t,n,e,i){var a;return function(){e&&n.isFinal&&e.disconnect(),n.value>=0&&(i||n.isFinal||"hidden"===document.visibilityState)&&(n.delta=n.value-(a||0),(n.delta||n.isFinal||void 0===a)&&(t(n),a=n.value))}},p=function(t){var n,e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],r=i("CLS",0),o=function(t){t.hadRecentInput||(r.value+=t.value,r.entries.push(t),n())},s=a("layout-shift",o);s&&(n=l(t,r,s,e),c((function(t){var e=t.isUnloading;s.takeRecords().map(o),e&&(r.isFinal=!0),n()})))},d=function(){return void 0===t&&(t="hidden"===document.visibilityState?0:1/0,c((function(n){var e=n.timeStamp;return t=e}),!0)),{get timeStamp(){return t}}},v=function(t){var n,e=i("FCP"),r=d(),o=a("paint",(function(t){"first-contentful-paint"===t.name&&t.startTime<r.timeStamp&&(e.value=t.startTime,e.isFinal=!0,e.entries.push(t),n())}));o&&(n=l(t,e,o))},f=function(t){var n=i("FID"),e=d(),r=function(t){t.startTime<e.timeStamp&&(n.value=t.processingStart-t.startTime,n.entries.push(t),n.isFinal=!0,s())},o=a("first-input",r),s=l(t,n,o);o?c((function(){o.takeRecords().map(r),o.disconnect()}),!0):window.perfMetrics&&window.perfMetrics.onFirstInputDelay&&window.perfMetrics.onFirstInputDelay((function(t,i){i.timeStamp<e.timeStamp&&(n.value=t,n.isFinal=!0,n.entries=[{entryType:"first-input",name:i.type,target:i.target,cancelable:i.cancelable,startTime:i.timeStamp,processingStart:i.timeStamp+t}],s())}))},m=function(){return n||(n=new Promise((function(t){return["scroll","keydown","pointerdown"].map((function(n){addEventListener(n,t,{once:!0,passive:!0,capture:!0})}))}))),n},g=function(t){var n,e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],r=i("LCP"),o=d(),s=function(t){var e=t.startTime;e<o.timeStamp?(r.value=e,r.entries.push(t)):r.isFinal=!0,n()},u=a("largest-contentful-paint",s);if(u){n=l(t,r,u,e);var p=function(){r.isFinal||(u.takeRecords().map(s),r.isFinal=!0,n())};m().then(p),c(p,!0)}},h=function(t){var n,e=i("TTFB");n=function(){try{var n=performance.getEntriesByType("navigation")[0]||function(){var t=performance.timing,n={entryType:"navigation",startTime:0};for(var e in t)"navigationStart"!==e&&"toJSON"!==e&&(n[e]=Math.max(t[e]-t.navigationStart,0));return n}();e.value=e.delta=n.responseStart,e.entries=[n],e.isFinal=!0,t(e)}catch(t){}},"complete"===document.readyState?setTimeout(n,0):addEventListener("pageshow",n)};export{p as getCLS,v as getFCP,f as getFID,g as getLCP,h as getTTFB};
|
||||
1
node_modules/web-vitals/dist/web-vitals.es5.umd.min.js
generated
vendored
Normal file
1
node_modules/web-vitals/dist/web-vitals.es5.umd.min.js
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).webVitals={})}(this,(function(t){"use strict";var e,n,i=function(){return"".concat(Date.now(),"-").concat(Math.floor(8999999999999*Math.random())+1e12)},a=function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:-1;return{name:t,value:e,delta:0,entries:[],id:i(),isFinal:!1}},r=function(t,e){try{if(PerformanceObserver.supportedEntryTypes.includes(t)){var n=new PerformanceObserver((function(t){return t.getEntries().map(e)}));return n.observe({type:t,buffered:!0}),n}}catch(t){}},o=!1,s=!1,u=function(t){o=!t.persisted},c=function(){addEventListener("pagehide",u),addEventListener("beforeunload",(function(){}))},f=function(t){var e=arguments.length>1&&void 0!==arguments[1]&&arguments[1];s||(c(),s=!0),addEventListener("visibilitychange",(function(e){var n=e.timeStamp;"hidden"===document.visibilityState&&t({timeStamp:n,isUnloading:o})}),{capture:!0,once:e})},d=function(t,e,n,i){var a;return function(){n&&e.isFinal&&n.disconnect(),e.value>=0&&(i||e.isFinal||"hidden"===document.visibilityState)&&(e.delta=e.value-(a||0),(e.delta||e.isFinal||void 0===a)&&(t(e),a=e.value))}},l=function(){return void 0===e&&(e="hidden"===document.visibilityState?0:1/0,f((function(t){var n=t.timeStamp;return e=n}),!0)),{get timeStamp(){return e}}},p=function(){return n||(n=new Promise((function(t){return["scroll","keydown","pointerdown"].map((function(e){addEventListener(e,t,{once:!0,passive:!0,capture:!0})}))}))),n};t.getCLS=function(t){var e,n=arguments.length>1&&void 0!==arguments[1]&&arguments[1],i=a("CLS",0),o=function(t){t.hadRecentInput||(i.value+=t.value,i.entries.push(t),e())},s=r("layout-shift",o);s&&(e=d(t,i,s,n),f((function(t){var n=t.isUnloading;s.takeRecords().map(o),n&&(i.isFinal=!0),e()})))},t.getFCP=function(t){var e,n=a("FCP"),i=l(),o=r("paint",(function(t){"first-contentful-paint"===t.name&&t.startTime<i.timeStamp&&(n.value=t.startTime,n.isFinal=!0,n.entries.push(t),e())}));o&&(e=d(t,n,o))},t.getFID=function(t){var e=a("FID"),n=l(),i=function(t){t.startTime<n.timeStamp&&(e.value=t.processingStart-t.startTime,e.entries.push(t),e.isFinal=!0,s())},o=r("first-input",i),s=d(t,e,o);o?f((function(){o.takeRecords().map(i),o.disconnect()}),!0):window.perfMetrics&&window.perfMetrics.onFirstInputDelay&&window.perfMetrics.onFirstInputDelay((function(t,i){i.timeStamp<n.timeStamp&&(e.value=t,e.isFinal=!0,e.entries=[{entryType:"first-input",name:i.type,target:i.target,cancelable:i.cancelable,startTime:i.timeStamp,processingStart:i.timeStamp+t}],s())}))},t.getLCP=function(t){var e,n=arguments.length>1&&void 0!==arguments[1]&&arguments[1],i=a("LCP"),o=l(),s=function(t){var n=t.startTime;n<o.timeStamp?(i.value=n,i.entries.push(t)):i.isFinal=!0,e()},u=r("largest-contentful-paint",s);if(u){e=d(t,i,u,n);var c=function(){i.isFinal||(u.takeRecords().map(s),i.isFinal=!0,e())};p().then(c),f(c,!0)}},t.getTTFB=function(t){var e,n=a("TTFB");e=function(){try{var e=performance.getEntriesByType("navigation")[0]||function(){var t=performance.timing,e={entryType:"navigation",startTime:0};for(var n in t)"navigationStart"!==n&&"toJSON"!==n&&(e[n]=Math.max(t[n]-t.navigationStart,0));return e}();n.value=n.delta=e.responseStart,n.entries=[e],n.isFinal=!0,t(n)}catch(t){}},"complete"===document.readyState?setTimeout(e,0):addEventListener("pageshow",e)},Object.defineProperty(t,"__esModule",{value:!0})}));
|
||||
86
node_modules/web-vitals/package.json
generated
vendored
Normal file
86
node_modules/web-vitals/package.json
generated
vendored
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
{
|
||||
"name": "web-vitals",
|
||||
"version": "0.2.4",
|
||||
"description": "Easily measure performance metrics in JavaScript",
|
||||
"main": "dist/web-vitals.es5.umd.min.js",
|
||||
"module": "dist/web-vitals.es5.min.js",
|
||||
"typings": "dist/index.d.ts",
|
||||
"files": [
|
||||
"dist",
|
||||
"src"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "run-s clean build:ts build:js",
|
||||
"build:ts": "tsc -b",
|
||||
"build:js": "rollup -c",
|
||||
"clean": "rm -rf dist tsconfig.tsbuildinfo",
|
||||
"dev": "run-p watch serve",
|
||||
"lint": "eslint \"*.js\" \"src/**/*.ts\" \"test/**/*.js\"",
|
||||
"lint:fix": "eslint --fix \"*.js\" \"src/**/*.ts\" \"test/**/*.js\"",
|
||||
"postversion": "git push --follow-tags",
|
||||
"release:major": "npm version major -m 'Release v%s' && npm publish",
|
||||
"release:minor": "npm version minor -m 'Release v%s' && npm publish",
|
||||
"release:patch": "npm version patch -m 'Release v%s' && npm publish",
|
||||
"test": "npm-run-all build -p -r test:*",
|
||||
"test:e2e": "wdio wdio.conf.js",
|
||||
"test:server": "node test/server.js",
|
||||
"watch": "run-p watch:*",
|
||||
"watch:ts": "tsc -b -w",
|
||||
"watch:js": "rollup -c -w",
|
||||
"version": "run-s build"
|
||||
},
|
||||
"keywords": [
|
||||
"crux",
|
||||
"performance",
|
||||
"metrics",
|
||||
"CLS",
|
||||
"FCP",
|
||||
"FID",
|
||||
"LCP",
|
||||
"TTFB"
|
||||
],
|
||||
"author": {
|
||||
"name": "Philip Walton",
|
||||
"email": "philip@philipwalton.com",
|
||||
"url": "http://philipwalton.com"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/GoogleChrome/web-vitals.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/GoogleChrome/web-vitals/issues"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "npm run lint"
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.10.5",
|
||||
"@babel/preset-env": "^7.10.4",
|
||||
"@typescript-eslint/eslint-plugin": "^3.7.0",
|
||||
"@typescript-eslint/parser": "^3.7.0",
|
||||
"@wdio/cli": "^6.3.4",
|
||||
"@wdio/local-runner": "^6.3.4",
|
||||
"@wdio/mocha-framework": "^6.3.0",
|
||||
"@wdio/selenium-standalone-service": "^6.1.14",
|
||||
"@wdio/spec-reporter": "^6.3.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"body-parser": "^1.19.0",
|
||||
"chromedriver": "^84.0.1",
|
||||
"eslint": "^7.5.0",
|
||||
"eslint-config-google": "^0.14.0",
|
||||
"express": "^4.17.1",
|
||||
"fs-extra": "^9.0.1",
|
||||
"husky": "^4.2.5",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"nunjucks": "^3.2.2",
|
||||
"rollup": "^2.23.0",
|
||||
"rollup-plugin-babel": "^4.4.0",
|
||||
"rollup-plugin-terser": "^6.1.0",
|
||||
"typescript": "^3.9.7",
|
||||
"wdio-chromedriver-service": "^6.0.3"
|
||||
}
|
||||
}
|
||||
57
node_modules/web-vitals/src/getCLS.ts
generated
vendored
Normal file
57
node_modules/web-vitals/src/getCLS.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {initMetric} from './lib/initMetric.js';
|
||||
import {observe, PerformanceEntryHandler} from './lib/observe.js';
|
||||
import {onHidden} from './lib/onHidden.js';
|
||||
import {bindReporter} from './lib/bindReporter.js';
|
||||
import {ReportHandler} from './types.js';
|
||||
|
||||
|
||||
// https://wicg.github.io/layout-instability/#sec-layout-shift
|
||||
interface LayoutShift extends PerformanceEntry {
|
||||
value: number;
|
||||
hadRecentInput: boolean;
|
||||
}
|
||||
|
||||
export const getCLS = (onReport: ReportHandler, reportAllChanges = false) => {
|
||||
const metric = initMetric('CLS', 0);
|
||||
|
||||
let report: ReturnType<typeof bindReporter>;
|
||||
|
||||
const entryHandler = (entry: LayoutShift) => {
|
||||
// Only count layout shifts without recent user input.
|
||||
if (!entry.hadRecentInput) {
|
||||
(metric.value as number) += entry.value;
|
||||
metric.entries.push(entry);
|
||||
report();
|
||||
}
|
||||
};
|
||||
|
||||
const po = observe('layout-shift', entryHandler as PerformanceEntryHandler);
|
||||
if (po) {
|
||||
report = bindReporter(onReport, metric, po, reportAllChanges);
|
||||
|
||||
onHidden(({isUnloading}) => {
|
||||
po.takeRecords().map(entryHandler as PerformanceEntryHandler);
|
||||
|
||||
if (isUnloading) {
|
||||
metric.isFinal = true;
|
||||
}
|
||||
report();
|
||||
});
|
||||
}
|
||||
};
|
||||
46
node_modules/web-vitals/src/getFCP.ts
generated
vendored
Normal file
46
node_modules/web-vitals/src/getFCP.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {bindReporter} from './lib/bindReporter.js';
|
||||
import {getFirstHidden} from './lib/getFirstHidden.js';
|
||||
import {initMetric} from './lib/initMetric.js';
|
||||
import {observe} from './lib/observe.js';
|
||||
import {ReportHandler} from './types.js';
|
||||
|
||||
|
||||
export const getFCP = (onReport: ReportHandler) => {
|
||||
const metric = initMetric('FCP');
|
||||
const firstHidden = getFirstHidden();
|
||||
|
||||
let report: ReturnType<typeof bindReporter>;
|
||||
|
||||
const entryHandler = (entry: PerformanceEntry) => {
|
||||
if (entry.name === 'first-contentful-paint') {
|
||||
// Only report if the page wasn't hidden prior to the first paint.
|
||||
if (entry.startTime < firstHidden.timeStamp) {
|
||||
metric.value = entry.startTime;
|
||||
metric.isFinal = true;
|
||||
metric.entries.push(entry);
|
||||
report();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const po = observe('paint', entryHandler);
|
||||
if (po) {
|
||||
report = bindReporter(onReport, metric, po);
|
||||
}
|
||||
};
|
||||
88
node_modules/web-vitals/src/getFID.ts
generated
vendored
Normal file
88
node_modules/web-vitals/src/getFID.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {bindReporter} from './lib/bindReporter.js';
|
||||
import {getFirstHidden} from './lib/getFirstHidden.js';
|
||||
import {initMetric} from './lib/initMetric.js';
|
||||
import {observe, PerformanceEntryHandler} from './lib/observe.js';
|
||||
import {onHidden} from './lib/onHidden.js';
|
||||
import {ReportHandler} from './types.js';
|
||||
|
||||
|
||||
interface FIDPolyfillCallback {
|
||||
(value: number, event: Event): void;
|
||||
}
|
||||
|
||||
interface FIDPolyfill {
|
||||
onFirstInputDelay: (onReport: FIDPolyfillCallback) => void;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
perfMetrics: FIDPolyfill;
|
||||
}
|
||||
}
|
||||
|
||||
// https://wicg.github.io/event-timing/#sec-performance-event-timing
|
||||
interface PerformanceEventTiming extends PerformanceEntry {
|
||||
processingStart: DOMHighResTimeStamp;
|
||||
cancelable?: boolean;
|
||||
target?: Element;
|
||||
}
|
||||
|
||||
export const getFID = (onReport: ReportHandler) => {
|
||||
const metric = initMetric('FID');
|
||||
const firstHidden = getFirstHidden();
|
||||
|
||||
const entryHandler = (entry: PerformanceEventTiming) => {
|
||||
// Only report if the page wasn't hidden prior to the first input.
|
||||
if (entry.startTime < firstHidden.timeStamp) {
|
||||
metric.value = entry.processingStart - entry.startTime;
|
||||
metric.entries.push(entry);
|
||||
metric.isFinal = true;
|
||||
report();
|
||||
}
|
||||
};
|
||||
|
||||
const po = observe('first-input', entryHandler as PerformanceEntryHandler);
|
||||
const report = bindReporter(onReport, metric, po);
|
||||
|
||||
if (po) {
|
||||
onHidden(() => {
|
||||
po.takeRecords().map(entryHandler as PerformanceEntryHandler);
|
||||
po.disconnect();
|
||||
}, true);
|
||||
} else {
|
||||
if (window.perfMetrics && window.perfMetrics.onFirstInputDelay) {
|
||||
window.perfMetrics.onFirstInputDelay((value: number, event: Event) => {
|
||||
// Only report if the page wasn't hidden prior to the first input.
|
||||
if (event.timeStamp < firstHidden.timeStamp) {
|
||||
metric.value = value;
|
||||
metric.isFinal = true;
|
||||
metric.entries = [{
|
||||
entryType: 'first-input',
|
||||
name: event.type,
|
||||
target: event.target,
|
||||
cancelable: event.cancelable,
|
||||
startTime: event.timeStamp,
|
||||
processingStart: event.timeStamp + value,
|
||||
} as PerformanceEventTiming];
|
||||
report();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
65
node_modules/web-vitals/src/getLCP.ts
generated
vendored
Normal file
65
node_modules/web-vitals/src/getLCP.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {bindReporter} from './lib/bindReporter.js';
|
||||
import {getFirstHidden} from './lib/getFirstHidden.js';
|
||||
import {initMetric} from './lib/initMetric.js';
|
||||
import {observe, PerformanceEntryHandler} from './lib/observe.js';
|
||||
import {onHidden} from './lib/onHidden.js';
|
||||
import {whenInput} from './lib/whenInput.js';
|
||||
import {ReportHandler} from './types.js';
|
||||
|
||||
|
||||
export const getLCP = (onReport: ReportHandler, reportAllChanges = false) => {
|
||||
const metric = initMetric('LCP');
|
||||
const firstHidden = getFirstHidden();
|
||||
|
||||
let report: ReturnType<typeof bindReporter>;
|
||||
|
||||
const entryHandler = (entry: PerformanceEntry) => {
|
||||
// The startTime attribute returns the value of the renderTime if it is not 0,
|
||||
// and the value of the loadTime otherwise.
|
||||
const value = entry.startTime;
|
||||
|
||||
// If the page was hidden prior to paint time of the entry,
|
||||
// ignore it and mark the metric as final, otherwise add the entry.
|
||||
if (value < firstHidden.timeStamp) {
|
||||
metric.value = value;
|
||||
metric.entries.push(entry);
|
||||
} else {
|
||||
metric.isFinal = true;
|
||||
}
|
||||
|
||||
report();
|
||||
};
|
||||
|
||||
const po = observe('largest-contentful-paint', entryHandler);
|
||||
|
||||
if (po) {
|
||||
report = bindReporter(onReport, metric, po, reportAllChanges);
|
||||
|
||||
const onFinal = () => {
|
||||
if (!metric.isFinal) {
|
||||
po.takeRecords().map(entryHandler as PerformanceEntryHandler);
|
||||
metric.isFinal = true;
|
||||
report();
|
||||
}
|
||||
}
|
||||
|
||||
whenInput().then(onFinal);
|
||||
onHidden(onFinal, true);
|
||||
}
|
||||
};
|
||||
117
node_modules/web-vitals/src/getTTFB.ts
generated
vendored
Normal file
117
node_modules/web-vitals/src/getTTFB.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {initMetric} from './lib/initMetric.js';
|
||||
import {ReportHandler} from './types.js';
|
||||
|
||||
|
||||
interface NavigationEntryShim {
|
||||
// From `PerformanceNavigationTimingEntry`.
|
||||
entryType: string;
|
||||
startTime: number;
|
||||
|
||||
// From `performance.timing`.
|
||||
connectEnd?: number;
|
||||
connectStart?: number;
|
||||
domComplete?: number;
|
||||
domContentLoadedEventEnd?: number;
|
||||
domContentLoadedEventStart?: number;
|
||||
domInteractive?: number;
|
||||
domainLookupEnd?: number;
|
||||
domainLookupStart?: number;
|
||||
fetchStart?: number;
|
||||
loadEventEnd?: number;
|
||||
loadEventStart?: number;
|
||||
redirectEnd?: number;
|
||||
redirectStart?: number;
|
||||
requestStart?: number;
|
||||
responseEnd?: number;
|
||||
responseStart?: number;
|
||||
secureConnectionStart?: number;
|
||||
unloadEventEnd?: number;
|
||||
unloadEventStart?: number;
|
||||
}
|
||||
|
||||
type PerformanceTimingKeys =
|
||||
'connectEnd' |
|
||||
'connectStart' |
|
||||
'domComplete' |
|
||||
'domContentLoadedEventEnd' |
|
||||
'domContentLoadedEventStart' |
|
||||
'domInteractive' |
|
||||
'domainLookupEnd' |
|
||||
'domainLookupStart' |
|
||||
'fetchStart' |
|
||||
'loadEventEnd' |
|
||||
'loadEventStart' |
|
||||
'redirectEnd' |
|
||||
'redirectStart' |
|
||||
'requestStart' |
|
||||
'responseEnd' |
|
||||
'responseStart' |
|
||||
'secureConnectionStart' |
|
||||
'unloadEventEnd' |
|
||||
'unloadEventStart';
|
||||
|
||||
const afterLoad = (callback: () => void) => {
|
||||
if (document.readyState === 'complete') {
|
||||
// Queue a task so the callback runs after `loadEventEnd`.
|
||||
setTimeout(callback, 0);
|
||||
} else {
|
||||
// Use `pageshow` so the callback runs after `loadEventEnd`.
|
||||
addEventListener('pageshow', callback);
|
||||
}
|
||||
}
|
||||
|
||||
const getNavigationEntryFromPerformanceTiming = () => {
|
||||
// Really annoying that TypeScript errors when using `PerformanceTiming`.
|
||||
const timing = performance.timing;
|
||||
|
||||
const navigationEntry: NavigationEntryShim = {
|
||||
entryType: 'navigation',
|
||||
startTime: 0,
|
||||
};
|
||||
|
||||
for (const key in timing) {
|
||||
if (key !== 'navigationStart' && key !== 'toJSON') {
|
||||
navigationEntry[key as PerformanceTimingKeys] = Math.max(
|
||||
timing[key as PerformanceTimingKeys] - timing.navigationStart, 0);
|
||||
}
|
||||
}
|
||||
return navigationEntry as PerformanceNavigationTiming;
|
||||
};
|
||||
|
||||
export const getTTFB = (onReport: ReportHandler) => {
|
||||
const metric = initMetric('TTFB');
|
||||
|
||||
afterLoad(() => {
|
||||
try {
|
||||
// Use the NavigationTiming L2 entry if available.
|
||||
const navigationEntry = performance.getEntriesByType('navigation')[0] ||
|
||||
getNavigationEntryFromPerformanceTiming();
|
||||
|
||||
metric.value = metric.delta =
|
||||
(navigationEntry as PerformanceNavigationTiming).responseStart;
|
||||
|
||||
metric.entries = [navigationEntry];
|
||||
metric.isFinal = true;
|
||||
|
||||
onReport(metric);
|
||||
} catch (error) {
|
||||
// Do nothing.
|
||||
}
|
||||
});
|
||||
};
|
||||
23
node_modules/web-vitals/src/index.ts
generated
vendored
Normal file
23
node_modules/web-vitals/src/index.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export {getCLS} from './getCLS.js';
|
||||
export {getFCP} from './getFCP.js';
|
||||
export {getFID} from './getFID.js';
|
||||
export {getLCP} from './getLCP.js';
|
||||
export {getTTFB} from './getTTFB.js';
|
||||
|
||||
export * from './types.js';
|
||||
48
node_modules/web-vitals/src/lib/bindReporter.ts
generated
vendored
Normal file
48
node_modules/web-vitals/src/lib/bindReporter.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {Metric, ReportHandler} from '../types.js';
|
||||
|
||||
|
||||
export const bindReporter = (
|
||||
callback: ReportHandler,
|
||||
metric: Metric,
|
||||
po: PerformanceObserver | undefined,
|
||||
observeAllUpdates?: boolean,
|
||||
) => {
|
||||
let prevValue: number;
|
||||
return () => {
|
||||
if (po && metric.isFinal) {
|
||||
po.disconnect();
|
||||
}
|
||||
if (metric.value >= 0) {
|
||||
if (observeAllUpdates ||
|
||||
metric.isFinal ||
|
||||
document.visibilityState === 'hidden') {
|
||||
metric.delta = metric.value - (prevValue || 0);
|
||||
|
||||
// Report the metric if there's a non-zero delta, if the metric is
|
||||
// final, or if no previous value exists (which can happen in the case
|
||||
// of the document becoming hidden when the metric value is 0).
|
||||
// See: https://github.com/GoogleChrome/web-vitals/issues/14
|
||||
if (metric.delta || metric.isFinal || prevValue === undefined) {
|
||||
callback(metric);
|
||||
prevValue = metric.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
24
node_modules/web-vitals/src/lib/generateUniqueID.ts
generated
vendored
Normal file
24
node_modules/web-vitals/src/lib/generateUniqueID.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Performantly generate a unique, 27-char string by combining the current
|
||||
* timestamp with a 13-digit random number.
|
||||
* @return {string}
|
||||
*/
|
||||
export const generateUniqueID = () => {
|
||||
return `${Date.now()}-${Math.floor(Math.random() * (9e12 - 1)) + 1e12}`;
|
||||
};
|
||||
39
node_modules/web-vitals/src/lib/getFirstHidden.ts
generated
vendored
Normal file
39
node_modules/web-vitals/src/lib/getFirstHidden.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {onHidden} from './onHidden.js';
|
||||
|
||||
|
||||
let firstHiddenTime: number
|
||||
|
||||
export const getFirstHidden = () => {
|
||||
if (firstHiddenTime === undefined) {
|
||||
// If the document is hidden when this code runs, assume it was hidden
|
||||
// since navigation start. This isn't a perfect heuristic, but it's the
|
||||
// best we can do until an API is available to support querying past
|
||||
// visibilityState.
|
||||
firstHiddenTime = document.visibilityState === 'hidden' ? 0 : Infinity;
|
||||
|
||||
// Update the time if/when the document becomes hidden.
|
||||
onHidden(({timeStamp}) => firstHiddenTime = timeStamp, true);
|
||||
}
|
||||
|
||||
return {
|
||||
get timeStamp() {
|
||||
return firstHiddenTime;
|
||||
}
|
||||
}
|
||||
};
|
||||
30
node_modules/web-vitals/src/lib/initMetric.ts
generated
vendored
Normal file
30
node_modules/web-vitals/src/lib/initMetric.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {Metric} from '../types.js';
|
||||
import {generateUniqueID} from './generateUniqueID.js';
|
||||
|
||||
|
||||
export const initMetric = (name: Metric['name'], value = -1): Metric => {
|
||||
return {
|
||||
name,
|
||||
value,
|
||||
delta: 0,
|
||||
entries: [],
|
||||
id: generateUniqueID(),
|
||||
isFinal: false
|
||||
};
|
||||
};
|
||||
45
node_modules/web-vitals/src/lib/observe.ts
generated
vendored
Normal file
45
node_modules/web-vitals/src/lib/observe.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export interface PerformanceEntryHandler {
|
||||
(entry: PerformanceEntry): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a performance entry type and a callback function, and creates a
|
||||
* `PerformanceObserver` instance that will observe the specified entry type
|
||||
* with buffering enabled and call the callback _for each entry_.
|
||||
*
|
||||
* This function also feature-detects entry support and wraps the logic in a
|
||||
* try/catch to avoid errors in unsupporting browsers.
|
||||
*/
|
||||
export const observe = (
|
||||
type: string,
|
||||
callback: PerformanceEntryHandler,
|
||||
): PerformanceObserver | undefined => {
|
||||
try {
|
||||
if (PerformanceObserver.supportedEntryTypes.includes(type)) {
|
||||
const po: PerformanceObserver =
|
||||
new PerformanceObserver((l) => l.getEntries().map(callback));
|
||||
|
||||
po.observe({type, buffered: true});
|
||||
return po;
|
||||
}
|
||||
} catch (e) {
|
||||
// Do nothing.
|
||||
}
|
||||
return;
|
||||
};
|
||||
50
node_modules/web-vitals/src/lib/onHidden.ts
generated
vendored
Normal file
50
node_modules/web-vitals/src/lib/onHidden.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
export interface OnHiddenCallback {
|
||||
// TODO(philipwalton): add `isPersisted` if needed for bfcache.
|
||||
({timeStamp, isUnloading}: {timeStamp: number; isUnloading: boolean}): void;
|
||||
}
|
||||
|
||||
let isUnloading = false;
|
||||
let listenersAdded = false;
|
||||
|
||||
const onPageHide = (event: PageTransitionEvent) => {
|
||||
isUnloading = !event.persisted;
|
||||
};
|
||||
|
||||
const addListeners = () => {
|
||||
addEventListener('pagehide', onPageHide);
|
||||
|
||||
// `beforeunload` is needed to fix this bug:
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=987409
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
addEventListener('beforeunload', () => {});
|
||||
}
|
||||
|
||||
export const onHidden = (cb: OnHiddenCallback, once = false) => {
|
||||
if (!listenersAdded) {
|
||||
addListeners();
|
||||
listenersAdded = true;
|
||||
}
|
||||
|
||||
addEventListener('visibilitychange', ({timeStamp}) => {
|
||||
if (document.visibilityState === 'hidden') {
|
||||
cb({timeStamp, isUnloading});
|
||||
}
|
||||
}, {capture: true, once});
|
||||
};
|
||||
32
node_modules/web-vitals/src/lib/whenInput.ts
generated
vendored
Normal file
32
node_modules/web-vitals/src/lib/whenInput.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
let inputPromise: Promise<Event>;
|
||||
|
||||
export const whenInput = () => {
|
||||
if (!inputPromise) {
|
||||
inputPromise = new Promise((r) => {
|
||||
return ['scroll', 'keydown', 'pointerdown'].map((type) => {
|
||||
addEventListener(type, r, {
|
||||
once: true,
|
||||
passive: true,
|
||||
capture: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
return inputPromise;
|
||||
};
|
||||
45
node_modules/web-vitals/src/types.ts
generated
vendored
Normal file
45
node_modules/web-vitals/src/types.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export interface Metric {
|
||||
// The name of the metric (in acronym form).
|
||||
name: 'CLS' | 'FCP' | 'FID' | 'LCP' | 'TTFB';
|
||||
|
||||
// The current value of the metric.
|
||||
value: number;
|
||||
|
||||
// The delta between the current value and the last-reported value.
|
||||
// On the first report, `delta` and `value` will always be the same.
|
||||
delta: number;
|
||||
|
||||
// A unique ID representing this particular metric that's specific to the
|
||||
// current page. This ID can be used by an analytics tool to dedupe
|
||||
// multiple values sent for the same metric, or to group multiple deltas
|
||||
// together and calculate a total.
|
||||
id: string;
|
||||
|
||||
// `false` if the value of the metric may change in the future,
|
||||
// for the current page.
|
||||
isFinal: boolean;
|
||||
|
||||
// Any performance entries used in the metric value calculation.
|
||||
// Note, entries will be added to the array as the value changes.
|
||||
entries: PerformanceEntry[];
|
||||
}
|
||||
|
||||
export interface ReportHandler {
|
||||
(metric: Metric): void;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue