UNPKG

@atlaskit/editor-plugin-interactivity

Version:

Interactivity plugin for @atlaskit/editor-core

137 lines (129 loc) 7.51 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SCHEMA_VERSION = exports.REPORTING_THRESHOLD_MS = void 0; exports.bucketKeyForMs = bucketKeyForMs; function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } /** * Bucket boundaries for the interaction latency buckets, version 1. * * Two ranges: * - 16 ms to 200 ms — a boundary every 8 ms: 16, 24, 32, … 200. Event Timing rounds * durations to 8 ms, so nothing finer is measurable. * - above 200 ms — each boundary ~15% above the previous one, five of them per doubling: * 222, 256, 294, 337, 388, 445, 512, … A 40 ms difference matters at 100 ms and is * noise at 4 seconds, so buckets grow with the latency instead of staying 8 ms wide. * * 500 ms — the Google INP "poor" threshold — falls inside the 445…512 bucket, so that one * bucket is split at 500 to count the threshold instead of interpolating it. 200 ms, the * "good" threshold, is already a boundary. * * Bump SCHEMA_VERSION whenever any boundary moves; queries group by it. */ var SCHEMA_VERSION = exports.SCHEMA_VERSION = 1; /** * Event Timing reporting threshold. Faster interactions are never delivered to the * observer, so they reach no bucket at all — `performance.interactionCount` is what counts * them, as `totalCount - observedCount`. This bucket holds the interactions reported at * exactly the threshold. */ var REPORTING_THRESHOLD_MS = exports.REPORTING_THRESHOLD_MS = 16; /** * Exceptions to the grid: latencies that must be a boundary of their own so that they are * counted exactly rather than read off a bucket that spans them. Each one splits the bucket it * falls inside. 500 ms is the Google INP "poor" threshold; the "good" one, 200 ms, needs no * exception because the evenly spaced range already ends there. * * Every entry has to sit above that range, and adding one changes the reported keys, so bump * SCHEMA_VERSION with it. */ var EXACT_THRESHOLDS_MS = [500]; var EVENLY_SPACED_MAX_MS = 200; /** * Keep this a multiple of 8 (8, 16, 24, …). Event Timing reports durations in 8 ms steps, so a * step that is not a multiple of 8 leaves buckets no interaction can ever land in. */ var EVENLY_SPACED_STEP_MS = 8; var BOUNDARIES_PER_DOUBLING = 5; /** * The first boundary at or above `latencyMs`, for the range above 200 ms. * * Five boundaries per doubling is the same as saying the nth boundary sits at `2^(n/5)` ms — * boundary 40 at 256 ms, 45 at 512 ms, 50 at 1024 ms. So this turns the latency into a * boundary number, rounds that up, and turns it back into milliseconds. * * Boundaries are floored to whole milliseconds, which keeps each one at or below the exact * value it stands for. That is what makes `bucketKeyForMs(boundary) === boundary` hold. */ function firstBoundaryAtOrAbove(latencyMs) { // `Math.log2(latencyMs) * 5` is the boundary number: log2 answers how many doublings of // 1 ms reach this latency, and five boundaries cover each doubling. // // A latency sitting on a boundary makes that a whole number, which `Math.ceil` has to // keep. ECMA-262 only requires `Math.log2` to be approximate, so 9.0000000000000002 for // `log2(512)` would round up to boundary 46 and report 512 ms as 588 ms. EPSILON is // larger than such imprecision and far smaller than the gap between two boundaries. var EPSILON = 1e-9; var boundaryNumber = Math.ceil(BOUNDARIES_PER_DOUBLING * Math.log2(latencyMs) - EPSILON); // One `Math.pow` over the whole exponent, so the result is rounded once. Multiplying the // ratio between neighbouring boundaries (2^(1/5) ≈ 1.1487) by itself instead accumulates // the rounding of every step, reaching 512.0000000000018 by boundary 45. return Math.floor(Math.pow(2, boundaryNumber / BOUNDARIES_PER_DOUBLING)); } /** The bucket each exact threshold splits — 500 ms splits the one ending at 512 ms. */ var EXACT_THRESHOLD_BUCKETS = EXACT_THRESHOLDS_MS.map(function (thresholdMs) { return { thresholdMs: thresholdMs, bucketMs: firstBoundaryAtOrAbove(thresholdMs) }; }); /** * The bucket a latency belongs to, identified by the bucket's upper boundary in whole * milliseconds — which is also its key in the reported buckets. * * Every latency gets a bucket, however slow: the boundaries continue upwards, so there is no * overflow bucket. Expects the whole-millisecond durations Event Timing reports; boundaries * are floored, so a fractional latency can land in a bucket whose key reads up to a * millisecond below it. Non-finite latencies never reach here — `InteractionTracker` drops * them as it reads the entry. */ function bucketKeyForMs(latencyMs) { if (latencyMs <= REPORTING_THRESHOLD_MS) { return REPORTING_THRESHOLD_MS; } if (latencyMs <= EVENLY_SPACED_MAX_MS) { // How many 8 ms steps above the threshold the latency is, rounded up: 17 ms is 0.125 // steps up and lands on the boundary one step up, 24 ms. Rounding up is what keeps the // set of keys fixed when a latency is not a multiple of 8 ms. // // No EPSILON here, unlike the branch below: subtracting whole numbers gives a whole // number, and dividing by a power of two shifts a binary float's exponent without // touching its digits, so a latency on a boundary cannot come out just above a whole // number of steps. var steps = Math.ceil((latencyMs - REPORTING_THRESHOLD_MS) / EVENLY_SPACED_STEP_MS); return REPORTING_THRESHOLD_MS + steps * EVENLY_SPACED_STEP_MS; } var boundary = firstBoundaryAtOrAbove(latencyMs); // A latency in the lower part of a split bucket is reported as the threshold itself, so the // threshold is counted exactly. var _iterator = _createForOfIteratorHelper(EXACT_THRESHOLD_BUCKETS), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var _step$value = _step.value, thresholdMs = _step$value.thresholdMs, bucketMs = _step$value.bucketMs; if (boundary === bucketMs && latencyMs <= thresholdMs) { return thresholdMs; } } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } return boundary; }