added orai

This commit is contained in:
szabomarton 2024-12-09 13:11:41 +01:00
parent 24a1542fff
commit bc5e7d251a
168 changed files with 24419 additions and 0 deletions

BIN
24_12_09/24_12_09.pptx Normal file

Binary file not shown.

23
24_12_09/komponensek/.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

View File

@ -0,0 +1,70 @@
# Getting Started with Create React App
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
## Available Scripts
In the project directory, you can run:
### `npm start`
Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
The page will reload when you make changes.\
You may also see any lint errors in the console.
### `npm test`
Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `npm run build`
Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.\
Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `npm run eject`
**Note: this is a one-way operation. Once you `eject`, you can't go back!**
If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
## Learn More
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
To learn React, check out the [React documentation](https://reactjs.org/).
### Code Splitting
This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
### Analyzing the Bundle Size
This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
### Making a Progressive Web App
This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
### Advanced Configuration
This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
### Deployment
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
### `npm run build` fails to minify
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)

15828
24_12_09/komponensek/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,35 @@
{
"name": "komponensek",
"version": "0.1.0",
"private": true,
"dependencies": {
"cra-template": "1.2.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-scripts": "5.0.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@ -0,0 +1,25 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

View File

@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

View File

@ -0,0 +1,16 @@
import React from "react";
function Alma(){
return (
<>
<p>
Ez alma!
</p>
<p>
Ugye?
</p>
</>
)
}
export default Alma;

View File

@ -0,0 +1,38 @@
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

View File

@ -0,0 +1,25 @@
import logo from './logo.svg';
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;

View File

@ -0,0 +1,8 @@
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});

View File

@ -0,0 +1,18 @@
import React from "react";
import { useState } from "react";
function Counter(){
const [count, setCount] = useState(0);
function handleClick(){
setCount(count + 1);
}
return (
<button onClick={handleClick}>
Ennyiszer nyomtál meg: {count}
</button>
)
}
export default Counter;

View File

@ -0,0 +1,45 @@
import React from 'react';
import Alma from './Alma';
import { useState } from 'react';
/*
class Gyumolcs extends React.Component {
render(){
return (
<>
<p>Első bekezdés</p>
<p>Második bekezdés</p>
</>
)
}
}
*/
/*
function Gyumolcs(props){
return (
<>
<p>Első bekezdés</p>
<p>Második bekezdés</p>
<p>
Alma komponens felhasználása
</p>
<Alma></Alma>
<p>A választott gyümölcs: {props.gyumolcs.nev}, ami {props.gyumolcs.iz} ízű</p>
</>
)
}
*/
function Gyumolcs(props){
let [nev, setNev] = useState(props.gyumolcs.nev);
return (
<>
<p>
A létrehozott gyümölcs: {nev}, ami {props.gyumolcs.iz} ízű
</p>
</>
)
}
export default Gyumolcs;

View File

@ -0,0 +1,13 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}

View File

@ -0,0 +1,23 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import Gyumolcs from './Gyumolcsok';
import Counter from './Button';
let gyumi = {nev: "Banán", iz: "Édes"};
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<>
<Gyumolcs gyumolcs={gyumi}/>
<Counter></Counter>
</>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -0,0 +1,13 @@
const reportWebVitals = onPerfEntry => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;

View File

@ -0,0 +1,5 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';

12
24_12_09/node_modules/.package-lock.json generated vendored Normal file
View File

@ -0,0 +1,12 @@
{
"name": "24_12_09",
"lockfileVersion": 3,
"requires": true,
"packages": {
"node_modules/web-vitals": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz",
"integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw=="
}
}
}

202
24_12_09/node_modules/web-vitals/LICENSE generated vendored Normal file
View 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.

1156
24_12_09/node_modules/web-vitals/README.md generated vendored Normal file

File diff suppressed because it is too large Load Diff

16
24_12_09/node_modules/web-vitals/attribution.d.ts generated vendored Normal file
View File

@ -0,0 +1,16 @@
/*
Copyright 2022 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 * from './dist/modules/attribution/index.js';

18
24_12_09/node_modules/web-vitals/attribution.js generated vendored Normal file
View File

@ -0,0 +1,18 @@
/*
Copyright 2022 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.
*/
// Creates the `web-vitals/attribution` import in node-based bundlers.
// This will not be needed when export maps are widely supported.
export * from './dist/web-vitals.attribution.js';

View File

@ -0,0 +1,7 @@
export {
/**
* @deprecated Use `onINP()` instead.
*/
onFID, } from './onFID.js';
export { FIDThresholds } from '../onFID.js';
export * from '../types.js';

View File

@ -0,0 +1,22 @@
/*
* Copyright 2024 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 {
/**
* @deprecated Use `onINP()` instead.
*/
onFID, } from './onFID.js';
export { FIDThresholds } from '../onFID.js';
export * from '../types.js';

View File

@ -0,0 +1,12 @@
export { onCLS } from './onCLS.js';
export { onFCP } from './onFCP.js';
export { onINP } from './onINP.js';
export { onLCP } from './onLCP.js';
export { onTTFB } from './onTTFB.js';
export { CLSThresholds } from '../onCLS.js';
export { FCPThresholds } from '../onFCP.js';
export { INPThresholds } from '../onINP.js';
export { LCPThresholds } from '../onLCP.js';
export { TTFBThresholds } from '../onTTFB.js';
export * from './deprecated.js';
export * from '../types.js';

View File

@ -0,0 +1,27 @@
/*
* Copyright 2022 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 { onCLS } from './onCLS.js';
export { onFCP } from './onFCP.js';
export { onINP } from './onINP.js';
export { onLCP } from './onLCP.js';
export { onTTFB } from './onTTFB.js';
export { CLSThresholds } from '../onCLS.js';
export { FCPThresholds } from '../onFCP.js';
export { INPThresholds } from '../onINP.js';
export { LCPThresholds } from '../onLCP.js';
export { TTFBThresholds } from '../onTTFB.js';
export * from './deprecated.js';
export * from '../types.js';

View File

@ -0,0 +1,23 @@
import { CLSMetricWithAttribution, ReportOpts } from '../types.js';
/**
* Calculates the [CLS](https://web.dev/articles/cls) value for the current page and
* calls the `callback` 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` (corresponding to a
* [layout shift score](https://web.dev/articles/cls#layout_shift_score)).
*
* If the `reportAllChanges` configuration option is set to `true`, the
* `callback` function will be called as soon as the value is initially
* determined as well as any time the value changes throughout the page
* lifespan.
*
* _**Important:** CLS should be continually monitored for changes throughout
* the entire lifespan of a pageincluding 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://developer.chrome.com/blog/page-lifecycle-api/#advice-hidden),
* `callback` is always called when the page's visibility state changes to
* hidden. As a result, the `callback` function might be called multiple times
* during the same page load._
*/
export declare const onCLS: (onReport: (metric: CLSMetricWithAttribution) => void, opts?: ReportOpts) => void;

View File

@ -0,0 +1,74 @@
/*
* Copyright 2022 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 { getLoadState } from '../lib/getLoadState.js';
import { getSelector } from '../lib/getSelector.js';
import { onCLS as unattributedOnCLS } from '../onCLS.js';
const getLargestLayoutShiftEntry = (entries) => {
return entries.reduce((a, b) => (a && a.value > b.value ? a : b));
};
const getLargestLayoutShiftSource = (sources) => {
return sources.find((s) => s.node && s.node.nodeType === 1) || sources[0];
};
const attributeCLS = (metric) => {
// Use an empty object if no other attribution has been set.
let attribution = {};
if (metric.entries.length) {
const largestEntry = getLargestLayoutShiftEntry(metric.entries);
if (largestEntry && largestEntry.sources && largestEntry.sources.length) {
const largestSource = getLargestLayoutShiftSource(largestEntry.sources);
if (largestSource) {
attribution = {
largestShiftTarget: getSelector(largestSource.node),
largestShiftTime: largestEntry.startTime,
largestShiftValue: largestEntry.value,
largestShiftSource: largestSource,
largestShiftEntry: largestEntry,
loadState: getLoadState(largestEntry.startTime),
};
}
}
}
// Use Object.assign to set property to keep tsc happy.
const metricWithAttribution = Object.assign(metric, { attribution });
return metricWithAttribution;
};
/**
* Calculates the [CLS](https://web.dev/articles/cls) value for the current page and
* calls the `callback` 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` (corresponding to a
* [layout shift score](https://web.dev/articles/cls#layout_shift_score)).
*
* If the `reportAllChanges` configuration option is set to `true`, the
* `callback` function will be called as soon as the value is initially
* determined as well as any time the value changes throughout the page
* lifespan.
*
* _**Important:** CLS should be continually monitored for changes throughout
* the entire lifespan of a pageincluding 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://developer.chrome.com/blog/page-lifecycle-api/#advice-hidden),
* `callback` is always called when the page's visibility state changes to
* hidden. As a result, the `callback` function might be called multiple times
* during the same page load._
*/
export const onCLS = (onReport, opts) => {
unattributedOnCLS((metric) => {
const metricWithAttribution = attributeCLS(metric);
onReport(metricWithAttribution);
}, opts);
};

View File

@ -0,0 +1,8 @@
import { FCPMetricWithAttribution, ReportOpts } from '../types.js';
/**
* Calculates the [FCP](https://web.dev/articles/fcp) value for the current page and
* calls the `callback` function once the value is ready, along with the
* relevant `paint` performance entry used to determine the value. The reported
* value is a `DOMHighResTimeStamp`.
*/
export declare const onFCP: (onReport: (metric: FCPMetricWithAttribution) => void, opts?: ReportOpts) => void;

View File

@ -0,0 +1,57 @@
/*
* Copyright 2022 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 { getBFCacheRestoreTime } from '../lib/bfcache.js';
import { getLoadState } from '../lib/getLoadState.js';
import { getNavigationEntry } from '../lib/getNavigationEntry.js';
import { onFCP as unattributedOnFCP } from '../onFCP.js';
const attributeFCP = (metric) => {
// Use a default object if no other attribution has been set.
let attribution = {
timeToFirstByte: 0,
firstByteToFCP: metric.value,
loadState: getLoadState(getBFCacheRestoreTime()),
};
if (metric.entries.length) {
const navigationEntry = getNavigationEntry();
const fcpEntry = metric.entries[metric.entries.length - 1];
if (navigationEntry) {
const activationStart = navigationEntry.activationStart || 0;
const ttfb = Math.max(0, navigationEntry.responseStart - activationStart);
attribution = {
timeToFirstByte: ttfb,
firstByteToFCP: metric.value - ttfb,
loadState: getLoadState(metric.entries[0].startTime),
navigationEntry,
fcpEntry,
};
}
}
// Use Object.assign to set property to keep tsc happy.
const metricWithAttribution = Object.assign(metric, { attribution });
return metricWithAttribution;
};
/**
* Calculates the [FCP](https://web.dev/articles/fcp) value for the current page and
* calls the `callback` function once the value is ready, along with the
* relevant `paint` performance entry used to determine the value. The reported
* value is a `DOMHighResTimeStamp`.
*/
export const onFCP = (onReport, opts) => {
unattributedOnFCP((metric) => {
const metricWithAttribution = attributeFCP(metric);
onReport(metricWithAttribution);
}, opts);
};

View File

@ -0,0 +1,11 @@
import { FIDMetricWithAttribution, ReportOpts } from '../types.js';
/**
* Calculates the [FID](https://web.dev/articles/fid) value for the current page and
* calls the `callback` function once the value is ready, along with the
* relevant `first-input` performance entry used to determine the value. 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._
*/
export declare const onFID: (onReport: (metric: FIDMetricWithAttribution) => void, opts?: ReportOpts) => void;

View File

@ -0,0 +1,46 @@
/*
* Copyright 2022 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 { getLoadState } from '../lib/getLoadState.js';
import { getSelector } from '../lib/getSelector.js';
import { onFID as unattributedOnFID } from '../onFID.js';
const attributeFID = (metric) => {
const fidEntry = metric.entries[0];
const attribution = {
eventTarget: getSelector(fidEntry.target),
eventType: fidEntry.name,
eventTime: fidEntry.startTime,
eventEntry: fidEntry,
loadState: getLoadState(fidEntry.startTime),
};
// Use Object.assign to set property to keep tsc happy.
const metricWithAttribution = Object.assign(metric, { attribution });
return metricWithAttribution;
};
/**
* Calculates the [FID](https://web.dev/articles/fid) value for the current page and
* calls the `callback` function once the value is ready, along with the
* relevant `first-input` performance entry used to determine the value. 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._
*/
export const onFID = (onReport, opts) => {
unattributedOnFID((metric) => {
const metricWithAttribution = attributeFID(metric);
onReport(metricWithAttribution);
}, opts);
};

View File

@ -0,0 +1,30 @@
import { INPMetricWithAttribution, ReportOpts } from '../types.js';
export declare const interactionTargetMap: Map<number, Node>;
/**
* Calculates the [INP](https://web.dev/articles/inp) value for the current
* page and calls the `callback` function once the value is ready, along with
* the `event` performance entries reported for that interaction. The reported
* value is a `DOMHighResTimeStamp`.
*
* A custom `durationThreshold` configuration option can optionally be passed to
* control what `event-timing` entries are considered for INP reporting. The
* default threshold is `40`, which means INP scores of less than 40 are
* reported as 0. Note that this will not affect your 75th percentile INP value
* unless that value is also less than 40 (well below the recommended
* [good](https://web.dev/articles/inp#what_is_a_good_inp_score) threshold).
*
* If the `reportAllChanges` configuration option is set to `true`, the
* `callback` function will be called as soon as the value is initially
* determined as well as any time the value changes throughout the page
* lifespan.
*
* _**Important:** INP should be continually monitored for changes throughout
* the entire lifespan of a pageincluding 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://developer.chrome.com/blog/page-lifecycle-api/#advice-hidden),
* `callback` is always called when the page's visibility state changes to
* hidden. As a result, the `callback` function might be called multiple times
* during the same page load._
*/
export declare const onINP: (onReport: (metric: INPMetricWithAttribution) => void, opts?: ReportOpts) => void;

View File

@ -0,0 +1,256 @@
/*
* Copyright 2022 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 { getLoadState } from '../lib/getLoadState.js';
import { getSelector } from '../lib/getSelector.js';
import { longestInteractionList, entryPreProcessingCallbacks, longestInteractionMap, } from '../lib/interactions.js';
import { observe } from '../lib/observe.js';
import { whenIdle } from '../lib/whenIdle.js';
import { onINP as unattributedOnINP } from '../onINP.js';
// The maximum number of previous frames for which data is kept.
// Storing data about previous frames is necessary to handle cases where event
// and LoAF entries are dispatched out of order, and so a buffer of previous
// frame data is needed to determine various bits of INP attribution once all
// the frame-related data has come in.
// In most cases this out-of-order data is only off by a frame or two, so
// keeping the most recent 50 should be more than sufficient.
const MAX_PREVIOUS_FRAMES = 50;
// A PerformanceObserver, observing new `long-animation-frame` entries.
// If this variable is defined it means the browser supports LoAF.
let loafObserver;
// A list of LoAF entries that have been dispatched and could potentially
// intersect with the INP candidate interaction. Note that periodically this
// list is cleaned up and entries that are known to not match INP are removed.
let pendingLoAFs = [];
// An array of groups of all the event timing entries that occurred within a
// particular frame. Note that periodically this array is cleaned up and entries
// that are known to not match INP are removed.
let pendingEntriesGroups = [];
// The `processingEnd` time of most recently-processed event, chronologically.
let latestProcessingEnd = 0;
// A WeakMap to look up the event-timing-entries group of a given entry.
// Note that this only maps from "important" entries: either the first input or
// those with an `interactionId`.
const entryToEntriesGroupMap = new WeakMap();
// A mapping of interactionIds to the target Node.
export const interactionTargetMap = new Map();
// A reference to the idle task used to clean up entries from the above
// variables. If the value is -1 it means no task is queue, and if it's
// greater than -1 the value corresponds to the idle callback handle.
let idleHandle = -1;
/**
* Adds new LoAF entries to the `pendingLoAFs` list.
*/
const handleLoAFEntries = (entries) => {
pendingLoAFs = pendingLoAFs.concat(entries);
queueCleanup();
};
// Get a reference to the interaction target element in case it's removed
// from the DOM later.
const saveInteractionTarget = (entry) => {
if (entry.interactionId &&
entry.target &&
!interactionTargetMap.has(entry.interactionId)) {
interactionTargetMap.set(entry.interactionId, entry.target);
}
};
/**
* Groups entries that were presented within the same animation frame by
* a common `renderTime`. This function works by referencing
* `pendingEntriesGroups` and using an existing render time if one is found
* (otherwise creating a new one). This function also adds all interaction
* entries to an `entryToRenderTimeMap` WeakMap so that the "grouped" entries
* can be looked up later.
*/
const groupEntriesByRenderTime = (entry) => {
const renderTime = entry.startTime + entry.duration;
let group;
latestProcessingEnd = Math.max(latestProcessingEnd, entry.processingEnd);
// Iterate over all previous render times in reverse order to find a match.
// Go in reverse since the most likely match will be at the end.
for (let i = pendingEntriesGroups.length - 1; i >= 0; i--) {
const potentialGroup = pendingEntriesGroups[i];
// If a group's render time is within 8ms of the entry's render time,
// assume they were part of the same frame and add it to the group.
if (Math.abs(renderTime - potentialGroup.renderTime) <= 8) {
group = potentialGroup;
group.startTime = Math.min(entry.startTime, group.startTime);
group.processingStart = Math.min(entry.processingStart, group.processingStart);
group.processingEnd = Math.max(entry.processingEnd, group.processingEnd);
group.entries.push(entry);
break;
}
}
// If there was no matching group, assume this is a new frame.
if (!group) {
group = {
startTime: entry.startTime,
processingStart: entry.processingStart,
processingEnd: entry.processingEnd,
renderTime,
entries: [entry],
};
pendingEntriesGroups.push(group);
}
// Store the grouped render time for this entry for reference later.
if (entry.interactionId || entry.entryType === 'first-input') {
entryToEntriesGroupMap.set(entry, group);
}
queueCleanup();
};
const queueCleanup = () => {
// Queue cleanup of entries that are not part of any INP candidates.
if (idleHandle < 0) {
idleHandle = whenIdle(cleanupEntries);
}
};
const cleanupEntries = () => {
// Delete any stored interaction target elements if they're not part of one
// of the 10 longest interactions.
if (interactionTargetMap.size > 10) {
interactionTargetMap.forEach((_, key) => {
if (!longestInteractionMap.has(key)) {
interactionTargetMap.delete(key);
}
});
}
// Keep all render times that are part of a pending INP candidate or
// that occurred within the 50 most recently-dispatched groups of events.
const longestInteractionGroups = longestInteractionList.map((i) => {
return entryToEntriesGroupMap.get(i.entries[0]);
});
const minIndex = pendingEntriesGroups.length - MAX_PREVIOUS_FRAMES;
pendingEntriesGroups = pendingEntriesGroups.filter((group, index) => {
if (index >= minIndex)
return true;
return longestInteractionGroups.includes(group);
});
// Keep all pending LoAF entries that either:
// 1) intersect with entries in the newly cleaned up `pendingEntriesGroups`
// 2) occur after the most recently-processed event entry (for up to MAX_PREVIOUS_FRAMES)
const loafsToKeep = new Set();
for (let i = 0; i < pendingEntriesGroups.length; i++) {
const group = pendingEntriesGroups[i];
getIntersectingLoAFs(group.startTime, group.processingEnd).forEach((loaf) => {
loafsToKeep.add(loaf);
});
}
const prevFrameIndexCutoff = pendingLoAFs.length - 1 - MAX_PREVIOUS_FRAMES;
// Filter `pendingLoAFs` to preserve LoAF order.
pendingLoAFs = pendingLoAFs.filter((loaf, index) => {
if (loaf.startTime > latestProcessingEnd && index > prevFrameIndexCutoff) {
return true;
}
return loafsToKeep.has(loaf);
});
// Reset the idle callback handle so it can be queued again.
idleHandle = -1;
};
entryPreProcessingCallbacks.push(saveInteractionTarget, groupEntriesByRenderTime);
const getIntersectingLoAFs = (start, end) => {
const intersectingLoAFs = [];
for (let i = 0, loaf; (loaf = pendingLoAFs[i]); i++) {
// If the LoAF ends before the given start time, ignore it.
if (loaf.startTime + loaf.duration < start)
continue;
// If the LoAF starts after the given end time, ignore it and all
// subsequent pending LoAFs (because they're in time order).
if (loaf.startTime > end)
break;
// Still here? If so this LoAF intersects with the interaction.
intersectingLoAFs.push(loaf);
}
return intersectingLoAFs;
};
const attributeINP = (metric) => {
const firstEntry = metric.entries[0];
const group = entryToEntriesGroupMap.get(firstEntry);
const processingStart = firstEntry.processingStart;
const processingEnd = group.processingEnd;
// Sort the entries in processing time order.
const processedEventEntries = group.entries.sort((a, b) => {
return a.processingStart - b.processingStart;
});
const longAnimationFrameEntries = getIntersectingLoAFs(firstEntry.startTime, processingEnd);
// The first interaction entry may not have a target defined, so use the
// first one found in the entry list.
// TODO: when the following bug is fixed just use `firstInteractionEntry`.
// https://bugs.chromium.org/p/chromium/issues/detail?id=1367329
// As a fallback, also check the interactionTargetMap (to account for
// cases where the element is removed from the DOM before reporting happens).
const firstEntryWithTarget = metric.entries.find((entry) => entry.target);
const interactionTargetElement = (firstEntryWithTarget && firstEntryWithTarget.target) ||
interactionTargetMap.get(firstEntry.interactionId);
// Since entry durations are rounded to the nearest 8ms, we need to clamp
// the `nextPaintTime` value to be higher than the `processingEnd` or
// end time of any LoAF entry.
const nextPaintTimeCandidates = [
firstEntry.startTime + firstEntry.duration,
processingEnd,
].concat(longAnimationFrameEntries.map((loaf) => loaf.startTime + loaf.duration));
const nextPaintTime = Math.max.apply(Math, nextPaintTimeCandidates);
const attribution = {
interactionTarget: getSelector(interactionTargetElement),
interactionTargetElement: interactionTargetElement,
interactionType: firstEntry.name.startsWith('key') ? 'keyboard' : 'pointer',
interactionTime: firstEntry.startTime,
nextPaintTime: nextPaintTime,
processedEventEntries: processedEventEntries,
longAnimationFrameEntries: longAnimationFrameEntries,
inputDelay: processingStart - firstEntry.startTime,
processingDuration: processingEnd - processingStart,
presentationDelay: Math.max(nextPaintTime - processingEnd, 0),
loadState: getLoadState(firstEntry.startTime),
};
// Use Object.assign to set property to keep tsc happy.
const metricWithAttribution = Object.assign(metric, { attribution });
return metricWithAttribution;
};
/**
* Calculates the [INP](https://web.dev/articles/inp) value for the current
* page and calls the `callback` function once the value is ready, along with
* the `event` performance entries reported for that interaction. The reported
* value is a `DOMHighResTimeStamp`.
*
* A custom `durationThreshold` configuration option can optionally be passed to
* control what `event-timing` entries are considered for INP reporting. The
* default threshold is `40`, which means INP scores of less than 40 are
* reported as 0. Note that this will not affect your 75th percentile INP value
* unless that value is also less than 40 (well below the recommended
* [good](https://web.dev/articles/inp#what_is_a_good_inp_score) threshold).
*
* If the `reportAllChanges` configuration option is set to `true`, the
* `callback` function will be called as soon as the value is initially
* determined as well as any time the value changes throughout the page
* lifespan.
*
* _**Important:** INP should be continually monitored for changes throughout
* the entire lifespan of a pageincluding 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://developer.chrome.com/blog/page-lifecycle-api/#advice-hidden),
* `callback` is always called when the page's visibility state changes to
* hidden. As a result, the `callback` function might be called multiple times
* during the same page load._
*/
export const onINP = (onReport, opts) => {
if (!loafObserver) {
loafObserver = observe('long-animation-frame', handleLoAFEntries);
}
unattributedOnINP((metric) => {
const metricWithAttribution = attributeINP(metric);
onReport(metricWithAttribution);
}, opts);
};

View File

@ -0,0 +1,13 @@
import { LCPMetricWithAttribution, ReportOpts } from '../types.js';
/**
* Calculates the [LCP](https://web.dev/articles/lcp) value for the current page and
* calls the `callback` function once the value is ready (along with the
* relevant `largest-contentful-paint` performance entry used to determine the
* value). The reported value is a `DOMHighResTimeStamp`.
*
* If the `reportAllChanges` configuration option is set to `true`, the
* `callback` 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.
*/
export declare const onLCP: (onReport: (metric: LCPMetricWithAttribution) => void, opts?: ReportOpts) => void;

View File

@ -0,0 +1,83 @@
/*
* Copyright 2022 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 { getNavigationEntry } from '../lib/getNavigationEntry.js';
import { getSelector } from '../lib/getSelector.js';
import { onLCP as unattributedOnLCP } from '../onLCP.js';
const attributeLCP = (metric) => {
// Use a default object if no other attribution has been set.
let attribution = {
timeToFirstByte: 0,
resourceLoadDelay: 0,
resourceLoadDuration: 0,
elementRenderDelay: metric.value,
};
if (metric.entries.length) {
const navigationEntry = getNavigationEntry();
if (navigationEntry) {
const activationStart = navigationEntry.activationStart || 0;
const lcpEntry = metric.entries[metric.entries.length - 1];
const lcpResourceEntry = lcpEntry.url &&
performance
.getEntriesByType('resource')
.filter((e) => e.name === lcpEntry.url)[0];
const ttfb = Math.max(0, navigationEntry.responseStart - activationStart);
const lcpRequestStart = Math.max(ttfb,
// Prefer `requestStart` (if TOA is set), otherwise use `startTime`.
lcpResourceEntry
? (lcpResourceEntry.requestStart || lcpResourceEntry.startTime) -
activationStart
: 0);
const lcpResponseEnd = Math.max(lcpRequestStart, lcpResourceEntry ? lcpResourceEntry.responseEnd - activationStart : 0);
const lcpRenderTime = Math.max(lcpResponseEnd, lcpEntry.startTime - activationStart);
attribution = {
element: getSelector(lcpEntry.element),
timeToFirstByte: ttfb,
resourceLoadDelay: lcpRequestStart - ttfb,
resourceLoadDuration: lcpResponseEnd - lcpRequestStart,
elementRenderDelay: lcpRenderTime - lcpResponseEnd,
navigationEntry,
lcpEntry,
};
// Only attribution the URL and resource entry if they exist.
if (lcpEntry.url) {
attribution.url = lcpEntry.url;
}
if (lcpResourceEntry) {
attribution.lcpResourceEntry = lcpResourceEntry;
}
}
}
// Use Object.assign to set property to keep tsc happy.
const metricWithAttribution = Object.assign(metric, { attribution });
return metricWithAttribution;
};
/**
* Calculates the [LCP](https://web.dev/articles/lcp) value for the current page and
* calls the `callback` function once the value is ready (along with the
* relevant `largest-contentful-paint` performance entry used to determine the
* value). The reported value is a `DOMHighResTimeStamp`.
*
* If the `reportAllChanges` configuration option is set to `true`, the
* `callback` 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.
*/
export const onLCP = (onReport, opts) => {
unattributedOnLCP((metric) => {
const metricWithAttribution = attributeLCP(metric);
onReport(metricWithAttribution);
}, opts);
};

View File

@ -0,0 +1,17 @@
import { TTFBMetricWithAttribution, ReportOpts } from '../types.js';
/**
* Calculates the [TTFB](https://web.dev/articles/ttfb) value for the
* current page and calls the `callback` 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 `callback`
* 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 time spent on DNS lookup, connection negotiation, network latency,
* and server processing time.
*/
export declare const onTTFB: (onReport: (metric: TTFBMetricWithAttribution) => void, opts?: ReportOpts) => void;

View File

@ -0,0 +1,76 @@
/*
* Copyright 2022 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 { onTTFB as unattributedOnTTFB } from '../onTTFB.js';
const attributeTTFB = (metric) => {
// Use a default object if no other attribution has been set.
let attribution = {
waitingDuration: 0,
cacheDuration: 0,
dnsDuration: 0,
connectionDuration: 0,
requestDuration: 0,
};
if (metric.entries.length) {
const navigationEntry = metric.entries[0];
const activationStart = navigationEntry.activationStart || 0;
// Measure from workerStart or fetchStart so any service worker startup
// time is included in cacheDuration (which also includes other sw time
// anyway, that cannot be accurately split out cross-browser).
const waitEnd = Math.max((navigationEntry.workerStart || navigationEntry.fetchStart) -
activationStart, 0);
const dnsStart = Math.max(navigationEntry.domainLookupStart - activationStart, 0);
const connectStart = Math.max(navigationEntry.connectStart - activationStart, 0);
const connectEnd = Math.max(navigationEntry.connectEnd - activationStart, 0);
attribution = {
waitingDuration: waitEnd,
cacheDuration: dnsStart - waitEnd,
// dnsEnd usually equals connectStart but use connectStart over dnsEnd
// for dnsDuration in case there ever is a gap.
dnsDuration: connectStart - dnsStart,
connectionDuration: connectEnd - connectStart,
// There is often a gap between connectEnd and requestStart. Attribute
// that to requestDuration so connectionDuration remains 0 for
// service worker controlled requests were connectStart and connectEnd
// are the same.
requestDuration: metric.value - connectEnd,
navigationEntry: navigationEntry,
};
}
// Use Object.assign to set property to keep tsc happy.
const metricWithAttribution = Object.assign(metric, { attribution });
return metricWithAttribution;
};
/**
* Calculates the [TTFB](https://web.dev/articles/ttfb) value for the
* current page and calls the `callback` 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 `callback`
* 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 time spent on DNS lookup, connection negotiation, network latency,
* and server processing time.
*/
export const onTTFB = (onReport, opts) => {
unattributedOnTTFB((metric) => {
const metricWithAttribution = attributeTTFB(metric);
onReport(metricWithAttribution);
}, opts);
};

View File

@ -0,0 +1,5 @@
export {
/**
* @deprecated Use `onINP()` instead.
*/
onFID, FIDThresholds, } from './onFID.js';

View File

@ -0,0 +1,20 @@
/*
* Copyright 2022 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 {
/**
* @deprecated Use `onINP()` instead.
*/
onFID, FIDThresholds, } from './onFID.js';

View File

@ -0,0 +1,7 @@
export { onCLS, CLSThresholds } from './onCLS.js';
export { onFCP, FCPThresholds } from './onFCP.js';
export { onINP, INPThresholds } from './onINP.js';
export { onLCP, LCPThresholds } from './onLCP.js';
export { onTTFB, TTFBThresholds } from './onTTFB.js';
export * from './deprecated.js';
export * from './types.js';

22
24_12_09/node_modules/web-vitals/dist/modules/index.js generated vendored Normal file
View File

@ -0,0 +1,22 @@
/*
* 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 { onCLS, CLSThresholds } from './onCLS.js';
export { onFCP, FCPThresholds } from './onFCP.js';
export { onINP, INPThresholds } from './onINP.js';
export { onLCP, LCPThresholds } from './onLCP.js';
export { onTTFB, TTFBThresholds } from './onTTFB.js';
export * from './deprecated.js';
export * from './types.js';

View File

@ -0,0 +1,6 @@
interface onBFCacheRestoreCallback {
(event: PageTransitionEvent): void;
}
export declare const getBFCacheRestoreTime: () => number;
export declare const onBFCacheRestore: (cb: onBFCacheRestoreCallback) => void;
export {};

View File

@ -0,0 +1,25 @@
/*
* 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 bfcacheRestoreTime = -1;
export const getBFCacheRestoreTime = () => bfcacheRestoreTime;
export const onBFCacheRestore = (cb) => {
addEventListener('pageshow', (event) => {
if (event.persisted) {
bfcacheRestoreTime = event.timeStamp;
cb(event);
}
}, true);
};

View File

@ -0,0 +1,26 @@
import { MetricType, MetricRatingThresholds } from '../types.js';
export declare const bindReporter: <MetricName extends "CLS" | "FCP" | "FID" | "INP" | "LCP" | "TTFB">(callback: (metric: Extract<import("../types.js").CLSMetric, {
name: MetricName;
}> | Extract<import("../types.js").FCPMetric, {
name: MetricName;
}> | Extract<import("../types.js").FIDMetric, {
name: MetricName;
}> | Extract<import("../types.js").INPMetric, {
name: MetricName;
}> | Extract<import("../types.js").LCPMetric, {
name: MetricName;
}> | Extract<import("../types.js").TTFBMetric, {
name: MetricName;
}>) => void, metric: Extract<import("../types.js").CLSMetric, {
name: MetricName;
}> | Extract<import("../types.js").FCPMetric, {
name: MetricName;
}> | Extract<import("../types.js").FIDMetric, {
name: MetricName;
}> | Extract<import("../types.js").INPMetric, {
name: MetricName;
}> | Extract<import("../types.js").LCPMetric, {
name: MetricName;
}> | Extract<import("../types.js").TTFBMetric, {
name: MetricName;
}>, thresholds: MetricRatingThresholds, reportAllChanges?: boolean) => (forceReport?: boolean) => void;

View 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.
*/
const getRating = (value, thresholds) => {
if (value > thresholds[1]) {
return 'poor';
}
if (value > thresholds[0]) {
return 'needs-improvement';
}
return 'good';
};
export const bindReporter = (callback, metric, thresholds, reportAllChanges) => {
let prevValue;
let delta;
return (forceReport) => {
if (metric.value >= 0) {
if (forceReport || reportAllChanges) {
delta = metric.value - (prevValue || 0);
// Report the metric if there's a non-zero delta 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 (delta || prevValue === undefined) {
prevValue = metric.value;
metric.delta = delta;
metric.rating = getRating(metric.value, thresholds);
callback(metric);
}
}
}
};
};

View File

@ -0,0 +1 @@
export declare const doubleRAF: (cb: () => unknown) => void;

View File

@ -0,0 +1,18 @@
/*
* Copyright 2022 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 doubleRAF = (cb) => {
requestAnimationFrame(() => requestAnimationFrame(() => cb()));
};

View File

@ -0,0 +1,6 @@
/**
* Performantly generate a unique, 30-char string by combining a version
* number, the current timestamp with a 13-digit number integer.
* @return {string}
*/
export declare const generateUniqueID: () => string;

View 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, 30-char string by combining a version
* number, the current timestamp with a 13-digit number integer.
* @return {string}
*/
export const generateUniqueID = () => {
return `v4-${Date.now()}-${Math.floor(Math.random() * (9e12 - 1)) + 1e12}`;
};

View File

@ -0,0 +1 @@
export declare const getActivationStart: () => number;

View File

@ -0,0 +1,20 @@
/*
* Copyright 2022 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 { getNavigationEntry } from './getNavigationEntry.js';
export const getActivationStart = () => {
const navEntry = getNavigationEntry();
return (navEntry && navEntry.activationStart) || 0;
};

View File

@ -0,0 +1,2 @@
import { LoadState } from '../types.js';
export declare const getLoadState: (timestamp: number) => LoadState;

View File

@ -0,0 +1,47 @@
/*
* Copyright 2022 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 { getNavigationEntry } from './getNavigationEntry.js';
export const getLoadState = (timestamp) => {
if (document.readyState === 'loading') {
// If the `readyState` is 'loading' there's no need to look at timestamps
// since the timestamp has to be the current time or earlier.
return 'loading';
}
else {
const navigationEntry = getNavigationEntry();
if (navigationEntry) {
if (timestamp < navigationEntry.domInteractive) {
return 'loading';
}
else if (navigationEntry.domContentLoadedEventStart === 0 ||
timestamp < navigationEntry.domContentLoadedEventStart) {
// If the `domContentLoadedEventStart` timestamp has not yet been
// set, or if the given timestamp is less than that value.
return 'dom-interactive';
}
else if (navigationEntry.domComplete === 0 ||
timestamp < navigationEntry.domComplete) {
// If the `domComplete` timestamp has not yet been
// set, or if the given timestamp is less than that value.
return 'dom-content-loaded';
}
}
}
// If any of the above fail, default to loaded. This could really only
// happy if the browser doesn't support the performance timeline, which
// most likely means this code would never run anyway.
return 'complete';
};

View File

@ -0,0 +1 @@
export declare const getNavigationEntry: () => PerformanceNavigationTiming | void;

View File

@ -0,0 +1,32 @@
/*
* Copyright 2022 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 getNavigationEntry = () => {
const navigationEntry = self.performance &&
performance.getEntriesByType &&
performance.getEntriesByType('navigation')[0];
// Check to ensure the `responseStart` property is present and valid.
// In some cases no value is reported by the browser (for
// privacy/security reasons), and in other cases (bugs) the value is
// negative or is larger than the current page time. Ignore these cases:
// https://github.com/GoogleChrome/web-vitals/issues/137
// https://github.com/GoogleChrome/web-vitals/issues/162
// https://github.com/GoogleChrome/web-vitals/issues/275
if (navigationEntry &&
navigationEntry.responseStart > 0 &&
navigationEntry.responseStart < performance.now()) {
return navigationEntry;
}
};

View File

@ -0,0 +1 @@
export declare const getSelector: (node: Node | null | undefined, maxLen?: number) => string;

View File

@ -0,0 +1,48 @@
/*
* Copyright 2022 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.
*/
const getName = (node) => {
const name = node.nodeName;
return node.nodeType === 1
? name.toLowerCase()
: name.toUpperCase().replace(/^#/, '');
};
export const getSelector = (node, maxLen) => {
let sel = '';
try {
while (node && node.nodeType !== 9) {
const el = node;
const part = el.id
? '#' + el.id
: getName(el) +
(el.classList &&
el.classList.value &&
el.classList.value.trim() &&
el.classList.value.trim().length
? '.' + el.classList.value.trim().replace(/\s+/g, '.')
: '');
if (sel.length + part.length > (maxLen || 100) - 1)
return sel || part;
sel = sel ? part + '>' + sel : part;
if (el.id)
break;
node = el.parentNode;
}
}
catch (err) {
// Do nothing...
}
return sel;
};

View File

@ -0,0 +1,3 @@
export declare const getVisibilityWatcher: () => {
readonly firstHiddenTime: number;
};

View File

@ -0,0 +1,80 @@
/*
* 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 { onBFCacheRestore } from './bfcache.js';
let firstHiddenTime = -1;
const initHiddenTime = () => {
// If the document is hidden when this code runs, assume it was always
// hidden and the page was loaded in the background, with the one exception
// that visibility state is always 'hidden' during prerendering, so we have
// to ignore that case until prerendering finishes (see: `prerenderingchange`
// event logic below).
return document.visibilityState === 'hidden' && !document.prerendering
? 0
: Infinity;
};
const onVisibilityUpdate = (event) => {
// If the document is 'hidden' and no previous hidden timestamp has been
// set, update it based on the current event data.
if (document.visibilityState === 'hidden' && firstHiddenTime > -1) {
// If the event is a 'visibilitychange' event, it means the page was
// visible prior to this change, so the event timestamp is the first
// hidden time.
// However, if the event is not a 'visibilitychange' event, then it must
// be a 'prerenderingchange' event, and the fact that the document is
// still 'hidden' from the above check means the tab was activated
// in a background state and so has always been hidden.
firstHiddenTime = event.type === 'visibilitychange' ? event.timeStamp : 0;
// Remove all listeners now that a `firstHiddenTime` value has been set.
removeChangeListeners();
}
};
const addChangeListeners = () => {
addEventListener('visibilitychange', onVisibilityUpdate, true);
// IMPORTANT: when a page is prerendering, its `visibilityState` is
// 'hidden', so in order to account for cases where this module checks for
// visibility during prerendering, an additional check after prerendering
// completes is also required.
addEventListener('prerenderingchange', onVisibilityUpdate, true);
};
const removeChangeListeners = () => {
removeEventListener('visibilitychange', onVisibilityUpdate, true);
removeEventListener('prerenderingchange', onVisibilityUpdate, true);
};
export const getVisibilityWatcher = () => {
if (firstHiddenTime < 0) {
// 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 = initHiddenTime();
addChangeListeners();
// Reset the time on bfcache restores.
onBFCacheRestore(() => {
// Schedule a task in order to track the `visibilityState` once it's
// had an opportunity to change to visible in all browsers.
// https://bugs.chromium.org/p/chromium/issues/detail?id=1133363
setTimeout(() => {
firstHiddenTime = initHiddenTime();
addChangeListeners();
}, 0);
});
}
return {
get firstHiddenTime() {
return firstHiddenTime;
},
};
};

View File

@ -0,0 +1,21 @@
export declare const initMetric: <MetricName extends "CLS" | "FCP" | "FID" | "INP" | "LCP" | "TTFB">(name: MetricName, value?: number) => {
name: MetricName;
value: number;
rating: "good";
delta: number;
entries: (Extract<import("../types.js").CLSMetric, {
name: MetricName;
}> | Extract<import("../types.js").FCPMetric, {
name: MetricName;
}> | Extract<import("../types.js").FIDMetric, {
name: MetricName;
}> | Extract<import("../types.js").INPMetric, {
name: MetricName;
}> | Extract<import("../types.js").LCPMetric, {
name: MetricName;
}> | Extract<import("../types.js").TTFBMetric, {
name: MetricName;
}>)["entries"];
id: string;
navigationType: "navigate" | "reload" | "back-forward" | "back-forward-cache" | "prerender" | "restore";
};

View 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 { getBFCacheRestoreTime } from './bfcache.js';
import { generateUniqueID } from './generateUniqueID.js';
import { getActivationStart } from './getActivationStart.js';
import { getNavigationEntry } from './getNavigationEntry.js';
export const initMetric = (name, value) => {
const navEntry = getNavigationEntry();
let navigationType = 'navigate';
if (getBFCacheRestoreTime() >= 0) {
navigationType = 'back-forward-cache';
}
else if (navEntry) {
if (document.prerendering || getActivationStart() > 0) {
navigationType = 'prerender';
}
else if (document.wasDiscarded) {
navigationType = 'restore';
}
else if (navEntry.type) {
navigationType = navEntry.type.replace(/_/g, '-');
}
}
// Use `entries` type specific for the metric.
const entries = [];
return {
name,
value: typeof value === 'undefined' ? -1 : value,
rating: 'good', // If needed, will be updated when reported. `const` to keep the type from widening to `string`.
delta: 0,
entries,
id: generateUniqueID(),
navigationType,
};
};

View File

@ -0,0 +1,31 @@
interface Interaction {
id: number;
latency: number;
entries: PerformanceEventTiming[];
}
interface EntryPreProcessingHook {
(entry: PerformanceEventTiming): void;
}
export declare const longestInteractionList: Interaction[];
export declare const longestInteractionMap: Map<number, Interaction>;
export declare const DEFAULT_DURATION_THRESHOLD = 40;
export declare const resetInteractions: () => void;
/**
* Returns the estimated p98 longest interaction based on the stored
* interaction candidates and the interaction count for the current page.
*/
export declare const estimateP98LongestInteraction: () => Interaction;
/**
* A list of callback functions to run before each entry is processed.
* Exposing this list allows the attribution build to hook into the
* entry processing pipeline.
*/
export declare const entryPreProcessingCallbacks: EntryPreProcessingHook[];
/**
* Takes a performance entry and adds it to the list of worst interactions
* if its duration is long enough to make it among the worst. If the
* entry is part of an existing interaction, it is merged and the latency
* and entries list is updated as needed.
*/
export declare const processInteractionEntry: (entry: PerformanceEventTiming) => void;
export {};

View File

@ -0,0 +1,107 @@
/*
* Copyright 2024 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 { getInteractionCount } from './polyfills/interactionCountPolyfill.js';
// A list of longest interactions on the page (by latency) sorted so the
// longest one is first. The list is at most MAX_INTERACTIONS_TO_CONSIDER long.
export const longestInteractionList = [];
// A mapping of longest interactions by their interaction ID.
// This is used for faster lookup.
export const longestInteractionMap = new Map();
// The default `durationThreshold` used across this library for observing
// `event` entries via PerformanceObserver.
export const DEFAULT_DURATION_THRESHOLD = 40;
// Used to store the interaction count after a bfcache restore, since p98
// interaction latencies should only consider the current navigation.
let prevInteractionCount = 0;
/**
* Returns the interaction count since the last bfcache restore (or for the
* full page lifecycle if there were no bfcache restores).
*/
const getInteractionCountForNavigation = () => {
return getInteractionCount() - prevInteractionCount;
};
export const resetInteractions = () => {
prevInteractionCount = getInteractionCount();
longestInteractionList.length = 0;
longestInteractionMap.clear();
};
/**
* Returns the estimated p98 longest interaction based on the stored
* interaction candidates and the interaction count for the current page.
*/
export const estimateP98LongestInteraction = () => {
const candidateInteractionIndex = Math.min(longestInteractionList.length - 1, Math.floor(getInteractionCountForNavigation() / 50));
return longestInteractionList[candidateInteractionIndex];
};
// To prevent unnecessary memory usage on pages with lots of interactions,
// store at most 10 of the longest interactions to consider as INP candidates.
const MAX_INTERACTIONS_TO_CONSIDER = 10;
/**
* A list of callback functions to run before each entry is processed.
* Exposing this list allows the attribution build to hook into the
* entry processing pipeline.
*/
export const entryPreProcessingCallbacks = [];
/**
* Takes a performance entry and adds it to the list of worst interactions
* if its duration is long enough to make it among the worst. If the
* entry is part of an existing interaction, it is merged and the latency
* and entries list is updated as needed.
*/
export const processInteractionEntry = (entry) => {
entryPreProcessingCallbacks.forEach((cb) => cb(entry));
// Skip further processing for entries that cannot be INP candidates.
if (!(entry.interactionId || entry.entryType === 'first-input'))
return;
// The least-long of the 10 longest interactions.
const minLongestInteraction = longestInteractionList[longestInteractionList.length - 1];
const existingInteraction = longestInteractionMap.get(entry.interactionId);
// Only process the entry if it's possibly one of the ten longest,
// or if it's part of an existing interaction.
if (existingInteraction ||
longestInteractionList.length < MAX_INTERACTIONS_TO_CONSIDER ||
entry.duration > minLongestInteraction.latency) {
// If the interaction already exists, update it. Otherwise create one.
if (existingInteraction) {
// If the new entry has a longer duration, replace the old entries,
// otherwise add to the array.
if (entry.duration > existingInteraction.latency) {
existingInteraction.entries = [entry];
existingInteraction.latency = entry.duration;
}
else if (entry.duration === existingInteraction.latency &&
entry.startTime === existingInteraction.entries[0].startTime) {
existingInteraction.entries.push(entry);
}
}
else {
const interaction = {
id: entry.interactionId,
latency: entry.duration,
entries: [entry],
};
longestInteractionMap.set(interaction.id, interaction);
longestInteractionList.push(interaction);
}
// Sort the entries by latency (descending) and keep only the top ten.
longestInteractionList.sort((a, b) => b.latency - a.latency);
if (longestInteractionList.length > MAX_INTERACTIONS_TO_CONSIDER) {
longestInteractionList
.splice(MAX_INTERACTIONS_TO_CONSIDER)
.forEach((i) => longestInteractionMap.delete(i.id));
}
}
};

View File

@ -0,0 +1,20 @@
interface PerformanceEntryMap {
'event': PerformanceEventTiming[];
'first-input': PerformanceEventTiming[];
'layout-shift': LayoutShift[];
'largest-contentful-paint': LargestContentfulPaint[];
'long-animation-frame': PerformanceLongAnimationFrameTiming[];
'paint': PerformancePaintTiming[];
'navigation': PerformanceNavigationTiming[];
'resource': PerformanceResourceTiming[];
}
/**
* 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: <K extends keyof PerformanceEntryMap>(type: K, callback: (entries: PerformanceEntryMap[K]) => void, opts?: PerformanceObserverInit) => PerformanceObserver | undefined;
export {};

View 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.
*/
/**
* 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, opts) => {
try {
if (PerformanceObserver.supportedEntryTypes.includes(type)) {
const po = new PerformanceObserver((list) => {
// Delay by a microtask to workaround a bug in Safari where the
// callback is invoked immediately, rather than in a separate task.
// See: https://github.com/GoogleChrome/web-vitals/issues/277
Promise.resolve().then(() => {
callback(list.getEntries());
});
});
po.observe(Object.assign({
type,
buffered: true,
}, opts || {}));
return po;
}
}
catch (e) {
// Do nothing.
}
return;
};

View File

@ -0,0 +1 @@
export declare const onHidden: (cb: () => void) => void;

View File

@ -0,0 +1,22 @@
/*
* 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 onHidden = (cb) => {
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
cb();
}
});
};

View File

@ -0,0 +1,7 @@
import { FirstInputPolyfillCallback } from '../../types.js';
/**
* Accepts a callback to be invoked once the first input delay and event
* are known.
*/
export declare const firstInputPolyfill: (onFirstInput: FirstInputPolyfillCallback) => void;
export declare const resetFirstInputPolyfill: () => void;

View File

@ -0,0 +1,147 @@
/*
* 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 firstInputEvent;
let firstInputDelay;
let firstInputTimeStamp;
let callbacks;
const listenerOpts = { passive: true, capture: true };
const startTimeStamp = new Date();
/**
* Accepts a callback to be invoked once the first input delay and event
* are known.
*/
export const firstInputPolyfill = (onFirstInput) => {
callbacks.push(onFirstInput);
reportFirstInputDelayIfRecordedAndValid();
};
export const resetFirstInputPolyfill = () => {
callbacks = [];
firstInputDelay = -1;
firstInputEvent = null;
eachEventType(addEventListener);
};
/**
* Records the first input delay and event, so subsequent events can be
* ignored. All added event listeners are then removed.
*/
const recordFirstInputDelay = (delay, event) => {
if (!firstInputEvent) {
firstInputEvent = event;
firstInputDelay = delay;
firstInputTimeStamp = new Date();
eachEventType(removeEventListener);
reportFirstInputDelayIfRecordedAndValid();
}
};
/**
* Reports the first input delay and event (if they're recorded and valid)
* by running the array of callback functions.
*/
const reportFirstInputDelayIfRecordedAndValid = () => {
// In some cases the recorded delay is clearly wrong, e.g. it's negative
// or it's larger than the delta between now and initialization.
// - https://github.com/GoogleChromeLabs/first-input-delay/issues/4
// - https://github.com/GoogleChromeLabs/first-input-delay/issues/6
// - https://github.com/GoogleChromeLabs/first-input-delay/issues/7
if (firstInputDelay >= 0 &&
// @ts-ignore (subtracting two dates always returns a number)
firstInputDelay < firstInputTimeStamp - startTimeStamp) {
const entry = {
entryType: 'first-input',
name: firstInputEvent.type,
target: firstInputEvent.target,
cancelable: firstInputEvent.cancelable,
startTime: firstInputEvent.timeStamp,
processingStart: firstInputEvent.timeStamp + firstInputDelay,
};
callbacks.forEach(function (callback) {
callback(entry);
});
callbacks = [];
}
};
/**
* Handles pointer down events, which are a special case.
* Pointer events can trigger main or compositor thread behavior.
* We differentiate these cases based on whether or not we see a
* 'pointercancel' event, which are fired when we scroll. If we're scrolling
* we don't need to report input delay since FID excludes scrolling and
* pinch/zooming.
*/
const onPointerDown = (delay, event) => {
/**
* Responds to 'pointerup' events and records a delay. If a pointer up event
* is the next event after a pointerdown event, then it's not a scroll or
* a pinch/zoom.
*/
const onPointerUp = () => {
recordFirstInputDelay(delay, event);
removePointerEventListeners();
};
/**
* Responds to 'pointercancel' events and removes pointer listeners.
* If a 'pointercancel' is the next event to fire after a pointerdown event,
* it means this is a scroll or pinch/zoom interaction.
*/
const onPointerCancel = () => {
removePointerEventListeners();
};
/**
* Removes added pointer event listeners.
*/
const removePointerEventListeners = () => {
removeEventListener('pointerup', onPointerUp, listenerOpts);
removeEventListener('pointercancel', onPointerCancel, listenerOpts);
};
addEventListener('pointerup', onPointerUp, listenerOpts);
addEventListener('pointercancel', onPointerCancel, listenerOpts);
};
/**
* Handles all input events and records the time between when the event
* was received by the operating system and when it's JavaScript listeners
* were able to run.
*/
const onInput = (event) => {
// Only count cancelable events, which should trigger behavior
// important to the user.
if (event.cancelable) {
// In some browsers `event.timeStamp` returns a `DOMTimeStamp` value
// (epoch time) instead of the newer `DOMHighResTimeStamp`
// (document-origin time). To check for that we assume any timestamp
// greater than 1 trillion is a `DOMTimeStamp`, and compare it using
// the `Date` object rather than `performance.now()`.
// - https://github.com/GoogleChromeLabs/first-input-delay/issues/4
const isEpochTime = event.timeStamp > 1e12;
const now = isEpochTime ? new Date() : performance.now();
// Input delay is the delta between when the system received the event
// (e.g. event.timeStamp) and when it could run the callback (e.g. `now`).
const delay = now - event.timeStamp;
if (event.type == 'pointerdown') {
onPointerDown(delay, event);
}
else {
recordFirstInputDelay(delay, event);
}
}
};
/**
* Invokes the passed callback const for = each event type with t =>he
* `onInput` const and = `listenerOpts =>`.
*/
const eachEventType = (callback) => {
const eventTypes = ['mousedown', 'keydown', 'touchstart', 'pointerdown'];
eventTypes.forEach((type) => callback(type, onInput, listenerOpts));
};

View File

@ -0,0 +1 @@
export declare const getFirstHiddenTime: () => number;

View File

@ -0,0 +1,25 @@
/*
* 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 firstHiddenTime = document.visibilityState === 'hidden' ? 0 : Infinity;
const onVisibilityChange = (event) => {
if (document.visibilityState === 'hidden') {
firstHiddenTime = event.timeStamp;
removeEventListener('visibilitychange', onVisibilityChange, true);
}
};
// Note: do not add event listeners unconditionally (outside of polyfills).
addEventListener('visibilitychange', onVisibilityChange, true);
export const getFirstHiddenTime = () => firstHiddenTime;

View File

@ -0,0 +1,14 @@
declare global {
interface Performance {
interactionCount: number;
}
}
/**
* Returns the `interactionCount` value using the native API (if available)
* or the polyfill estimate in this module.
*/
export declare const getInteractionCount: () => number;
/**
* Feature detects native support or initializes the polyfill if needed.
*/
export declare const initInteractionCountPolyfill: () => void;

View File

@ -0,0 +1,50 @@
/*
* Copyright 2022 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 { observe } from '../observe.js';
let interactionCountEstimate = 0;
let minKnownInteractionId = Infinity;
let maxKnownInteractionId = 0;
const updateEstimate = (entries) => {
entries.forEach((e) => {
if (e.interactionId) {
minKnownInteractionId = Math.min(minKnownInteractionId, e.interactionId);
maxKnownInteractionId = Math.max(maxKnownInteractionId, e.interactionId);
interactionCountEstimate = maxKnownInteractionId
? (maxKnownInteractionId - minKnownInteractionId) / 7 + 1
: 0;
}
});
};
let po;
/**
* Returns the `interactionCount` value using the native API (if available)
* or the polyfill estimate in this module.
*/
export const getInteractionCount = () => {
return po ? interactionCountEstimate : performance.interactionCount || 0;
};
/**
* Feature detects native support or initializes the polyfill if needed.
*/
export const initInteractionCountPolyfill = () => {
if ('interactionCount' in performance || po)
return;
po = observe('event', updateEstimate, {
type: 'event',
buffered: true,
durationThreshold: 0,
});
};

View File

@ -0,0 +1 @@
export declare const runOnce: (cb: () => void) => () => void;

View File

@ -0,0 +1,24 @@
/*
* Copyright 2022 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 runOnce = (cb) => {
let called = false;
return () => {
if (!called) {
cb();
called = true;
}
};
};

View File

@ -0,0 +1 @@
export declare const whenActivated: (callback: () => void) => void;

View File

@ -0,0 +1,23 @@
/*
* Copyright 2022 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 whenActivated = (callback) => {
if (document.prerendering) {
addEventListener('prerenderingchange', () => callback(), true);
}
else {
callback();
}
};

View File

@ -0,0 +1,5 @@
/**
* Runs the passed callback during the next idle period, or immediately
* if the browser's visibility state is (or becomes) hidden.
*/
export declare const whenIdle: (cb: () => void) => number;

View File

@ -0,0 +1,36 @@
/*
* Copyright 2024 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';
import { runOnce } from './runOnce.js';
/**
* Runs the passed callback during the next idle period, or immediately
* if the browser's visibility state is (or becomes) hidden.
*/
export const whenIdle = (cb) => {
const rIC = self.requestIdleCallback || self.setTimeout;
let handle = -1;
cb = runOnce(cb);
// If the document is hidden, run the callback immediately, otherwise
// race an idle callback with the next `visibilitychange` event.
if (document.visibilityState === 'hidden') {
cb();
}
else {
handle = rIC(cb);
onHidden(cb);
}
return handle;
};

View File

@ -0,0 +1,25 @@
import { CLSMetric, MetricRatingThresholds, ReportOpts } from './types.js';
/** Thresholds for CLS. See https://web.dev/articles/cls#what_is_a_good_cls_score */
export declare const CLSThresholds: MetricRatingThresholds;
/**
* Calculates the [CLS](https://web.dev/articles/cls) value for the current page and
* calls the `callback` 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` (corresponding to a
* [layout shift score](https://web.dev/articles/cls#layout_shift_score)).
*
* If the `reportAllChanges` configuration option is set to `true`, the
* `callback` function will be called as soon as the value is initially
* determined as well as any time the value changes throughout the page
* lifespan.
*
* _**Important:** CLS should be continually monitored for changes throughout
* the entire lifespan of a pageincluding 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://developer.chrome.com/blog/page-lifecycle-api/#advice-hidden),
* `callback` is always called when the page's visibility state changes to
* hidden. As a result, the `callback` function might be called multiple times
* during the same page load._
*/
export declare const onCLS: (onReport: (metric: CLSMetric) => void, opts?: ReportOpts) => void;

108
24_12_09/node_modules/web-vitals/dist/modules/onCLS.js generated vendored Normal file
View File

@ -0,0 +1,108 @@
/*
* 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 { onBFCacheRestore } from './lib/bfcache.js';
import { initMetric } from './lib/initMetric.js';
import { observe } from './lib/observe.js';
import { bindReporter } from './lib/bindReporter.js';
import { doubleRAF } from './lib/doubleRAF.js';
import { onHidden } from './lib/onHidden.js';
import { runOnce } from './lib/runOnce.js';
import { onFCP } from './onFCP.js';
/** Thresholds for CLS. See https://web.dev/articles/cls#what_is_a_good_cls_score */
export const CLSThresholds = [0.1, 0.25];
/**
* Calculates the [CLS](https://web.dev/articles/cls) value for the current page and
* calls the `callback` 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` (corresponding to a
* [layout shift score](https://web.dev/articles/cls#layout_shift_score)).
*
* If the `reportAllChanges` configuration option is set to `true`, the
* `callback` function will be called as soon as the value is initially
* determined as well as any time the value changes throughout the page
* lifespan.
*
* _**Important:** CLS should be continually monitored for changes throughout
* the entire lifespan of a pageincluding 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://developer.chrome.com/blog/page-lifecycle-api/#advice-hidden),
* `callback` is always called when the page's visibility state changes to
* hidden. As a result, the `callback` function might be called multiple times
* during the same page load._
*/
export const onCLS = (onReport, opts) => {
// Set defaults
opts = opts || {};
// Start monitoring FCP so we can only report CLS if FCP is also reported.
// Note: this is done to match the current behavior of CrUX.
onFCP(runOnce(() => {
let metric = initMetric('CLS', 0);
let report;
let sessionValue = 0;
let sessionEntries = [];
const handleEntries = (entries) => {
entries.forEach((entry) => {
// Only count layout shifts without recent user input.
if (!entry.hadRecentInput) {
const firstSessionEntry = sessionEntries[0];
const lastSessionEntry = sessionEntries[sessionEntries.length - 1];
// If the entry occurred less than 1 second after the previous entry
// and less than 5 seconds after the first entry in the session,
// include the entry in the current session. Otherwise, start a new
// session.
if (sessionValue &&
entry.startTime - lastSessionEntry.startTime < 1000 &&
entry.startTime - firstSessionEntry.startTime < 5000) {
sessionValue += entry.value;
sessionEntries.push(entry);
}
else {
sessionValue = entry.value;
sessionEntries = [entry];
}
}
});
// If the current session value is larger than the current CLS value,
// update CLS and the entries contributing to it.
if (sessionValue > metric.value) {
metric.value = sessionValue;
metric.entries = sessionEntries;
report();
}
};
const po = observe('layout-shift', handleEntries);
if (po) {
report = bindReporter(onReport, metric, CLSThresholds, opts.reportAllChanges);
onHidden(() => {
handleEntries(po.takeRecords());
report(true);
});
// Only report after a bfcache restore if the `PerformanceObserver`
// successfully registered.
onBFCacheRestore(() => {
sessionValue = 0;
metric = initMetric('CLS', 0);
report = bindReporter(onReport, metric, CLSThresholds, opts.reportAllChanges);
doubleRAF(() => report());
});
// Queue a task to report (if nothing else triggers a report first).
// This allows CLS to be reported as soon as FCP fires when
// `reportAllChanges` is true.
setTimeout(report, 0);
}
}));
};

View File

@ -0,0 +1,10 @@
import { FCPMetric, MetricRatingThresholds, ReportOpts } from './types.js';
/** Thresholds for FCP. See https://web.dev/articles/fcp#what_is_a_good_fcp_score */
export declare const FCPThresholds: MetricRatingThresholds;
/**
* Calculates the [FCP](https://web.dev/articles/fcp) value for the current page and
* calls the `callback` function once the value is ready, along with the
* relevant `paint` performance entry used to determine the value. The reported
* value is a `DOMHighResTimeStamp`.
*/
export declare const onFCP: (onReport: (metric: FCPMetric) => void, opts?: ReportOpts) => void;

71
24_12_09/node_modules/web-vitals/dist/modules/onFCP.js generated vendored Normal file
View File

@ -0,0 +1,71 @@
/*
* 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 { onBFCacheRestore } from './lib/bfcache.js';
import { bindReporter } from './lib/bindReporter.js';
import { doubleRAF } from './lib/doubleRAF.js';
import { getActivationStart } from './lib/getActivationStart.js';
import { getVisibilityWatcher } from './lib/getVisibilityWatcher.js';
import { initMetric } from './lib/initMetric.js';
import { observe } from './lib/observe.js';
import { whenActivated } from './lib/whenActivated.js';
/** Thresholds for FCP. See https://web.dev/articles/fcp#what_is_a_good_fcp_score */
export const FCPThresholds = [1800, 3000];
/**
* Calculates the [FCP](https://web.dev/articles/fcp) value for the current page and
* calls the `callback` function once the value is ready, along with the
* relevant `paint` performance entry used to determine the value. The reported
* value is a `DOMHighResTimeStamp`.
*/
export const onFCP = (onReport, opts) => {
// Set defaults
opts = opts || {};
whenActivated(() => {
const visibilityWatcher = getVisibilityWatcher();
let metric = initMetric('FCP');
let report;
const handleEntries = (entries) => {
entries.forEach((entry) => {
if (entry.name === 'first-contentful-paint') {
po.disconnect();
// Only report if the page wasn't hidden prior to the first paint.
if (entry.startTime < visibilityWatcher.firstHiddenTime) {
// The activationStart reference is used because FCP should be
// relative to page activation rather than navigation start if the
// page was prerendered. But in cases where `activationStart` occurs
// after the FCP, this time should be clamped at 0.
metric.value = Math.max(entry.startTime - getActivationStart(), 0);
metric.entries.push(entry);
report(true);
}
}
});
};
const po = observe('paint', handleEntries);
if (po) {
report = bindReporter(onReport, metric, FCPThresholds, opts.reportAllChanges);
// Only report after a bfcache restore if the `PerformanceObserver`
// successfully registered or the `paint` entry exists.
onBFCacheRestore((event) => {
metric = initMetric('FCP');
report = bindReporter(onReport, metric, FCPThresholds, opts.reportAllChanges);
doubleRAF(() => {
metric.value = performance.now() - event.timeStamp;
report(true);
});
});
}
});
};

View File

@ -0,0 +1,13 @@
import { FIDMetric, MetricRatingThresholds, ReportOpts } from './types.js';
/** Thresholds for FID. See https://web.dev/articles/fid#what_is_a_good_fid_score */
export declare const FIDThresholds: MetricRatingThresholds;
/**
* Calculates the [FID](https://web.dev/articles/fid) value for the current page and
* calls the `callback` function once the value is ready, along with the
* relevant `first-input` performance entry used to determine the value. 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._
*/
export declare const onFID: (onReport: (metric: FIDMetric) => void, opts?: ReportOpts) => void;

70
24_12_09/node_modules/web-vitals/dist/modules/onFID.js generated vendored Normal file
View File

@ -0,0 +1,70 @@
/*
* 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 { onBFCacheRestore } from './lib/bfcache.js';
import { bindReporter } from './lib/bindReporter.js';
import { getVisibilityWatcher } from './lib/getVisibilityWatcher.js';
import { initMetric } from './lib/initMetric.js';
import { observe } from './lib/observe.js';
import { onHidden } from './lib/onHidden.js';
import { firstInputPolyfill, resetFirstInputPolyfill, } from './lib/polyfills/firstInputPolyfill.js';
import { runOnce } from './lib/runOnce.js';
import { whenActivated } from './lib/whenActivated.js';
/** Thresholds for FID. See https://web.dev/articles/fid#what_is_a_good_fid_score */
export const FIDThresholds = [100, 300];
/**
* Calculates the [FID](https://web.dev/articles/fid) value for the current page and
* calls the `callback` function once the value is ready, along with the
* relevant `first-input` performance entry used to determine the value. 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._
*/
export const onFID = (onReport, opts) => {
// Set defaults
opts = opts || {};
whenActivated(() => {
const visibilityWatcher = getVisibilityWatcher();
let metric = initMetric('FID');
let report;
const handleEntry = (entry) => {
// Only report if the page wasn't hidden prior to the first input.
if (entry.startTime < visibilityWatcher.firstHiddenTime) {
metric.value = entry.processingStart - entry.startTime;
metric.entries.push(entry);
report(true);
}
};
const handleEntries = (entries) => {
entries.forEach(handleEntry);
};
const po = observe('first-input', handleEntries);
report = bindReporter(onReport, metric, FIDThresholds, opts.reportAllChanges);
if (po) {
onHidden(runOnce(() => {
handleEntries(po.takeRecords());
po.disconnect();
}));
onBFCacheRestore(() => {
metric = initMetric('FID');
report = bindReporter(onReport, metric, FIDThresholds, opts.reportAllChanges);
// Browsers don't re-emit FID on bfcache restore so fake it until you make it
resetFirstInputPolyfill();
firstInputPolyfill(handleEntry);
});
}
});
};

View File

@ -0,0 +1,31 @@
import { INPMetric, MetricRatingThresholds, ReportOpts } from './types.js';
/** Thresholds for INP. See https://web.dev/articles/inp#what_is_a_good_inp_score */
export declare const INPThresholds: MetricRatingThresholds;
/**
* Calculates the [INP](https://web.dev/articles/inp) value for the current
* page and calls the `callback` function once the value is ready, along with
* the `event` performance entries reported for that interaction. The reported
* value is a `DOMHighResTimeStamp`.
*
* A custom `durationThreshold` configuration option can optionally be passed to
* control what `event-timing` entries are considered for INP reporting. The
* default threshold is `40`, which means INP scores of less than 40 are
* reported as 0. Note that this will not affect your 75th percentile INP value
* unless that value is also less than 40 (well below the recommended
* [good](https://web.dev/articles/inp#what_is_a_good_inp_score) threshold).
*
* If the `reportAllChanges` configuration option is set to `true`, the
* `callback` function will be called as soon as the value is initially
* determined as well as any time the value changes throughout the page
* lifespan.
*
* _**Important:** INP should be continually monitored for changes throughout
* the entire lifespan of a pageincluding 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://developer.chrome.com/blog/page-lifecycle-api/#advice-hidden),
* `callback` is always called when the page's visibility state changes to
* hidden. As a result, the `callback` function might be called multiple times
* during the same page load._
*/
export declare const onINP: (onReport: (metric: INPMetric) => void, opts?: ReportOpts) => void;

111
24_12_09/node_modules/web-vitals/dist/modules/onINP.js generated vendored Normal file
View File

@ -0,0 +1,111 @@
/*
* Copyright 2022 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 { onBFCacheRestore } from './lib/bfcache.js';
import { bindReporter } from './lib/bindReporter.js';
import { initMetric } from './lib/initMetric.js';
import { DEFAULT_DURATION_THRESHOLD, processInteractionEntry, estimateP98LongestInteraction, resetInteractions, } from './lib/interactions.js';
import { observe } from './lib/observe.js';
import { onHidden } from './lib/onHidden.js';
import { initInteractionCountPolyfill } from './lib/polyfills/interactionCountPolyfill.js';
import { whenActivated } from './lib/whenActivated.js';
import { whenIdle } from './lib/whenIdle.js';
/** Thresholds for INP. See https://web.dev/articles/inp#what_is_a_good_inp_score */
export const INPThresholds = [200, 500];
/**
* Calculates the [INP](https://web.dev/articles/inp) value for the current
* page and calls the `callback` function once the value is ready, along with
* the `event` performance entries reported for that interaction. The reported
* value is a `DOMHighResTimeStamp`.
*
* A custom `durationThreshold` configuration option can optionally be passed to
* control what `event-timing` entries are considered for INP reporting. The
* default threshold is `40`, which means INP scores of less than 40 are
* reported as 0. Note that this will not affect your 75th percentile INP value
* unless that value is also less than 40 (well below the recommended
* [good](https://web.dev/articles/inp#what_is_a_good_inp_score) threshold).
*
* If the `reportAllChanges` configuration option is set to `true`, the
* `callback` function will be called as soon as the value is initially
* determined as well as any time the value changes throughout the page
* lifespan.
*
* _**Important:** INP should be continually monitored for changes throughout
* the entire lifespan of a pageincluding 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://developer.chrome.com/blog/page-lifecycle-api/#advice-hidden),
* `callback` is always called when the page's visibility state changes to
* hidden. As a result, the `callback` function might be called multiple times
* during the same page load._
*/
export const onINP = (onReport, opts) => {
// Return if the browser doesn't support all APIs needed to measure INP.
if (!('PerformanceEventTiming' in self &&
'interactionId' in PerformanceEventTiming.prototype)) {
return;
}
// Set defaults
opts = opts || {};
whenActivated(() => {
// TODO(philipwalton): remove once the polyfill is no longer needed.
initInteractionCountPolyfill();
let metric = initMetric('INP');
let report;
const handleEntries = (entries) => {
// Queue the `handleEntries()` callback in the next idle task.
// This is needed to increase the chances that all event entries that
// occurred between the user interaction and the next paint
// have been dispatched. Note: there is currently an experiment
// running in Chrome (EventTimingKeypressAndCompositionInteractionId)
// 123+ that if rolled out fully may make this no longer necessary.
whenIdle(() => {
entries.forEach(processInteractionEntry);
const inp = estimateP98LongestInteraction();
if (inp && inp.latency !== metric.value) {
metric.value = inp.latency;
metric.entries = inp.entries;
report();
}
});
};
const po = observe('event', handleEntries, {
// Event Timing entries have their durations rounded to the nearest 8ms,
// so a duration of 40ms would be any event that spans 2.5 or more frames
// at 60Hz. This threshold is chosen to strike a balance between usefulness
// and performance. Running this callback for any interaction that spans
// just one or two frames is likely not worth the insight that could be
// gained.
durationThreshold: opts.durationThreshold ?? DEFAULT_DURATION_THRESHOLD,
});
report = bindReporter(onReport, metric, INPThresholds, opts.reportAllChanges);
if (po) {
// Also observe entries of type `first-input`. This is useful in cases
// where the first interaction is less than the `durationThreshold`.
po.observe({ type: 'first-input', buffered: true });
onHidden(() => {
handleEntries(po.takeRecords());
report(true);
});
// Only report after a bfcache restore if the `PerformanceObserver`
// successfully registered.
onBFCacheRestore(() => {
resetInteractions();
metric = initMetric('INP');
report = bindReporter(onReport, metric, INPThresholds, opts.reportAllChanges);
});
}
});
};

View File

@ -0,0 +1,15 @@
import { LCPMetric, MetricRatingThresholds, ReportOpts } from './types.js';
/** Thresholds for LCP. See https://web.dev/articles/lcp#what_is_a_good_lcp_score */
export declare const LCPThresholds: MetricRatingThresholds;
/**
* Calculates the [LCP](https://web.dev/articles/lcp) value for the current page and
* calls the `callback` function once the value is ready (along with the
* relevant `largest-contentful-paint` performance entry used to determine the
* value). The reported value is a `DOMHighResTimeStamp`.
*
* If the `reportAllChanges` configuration option is set to `true`, the
* `callback` 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.
*/
export declare const onLCP: (onReport: (metric: LCPMetric) => void, opts?: ReportOpts) => void;

106
24_12_09/node_modules/web-vitals/dist/modules/onLCP.js generated vendored Normal file
View File

@ -0,0 +1,106 @@
/*
* 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 { onBFCacheRestore } from './lib/bfcache.js';
import { bindReporter } from './lib/bindReporter.js';
import { doubleRAF } from './lib/doubleRAF.js';
import { getActivationStart } from './lib/getActivationStart.js';
import { getVisibilityWatcher } from './lib/getVisibilityWatcher.js';
import { initMetric } from './lib/initMetric.js';
import { observe } from './lib/observe.js';
import { onHidden } from './lib/onHidden.js';
import { runOnce } from './lib/runOnce.js';
import { whenActivated } from './lib/whenActivated.js';
import { whenIdle } from './lib/whenIdle.js';
/** Thresholds for LCP. See https://web.dev/articles/lcp#what_is_a_good_lcp_score */
export const LCPThresholds = [2500, 4000];
const reportedMetricIDs = {};
/**
* Calculates the [LCP](https://web.dev/articles/lcp) value for the current page and
* calls the `callback` function once the value is ready (along with the
* relevant `largest-contentful-paint` performance entry used to determine the
* value). The reported value is a `DOMHighResTimeStamp`.
*
* If the `reportAllChanges` configuration option is set to `true`, the
* `callback` 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.
*/
export const onLCP = (onReport, opts) => {
// Set defaults
opts = opts || {};
whenActivated(() => {
const visibilityWatcher = getVisibilityWatcher();
let metric = initMetric('LCP');
let report;
const handleEntries = (entries) => {
// If reportAllChanges is set then call this function for each entry,
// otherwise only consider the last one.
if (!opts.reportAllChanges) {
entries = entries.slice(-1);
}
entries.forEach((entry) => {
// Only report if the page wasn't hidden prior to LCP.
if (entry.startTime < visibilityWatcher.firstHiddenTime) {
// The startTime attribute returns the value of the renderTime if it is
// not 0, and the value of the loadTime otherwise. The activationStart
// reference is used because LCP should be relative to page activation
// rather than navigation start if the page was prerendered. But in cases
// where `activationStart` occurs after the LCP, this time should be
// clamped at 0.
metric.value = Math.max(entry.startTime - getActivationStart(), 0);
metric.entries = [entry];
report();
}
});
};
const po = observe('largest-contentful-paint', handleEntries);
if (po) {
report = bindReporter(onReport, metric, LCPThresholds, opts.reportAllChanges);
const stopListening = runOnce(() => {
if (!reportedMetricIDs[metric.id]) {
handleEntries(po.takeRecords());
po.disconnect();
reportedMetricIDs[metric.id] = true;
report(true);
}
});
// Stop listening after input. Note: while scrolling is an input that
// stops LCP observation, it's unreliable since it can be programmatically
// generated. See: https://github.com/GoogleChrome/web-vitals/issues/75
['keydown', 'click'].forEach((type) => {
// Wrap in a setTimeout so the callback is run in a separate task
// to avoid extending the keyboard/click handler to reduce INP impact
// https://github.com/GoogleChrome/web-vitals/issues/383
addEventListener(type, () => whenIdle(stopListening), {
once: true,
capture: true,
});
});
onHidden(stopListening);
// Only report after a bfcache restore if the `PerformanceObserver`
// successfully registered.
onBFCacheRestore((event) => {
metric = initMetric('LCP');
report = bindReporter(onReport, metric, LCPThresholds, opts.reportAllChanges);
doubleRAF(() => {
metric.value = performance.now() - event.timeStamp;
reportedMetricIDs[metric.id] = true;
report(true);
});
});
}
});
};

View File

@ -0,0 +1,19 @@
import { MetricRatingThresholds, ReportOpts, TTFBMetric } from './types.js';
/** Thresholds for TTFB. See https://web.dev/articles/ttfb#what_is_a_good_ttfb_score */
export declare const TTFBThresholds: MetricRatingThresholds;
/**
* Calculates the [TTFB](https://web.dev/articles/ttfb) value for the
* current page and calls the `callback` 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 `callback`
* 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 time spent on DNS lookup, connection negotiation, network latency,
* and server processing time.
*/
export declare const onTTFB: (onReport: (metric: TTFBMetric) => void, opts?: ReportOpts) => void;

View File

@ -0,0 +1,79 @@
/*
* 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 { initMetric } from './lib/initMetric.js';
import { onBFCacheRestore } from './lib/bfcache.js';
import { getNavigationEntry } from './lib/getNavigationEntry.js';
import { getActivationStart } from './lib/getActivationStart.js';
import { whenActivated } from './lib/whenActivated.js';
/** Thresholds for TTFB. See https://web.dev/articles/ttfb#what_is_a_good_ttfb_score */
export const TTFBThresholds = [800, 1800];
/**
* Runs in the next task after the page is done loading and/or prerendering.
* @param callback
*/
const whenReady = (callback) => {
if (document.prerendering) {
whenActivated(() => whenReady(callback));
}
else if (document.readyState !== 'complete') {
addEventListener('load', () => whenReady(callback), true);
}
else {
// Queue a task so the callback runs after `loadEventEnd`.
setTimeout(callback, 0);
}
};
/**
* Calculates the [TTFB](https://web.dev/articles/ttfb) value for the
* current page and calls the `callback` 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 `callback`
* 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 time spent on DNS lookup, connection negotiation, network latency,
* and server processing time.
*/
export const onTTFB = (onReport, opts) => {
// Set defaults
opts = opts || {};
let metric = initMetric('TTFB');
let report = bindReporter(onReport, metric, TTFBThresholds, opts.reportAllChanges);
whenReady(() => {
const navigationEntry = getNavigationEntry();
if (navigationEntry) {
// The activationStart reference is used because TTFB should be
// relative to page activation rather than navigation start if the
// page was prerendered. But in cases where `activationStart` occurs
// after the first byte is received, this time should be clamped at 0.
metric.value = Math.max(navigationEntry.responseStart - getActivationStart(), 0);
metric.entries = [navigationEntry];
report(true);
// Only report TTFB after bfcache restores if a `navigation` entry
// was reported for the initial load.
onBFCacheRestore(() => {
metric = initMetric('TTFB', 0);
report = bindReporter(onReport, metric, TTFBThresholds, opts.reportAllChanges);
report(true);
});
}
});
};

View File

@ -0,0 +1,54 @@
export * from './types/base.js';
export * from './types/polyfills.js';
export * from './types/cls.js';
export * from './types/fcp.js';
export * from './types/fid.js';
export * from './types/inp.js';
export * from './types/lcp.js';
export * from './types/ttfb.js';
interface PerformanceEntryMap {
navigation: PerformanceNavigationTiming;
resource: PerformanceResourceTiming;
paint: PerformancePaintTiming;
}
declare global {
interface Document {
prerendering?: boolean;
wasDiscarded?: boolean;
}
interface Performance {
getEntriesByType<K extends keyof PerformanceEntryMap>(type: K): PerformanceEntryMap[K][];
}
interface PerformanceObserverInit {
durationThreshold?: number;
}
interface PerformanceNavigationTiming {
activationStart?: number;
}
interface PerformanceEventTiming extends PerformanceEntry {
duration: DOMHighResTimeStamp;
interactionId: number;
}
interface LayoutShiftAttribution {
node?: Node;
previousRect: DOMRectReadOnly;
currentRect: DOMRectReadOnly;
}
interface LayoutShift extends PerformanceEntry {
value: number;
sources: LayoutShiftAttribution[];
hadRecentInput: boolean;
}
interface LargestContentfulPaint extends PerformanceEntry {
readonly renderTime: DOMHighResTimeStamp;
readonly loadTime: DOMHighResTimeStamp;
readonly size: number;
readonly id: string;
readonly url: string;
readonly element: Element | null;
}
interface PerformanceLongAnimationFrameTiming extends PerformanceEntry {
renderStart: DOMHighResTimeStamp;
duration: DOMHighResTimeStamp;
}
}

23
24_12_09/node_modules/web-vitals/dist/modules/types.js generated vendored Normal file
View 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 * from './types/base.js';
export * from './types/polyfills.js';
export * from './types/cls.js';
export * from './types/fcp.js';
export * from './types/fid.js';
export * from './types/inp.js';
export * from './types/lcp.js';
export * from './types/ttfb.js';

View File

@ -0,0 +1,101 @@
import type { CLSMetric, CLSMetricWithAttribution } from './cls.js';
import type { FCPMetric, FCPMetricWithAttribution } from './fcp.js';
import type { FIDMetric, FIDMetricWithAttribution } from './fid.js';
import type { INPMetric, INPMetricWithAttribution } from './inp.js';
import type { LCPMetric, LCPMetricWithAttribution } from './lcp.js';
import type { TTFBMetric, TTFBMetricWithAttribution } from './ttfb.js';
export interface Metric {
/**
* The name of the metric (in acronym form).
*/
name: 'CLS' | 'FCP' | 'FID' | 'INP' | 'LCP' | 'TTFB';
/**
* The current value of the metric.
*/
value: number;
/**
* The rating as to whether the metric value is within the "good",
* "needs improvement", or "poor" thresholds of the metric.
*/
rating: 'good' | 'needs-improvement' | 'poor';
/**
* 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 instance. This ID can
* be used by an analytics tool to dedupe multiple values sent for the same
* metric instance, or to group multiple deltas together and calculate a
* total. It can also be used to differentiate multiple different metric
* instances sent from the same page, which can happen if the page is
* restored from the back/forward cache (in that case new metrics object
* get created).
*/
id: string;
/**
* Any performance entries relevant to the metric value calculation.
* The array may also be empty if the metric value was not based on any
* entries (e.g. a CLS value of 0 given no layout shifts).
*/
entries: PerformanceEntry[];
/**
* The type of navigation.
*
* This will be the value returned by the Navigation Timing API (or
* `undefined` if the browser doesn't support that API), with the following
* exceptions:
* - 'back-forward-cache': for pages that are restored from the bfcache.
* - 'back_forward' is renamed to 'back-forward' for consistency.
* - 'prerender': for pages that were prerendered.
* - 'restore': for pages that were discarded by the browser and then
* restored by the user.
*/
navigationType: 'navigate' | 'reload' | 'back-forward' | 'back-forward-cache' | 'prerender' | 'restore';
}
/** The union of supported metric types. */
export type MetricType = CLSMetric | FCPMetric | FIDMetric | INPMetric | LCPMetric | TTFBMetric;
/** The union of supported metric attribution types. */
export type MetricWithAttribution = CLSMetricWithAttribution | FCPMetricWithAttribution | FIDMetricWithAttribution | INPMetricWithAttribution | LCPMetricWithAttribution | TTFBMetricWithAttribution;
/**
* The thresholds of metric's "good", "needs improvement", and "poor" ratings.
*
* - Metric values up to and including [0] are rated "good"
* - Metric values up to and including [1] are rated "needs improvement"
* - Metric values above [1] are "poor"
*
* | Metric value | Rating |
* | --------------- | ------------------- |
* | [0] | "good" |
* | > [0] and [1] | "needs improvement" |
* | > [1] | "poor" |
*/
export type MetricRatingThresholds = [number, number];
/**
* @deprecated Use metric-specific function types instead, such as:
* `(metric: LCPMetric) => void`. If a single callback type is needed for
* multiple metrics, use `(metric: MetricType) => void`.
*/
export interface ReportCallback {
(metric: MetricType): void;
}
export interface ReportOpts {
reportAllChanges?: boolean;
durationThreshold?: number;
}
/**
* The loading state of the document. Note: this value is similar to
* `document.readyState` but it subdivides the "interactive" state into the
* time before and after the DOMContentLoaded event fires.
*
* State descriptions:
* - `loading`: the initial document response has not yet been fully downloaded
* and parsed. This is equivalent to the corresponding `readyState` value.
* - `dom-interactive`: the document has been fully loaded and parsed, but
* scripts may not have yet finished loading and executing.
* - `dom-content-loaded`: the document is fully loaded and parsed, and all
* scripts (except `async` scripts) have loaded and finished executing.
* - `complete`: the document and all of its sub-resources have finished
* loading. This is equivalent to the corresponding `readyState` value.
*/
export type LoadState = 'loading' | 'dom-interactive' | 'dom-content-loaded' | 'complete';

Some files were not shown because too many files have changed in this diff Show More