@zendesk/react-measure-timing-hooks
Version:
react hooks for measuring time to interactive and time to render of components
87 lines • 3.44 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.adjustTimestampBy = exports.ensureTimestamp = void 0;
exports.getEpochCorrectedForDrift = getEpochCorrectedForDrift;
const JUST_CREATED_THRESHOLD_MS_AGO = 10;
/**
* Ensures that the input timestamp object has both epoch and performance.now() time.
* If no input is provided, it generates a new timestamp with the current time.
*
* why store both timestamps?:
* - epoch time: for reporting absolute time / correlating with backends
* - durations: for calculating durations between two timestamps, performance.now() time makes more sense
* - both are needed to correctly calculate a drift-adjusted epoch time
*
* @param input - Partial timestamp object that may contain either epoch or now or both.
* @returns Full timestamp object with both epoch and performance.now() time.
*/
const ensureTimestamp = (input) => {
const inputEpoch = input?.epoch;
const inputNow = input?.now;
const hasInputEpoch = typeof inputEpoch === 'number';
const hasInputNow = typeof inputNow === 'number';
if (hasInputEpoch && hasInputNow) {
return input;
}
if (hasInputEpoch) {
const differenceFromNow = Date.now() - inputEpoch;
return {
epoch: inputEpoch,
now: Math.abs(differenceFromNow) < JUST_CREATED_THRESHOLD_MS_AGO
? performance.now()
: performance.now() + differenceFromNow,
};
}
if (hasInputNow) {
const elapsedTimeSinceInput = performance.now() - inputNow;
const epoch = Date.now() - elapsedTimeSinceInput;
return {
epoch,
now: inputNow,
};
}
// no data provided, use current time
return {
now: performance.now(),
epoch: Date.now(),
};
};
exports.ensureTimestamp = ensureTimestamp;
// below inspired by Datadog SDK
// https://github.com/DataDog/browser-sdk/blob/16efff71b42530ead10400eb23c096b54e83fad4/packages/core/src/tools/utils/timeUtils.ts#L23-L30
/**
* Navigation start slightly change on some rare cases
*/
let navigationStart;
function getNavigationStart() {
if (navigationStart === undefined) {
navigationStart = performance.timing.navigationStart;
}
return navigationStart;
}
/**
* Why do we need to account for drift?
* - monotonic clock (performance.now()) is not guaranteed to be in sync with wall clock time (Date.now())
* - Earth’s rotation speed is not constant
* - browser bugs cause the monotonic clock to not tick when the computer is asleep or when the process is throttled
* @see
* - https://github.com/w3c/performance-timeline/issues/206
* - https://dev.to/noamr/when-a-millisecond-is-not-a-millisecond-3h6
* - https://issues.chromium.org/issues/41450546
*/
function getEpochCorrectedForDrift({ epoch, now }) {
// two different time origins
const calculatedTimeOrigin = epoch - now;
const navigationStartEpoch = getNavigationStart();
// apply correction only for positive drift
if (calculatedTimeOrigin > navigationStartEpoch) {
return Math.round(calculatedTimeOrigin + now);
}
return Math.round(navigationStartEpoch + now);
}
const adjustTimestampBy = (timestamp, adjustment) => ({
epoch: timestamp.epoch + adjustment,
now: timestamp.now + adjustment,
});
exports.adjustTimestampBy = adjustTimestampBy;
//# sourceMappingURL=ensureTimestamp.js.map
;