init commit

This commit is contained in:
rxliuli
2025-11-04 05:03:50 +08:00
commit bce557cc2d
1396 changed files with 172991 additions and 0 deletions

View File

@@ -0,0 +1,105 @@
import { bindReporter } from './lib/bindReporter.js';
import { initMetric } from './lib/initMetric.js';
import { observe } from './lib/observe.js';
import { onHidden } from './lib/onHidden.js';
/*
* 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.
*/
/**
* Calculates the [CLS](https://web.dev/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/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._
*/
const onCLS = (onReport) => {
const metric = initMetric('CLS', 0);
let report;
let sessionValue = 0;
let sessionEntries = [];
// const handleEntries = (entries: Metric['entries']) => {
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 &&
sessionEntries.length !== 0 &&
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;
if (report) {
report();
}
}
}
});
};
const po = observe('layout-shift', handleEntries);
if (po) {
report = bindReporter(onReport, metric);
const stopListening = () => {
handleEntries(po.takeRecords() );
report(true);
};
onHidden(stopListening);
return stopListening;
}
return;
};
export { onCLS };
//# sourceMappingURL=getCLS.js.map

View File

@@ -0,0 +1,63 @@
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';
/*
* 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.
*/
/**
* Calculates the [FID](https://web.dev/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._
*/
const onFID = (onReport) => {
const visibilityWatcher = getVisibilityWatcher();
const metric = initMetric('FID');
// eslint-disable-next-line prefer-const
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);
if (po) {
onHidden(() => {
handleEntries(po.takeRecords() );
po.disconnect();
}, true);
}
};
export { onFID };
//# sourceMappingURL=getFID.js.map

View File

@@ -0,0 +1,85 @@
import { bindReporter } from './lib/bindReporter.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';
/*
* 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 reportedMetricIDs = {};
/**
* Calculates the [LCP](https://web.dev/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`.
*/
const onLCP = (onReport) => {
const visibilityWatcher = getVisibilityWatcher();
const metric = initMetric('LCP');
let report;
const handleEntries = (entries) => {
const lastEntry = entries[entries.length - 1] ;
if (lastEntry) {
// 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.
const value = Math.max(lastEntry.startTime - getActivationStart(), 0);
// Only report if the page wasn't hidden prior to LCP.
if (value < visibilityWatcher.firstHiddenTime) {
metric.value = value;
metric.entries = [lastEntry];
report();
}
}
};
const po = observe('largest-contentful-paint', handleEntries);
if (po) {
report = bindReporter(onReport, metric);
const stopListening = () => {
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
// stop LCP observation, it's unreliable since it can be programmatically
// generated. See: https://github.com/GoogleChrome/web-vitals/issues/75
['keydown', 'click'].forEach(type => {
addEventListener(type, stopListening, { once: true, capture: true });
});
onHidden(stopListening, true);
return stopListening;
}
return;
};
export { onLCP };
//# sourceMappingURL=getLCP.js.map

View File

@@ -0,0 +1,28 @@
const bindReporter = (
callback,
metric,
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;
callback(metric);
}
}
}
};
};
export { bindReporter };
//# sourceMappingURL=bindReporter.js.map

View File

@@ -0,0 +1,27 @@
/*
* 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}
*/
const generateUniqueID = () => {
return `v3-${Date.now()}-${Math.floor(Math.random() * (9e12 - 1)) + 1e12}`;
};
export { generateUniqueID };
//# sourceMappingURL=generateUniqueID.js.map

View File

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

View File

@@ -0,0 +1,53 @@
import { WINDOW } from '../../types.js';
/*
* 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 getNavigationEntryFromPerformanceTiming = () => {
// eslint-disable-next-line deprecation/deprecation
const timing = WINDOW.performance.timing;
// eslint-disable-next-line deprecation/deprecation
const type = WINDOW.performance.navigation.type;
const navigationEntry = {
entryType: 'navigation',
startTime: 0,
type: type == 2 ? 'back_forward' : type === 1 ? 'reload' : 'navigate',
};
for (const key in timing) {
if (key !== 'navigationStart' && key !== 'toJSON') {
// eslint-disable-next-line deprecation/deprecation
navigationEntry[key] = Math.max((timing[key ] ) - timing.navigationStart, 0);
}
}
return navigationEntry ;
};
const getNavigationEntry = () => {
if (WINDOW.__WEB_VITALS_POLYFILL__) {
return (
WINDOW.performance &&
((performance.getEntriesByType && performance.getEntriesByType('navigation')[0]) ||
getNavigationEntryFromPerformanceTiming())
);
} else {
return WINDOW.performance && performance.getEntriesByType && performance.getEntriesByType('navigation')[0];
}
};
export { getNavigationEntry };
//# sourceMappingURL=getNavigationEntry.js.map

View File

@@ -0,0 +1,54 @@
import { WINDOW } from '../../types.js';
import { onHidden } from './onHidden.js';
/*
* 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 = -1;
const initHiddenTime = () => {
// If the document is hidden and not prerendering, assume it was always
// hidden and the page was loaded in the background.
return WINDOW.document.visibilityState === 'hidden' && !WINDOW.document.prerendering ? 0 : Infinity;
};
const trackChanges = () => {
// Update the time if/when the document becomes hidden.
onHidden(({ timeStamp }) => {
firstHiddenTime = timeStamp;
}, true);
};
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();
trackChanges();
}
return {
get firstHiddenTime() {
return firstHiddenTime;
},
};
};
export { getVisibilityWatcher };
//# sourceMappingURL=getVisibilityWatcher.js.map

View File

@@ -0,0 +1,46 @@
import { WINDOW } from '../../types.js';
import { generateUniqueID } from './generateUniqueID.js';
import { getActivationStart } from './getActivationStart.js';
import { getNavigationEntry } from './getNavigationEntry.js';
/*
* 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 initMetric = (name, value) => {
const navEntry = getNavigationEntry();
let navigationType = 'navigate';
if (navEntry) {
if (WINDOW.document.prerendering || getActivationStart() > 0) {
navigationType = 'prerender';
} else {
navigationType = navEntry.type.replace(/_/g, '-') ;
}
}
return {
name,
value: typeof value === 'undefined' ? -1 : value,
rating: 'good', // Will be updated if the value changes.
delta: 0,
entries: [],
id: generateUniqueID(),
navigationType,
};
};
export { initMetric };
//# sourceMappingURL=initMetric.js.map

View File

@@ -0,0 +1,37 @@
/**
* 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.
*/
const observe = (
type,
callback,
opts,
) => {
try {
if (PerformanceObserver.supportedEntryTypes.includes(type)) {
const po = new PerformanceObserver(list => {
callback(list.getEntries() );
});
po.observe(
Object.assign(
{
type,
buffered: true,
},
opts || {},
) ,
);
return po;
}
} catch (e) {
// Do nothing.
}
return;
};
export { observe };
//# sourceMappingURL=observe.js.map

View File

@@ -0,0 +1,36 @@
import { WINDOW } from '../../types.js';
/*
* 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 onHidden = (cb, once) => {
const onHiddenOrPageHide = (event) => {
if (event.type === 'pagehide' || WINDOW.document.visibilityState === 'hidden') {
cb(event);
if (once) {
removeEventListener('visibilitychange', onHiddenOrPageHide, true);
removeEventListener('pagehide', onHiddenOrPageHide, true);
}
}
};
addEventListener('visibilitychange', onHiddenOrPageHide, true);
// Some browsers have buggy implementations of visibilitychange,
// so we use pagehide in addition, just to be safe.
addEventListener('pagehide', onHiddenOrPageHide, true);
};
export { onHidden };
//# sourceMappingURL=onHidden.js.map