From cd560dc372ddc30d1327246ee2d28cbc478480a7 Mon Sep 17 00:00:00 2001 From: Barry Pollard Date: Mon, 12 Dec 2022 23:17:07 +0000 Subject: [PATCH] Add prettier (#288) * Add prettier * Align formats * More formatting * Add GitHub Action * Add Husky * Add JSON * Fix workflow directory * Fix workflow * Fix workflow * Test bad format * Revert test change * Update selenium-standalone to allow tests to run * Update README.md Co-authored-by: Philip Walton * Review feedback * Set quoteProps to preserve * Revert CoC * Try link-staged * Try link staged * Revert lint-staged * Add git add * Test commit * Switch to lint-staged Co-authored-by: Philip Walton --- .editorconfig | 16 + .eslintrc | 28 +- .github/workflows/lint.yml | 20 + .gitignore | 3 - .husky/pre-commit | 4 + README.md | 130 +- docs/code-of-conduct.md | 14 +- package-lock.json | 1255 ++++++++++++++++- package.json | 17 +- rollup.config.js | 21 +- src/attribution/onCLS.ts | 33 +- src/attribution/onFCP.ts | 25 +- src/attribution/onFID.ts | 25 +- src/attribution/onINP.ts | 33 +- src/attribution/onLCP.ts | 37 +- src/attribution/onTTFB.ts | 37 +- src/lib/bfcache.ts | 16 +- src/lib/bindReporter.ts | 8 +- src/lib/getActivationStart.ts | 3 +- src/lib/getLoadState.ts | 16 +- src/lib/getNavigationEntry.ts | 55 +- src/lib/getSelector.ts | 19 +- src/lib/getVisibilityWatcher.ts | 15 +- src/lib/initMetric.ts | 7 +- src/lib/observe.ts | 21 +- src/lib/onHidden.ts | 4 +- src/lib/polyfills/firstInputPolyfill.ts | 56 +- .../polyfills/getFirstHiddenTimePolyfill.ts | 5 +- src/lib/polyfills/interactionCountPolyfill.ts | 10 +- src/lib/whenActivated.ts | 3 +- src/onCLS.ts | 21 +- src/onFCP.ts | 12 +- src/onFID.ts | 40 +- src/onINP.ts | 59 +- src/onLCP.ts | 17 +- src/onTTFB.ts | 15 +- src/polyfill.ts | 5 +- src/types.ts | 11 +- src/types/base.ts | 27 +- src/types/cls.ts | 3 +- src/types/fcp.ts | 7 +- src/types/fid.ts | 3 +- src/types/inp.ts | 1 - src/types/lcp.ts | 5 +- src/types/polyfills.ts | 20 +- src/types/ttfb.ts | 3 +- test/e2e/onCLS-test.js | 81 +- test/e2e/onFCP-test.js | 63 +- test/e2e/onFID-test.js | 32 +- test/e2e/onINP-test.js | 58 +- test/e2e/onLCP-test.js | 134 +- test/e2e/onTTFB-test.js | 109 +- test/server.js | 5 +- test/tsconfig.json | 4 +- test/utils/beacons.js | 3 +- test/utils/browserSupportsEntry.js | 15 +- test/utils/domReadyState.js | 1 - test/utils/imagesPainted.js | 6 +- test/utils/nextFrame.js | 1 - test/utils/stubForwardBack.js | 1 - test/utils/stubVisibilityChange.js | 1 - tsconfig.json | 4 +- wdio.conf.cjs | 14 +- 63 files changed, 2124 insertions(+), 593 deletions(-) create mode 100644 .editorconfig create mode 100644 .github/workflows/lint.yml create mode 100755 .husky/pre-commit diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..f0f92073 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +# editorconfig.org +# For Visual Studio code use this extension to enforce below rules: +# https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig +# Other IDEs maye have built in editorconfig support, or their own extensions +# +# The similar .ecrc file is used by an editorconfig GitHub action to catch those +# that don't use this. +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/.eslintrc b/.eslintrc index 6ccc10c1..ff8ab59e 100644 --- a/.eslintrc +++ b/.eslintrc @@ -29,11 +29,24 @@ "eslint:recommended", "google" ], "rules": { + "comma-dangle": ["error", { + "arrays": "always-multiline", + "objects": "always-multiline", + "imports": "always-multiline", + "exports": "always-multiline", + "functions": "never" + }], + "indent": ["error", 2], "no-invalid-this": "off", "max-len": [2, { "ignorePattern": "^\\s*import|= require\\(|^\\s*it\\(|^\\s*describe\\(", "ignoreUrls": true }], + "space-before-function-paren": ["error", { + "anonymous": "always", + "named": "never", + "asyncArrow": "always" + }] } }, { @@ -49,11 +62,24 @@ "@typescript-eslint/explicit-module-boundary-types": "off", "@typescript-eslint/ban-ts-comment": "off", "@typescript-eslint/camelcase": "off", + "comma-dangle": ["error", { + "arrays": "always-multiline", + "objects": "always-multiline", + "imports": "always-multiline", + "exports": "always-multiline", + "functions": "never" + }], + "indent": ["error", 2], "node/no-missing-import": "off", "node/no-unsupported-features/es-syntax": "off", "node/no-missing-require": "off", "node/shebang": "off", - "no-dupe-class-members": "off" + "no-dupe-class-members": "off", + "space-before-function-paren": ["error", { + "anonymous": "always", + "named": "never", + "asyncArrow": "always" + }] }, "parserOptions": { "ecmaVersion": 2018, diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000..4fa8f173 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,20 @@ +name: Lint Code Base +on: + pull_request: + push: + branches: + - main + workflow_dispatch: +jobs: + lint: + name: Lint Code Base + runs-on: ubuntu-20.04 + steps: + - name: Checkout Code + uses: actions/checkout@v3 + - name: NPM install + run: npm install + - name: Run Prettier + run: npm run format:check + - name: Run ESlint + run: npm run lint diff --git a/.gitignore b/.gitignore index 76d05e34..8df20a2c 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,3 @@ tsconfig.tsbuildinfo # Dist files dist - -# Husky files -.husky diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 00000000..a4fc3255 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +npm exec lint-staged diff --git a/README.md b/README.md index db258005..f9dc4f09 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,7 @@ Then, inline the code from `dist/polyfill.js` into the `` of your pages. T ``` -It's important that the code is inlined directly into the HTML. *Do not link to an external script file, as that will negatively affect performance: +It's important that the code is inlined directly into the HTML. _Do not link to an external script file, as that will negatively affect performance:_ ```html @@ -174,18 +174,18 @@ _**Important!** The [unpkg.com](https://unpkg.com) CDN is shown here for example ```html ``` @@ -194,7 +194,11 @@ _**Important!** The [unpkg.com](https://unpkg.com) CDN is shown here for example ```html ``` @@ -306,7 +311,7 @@ function sendToAnalytics(metric) { // Use `navigator.sendBeacon()` if available, falling back to `fetch()`. (navigator.sendBeacon && navigator.sendBeacon('/analytics', body)) || - fetch('/analytics', {body, method: 'POST', keepalive: true}); + fetch('/analytics', {body, method: 'POST', keepalive: true}); } onCLS(sendToAnalytics); @@ -451,7 +456,7 @@ function sendToGoogleAnalytics({name, delta, value, id, attribution}) { metric_id: id, // Needed to aggregate events. metric_value: value, // Optional. metric_delta: delta, // Optional. - } + }; switch (name) { case 'CLS': @@ -503,7 +508,7 @@ function flushQueue() { // Use `navigator.sendBeacon()` if available, falling back to `fetch()`. (navigator.sendBeacon && navigator.sendBeacon('/analytics', body)) || - fetch('/analytics', {body, method: 'POST', keepalive: true}); + fetch('/analytics', {body, method: 'POST', keepalive: true}); queue.clear(); } @@ -691,7 +696,12 @@ interface Metric { * 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 | LayoutShift | FirstInputPolyfillEntry | NavigationTimingPolyfillEntry)[]; + entries: ( + | PerformanceEntry + | LayoutShift + | FirstInputPolyfillEntry + | NavigationTimingPolyfillEntry + )[]; /** * The type of navigation. @@ -704,7 +714,13 @@ interface Metric { * - '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'; + navigationType: + | 'navigate' + | 'reload' + | 'back-forward' + | 'back-forward-cache' + | 'prerender' + | 'restore'; } ``` @@ -728,7 +744,7 @@ interface MetricWithAttribution extends Metric { * can be sent along with the metric value for the current page visit in * order to help identify issues happening to real-users in the field. */ - attribution: {[key: string]: unknown}; + attribution: {[key: string]: unknown}; } ``` @@ -787,7 +803,11 @@ The `LoadState` type is used in several of the metric [attribution objects](#att * - `complete`: the document and all of its sub-resources have finished * loading. This is equivalent to the corresponding `readyState` value. */ -type LoadState = 'loading' | 'dom-interactive' | 'dom-content-loaded' | 'complete'; +type LoadState = + | 'loading' + | 'dom-interactive' + | 'dom-content-loaded' + | 'complete'; ``` #### `FirstInputPolyfillEntry` @@ -795,7 +815,10 @@ type LoadState = 'loading' | 'dom-interactive' | 'dom-content-loaded' | 'complet If using the "base+polyfill" build (and if the browser doesn't natively support the Event Timing API), the `metric.entries` reported by `onFID()` will contain an object that polyfills the `PerformanceEventTiming` entry: ```ts -type FirstInputPolyfillEntry = Omit +type FirstInputPolyfillEntry = Omit< + PerformanceEventTiming, + 'processingEnd' | 'toJSON' +>; ``` #### `FirstInputPolyfillCallback` @@ -811,11 +834,18 @@ interface FirstInputPolyfillCallback { If using the "base+polyfill" build (and if the browser doesn't support the [Navigation Timing API Level 2](https://www.w3.org/TR/navigation-timing-2/) interface), the `metric.entries` reported by `onTTFB()` will contain an object that polyfills the `PerformanceNavigationTiming` entry using timings from the legacy `performance.timing` interface: ```ts -type NavigationTimingPolyfillEntry = Omit & { +type NavigationTimingPolyfillEntry = Omit< + PerformanceNavigationTiming, + | 'initiatorType' + | 'nextHopProtocol' + | 'redirectCount' + | 'transferSize' + | 'encodedBodySize' + | 'decodedBodySize' + | 'type' +> & { type: PerformanceNavigationTiming['type']; -} +}; ``` #### `WebVitalsGlobal` @@ -835,7 +865,7 @@ interface WebVitalsGlobal { #### `onCLS()` ```ts -type onCLS = (callback: CLSReportCallback, opts?: ReportOpts) => void +type onCLS = (callback: CLSReportCallback, opts?: ReportOpts) => void; ``` 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](https://heycam.github.io/webidl/#idl-double) (corresponding to a [layout shift score](https://web.dev/cls/#layout-shift-score)). @@ -847,7 +877,7 @@ _**Important:** CLS should be continually monitored for changes throughout the e #### `onFCP()` ```ts -type onFCP = (callback: FCPReportCallback, opts?: ReportOpts) => void +type onFCP = (callback: FCPReportCallback, opts?: ReportOpts) => void; ``` Calculates the [FCP](https://web.dev/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`](https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp). @@ -855,7 +885,7 @@ Calculates the [FCP](https://web.dev/fcp/) value for the current page and calls #### `onFID()` ```ts -type onFID = (callback: FIDReportCallback, opts?: ReportOpts) => void +type onFID = (callback: FIDReportCallback, opts?: ReportOpts) => void; ``` 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`](https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp). @@ -865,10 +895,10 @@ _**Important:** since FID is only reported after the user interacts with the pag #### `onINP()` ```ts -type onINP = (callback: INPReportCallback, opts?: ReportOpts) => void +type onINP = (callback: INPReportCallback, opts?: ReportOpts) => void; ``` -Calculates the [INP](https://web.dev/responsiveness/) 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`](https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp). +Calculates the [INP](https://web.dev/responsiveness/) 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`](https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp). A custom `durationThreshold` [configuration option](#reportopts) 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/inp/#what-is-a-good-inp-score) threshold). @@ -879,7 +909,7 @@ _**Important:** INP should be continually monitored for changes throughout the e #### `onLCP()` ```ts -type onLCP = (callback: LCPReportCallback, opts?: ReportOpts) => void +type onLCP = (callback: LCPReportCallback, opts?: ReportOpts) => void; ``` 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`](https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp). @@ -889,7 +919,7 @@ If the `reportAllChanges` [configuration option](#reportopts) is set to `true`, #### `onTTFB()` ```ts -type onTTFB = (callback: TTFBReportCallback, opts?: ReportOpts) => void +type onTTFB = (callback: TTFBReportCallback, opts?: ReportOpts) => void; ``` Calculates the [TTFB](https://web.dev/time-to-first-byte/) 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`](https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp). @@ -977,11 +1007,11 @@ interface FCPAttribution { * `LoadState` for details). Ideally, documents can paint before they finish * loading (e.g. the `loading` or `dom-interactive` phases). */ - loadState: LoadState, + loadState: LoadState; /** * The `PerformancePaintTiming` entry corresponding to FCP. */ - fcpEntry?: PerformancePaintTiming, + fcpEntry?: PerformancePaintTiming; /** * The `navigation` entry of the current page, which is useful for diagnosing * general page load issues. @@ -1012,7 +1042,7 @@ interface FIDAttribution { * The `PerformanceEventTiming` entry corresponding to FID (or the * polyfill entry in browsers that don't support Event Timing). */ - eventEntry: PerformanceEventTiming | FirstInputPolyfillEntry, + eventEntry: PerformanceEventTiming | FirstInputPolyfillEntry; /** * The loading state of the document at the time when the first interaction * occurred (see `LoadState` for details). If the first interaction occurred @@ -1063,12 +1093,12 @@ interface LCPAttribution { /** * The element corresponding to the largest contentful paint for the page. */ - element?: string, + 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, + 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 diff --git a/docs/code-of-conduct.md b/docs/code-of-conduct.md index f8b12cb5..0aa885b2 100644 --- a/docs/code-of-conduct.md +++ b/docs/code-of-conduct.md @@ -14,17 +14,17 @@ We value diverse opinions, but we value respectful behavior more. Respectful behavior includes: -* Being considerate, kind, constructive, and helpful. -* Not engaging in demeaning, discriminatory, harassing, hateful, sexualized, or +- Being considerate, kind, constructive, and helpful. +- Not engaging in demeaning, discriminatory, harassing, hateful, sexualized, or physically threatening behavior, speech, and imagery. -* Not engaging in unwanted physical contact. +- Not engaging in unwanted physical contact. Some Google open source projects [may adopt][] an explicit project code of conduct, which may have additional detailed expectations for participants. Most of those projects will use our [modified Contributor Covenant][]. [may adopt]: https://opensource.google/docs/releasing/preparing/#conduct -[modified Contributor Covenant]: https://opensource.google/docs/releasing/template/CODE_OF_CONDUCT/ +[modified contributor covenant]: https://opensource.google/docs/releasing/template/CODE_OF_CONDUCT/ ## Resolve peacefully @@ -57,7 +57,7 @@ report supplied to the accused. In potentially harmful situations, such as ongoing harassment or threats to anyone's safety, we may take action without notice. -*This document was adapted from the [IndieWeb Code of Conduct][] and can also -be found at .* +_This document was adapted from the [IndieWeb Code of Conduct][] and can also +be found at ._ -[IndieWeb Code of Conduct]: https://indieweb.org/code-of-conduct +[indieweb code of conduct]: https://indieweb.org/code-of-conduct diff --git a/package-lock.json b/package-lock.json index bc338996..bc177b5f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "@wdio/cli": "^7.25.1", "@wdio/local-runner": "^7.25.1", "@wdio/mocha-framework": "^7.25.1", - "@wdio/selenium-standalone-service": "^7.25.1", + "@wdio/selenium-standalone-service": "^7.26.0", "@wdio/spec-reporter": "^7.25.1", "body-parser": "^1.20.0", "chromedriver": "^107.0.3", @@ -26,11 +26,14 @@ "express": "^4.18.1", "fs-extra": "^10.1.0", "husky": "^8.0.1", + "lint-staged": "^13.0.3", "npm-run-all": "^4.1.5", "nunjucks": "^3.2.3", + "prettier": "^2.8.0", "rollup": "^2.79.1", "rollup-plugin-babel": "^4.4.0", "rollup-plugin-terser": "^7.0.2", + "selenium-standalone": "^8.2.3", "typescript": "^4.8.4", "wdio-chromedriver-service": "^8.0.0" } @@ -2818,17 +2821,17 @@ } }, "node_modules/@wdio/selenium-standalone-service": { - "version": "7.25.1", - "resolved": "https://registry.npmjs.org/@wdio/selenium-standalone-service/-/selenium-standalone-service-7.25.1.tgz", - "integrity": "sha512-TRD4hAxdHuZ0z414eDayE6q2gEmyAg7YdMrF+CJHWbjZKhJG4cqTSpV04zgMfQmTov5Y2+WtasdlGnqV5AXfMg==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@wdio/selenium-standalone-service/-/selenium-standalone-service-7.26.0.tgz", + "integrity": "sha512-bP79aBzRBvgNCaJihLaQT/Qxa604o8UsAv9Ce2tARlVb4jFjpGH0w/xkuxUTqSpeQy/Sj+3hLExhNh1YgLe/Bg==", "dev": true, "dependencies": { "@types/fs-extra": "^9.0.1", "@types/node": "^18.0.0", "@types/selenium-standalone": "^7.0.0", - "@wdio/config": "7.25.1", - "@wdio/logger": "7.19.0", - "@wdio/types": "7.25.1", + "@wdio/config": "7.26.0", + "@wdio/logger": "7.26.0", + "@wdio/types": "7.26.0", "fs-extra": "^10.0.0", "selenium-standalone": "^8.0.3" }, @@ -2839,6 +2842,133 @@ "@wdio/cli": "^7.0.0" } }, + "node_modules/@wdio/selenium-standalone-service/node_modules/@wdio/config": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-7.26.0.tgz", + "integrity": "sha512-GO6kFGgFrx2Hiq+Ww6V9I7cZfShPjfPVhPy3uXnKN2B4FilX8ilLAp5cIFuMuHPeOQq0crYX9cnLYXka6dCGgg==", + "dev": true, + "dependencies": { + "@wdio/logger": "7.26.0", + "@wdio/types": "7.26.0", + "@wdio/utils": "7.26.0", + "deepmerge": "^4.0.0", + "glob": "^8.0.3" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/selenium-standalone-service/node_modules/@wdio/logger": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.26.0.tgz", + "integrity": "sha512-kQj9s5JudAG9qB+zAAcYGPHVfATl2oqKgqj47yjehOQ1zzG33xmtL1ArFbQKWhDG32y1A8sN6b0pIqBEIwgg8Q==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/selenium-standalone-service/node_modules/@wdio/types": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.26.0.tgz", + "integrity": "sha512-mOTfWAGQ+iT58iaZhJMwlUkdEn3XEWE4jthysMLXFnSuZ2eaODVAiK31SmlS/eUqgSIaupeGqYUrtCuSNbLefg==", + "dev": true, + "dependencies": { + "@types/node": "^18.0.0", + "got": "^11.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "typescript": "^4.6.2" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@wdio/selenium-standalone-service/node_modules/@wdio/utils": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-7.26.0.tgz", + "integrity": "sha512-pVq2MPXZAYLkKGKIIHktHejnHqg4TYKoNYSi2EDv+I3GlT8VZKXHazKhci82ov0tD+GdF27+s4DWNDCfGYfBdQ==", + "dev": true, + "dependencies": { + "@wdio/logger": "7.26.0", + "@wdio/types": "7.26.0", + "p-iteration": "^1.1.8" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/selenium-standalone-service/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@wdio/selenium-standalone-service/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@wdio/selenium-standalone-service/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@wdio/selenium-standalone-service/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@wdio/selenium-standalone-service/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@wdio/spec-reporter": { "version": "7.25.1", "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-7.25.1.tgz", @@ -3007,6 +3137,19 @@ "node": ">= 6.0.0" } }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -3200,6 +3343,15 @@ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "dev": true }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/async": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", @@ -3782,6 +3934,15 @@ "integrity": "sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg==", "dev": true }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -3806,6 +3967,72 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cli-truncate": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", + "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", + "dev": true, + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/cli-truncate/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/cli-width": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", @@ -3862,6 +4089,12 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, + "node_modules/colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", + "dev": true + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -4272,6 +4505,12 @@ "node": ">=6.0.0" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, "node_modules/easy-table": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.2.0.tgz", @@ -4775,6 +5014,68 @@ "node": ">= 0.6" } }, + "node_modules/execa": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-6.1.0.tgz", + "integrity": "sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^3.0.1", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/execa/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/execa/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/expect": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/expect/-/expect-28.1.3.tgz", @@ -5627,6 +5928,15 @@ "node": ">= 6" } }, + "node_modules/human-signals": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-3.0.1.tgz", + "integrity": "sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==", + "dev": true, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/husky": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.1.tgz", @@ -5708,6 +6018,15 @@ "node": ">=0.8.19" } }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -6064,6 +6383,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-string": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", @@ -6773,37 +7104,187 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, - "node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "node_modules/lilconfig": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", + "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, "engines": { - "node": ">=4" + "node": ">=10" } }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "node_modules/lint-staged": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.1.0.tgz", + "integrity": "sha512-pn/sR8IrcF/T0vpWLilih8jmVouMlxqXxKuAojmbiGX5n/gDnz+abdPptlj0vYnbfE0SQNl3CY/HwtM0+yfOVQ==", "dev": true, "dependencies": { - "p-locate": "^5.0.0" + "cli-truncate": "^3.1.0", + "colorette": "^2.0.19", + "commander": "^9.4.1", + "debug": "^4.3.4", + "execa": "^6.1.0", + "lilconfig": "2.0.6", + "listr2": "^5.0.5", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-inspect": "^1.12.2", + "pidtree": "^0.6.0", + "string-argv": "^0.3.1", + "yaml": "^2.1.3" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" }, "engines": { - "node": ">=10" + "node": "^14.13.1 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/lint-staged" } }, - "node_modules/lodash": { + "node_modules/lint-staged/node_modules/commander": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.1.tgz", + "integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==", + "dev": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/lint-staged/node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/listr2": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-5.0.6.tgz", + "integrity": "sha512-u60KxKBy1BR2uLJNTWNptzWQ1ob/gjMzIJPZffAENzpZqbMZ/5PrXXOomDcevIS/+IB7s1mmCEtSlT2qHWMqag==", + "dev": true, + "dependencies": { + "cli-truncate": "^2.1.0", + "colorette": "^2.0.19", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rfdc": "^1.3.0", + "rxjs": "^7.5.7", + "through": "^2.3.8", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": "^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "enquirer": ">= 2.3.0 < 3" + }, + "peerDependenciesMeta": { + "enquirer": { + "optional": true + } + } + }, + "node_modules/listr2/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/listr2/node_modules/cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "dependencies": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/listr2/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/listr2/node_modules/slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", @@ -6964,6 +7445,88 @@ "node": ">=8" } }, + "node_modules/log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-update/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-update/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/loglevel": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.0.tgz", @@ -7520,6 +8083,33 @@ "which": "bin/which" } }, + "node_modules/npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/nunjucks": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.3.tgz", @@ -7775,6 +8365,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -8014,6 +8619,21 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.0.tgz", + "integrity": "sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/pretty-format": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", @@ -8642,6 +9262,12 @@ "node": ">=0.10.0" } }, + "node_modules/rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "dev": true + }, "node_modules/rgb2hex": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/rgb2hex/-/rgb2hex-0.2.5.tgz", @@ -8785,9 +9411,9 @@ } }, "node_modules/rxjs": { - "version": "7.5.6", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.6.tgz", - "integrity": "sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.6.0.tgz", + "integrity": "sha512-DDa7d8TFNUalGC9VqXvQ1euWNN7sc63TrUCuM9J998+ViviahMIjKSOU7rfcgFOF+FCD71BhDRv4hrFz+ImDLQ==", "dev": true, "dependencies": { "tslib": "^2.1.0" @@ -8806,9 +9432,9 @@ "dev": true }, "node_modules/selenium-standalone": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/selenium-standalone/-/selenium-standalone-8.2.0.tgz", - "integrity": "sha512-gRFJm2A91sL0/4PavIsfTVNjyqNjk+zbdJg/zAYgTMjuoWJv+BlYJh+1UbEtyjt4YvZACYif1DFAzFjQapqiOA==", + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/selenium-standalone/-/selenium-standalone-8.2.3.tgz", + "integrity": "sha512-WNiVh6uNwgXjM04NzsDgm5hWJGSmAI7fU5vCMWmb9AslQaoFDhvFgLE8wfp+nhdizqCW1bDTCZl3BnNHdaZG8g==", "dev": true, "dependencies": { "commander": "^9.0.0", @@ -9010,6 +9636,46 @@ "node": ">=8" } }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -9144,6 +9810,15 @@ } ] }, + "node_modules/string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -9224,6 +9899,18 @@ "node": ">=4" } }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -10018,6 +10705,15 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "node_modules/yaml": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.3.tgz", + "integrity": "sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, "node_modules/yargs": { "version": "17.5.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", @@ -12223,19 +12919,110 @@ } }, "@wdio/selenium-standalone-service": { - "version": "7.25.1", - "resolved": "https://registry.npmjs.org/@wdio/selenium-standalone-service/-/selenium-standalone-service-7.25.1.tgz", - "integrity": "sha512-TRD4hAxdHuZ0z414eDayE6q2gEmyAg7YdMrF+CJHWbjZKhJG4cqTSpV04zgMfQmTov5Y2+WtasdlGnqV5AXfMg==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@wdio/selenium-standalone-service/-/selenium-standalone-service-7.26.0.tgz", + "integrity": "sha512-bP79aBzRBvgNCaJihLaQT/Qxa604o8UsAv9Ce2tARlVb4jFjpGH0w/xkuxUTqSpeQy/Sj+3hLExhNh1YgLe/Bg==", "dev": true, "requires": { "@types/fs-extra": "^9.0.1", "@types/node": "^18.0.0", "@types/selenium-standalone": "^7.0.0", - "@wdio/config": "7.25.1", - "@wdio/logger": "7.19.0", - "@wdio/types": "7.25.1", + "@wdio/config": "7.26.0", + "@wdio/logger": "7.26.0", + "@wdio/types": "7.26.0", "fs-extra": "^10.0.0", "selenium-standalone": "^8.0.3" + }, + "dependencies": { + "@wdio/config": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-7.26.0.tgz", + "integrity": "sha512-GO6kFGgFrx2Hiq+Ww6V9I7cZfShPjfPVhPy3uXnKN2B4FilX8ilLAp5cIFuMuHPeOQq0crYX9cnLYXka6dCGgg==", + "dev": true, + "requires": { + "@wdio/logger": "7.26.0", + "@wdio/types": "7.26.0", + "@wdio/utils": "7.26.0", + "deepmerge": "^4.0.0", + "glob": "^8.0.3" + } + }, + "@wdio/logger": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.26.0.tgz", + "integrity": "sha512-kQj9s5JudAG9qB+zAAcYGPHVfATl2oqKgqj47yjehOQ1zzG33xmtL1ArFbQKWhDG32y1A8sN6b0pIqBEIwgg8Q==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + } + }, + "@wdio/types": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.26.0.tgz", + "integrity": "sha512-mOTfWAGQ+iT58iaZhJMwlUkdEn3XEWE4jthysMLXFnSuZ2eaODVAiK31SmlS/eUqgSIaupeGqYUrtCuSNbLefg==", + "dev": true, + "requires": { + "@types/node": "^18.0.0", + "got": "^11.8.1" + } + }, + "@wdio/utils": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-7.26.0.tgz", + "integrity": "sha512-pVq2MPXZAYLkKGKIIHktHejnHqg4TYKoNYSi2EDv+I3GlT8VZKXHazKhci82ov0tD+GdF27+s4DWNDCfGYfBdQ==", + "dev": true, + "requires": { + "@wdio/logger": "7.26.0", + "@wdio/types": "7.26.0", + "p-iteration": "^1.1.8" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "@wdio/spec-reporter": { @@ -12356,6 +13143,16 @@ "debug": "4" } }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -12511,6 +13308,12 @@ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "dev": true }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, "async": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", @@ -12937,6 +13740,12 @@ "integrity": "sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg==", "dev": true }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, "cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -12952,6 +13761,50 @@ "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", "dev": true }, + "cli-truncate": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", + "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", + "dev": true, + "requires": { + "slice-ansi": "^5.0.0", + "string-width": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + } + } + }, "cli-width": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", @@ -12999,6 +13852,12 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, + "colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", + "dev": true + }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -13299,6 +14158,12 @@ "esutils": "^2.0.2" } }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, "easy-table": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.2.0.tgz", @@ -13677,6 +14542,46 @@ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "dev": true }, + "execa": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-6.1.0.tgz", + "integrity": "sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^3.0.1", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + }, + "dependencies": { + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, + "mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true + }, + "onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "requires": { + "mimic-fn": "^4.0.0" + } + } + } + }, "expect": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/expect/-/expect-28.1.3.tgz", @@ -14330,6 +15235,12 @@ "debug": "4" } }, + "human-signals": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-3.0.1.tgz", + "integrity": "sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==", + "dev": true + }, "husky": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.1.tgz", @@ -14373,6 +15284,12 @@ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -14623,6 +15540,12 @@ "call-bind": "^1.0.2" } }, + "is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true + }, "is-string": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", @@ -15175,6 +16098,110 @@ } } }, + "lilconfig": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", + "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", + "dev": true + }, + "lint-staged": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.1.0.tgz", + "integrity": "sha512-pn/sR8IrcF/T0vpWLilih8jmVouMlxqXxKuAojmbiGX5n/gDnz+abdPptlj0vYnbfE0SQNl3CY/HwtM0+yfOVQ==", + "dev": true, + "requires": { + "cli-truncate": "^3.1.0", + "colorette": "^2.0.19", + "commander": "^9.4.1", + "debug": "^4.3.4", + "execa": "^6.1.0", + "lilconfig": "2.0.6", + "listr2": "^5.0.5", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-inspect": "^1.12.2", + "pidtree": "^0.6.0", + "string-argv": "^0.3.1", + "yaml": "^2.1.3" + }, + "dependencies": { + "commander": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.1.tgz", + "integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==", + "dev": true + }, + "pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true + } + } + }, + "listr2": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-5.0.6.tgz", + "integrity": "sha512-u60KxKBy1BR2uLJNTWNptzWQ1ob/gjMzIJPZffAENzpZqbMZ/5PrXXOomDcevIS/+IB7s1mmCEtSlT2qHWMqag==", + "dev": true, + "requires": { + "cli-truncate": "^2.1.0", + "colorette": "^2.0.19", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rfdc": "^1.3.0", + "rxjs": "^7.5.7", + "through": "^2.3.8", + "wrap-ansi": "^7.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "requires": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + } + } + }, "load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", @@ -15335,6 +16362,66 @@ } } }, + "log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, + "requires": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + } + } + }, "loglevel": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.0.tgz", @@ -15750,6 +16837,23 @@ } } }, + "npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "requires": { + "path-key": "^4.0.0" + }, + "dependencies": { + "path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true + } + } + }, "nunjucks": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.3.tgz", @@ -15924,6 +17028,15 @@ "p-limit": "^3.0.2" } }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -16096,6 +17209,12 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, + "prettier": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.0.tgz", + "integrity": "sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA==", + "dev": true + }, "pretty-format": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", @@ -16588,6 +17707,12 @@ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true }, + "rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "dev": true + }, "rgb2hex": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/rgb2hex/-/rgb2hex-0.2.5.tgz", @@ -16694,9 +17819,9 @@ } }, "rxjs": { - "version": "7.5.6", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.6.tgz", - "integrity": "sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.6.0.tgz", + "integrity": "sha512-DDa7d8TFNUalGC9VqXvQ1euWNN7sc63TrUCuM9J998+ViviahMIjKSOU7rfcgFOF+FCD71BhDRv4hrFz+ImDLQ==", "dev": true, "requires": { "tslib": "^2.1.0" @@ -16715,9 +17840,9 @@ "dev": true }, "selenium-standalone": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/selenium-standalone/-/selenium-standalone-8.2.0.tgz", - "integrity": "sha512-gRFJm2A91sL0/4PavIsfTVNjyqNjk+zbdJg/zAYgTMjuoWJv+BlYJh+1UbEtyjt4YvZACYif1DFAzFjQapqiOA==", + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/selenium-standalone/-/selenium-standalone-8.2.3.tgz", + "integrity": "sha512-WNiVh6uNwgXjM04NzsDgm5hWJGSmAI7fU5vCMWmb9AslQaoFDhvFgLE8wfp+nhdizqCW1bDTCZl3BnNHdaZG8g==", "dev": true, "requires": { "commander": "^9.0.0", @@ -16884,6 +18009,30 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, + "slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true + } + } + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -16990,6 +18139,12 @@ } } }, + "string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true + }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -17049,6 +18204,12 @@ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true }, + "strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true + }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -17627,6 +18788,12 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "yaml": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.3.tgz", + "integrity": "sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg==", + "dev": true + }, "yargs": { "version": "17.5.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", diff --git a/package.json b/package.json index 96b236c0..ede3abb4 100644 --- a/package.json +++ b/package.json @@ -96,6 +96,8 @@ "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", @@ -142,6 +144,12 @@ "pre-commit": "npm run lint" } }, + "prettier": { + "arrowParens": "always", + "bracketSpacing": false, + "quoteProps": "preserve", + "singleQuote": true + }, "devDependencies": { "@babel/core": "^7.19.3", "@babel/preset-env": "^7.19.3", @@ -151,7 +159,7 @@ "@wdio/cli": "^7.25.1", "@wdio/local-runner": "^7.25.1", "@wdio/mocha-framework": "^7.25.1", - "@wdio/selenium-standalone-service": "^7.25.1", + "@wdio/selenium-standalone-service": "^7.26.0", "@wdio/spec-reporter": "^7.25.1", "body-parser": "^1.20.0", "chromedriver": "^107.0.3", @@ -160,12 +168,19 @@ "express": "^4.18.1", "fs-extra": "^10.1.0", "husky": "^8.0.1", + "lint-staged": "^13.0.3", "npm-run-all": "^4.1.5", "nunjucks": "^3.2.3", + "prettier": "^2.8.0", "rollup": "^2.79.1", "rollup-plugin-babel": "^4.4.0", "rollup-plugin-terser": "^7.0.2", + "selenium-standalone": "^8.2.3", "typescript": "^4.8.4", "wdio-chromedriver-service": "^8.0.0" + }, + "lint-staged": { + "**/*.{js,ts}": "eslint --fix --ignore-path .gitignore", + "**/*.{cjs,css,html,js,json,html,md,ts,yml,yaml}": "prettier --write --ignore-path .gitignore" } } diff --git a/rollup.config.js b/rollup.config.js index 087c6c80..1a85592c 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -20,11 +20,16 @@ import babel from 'rollup-plugin-babel'; const configurePlugins = ({module, polyfill = false}) => { return [ babel({ - presets: [['@babel/preset-env', { - targets: { - browsers: ['ie 11'], - }, - }]], + presets: [ + [ + '@babel/preset-env', + { + targets: { + browsers: ['ie 11'], + }, + }, + ], + ], }), terser({ module, @@ -36,9 +41,9 @@ const configurePlugins = ({module, polyfill = false}) => { 'window.__WEB_VITALS_POLYFILL__': polyfill, }, preventAssignment: true, - }) - ] -} + }), + ]; +}; const configs = [ { diff --git a/src/attribution/onCLS.ts b/src/attribution/onCLS.ts index 8dbcd53a..0e62a7d8 100644 --- a/src/attribution/onCLS.ts +++ b/src/attribution/onCLS.ts @@ -17,16 +17,21 @@ import {getLoadState} from '../lib/getLoadState.js'; import {getSelector} from '../lib/getSelector.js'; import {onCLS as unattributedOnCLS} from '../onCLS.js'; -import {CLSReportCallback, CLSReportCallbackWithAttribution, CLSMetric, CLSMetricWithAttribution, ReportOpts} from '../types.js'; - +import { + CLSReportCallback, + CLSReportCallbackWithAttribution, + CLSMetric, + CLSMetricWithAttribution, + ReportOpts, +} from '../types.js'; const getLargestLayoutShiftEntry = (entries: LayoutShift[]) => { - return entries.reduce((a, b) => a && a.value > b.value ? a : b); -} + return entries.reduce((a, b) => (a && a.value > b.value ? a : b)); +}; const getLargestLayoutShiftSource = (sources: LayoutShiftAttribution[]) => { return sources.find((s) => s.node && s.node.nodeType === 1) || sources[0]; -} +}; const attributeCLS = (metric: CLSMetric): void => { if (metric.entries.length) { @@ -48,7 +53,7 @@ const attributeCLS = (metric: CLSMetric): void => { } // Set an empty object if no other attribution has been set. (metric as CLSMetricWithAttribution).attribution = {}; -} +}; /** * Calculates the [CLS](https://web.dev/cls/) value for the current page and @@ -71,9 +76,15 @@ const attributeCLS = (metric: CLSMetric): void => { * hidden. As a result, the `callback` function might be called multiple times * during the same page load._ */ -export const onCLS = (onReport: CLSReportCallbackWithAttribution, opts?: ReportOpts) => { - unattributedOnCLS(((metric: CLSMetric) => { - attributeCLS(metric); - onReport(metric); - }) as CLSReportCallback, opts); +export const onCLS = ( + onReport: CLSReportCallbackWithAttribution, + opts?: ReportOpts +) => { + unattributedOnCLS( + ((metric: CLSMetric) => { + attributeCLS(metric); + onReport(metric); + }) as CLSReportCallback, + opts + ); }; diff --git a/src/attribution/onFCP.ts b/src/attribution/onFCP.ts index 408eeb42..55b7f3c9 100644 --- a/src/attribution/onFCP.ts +++ b/src/attribution/onFCP.ts @@ -18,8 +18,13 @@ 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'; -import {FCPMetric, FCPMetricWithAttribution, FCPReportCallback, FCPReportCallbackWithAttribution, ReportOpts} from '../types.js'; - +import { + FCPMetric, + FCPMetricWithAttribution, + FCPReportCallback, + FCPReportCallbackWithAttribution, + ReportOpts, +} from '../types.js'; const attributeFCP = (metric: FCPMetric): void => { if (metric.entries.length) { @@ -54,9 +59,15 @@ const attributeFCP = (metric: FCPMetric): void => { * relevant `paint` performance entry used to determine the value. The reported * value is a `DOMHighResTimeStamp`. */ -export const onFCP = (onReport: FCPReportCallbackWithAttribution, opts?: ReportOpts) => { - unattributedOnFCP(((metric: FCPMetricWithAttribution) => { - attributeFCP(metric); - onReport(metric); - }) as FCPReportCallback, opts); +export const onFCP = ( + onReport: FCPReportCallbackWithAttribution, + opts?: ReportOpts +) => { + unattributedOnFCP( + ((metric: FCPMetricWithAttribution) => { + attributeFCP(metric); + onReport(metric); + }) as FCPReportCallback, + opts + ); }; diff --git a/src/attribution/onFID.ts b/src/attribution/onFID.ts index 5f811936..029bb5c1 100644 --- a/src/attribution/onFID.ts +++ b/src/attribution/onFID.ts @@ -17,8 +17,13 @@ import {getLoadState} from '../lib/getLoadState.js'; import {getSelector} from '../lib/getSelector.js'; import {onFID as unattributedOnFID} from '../onFID.js'; -import {FIDMetric, FIDMetricWithAttribution, FIDReportCallback, FIDReportCallbackWithAttribution, ReportOpts} from '../types.js'; - +import { + FIDMetric, + FIDMetricWithAttribution, + FIDReportCallback, + FIDReportCallbackWithAttribution, + ReportOpts, +} from '../types.js'; const attributeFID = (metric: FIDMetric): void => { const fidEntry = metric.entries[0]; @@ -40,9 +45,15 @@ const attributeFID = (metric: FIDMetric): void => { * _**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: FIDReportCallbackWithAttribution, opts?: ReportOpts) => { - unattributedOnFID(((metric: FIDMetricWithAttribution) => { - attributeFID(metric); - onReport(metric); - }) as FIDReportCallback, opts); +export const onFID = ( + onReport: FIDReportCallbackWithAttribution, + opts?: ReportOpts +) => { + unattributedOnFID( + ((metric: FIDMetricWithAttribution) => { + attributeFID(metric); + onReport(metric); + }) as FIDReportCallback, + opts + ); }; diff --git a/src/attribution/onINP.ts b/src/attribution/onINP.ts index 9f6a8685..42555f12 100644 --- a/src/attribution/onINP.ts +++ b/src/attribution/onINP.ts @@ -17,15 +17,24 @@ import {getLoadState} from '../lib/getLoadState.js'; import {getSelector} from '../lib/getSelector.js'; import {onINP as unattributedOnINP} from '../onINP.js'; -import {INPMetric, INPMetricWithAttribution, INPReportCallback, INPReportCallbackWithAttribution, ReportOpts} from '../types.js'; - +import { + INPMetric, + INPMetricWithAttribution, + INPReportCallback, + INPReportCallbackWithAttribution, + ReportOpts, +} from '../types.js'; const attributeINP = (metric: INPMetric): void => { if (metric.entries.length) { const longestEntry = metric.entries.sort((a, b) => { // Sort by: 1) duration (DESC), then 2) processing time (DESC) - return b.duration - a.duration || (b.processingEnd - b.processingStart) - - (a.processingEnd - a.processingStart); + return ( + b.duration - a.duration || + b.processingEnd - + b.processingStart - + (a.processingEnd - a.processingStart) + ); })[0]; (metric as INPMetricWithAttribution).attribution = { @@ -68,9 +77,15 @@ const attributeINP = (metric: INPMetric): void => { * hidden. As a result, the `callback` function might be called multiple times * during the same page load._ */ -export const onINP = (onReport: INPReportCallbackWithAttribution, opts?: ReportOpts) => { - unattributedOnINP(((metric: INPMetricWithAttribution) => { - attributeINP(metric); - onReport(metric); - }) as INPReportCallback, opts); +export const onINP = ( + onReport: INPReportCallbackWithAttribution, + opts?: ReportOpts +) => { + unattributedOnINP( + ((metric: INPMetricWithAttribution) => { + attributeINP(metric); + onReport(metric); + }) as INPReportCallback, + opts + ); }; diff --git a/src/attribution/onLCP.ts b/src/attribution/onLCP.ts index 1a8e4128..fdbeb4eb 100644 --- a/src/attribution/onLCP.ts +++ b/src/attribution/onLCP.ts @@ -14,12 +14,17 @@ * limitations under the License. */ - import {getNavigationEntry} from '../lib/getNavigationEntry.js'; import {getSelector} from '../lib/getSelector.js'; import {onLCP as unattributedOnLCP} from '../onLCP.js'; -import {LCPAttribution, LCPMetric, LCPMetricWithAttribution, LCPReportCallback, LCPReportCallbackWithAttribution, ReportOpts} from '../types.js'; - +import { + LCPAttribution, + LCPMetric, + LCPMetricWithAttribution, + LCPReportCallback, + LCPReportCallbackWithAttribution, + ReportOpts, +} from '../types.js'; const attributeLCP = (metric: LCPMetric) => { if (metric.entries.length) { @@ -28,7 +33,9 @@ const attributeLCP = (metric: LCPMetric) => { if (navigationEntry) { const activationStart = navigationEntry.activationStart || 0; const lcpEntry = metric.entries[metric.entries.length - 1]; - const lcpResourceEntry = lcpEntry.url &&performance + const lcpResourceEntry = + lcpEntry.url && + performance .getEntriesByType('resource') .filter((e) => e.name === lcpEntry.url)[0]; @@ -37,8 +44,10 @@ const attributeLCP = (metric: LCPMetric) => { const lcpRequestStart = Math.max( ttfb, // Prefer `requestStart` (if TOA is set), otherwise use `startTime`. - lcpResourceEntry ? (lcpResourceEntry.requestStart || - lcpResourceEntry.startTime) - activationStart : 0 + lcpResourceEntry + ? (lcpResourceEntry.requestStart || lcpResourceEntry.startTime) - + activationStart + : 0 ); const lcpResponseEnd = Math.max( lcpRequestStart, @@ -91,9 +100,15 @@ const attributeLCP = (metric: LCPMetric) => { * performance entry is dispatched, or once the final value of the metric has * been determined. */ -export const onLCP = (onReport: LCPReportCallbackWithAttribution, opts?: ReportOpts) => { - unattributedOnLCP(((metric: LCPMetricWithAttribution) => { - attributeLCP(metric); - onReport(metric); - }) as LCPReportCallback, opts); +export const onLCP = ( + onReport: LCPReportCallbackWithAttribution, + opts?: ReportOpts +) => { + unattributedOnLCP( + ((metric: LCPMetricWithAttribution) => { + attributeLCP(metric); + onReport(metric); + }) as LCPReportCallback, + opts + ); }; diff --git a/src/attribution/onTTFB.ts b/src/attribution/onTTFB.ts index d7716bcc..d40009a5 100644 --- a/src/attribution/onTTFB.ts +++ b/src/attribution/onTTFB.ts @@ -15,8 +15,13 @@ */ import {onTTFB as unattributedOnTTFB} from '../onTTFB.js'; -import {TTFBMetric, TTFBMetricWithAttribution, TTFBReportCallback, TTFBReportCallbackWithAttribution, ReportOpts} from '../types.js'; - +import { + TTFBMetric, + TTFBMetricWithAttribution, + TTFBReportCallback, + TTFBReportCallbackWithAttribution, + ReportOpts, +} from '../types.js'; const attributeTTFB = (metric: TTFBMetric): void => { if (metric.entries.length) { @@ -24,11 +29,17 @@ const attributeTTFB = (metric: TTFBMetric): void => { const activationStart = navigationEntry.activationStart || 0; const dnsStart = Math.max( - navigationEntry.domainLookupStart - activationStart, 0); + navigationEntry.domainLookupStart - activationStart, + 0 + ); const connectStart = Math.max( - navigationEntry.connectStart - activationStart, 0); + navigationEntry.connectStart - activationStart, + 0 + ); const requestStart = Math.max( - navigationEntry.requestStart - activationStart, 0); + navigationEntry.requestStart - activationStart, + 0 + ); (metric as TTFBMetricWithAttribution).attribution = { waitingTime: dnsStart, @@ -63,9 +74,15 @@ const attributeTTFB = (metric: TTFBMetric): void => { * includes time spent on DNS lookup, connection negotiation, network latency, * and server processing time. */ -export const onTTFB = (onReport: TTFBReportCallbackWithAttribution, opts?: ReportOpts) => { - unattributedOnTTFB(((metric: TTFBMetricWithAttribution) => { - attributeTTFB(metric); - onReport(metric); - }) as TTFBReportCallback, opts); +export const onTTFB = ( + onReport: TTFBReportCallbackWithAttribution, + opts?: ReportOpts +) => { + unattributedOnTTFB( + ((metric: TTFBMetricWithAttribution) => { + attributeTTFB(metric); + onReport(metric); + }) as TTFBReportCallback, + opts + ); }; diff --git a/src/lib/bfcache.ts b/src/lib/bfcache.ts index 5908ff48..cf81e8a0 100644 --- a/src/lib/bfcache.ts +++ b/src/lib/bfcache.ts @@ -23,10 +23,14 @@ let bfcacheRestoreTime = -1; export const getBFCacheRestoreTime = () => bfcacheRestoreTime; export const onBFCacheRestore = (cb: onBFCacheRestoreCallback) => { - addEventListener('pageshow', (event) => { - if (event.persisted) { - bfcacheRestoreTime = event.timeStamp; - cb(event); - } - }, true); + addEventListener( + 'pageshow', + (event) => { + if (event.persisted) { + bfcacheRestoreTime = event.timeStamp; + cb(event); + } + }, + true + ); }; diff --git a/src/lib/bindReporter.ts b/src/lib/bindReporter.ts index e930f10d..d758055c 100644 --- a/src/lib/bindReporter.ts +++ b/src/lib/bindReporter.ts @@ -16,7 +16,6 @@ import {Metric, ReportCallback} from '../types.js'; - const getRating = (value: number, thresholds: number[]) => { if (value > thresholds[1]) { return 'poor'; @@ -27,12 +26,11 @@ const getRating = (value: number, thresholds: number[]) => { return 'good'; }; - export const bindReporter = ( callback: ReportCallback, metric: Metric, thresholds: number[], - reportAllChanges?: boolean, + reportAllChanges?: boolean ) => { let prevValue: number; let delta: number; @@ -53,5 +51,5 @@ export const bindReporter = ( } } } - } -} + }; +}; diff --git a/src/lib/getActivationStart.ts b/src/lib/getActivationStart.ts index 03b6446c..3991c0e9 100644 --- a/src/lib/getActivationStart.ts +++ b/src/lib/getActivationStart.ts @@ -16,8 +16,7 @@ import {getNavigationEntry} from './getNavigationEntry.js'; - export const getActivationStart = (): number => { const navEntry = getNavigationEntry(); - return navEntry && navEntry.activationStart || 0; + return (navEntry && navEntry.activationStart) || 0; }; diff --git a/src/lib/getLoadState.ts b/src/lib/getLoadState.ts index 39aa8738..788db0f4 100644 --- a/src/lib/getLoadState.ts +++ b/src/lib/getLoadState.ts @@ -17,7 +17,6 @@ import {getNavigationEntry} from './getNavigationEntry.js'; import {LoadState} from '../types.js'; - export const getLoadState = (timestamp: number): LoadState => { if (document.readyState === 'loading') { // If the `readyState` is 'loading' there's no need to look at timestamps @@ -26,16 +25,19 @@ export const getLoadState = (timestamp: number): LoadState => { } else { const navigationEntry = getNavigationEntry(); if (navigationEntry) { - if (timestamp < navigationEntry.domInteractive) { return 'loading'; - } else if (navigationEntry.domContentLoadedEventStart === 0 || - timestamp < navigationEntry.domContentLoadedEventStart) { + } 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) { + } 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'; @@ -46,4 +48,4 @@ export const getLoadState = (timestamp: number): LoadState => { // happy if the browser doesn't support the performance timeline, which // most likely means this code would never run anyway. return 'complete'; -} +}; diff --git a/src/lib/getNavigationEntry.ts b/src/lib/getNavigationEntry.ts index b56c81e7..5f8270ce 100644 --- a/src/lib/getNavigationEntry.ts +++ b/src/lib/getNavigationEntry.ts @@ -16,34 +16,45 @@ import {NavigationTimingPolyfillEntry} from '../types.js'; +const getNavigationEntryFromPerformanceTiming = + (): NavigationTimingPolyfillEntry => { + const timing = performance.timing; + const type = performance.navigation.type; -const getNavigationEntryFromPerformanceTiming = (): NavigationTimingPolyfillEntry => { - const timing = performance.timing; - const type = performance.navigation.type; + const navigationEntry: {[key: string]: number | string} = { + entryType: 'navigation', + startTime: 0, + type: type == 2 ? 'back_forward' : type === 1 ? 'reload' : 'navigate', + }; - const navigationEntry: {[key: string]: number | string} = { - entryType: 'navigation', - startTime: 0, - type: type == 2 ? 'back_forward' : (type === 1 ? 'reload' : 'navigate'), - }; - - for (const key in timing) { - if (key !== 'navigationStart' && key !== 'toJSON') { - navigationEntry[key] = Math.max( + for (const key in timing) { + if (key !== 'navigationStart' && key !== 'toJSON') { + navigationEntry[key] = Math.max( (timing[key as keyof PerformanceTiming] as number) - - timing.navigationStart, 0); + timing.navigationStart, + 0 + ); + } } - } - return navigationEntry as unknown as NavigationTimingPolyfillEntry; -}; + return navigationEntry as unknown as NavigationTimingPolyfillEntry; + }; -export const getNavigationEntry = (): PerformanceNavigationTiming | NavigationTimingPolyfillEntry | undefined => { +export const getNavigationEntry = (): + | PerformanceNavigationTiming + | NavigationTimingPolyfillEntry + | undefined => { if (window.__WEB_VITALS_POLYFILL__) { - return window.performance && (performance.getEntriesByType && - performance.getEntriesByType('navigation')[0] || - getNavigationEntryFromPerformanceTiming()); + return ( + window.performance && + ((performance.getEntriesByType && + performance.getEntriesByType('navigation')[0]) || + getNavigationEntryFromPerformanceTiming()) + ); } else { - return window.performance && (performance.getEntriesByType && - performance.getEntriesByType('navigation')[0]); + return ( + window.performance && + performance.getEntriesByType && + performance.getEntriesByType('navigation')[0] + ); } }; diff --git a/src/lib/getSelector.ts b/src/lib/getSelector.ts index 3c48a760..f71f1237 100644 --- a/src/lib/getSelector.ts +++ b/src/lib/getSelector.ts @@ -1,4 +1,3 @@ - /* * Copyright 2022 Google LLC * @@ -17,19 +16,23 @@ const getName = (node: Node) => { const name = node.nodeName; - return node.nodeType === 1 ? - name.toLowerCase() : name.toUpperCase().replace(/^#/, ''); -} + return node.nodeType === 1 + ? name.toLowerCase() + : name.toUpperCase().replace(/^#/, ''); +}; export const getSelector = (node: Node | null | undefined, maxLen?: number) => { let sel = ''; try { while (node && node.nodeType !== 9) { - const el: Element = (node as Element); - const part = el.id ? '#' + el.id : getName(el) + ( - (el.className && el.className.length) ? - '.' + el.className.replace(/\s+/g, '.') : ''); + const el: Element = node as Element; + const part = el.id + ? '#' + el.id + : getName(el) + + (el.className && el.className.length + ? '.' + el.className.replace(/\s+/g, '.') + : ''); if (sel.length + part.length > (maxLen || 100) - 1) return sel || part; sel = sel ? part + '>' + sel : part; if (el.id) break; diff --git a/src/lib/getVisibilityWatcher.ts b/src/lib/getVisibilityWatcher.ts index bae72d34..35f09400 100644 --- a/src/lib/getVisibilityWatcher.ts +++ b/src/lib/getVisibilityWatcher.ts @@ -16,7 +16,6 @@ import {onBFCacheRestore} from './bfcache.js'; - let firstHiddenTime = -1; const initHiddenTime = () => { @@ -25,9 +24,10 @@ const initHiddenTime = () => { // 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; -} + return document.visibilityState === 'hidden' && !document.prerendering + ? 0 + : Infinity; +}; const onVisibilityUpdate = (event: Event) => { // If the document is 'hidden' and no previous hidden timestamp has been @@ -45,7 +45,7 @@ const onVisibilityUpdate = (event: Event) => { // Remove all listeners now that a `firstHiddenTime` value has been set. removeChangeListeners(); } -} +}; const addChangeListeners = () => { addEventListener('visibilitychange', onVisibilityUpdate, true); @@ -61,7 +61,6 @@ const removeChangeListeners = () => { removeEventListener('prerenderingchange', onVisibilityUpdate, true); }; - export const getVisibilityWatcher = () => { if (firstHiddenTime < 0) { // If the document is hidden when this code runs, assume it was hidden @@ -92,6 +91,6 @@ export const getVisibilityWatcher = () => { return { get firstHiddenTime() { return firstHiddenTime; - } - } + }, + }; }; diff --git a/src/lib/initMetric.ts b/src/lib/initMetric.ts index 57453bd6..d61ed9cc 100644 --- a/src/lib/initMetric.ts +++ b/src/lib/initMetric.ts @@ -20,7 +20,6 @@ import {getActivationStart} from './getActivationStart.js'; import {getNavigationEntry} from './getNavigationEntry.js'; import {Metric} from '../types.js'; - export const initMetric = (name: Metric['name'], value?: number): Metric => { const navEntry = getNavigationEntry(); let navigationType: Metric['navigationType'] = 'navigate'; @@ -33,8 +32,10 @@ export const initMetric = (name: Metric['name'], value?: number): Metric => { } else if (document.wasDiscarded) { navigationType = 'restore'; } else { - navigationType = - navEntry.type.replace(/_/g, '-') as Metric['navigationType']; + navigationType = navEntry.type.replace( + /_/g, + '-' + ) as Metric['navigationType']; } } diff --git a/src/lib/observe.ts b/src/lib/observe.ts index 993504b3..b092e2f3 100644 --- a/src/lib/observe.ts +++ b/src/lib/observe.ts @@ -14,8 +14,10 @@ * limitations under the License. */ -import {FirstInputPolyfillEntry, NavigationTimingPolyfillEntry} from '../types.js'; - +import { + FirstInputPolyfillEntry, + NavigationTimingPolyfillEntry, +} from '../types.js'; interface PerformanceEntryMap { 'event': PerformanceEventTiming[]; @@ -38,7 +40,7 @@ interface PerformanceEntryMap { export const observe = ( type: K, callback: (entries: PerformanceEntryMap[K]) => void, - opts?: PerformanceObserverInit, + opts?: PerformanceObserverInit ): PerformanceObserver | undefined => { try { if (PerformanceObserver.supportedEntryTypes.includes(type)) { @@ -50,10 +52,15 @@ export const observe = ( callback(list.getEntries() as PerformanceEntryMap[K]); }); }); - po.observe(Object.assign({ - type, - buffered: true, - }, opts || {}) as PerformanceObserverInit); + po.observe( + Object.assign( + { + type, + buffered: true, + }, + opts || {} + ) as PerformanceObserverInit + ); return po; } } catch (e) { diff --git a/src/lib/onHidden.ts b/src/lib/onHidden.ts index c88d6278..21d191eb 100644 --- a/src/lib/onHidden.ts +++ b/src/lib/onHidden.ts @@ -14,12 +14,10 @@ * limitations under the License. */ - export interface OnHiddenCallback { (event: Event): void; } - export const onHidden = (cb: OnHiddenCallback, once?: boolean) => { const onHiddenOrPageHide = (event: Event) => { if (event.type === 'pagehide' || document.visibilityState === 'hidden') { @@ -29,7 +27,7 @@ export const onHidden = (cb: OnHiddenCallback, once?: boolean) => { 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. diff --git a/src/lib/polyfills/firstInputPolyfill.ts b/src/lib/polyfills/firstInputPolyfill.ts index 41c46e3e..6ac0314c 100644 --- a/src/lib/polyfills/firstInputPolyfill.ts +++ b/src/lib/polyfills/firstInputPolyfill.ts @@ -14,16 +14,19 @@ * limitations under the License. */ -import {FirstInputPolyfillEntry, FirstInputPolyfillCallback} from '../../types.js'; - +import { + FirstInputPolyfillEntry, + FirstInputPolyfillCallback, +} from '../../types.js'; type addOrRemoveEventListener = - typeof addEventListener | typeof removeEventListener; + | typeof addEventListener + | typeof removeEventListener; -let firstInputEvent: Event|null; +let firstInputEvent: Event | null; let firstInputDelay: number; let firstInputTimeStamp: Date; -let callbacks: FirstInputPolyfillCallback[] +let callbacks: FirstInputPolyfillCallback[]; const listenerOpts: AddEventListenerOptions = {passive: true, capture: true}; const startTimeStamp: Date = new Date(); @@ -37,14 +40,14 @@ export const firstInputPolyfill = ( ) => { 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 @@ -54,12 +57,12 @@ const recordFirstInputDelay = (delay: number, event: Event) => { if (!firstInputEvent) { firstInputEvent = event; firstInputDelay = delay; - firstInputTimeStamp = new Date; + firstInputTimeStamp = new Date(); eachEventType(removeEventListener); reportFirstInputDelayIfRecordedAndValid(); } -} +}; /** * Reports the first input delay and event (if they're recorded and valid) @@ -71,9 +74,11 @@ const reportFirstInputDelayIfRecordedAndValid = () => { // - 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) { + if ( + firstInputDelay >= 0 && + // @ts-ignore (subtracting two dates always returns a number) + firstInputDelay < firstInputTimeStamp - startTimeStamp + ) { const entry = { entryType: 'first-input', name: firstInputEvent!.type, @@ -82,12 +87,12 @@ const reportFirstInputDelayIfRecordedAndValid = () => { startTime: firstInputEvent!.timeStamp, processingStart: firstInputEvent!.timeStamp + firstInputDelay, } as FirstInputPolyfillEntry; - callbacks.forEach(function(callback) { + callbacks.forEach(function (callback) { callback(entry); }); callbacks = []; } -} +}; /** * Handles pointer down events, which are a special case. @@ -106,7 +111,7 @@ const onPointerDown = (delay: number, event: Event) => { const onPointerUp = () => { recordFirstInputDelay(delay, event); removePointerEventListeners(); - } + }; /** * Responds to 'pointercancel' events and removes pointer listeners. @@ -115,7 +120,7 @@ const onPointerDown = (delay: number, event: Event) => { */ const onPointerCancel = () => { removePointerEventListeners(); - } + }; /** * Removes added pointer event listeners. @@ -123,11 +128,11 @@ const onPointerDown = (delay: number, event: Event) => { 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 @@ -145,11 +150,11 @@ const onInput = (event: Event) => { // 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(); + 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 as number - event.timeStamp; + const delay = (now as number) - event.timeStamp; if (event.type == 'pointerdown') { onPointerDown(delay, event); @@ -157,18 +162,13 @@ const onInput = (event: Event) => { recordFirstInputDelay(delay, event); } } -} +}; /** * Invokes the passed callback const for = each event type with t =>he * `onInput` const and = `listenerOpts =>`. */ const eachEventType = (callback: addOrRemoveEventListener) => { - const eventTypes = [ - 'mousedown', - 'keydown', - 'touchstart', - 'pointerdown', - ]; + const eventTypes = ['mousedown', 'keydown', 'touchstart', 'pointerdown']; eventTypes.forEach((type) => callback(type, onInput, listenerOpts)); -} +}; diff --git a/src/lib/polyfills/getFirstHiddenTimePolyfill.ts b/src/lib/polyfills/getFirstHiddenTimePolyfill.ts index 04c67d6d..9d7f26a4 100644 --- a/src/lib/polyfills/getFirstHiddenTimePolyfill.ts +++ b/src/lib/polyfills/getFirstHiddenTimePolyfill.ts @@ -14,15 +14,14 @@ * limitations under the License. */ -let firstHiddenTime = - document.visibilityState === 'hidden' ? 0 : Infinity; +let firstHiddenTime = document.visibilityState === 'hidden' ? 0 : Infinity; const onVisibilityChange = (event: 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); diff --git a/src/lib/polyfills/interactionCountPolyfill.ts b/src/lib/polyfills/interactionCountPolyfill.ts index 18e994c1..fd29d103 100644 --- a/src/lib/polyfills/interactionCountPolyfill.ts +++ b/src/lib/polyfills/interactionCountPolyfill.ts @@ -17,7 +17,6 @@ import {observe} from '../observe.js'; import {Metric} from '../../types.js'; - declare global { interface Performance { interactionCount: number; @@ -34,11 +33,12 @@ const updateEstimate = (entries: Metric['entries']) => { minKnownInteractionId = Math.min(minKnownInteractionId, e.interactionId); maxKnownInteractionId = Math.max(maxKnownInteractionId, e.interactionId); - interactionCountEstimate = maxKnownInteractionId ? - (maxKnownInteractionId - minKnownInteractionId) / 7 + 1 : 0; + interactionCountEstimate = maxKnownInteractionId + ? (maxKnownInteractionId - minKnownInteractionId) / 7 + 1 + : 0; } }); -} +}; let po: PerformanceObserver | undefined; @@ -48,7 +48,7 @@ let po: PerformanceObserver | undefined; */ export const getInteractionCount = () => { return po ? interactionCountEstimate : performance.interactionCount || 0; -} +}; /** * Feature detects native support or initializes the polyfill if needed. diff --git a/src/lib/whenActivated.ts b/src/lib/whenActivated.ts index d9769743..e4046c95 100644 --- a/src/lib/whenActivated.ts +++ b/src/lib/whenActivated.ts @@ -14,11 +14,10 @@ * limitations under the License. */ - export const whenActivated = (callback: () => void) => { if (document.prerendering) { addEventListener('prerenderingchange', () => callback(), true); } else { callback(); } -} +}; diff --git a/src/onCLS.ts b/src/onCLS.ts index 7d4a6f91..04844e73 100644 --- a/src/onCLS.ts +++ b/src/onCLS.ts @@ -24,7 +24,6 @@ import {whenActivated} from './lib/whenActivated.js'; import {onFCP} from './onFCP.js'; import {CLSMetric, CLSReportCallback, ReportOpts} from './types.js'; - /** * 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 @@ -79,9 +78,11 @@ export const onCLS = (onReport: CLSReportCallback, opts?: ReportOpts) => { // 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) { + if ( + sessionValue && + entry.startTime - lastSessionEntry.startTime < 1000 && + entry.startTime - firstSessionEntry.startTime < 5000 + ) { sessionValue += entry.value; sessionEntries.push(entry); } else { @@ -103,7 +104,11 @@ export const onCLS = (onReport: CLSReportCallback, opts?: ReportOpts) => { const po = observe('layout-shift', handleEntries); if (po) { report = bindReporter( - onReportWrapped, metric, thresholds, opts!.reportAllChanges); + onReportWrapped, + metric, + thresholds, + opts!.reportAllChanges + ); // 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. @@ -129,7 +134,11 @@ export const onCLS = (onReport: CLSReportCallback, opts?: ReportOpts) => { fcpValue = -1; metric = initMetric('CLS', 0); report = bindReporter( - onReportWrapped, metric, thresholds, opts!.reportAllChanges); + onReportWrapped, + metric, + thresholds, + opts!.reportAllChanges + ); doubleRAF(() => report()); }); diff --git a/src/onFCP.ts b/src/onFCP.ts index feaadbdd..fb804838 100644 --- a/src/onFCP.ts +++ b/src/onFCP.ts @@ -65,14 +65,22 @@ export const onFCP = (onReport: FCPReportCallback, opts?: ReportOpts) => { if (po) { report = bindReporter( - onReport, metric, thresholds, opts!.reportAllChanges); + onReport, + metric, + thresholds, + 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, thresholds, opts!.reportAllChanges); + onReport, + metric, + thresholds, + opts!.reportAllChanges + ); doubleRAF(() => { metric.value = performance.now() - event.timeStamp; diff --git a/src/onFID.ts b/src/onFID.ts index 835f464d..3a91847d 100644 --- a/src/onFID.ts +++ b/src/onFID.ts @@ -20,9 +20,17 @@ 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 { + firstInputPolyfill, + resetFirstInputPolyfill, +} from './lib/polyfills/firstInputPolyfill.js'; import {whenActivated} from './lib/whenActivated.js'; -import {FIDMetric, FirstInputPolyfillCallback, ReportCallback, ReportOpts} from './types.js'; +import { + FIDMetric, + FirstInputPolyfillCallback, + ReportCallback, + ReportOpts, +} from './types.js'; /** * Calculates the [FID](https://web.dev/fid/) value for the current page and @@ -52,11 +60,11 @@ export const onFID = (onReport: ReportCallback, opts?: ReportOpts) => { metric.entries.push(entry); report(true); } - } + }; const handleEntries = (entries: FIDMetric['entries']) => { (entries as PerformanceEventTiming[]).forEach(handleEntry); - } + }; const po = observe('first-input', handleEntries); report = bindReporter(onReport, metric, thresholds, opts!.reportAllChanges); @@ -69,19 +77,29 @@ export const onFID = (onReport: ReportCallback, opts?: ReportOpts) => { } if (window.__WEB_VITALS_POLYFILL__) { - console.warn('The web-vitals "base+polyfill" build is deprecated. See: https://bit.ly/3aqzsGm'); + console.warn( + 'The web-vitals "base+polyfill" build is deprecated. See: https://bit.ly/3aqzsGm' + ); // Prefer the native implementation if available, if (!po) { - window.webVitals.firstInputPolyfill(handleEntry as FirstInputPolyfillCallback) + window.webVitals.firstInputPolyfill( + handleEntry as FirstInputPolyfillCallback + ); } onBFCacheRestore(() => { metric = initMetric('FID'); report = bindReporter( - onReport, metric, thresholds, opts!.reportAllChanges); + onReport, + metric, + thresholds, + opts!.reportAllChanges + ); window.webVitals.resetFirstInputPolyfill(); - window.webVitals.firstInputPolyfill(handleEntry as FirstInputPolyfillCallback); + window.webVitals.firstInputPolyfill( + handleEntry as FirstInputPolyfillCallback + ); }); } else { // Only monitor bfcache restores if the browser supports FID natively. @@ -89,7 +107,11 @@ export const onFID = (onReport: ReportCallback, opts?: ReportOpts) => { onBFCacheRestore(() => { metric = initMetric('FID'); report = bindReporter( - onReport, metric, thresholds, opts!.reportAllChanges); + onReport, + metric, + thresholds, + opts!.reportAllChanges + ); resetFirstInputPolyfill(); firstInputPolyfill(handleEntry as FirstInputPolyfillCallback); diff --git a/src/onINP.ts b/src/onINP.ts index d6d777ab..55415613 100644 --- a/src/onINP.ts +++ b/src/onINP.ts @@ -19,7 +19,10 @@ import {bindReporter} from './lib/bindReporter.js'; import {initMetric} from './lib/initMetric.js'; import {observe} from './lib/observe.js'; import {onHidden} from './lib/onHidden.js'; -import {getInteractionCount, initInteractionCountPolyfill} from './lib/polyfills/interactionCountPolyfill.js'; +import { + getInteractionCount, + initInteractionCountPolyfill, +} from './lib/polyfills/interactionCountPolyfill.js'; import {whenActivated} from './lib/whenActivated.js'; import {INPMetric, ReportCallback, ReportOpts} from './types.js'; @@ -39,7 +42,7 @@ let prevInteractionCount = 0; */ const getInteractionCountForNavigation = () => { return getInteractionCount() - prevInteractionCount; -} +}; // To prevent unnecessary memory usage on pages with lots of interactions, // store at most 10 of the longest interactions to consider as INP candidates. @@ -62,26 +65,30 @@ const longestInteractionMap: {[interactionId: string]: Interaction} = {}; const processEntry = (entry: PerformanceEventTiming) => { // The least-long of the 10 longest interactions. const minLongestInteraction = - longestInteractionList[longestInteractionList.length - 1] + longestInteractionList[longestInteractionList.length - 1]; const existingInteraction = longestInteractionMap[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 ( + existingInteraction || + longestInteractionList.length < MAX_INTERACTIONS_TO_CONSIDER || + entry.duration > minLongestInteraction.latency + ) { // If the interaction already exists, update it. Otherwise create one. if (existingInteraction) { existingInteraction.entries.push(entry); - existingInteraction.latency = - Math.max(existingInteraction.latency, entry.duration); + existingInteraction.latency = Math.max( + existingInteraction.latency, + entry.duration + ); } else { const interaction = { id: entry.interactionId!, latency: entry.duration, entries: [entry], - } + }; longestInteractionMap[interaction.id] = interaction; longestInteractionList.push(interaction); } @@ -92,18 +99,20 @@ const processEntry = (entry: PerformanceEventTiming) => { delete longestInteractionMap[i.id]; }); } -} +}; /** * Returns the estimated p98 longest interaction based on the stored * interaction candidates and the interaction count for the current page. */ const estimateP98LongestInteraction = () => { - const candidateInteractionIndex = Math.min(longestInteractionList.length - 1, - Math.floor(getInteractionCountForNavigation() / 50)); + const candidateInteractionIndex = Math.min( + longestInteractionList.length - 1, + Math.floor(getInteractionCountForNavigation() / 50) + ); - return longestInteractionList[candidateInteractionIndex]; -} + return longestInteractionList[candidateInteractionIndex]; +}; /** * Calculates the [INP](https://web.dev/responsiveness/) value for the current @@ -161,12 +170,16 @@ export const onINP = (onReport: ReportCallback, opts?: ReportOpts) => { // it's not an issue at the moment. // TODO(philipwalton): remove once crbug.com/1325826 is fixed. if (entry.entryType === 'first-input') { - const noMatchingEntry = !longestInteractionList.some((interaction) => { - return interaction.entries.some((prevEntry) => { - return entry.duration === prevEntry.duration && - entry.startTime === prevEntry.startTime; - }); - }); + const noMatchingEntry = !longestInteractionList.some( + (interaction) => { + return interaction.entries.some((prevEntry) => { + return ( + entry.duration === prevEntry.duration && + entry.startTime === prevEntry.startTime + ); + }); + } + ); if (noMatchingEntry) { processEntry(entry); } @@ -222,7 +235,11 @@ export const onINP = (onReport: ReportCallback, opts?: ReportOpts) => { metric = initMetric('INP'); report = bindReporter( - onReport, metric, thresholds, opts!.reportAllChanges); + onReport, + metric, + thresholds, + opts!.reportAllChanges + ); }); } }); diff --git a/src/onLCP.ts b/src/onLCP.ts index 551d8d5f..1f218273 100644 --- a/src/onLCP.ts +++ b/src/onLCP.ts @@ -25,7 +25,6 @@ import {onHidden} from './lib/onHidden.js'; import {whenActivated} from './lib/whenActivated.js'; import {LCPMetric, ReportCallback, ReportOpts} from './types.js'; - const reportedMetricIDs: Record = {}; /** @@ -52,7 +51,7 @@ export const onLCP = (onReport: ReportCallback, opts?: ReportOpts) => { let report: ReturnType; const handleEntries = (entries: LCPMetric['entries']) => { - const lastEntry = (entries[entries.length - 1] as LargestContentfulPaint); + const lastEntry = entries[entries.length - 1] as LargestContentfulPaint; 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 @@ -75,7 +74,11 @@ export const onLCP = (onReport: ReportCallback, opts?: ReportOpts) => { if (po) { report = bindReporter( - onReport, metric, thresholds, opts!.reportAllChanges); + onReport, + metric, + thresholds, + opts!.reportAllChanges + ); const stopListening = () => { if (!reportedMetricIDs[metric.id]) { @@ -84,7 +87,7 @@ export const onLCP = (onReport: ReportCallback, opts?: ReportOpts) => { 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 @@ -100,7 +103,11 @@ export const onLCP = (onReport: ReportCallback, opts?: ReportOpts) => { onBFCacheRestore((event) => { metric = initMetric('LCP'); report = bindReporter( - onReport, metric, thresholds, opts!.reportAllChanges); + onReport, + metric, + thresholds, + opts!.reportAllChanges + ); doubleRAF(() => { metric.value = performance.now() - event.timeStamp; diff --git a/src/onTTFB.ts b/src/onTTFB.ts index c9fa86e6..3f8407c0 100644 --- a/src/onTTFB.ts +++ b/src/onTTFB.ts @@ -22,7 +22,6 @@ import {ReportCallback, ReportOpts} from './types.js'; import {getActivationStart} from './lib/getActivationStart.js'; import {whenActivated} from './lib/whenActivated.js'; - /** * Runs in the next task after the page is done loading and/or prerendering. * @param callback @@ -36,7 +35,7 @@ const whenReady = (callback: () => void) => { // Queue a task so the callback runs after `loadEventEnd`. setTimeout(callback, 0); } -} +}; /** * Calculates the [TTFB](https://web.dev/time-to-first-byte/) value for the @@ -62,7 +61,11 @@ export const onTTFB = (onReport: ReportCallback, opts?: ReportOpts) => { let metric = initMetric('TTFB'); let report = bindReporter( - onReport, metric, thresholds, opts.reportAllChanges); + onReport, + metric, + thresholds, + opts.reportAllChanges + ); whenReady(() => { const navEntry = getNavigationEntry(); @@ -92,7 +95,11 @@ export const onTTFB = (onReport: ReportCallback, opts?: ReportOpts) => { onBFCacheRestore(() => { metric = initMetric('TTFB', 0); report = bindReporter( - onReport, metric, thresholds, opts!.reportAllChanges); + onReport, + metric, + thresholds, + opts!.reportAllChanges + ); report(true); }); diff --git a/src/polyfill.ts b/src/polyfill.ts index 94a2d0e1..4b286344 100644 --- a/src/polyfill.ts +++ b/src/polyfill.ts @@ -14,7 +14,10 @@ * limitations under the License. */ -import {firstInputPolyfill, resetFirstInputPolyfill} from './lib/polyfills/firstInputPolyfill.js'; +import { + firstInputPolyfill, + resetFirstInputPolyfill, +} from './lib/polyfills/firstInputPolyfill.js'; import {getFirstHiddenTime} from './lib/polyfills/getFirstHiddenTimePolyfill.js'; resetFirstInputPolyfill(); diff --git a/src/types.ts b/src/types.ts index e75751a7..3737ff83 100644 --- a/src/types.ts +++ b/src/types.ts @@ -26,7 +26,6 @@ export * from './types/inp.js'; export * from './types/lcp.js'; export * from './types/ttfb.js'; - // -------------------------------------------------------------------------- // Web Vitals package globals // -------------------------------------------------------------------------- @@ -51,9 +50,9 @@ declare global { // -------------------------------------------------------------------------- interface PerformanceEntryMap { - 'navigation': PerformanceNavigationTiming; - 'resource': PerformanceResourceTiming; - 'paint': PerformancePaintTiming; + navigation: PerformanceNavigationTiming; + resource: PerformanceResourceTiming; + paint: PerformancePaintTiming; } // Update built-in types to be more accurate. @@ -66,7 +65,9 @@ declare global { } interface Performance { - getEntriesByType(type: K): PerformanceEntryMap[K][] + getEntriesByType( + type: K + ): PerformanceEntryMap[K][]; } // https://w3c.github.io/event-timing/#sec-modifications-perf-timeline diff --git a/src/types/base.ts b/src/types/base.ts index a0e6c9d8..70250f4a 100644 --- a/src/types/base.ts +++ b/src/types/base.ts @@ -14,8 +14,10 @@ * limitations under the License. */ -import {FirstInputPolyfillEntry, NavigationTimingPolyfillEntry} from './polyfills.js'; - +import { + FirstInputPolyfillEntry, + NavigationTimingPolyfillEntry, +} from './polyfills.js'; export interface Metric { /** @@ -56,7 +58,12 @@ export interface Metric { * 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 | LayoutShift | FirstInputPolyfillEntry | NavigationTimingPolyfillEntry)[]; + entries: ( + | PerformanceEntry + | LayoutShift + | FirstInputPolyfillEntry + | NavigationTimingPolyfillEntry + )[]; /** * The type of navigation. @@ -69,7 +76,13 @@ export interface Metric { * - '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'; + navigationType: + | 'navigate' + | 'reload' + | 'back-forward' + | 'back-forward-cache' + | 'prerender' + | 'restore'; } /** @@ -108,4 +121,8 @@ export interface ReportOpts { * - `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'; +export type LoadState = + | 'loading' + | 'dom-interactive' + | 'dom-content-loaded' + | 'complete'; diff --git a/src/types/cls.ts b/src/types/cls.ts index 055e1f94..58dbce35 100644 --- a/src/types/cls.ts +++ b/src/types/cls.ts @@ -16,7 +16,6 @@ import {LoadState, Metric, ReportCallback} from './base.js'; - /** * A CLS-specific version of the Metric object. */ @@ -30,7 +29,7 @@ export interface CLSMetric extends Metric { * 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 { +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 diff --git a/src/types/fcp.ts b/src/types/fcp.ts index 82ae65ed..df1a78cc 100644 --- a/src/types/fcp.ts +++ b/src/types/fcp.ts @@ -17,7 +17,6 @@ import {LoadState, Metric, ReportCallback} from './base.js'; import {NavigationTimingPolyfillEntry} from './polyfills.js'; - /** * An FCP-specific version of the Metric object. */ @@ -31,7 +30,7 @@ export interface FCPMetric extends Metric { * 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 { +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). @@ -46,11 +45,11 @@ export interface FCPMetric extends Metric { * `LoadState` for details). Ideally, documents can paint before they finish * loading (e.g. the `loading` or `dom-interactive` phases). */ - loadState: LoadState, + loadState: LoadState; /** * The `PerformancePaintTiming` entry corresponding to FCP. */ - fcpEntry?: PerformancePaintTiming, + fcpEntry?: PerformancePaintTiming; /** * The `navigation` entry of the current page, which is useful for diagnosing * general page load issues. diff --git a/src/types/fid.ts b/src/types/fid.ts index 1aa34c21..a5449753 100644 --- a/src/types/fid.ts +++ b/src/types/fid.ts @@ -17,7 +17,6 @@ import {LoadState, Metric, ReportCallback} from './base.js'; import {FirstInputPolyfillEntry} from './polyfills.js'; - /** * An FID-specific version of the Metric object. */ @@ -50,7 +49,7 @@ export interface FIDAttribution { * The `PerformanceEventTiming` entry corresponding to FID (or the * polyfill entry in browsers that don't support Event Timing). */ - eventEntry: PerformanceEventTiming | FirstInputPolyfillEntry, + eventEntry: PerformanceEventTiming | FirstInputPolyfillEntry; /** * The loading state of the document at the time when the first interaction * occurred (see `LoadState` for details). If the first interaction occurred diff --git a/src/types/inp.ts b/src/types/inp.ts index b2d4d341..cc1a0794 100644 --- a/src/types/inp.ts +++ b/src/types/inp.ts @@ -16,7 +16,6 @@ import {LoadState, Metric, ReportCallback} from './base.js'; - /** * An INP-specific version of the Metric object. */ diff --git a/src/types/lcp.ts b/src/types/lcp.ts index 486f735d..d36b860a 100644 --- a/src/types/lcp.ts +++ b/src/types/lcp.ts @@ -17,7 +17,6 @@ import {Metric, ReportCallback} from './base.js'; import {NavigationTimingPolyfillEntry} from './polyfills.js'; - /** * An LCP-specific version of the Metric object. */ @@ -35,12 +34,12 @@ export interface LCPAttribution { /** * The element corresponding to the largest contentful paint for the page. */ - element?: string, + 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, + 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 diff --git a/src/types/polyfills.ts b/src/types/polyfills.ts index 3d4d822a..c174fe27 100644 --- a/src/types/polyfills.ts +++ b/src/types/polyfills.ts @@ -14,14 +14,24 @@ * limitations under the License. */ -export type FirstInputPolyfillEntry = Omit; +export type FirstInputPolyfillEntry = Omit< + PerformanceEventTiming, + 'processingEnd' +>; export interface FirstInputPolyfillCallback { (entry: FirstInputPolyfillEntry): void; } -export type NavigationTimingPolyfillEntry = Omit & { +export type NavigationTimingPolyfillEntry = Omit< + PerformanceNavigationTiming, + | 'initiatorType' + | 'nextHopProtocol' + | 'redirectCount' + | 'transferSize' + | 'encodedBodySize' + | 'decodedBodySize' + | 'type' +> & { type: PerformanceNavigationTiming['type']; -} +}; diff --git a/src/types/ttfb.ts b/src/types/ttfb.ts index 838a0a1c..14208160 100644 --- a/src/types/ttfb.ts +++ b/src/types/ttfb.ts @@ -17,7 +17,6 @@ import {Metric, ReportCallback} from './base.js'; import {NavigationTimingPolyfillEntry} from './polyfills.js'; - /** * A TTFB-specific version of the Metric object. */ @@ -31,7 +30,7 @@ export interface TTFBMetric extends Metric { * 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. */ - export interface TTFBAttribution { +export interface TTFBAttribution { /** * The total time from when the user initiates loading the page to when the * DNS lookup begins. This includes redirects, service worker startup, and diff --git a/test/e2e/onCLS-test.js b/test/e2e/onCLS-test.js index 79c41896..751b1688 100644 --- a/test/e2e/onCLS-test.js +++ b/test/e2e/onCLS-test.js @@ -23,21 +23,20 @@ import {nextFrame} from '../utils/nextFrame.js'; import {stubForwardBack} from '../utils/stubForwardBack.js'; import {stubVisibilityChange} from '../utils/stubVisibilityChange.js'; - -describe('onCLS()', async function() { +describe('onCLS()', async function () { // Retry all tests in this suite up to 2 times. this.retries(2); let browserSupportsCLS; - before(async function() { + before(async function () { browserSupportsCLS = await browserSupportsEntry('layout-shift'); }); - beforeEach(async function() { + beforeEach(async function () { await clearBeacons(); }); - it('reports the correct value on visibility hidden after shifts (reportAllChanges === false)', async function() { + it('reports the correct value on visibility hidden after shifts (reportAllChanges === false)', async function () { if (!browserSupportsCLS) this.skip(); await browser.url('/test/cls'); @@ -58,7 +57,7 @@ describe('onCLS()', async function() { assert.match(cls.navigationType, /navigate|reload/); }); - it('reports the correct value on page unload after shifts (reportAllChanges === false)', async function() { + it('reports the correct value on page unload after shifts (reportAllChanges === false)', async function () { if (!browserSupportsCLS) this.skip(); await browser.url('/test/cls'); @@ -79,7 +78,7 @@ describe('onCLS()', async function() { assert.match(cls.navigationType, /navigate|reload/); }); - it('resets the session after timeout or gap elapses', async function() { + it('resets the session after timeout or gap elapses', async function () { if (!browserSupportsCLS) this.skip(); await browser.url('/test/cls'); @@ -182,7 +181,7 @@ describe('onCLS()', async function() { assert.strictEqual(beacons.length, 0); }); - it('does not report if the browser does not support CLS', async function() { + it('does not report if the browser does not support CLS', async function () { if (browserSupportsCLS) this.skip(); await browser.url('/test/cls'); @@ -203,7 +202,7 @@ describe('onCLS()', async function() { assert.strictEqual(beacons.length, 0); }); - it('reports no new values on visibility hidden after shifts (reportAllChanges === true)', async function() { + it('reports no new values on visibility hidden after shifts (reportAllChanges === true)', async function () { if (!browserSupportsCLS) this.skip(); await browser.url('/test/cls?reportAllChanges=1'); @@ -247,7 +246,7 @@ describe('onCLS()', async function() { assert.strictEqual(beacons.length, 0); }); - it('does not report if the value has not changed (reportAllChanges === true)', async function() { + it('does not report if the value has not changed (reportAllChanges === true)', async function () { if (!browserSupportsCLS) this.skip(); await browser.url('/test/cls?reportAllChanges=1'); @@ -292,7 +291,7 @@ describe('onCLS()', async function() { assert.strictEqual(beacons.length, 0); }); - it('continues reporting after visibilitychange (reportAllChanges === false)', async function() { + it('continues reporting after visibilitychange (reportAllChanges === false)', async function () { if (!browserSupportsCLS) this.skip(); await browser.url(`/test/cls`); @@ -336,7 +335,7 @@ describe('onCLS()', async function() { assert.match(cls2.navigationType, /navigate|reload/); }); - it('continues reporting after visibilitychange (reportAllChanges === true)', async function() { + it('continues reporting after visibilitychange (reportAllChanges === true)', async function () { if (!browserSupportsCLS) this.skip(); await browser.url(`/test/cls?reportAllChanges=1`); @@ -390,7 +389,7 @@ describe('onCLS()', async function() { assert.match(cls4.navigationType, /navigate|reload/); }); - it('continues reporting after bfcache restore (reportAllChanges === false)', async function() { + it('continues reporting after bfcache restore (reportAllChanges === false)', async function () { if (!browserSupportsCLS) this.skip(); await browser.url(`/test/cls`); @@ -449,7 +448,7 @@ describe('onCLS()', async function() { assert.strictEqual(cls3.navigationType, 'back-forward-cache'); }); - it('continues reporting after bfcache restore (reportAllChanges === true)', async function() { + it('continues reporting after bfcache restore (reportAllChanges === true)', async function () { if (!browserSupportsCLS) this.skip(); await browser.url(`/test/cls?reportAllChanges=1`); @@ -510,7 +509,7 @@ describe('onCLS()', async function() { assert.strictEqual(cls5.navigationType, 'back-forward-cache'); }); - it('reports zero if no layout shifts occurred on first visibility hidden (reportAllChanges === false)', async function() { + it('reports zero if no layout shifts occurred on first visibility hidden (reportAllChanges === false)', async function () { if (!browserSupportsCLS) this.skip(); await browser.url(`/test/cls?noLayoutShifts=1`); @@ -531,7 +530,7 @@ describe('onCLS()', async function() { assert.match(cls.navigationType, /navigate|reload/); }); - it('reports zero if no layout shifts occurred on first visibility hidden (reportAllChanges === true)', async function() { + it('reports zero if no layout shifts occurred on first visibility hidden (reportAllChanges === true)', async function () { if (!browserSupportsCLS) this.skip(); await browser.url(`/test/cls?reportAllChanges=1&noLayoutShifts=1`); @@ -552,7 +551,7 @@ describe('onCLS()', async function() { assert.match(cls.navigationType, /navigate|reload/); }); - it('reports zero if no layout shifts occurred on page unload (reportAllChanges === false)', async function() { + it('reports zero if no layout shifts occurred on page unload (reportAllChanges === false)', async function () { if (!browserSupportsCLS) this.skip(); await browser.url(`/test/cls?noLayoutShifts=1`); @@ -573,7 +572,7 @@ describe('onCLS()', async function() { assert.match(cls.navigationType, /navigate|reload/); }); - it('reports zero if no layout shifts occurred on page unload (reportAllChanges === true)', async function() { + it('reports zero if no layout shifts occurred on page unload (reportAllChanges === true)', async function () { if (!browserSupportsCLS) this.skip(); await browser.url(`/test/cls?noLayoutShifts=1&reportAllChanges=1`); @@ -594,7 +593,7 @@ describe('onCLS()', async function() { assert.match(cls.navigationType, /navigate|reload/); }); - it('does not report if the document was hidden at page load time', async function() { + it('does not report if the document was hidden at page load time', async function () { await browser.url('/test/cls?hidden=1'); await stubVisibilityChange('visible'); @@ -606,7 +605,7 @@ describe('onCLS()', async function() { assert.strictEqual(beacons.length, 0); }); - it('reports if the page is restored from bfcache even when the document was hidden at page load time', async function() { + it('reports if the page is restored from bfcache even when the document was hidden at page load time', async function () { if (!browserSupportsCLS) this.skip(); await browser.url('/test/cls?hidden=1'); @@ -632,7 +631,7 @@ describe('onCLS()', async function() { assert.strictEqual(cls.navigationType, 'back-forward-cache'); }); - it('reports prerender as nav type for prerender', async function() { + it('reports prerender as nav type for prerender', async function () { if (!browserSupportsCLS) this.skip(); await browser.url('/test/cls?prerender=1'); @@ -641,7 +640,6 @@ describe('onCLS()', async function() { await imagesPainted(); await stubVisibilityChange('hidden'); - await beaconCountIs(1); const [cls] = await getBeacons(); @@ -654,7 +652,7 @@ describe('onCLS()', async function() { assert.strictEqual(cls.navigationType, 'prerender'); }); - it('reports restore as nav type for wasDiscarded', async function() { + it('reports restore as nav type for wasDiscarded', async function () { if (!browserSupportsCLS) this.skip(); await browser.url('/test/cls?wasDiscarded=1'); @@ -675,8 +673,8 @@ describe('onCLS()', async function() { assert.strictEqual(cls.navigationType, 'restore'); }); - describe('attribution', function() { - it('includes attribution data on the metric object', async function() { + describe('attribution', function () { + it('includes attribution data on the metric object', async function () { if (!browserSupportsCLS) this.skip(); await browser.url('/test/cls?attribution=1&delayDCL=2000'); @@ -696,10 +694,9 @@ describe('onCLS()', async function() { assert.strictEqual(cls.entries.length, 2); assert.match(cls.navigationType, /navigate|reload/); - const { - largestShiftEntry, - largestShiftSource, - } = getAttribution(cls.entries); + const {largestShiftEntry, largestShiftSource} = getAttribution( + cls.entries + ); assert.deepEqual(cls.attribution.largestShiftEntry, largestShiftEntry); assert.deepEqual(cls.attribution.largestShiftSource, largestShiftSource); @@ -707,14 +704,18 @@ describe('onCLS()', async function() { assert.equal(cls.attribution.largestShiftValue, largestShiftEntry.value); assert.equal(cls.attribution.largestShiftTarget, '#p3'); assert.equal( - cls.attribution.largestShiftTime, largestShiftEntry.startTime); + cls.attribution.largestShiftTime, + largestShiftEntry.startTime + ); // The first shift (before the second image loads) is the largest. - assert.match(cls.attribution.loadState, - /^dom-(interactive|content-loaded)$/); + assert.match( + cls.attribution.loadState, + /^dom-(interactive|content-loaded)$/ + ); }); - it('reports whether the largest shift was before or after load', async function() { + it('reports whether the largest shift was before or after load', async function () { if (!browserSupportsCLS) this.skip(); await browser.url('/test/cls?attribution=1&noLayoutShifts=1'); @@ -734,10 +735,9 @@ describe('onCLS()', async function() { assert.strictEqual(cls.entries.length, 1); assert.match(cls.navigationType, /navigate|reload/); - const { - largestShiftEntry, - largestShiftSource, - } = getAttribution(cls.entries); + const {largestShiftEntry, largestShiftSource} = getAttribution( + cls.entries + ); assert.deepEqual(cls.attribution.largestShiftEntry, largestShiftEntry); assert.deepEqual(cls.attribution.largestShiftSource, largestShiftSource); @@ -745,13 +745,15 @@ describe('onCLS()', async function() { assert.equal(cls.attribution.largestShiftValue, largestShiftEntry.value); assert.equal(cls.attribution.largestShiftTarget, 'html>body>main>h1'); assert.equal( - cls.attribution.largestShiftTime, largestShiftEntry.startTime); + cls.attribution.largestShiftTime, + largestShiftEntry.startTime + ); // The first shift (before the second image loads) is the largest. assert.equal(cls.attribution.loadState, 'complete'); }); - it('reports an empty object when no shifts', async function() { + it('reports an empty object when no shifts', async function () { if (!browserSupportsCLS) this.skip(); await browser.url('/test/cls?attribution=1&noLayoutShifts=1'); @@ -808,4 +810,3 @@ function getAttribution(entries) { return {largestShiftEntry, largestShiftSource}; } - diff --git a/test/e2e/onFCP-test.js b/test/e2e/onFCP-test.js index aa065951..8363dc68 100644 --- a/test/e2e/onFCP-test.js +++ b/test/e2e/onFCP-test.js @@ -21,21 +21,20 @@ import {domReadyState} from '../utils/domReadyState.js'; import {stubForwardBack} from '../utils/stubForwardBack.js'; import {stubVisibilityChange} from '../utils/stubVisibilityChange.js'; - -describe('onFCP()', async function() { +describe('onFCP()', async function () { // Retry all tests in this suite up to 2 times. this.retries(2); let browserSupportsFCP; - before(async function() { + before(async function () { browserSupportsFCP = await browserSupportsEntry('paint'); }); - beforeEach(async function() { + beforeEach(async function () { await clearBeacons(); }); - it('reports the correct value after the first paint', async function() { + it('reports the correct value after the first paint', async function () { if (!browserSupportsFCP) this.skip(); await browser.url('/test/fcp'); @@ -52,7 +51,7 @@ describe('onFCP()', async function() { assert.match(fcp.navigationType, /navigate|reload/); }); - it('accounts for time prerendering the page', async function() { + it('accounts for time prerendering the page', async function () { if (!browserSupportsFCP) this.skip(); await browser.url('/test/fcp?prerender=1'); @@ -75,7 +74,7 @@ describe('onFCP()', async function() { assert.strictEqual(fcp.navigationType, 'prerender'); }); - it('does not report if the browser does not support FCP (including bfcache restores)', async function() { + it('does not report if the browser does not support FCP (including bfcache restores)', async function () { if (browserSupportsFCP) this.skip(); await browser.url('/test/fcp'); @@ -96,7 +95,7 @@ describe('onFCP()', async function() { assert.strictEqual(bfcacheRestoreBeacons.length, 0); }); - it('does not report if the document was hidden at page load time', async function() { + it('does not report if the document was hidden at page load time', async function () { if (!browserSupportsFCP) this.skip(); await browser.url('/test/fcp?hidden=1'); @@ -111,7 +110,7 @@ describe('onFCP()', async function() { assert.strictEqual(beacons.length, 0); }); - it('does not report if the document changes to hidden before the first entry', async function() { + it('does not report if the document changes to hidden before the first entry', async function () { if (!browserSupportsFCP) this.skip(); await browser.url('/test/fcp?invisible=1'); @@ -126,7 +125,7 @@ describe('onFCP()', async function() { assert.strictEqual(beacons.length, 0); }); - it('reports after a render delay before the page changes to hidden', async function() { + it('reports after a render delay before the page changes to hidden', async function () { if (!browserSupportsFCP) this.skip(); await browser.url('/test/fcp?renderBlocking=2000'); @@ -145,7 +144,7 @@ describe('onFCP()', async function() { assert.match(fcp.navigationType, /navigate|reload/); }); - it('reports if the page is restored from bfcache', async function() { + it('reports if the page is restored from bfcache', async function () { if (!browserSupportsFCP) this.skip(); await browser.url('/test/fcp'); @@ -192,7 +191,7 @@ describe('onFCP()', async function() { assert.strictEqual(fcp3.navigationType, 'back-forward-cache'); }); - it('reports if the page is restored from bfcache even when the document was hidden at page load time', async function() { + it('reports if the page is restored from bfcache even when the document was hidden at page load time', async function () { if (!browserSupportsFCP) this.skip(); await browser.url('/test/fcp?hidden=1'); @@ -235,7 +234,7 @@ describe('onFCP()', async function() { assert.strictEqual(fcp2.navigationType, 'back-forward-cache'); }); - it('reports restore as nav type for wasDiscarded', async function() { + it('reports restore as nav type for wasDiscarded', async function () { if (!browserSupportsFCP) this.skip(); await browser.url('/test/fcp?wasDiscarded=1'); @@ -252,8 +251,8 @@ describe('onFCP()', async function() { assert.strictEqual(fcp.navigationType, 'restore'); }); - describe('attribution', function() { - it('includes attribution data on the metric object', async function() { + describe('attribution', function () { + it('includes attribution data on the metric object', async function () { if (!browserSupportsFCP) this.skip(); await browser.url('/test/fcp?attribution=1'); @@ -266,7 +265,8 @@ describe('onFCP()', async function() { }); const fcpEntry = await browser.execute(() => { return performance - .getEntriesByName('first-contentful-paint')[0].toJSON(); + .getEntriesByName('first-contentful-paint')[0] + .toJSON(); }); const [fcp] = await getBeacons(); @@ -280,10 +280,14 @@ describe('onFCP()', async function() { assert.match(fcp.navigationType, /navigate|reload/); assert.equal(fcp.attribution.timeToFirstByte, navEntry.responseStart); - assert.equal(fcp.attribution.firstByteToFCP, - fcp.value - navEntry.responseStart); - assert.match(fcp.attribution.loadState, - /^(loading|dom-(interactive|content-loaded)|complete)$/); + assert.equal( + fcp.attribution.firstByteToFCP, + fcp.value - navEntry.responseStart + ); + assert.match( + fcp.attribution.loadState, + /^(loading|dom-(interactive|content-loaded)|complete)$/ + ); assert.deepEqual(fcp.attribution.fcpEntry, fcpEntry); @@ -296,7 +300,7 @@ describe('onFCP()', async function() { assert.equal(attributionNavEntry.responseStart, navEntry.responseStart); }); - it('accounts for time prerendering the page', async function() { + it('accounts for time prerendering the page', async function () { if (!browserSupportsFCP) this.skip(); await browser.url('/test/fcp?attribution=1&prerender=1'); @@ -309,7 +313,8 @@ describe('onFCP()', async function() { }); const fcpEntry = await browser.execute(() => { return performance - .getEntriesByName('first-contentful-paint')[0].toJSON(); + .getEntriesByName('first-contentful-paint')[0] + .toJSON(); }); // Since this value is stubbed in the browser, get it separately. @@ -326,10 +331,14 @@ describe('onFCP()', async function() { assert.strictEqual(fcp.entries.length, 1); assert.strictEqual(fcp.navigationType, 'prerender'); - assert.equal(fcp.attribution.timeToFirstByte, - Math.max(0, navEntry.responseStart - activationStart)); - assert.equal(fcp.attribution.firstByteToFCP, - fcp.value - Math.max(0, navEntry.responseStart - activationStart)); + assert.equal( + fcp.attribution.timeToFirstByte, + Math.max(0, navEntry.responseStart - activationStart) + ); + assert.equal( + fcp.attribution.firstByteToFCP, + fcp.value - Math.max(0, navEntry.responseStart - activationStart) + ); assert.deepEqual(fcp.attribution.fcpEntry, fcpEntry); @@ -342,7 +351,7 @@ describe('onFCP()', async function() { assert.equal(attributionNavEntry.responseStart, navEntry.responseStart); }); - it('reports after a bfcache restore', async function() { + it('reports after a bfcache restore', async function () { if (!browserSupportsFCP) this.skip(); await browser.url('/test/fcp?attribution=1'); diff --git a/test/e2e/onFID-test.js b/test/e2e/onFID-test.js index 0dd05b23..3b1cf0cd 100644 --- a/test/e2e/onFID-test.js +++ b/test/e2e/onFID-test.js @@ -21,21 +21,20 @@ import {domReadyState} from '../utils/domReadyState.js'; import {stubForwardBack} from '../utils/stubForwardBack.js'; import {stubVisibilityChange} from '../utils/stubVisibilityChange.js'; - -describe('onFID()', async function() { +describe('onFID()', async function () { // Retry all tests in this suite up to 2 times. this.retries(2); let browserSupportsFID; - before(async function() { + before(async function () { browserSupportsFID = await browserSupportsEntry('first-input'); }); - beforeEach(async function() { + beforeEach(async function () { await clearBeacons(); }); - it('reports the correct value after input', async function() { + it('reports the correct value after input', async function () { if (!browserSupportsFID) this.skip(); await browser.url('/test/fid'); @@ -56,7 +55,7 @@ describe('onFID()', async function() { assert.match(fid.entries[0].name, /(mouse|pointer)down/); }); - it('does not report if the browser does not support FID and the polyfill is not used', async function() { + it('does not report if the browser does not support FID and the polyfill is not used', async function () { if (browserSupportsFID) this.skip(); await browser.url('/test/fid'); @@ -84,7 +83,7 @@ describe('onFID()', async function() { assert.strictEqual(bfcacheRestoreBeacons.length, 0); }); - it('falls back to the polyfill in non-supporting browsers', async function() { + it('falls back to the polyfill in non-supporting browsers', async function () { // Ignore Safari until this bug is fixed: // https://bugs.webkit.org/show_bug.cgi?id=211101 if (browser.capabilities.browserName === 'Safari') this.skip(); @@ -113,7 +112,7 @@ describe('onFID()', async function() { } }); - it('does not report if the document was hidden at page load time', async function() { + it('does not report if the document was hidden at page load time', async function () { // Ignore Safari until this bug is fixed: // https://bugs.webkit.org/show_bug.cgi?id=211101 if (browser.capabilities.browserName === 'Safari') this.skip(); @@ -134,7 +133,7 @@ describe('onFID()', async function() { assert.strictEqual(beacons.length, 0); }); - it('does not report if the document changes to hidden before the first input', async function() { + it('does not report if the document changes to hidden before the first input', async function () { // Ignore Safari until this bug is fixed: // https://bugs.webkit.org/show_bug.cgi?id=211101 if (browser.capabilities.browserName === 'Safari') this.skip(); @@ -155,7 +154,7 @@ describe('onFID()', async function() { assert.strictEqual(beacons.length, 0); }); - it('reports the first input delay after bfcache restores', async function() { + it('reports the first input delay after bfcache restores', async function () { if (!browserSupportsFID) this.skip(); await browser.url('/test/fid'); @@ -194,7 +193,7 @@ describe('onFID()', async function() { assert.match(fid2.entries[0].name, /(mouse|pointer)down/); }); - it('reports prerender as nav type for prerender', async function() { + it('reports prerender as nav type for prerender', async function () { if (!browserSupportsFID) this.skip(); await browser.url('/test/fid?prerender=1'); @@ -215,7 +214,7 @@ describe('onFID()', async function() { assert.match(fid.entries[0].name, /(mouse|pointer)down/); }); - it('reports restore as nav type for wasDiscarded', async function() { + it('reports restore as nav type for wasDiscarded', async function () { if (!browserSupportsFID) this.skip(); await browser.url('/test/fid?wasDiscarded=1'); @@ -236,9 +235,8 @@ describe('onFID()', async function() { assert.match(fid.entries[0].name, /(mouse|pointer)down/); }); - - describe('attribution', function() { - it('includes attribution data on the metric object', async function() { + describe('attribution', function () { + it('includes attribution data on the metric object', async function () { if (!browserSupportsFID) this.skip(); await browser.url('/test/fid?attribution=1'); @@ -267,7 +265,7 @@ describe('onFID()', async function() { assert.equal(fid.attribution.loadState, 'complete'); }); - it('reports the domReadyState when input occurred', async function() { + it('reports the domReadyState when input occurred', async function () { if (!browserSupportsFID) this.skip(); await browser.url('/test/fid?attribution=1&delayDCL=1000'); @@ -296,5 +294,3 @@ describe('onFID()', async function() { }); }); }); - - diff --git a/test/e2e/onINP-test.js b/test/e2e/onINP-test.js index 45f93dc0..f6376d93 100644 --- a/test/e2e/onINP-test.js +++ b/test/e2e/onINP-test.js @@ -21,24 +21,22 @@ import {nextFrame} from '../utils/nextFrame.js'; import {stubForwardBack} from '../utils/stubForwardBack.js'; import {stubVisibilityChange} from '../utils/stubVisibilityChange.js'; - const ROUNDING_ERROR = 8; - -describe('onINP()', async function() { +describe('onINP()', async function () { // Retry all tests in this suite up to 2 times. this.retries(2); let browserSupportsINP; - before(async function() { + before(async function () { browserSupportsINP = await browserSupportsEntry('event'); }); - beforeEach(async function() { + beforeEach(async function () { await clearBeacons(); }); - it('reports the correct value on visibility hidden after interactions (reportAllChanges === false)', async function() { + it('reports the correct value on visibility hidden after interactions (reportAllChanges === false)', async function () { if (!browserSupportsINP) this.skip(); await browser.url('/test/inp?click=100'); @@ -62,7 +60,7 @@ describe('onINP()', async function() { assert.match(inp.navigationType, /navigate|reload/); }); - it('reports the correct value on visibility hidden after interactions (reportAllChanges === true)', async function() { + it('reports the correct value on visibility hidden after interactions (reportAllChanges === true)', async function () { if (!browserSupportsINP) this.skip(); await browser.url('/test/inp?click=100&reportAllChanges=1'); @@ -86,7 +84,7 @@ describe('onINP()', async function() { assert.match(inp.navigationType, /navigate|reload/); }); - it('reports the correct value on page unload after interactions (reportAllChanges === false)', async function() { + it('reports the correct value on page unload after interactions (reportAllChanges === false)', async function () { if (!browserSupportsINP) this.skip(); await browser.url('/test/inp?click=100'); @@ -110,7 +108,7 @@ describe('onINP()', async function() { assert.match(inp.navigationType, /navigate|reload/); }); - it('reports the correct value on page unload after interactions (reportAllChanges === true)', async function() { + it('reports the correct value on page unload after interactions (reportAllChanges === true)', async function () { if (!browserSupportsINP) this.skip(); await browser.url('/test/inp?click=100&reportAllChanges=1'); @@ -134,7 +132,7 @@ describe('onINP()', async function() { assert.match(inp.navigationType, /navigate|reload/); }); - it('reports approx p98 interaction when 50+ interactions (reportAllChanges === false)', async function() { + it('reports approx p98 interaction when 50+ interactions (reportAllChanges === false)', async function () { if (!browserSupportsINP) this.skip(); await browser.url('/test/inp?click=60&pointerdown=600'); @@ -191,7 +189,7 @@ describe('onINP()', async function() { assert.strictEqual(inp3.rating, 'needs-improvement'); }); - it('reports approx p98 interaction when 50+ interactions (reportAllChanges === true)', async function() { + it('reports approx p98 interaction when 50+ interactions (reportAllChanges === true)', async function () { if (!browserSupportsINP) this.skip(); await browser.url('/test/inp?click=60&pointerdown=600&reportAllChanges=1'); @@ -227,7 +225,7 @@ describe('onINP()', async function() { assert.strictEqual(inp3.rating, 'needs-improvement'); }); - it('reports a new interaction after bfcache restore', async function() { + it('reports a new interaction after bfcache restore', async function () { if (!browserSupportsINP) this.skip(); await browser.url('/test/inp'); @@ -302,7 +300,7 @@ describe('onINP()', async function() { assert.strictEqual(inp3.navigationType, 'back-forward-cache'); }); - it('does not report if there were no interactions', async function() { + it('does not report if there were no interactions', async function () { if (!browserSupportsINP) this.skip(); await browser.url('/test/inp'); @@ -316,12 +314,11 @@ describe('onINP()', async function() { assert.strictEqual(beacons.length, 0); }); - it('reports prerender as nav type for prerender', async function() { + it('reports prerender as nav type for prerender', async function () { if (!browserSupportsINP) this.skip(); await browser.url('/test/inp?click=100&prerender=1'); - const h1 = await $('h1'); await h1.click(); @@ -341,7 +338,7 @@ describe('onINP()', async function() { assert.strictEqual(inp.navigationType, 'prerender'); }); - it('reports restore as nav type for wasDiscarded', async function() { + it('reports restore as nav type for wasDiscarded', async function () { if (!browserSupportsINP) this.skip(); await browser.url('/test/inp?click=100&wasDiscarded=1'); @@ -365,8 +362,8 @@ describe('onINP()', async function() { assert.strictEqual(inp.navigationType, 'restore'); }); - describe('attribution', function() { - it('includes attribution data on the metric object', async function() { + describe('attribution', function () { + it('includes attribution data on the metric object', async function () { if (!browserSupportsINP) this.skip(); await browser.url('/test/inp?click=100&attribution=1'); @@ -447,11 +444,13 @@ describe('onINP()', async function() { assert.equal(eventEntry2.processingStart, pointerupEntry.processingStart); }); - it('reports the domReadyState when input occurred', async function() { + it('reports the domReadyState when input occurred', async function () { if (!browserSupportsINP) this.skip(); - await browser.url('/test/inp?' + - 'attribution=1&reportAllChanges=1&click=100&delayDCL=1000'); + await browser.url( + '/test/inp?' + + 'attribution=1&reportAllChanges=1&click=100&delayDCL=1000' + ); // Click on the

. const h1 = await $('h1'); @@ -465,8 +464,10 @@ describe('onINP()', async function() { await clearBeacons(); - await browser.url('/test/inp?' + - 'attribution=1&reportAllChanges=1&click=100&delayResponse=1000'); + await browser.url( + '/test/inp?' + + 'attribution=1&reportAllChanges=1&click=100&delayResponse=1000' + ); // Click on the