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

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 page—including if the user returns to the page after
* it's been hidden/backgrounded. However, since browsers often [will not fire
* additional callbacks once the user has backgrounded a
* page](https://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 page—including if the user returns to the page after
* it's been hidden/backgrounded. However, since browsers often [will not fire
* additional callbacks once the user has backgrounded a
* page](https://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 page—including if the user returns to the page after
* it's been hidden/backgrounded. However, since browsers often [will not fire
* additional callbacks once the user has backgrounded a
* page](https://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 page—including if the user returns to the page after
* it's been hidden/backgrounded. However, since browsers often [will not fire
* additional callbacks once the user has backgrounded a
* page](https://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 page—including if the user returns to the page after
* it's been hidden/backgrounded. However, since browsers often [will not fire
* additional callbacks once the user has backgrounded a
* page](https://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 page—including if the user returns to the page after
* it's been hidden/backgrounded. However, since browsers often [will not fire
* additional callbacks once the user has backgrounded a
* page](https://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 page—including if the user returns to the page after
* it's been hidden/backgrounded. However, since browsers often [will not fire
* additional callbacks once the user has backgrounded a
* page](https://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 page—including if the user returns to the page after
* it's been hidden/backgrounded. However, since browsers often [will not fire
* additional callbacks once the user has backgrounded a
* page](https://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';

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 {};

View File

@@ -0,0 +1,55 @@
import type { LoadState, Metric } from './base.js';
/**
* A CLS-specific version of the Metric object.
*/
export interface CLSMetric extends Metric {
name: 'CLS';
entries: LayoutShift[];
}
/**
* An object containing potentially-helpful debugging information that
* can be sent along with the CLS value for the current page visit in order
* to help identify issues happening to real-users in the field.
*/
export interface CLSAttribution {
/**
* A selector identifying the first element (in document order) that
* shifted when the single largest layout shift contributing to the page's
* CLS score occurred.
*/
largestShiftTarget?: string;
/**
* The time when the single largest layout shift contributing to the page's
* CLS score occurred.
*/
largestShiftTime?: DOMHighResTimeStamp;
/**
* The layout shift score of the single largest layout shift contributing to
* the page's CLS score.
*/
largestShiftValue?: number;
/**
* The `LayoutShiftEntry` representing the single largest layout shift
* contributing to the page's CLS score. (Useful when you need more than just
* `largestShiftTarget`, `largestShiftTime`, and `largestShiftValue`).
*/
largestShiftEntry?: LayoutShift;
/**
* The first element source (in document order) among the `sources` list
* of the `largestShiftEntry` object. (Also useful when you need more than
* just `largestShiftTarget`, `largestShiftTime`, and `largestShiftValue`).
*/
largestShiftSource?: LayoutShiftAttribution;
/**
* The loading state of the document at the time when the largest layout
* shift contribution to the page's CLS score occurred (see `LoadState`
* for details).
*/
loadState?: LoadState;
}
/**
* A CLS-specific version of the Metric object with attribution.
*/
export interface CLSMetricWithAttribution extends CLSMetric {
attribution: CLSAttribution;
}

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 {};

View File

@@ -0,0 +1,46 @@
import type { LoadState, Metric } from './base.js';
/**
* An FCP-specific version of the Metric object.
*/
export interface FCPMetric extends Metric {
name: 'FCP';
entries: PerformancePaintTiming[];
}
/**
* An object containing potentially-helpful debugging information that
* can be sent along with the FCP value for the current page visit in order
* to help identify issues happening to real-users in the field.
*/
export interface FCPAttribution {
/**
* The time from when the user initiates loading the page until when the
* browser receives the first byte of the response (a.k.a. TTFB).
*/
timeToFirstByte: number;
/**
* The delta between TTFB and the first contentful paint (FCP).
*/
firstByteToFCP: number;
/**
* The loading state of the document at the time when FCP `occurred (see
* `LoadState` for details). Ideally, documents can paint before they finish
* loading (e.g. the `loading` or `dom-interactive` phases).
*/
loadState: LoadState;
/**
* The `PerformancePaintTiming` entry corresponding to FCP.
*/
fcpEntry?: PerformancePaintTiming;
/**
* The `navigation` entry of the current page, which is useful for diagnosing
* general page load issues. This can be used to access `serverTiming` for example:
* navigationEntry?.serverTiming
*/
navigationEntry?: PerformanceNavigationTiming;
}
/**
* An FCP-specific version of the Metric object with attribution.
*/
export interface FCPMetricWithAttribution extends FCPMetric {
attribution: FCPAttribution;
}

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 {};

View File

@@ -0,0 +1,46 @@
import type { LoadState, Metric } from './base.js';
/**
* An FID-specific version of the Metric object.
*/
export interface FIDMetric extends Metric {
name: 'FID';
entries: PerformanceEventTiming[];
}
/**
* An object containing potentially-helpful debugging information that
* can be sent along with the FID value for the current page visit in order
* to help identify issues happening to real-users in the field.
*/
export interface FIDAttribution {
/**
* A selector identifying the element that the user interacted with. This
* element will be the `target` of the `event` dispatched.
*/
eventTarget: string;
/**
* The time when the user interacted. This time will match the `timeStamp`
* value of the `event` dispatched.
*/
eventTime: number;
/**
* The `type` of the `event` dispatched from the user interaction.
*/
eventType: string;
/**
* The `PerformanceEventTiming` entry corresponding to FID.
*/
eventEntry: PerformanceEventTiming;
/**
* The loading state of the document at the time when the first interaction
* occurred (see `LoadState` for details). If the first interaction occurred
* while the document was loading and executing script (e.g. usually in the
* `dom-interactive` phase) it can result in long input delays.
*/
loadState: LoadState;
}
/**
* An FID-specific version of the Metric object with attribution.
*/
export interface FIDMetricWithAttribution extends FIDMetric {
attribution: FIDAttribution;
}

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 {};

View File

@@ -0,0 +1,103 @@
import type { LoadState, Metric } from './base.js';
/**
* An INP-specific version of the Metric object.
*/
export interface INPMetric extends Metric {
name: 'INP';
entries: PerformanceEventTiming[];
}
/**
* An object containing potentially-helpful debugging information that
* can be sent along with the INP value for the current page visit in order
* to help identify issues happening to real-users in the field.
*/
export interface INPAttribution {
/**
* A selector identifying the element that the user first interacted with
* as part of the frame where the INP candidate interaction occurred.
* If this value is an empty string, that generally means the element was
* removed from the DOM after the interaction.
*/
interactionTarget: string;
/**
* A reference to the HTML element identified by `interactionTargetSelector`.
* NOTE: for attribution purpose, a selector identifying the element is
* typically more useful than the element itself. However, the element is
* also made available in case additional context is needed.
*/
interactionTargetElement: Node | undefined;
/**
* The time when the user first interacted during the frame where the INP
* candidate interaction occurred (if more than one interaction occurred
* within the frame, only the first time is reported).
*/
interactionTime: DOMHighResTimeStamp;
/**
* The best-guess timestamp of the next paint after the interaction.
* In general, this timestamp is the same as the `startTime + duration` of
* the event timing entry. However, since `duration` values are rounded to
* the nearest 8ms, it can sometimes appear that the paint occurred before
* processing ended (which cannot happen). This value clamps the paint time
* so it's always after `processingEnd` from the Event Timing API and
* `renderStart` from the Long Animation Frame API (where available).
* It also averages the duration values for all entries in the same
* animation frame, which should be closer to the "real" value.
*/
nextPaintTime: DOMHighResTimeStamp;
/**
* The type of interaction, based on the event type of the `event` entry
* that corresponds to the interaction (i.e. the first `event` entry
* containing an `interactionId` dispatched in a given animation frame).
* For "pointerdown", "pointerup", or "click" events this will be "pointer",
* and for "keydown" or "keyup" events this will be "keyboard".
*/
interactionType: 'pointer' | 'keyboard';
/**
* An array of Event Timing entries that were processed within the same
* animation frame as the INP candidate interaction.
*/
processedEventEntries: PerformanceEventTiming[];
/**
* If the browser supports the Long Animation Frame API, this array will
* include any `long-animation-frame` entries that intersect with the INP
* candidate interaction's `startTime` and the `processingEnd` time of the
* last event processed within that animation frame. If the browser does not
* support the Long Animation Frame API or no `long-animation-frame` entries
* are detect, this array will be empty.
*/
longAnimationFrameEntries: PerformanceLongAnimationFrameTiming[];
/**
* The time from when the user interacted with the page until when the
* browser was first able to start processing event listeners for that
* interaction. This time captures the delay before event processing can
* begin due to the main thread being busy with other work.
*/
inputDelay: number;
/**
* The time from when the first event listener started running in response to
* the user interaction until when all event listener processing has finished.
*/
processingDuration: number;
/**
* The time from when the browser finished processing all event listeners for
* the user interaction until the next frame is presented on the screen and
* visible to the user. This time includes work on the main thread (such as
* `requestAnimationFrame()` callbacks, `ResizeObserver` and
* `IntersectionObserver` callbacks, and style/layout calculation) as well
* as off-main-thread work (such as compositor, GPU, and raster work).
*/
presentationDelay: number;
/**
* The loading state of the document at the time when the interaction
* corresponding to INP occurred (see `LoadState` for details). If the
* interaction occurred while the document was loading and executing script
* (e.g. usually in the `dom-interactive` phase) it can result in long delays.
*/
loadState: LoadState;
}
/**
* An INP-specific version of the Metric object with attribution.
*/
export interface INPMetricWithAttribution extends INPMetric {
attribution: INPAttribution;
}

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 {};

View File

@@ -0,0 +1,69 @@
import type { Metric } from './base.js';
/**
* An LCP-specific version of the Metric object.
*/
export interface LCPMetric extends Metric {
name: 'LCP';
entries: LargestContentfulPaint[];
}
/**
* An object containing potentially-helpful debugging information that
* can be sent along with the LCP value for the current page visit in order
* to help identify issues happening to real-users in the field.
*/
export interface LCPAttribution {
/**
* The element corresponding to the largest contentful paint for the page.
*/
element?: string;
/**
* The URL (if applicable) of the LCP image resource. If the LCP element
* is a text node, this value will not be set.
*/
url?: string;
/**
* The time from when the user initiates loading the page until when the
* browser receives the first byte of the response (a.k.a. TTFB). See
* [Optimize LCP](https://web.dev/articles/optimize-lcp) for details.
*/
timeToFirstByte: number;
/**
* The delta between TTFB and when the browser starts loading the LCP
* resource (if there is one, otherwise 0). See [Optimize
* LCP](https://web.dev/articles/optimize-lcp) for details.
*/
resourceLoadDelay: number;
/**
* The total time it takes to load the LCP resource itself (if there is one,
* otherwise 0). See [Optimize LCP](https://web.dev/articles/optimize-lcp) for
* details.
*/
resourceLoadDuration: number;
/**
* The delta between when the LCP resource finishes loading until the LCP
* element is fully rendered. See [Optimize
* LCP](https://web.dev/articles/optimize-lcp) for details.
*/
elementRenderDelay: number;
/**
* The `navigation` entry of the current page, which is useful for diagnosing
* general page load issues. This can be used to access `serverTiming` for example:
* navigationEntry?.serverTiming
*/
navigationEntry?: PerformanceNavigationTiming;
/**
* The `resource` entry for the LCP resource (if applicable), which is useful
* for diagnosing resource load issues.
*/
lcpResourceEntry?: PerformanceResourceTiming;
/**
* The `LargestContentfulPaint` entry corresponding to LCP.
*/
lcpEntry?: LargestContentfulPaint;
}
/**
* An LCP-specific version of the Metric object with attribution.
*/
export interface LCPMetricWithAttribution extends LCPMetric {
attribution: LCPAttribution;
}

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 {};

View File

@@ -0,0 +1,4 @@
export type FirstInputPolyfillEntry = Omit<PerformanceEventTiming, 'processingEnd'>;
export interface FirstInputPolyfillCallback {
(entry: FirstInputPolyfillEntry): void;
}

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 {};

View File

@@ -0,0 +1,59 @@
import type { Metric } from './base.js';
/**
* A TTFB-specific version of the Metric object.
*/
export interface TTFBMetric extends Metric {
name: 'TTFB';
entries: PerformanceNavigationTiming[];
}
/**
* An object containing potentially-helpful debugging information that
* can be sent along with the TTFB value for the current page visit in order
* to help identify issues happening to real-users in the field.
*
* NOTE: these values are primarily useful for page loads not handled via
* service worker, as browsers differ in what they report when service worker
* is involved, see: https://github.com/w3c/navigation-timing/issues/199
*/
export interface TTFBAttribution {
/**
* The total time from when the user initiates loading the page to when the
* page starts to handle the request. Large values here are typically due
* to HTTP redirects, though other browser processing contributes to this
* duration as well (so even without redirect it's generally not zero).
*/
waitingDuration: number;
/**
* The total time spent checking the HTTP cache for a match. For navigations
* handled via service worker, this duration usually includes service worker
* start-up time as well as time processing `fetch` event listeners, with
* some exceptions, see: https://github.com/w3c/navigation-timing/issues/199
*/
cacheDuration: number;
/**
* The total time to resolve the DNS for the requested domain.
*/
dnsDuration: number;
/**
* The total time to create the connection to the requested domain.
*/
connectionDuration: number;
/**
* The total time from when the request was sent until the first byte of the
* response was received. This includes network time as well as server
* processing time.
*/
requestDuration: number;
/**
* The `navigation` entry of the current page, which is useful for diagnosing
* general page load issues. This can be used to access `serverTiming` for
* example: navigationEntry?.serverTiming
*/
navigationEntry?: PerformanceNavigationTiming;
}
/**
* A TTFB-specific version of the Metric object with attribution.
*/
export interface TTFBMetricWithAttribution extends TTFBMetric {
attribution: TTFBAttribution;
}

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 {};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
24_12_09/node_modules/web-vitals/dist/web-vitals.js generated vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

174
24_12_09/node_modules/web-vitals/package.json generated vendored Normal file
View File

@@ -0,0 +1,174 @@
{
"name": "web-vitals",
"version": "4.2.4",
"description": "Easily measure performance metrics in JavaScript",
"type": "module",
"typings": "dist/modules/index.d.ts",
"main": "dist/web-vitals.umd.cjs",
"module": "dist/web-vitals.js",
"unpkg": "dist/web-vitals.iife.js",
"exports": {
".": {
"types": "./dist/modules/index.d.ts",
"require": "./dist/web-vitals.umd.cjs",
"default": "./dist/web-vitals.js"
},
"./attribution": {
"types": "./dist/modules/attribution/index.d.ts",
"require": "./dist/web-vitals.attribution.umd.cjs",
"default": "./dist/web-vitals.attribution.js"
},
"./attribution.js": {
"types": "./dist/modules/attribution/index.d.ts",
"require": "./dist/web-vitals.attribution.umd.cjs",
"default": "./dist/web-vitals.attribution.js"
},
"./onCLS.js": {
"types": "./dist/modules/onCLS.d.ts",
"default": "./dist/modules/onCLS.js"
},
"./onFCP.js": {
"types": "./dist/modules/onFCP.d.ts",
"default": "./dist/modules/onFCP.js"
},
"./onFID.js": {
"types": "./dist/modules/onFID.d.ts",
"default": "./dist/modules/onFID.js"
},
"./onINP.js": {
"types": "./dist/modules/onINP.d.ts",
"default": "./dist/modules/onINP.js"
},
"./onLCP.js": {
"types": "./dist/modules/onLCP.d.ts",
"default": "./dist/modules/onLCP.js"
},
"./onTTFB.js": {
"types": "./dist/modules/onTTFB.d.ts",
"default": "./dist/modules/onTTFB.js"
},
"./attribution/onCLS.js": {
"types": "./dist/modules/attribution/onCLS.d.ts",
"default": "./dist/modules/attribution/onCLS.js"
},
"./attribution/onFCP.js": {
"types": "./dist/modules/attribution/onFCP.d.ts",
"default": "./dist/modules/attribution/onFCP.js"
},
"./attribution/onFID.js": {
"types": "./dist/modules/attribution/onFID.d.ts",
"default": "./dist/modules/attribution/onFID.js"
},
"./attribution/onINP.js": {
"types": "./dist/modules/attribution/onINP.d.ts",
"default": "./dist/modules/attribution/onINP.js"
},
"./attribution/onLCP.js": {
"types": "./dist/modules/attribution/onLCP.d.ts",
"default": "./dist/modules/attribution/onLCP.js"
},
"./attribution/onTTFB.js": {
"types": "./dist/modules/attribution/onTTFB.d.ts",
"default": "./dist/modules/attribution/onTTFB.js"
}
},
"files": [
"attribution.js",
"attribution.d.ts",
"dist",
"src"
],
"scripts": {
"build": "run-s clean build:ts build:js",
"build:ts": "tsc -b",
"build:js": "rollup -c",
"clean": "rm -rf dist tsconfig.tsbuildinfo",
"dev": "run-p watch test:server",
"format": "prettier \"**/*.{cjs,css,html,js,json,md,ts,yml,yaml}\" --write --ignore-path .gitignore",
"format:check": "prettier \"**/*.{cjs,css,html,js,json,html,md,ts,yml,yaml}\" --check --ignore-path .gitignore",
"lint": "eslint \"*.js\" \"src/**/*.ts\" \"test/**/*.js\"",
"lint:fix": "eslint --fix \"*.js\" \"src/**/*.ts\" \"test/**/*.js\"",
"postversion": "git push --follow-tags",
"release:major": "npm version major -m 'Release v%s' && npm publish",
"release:minor": "npm version minor -m 'Release v%s' && npm publish",
"release:patch": "npm version patch -m 'Release v%s' && npm publish",
"test": "npm-run-all build test:unit -p -r test:e2e test:server",
"test:e2e": "wdio wdio.conf.cjs",
"test:server": "node test/server.js",
"test:unit": "node --test test/unit/*test.js",
"start": "run-s build:ts test:server watch",
"watch": "run-p watch:*",
"watch:ts": "tsc -b -w",
"watch:js": "rollup -c -w",
"version": "run-s build",
"prepare": "husky install"
},
"keywords": [
"crux",
"performance",
"metrics",
"Core Web Vitals",
"CLS",
"FCP",
"FID",
"INP",
"LCP",
"TTFB"
],
"author": {
"name": "Philip Walton",
"email": "philip@philipwalton.com",
"url": "http://philipwalton.com"
},
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "https://github.com/GoogleChrome/web-vitals.git"
},
"bugs": {
"url": "https://github.com/GoogleChrome/web-vitals/issues"
},
"husky": {
"hooks": {
"pre-commit": "npm run lint"
}
},
"prettier": {
"arrowParens": "always",
"bracketSpacing": false,
"quoteProps": "preserve",
"singleQuote": true
},
"devDependencies": {
"@babel/core": "^7.23.6",
"@babel/preset-env": "^7.23.6",
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-replace": "^5.0.5",
"@rollup/plugin-terser": "^0.4.4",
"@typescript-eslint/eslint-plugin": "^6.16.0",
"@typescript-eslint/parser": "^6.16.0",
"@wdio/cli": "^8.32.3",
"@wdio/local-runner": "^8.32.3",
"@wdio/mocha-framework": "^8.32.3",
"@wdio/spec-reporter": "^8.32.2",
"body-parser": "^1.20.2",
"chromedriver": "^122.0.4",
"eslint": "^8.56.0",
"express": "^4.18.2",
"fs-extra": "^11.2.0",
"husky": "^8.0.3",
"lint-staged": "^15.2.0",
"npm-run-all": "^4.1.5",
"nunjucks": "^3.2.4",
"prettier": "^3.1.1",
"rollup": "^4.9.1",
"selenium-standalone": "^9.3.1",
"typescript": "^5.3.3",
"wdio-chromedriver-service": "^8.1.1",
"yargs": "^17.7.2"
},
"lint-staged": {
"**/*.{js,ts}": "eslint --fix --ignore-path .gitignore",
"**/*.{cjs,css,html,js,json,html,md,ts,yml,yaml}": "prettier --write --ignore-path .gitignore"
}
}

View File

@@ -0,0 +1,26 @@
/*
* 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';

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