From caca5a3e393aebe9206f6a2cf42c66af4c3e7df3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 25 Jan 2022 21:04:52 +0000 Subject: [PATCH 01/60] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- pyproject.toml | 1 - src/pytest_html/plugin.py | 3 +-- src/pytest_html/resources/index.html | 14 +++++++------- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 512272eb..2683f072 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,4 +11,3 @@ build-backend = "setuptools.build_meta" [tool.setuptools_scm] local_scheme = "no-local-version" write_to = "src/pytest_html/__version.py" - diff --git a/src/pytest_html/plugin.py b/src/pytest_html/plugin.py index fdd0fe6d..3961bcde 100644 --- a/src/pytest_html/plugin.py +++ b/src/pytest_html/plugin.py @@ -4,13 +4,12 @@ import os import pytest +from _pytest.pathlib import Path from . import extras # noqa: F401 from .html_report import HTMLReport from .nextgen import NextGenReport -from _pytest.pathlib import Path - def pytest_addhooks(pluginmanager): from . import hooks diff --git a/src/pytest_html/resources/index.html b/src/pytest_html/resources/index.html index 417809e0..f75cf531 100644 --- a/src/pytest_html/resources/index.html +++ b/src/pytest_html/resources/index.html @@ -64,17 +64,17 @@

Environment

Summary

(Un)check the boxes to filter the results.

- - + + - - + + - +
@@ -86,7 +86,7 @@

Summary

Results

- +
- \ No newline at end of file + From 680fdc120f01016d7b684af2a209efbb67c30dd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jim=20Br=C3=A4nnlund?= Date: Tue, 25 Jan 2022 22:34:48 +0100 Subject: [PATCH 02/60] fix main.js conflicts --- src/pytest_html/html_report.py | 2 +- src/pytest_html/resources/main.js | 26 +-- src/pytest_html/resources/old_main.js | 246 ++++++++++++++++++++++++++ 3 files changed, 252 insertions(+), 22 deletions(-) create mode 100644 src/pytest_html/resources/old_main.js diff --git a/src/pytest_html/html_report.py b/src/pytest_html/html_report.py index 66e10f07..28afe823 100644 --- a/src/pytest_html/html_report.py +++ b/src/pytest_html/html_report.py @@ -178,7 +178,7 @@ def _generate_report(self, session): ] with open( - os.path.join(os.path.dirname(__file__), "resources", "main.js") + os.path.join(os.path.dirname(__file__), "resources", "old_main.js") ) as main_js_fp: main_js = main_js_fp.read() diff --git a/src/pytest_html/resources/main.js b/src/pytest_html/resources/main.js index b8736f1e..63c26498 100644 --- a/src/pytest_html/resources/main.js +++ b/src/pytest_html/resources/main.js @@ -56,29 +56,13 @@ function hideExtras(colresultElem) { expandcollapse.classList.add('expander'); } -function showFilters() { - let visibleString = getQueryParameter('visible') || 'all'; - visibleString = visibleString.toLowerCase(); - const checkedItems = visibleString.split(','); - - const filterItems = document.getElementsByClassName('filter'); - for (let i = 0; i < filterItems.length; i++) { - filterItems[i].hidden = false; - - if (visibleString != 'all') { - filterItems[i].checked = checkedItems.includes(filterItems[i].getAttribute('data-test-result')); - filterTable(filterItems[i]); - } - } -} - function addCollapse() { // Add links for show/hide all const resulttable = find('table#results-table'); const showhideall = document.createElement('p'); showhideall.innerHTML = - 'Show all details / ' + - 'Hide all details'; + 'Show all details / ' + + 'Hide all details'; resulttable.parentElement.insertBefore(showhideall, resulttable); // Add show/hide link to each result @@ -98,9 +82,9 @@ function addCollapse() { elem.addEventListener('click', function (event) { if ( - event.currentTarget.parentNode.nextElementSibling.classList.contains( - 'collapsed' - ) + event.currentTarget.parentNode.nextElementSibling.classList.contains( + 'collapsed' + ) ) { showExtras(event.currentTarget); } else { diff --git a/src/pytest_html/resources/old_main.js b/src/pytest_html/resources/old_main.js new file mode 100644 index 00000000..f26513c9 --- /dev/null +++ b/src/pytest_html/resources/old_main.js @@ -0,0 +1,246 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +function toArray(iter) { + if (iter === null) { + return null; + } + return Array.prototype.slice.call(iter); +} + +function find(selector, elem) { // eslint-disable-line no-redeclare + if (!elem) { + elem = document; + } + return elem.querySelector(selector); +} + +function findAll(selector, elem) { + if (!elem) { + elem = document; + } + return toArray(elem.querySelectorAll(selector)); +} + +function sortColumn(elem) { + toggleSortStates(elem); + const colIndex = toArray(elem.parentNode.childNodes).indexOf(elem); + let key; + if (elem.classList.contains('result')) { + key = keyResult; + } else if (elem.classList.contains('links')) { + key = keyLink; + } else { + key = keyAlpha; + } + sortTable(elem, key(colIndex)); +} + +function showAllExtras() { // eslint-disable-line no-unused-vars + findAll('.col-result').forEach(showExtras); +} + +function hideAllExtras() { // eslint-disable-line no-unused-vars + findAll('.col-result').forEach(hideExtras); +} + +function showExtras(colresultElem) { + const extras = colresultElem.parentNode.nextElementSibling; + const expandcollapse = colresultElem.firstElementChild; + extras.classList.remove('collapsed'); + expandcollapse.classList.remove('expander'); + expandcollapse.classList.add('collapser'); +} + +function hideExtras(colresultElem) { + const extras = colresultElem.parentNode.nextElementSibling; + const expandcollapse = colresultElem.firstElementChild; + extras.classList.add('collapsed'); + expandcollapse.classList.remove('collapser'); + expandcollapse.classList.add('expander'); +} + +function showFilters() { + let visibleString = getQueryParameter('visible') || 'all'; + visibleString = visibleString.toLowerCase(); + const checkedItems = visibleString.split(','); + + const filterItems = document.getElementsByClassName('filter'); + for (let i = 0; i < filterItems.length; i++) { + filterItems[i].hidden = false; + + if (visibleString != 'all') { + filterItems[i].checked = checkedItems.includes(filterItems[i].getAttribute('data-test-result')); + filterTable(filterItems[i]); + } + } +} + +function addCollapse() { + // Add links for show/hide all + const resulttable = find('table#results-table'); + const showhideall = document.createElement('p'); + showhideall.innerHTML = 'Show all details / ' + + 'Hide all details'; + resulttable.parentElement.insertBefore(showhideall, resulttable); + + // Add show/hide link to each result + findAll('.col-result').forEach(function(elem) { + const collapsed = getQueryParameter('collapsed') || 'Passed'; + const extras = elem.parentNode.nextElementSibling; + const expandcollapse = document.createElement('span'); + if (extras.classList.contains('collapsed')) { + expandcollapse.classList.add('expander'); + } else if (collapsed.includes(elem.innerHTML)) { + extras.classList.add('collapsed'); + expandcollapse.classList.add('expander'); + } else { + expandcollapse.classList.add('collapser'); + } + elem.appendChild(expandcollapse); + + elem.addEventListener('click', function(event) { + if (event.currentTarget.parentNode.nextElementSibling.classList.contains('collapsed')) { + showExtras(event.currentTarget); + } else { + hideExtras(event.currentTarget); + } + }); + }); +} + +function getQueryParameter(name) { + const match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search); + return match && decodeURIComponent(match[1].replace(/\+/g, ' ')); +} + +function init () { // eslint-disable-line no-unused-vars + resetSortHeaders(); + + addCollapse(); + + showFilters(); + + sortColumn(find('.initial-sort')); + + findAll('.sortable').forEach(function(elem) { + elem.addEventListener('click', + function() { + sortColumn(elem); + }, false); + }); +} + +function sortTable(clicked, keyFunc) { + const rows = findAll('.results-table-row'); + const reversed = !clicked.classList.contains('asc'); + const sortedRows = sort(rows, keyFunc, reversed); + /* Whole table is removed here because browsers acts much slower + * when appending existing elements. + */ + const thead = document.getElementById('results-table-head'); + document.getElementById('results-table').remove(); + const parent = document.createElement('table'); + parent.id = 'results-table'; + parent.appendChild(thead); + sortedRows.forEach(function(elem) { + parent.appendChild(elem); + }); + document.getElementsByTagName('BODY')[0].appendChild(parent); +} + +function sort(items, keyFunc, reversed) { + const sortArray = items.map(function(item, i) { + return [keyFunc(item), i]; + }); + + sortArray.sort(function(a, b) { + const keyA = a[0]; + const keyB = b[0]; + + if (keyA == keyB) return 0; + + if (reversed) { + return keyA < keyB ? 1 : -1; + } else { + return keyA > keyB ? 1 : -1; + } + }); + + return sortArray.map(function(item) { + const index = item[1]; + return items[index]; + }); +} + +function keyAlpha(colIndex) { + return function(elem) { + return elem.childNodes[1].childNodes[colIndex].firstChild.data.toLowerCase(); + }; +} + +function keyLink(colIndex) { + return function(elem) { + const dataCell = elem.childNodes[1].childNodes[colIndex].firstChild; + return dataCell == null ? '' : dataCell.innerText.toLowerCase(); + }; +} + +function keyResult(colIndex) { + return function(elem) { + const strings = ['Error', 'Failed', 'Rerun', 'XFailed', 'XPassed', + 'Skipped', 'Passed']; + return strings.indexOf(elem.childNodes[1].childNodes[colIndex].firstChild.data); + }; +} + +function resetSortHeaders() { + findAll('.sort-icon').forEach(function(elem) { + elem.parentNode.removeChild(elem); + }); + findAll('.sortable').forEach(function(elem) { + const icon = document.createElement('div'); + icon.className = 'sort-icon'; + icon.textContent = 'vvv'; + elem.insertBefore(icon, elem.firstChild); + elem.classList.remove('desc', 'active'); + elem.classList.add('asc', 'inactive'); + }); +} + +function toggleSortStates(elem) { + //if active, toggle between asc and desc + if (elem.classList.contains('active')) { + elem.classList.toggle('asc'); + elem.classList.toggle('desc'); + } + + //if inactive, reset all other functions and add ascending active + if (elem.classList.contains('inactive')) { + resetSortHeaders(); + elem.classList.remove('inactive'); + elem.classList.add('active'); + } +} + +function isAllRowsHidden(value) { + return value.hidden == false; +} + +function filterTable(elem) { // eslint-disable-line no-unused-vars + const outcomeAtt = 'data-test-result'; + const outcome = elem.getAttribute(outcomeAtt); + const classOutcome = outcome + ' results-table-row'; + const outcomeRows = document.getElementsByClassName(classOutcome); + + for(let i = 0; i < outcomeRows.length; i++){ + outcomeRows[i].hidden = !elem.checked; + } + + const rows = findAll('.results-table-row').filter(isAllRowsHidden); + const allRowsHidden = rows.length == 0 ? true : false; + const notFoundMessage = document.getElementById('not-found-message'); + notFoundMessage.hidden = !allRowsHidden; +} From b780107c92ea29dbfca3f5fcfbefae4d79ca7104 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jim=20Br=C3=A4nnlund?= Date: Tue, 25 Jan 2022 22:37:45 +0100 Subject: [PATCH 03/60] fix js test --- testing/js_test_report.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/js_test_report.html b/testing/js_test_report.html index 5b4fae82..205fab29 100644 --- a/testing/js_test_report.html +++ b/testing/js_test_report.html @@ -12,7 +12,7 @@ - +
From 4a324b714654de8646ea4f3c8cc5e68acbc27452 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jim=20Br=C3=A4nnlund?= Date: Tue, 25 Jan 2022 22:41:54 +0100 Subject: [PATCH 04/60] fix resource (main.js) test --- testing/test_pytest_html.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/test_pytest_html.py b/testing/test_pytest_html.py index 69341caf..aaa7bd2e 100644 --- a/testing/test_pytest_html.py +++ b/testing/test_pytest_html.py @@ -369,7 +369,7 @@ def test_resources(self, testdir): assert result.ret == 0 content = pkg_resources.resource_string( - "pytest_html", os.path.join("resources", "main.js") + "pytest_html", os.path.join("resources", "old_main.js") ) content = content.decode("utf-8") assert content From f84c7d4b8158b415a79dcb46709c0cb81dc92eff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jim=20Br=C3=A4nnlund?= Date: Tue, 25 Jan 2022 22:45:48 +0100 Subject: [PATCH 05/60] ignore linting old main.js --- .eslintignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .eslintignore diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..1d5235ca --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +old_main.js From ca59818df38fc7b83d2cfe339390c36bd77b172c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jim=20Br=C3=A4nnlund?= Date: Tue, 25 Jan 2022 22:47:57 +0100 Subject: [PATCH 06/60] ignore more js-files for now --- .eslintignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.eslintignore b/.eslintignore index 1d5235ca..cf0dabd2 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1,3 @@ old_main.js +events.js +dom.js From e70971025db614f1fa122ac3971d2f9b918a1170 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jim=20Br=C3=A4nnlund?= Date: Tue, 25 Jan 2022 22:49:43 +0100 Subject: [PATCH 07/60] sigh --- .eslintignore | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.eslintignore b/.eslintignore index cf0dabd2..a6c7c285 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1 @@ -old_main.js -events.js -dom.js +*.js From 03f7bb8a5c85609dd96043b8a77aaabe84d177af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jim=20Br=C3=A4nnlund?= Date: Tue, 25 Jan 2022 22:52:18 +0100 Subject: [PATCH 08/60] lets skip eslint for now --- .eslintignore | 1 - .pre-commit-config.yaml | 14 +++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) delete mode 100644 .eslintignore diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index a6c7c285..00000000 --- a/.eslintignore +++ /dev/null @@ -1 +0,0 @@ -*.js diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b5757483..dcbc8a52 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,13 +37,13 @@ repos: hooks: - id: pyupgrade args: [--py3-plus] - - repo: https://github.com/pre-commit/mirrors-eslint - rev: v7.13.0 - hooks: - - id: eslint - additional_dependencies: - - eslint@7.13.0 - args: [src] +# - repo: https://github.com/pre-commit/mirrors-eslint +# rev: v7.13.0 +# hooks: +# - id: eslint +# additional_dependencies: +# - eslint@7.13.0 +# args: [src] - repo: local hooks: - id: rst From b4295ead29a414787f530ed9d9c336dad6110797 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jim=20Br=C3=A4nnlund?= Date: Sat, 15 Jan 2022 17:14:44 +0100 Subject: [PATCH 09/60] add backend --- .../resources/.prettierrc => .prettierrc | 0 poetry.lock | 606 ++++++++++++++++++ pyproject.toml | 16 + setup.py | 4 +- src/pytest_html/hooks.py | 10 +- src/pytest_html/nextgen.py | 130 +++- src/pytest_html/plugin.py | 20 +- .../assets/snapshot_of_what_went_wrong.png | Bin 125033 -> 0 bytes src/pytest_html/resources/index.html | 180 +++--- src/pytest_html/resources/index.jinja2 | 99 +++ src/pytest_html/resources/main.js | 305 ++++++--- src/pytest_html/{resources => scripts}/dom.js | 0 .../{resources => scripts}/events.js | 0 .../{resources => scripts}/index.js | 0 src/pytest_html/scripts/main.js | 115 ++++ .../{resources => scripts}/sort.js | 0 src/pytest_html/util.py | 26 + 17 files changed, 1287 insertions(+), 224 deletions(-) rename src/pytest_html/resources/.prettierrc => .prettierrc (100%) create mode 100644 poetry.lock delete mode 100644 src/pytest_html/resources/assets/snapshot_of_what_went_wrong.png create mode 100644 src/pytest_html/resources/index.jinja2 rename src/pytest_html/{resources => scripts}/dom.js (100%) rename src/pytest_html/{resources => scripts}/events.js (100%) rename src/pytest_html/{resources => scripts}/index.js (100%) create mode 100644 src/pytest_html/scripts/main.js rename src/pytest_html/{resources => scripts}/sort.js (100%) diff --git a/src/pytest_html/resources/.prettierrc b/.prettierrc similarity index 100% rename from src/pytest_html/resources/.prettierrc rename to .prettierrc diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 00000000..603a275f --- /dev/null +++ b/poetry.lock @@ -0,0 +1,606 @@ +[[package]] +name = "appdirs" +version = "1.4.4" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "atomicwrites" +version = "1.4.0" +description = "Atomic file writes." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "attrs" +version = "21.2.0" +description = "Classes Without Boilerplate" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.extras] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"] +docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"] + +[[package]] +name = "black" +version = "20.8b1" +description = "The uncompromising code formatter." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +appdirs = "*" +click = ">=7.1.2" +dataclasses = {version = ">=0.6", markers = "python_version < \"3.7\""} +mypy-extensions = ">=0.4.3" +pathspec = ">=0.6,<1" +regex = ">=2020.1.8" +toml = ">=0.10.1" +typed-ast = ">=1.4.0" +typing-extensions = ">=3.7.4" + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] + +[[package]] +name = "click" +version = "8.0.0" +description = "Composable command line interface toolkit" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.4" +description = "Cross-platform colored terminal text." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "dataclasses" +version = "0.8" +description = "A backport of the dataclasses module for Python 3.6" +category = "dev" +optional = false +python-versions = ">=3.6, <3.7" + +[[package]] +name = "distlib" +version = "0.3.1" +description = "Distribution utilities" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "filelock" +version = "3.0.12" +description = "A platform independent file lock." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "flake8" +version = "3.8.4" +description = "the modular source code checker: pep8 pyflakes and co" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" + +[package.dependencies] +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} +mccabe = ">=0.6.0,<0.7.0" +pycodestyle = ">=2.6.0a1,<2.7.0" +pyflakes = ">=2.2.0,<2.3.0" + +[[package]] +name = "importlib-metadata" +version = "4.0.1" +description = "Read metadata from Python packages" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} +zipp = ">=0.5" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] + +[[package]] +name = "importlib-resources" +version = "5.1.2" +description = "Read resources from Python packages" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +zipp = {version = ">=0.4", markers = "python_version < \"3.8\""} + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "pytest-black (>=0.3.7)", "pytest-mypy"] + +[[package]] +name = "iniconfig" +version = "1.1.1" +description = "iniconfig: brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "jinja2" +version = "2.11.3" +description = "A very fast and expressive template engine." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.dependencies] +MarkupSafe = ">=0.23" + +[package.extras] +i18n = ["Babel (>=0.8)"] + +[[package]] +name = "markupsafe" +version = "2.0.0" +description = "Safely add untrusted strings to HTML/XML markup." +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "mccabe" +version = "0.6.1" +description = "McCabe checker, plugin for flake8" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "mypy-extensions" +version = "0.4.3" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "packaging" +version = "20.9" +description = "Core utilities for Python packages" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.dependencies] +pyparsing = ">=2.0.2" + +[[package]] +name = "pathspec" +version = "0.8.1" +description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "pluggy" +version = "0.13.1" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.dependencies] +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} + +[package.extras] +dev = ["pre-commit", "tox"] + +[[package]] +name = "py" +version = "1.10.0" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "pycodestyle" +version = "2.6.0" +description = "Python style guide checker" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "pyflakes" +version = "2.2.0" +description = "passive checker of Python programs" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "pyparsing" +version = "2.4.7" +description = "Python parsing module" +category = "dev" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "pytest" +version = "6.2.4" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} +attrs = ">=19.2.0" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<1.0.0a1" +py = ">=1.8.2" +toml = "*" + +[package.extras] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] + +[[package]] +name = "regex" +version = "2021.4.4" +description = "Alternative regular expression module, to replace re." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +category = "dev" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "tox" +version = "3.23.1" +description = "tox is a generic virtualenv management and test command line tool" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" + +[package.dependencies] +colorama = {version = ">=0.4.1", markers = "platform_system == \"Windows\""} +filelock = ">=3.0.0" +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +packaging = ">=14" +pluggy = ">=0.12.0" +py = ">=1.4.17" +six = ">=1.14.0" +toml = ">=0.9.4" +virtualenv = ">=16.0.0,<20.0.0 || >20.0.0,<20.0.1 || >20.0.1,<20.0.2 || >20.0.2,<20.0.3 || >20.0.3,<20.0.4 || >20.0.4,<20.0.5 || >20.0.5,<20.0.6 || >20.0.6,<20.0.7 || >20.0.7" + +[package.extras] +docs = ["pygments-github-lexers (>=0.0.5)", "sphinx (>=2.0.0)", "sphinxcontrib-autoprogram (>=0.1.5)", "towncrier (>=18.5.0)"] +testing = ["flaky (>=3.4.0)", "freezegun (>=0.3.11)", "psutil (>=5.6.1)", "pytest (>=4.0.0)", "pytest-cov (>=2.5.1)", "pytest-mock (>=1.10.0)", "pytest-randomly (>=1.0.0)", "pytest-xdist (>=1.22.2)", "pathlib2 (>=2.3.3)"] + +[[package]] +name = "typed-ast" +version = "1.4.3" +description = "a fork of Python 2 and 3 ast modules with type comment support" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "typing-extensions" +version = "3.10.0.0" +description = "Backported and Experimental Type Hints for Python 3.5+" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "virtualenv" +version = "20.4.6" +description = "Virtual Python Environment builder" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" + +[package.dependencies] +appdirs = ">=1.4.3,<2" +distlib = ">=0.3.1,<1" +filelock = ">=3.0.0,<4" +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +importlib-resources = {version = ">=1.0", markers = "python_version < \"3.7\""} +six = ">=1.9.0,<2" + +[package.extras] +docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)"] +testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)", "xonsh (>=0.9.16)"] + +[[package]] +name = "zipp" +version = "3.4.1" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] + +[metadata] +lock-version = "1.1" +python-versions = ">=3.6" +content-hash = "d36eaa646e026d7c80966befc4950d025bf17c0736c2e76f70476150dc26429c" + +[metadata.files] +appdirs = [ + {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, + {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, +] +atomicwrites = [ + {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, + {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, +] +attrs = [ + {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, + {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, +] +black = [ + {file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"}, +] +click = [ + {file = "click-8.0.0-py3-none-any.whl", hash = "sha256:e90e62ced43dc8105fb9a26d62f0d9340b5c8db053a814e25d95c19873ae87db"}, + {file = "click-8.0.0.tar.gz", hash = "sha256:7d8c289ee437bcb0316820ccee14aefcb056e58d31830ecab8e47eda6540e136"}, +] +colorama = [ + {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, +] +dataclasses = [ + {file = "dataclasses-0.8-py3-none-any.whl", hash = "sha256:0201d89fa866f68c8ebd9d08ee6ff50c0b255f8ec63a71c16fda7af82bb887bf"}, + {file = "dataclasses-0.8.tar.gz", hash = "sha256:8479067f342acf957dc82ec415d355ab5edb7e7646b90dc6e2fd1d96ad084c97"}, +] +distlib = [ + {file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"}, + {file = "distlib-0.3.1.zip", hash = "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1"}, +] +filelock = [ + {file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"}, + {file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"}, +] +flake8 = [ + {file = "flake8-3.8.4-py2.py3-none-any.whl", hash = "sha256:749dbbd6bfd0cf1318af27bf97a14e28e5ff548ef8e5b1566ccfb25a11e7c839"}, + {file = "flake8-3.8.4.tar.gz", hash = "sha256:aadae8761ec651813c24be05c6f7b4680857ef6afaae4651a4eccaef97ce6c3b"}, +] +importlib-metadata = [ + {file = "importlib_metadata-4.0.1-py3-none-any.whl", hash = "sha256:d7eb1dea6d6a6086f8be21784cc9e3bcfa55872b52309bc5fad53a8ea444465d"}, + {file = "importlib_metadata-4.0.1.tar.gz", hash = "sha256:8c501196e49fb9df5df43833bdb1e4328f64847763ec8a50703148b73784d581"}, +] +importlib-resources = [ + {file = "importlib_resources-5.1.2-py3-none-any.whl", hash = "sha256:ebab3efe74d83b04d6bf5cd9a17f0c5c93e60fb60f30c90f56265fce4682a469"}, + {file = "importlib_resources-5.1.2.tar.gz", hash = "sha256:642586fc4740bd1cad7690f836b3321309402b20b332529f25617ff18e8e1370"}, +] +iniconfig = [ + {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, + {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, +] +jinja2 = [ + {file = "Jinja2-2.11.3-py2.py3-none-any.whl", hash = "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419"}, + {file = "Jinja2-2.11.3.tar.gz", hash = "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6"}, +] +markupsafe = [ + {file = "MarkupSafe-2.0.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2efaeb1baff547063bad2b2893a8f5e9c459c4624e1a96644bbba08910ae34e0"}, + {file = "MarkupSafe-2.0.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:441ce2a8c17683d97e06447fcbccbdb057cbf587c78eb75ae43ea7858042fe2c"}, + {file = "MarkupSafe-2.0.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:45535241baa0fc0ba2a43961a1ac7562ca3257f46c4c3e9c0de38b722be41bd1"}, + {file = "MarkupSafe-2.0.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:90053234a6479738fd40d155268af631c7fca33365f964f2208867da1349294b"}, + {file = "MarkupSafe-2.0.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:3b54a9c68995ef4164567e2cd1a5e16db5dac30b2a50c39c82db8d4afaf14f63"}, + {file = "MarkupSafe-2.0.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:f58b5ba13a5689ca8317b98439fccfbcc673acaaf8241c1869ceea40f5d585bf"}, + {file = "MarkupSafe-2.0.0-cp36-cp36m-win32.whl", hash = "sha256:a00dce2d96587651ef4fa192c17e039e8cfab63087c67e7d263a5533c7dad715"}, + {file = "MarkupSafe-2.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:007dc055dbce5b1104876acee177dbfd18757e19d562cd440182e1f492e96b95"}, + {file = "MarkupSafe-2.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a08cd07d3c3c17cd33d9e66ea9dee8f8fc1c48e2d11bd88fd2dc515a602c709b"}, + {file = "MarkupSafe-2.0.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:3c352ff634e289061711608f5e474ec38dbaa21e3e168820d53d5f4015e5b91b"}, + {file = "MarkupSafe-2.0.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:32200f562daaab472921a11cbb63780f1654552ae49518196fc361ed8e12e901"}, + {file = "MarkupSafe-2.0.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:fef86115fdad7ae774720d7103aa776144cf9b66673b4afa9bcaa7af990ed07b"}, + {file = "MarkupSafe-2.0.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:e79212d09fc0e224d20b43ad44bb0a0a3416d1e04cf6b45fed265114a5d43d20"}, + {file = "MarkupSafe-2.0.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:79b2ae94fa991be023832e6bcc00f41dbc8e5fe9d997a02db965831402551730"}, + {file = "MarkupSafe-2.0.0-cp37-cp37m-win32.whl", hash = "sha256:3261fae28155e5c8634dd7710635fe540a05b58f160cef7713c7700cb9980e66"}, + {file = "MarkupSafe-2.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e4570d16f88c7f3032ed909dc9e905a17da14a1c4cfd92608e3fda4cb1208bbd"}, + {file = "MarkupSafe-2.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8f806bfd0f218477d7c46a11d3e52dc7f5fdfaa981b18202b7dc84bbc287463b"}, + {file = "MarkupSafe-2.0.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e77e4b983e2441aff0c0d07ee711110c106b625f440292dfe02a2f60c8218bd6"}, + {file = "MarkupSafe-2.0.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:031bf79a27d1c42f69c276d6221172417b47cb4b31cdc73d362a9bf5a1889b9f"}, + {file = "MarkupSafe-2.0.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:83cf0228b2f694dcdba1374d5312f2277269d798e65f40344964f642935feac1"}, + {file = "MarkupSafe-2.0.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:4cc563836f13c57f1473bc02d1e01fc37bab70ad4ee6be297d58c1d66bc819bf"}, + {file = "MarkupSafe-2.0.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:d00a669e4a5bec3ee6dbeeeedd82a405ced19f8aeefb109a012ea88a45afff96"}, + {file = "MarkupSafe-2.0.0-cp38-cp38-win32.whl", hash = "sha256:161d575fa49395860b75da5135162481768b11208490d5a2143ae6785123e77d"}, + {file = "MarkupSafe-2.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:58bc9fce3e1557d463ef5cee05391a05745fd95ed660f23c1742c711712c0abb"}, + {file = "MarkupSafe-2.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3fb47f97f1d338b943126e90b79cad50d4fcfa0b80637b5a9f468941dbbd9ce5"}, + {file = "MarkupSafe-2.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dab0c685f21f4a6c95bfc2afd1e7eae0033b403dd3d8c1b6d13a652ada75b348"}, + {file = "MarkupSafe-2.0.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:664832fb88b8162268928df233f4b12a144a0c78b01d38b81bdcf0fc96668ecb"}, + {file = "MarkupSafe-2.0.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:df561f65049ed3556e5b52541669310e88713fdae2934845ec3606f283337958"}, + {file = "MarkupSafe-2.0.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:24bbc3507fb6dfff663af7900a631f2aca90d5a445f272db5fc84999fa5718bc"}, + {file = "MarkupSafe-2.0.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:87de598edfa2230ff274c4de7fcf24c73ffd96208c8e1912d5d0fee459767d75"}, + {file = "MarkupSafe-2.0.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:a19d39b02a24d3082856a5b06490b714a9d4179321225bbf22809ff1e1887cc8"}, + {file = "MarkupSafe-2.0.0-cp39-cp39-win32.whl", hash = "sha256:4aca81a687975b35e3e80bcf9aa93fe10cd57fac37bf18b2314c186095f57e05"}, + {file = "MarkupSafe-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:70820a1c96311e02449591cbdf5cd1c6a34d5194d5b55094ab725364375c9eb2"}, + {file = "MarkupSafe-2.0.0.tar.gz", hash = "sha256:4fae0677f712ee090721d8b17f412f1cbceefbf0dc180fe91bab3232f38b4527"}, +] +mccabe = [ + {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, + {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, +] +mypy-extensions = [ + {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, + {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, +] +packaging = [ + {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"}, + {file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"}, +] +pathspec = [ + {file = "pathspec-0.8.1-py2.py3-none-any.whl", hash = "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"}, + {file = "pathspec-0.8.1.tar.gz", hash = "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd"}, +] +pluggy = [ + {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, + {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, +] +py = [ + {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, + {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, +] +pycodestyle = [ + {file = "pycodestyle-2.6.0-py2.py3-none-any.whl", hash = "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367"}, + {file = "pycodestyle-2.6.0.tar.gz", hash = "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"}, +] +pyflakes = [ + {file = "pyflakes-2.2.0-py2.py3-none-any.whl", hash = "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92"}, + {file = "pyflakes-2.2.0.tar.gz", hash = "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"}, +] +pyparsing = [ + {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, + {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, +] +pytest = [ + {file = "pytest-6.2.4-py3-none-any.whl", hash = "sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890"}, + {file = "pytest-6.2.4.tar.gz", hash = "sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b"}, +] +regex = [ + {file = "regex-2021.4.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:619d71c59a78b84d7f18891fe914446d07edd48dc8328c8e149cbe0929b4e000"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:47bf5bf60cf04d72bf6055ae5927a0bd9016096bf3d742fa50d9bf9f45aa0711"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:281d2fd05555079448537fe108d79eb031b403dac622621c78944c235f3fcf11"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:bd28bc2e3a772acbb07787c6308e00d9626ff89e3bfcdebe87fa5afbfdedf968"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:7c2a1af393fcc09e898beba5dd59196edaa3116191cc7257f9224beaed3e1aa0"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c38c71df845e2aabb7fb0b920d11a1b5ac8526005e533a8920aea97efb8ec6a4"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:96fcd1888ab4d03adfc9303a7b3c0bd78c5412b2bfbe76db5b56d9eae004907a"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:ade17eb5d643b7fead300a1641e9f45401c98eee23763e9ed66a43f92f20b4a7"}, + {file = "regex-2021.4.4-cp36-cp36m-win32.whl", hash = "sha256:e8e5b509d5c2ff12f8418006d5a90e9436766133b564db0abaec92fd27fcee29"}, + {file = "regex-2021.4.4-cp36-cp36m-win_amd64.whl", hash = "sha256:11d773d75fa650cd36f68d7ca936e3c7afaae41b863b8c387a22aaa78d3c5c79"}, + {file = "regex-2021.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d3029c340cfbb3ac0a71798100ccc13b97dddf373a4ae56b6a72cf70dfd53bc8"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:18c071c3eb09c30a264879f0d310d37fe5d3a3111662438889ae2eb6fc570c31"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:4c557a7b470908b1712fe27fb1ef20772b78079808c87d20a90d051660b1d69a"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:01afaf2ec48e196ba91b37451aa353cb7eda77efe518e481707e0515025f0cd5"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:3a9cd17e6e5c7eb328517969e0cb0c3d31fd329298dd0c04af99ebf42e904f82"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:90f11ff637fe8798933fb29f5ae1148c978cccb0452005bf4c69e13db951e765"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:919859aa909429fb5aa9cf8807f6045592c85ef56fdd30a9a3747e513db2536e"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:339456e7d8c06dd36a22e451d58ef72cef293112b559010db3d054d5560ef439"}, + {file = "regex-2021.4.4-cp37-cp37m-win32.whl", hash = "sha256:67bdb9702427ceddc6ef3dc382455e90f785af4c13d495f9626861763ee13f9d"}, + {file = "regex-2021.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:32e65442138b7b76dd8173ffa2cf67356b7bc1768851dded39a7a13bf9223da3"}, + {file = "regex-2021.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1e1c20e29358165242928c2de1482fb2cf4ea54a6a6dea2bd7a0e0d8ee321500"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:314d66636c494ed9c148a42731b3834496cc9a2c4251b1661e40936814542b14"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6d1b01031dedf2503631d0903cb563743f397ccaf6607a5e3b19a3d76fc10480"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:741a9647fcf2e45f3a1cf0e24f5e17febf3efe8d4ba1281dcc3aa0459ef424dc"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:4c46e22a0933dd783467cf32b3516299fb98cfebd895817d685130cc50cd1093"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:e512d8ef5ad7b898cdb2d8ee1cb09a8339e4f8be706d27eaa180c2f177248a10"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:980d7be47c84979d9136328d882f67ec5e50008681d94ecc8afa8a65ed1f4a6f"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:ce15b6d103daff8e9fee13cf7f0add05245a05d866e73926c358e871221eae87"}, + {file = "regex-2021.4.4-cp38-cp38-win32.whl", hash = "sha256:a91aa8619b23b79bcbeb37abe286f2f408d2f2d6f29a17237afda55bb54e7aac"}, + {file = "regex-2021.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:c0502c0fadef0d23b128605d69b58edb2c681c25d44574fc673b0e52dce71ee2"}, + {file = "regex-2021.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:598585c9f0af8374c28edd609eb291b5726d7cbce16be6a8b95aa074d252ee17"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux1_i686.whl", hash = "sha256:ee54ff27bf0afaf4c3b3a62bcd016c12c3fdb4ec4f413391a90bd38bc3624605"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7d9884d86dd4dd489e981d94a65cd30d6f07203d90e98f6f657f05170f6324c9"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:bf5824bfac591ddb2c1f0a5f4ab72da28994548c708d2191e3b87dd207eb3ad7"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:563085e55b0d4fb8f746f6a335893bda5c2cef43b2f0258fe1020ab1dd874df8"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9c3db21af35e3b3c05764461b262d6f05bbca08a71a7849fd79d47ba7bc33ed"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:3916d08be28a1149fb97f7728fca1f7c15d309a9f9682d89d79db75d5e52091c"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:fd45ff9293d9274c5008a2054ecef86a9bfe819a67c7be1afb65e69b405b3042"}, + {file = "regex-2021.4.4-cp39-cp39-win32.whl", hash = "sha256:fa4537fb4a98fe8fde99626e4681cc644bdcf2a795038533f9f711513a862ae6"}, + {file = "regex-2021.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:97f29f57d5b84e73fbaf99ab3e26134e6687348e95ef6b48cfd2c06807005a07"}, + {file = "regex-2021.4.4.tar.gz", hash = "sha256:52ba3d3f9b942c49d7e4bc105bb28551c44065f139a65062ab7912bef10c9afb"}, +] +six = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] +toml = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] +tox = [ + {file = "tox-3.23.1-py2.py3-none-any.whl", hash = "sha256:b0b5818049a1c1997599d42012a637a33f24c62ab8187223fdd318fa8522637b"}, + {file = "tox-3.23.1.tar.gz", hash = "sha256:307a81ddb82bd463971a273f33e9533a24ed22185f27db8ce3386bff27d324e3"}, +] +typed-ast = [ + {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6"}, + {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075"}, + {file = "typed_ast-1.4.3-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528"}, + {file = "typed_ast-1.4.3-cp35-cp35m-win32.whl", hash = "sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428"}, + {file = "typed_ast-1.4.3-cp35-cp35m-win_amd64.whl", hash = "sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3"}, + {file = "typed_ast-1.4.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f"}, + {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341"}, + {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace"}, + {file = "typed_ast-1.4.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f"}, + {file = "typed_ast-1.4.3-cp36-cp36m-win32.whl", hash = "sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363"}, + {file = "typed_ast-1.4.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7"}, + {file = "typed_ast-1.4.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266"}, + {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e"}, + {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04"}, + {file = "typed_ast-1.4.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899"}, + {file = "typed_ast-1.4.3-cp37-cp37m-win32.whl", hash = "sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c"}, + {file = "typed_ast-1.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805"}, + {file = "typed_ast-1.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a"}, + {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff"}, + {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41"}, + {file = "typed_ast-1.4.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39"}, + {file = "typed_ast-1.4.3-cp38-cp38-win32.whl", hash = "sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927"}, + {file = "typed_ast-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40"}, + {file = "typed_ast-1.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3"}, + {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4"}, + {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0"}, + {file = "typed_ast-1.4.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3"}, + {file = "typed_ast-1.4.3-cp39-cp39-win32.whl", hash = "sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808"}, + {file = "typed_ast-1.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c"}, + {file = "typed_ast-1.4.3.tar.gz", hash = "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"}, +] +typing-extensions = [ + {file = "typing_extensions-3.10.0.0-py2-none-any.whl", hash = "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497"}, + {file = "typing_extensions-3.10.0.0-py3-none-any.whl", hash = "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"}, + {file = "typing_extensions-3.10.0.0.tar.gz", hash = "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342"}, +] +virtualenv = [ + {file = "virtualenv-20.4.6-py2.py3-none-any.whl", hash = "sha256:307a555cf21e1550885c82120eccaf5acedf42978fd362d32ba8410f9593f543"}, + {file = "virtualenv-20.4.6.tar.gz", hash = "sha256:72cf267afc04bf9c86ec932329b7e94db6a0331ae9847576daaa7ca3c86b29a4"}, +] +zipp = [ + {file = "zipp-3.4.1-py3-none-any.whl", hash = "sha256:51cb66cc54621609dd593d1787f286ee42a5c0adbb4b29abea5a63edc3e03098"}, + {file = "zipp-3.4.1.tar.gz", hash = "sha256:3607921face881ba3e026887d8150cca609d517579abe052ac81fc5aeffdbd76"}, +] diff --git a/pyproject.toml b/pyproject.toml index 2683f072..ac1ff4b9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,19 @@ +[tool.poetry] +name = "pytest-html" +description = "pytest plugin for generating HTML reports" +version = "4.0.0-next" +authors = ["Jim", "Victor"] + +[tool.poetry.dependencies] +python = ">=3.6" +Jinja2 = "^2.11.3" + +[tool.poetry.dev-dependencies] +black = "20.8b1" +flake8 = "3.8.4" +tox = "^3.23.1" +pytest = "^6.2.4" + [build-system] requires = [ "pip >= 19.3.1", diff --git a/setup.py b/setup.py index 5c01738b..dd2d6e5b 100644 --- a/setup.py +++ b/setup.py @@ -10,10 +10,10 @@ url="https://github.com/pytest-dev/pytest-html", package_dir={"": "src"}, packages=["pytest_html"], - package_data={"pytest_html": ["resources/*"]}, + package_data={"pytest_html": ["resources/*", "scripts/*"]}, entry_points={"pytest11": ["html = pytest_html.plugin"]}, setup_requires=["setuptools_scm"], - install_requires=["pytest>=5.0,!=6.0.0", "pytest-metadata"], + install_requires=["pytest>=5.0,!=6.0.0", "pytest-metadata", "jinja2>=3.0,<4.0"], license="Mozilla Public License 2.0 (MPL 2.0)", keywords="py.test pytest html report", python_requires=">=3.6", diff --git a/src/pytest_html/hooks.py b/src/pytest_html/hooks.py index 7b75120b..f81f9e25 100644 --- a/src/pytest_html/hooks.py +++ b/src/pytest_html/hooks.py @@ -4,20 +4,20 @@ def pytest_html_report_title(report): - """ Called before adding the title to the report """ + """Called before adding the title to the report""" def pytest_html_results_summary(prefix, summary, postfix): - """ Called before adding the summary section to the report """ + """Called before adding the summary section to the report""" def pytest_html_results_table_header(cells): - """ Called after building results table header. """ + """Called after building results table header.""" def pytest_html_results_table_row(report, cells): - """ Called after building results table row. """ + """Called after building results table row.""" def pytest_html_results_table_html(report, data): - """ Called after building results table additional HTML. """ + """Called after building results table additional HTML.""" diff --git a/src/pytest_html/nextgen.py b/src/pytest_html/nextgen.py index 716ad4e6..e054396f 100644 --- a/src/pytest_html/nextgen.py +++ b/src/pytest_html/nextgen.py @@ -1,24 +1,108 @@ +import datetime import json -from typing import Any -from typing import Dict +import os import pytest +import re +import shutil + +from . import __version__ +from .util import cleanup_unserializable, get_scripts + +from jinja2 import Environment, FileSystemLoader, PackageLoader + +from _pytest.pathlib import Path class NextGenReport: - def __init__(self, config, data_file): + def __init__(self, report_path, config): + _plugin_path = os.path.dirname(__file__) + self._report_absolute_path = Path(report_path).absolute() + self._report_path = self._report_absolute_path.parent + + self._assets_path = Path(self._report_path, "assets") + self._assets_path.mkdir(parents=True, exist_ok=True) + + self._scripts_dest_path = Path(self._report_path, "scripts") + self._scripts_dest_path.mkdir(parents=True, exist_ok=True) + self._scripts_src_path = Path(_plugin_path, "scripts") + + self._resources_path = Path(_plugin_path, "resources") + + self._default_css_path = Path(self._resources_path, "style.css") + self._config = config - self._data_file = data_file + self._data_file = Path(self._assets_path, "test_data.js") + self._template = None + self._template_filename = "index.jinja2" - self._title = "Next Gen Report" self._data = { - "title": self._title, "collectedItems": 0, "environment": {}, "tests": [], } - self._data_file.parent.mkdir(parents=True, exist_ok=True) + def _generate_environment(self): + metadata = self._config._metadata + for key in metadata.keys(): + value = metadata[key] + if self._is_redactable_environment_variable(key): + black_box_ascii_value = 0x2593 + metadata[key] = "".join(chr(black_box_ascii_value) for _ in str(value)) + + return metadata + + def _generate_report(self): + self._template = self._read_template() + + # Copy scripts + scripts_dest = shutil.copytree( + self._scripts_src_path, self._scripts_dest_path, dirs_exist_ok=True + ) + + # Copy default css file (style.css) to assets directory + new_css_path = shutil.copy(self._default_css_path, self._assets_path) + + generated = datetime.datetime.now() + css_files = [new_css_path] + self._config.getoption("css") + rendered_report = self._render_html( + "Test Report", + generated.strftime("%d-%b-%Y"), + generated.strftime("%H:%M:%S"), + __version__, + self._data_file.name, + css_files, + get_scripts(scripts_dest), + ) + + with self._report_absolute_path.open("w", encoding="utf-8") as f: + f.write(rendered_report) + + def _is_redactable_environment_variable(self, environment_variable): + redactable_regexes = self._config.getini("environment_table_redact_list") + for redactable_regex in redactable_regexes: + if re.match(redactable_regex, environment_variable): + return True + + return False + + def _read_template(self): + env = Environment( + loader=FileSystemLoader(self._resources_path), autoescape=True + ) + + return env.get_template(self._template_filename) + + def _render_html(self, title, date, time, version, data_file, styles, scripts): + return self._template.render( + title=title, + date=date, + time=time, + version=version, + data_file=data_file, + styles=styles, + scripts=scripts, + ) def _write(self): try: @@ -28,18 +112,17 @@ def _write(self): data = json.dumps(data) with self._data_file.open("w", buffering=1, encoding="UTF-8") as f: - f.write("const jsonData = ") - f.write(data) - f.write("\n") + f.write(f"const jsonData = {data}\n") @pytest.hookimpl(trylast=True) def pytest_sessionstart(self, session): config = session.config if hasattr(config, "_metadata") and config._metadata: - metadata = config._metadata - self._data["environment"] = metadata + self._data["environment"] = self._generate_environment() self._write() + self._generate_report() + @pytest.hookimpl(trylast=True) def pytest_collection_finish(self, session): self._data["collectedItems"] = len(session.items) @@ -51,24 +134,11 @@ def pytest_runtest_logreport(self, report): config=self._config, report=report ) - # rename to "extras" since list - if hasattr(report, "extra"): - for extra in report.extra: - print(extra) - if extra["mime_type"] is not None and "image" in extra["mime_type"]: - data.update({"extras": extra}) + # # TODO rename to "extras" since list + # if hasattr(report, "extra"): + # for extra in report.extra: + # if extra["mime_type"] is not None and "image" in extra["mime_type"]: + # data.update({"extras": extra}) self._data["tests"].append(data) self._write() - - -def cleanup_unserializable(d: Dict[str, Any]) -> Dict[str, Any]: - """Return new dict with entries that are not json serializable by their str().""" - result = {} - for k, v in d.items(): - try: - json.dumps({k: v}) - except TypeError: - v = str(v) - result[k] = v - return result diff --git a/src/pytest_html/plugin.py b/src/pytest_html/plugin.py index 3961bcde..e7da790b 100644 --- a/src/pytest_html/plugin.py +++ b/src/pytest_html/plugin.py @@ -81,10 +81,10 @@ def pytest_configure(config): if not hasattr(config, "workerinput"): # prevent opening htmlpath on worker nodes (xdist) config._html = HTMLReport(htmlpath, config) - - config._next_gen = NextGenReport(config, Path("nextgendata.js")) config.pluginmanager.register(config._html) - config.pluginmanager.register(config._next_gen) + + # config._next_gen = NextGenReport(htmlpath, config) + # config.pluginmanager.register(config._next_gen) def pytest_unconfigure(config): @@ -93,10 +93,10 @@ def pytest_unconfigure(config): del config._html config.pluginmanager.unregister(html) - next_gen = getattr(config, "_next_gen", None) - if next_gen: - del config._next_gen - config.pluginmanager.unregister(next_gen) + # next_gen = getattr(config, "_next_gen", None) + # if next_gen: + # del config._next_gen + # config.pluginmanager.unregister(next_gen) @pytest.hookimpl(tryfirst=True, hookwrapper=True) @@ -105,8 +105,10 @@ def pytest_runtest_makereport(item, call): report = outcome.get_result() if report.when == "call": fixture_extras = getattr(item.config, "extras", []) - plugin_extras = getattr(report, "extra", []) - report.extra = fixture_extras + plugin_extras + plugin_extras = getattr(report, "extras", []) + # print("fix: ", fixture_extras) + # print("plugin: ", plugin_extras) + report.extras = fixture_extras + plugin_extras @pytest.fixture diff --git a/src/pytest_html/resources/assets/snapshot_of_what_went_wrong.png b/src/pytest_html/resources/assets/snapshot_of_what_went_wrong.png deleted file mode 100644 index 1ee54a5af8ab3e5d707f85b6d38c9ea95bcb46bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 125033 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 diff --git a/src/pytest_html/resources/index.html b/src/pytest_html/resources/index.html index f75cf531..b6743462 100644 --- a/src/pytest_html/resources/index.html +++ b/src/pytest_html/resources/index.html @@ -1,100 +1,98 @@ - - - Test Report - - - -

-
-

Environment

-

Report generated on 17-Dec-2020 at 14:02:36 by pytest-html v1.22.0

- - - - + + + Test Report + + + +

+

Report generated on 17-Dec-2020 at 14:02:36 by pytest-html v1.22.0

+

Environment

+
+ + + + - - - - -
-
-

Summary

-

-

(Un)check the boxes to filter the results.

+ + + + +
+
+

Summary

+

+

(Un)check the boxes to filter the results.

- - - - - - - + + + + + + + - - -
-
-
-
There are still tests running.
Reload this page to ge the latest results!
-
-
-
-
+
+
+
+
There are still tests running.
Reload this page to ge the latest results!
-

Results

- + +
+
+ +

Results

+
-
- -
- - - - - - -
+ + +
+ + + + + + +
diff --git a/src/pytest_html/resources/index.jinja2 b/src/pytest_html/resources/index.jinja2 new file mode 100644 index 00000000..a0960e59 --- /dev/null +++ b/src/pytest_html/resources/index.jinja2 @@ -0,0 +1,99 @@ + + + + + + {{ title }} + {% for style in styles %} + + {% endfor %} + + +

{{ title }}

+

Report generated on {{ date }} at {{ time }} by pytest-html + v{{ version }}

+

Environment

+
+ + + + + + + + + +
+
+

Summary

+

+

(Un)check the boxes to filter the results.

+ + + + + + + + + +
+
+
+
There are still tests running.
Reload this page to ge the latest results!
+
+
+
+
+
+

Results

+ + +
+ +
+ + {% for script in scripts %} + + {% endfor %} +
+ diff --git a/src/pytest_html/resources/main.js b/src/pytest_html/resources/main.js index 63c26498..f26513c9 100644 --- a/src/pytest_html/resources/main.js +++ b/src/pytest_html/resources/main.js @@ -2,114 +2,245 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -function is_all_rows_hidden(value) { - return value.hidden == false; -} -function filter_table(elem) { - var outcome_att = 'data-test-result'; - var outcome = elem.getAttribute(outcome_att); - console.log(outcome); - class_outcome = outcome + ' results-table-row'; - var outcome_rows = document.getElementsByClassName(class_outcome); +function toArray(iter) { + if (iter === null) { + return null; + } + return Array.prototype.slice.call(iter); +} - for (var i = 0; i < outcome_rows.length; i++) { - outcome_rows[i].hidden = !elem.checked; - } +function find(selector, elem) { // eslint-disable-line no-redeclare + if (!elem) { + elem = document; + } + return elem.querySelector(selector); +} - var rows = find_all('.results-table-row').filter(is_all_rows_hidden); - var all_rows_hidden = rows.length == 0 ? true : false; - var not_found_message = document.getElementById('not-found-message'); - not_found_message.hidden = !all_rows_hidden; +function findAll(selector, elem) { + if (!elem) { + elem = document; + } + return toArray(elem.querySelectorAll(selector)); } -function toArray(iter) { - if (iter === null) { - return null; - } - return Array.prototype.slice.call(iter); +function sortColumn(elem) { + toggleSortStates(elem); + const colIndex = toArray(elem.parentNode.childNodes).indexOf(elem); + let key; + if (elem.classList.contains('result')) { + key = keyResult; + } else if (elem.classList.contains('links')) { + key = keyLink; + } else { + key = keyAlpha; + } + sortTable(elem, key(colIndex)); } -function showAllExtras() { - // eslint-disable-line no-unused-vars - findAll('.col-result').forEach(showExtras); +function showAllExtras() { // eslint-disable-line no-unused-vars + findAll('.col-result').forEach(showExtras); } -function hideAllExtras() { - // eslint-disable-line no-unused-vars - findAll('.col-result').forEach(hideExtras); +function hideAllExtras() { // eslint-disable-line no-unused-vars + findAll('.col-result').forEach(hideExtras); } function showExtras(colresultElem) { - const extras = colresultElem.parentNode.nextElementSibling; - const expandcollapse = colresultElem.firstElementChild; - extras.classList.remove('collapsed'); - expandcollapse.classList.remove('expander'); - expandcollapse.classList.add('collapser'); + const extras = colresultElem.parentNode.nextElementSibling; + const expandcollapse = colresultElem.firstElementChild; + extras.classList.remove('collapsed'); + expandcollapse.classList.remove('expander'); + expandcollapse.classList.add('collapser'); } function hideExtras(colresultElem) { - const extras = colresultElem.parentNode.nextElementSibling; - const expandcollapse = colresultElem.firstElementChild; - extras.classList.add('collapsed'); - expandcollapse.classList.remove('collapser'); - expandcollapse.classList.add('expander'); + const extras = colresultElem.parentNode.nextElementSibling; + const expandcollapse = colresultElem.firstElementChild; + extras.classList.add('collapsed'); + expandcollapse.classList.remove('collapser'); + expandcollapse.classList.add('expander'); } -function addCollapse() { - // Add links for show/hide all - const resulttable = find('table#results-table'); - const showhideall = document.createElement('p'); - showhideall.innerHTML = - 'Show all details / ' + - 'Hide all details'; - resulttable.parentElement.insertBefore(showhideall, resulttable); - - // Add show/hide link to each result - findAll('.col-result').forEach(function (elem) { - const collapsed = getQueryParameter('collapsed') || 'Passed'; - const extras = elem.parentNode.nextElementSibling; - const expandcollapse = document.createElement('span'); - if (extras.classList.contains('collapsed')) { - expandcollapse.classList.add('expander'); - } else if (collapsed.includes(elem.innerHTML)) { - extras.classList.add('collapsed'); - expandcollapse.classList.add('expander'); - } else { - expandcollapse.classList.add('collapser'); +function showFilters() { + let visibleString = getQueryParameter('visible') || 'all'; + visibleString = visibleString.toLowerCase(); + const checkedItems = visibleString.split(','); + + const filterItems = document.getElementsByClassName('filter'); + for (let i = 0; i < filterItems.length; i++) { + filterItems[i].hidden = false; + + if (visibleString != 'all') { + filterItems[i].checked = checkedItems.includes(filterItems[i].getAttribute('data-test-result')); + filterTable(filterItems[i]); + } } - elem.appendChild(expandcollapse); - - elem.addEventListener('click', function (event) { - if ( - event.currentTarget.parentNode.nextElementSibling.classList.contains( - 'collapsed' - ) - ) { - showExtras(event.currentTarget); - } else { - hideExtras(event.currentTarget); - } +} + +function addCollapse() { + // Add links for show/hide all + const resulttable = find('table#results-table'); + const showhideall = document.createElement('p'); + showhideall.innerHTML = 'Show all details / ' + + 'Hide all details'; + resulttable.parentElement.insertBefore(showhideall, resulttable); + + // Add show/hide link to each result + findAll('.col-result').forEach(function(elem) { + const collapsed = getQueryParameter('collapsed') || 'Passed'; + const extras = elem.parentNode.nextElementSibling; + const expandcollapse = document.createElement('span'); + if (extras.classList.contains('collapsed')) { + expandcollapse.classList.add('expander'); + } else if (collapsed.includes(elem.innerHTML)) { + extras.classList.add('collapsed'); + expandcollapse.classList.add('expander'); + } else { + expandcollapse.classList.add('collapser'); + } + elem.appendChild(expandcollapse); + + elem.addEventListener('click', function(event) { + if (event.currentTarget.parentNode.nextElementSibling.classList.contains('collapsed')) { + showExtras(event.currentTarget); + } else { + hideExtras(event.currentTarget); + } + }); }); - }); } function getQueryParameter(name) { - const match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search); - return match && decodeURIComponent(match[1].replace(/\+/g, ' ')); -} -function init() { - // eslint-disable-line no-unused-vars - setTimeout(() => { - findAll('input[name="filter_checkbox"]').forEach((elem) => { - elem.addEventListener('click', (evt) => { - const { target: element } = evt; - const { testResult } = element.dataset; - - doFilter(testResult, element.checked); - }); - }); - initRender(); + const match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search); + return match && decodeURIComponent(match[1].replace(/\+/g, ' ')); +} + +function init () { // eslint-disable-line no-unused-vars + resetSortHeaders(); + addCollapse(); - }); + + showFilters(); + + sortColumn(find('.initial-sort')); + + findAll('.sortable').forEach(function(elem) { + elem.addEventListener('click', + function() { + sortColumn(elem); + }, false); + }); +} + +function sortTable(clicked, keyFunc) { + const rows = findAll('.results-table-row'); + const reversed = !clicked.classList.contains('asc'); + const sortedRows = sort(rows, keyFunc, reversed); + /* Whole table is removed here because browsers acts much slower + * when appending existing elements. + */ + const thead = document.getElementById('results-table-head'); + document.getElementById('results-table').remove(); + const parent = document.createElement('table'); + parent.id = 'results-table'; + parent.appendChild(thead); + sortedRows.forEach(function(elem) { + parent.appendChild(elem); + }); + document.getElementsByTagName('BODY')[0].appendChild(parent); +} + +function sort(items, keyFunc, reversed) { + const sortArray = items.map(function(item, i) { + return [keyFunc(item), i]; + }); + + sortArray.sort(function(a, b) { + const keyA = a[0]; + const keyB = b[0]; + + if (keyA == keyB) return 0; + + if (reversed) { + return keyA < keyB ? 1 : -1; + } else { + return keyA > keyB ? 1 : -1; + } + }); + + return sortArray.map(function(item) { + const index = item[1]; + return items[index]; + }); +} + +function keyAlpha(colIndex) { + return function(elem) { + return elem.childNodes[1].childNodes[colIndex].firstChild.data.toLowerCase(); + }; +} + +function keyLink(colIndex) { + return function(elem) { + const dataCell = elem.childNodes[1].childNodes[colIndex].firstChild; + return dataCell == null ? '' : dataCell.innerText.toLowerCase(); + }; +} + +function keyResult(colIndex) { + return function(elem) { + const strings = ['Error', 'Failed', 'Rerun', 'XFailed', 'XPassed', + 'Skipped', 'Passed']; + return strings.indexOf(elem.childNodes[1].childNodes[colIndex].firstChild.data); + }; +} + +function resetSortHeaders() { + findAll('.sort-icon').forEach(function(elem) { + elem.parentNode.removeChild(elem); + }); + findAll('.sortable').forEach(function(elem) { + const icon = document.createElement('div'); + icon.className = 'sort-icon'; + icon.textContent = 'vvv'; + elem.insertBefore(icon, elem.firstChild); + elem.classList.remove('desc', 'active'); + elem.classList.add('asc', 'inactive'); + }); +} + +function toggleSortStates(elem) { + //if active, toggle between asc and desc + if (elem.classList.contains('active')) { + elem.classList.toggle('asc'); + elem.classList.toggle('desc'); + } + + //if inactive, reset all other functions and add ascending active + if (elem.classList.contains('inactive')) { + resetSortHeaders(); + elem.classList.remove('inactive'); + elem.classList.add('active'); + } +} + +function isAllRowsHidden(value) { + return value.hidden == false; +} + +function filterTable(elem) { // eslint-disable-line no-unused-vars + const outcomeAtt = 'data-test-result'; + const outcome = elem.getAttribute(outcomeAtt); + const classOutcome = outcome + ' results-table-row'; + const outcomeRows = document.getElementsByClassName(classOutcome); + + for(let i = 0; i < outcomeRows.length; i++){ + outcomeRows[i].hidden = !elem.checked; + } + + const rows = findAll('.results-table-row').filter(isAllRowsHidden); + const allRowsHidden = rows.length == 0 ? true : false; + const notFoundMessage = document.getElementById('not-found-message'); + notFoundMessage.hidden = !allRowsHidden; } diff --git a/src/pytest_html/resources/dom.js b/src/pytest_html/scripts/dom.js similarity index 100% rename from src/pytest_html/resources/dom.js rename to src/pytest_html/scripts/dom.js diff --git a/src/pytest_html/resources/events.js b/src/pytest_html/scripts/events.js similarity index 100% rename from src/pytest_html/resources/events.js rename to src/pytest_html/scripts/events.js diff --git a/src/pytest_html/resources/index.js b/src/pytest_html/scripts/index.js similarity index 100% rename from src/pytest_html/resources/index.js rename to src/pytest_html/scripts/index.js diff --git a/src/pytest_html/scripts/main.js b/src/pytest_html/scripts/main.js new file mode 100644 index 00000000..9419fe76 --- /dev/null +++ b/src/pytest_html/scripts/main.js @@ -0,0 +1,115 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +function is_all_rows_hidden(value) { + return value.hidden == false; +} + +function filter_table(elem) { + var outcome_att = 'data-test-result'; + var outcome = elem.getAttribute(outcome_att); + console.log(outcome); + class_outcome = outcome + ' results-table-row'; + var outcome_rows = document.getElementsByClassName(class_outcome); + + for (var i = 0; i < outcome_rows.length; i++) { + outcome_rows[i].hidden = !elem.checked; + } + + var rows = find_all('.results-table-row').filter(is_all_rows_hidden); + var all_rows_hidden = rows.length == 0 ? true : false; + var not_found_message = document.getElementById('not-found-message'); + not_found_message.hidden = !all_rows_hidden; +} + +function toArray(iter) { + if (iter === null) { + return null; + } + return Array.prototype.slice.call(iter); +} + +function showAllExtras() { + // eslint-disable-line no-unused-vars + findAll('.col-result').forEach(showExtras); +} + +function hideAllExtras() { + // eslint-disable-line no-unused-vars + findAll('.col-result').forEach(hideExtras); +} + +function showExtras(colresultElem) { + const extras = colresultElem.parentNode.nextElementSibling; + const expandcollapse = colresultElem.firstElementChild; + extras.classList.remove('collapsed'); + expandcollapse.classList.remove('expander'); + expandcollapse.classList.add('collapser'); +} + +function hideExtras(colresultElem) { + const extras = colresultElem.parentNode.nextElementSibling; + const expandcollapse = colresultElem.firstElementChild; + extras.classList.add('collapsed'); + expandcollapse.classList.remove('collapser'); + expandcollapse.classList.add('expander'); +} + +function addCollapse() { + // Add links for show/hide all + const resulttable = find('table#results-table'); + const showhideall = document.createElement('p'); + showhideall.innerHTML = + 'Show all details / ' + + 'Hide all details'; + resulttable.parentElement.insertBefore(showhideall, resulttable); + + // Add show/hide link to each result + findAll('.col-result').forEach(function (elem) { + const collapsed = getQueryParameter('collapsed') || 'Passed'; + const extras = elem.parentNode.nextElementSibling; + const expandcollapse = document.createElement('span'); + if (extras.classList.contains('collapsed')) { + expandcollapse.classList.add('expander'); + } else if (collapsed.includes(elem.innerHTML)) { + extras.classList.add('collapsed'); + expandcollapse.classList.add('expander'); + } else { + expandcollapse.classList.add('collapser'); + } + elem.appendChild(expandcollapse); + + elem.addEventListener('click', function (event) { + if ( + event.currentTarget.parentNode.nextElementSibling.classList.contains( + 'collapsed' + ) + ) { + showExtras(event.currentTarget); + } else { + hideExtras(event.currentTarget); + } + }); + }); +} + +function getQueryParameter(name) { + const match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search); + return match && decodeURIComponent(match[1].replace(/\+/g, ' ')); +} +function init() { + // eslint-disable-line no-unused-vars + setTimeout(() => { + findAll('input[name="filter_checkbox"]').forEach((elem) => { + elem.addEventListener('click', (evt) => { + const { target: element } = evt; + const { testResult } = element.dataset; + + doFilter(testResult, element.checked); + }); + }); + initRender(); + addCollapse(); + }); +} diff --git a/src/pytest_html/resources/sort.js b/src/pytest_html/scripts/sort.js similarity index 100% rename from src/pytest_html/resources/sort.js rename to src/pytest_html/scripts/sort.js diff --git a/src/pytest_html/util.py b/src/pytest_html/util.py index 37259ec7..4e52992d 100644 --- a/src/pytest_html/util.py +++ b/src/pytest_html/util.py @@ -1,6 +1,12 @@ import importlib +import json +import os + from functools import lru_cache +from typing import Any +from typing import Dict + @lru_cache() def ansi_support(): @@ -10,3 +16,23 @@ def ansi_support(): except ImportError: # ansi2html is not installed pass + + +def cleanup_unserializable(d: Dict[str, Any]) -> Dict[str, Any]: + """Return new dict with entries that are not json serializable by their str().""" + result = {} + for k, v in d.items(): + try: + json.dumps({k: v}) + except TypeError: + v = str(v) + result[k] = v + return result + + +def get_scripts(scripts_path): + return [ + f + for f in os.listdir(scripts_path) + if os.path.isfile(os.path.join(scripts_path, f)) + ] From a52a505a02be1bd7f76e624e7563608c499af56d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jim=20Br=C3=A4nnlund?= Date: Sun, 16 Jan 2022 20:34:06 +0100 Subject: [PATCH 10/60] split nextgen class --- poetry.lock | 33 ++++- src/pytest_html/nextgen.py | 163 ++++++++++++++++--------- src/pytest_html/plugin.py | 38 +++--- src/pytest_html/resources/index.jinja2 | 38 ++++-- 4 files changed, 187 insertions(+), 85 deletions(-) diff --git a/poetry.lock b/poetry.lock index 603a275f..05d108a2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -28,6 +28,21 @@ docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"] tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"] +[[package]] +name = "beautifulsoup4" +version = "4.10.0" +description = "Screen-scraping library" +category = "main" +optional = false +python-versions = ">3.0.0" + +[package.dependencies] +soupsieve = ">1.2" + +[package.extras] +html5lib = ["html5lib"] +lxml = ["lxml"] + [[package]] name = "black" version = "20.8b1" @@ -288,6 +303,14 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +[[package]] +name = "soupsieve" +version = "2.3.1" +description = "A modern CSS selector implementation for Beautiful Soup." +category = "main" +optional = false +python-versions = ">=3.6" + [[package]] name = "toml" version = "0.10.2" @@ -370,7 +393,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pyt [metadata] lock-version = "1.1" python-versions = ">=3.6" -content-hash = "d36eaa646e026d7c80966befc4950d025bf17c0736c2e76f70476150dc26429c" +content-hash = "bd5e8b96c18260977691034fd96f13216c4e51f61a5112e61666db19a9559ec8" [metadata.files] appdirs = [ @@ -385,6 +408,10 @@ attrs = [ {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, ] +beautifulsoup4 = [ + {file = "beautifulsoup4-4.10.0-py3-none-any.whl", hash = "sha256:9a315ce70049920ea4572a4055bc4bd700c940521d36fc858205ad4fcde149bf"}, + {file = "beautifulsoup4-4.10.0.tar.gz", hash = "sha256:c23ad23c521d818955a4151a67d81580319d4bf548d3d49f4223ae041ff98891"}, +] black = [ {file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"}, ] @@ -551,6 +578,10 @@ six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] +soupsieve = [ + {file = "soupsieve-2.3.1-py3-none-any.whl", hash = "sha256:1a3cca2617c6b38c0343ed661b1fa5de5637f257d4fe22bd9f1338010a1efefb"}, + {file = "soupsieve-2.3.1.tar.gz", hash = "sha256:b8d49b1cd4f037c7082a9683dfa1801aa2597fb11c3a1155b7a5b94829b4f1f9"}, +] toml = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, diff --git a/src/pytest_html/nextgen.py b/src/pytest_html/nextgen.py index e054396f..5b5ae913 100644 --- a/src/pytest_html/nextgen.py +++ b/src/pytest_html/nextgen.py @@ -9,30 +9,20 @@ from . import __version__ from .util import cleanup_unserializable, get_scripts -from jinja2 import Environment, FileSystemLoader, PackageLoader +from jinja2 import Environment, FileSystemLoader from _pytest.pathlib import Path -class NextGenReport: +class BaseReport(object): def __init__(self, report_path, config): _plugin_path = os.path.dirname(__file__) self._report_absolute_path = Path(report_path).absolute() self._report_path = self._report_absolute_path.parent - - self._assets_path = Path(self._report_path, "assets") - self._assets_path.mkdir(parents=True, exist_ok=True) - - self._scripts_dest_path = Path(self._report_path, "scripts") - self._scripts_dest_path.mkdir(parents=True, exist_ok=True) + self._report_path.mkdir(parents=True, exist_ok=True) self._scripts_src_path = Path(_plugin_path, "scripts") - self._resources_path = Path(_plugin_path, "resources") - - self._default_css_path = Path(self._resources_path, "style.css") - self._config = config - self._data_file = Path(self._assets_path, "test_data.js") self._template = None self._template_filename = "index.jinja2" @@ -42,6 +32,12 @@ def __init__(self, report_path, config): "tests": [], } + def _write_test_data(self): + pass + + def _generate_report(self): + pass + def _generate_environment(self): metadata = self._config._metadata for key in metadata.keys(): @@ -52,32 +48,6 @@ def _generate_environment(self): return metadata - def _generate_report(self): - self._template = self._read_template() - - # Copy scripts - scripts_dest = shutil.copytree( - self._scripts_src_path, self._scripts_dest_path, dirs_exist_ok=True - ) - - # Copy default css file (style.css) to assets directory - new_css_path = shutil.copy(self._default_css_path, self._assets_path) - - generated = datetime.datetime.now() - css_files = [new_css_path] + self._config.getoption("css") - rendered_report = self._render_html( - "Test Report", - generated.strftime("%d-%b-%Y"), - generated.strftime("%H:%M:%S"), - __version__, - self._data_file.name, - css_files, - get_scripts(scripts_dest), - ) - - with self._report_absolute_path.open("w", encoding="utf-8") as f: - f.write(rendered_report) - def _is_redactable_environment_variable(self, environment_variable): redactable_regexes = self._config.getini("environment_table_redact_list") for redactable_regex in redactable_regexes: @@ -86,47 +56,55 @@ def _is_redactable_environment_variable(self, environment_variable): return False - def _read_template(self): + def _read_template(self, search_paths): env = Environment( - loader=FileSystemLoader(self._resources_path), autoescape=True + loader=FileSystemLoader(search_paths), + autoescape=True, ) return env.get_template(self._template_filename) - def _render_html(self, title, date, time, version, data_file, styles, scripts): + def _render_html( + self, + title, + date, + time, + version, + styles, + scripts, + self_contained, + data_file=None, + test_data=None, + ): return self._template.render( title=title, date=date, time=time, version=version, - data_file=data_file, styles=styles, scripts=scripts, + self_contained=self_contained, + data_file=data_file, + test_data=test_data, ) - def _write(self): - try: - data = json.dumps(self._data) - except TypeError: - data = cleanup_unserializable(self._data) - data = json.dumps(data) - - with self._data_file.open("w", buffering=1, encoding="UTF-8") as f: - f.write(f"const jsonData = {data}\n") + def _write_report(self, rendered_report): + with self._report_absolute_path.open("w", encoding="utf-8") as f: + f.write(rendered_report) @pytest.hookimpl(trylast=True) def pytest_sessionstart(self, session): config = session.config if hasattr(config, "_metadata") and config._metadata: self._data["environment"] = self._generate_environment() - self._write() + self._write_test_data() self._generate_report() @pytest.hookimpl(trylast=True) def pytest_collection_finish(self, session): self._data["collectedItems"] = len(session.items) - self._write() + self._write_test_data() @pytest.hookimpl(trylast=True) def pytest_runtest_logreport(self, report): @@ -141,4 +119,79 @@ def pytest_runtest_logreport(self, report): # data.update({"extras": extra}) self._data["tests"].append(data) - self._write() + self._write_test_data() + self._generate_report() + + +class NextGenReport(BaseReport): + def __init__(self, report_path, config): + super(NextGenReport, self).__init__(report_path, config) + self._assets_path = Path(self._report_path, "assets") + self._assets_path.mkdir(parents=True, exist_ok=True) + self._scripts_dest_path = Path(self._report_path, "scripts") + self._scripts_dest_path.mkdir(parents=True, exist_ok=True) + self._default_css_path = Path(self._resources_path, "style.css") + self._data_file = Path(self._assets_path, "test_data.js") + + def _generate_report(self): + self._template = self._read_template( + [self._scripts_src_path, self._resources_path, self._assets_path] + ) + + # Copy scripts + scripts_dest = shutil.copytree( + self._scripts_src_path, self._scripts_dest_path, dirs_exist_ok=True + ) + + # Copy default css file (style.css) to assets directory + new_css_path = shutil.copy(self._default_css_path, self._assets_path) + + generated = datetime.datetime.now() + css_files = [new_css_path] + self._config.getoption("css") + rendered_report = self._render_html( + "Test Report", + generated.strftime("%d-%b-%Y"), + generated.strftime("%H:%M:%S"), + __version__, + css_files, + get_scripts(scripts_dest), + self_contained=False, + data_file=self._data_file.name, + ) + + self._write_report(rendered_report) + + def _write_test_data(self): + try: + data = json.dumps(self._data) + except TypeError: + data = cleanup_unserializable(self._data) + data = json.dumps(data) + + with self._data_file.open("w", buffering=1, encoding="UTF-8") as f: + f.write(f"const jsonData = {data}\n") + + +class NextGenSelfContainedReport(BaseReport): + def __init__(self, report_path, config): + super(NextGenSelfContainedReport, self).__init__(report_path, config) + + def _generate_report(self): + self._template = self._read_template( + [self._scripts_src_path, self._resources_path] + ) + + generated = datetime.datetime.now() + css_files = ["style.css"] + self._config.getoption("css") + rendered_report = self._render_html( + "Test Report", + generated.strftime("%d-%b-%Y"), + generated.strftime("%H:%M:%S"), + __version__, + css_files, + get_scripts(self._scripts_src_path), + self_contained=True, + test_data=cleanup_unserializable(self._data), + ) + + self._write_report(rendered_report) diff --git a/src/pytest_html/plugin.py b/src/pytest_html/plugin.py index e7da790b..e9086cdb 100644 --- a/src/pytest_html/plugin.py +++ b/src/pytest_html/plugin.py @@ -8,7 +8,7 @@ from . import extras # noqa: F401 from .html_report import HTMLReport -from .nextgen import NextGenReport +from .nextgen import NextGenReport, NextGenSelfContainedReport def pytest_addhooks(pluginmanager): @@ -64,27 +64,31 @@ def pytest_addoption(parser): def pytest_configure(config): - htmlpath = config.getoption("htmlpath") - if htmlpath: + html_path = config.getoption("htmlpath") + if html_path: missing_css_files = [] - for csspath in config.getoption("css"): - if not os.path.exists(csspath): - missing_css_files.append(csspath) + for css_path in config.getoption("css"): + if not os.path.exists(css_path): + missing_css_files.append(css_path) if missing_css_files: - oserror = ( + os_error = ( f"Missing CSS file{'s' if len(missing_css_files) > 1 else ''}:" f" {', '.join(missing_css_files)}" ) - raise OSError(oserror) + raise OSError(os_error) if not hasattr(config, "workerinput"): # prevent opening htmlpath on worker nodes (xdist) - config._html = HTMLReport(htmlpath, config) - config.pluginmanager.register(config._html) + # config._html = HTMLReport(htmlpath, config) + # config.pluginmanager.register(config._html) - # config._next_gen = NextGenReport(htmlpath, config) - # config.pluginmanager.register(config._next_gen) + if config.getoption("self_contained_html"): + config._next_gen = NextGenSelfContainedReport(html_path, config) + else: + config._next_gen = NextGenReport(html_path, config) + + config.pluginmanager.register(config._next_gen) def pytest_unconfigure(config): @@ -93,10 +97,10 @@ def pytest_unconfigure(config): del config._html config.pluginmanager.unregister(html) - # next_gen = getattr(config, "_next_gen", None) - # if next_gen: - # del config._next_gen - # config.pluginmanager.unregister(next_gen) + next_gen = getattr(config, "_next_gen", None) + if next_gen: + del config._next_gen + config.pluginmanager.unregister(next_gen) @pytest.hookimpl(tryfirst=True, hookwrapper=True) @@ -106,8 +110,6 @@ def pytest_runtest_makereport(item, call): if report.when == "call": fixture_extras = getattr(item.config, "extras", []) plugin_extras = getattr(report, "extras", []) - # print("fix: ", fixture_extras) - # print("plugin: ", plugin_extras) report.extras = fixture_extras + plugin_extras diff --git a/src/pytest_html/resources/index.jinja2 b/src/pytest_html/resources/index.jinja2 index a0960e59..613169b5 100644 --- a/src/pytest_html/resources/index.jinja2 +++ b/src/pytest_html/resources/index.jinja2 @@ -4,9 +4,17 @@ {{ title }} - {% for style in styles %} - - {% endfor %} + {% if self_contained %} + + {% else %} + {% for style in styles -%} + + {% endfor -%} + {% endif %}

{{ title }}

@@ -29,7 +37,6 @@ - - +
@@ -113,6 +121,7 @@ {% for p in postfix %} {{ p|safe }} {% endfor %} +
diff --git a/src/pytest_html/resources/style.css b/src/pytest_html/resources/style.css index 93bd1ead..20fe0a18 100644 --- a/src/pytest_html/resources/style.css +++ b/src/pytest_html/resources/style.css @@ -168,18 +168,21 @@ div.media { display: none; } -.expander::after { - content: " (show details)"; +.col-result { + cursor: pointer; +} +.col-result:hover::after { color: #bbb; font-style: italic; cursor: pointer; } -.collapser::after { +.col-result.collapser:hover::after { content: " (hide details)"; - color: #bbb; - font-style: italic; - cursor: pointer; +} + +.col-result.expander:hover::after { + content: " (show details)"; } /*------------------ diff --git a/src/pytest_html/scripts/datamanager.js b/src/pytest_html/scripts/datamanager.js index 42c7f71f..7d6e7cef 100644 --- a/src/pytest_html/scripts/datamanager.js +++ b/src/pytest_html/scripts/datamanager.js @@ -1,7 +1,12 @@ class DataManager { setManager(data) { - this.data = { ...data } - this.renderData = { ...data } + const dataBlob = { ...data, tests: data.tests.map((test, index) => ({ + ...test, + id: `test_${index}`, + collapsed: false, + })) } + this.data = { ...dataBlob } + this.renderData = { ...dataBlob } } get allData() { return { ...this.data } @@ -10,11 +15,15 @@ class DataManager { this.renderData = { ...this.data } } setRender(data) { - this.renderData.tests = data + this.renderData.tests = [...data] + } + toggleCollapsedItem(id) { + this.renderData.tests = this.renderData.tests.map((test) => + test.id === id ? { ...test, collapsed: !test.collapsed } : test, + ) } - set allCollapsed(collapsed) { - this.renderData = { ...this.data, tests: [...this.data.tests.map((test) => ( + this.renderData = { ...this.renderData, tests: [...this.renderData.tests.map((test) => ( { ...test, collapsed } ))] } } diff --git a/src/pytest_html/scripts/dom.js b/src/pytest_html/scripts/dom.js index 6b4f8d41..0dce623a 100644 --- a/src/pytest_html/scripts/dom.js +++ b/src/pytest_html/scripts/dom.js @@ -1,6 +1,8 @@ +const storageModule = require('./storage.js') const { formatDuration } = require('./utils.js') const mediaViewer = require('./mediaViewer.js') const templateEnvRow = document.querySelector('#template_environment_row') +const templateCollGroup = document.querySelector('#template_table-colgroup') const templateResult = document.querySelector('#template_results-table__tbody') const aTag = document.querySelector('#template_a') const listHeader = document.querySelector('#template_results-table__head') @@ -48,8 +50,8 @@ const dom = { }, getListHeader: ({ resultsTableHeader }) => { const header = listHeader.content.cloneNode(true) - const sortAttr = localStorage.getItem('sort') - const sortAsc = JSON.parse(localStorage.getItem('sortAsc')) + const sortAttr = storageModule.getSort() + const sortAsc = JSON.parse(storageModule.getSortDirection()) const sortables = ['outcome', 'nodeid', 'duration'] sortables.forEach((sortCol) => { @@ -64,11 +66,14 @@ const dom = { return header }, getListHeaderEmpty: () => listHeaderEmpty.content.cloneNode(true), - getResultTBody: ({ nodeid, longreprtext, duration, extras, resultsTableRow, tableHtml, outcome, collapsed }) => { + getColGroup: () => templateCollGroup.content.cloneNode(true), + getResultTBody: ({ nodeid, id, longreprtext, duration, extras, resultsTableRow, tableHtml, outcome, collapsed }) => { const outcomeLower = outcome.toLowerCase() const resultBody = templateResult.content.cloneNode(true) resultBody.querySelector('tbody').classList.add(outcomeLower) resultBody.querySelector('.col-result').innerText = outcome + resultBody.querySelector('.col-result').classList.add(`${collapsed ? 'expander' : 'collapser'}`) + resultBody.querySelector('.col-result').dataset.id = id resultBody.querySelector('.col-name').innerText = nodeid resultBody.querySelector('.col-duration').innerText = `${formatDuration(duration)}s` diff --git a/src/pytest_html/scripts/filter.js b/src/pytest_html/scripts/filter.js index 52ad0208..bfb994eb 100644 --- a/src/pytest_html/scripts/filter.js +++ b/src/pytest_html/scripts/filter.js @@ -1,28 +1,27 @@ const { manager } = require('./datamanager.js') -const localStorageModule = require('./localstorage_utils.js') +const storageModule = require('./storage.js') const getFilteredSubSet = (filter) => - manager.allData.tests.filter(({ outcome }) => !filter.includes(outcome)) + manager.allData.tests.filter(({ outcome }) => !filter.includes(outcome.toLowerCase())) const doInitFilter = () => { - const currentFilter = localStorageModule.getFilter() + const currentFilter = storageModule.getFilter() const filteredSubset = getFilteredSubSet(currentFilter) manager.setRender(filteredSubset) } const doFilter = (type, apply) => { - const currentFilter = localStorageModule.getFilter() + const currentFilter = storageModule.getFilter() if (!apply) { currentFilter.push(type) } else { - const index = currentFilter.indexOf(type); + const index = currentFilter.indexOf(type) if (index > -1) { currentFilter.splice(index, 1) } } - localStorageModule.setFilter(currentFilter) - + storageModule.setFilter(currentFilter) if (currentFilter.length) { const filteredSubset = getFilteredSubSet(currentFilter) manager.setRender(filteredSubset) diff --git a/src/pytest_html/scripts/index.js b/src/pytest_html/scripts/index.js index e49ce8b3..7fcab5fe 100644 --- a/src/pytest_html/scripts/index.js +++ b/src/pytest_html/scripts/index.js @@ -1,4 +1,4 @@ -const { redraw } = require('./main.js') +const { redraw, bindEvents } = require('./main.js') const { doInitFilter } = require('./filter.js') const { doInitSort } = require('./sort.js') const { manager } = require('./datamanager.js') @@ -9,6 +9,7 @@ function init() { doInitFilter() doInitSort() redraw() + bindEvents() } init() diff --git a/src/pytest_html/scripts/localstorage_utils.js b/src/pytest_html/scripts/localstorage_utils.js deleted file mode 100644 index 25424294..00000000 --- a/src/pytest_html/scripts/localstorage_utils.js +++ /dev/null @@ -1,22 +0,0 @@ -const getFilter = () => [...new Set(JSON.parse(localStorage.getItem('filter')))] -const setFilter = (currentFilter) => { - localStorage.setItem('filter', JSON.stringify(currentFilter)) -} - -const getSort = () => localStorage.getItem('sort') -const setSort = (type) => localStorage.setItem('sort', type) - -const getSortDirection = () => JSON.parse(localStorage.getItem('sortAsc')) - -const setSortDirection = (ascending) => { - localStorage.setItem('sortAsc', ascending) -} - -module.exports = { - getFilter, - setFilter, - getSort, - getSortDirection, - setSort, - setSortDirection, -} diff --git a/src/pytest_html/scripts/main.js b/src/pytest_html/scripts/main.js index 009e4416..23898611 100644 --- a/src/pytest_html/scripts/main.js +++ b/src/pytest_html/scripts/main.js @@ -3,7 +3,7 @@ const { dom, findAll } = require('./dom.js') const { manager } = require('./datamanager.js') const { doSort } = require('./sort.js') const { doFilter } = require('./filter.js') -const { getFilter } = require('./localstorage_utils.js') +const { getFilter } = require('./storage.js') const removeChildren = (node) => { while (node.firstChild) { @@ -37,7 +37,7 @@ const renderContent = (tests) => { if (!rows.length) { tableHeader.appendChild(dom.getListHeaderEmpty()) } - + table.appendChild(dom.getColGroup()) table.appendChild(tableHeader) rows.forEach((row) => !!row && table.appendChild(row)) @@ -45,6 +45,20 @@ const renderContent = (tests) => { table.querySelectorAll('.extra').forEach((item) => { item.colSpan = document.querySelectorAll('th').length }) + findAll('.sortable').forEach((elem) => { + elem.addEventListener('click', (evt) => { + const { target: element } = evt + const { columnType } = element.dataset + doSort(columnType) + redraw() + }) + }) + findAll('.col-result').forEach((elem) => { + elem.addEventListener('click', ({ target }) => { + manager.toggleCollapsedItem(target.dataset.id) + redraw() + }) + }) } const renderDerived = (tests, collectedItems, isFinished) => { @@ -86,23 +100,16 @@ const renderDerived = (tests, collectedItems, isFinished) => { } const bindEvents = () => { - findAll('.sortable').forEach((elem) => { - elem.addEventListener('click', (evt) => { - const { target: element } = evt - const { columnType } = element.dataset + const filterColumn = (evt) => { + const { target: element } = evt + const { testResult } = element.dataset - doSort(columnType) - redraw() - }) - }) + doFilter(testResult, element.checked) + redraw() + } findAll('input[name="filter_checkbox"]').forEach((elem) => { - elem.addEventListener('click', (evt) => { - const { target: element } = evt - const { testResult } = element.dataset - - doFilter(testResult, element.checked) - redraw() - }) + elem.removeEventListener('click', filterColumn) + elem.addEventListener('click', filterColumn) }) document.querySelector('#show_all_details').addEventListener('click', () => { manager.allCollapsed = false @@ -114,7 +121,7 @@ const bindEvents = () => { }) } -const renderPage = () => { +const redraw = () => { const { testSubset, allTests, collectedItems, isFinished } = manager renderStatic() @@ -122,11 +129,5 @@ const renderPage = () => { renderDerived(allTests, collectedItems, isFinished) } -const redraw = () => { - setTimeout(() => { - renderPage() - bindEvents() - }, 0) -} - exports.redraw = redraw +exports.bindEvents = bindEvents diff --git a/src/pytest_html/scripts/sort.js b/src/pytest_html/scripts/sort.js index 1711ea2e..aee74719 100644 --- a/src/pytest_html/scripts/sort.js +++ b/src/pytest_html/scripts/sort.js @@ -1,5 +1,5 @@ const { manager } = require('./datamanager.js') -const localStorageModule = require('./localstorage_utils.js') +const storageModule = require('./storage.js') const genericSort = (list, key, ascending) => { const sorted = list.sort((a, b) => a[key] === b[key] ? 0 : a[key] > b[key] ? 1 : -1) @@ -11,19 +11,19 @@ const genericSort = (list, key, ascending) => { } const doInitSort = () => { - const type = localStorageModule.getSort() - const ascending = localStorageModule.getSortDirection() + const type = storageModule.getSort() + const ascending = storageModule.getSortDirection() const list = manager.testSubset const sortedList = genericSort(list, type, ascending) manager.setRender(sortedList) } const doSort = (type) => { - const newSortType = localStorageModule.getSort() !== type - const currentAsc = localStorageModule.getSortDirection() + const newSortType = storageModule.getSort() !== type + const currentAsc = storageModule.getSortDirection() const ascending = newSortType ? true : !currentAsc - localStorageModule.setSort(type) - localStorageModule.setSortDirection(ascending) + storageModule.setSort(type) + storageModule.setSortDirection(ascending) const list = manager.testSubset const sortedList = genericSort(list, type, ascending) diff --git a/src/pytest_html/scripts/storage.js b/src/pytest_html/scripts/storage.js new file mode 100644 index 00000000..794806e3 --- /dev/null +++ b/src/pytest_html/scripts/storage.js @@ -0,0 +1,22 @@ +const getFilter = () => [...new Set(JSON.parse(sessionStorage.getItem('filter')))] +const setFilter = (currentFilter) => { + sessionStorage.setItem('filter', JSON.stringify(currentFilter)) +} + +const getSort = () => sessionStorage.getItem('sort') +const setSort = (type) => sessionStorage.setItem('sort', type) + +const getSortDirection = () => JSON.parse(sessionStorage.getItem('sortAsc')) + +const setSortDirection = (ascending) => { + sessionStorage.setItem('sortAsc', ascending) +} + +module.exports = { + getFilter, + setFilter, + getSort, + getSortDirection, + setSort, + setSortDirection, +} diff --git a/testing/unittest.js b/testing/unittest.js index 530a6c24..0429fdb8 100644 --- a/testing/unittest.js +++ b/testing/unittest.js @@ -3,75 +3,83 @@ const sinon = require('sinon') const { doInitFilter, doFilter } = require('../src/pytest_html/scripts/filter.js') const { doInitSort, doSort } = require('../src/pytest_html/scripts/sort.js') const dataModule = require('../src/pytest_html/scripts/datamanager.js') -const localStorageModule = require('../src/pytest_html/scripts/localstorage_utils.js') - +const storageModule = require('../src/pytest_html/scripts/storage.js') describe('Filter tests', () => { - let getFilterMock, managerSpy + let getFilterMock + let managerSpy before(() => { const jsonDatan = { - "tests": + 'tests': [ { - "outcome": "passed", + 'id': 'passed_1', + 'outcome': 'passed', }, { - "outcome": "failed", + 'id': 'failed_2', + 'outcome': 'failed', }, { - "outcome": "passed", + 'id': 'passed_3', + 'outcome': 'passed', }, { - "outcome": "passed", + 'id': 'passed_4', + 'outcome': 'passed', }, { - "outcome": "passed", + 'id': 'passed_5', + 'outcome': 'passed', }, { - "outcome": "passed", - } - ] + 'id': 'passed_6', + 'outcome': 'passed', + }, + ], } dataModule.manager.setManager(jsonDatan) }) - afterEach(() => [getFilterMock, managerSpy].forEach( fn => fn.restore())) - after(() => dataModule.manager.setManager({})) + afterEach(() => [getFilterMock, managerSpy].forEach((fn) => fn.restore())) + after(() => dataModule.manager.setManager({ tests: [] })) describe('doInitFilter', () => { - - - it("has no stored filters", () => { - getFilterMock = sinon.stub(localStorageModule, 'getFilter').returns([]) + it('has no stored filters', () => { + getFilterMock = sinon.stub(storageModule, 'getFilter').returns([]) managerSpy = sinon.spy(dataModule.manager, 'setRender') doInitFilter() expect(managerSpy.callCount).to.eql(1) - expect(dataModule.manager.testSubset.map(({outcome}) => outcome)).to.eql([ 'passed', 'failed', 'passed', 'passed', 'passed', 'passed' ]) + expect(dataModule.manager.testSubset.map(({ outcome }) => outcome)).to.eql([ + 'passed', 'failed', 'passed', 'passed', 'passed', 'passed', + ]) }) - it("exclude passed", () => { - getFilterMock = sinon.stub(localStorageModule, 'getFilter').returns(['passed']) + it('exclude passed', () => { + getFilterMock = sinon.stub(storageModule, 'getFilter').returns(['passed']) managerSpy = sinon.spy(dataModule.manager, 'setRender') doInitFilter() expect(managerSpy.callCount).to.eql(1) - expect(dataModule.manager.testSubset.map(({outcome}) => outcome)).to.eql([ 'failed' ]) + expect(dataModule.manager.testSubset.map(({ outcome }) => outcome)).to.eql(['failed']) }) }) describe('doFilter', () => { - var setFilterMock + let setFilterMock afterEach(() => setFilterMock.restore()) - it("removes a filter", () => { - getFilterMock = sinon.stub(localStorageModule, 'getFilter').returns(['passed']) - setFilterMock = sinon.stub(localStorageModule, 'setFilter') + it('removes a filter', () => { + getFilterMock = sinon.stub(storageModule, 'getFilter').returns(['passed']) + setFilterMock = sinon.stub(storageModule, 'setFilter') managerSpy = sinon.spy(dataModule.manager, 'setRender') doFilter('passed', true) expect(managerSpy.callCount).to.eql(0) - expect(dataModule.manager.testSubset.map(({outcome}) => outcome)).to.eql([ 'passed', 'failed', 'passed', 'passed', 'passed', 'passed' ]) + expect(dataModule.manager.testSubset.map(({ outcome }) => outcome)).to.eql([ + 'passed', 'failed', 'passed', 'passed', 'passed', 'passed' + ]) }) - it("applies a filter", () => { - getFilterMock = sinon.stub(localStorageModule, 'getFilter').returns([]) - setFilterMock = sinon.stub(localStorageModule, 'setFilter') + it('applies a filter', () => { + getFilterMock = sinon.stub(storageModule, 'getFilter').returns([]) + setFilterMock = sinon.stub(storageModule, 'setFilter') managerSpy = sinon.spy(dataModule.manager, 'setRender') doFilter('passed', false) @@ -85,64 +93,76 @@ describe('Filter tests', () => { describe('Sort tests', () => { before(() => { const jsonDatan = { - "tests": + 'tests': [ { - "outcome": "passed", + 'id': 'outcome_1', + 'outcome': 'passed', }, { - "outcome": "failed", + 'id': 'outcome_2', + 'outcome': 'failed', }, { - "outcome": "passed", + 'id': 'outcome_3', + 'outcome': 'passed', }, { - "outcome": "passed", + 'id': 'outcome_4', + 'outcome': 'passed', }, { - "outcome": "passed", + 'id': 'outcome_5', + 'outcome': 'passed', }, { - "outcome": "passed", - } - ] + 'id': 'outcome_6', + 'outcome': 'passed', + }, + ], } dataModule.manager.setManager(jsonDatan) }) - after(() => dataModule.manager.setManager({})) - describe('doInitSort', () => { - let managerSpy, sortMock, sortDirectionMock + after(() => dataModule.manager.setManager({ tests: [] })) + describe('doInitSort', () => { + let managerSpy + let sortMock + let sortDirectionMock beforeEach(() => dataModule.manager.resetRender()) - afterEach(() => [sortMock,sortDirectionMock,managerSpy].forEach(fn => fn.restore())) - it("has no stored sort", () => { - sortMock = sinon.stub(localStorageModule, 'getSort').returns(null) - sortDirectionMock = sinon.stub(localStorageModule, 'getSortDirection').returns(null) + afterEach(() => [sortMock,sortDirectionMock, managerSpy].forEach((fn) => fn.restore())) + it('has no stored sort', () => { + sortMock = sinon.stub(storageModule, 'getSort').returns(null) + sortDirectionMock = sinon.stub(storageModule, 'getSortDirection').returns(null) managerSpy = sinon.spy(dataModule.manager, 'setRender') doInitSort() expect(managerSpy.callCount).to.eql(1) - expect(dataModule.manager.testSubset.map(({outcome}) => outcome)).to.eql([ 'passed', 'failed', 'passed', 'passed', 'passed', 'passed' ]) + expect(dataModule.manager.testSubset.map(({ outcome }) => outcome)).to.eql([ + 'passed', 'failed', 'passed', 'passed', 'passed', 'passed', + ]) }) - it("has stored sort preference", () => { - sortMock = sinon.stub(localStorageModule, 'getSort').returns('outcome') - sortDirectionMock = sinon.stub(localStorageModule, 'getSortDirection').returns(false) + it('has stored sort preference', () => { + sortMock = sinon.stub(storageModule, 'getSort').returns('outcome') + sortDirectionMock = sinon.stub(storageModule, 'getSortDirection').returns(false) managerSpy = sinon.spy(dataModule.manager, 'setRender') doInitSort() expect(managerSpy.callCount).to.eql(1) - expect(dataModule.manager.testSubset.map(({outcome}) => outcome)).to.eql([ 'failed', 'passed', 'passed', 'passed', 'passed', 'passed' ]) + expect(dataModule.manager.testSubset.map(({ outcome }) => outcome)).to.eql([ + 'failed', 'passed', 'passed', 'passed', 'passed', 'passed', + ]) }) }) describe('doSort', () => { let getSortMock, setSortMock, getSortDirectionMock, setSortDirection, managerSpy afterEach(() => [getSortMock, setSortMock, getSortDirectionMock, setSortDirection, managerSpy].forEach(fn => fn.restore())) - it("sort on outcome", () => { - getSortMock = sinon.stub(localStorageModule, 'getSort').returns(null) - setSortMock = sinon.stub(localStorageModule, 'setSort') - getSortDirectionMock = sinon.stub(localStorageModule, 'getSortDirection').returns(null) - setSortDirection = sinon.stub(localStorageModule, 'setSortDirection') + it('sort on outcome', () => { + getSortMock = sinon.stub(storageModule, 'getSort').returns(null) + setSortMock = sinon.stub(storageModule, 'setSort') + getSortDirectionMock = sinon.stub(storageModule, 'getSortDirection').returns(null) + setSortDirection = sinon.stub(storageModule, 'setSortDirection') managerSpy = sinon.spy(dataModule.manager, 'setRender') doSort('outcome') From fd62881e224c66878e670581947c8eb8e233a21f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jim=20Br=C3=A4nnlund?= Date: Tue, 25 Oct 2022 12:18:31 +0200 Subject: [PATCH 55/60] add no log output captured string --- src/pytest_html/nextgen.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/pytest_html/nextgen.py b/src/pytest_html/nextgen.py index 66f30bd7..2a344d28 100644 --- a/src/pytest_html/nextgen.py +++ b/src/pytest_html/nextgen.py @@ -229,8 +229,7 @@ def pytest_runtest_logreport(self, report): test_id += f"::{report.when}" data["nodeid"] = test_id - if report.longrepr: - data["longreprtext"] = report.longreprtext + data["longreprtext"] = report.longreprtext or "No log output captured." data["outcome"] = _process_outcome(report) @@ -250,7 +249,7 @@ def pytest_runtest_logreport(self, report): class NextGenReport(BaseReport): def __init__(self, report_path, config): super().__init__(report_path, config) - self._assets_path = Path(self._report_path.parent, "assets") + self._assets_path = Path("assets") self._assets_path.mkdir(parents=True, exist_ok=True) self._default_css_path = Path(self._resources_path, "style.css") @@ -275,9 +274,9 @@ def _media_content(self, content, asset_name, *args, **kwargs): return content def _write_content(self, content, asset_name): - content_path = Path(self._assets_path, asset_name) - content_path.write_bytes(content) - return content_path.as_uri() + content_relative_path = Path(self._assets_path, asset_name) + Path(self._report_path.parent, content_relative_path).write_bytes(content) + return str(content_relative_path) class NextGenSelfContainedReport(BaseReport): From 6400ef451285b117c91869230a29a7577cc4f7bd Mon Sep 17 00:00:00 2001 From: Viktor Gustafsson Date: Wed, 26 Oct 2022 00:59:52 +0200 Subject: [PATCH 56/60] Query params (#25) * Add query params --- src/pytest_html/scripts/datamanager.js | 6 ++- src/pytest_html/scripts/filter.js | 19 ++++---- src/pytest_html/scripts/main.js | 6 +-- src/pytest_html/scripts/storage.js | 65 ++++++++++++++++++++++---- testing/unittest.js | 8 ++-- 5 files changed, 77 insertions(+), 27 deletions(-) diff --git a/src/pytest_html/scripts/datamanager.js b/src/pytest_html/scripts/datamanager.js index 7d6e7cef..58344972 100644 --- a/src/pytest_html/scripts/datamanager.js +++ b/src/pytest_html/scripts/datamanager.js @@ -1,13 +1,17 @@ +const { getCollapsedCategory } = require('./storage.js') + class DataManager { setManager(data) { + const collapsedCategories = getCollapsedCategory() const dataBlob = { ...data, tests: data.tests.map((test, index) => ({ ...test, id: `test_${index}`, - collapsed: false, + collapsed: collapsedCategories.includes(test.outcome.toLowerCase()), })) } this.data = { ...dataBlob } this.renderData = { ...dataBlob } } + get allData() { return { ...this.data } } diff --git a/src/pytest_html/scripts/filter.js b/src/pytest_html/scripts/filter.js index bfb994eb..4c2ccac7 100644 --- a/src/pytest_html/scripts/filter.js +++ b/src/pytest_html/scripts/filter.js @@ -2,26 +2,23 @@ const { manager } = require('./datamanager.js') const storageModule = require('./storage.js') const getFilteredSubSet = (filter) => - manager.allData.tests.filter(({ outcome }) => !filter.includes(outcome.toLowerCase())) + manager.allData.tests.filter(({ outcome }) => filter.includes(outcome.toLowerCase())) const doInitFilter = () => { - const currentFilter = storageModule.getFilter() + const currentFilter = storageModule.getVisible() const filteredSubset = getFilteredSubSet(currentFilter) manager.setRender(filteredSubset) } -const doFilter = (type, apply) => { - const currentFilter = storageModule.getFilter() - if (!apply) { - currentFilter.push(type) +const doFilter = (type, show) => { + if (show) { + storageModule.showCategory(type) } else { - const index = currentFilter.indexOf(type) - if (index > -1) { - currentFilter.splice(index, 1) - } + storageModule.hideCategory(type) } - storageModule.setFilter(currentFilter) + const currentFilter = storageModule.getVisible() + if (currentFilter.length) { const filteredSubset = getFilteredSubSet(currentFilter) manager.setRender(filteredSubset) diff --git a/src/pytest_html/scripts/main.js b/src/pytest_html/scripts/main.js index 23898611..c4d1787c 100644 --- a/src/pytest_html/scripts/main.js +++ b/src/pytest_html/scripts/main.js @@ -3,7 +3,7 @@ const { dom, findAll } = require('./dom.js') const { manager } = require('./datamanager.js') const { doSort } = require('./sort.js') const { doFilter } = require('./filter.js') -const { getFilter } = require('./storage.js') +const { getVisible } = require('./storage.js') const removeChildren = (node) => { while (node.firstChild) { @@ -74,14 +74,14 @@ const renderDerived = (tests, collectedItems, isFinished) => { { outcome: 'rerun', label: 'Reruns' }, ] - const currentFilter = getFilter() + const currentFilter = getVisible() possibleOutcomes.forEach(({ outcome, label }) => { const count = renderSet.filter((test) => test.outcome.toLowerCase() === outcome).length const input = document.querySelector(`input[data-test-result="${outcome}"]`) document.querySelector(`.${outcome}`).innerText = `${count} ${label}` input.disabled = !count - input.checked = !currentFilter.includes(outcome) + input.checked = currentFilter.includes(outcome) }) const numberOfTests = renderSet.filter(({ outcome }) => diff --git a/src/pytest_html/scripts/storage.js b/src/pytest_html/scripts/storage.js index 794806e3..c7f5fff1 100644 --- a/src/pytest_html/scripts/storage.js +++ b/src/pytest_html/scripts/storage.js @@ -1,22 +1,71 @@ -const getFilter = () => [...new Set(JSON.parse(sessionStorage.getItem('filter')))] +const possibleFiltes = ['passed', 'skipped', 'failed', 'error', 'xfailed', 'xpassed', 'rerun'] + +const getVisible = () => { + const url = new URL(window.location.href) + const settings = new URLSearchParams(url.search).get('visible') || '' + const toret = settings ? + [...new Set(settings.split(',').filter((filter) => possibleFiltes.includes(filter)))] : possibleFiltes + + return toret +} +const hideCategory = (categoryToHide) => { + const url = new URL(window.location.href) + const visibleParams = new URLSearchParams(url.search).get('visible') + const currentVisible = visibleParams ? visibleParams.split(',') : [...possibleFiltes] + const settings = [...new Set(currentVisible)].filter((f) => f !== categoryToHide).join(',') + + url.searchParams.set('visible', settings) + history.pushState({}, null, unescape(url.href)) +} + +const showCategory = (categoryToShow) => { + const url = new URL(window.location.href) + const currentVisible = new URLSearchParams(url.search).get('visible')?.split(',') || [...possibleFiltes] + const settings = [...new Set([categoryToShow, ...currentVisible])] + const noFilter = possibleFiltes.length === settings.length || !settings.length + + noFilter ? url.searchParams.delete('visible') : url.searchParams.set('visible', settings.join(',')) + history.pushState({}, null, unescape(url.href)) +} const setFilter = (currentFilter) => { - sessionStorage.setItem('filter', JSON.stringify(currentFilter)) + if (!possibleFiltes.includes(currentFilter)) { + return + } + const url = new URL(window.location.href) + const settings = [currentFilter, ...new Set(new URLSearchParams(url.search).get('filter').split(','))] + + url.searchParams.set('filter', settings) + history.pushState({}, null, unescape(url.href)) } -const getSort = () => sessionStorage.getItem('sort') -const setSort = (type) => sessionStorage.setItem('sort', type) +const getSort = () => { + const url = new URL(window.location.href) + return new URLSearchParams(url.search).get('sort') || 'outcome' +} +const setSort = (type) => { + const url = new URL(window.location.href) + url.searchParams.set('sort', type) + history.pushState({}, null, unescape(url.href)) +} + +const getCollapsedCategory = () => { + const url = new URL(window.location.href) + const collapsedItems = new URLSearchParams(url.search).get('collapsed') + return collapsedItems?.split(',') || [] +} const getSortDirection = () => JSON.parse(sessionStorage.getItem('sortAsc')) -const setSortDirection = (ascending) => { - sessionStorage.setItem('sortAsc', ascending) -} +const setSortDirection = (ascending) => sessionStorage.setItem('sortAsc', ascending) module.exports = { - getFilter, + getVisible, setFilter, + hideCategory, + showCategory, getSort, getSortDirection, setSort, setSortDirection, + getCollapsedCategory, } diff --git a/testing/unittest.js b/testing/unittest.js index 0429fdb8..67f19b35 100644 --- a/testing/unittest.js +++ b/testing/unittest.js @@ -45,7 +45,7 @@ describe('Filter tests', () => { after(() => dataModule.manager.setManager({ tests: [] })) describe('doInitFilter', () => { it('has no stored filters', () => { - getFilterMock = sinon.stub(storageModule, 'getFilter').returns([]) + getFilterMock = sinon.stub(storageModule, 'getVisible').returns([]) managerSpy = sinon.spy(dataModule.manager, 'setRender') doInitFilter() @@ -55,7 +55,7 @@ describe('Filter tests', () => { ]) }) it('exclude passed', () => { - getFilterMock = sinon.stub(storageModule, 'getFilter').returns(['passed']) + getFilterMock = sinon.stub(storageModule, 'getVisible').returns(['passed']) managerSpy = sinon.spy(dataModule.manager, 'setRender') doInitFilter() @@ -67,7 +67,7 @@ describe('Filter tests', () => { let setFilterMock afterEach(() => setFilterMock.restore()) it('removes a filter', () => { - getFilterMock = sinon.stub(storageModule, 'getFilter').returns(['passed']) + getFilterMock = sinon.stub(storageModule, 'getVisible').returns(['passed']) setFilterMock = sinon.stub(storageModule, 'setFilter') managerSpy = sinon.spy(dataModule.manager, 'setRender') @@ -78,7 +78,7 @@ describe('Filter tests', () => { ]) }) it('applies a filter', () => { - getFilterMock = sinon.stub(storageModule, 'getFilter').returns([]) + getFilterMock = sinon.stub(storageModule, 'getVisible').returns([]) setFilterMock = sinon.stub(storageModule, 'setFilter') managerSpy = sinon.spy(dataModule.manager, 'setRender') From eff9f0db6af7e727d999c79cf06b798ab7bb97e3 Mon Sep 17 00:00:00 2001 From: Viktor Gustafsson Date: Sat, 11 Feb 2023 12:58:46 +0100 Subject: [PATCH 57/60] adjust tests (#26) --- src/pytest_html/scripts/storage.js | 19 ++-- testing/unittest.js | 136 +++++++++++------------------ 2 files changed, 66 insertions(+), 89 deletions(-) diff --git a/src/pytest_html/scripts/storage.js b/src/pytest_html/scripts/storage.js index c7f5fff1..5d047ae3 100644 --- a/src/pytest_html/scripts/storage.js +++ b/src/pytest_html/scripts/storage.js @@ -3,10 +3,8 @@ const possibleFiltes = ['passed', 'skipped', 'failed', 'error', 'xfailed', 'xpas const getVisible = () => { const url = new URL(window.location.href) const settings = new URLSearchParams(url.search).get('visible') || '' - const toret = settings ? + return settings ? [...new Set(settings.split(',').filter((filter) => possibleFiltes.includes(filter)))] : possibleFiltes - - return toret } const hideCategory = (categoryToHide) => { const url = new URL(window.location.href) @@ -19,6 +17,9 @@ const hideCategory = (categoryToHide) => { } const showCategory = (categoryToShow) => { + if (typeof window === 'undefined') { + return + } const url = new URL(window.location.href) const currentVisible = new URLSearchParams(url.search).get('visible')?.split(',') || [...possibleFiltes] const settings = [...new Set([categoryToShow, ...currentVisible])] @@ -49,9 +50,15 @@ const setSort = (type) => { } const getCollapsedCategory = () => { - const url = new URL(window.location.href) - const collapsedItems = new URLSearchParams(url.search).get('collapsed') - return collapsedItems?.split(',') || [] + let categotries + if (typeof window !== 'undefined') { + const url = new URL(window.location.href) + const collapsedItems = new URLSearchParams(url.search).get('collapsed') + categotries = collapsedItems?.split(',') || [] + } else { + categotries = [] + } + return categotries } const getSortDirection = () => JSON.parse(sessionStorage.getItem('sortAsc')) diff --git a/testing/unittest.js b/testing/unittest.js index 67f19b35..667b4fae 100644 --- a/testing/unittest.js +++ b/testing/unittest.js @@ -6,43 +6,47 @@ const dataModule = require('../src/pytest_html/scripts/datamanager.js') const storageModule = require('../src/pytest_html/scripts/storage.js') +const setTestData = () => { + const jsonDatan = { + 'tests': + [ + { + 'id': 'passed_1', + 'outcome': 'passed', + }, + { + 'id': 'failed_2', + 'outcome': 'failed', + }, + { + 'id': 'passed_3', + 'outcome': 'passed', + }, + { + 'id': 'passed_4', + 'outcome': 'passed', + }, + { + 'id': 'passed_5', + 'outcome': 'passed', + }, + { + 'id': 'passed_6', + 'outcome': 'passed', + }, + ], + } + dataModule.manager.setManager(jsonDatan) +} + describe('Filter tests', () => { let getFilterMock let managerSpy - before(() => { - const jsonDatan = { - 'tests': - [ - { - 'id': 'passed_1', - 'outcome': 'passed', - }, - { - 'id': 'failed_2', - 'outcome': 'failed', - }, - { - 'id': 'passed_3', - 'outcome': 'passed', - }, - { - 'id': 'passed_4', - 'outcome': 'passed', - }, - { - 'id': 'passed_5', - 'outcome': 'passed', - }, - { - 'id': 'passed_6', - 'outcome': 'passed', - }, - ], - } - dataModule.manager.setManager(jsonDatan) - }) + + beforeEach(setTestData) afterEach(() => [getFilterMock, managerSpy].forEach((fn) => fn.restore())) after(() => dataModule.manager.setManager({ tests: [] })) + describe('doInitFilter', () => { it('has no stored filters', () => { getFilterMock = sinon.stub(storageModule, 'getVisible').returns([]) @@ -50,12 +54,10 @@ describe('Filter tests', () => { doInitFilter() expect(managerSpy.callCount).to.eql(1) - expect(dataModule.manager.testSubset.map(({ outcome }) => outcome)).to.eql([ - 'passed', 'failed', 'passed', 'passed', 'passed', 'passed', - ]) + expect(dataModule.manager.testSubset.map(({ outcome }) => outcome)).to.eql([]) }) it('exclude passed', () => { - getFilterMock = sinon.stub(storageModule, 'getVisible').returns(['passed']) + getFilterMock = sinon.stub(storageModule, 'getVisible').returns(['failed']) managerSpy = sinon.spy(dataModule.manager, 'setRender') doInitFilter() @@ -66,63 +68,23 @@ describe('Filter tests', () => { describe('doFilter', () => { let setFilterMock afterEach(() => setFilterMock.restore()) - it('removes a filter', () => { + it('removes all but passed', () => { getFilterMock = sinon.stub(storageModule, 'getVisible').returns(['passed']) setFilterMock = sinon.stub(storageModule, 'setFilter') managerSpy = sinon.spy(dataModule.manager, 'setRender') doFilter('passed', true) - expect(managerSpy.callCount).to.eql(0) + expect(managerSpy.callCount).to.eql(1) expect(dataModule.manager.testSubset.map(({ outcome }) => outcome)).to.eql([ - 'passed', 'failed', 'passed', 'passed', 'passed', 'passed' + 'passed', 'passed', 'passed', 'passed', 'passed', ]) }) - it('applies a filter', () => { - getFilterMock = sinon.stub(storageModule, 'getVisible').returns([]) - setFilterMock = sinon.stub(storageModule, 'setFilter') - managerSpy = sinon.spy(dataModule.manager, 'setRender') - - doFilter('passed', false) - expect(managerSpy.callCount).to.eql(1) - expect(dataModule.manager.testSubset.map(({outcome}) => outcome)).to.eql([ 'failed' ]) - }) }) }) describe('Sort tests', () => { - before(() => { - const jsonDatan = { - 'tests': - [ - { - 'id': 'outcome_1', - 'outcome': 'passed', - }, - { - 'id': 'outcome_2', - 'outcome': 'failed', - }, - { - 'id': 'outcome_3', - 'outcome': 'passed', - }, - { - 'id': 'outcome_4', - 'outcome': 'passed', - }, - { - 'id': 'outcome_5', - 'outcome': 'passed', - }, - { - 'id': 'outcome_6', - 'outcome': 'passed', - }, - ], - } - dataModule.manager.setManager(jsonDatan) - }) + beforeEach(setTestData) after(() => dataModule.manager.setManager({ tests: [] })) describe('doInitSort', () => { let managerSpy @@ -155,9 +117,15 @@ describe('Sort tests', () => { }) }) describe('doSort', () => { - let getSortMock, setSortMock, getSortDirectionMock, setSortDirection, managerSpy + let getSortMock + let setSortMock + let getSortDirectionMock + let setSortDirection + let managerSpy - afterEach(() => [getSortMock, setSortMock, getSortDirectionMock, setSortDirection, managerSpy].forEach(fn => fn.restore())) + afterEach(() => [ + getSortMock, setSortMock, getSortDirectionMock, setSortDirection, managerSpy, + ].forEach((fn) => fn.restore())) it('sort on outcome', () => { getSortMock = sinon.stub(storageModule, 'getSort').returns(null) setSortMock = sinon.stub(storageModule, 'setSort') @@ -167,7 +135,9 @@ describe('Sort tests', () => { doSort('outcome') expect(managerSpy.callCount).to.eql(1) - expect(dataModule.manager.testSubset.map(({outcome}) => outcome)).to.eql([ 'passed', 'passed', 'passed', 'passed', 'passed', 'failed' ]) + expect(dataModule.manager.testSubset.map(({ outcome }) => outcome)).to.eql([ + 'passed', 'passed', 'passed', 'passed', 'passed', 'failed', + ]) }) }) }) From 23fef75c4d32ec1da83fdaf8a0bdcaf39436825e Mon Sep 17 00:00:00 2001 From: Viktor Gustafsson Date: Sat, 11 Feb 2023 13:01:10 +0100 Subject: [PATCH 58/60] Duration format (#27) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * adjust tests * build format handler * remove dependency --------- Co-authored-by: Jim Brännlund --- package.json | 9 +++------ src/pytest_html/scripts/datamanager.js | 3 --- src/pytest_html/scripts/dom.js | 2 +- src/pytest_html/scripts/filter.js | 2 +- src/pytest_html/scripts/utils.js | 27 ++++++++++++++++---------- testing/unittest.js | 17 +++++++++++++++- 6 files changed, 38 insertions(+), 22 deletions(-) diff --git a/package.json b/package.json index bec0f258..bb07108b 100644 --- a/package.json +++ b/package.json @@ -5,16 +5,13 @@ "build:jsapp": "browserify ./src/pytest_html/scripts/index.js > ./src/pytest_html/resources/app.js", "build": "npm run unit && npm run build:css && npm run build:jsapp" }, - "dependencies": { - "dayjs": "^1.11.3" - }, "devDependencies": { "browserify": "^17.0.0", "chai": "^4.3.6", + "eslint": "^8.20.0", + "eslint-config-google": "^0.14.0", "mocha": "^10.0.0", "sass": "^1.52.3", - "sinon": "^14.0.0", - "eslint": "^8.20.0", - "eslint-config-google": "^0.14.0" + "sinon": "^14.0.0" } } diff --git a/src/pytest_html/scripts/datamanager.js b/src/pytest_html/scripts/datamanager.js index 58344972..20e3adfb 100644 --- a/src/pytest_html/scripts/datamanager.js +++ b/src/pytest_html/scripts/datamanager.js @@ -47,9 +47,6 @@ class DataManager { get collectedItems() { return this.renderData.collectedItems } - get durationFormat() { - return this.renderData.durationFormat - } get isFinished() { return this.data.runningState === 'Finished' } diff --git a/src/pytest_html/scripts/dom.js b/src/pytest_html/scripts/dom.js index 0dce623a..b8a4a9fa 100644 --- a/src/pytest_html/scripts/dom.js +++ b/src/pytest_html/scripts/dom.js @@ -75,7 +75,7 @@ const dom = { resultBody.querySelector('.col-result').classList.add(`${collapsed ? 'expander' : 'collapser'}`) resultBody.querySelector('.col-result').dataset.id = id resultBody.querySelector('.col-name').innerText = nodeid - resultBody.querySelector('.col-duration').innerText = `${formatDuration(duration)}s` + resultBody.querySelector('.col-duration').innerText = formatDuration(duration) if (longreprtext) { diff --git a/src/pytest_html/scripts/filter.js b/src/pytest_html/scripts/filter.js index 4c2ccac7..c78a4989 100644 --- a/src/pytest_html/scripts/filter.js +++ b/src/pytest_html/scripts/filter.js @@ -27,7 +27,7 @@ const doFilter = (type, show) => { } } -module.exports= { +module.exports = { doFilter, doInitFilter, } diff --git a/src/pytest_html/scripts/utils.js b/src/pytest_html/scripts/utils.js index 29e06112..3dd5e156 100644 --- a/src/pytest_html/scripts/utils.js +++ b/src/pytest_html/scripts/utils.js @@ -1,16 +1,23 @@ -const { manager } = require('./datamanager.js') +const formatedNumber = (number) => + number.toLocaleString('en-US', { + minimumIntegerDigits: 2, + useGrouping: false, + }) -const dayjs = require('dayjs') -const duration = require('dayjs/plugin/duration') -dayjs.extend(duration) -const formatDuration = (dur) => { - const durationFormat = manager.durationFormat - if (durationFormat.length === 0) { - return dur.toFixed(2) - } else { - return dayjs.duration(dur * 1000).format(durationFormat) +const formatDuration = ( ms ) => { + const totalSeconds = ms / 1000 + + if (totalSeconds < 1) { + return `${ms}ms` } + const hours = Math.floor(totalSeconds / 3600) + let remainingSeconds = totalSeconds % 3600 + const minutes = Math.floor(remainingSeconds / 60) + remainingSeconds = remainingSeconds % 60 + const seconds = Math.round(remainingSeconds) + + return `${formatedNumber(hours)}:${formatedNumber(minutes)}:${formatedNumber(seconds)}` } module.exports = { formatDuration } diff --git a/testing/unittest.js b/testing/unittest.js index 667b4fae..f3c46eee 100644 --- a/testing/unittest.js +++ b/testing/unittest.js @@ -2,6 +2,7 @@ const { expect } = require('chai') const sinon = require('sinon') const { doInitFilter, doFilter } = require('../src/pytest_html/scripts/filter.js') const { doInitSort, doSort } = require('../src/pytest_html/scripts/sort.js') +const { formatDuration } = require('../src/pytest_html/scripts/utils.js') const dataModule = require('../src/pytest_html/scripts/datamanager.js') const storageModule = require('../src/pytest_html/scripts/storage.js') @@ -92,7 +93,7 @@ describe('Sort tests', () => { let sortDirectionMock beforeEach(() => dataModule.manager.resetRender()) - afterEach(() => [sortMock,sortDirectionMock, managerSpy].forEach((fn) => fn.restore())) + afterEach(() => [sortMock, sortDirectionMock, managerSpy].forEach((fn) => fn.restore())) it('has no stored sort', () => { sortMock = sinon.stub(storageModule, 'getSort').returns(null) sortDirectionMock = sinon.stub(storageModule, 'getSortDirection').returns(null) @@ -141,3 +142,17 @@ describe('Sort tests', () => { }) }) }) + +describe('utils tests', () => { + describe('formatDuration', () => { + it('handles small durations', () => { + expect(formatDuration(123)).to.eql('123ms') + expect(formatDuration(0)).to.eql('0ms') + expect(formatDuration(999)).to.eql('999ms') + }) + it('handles larger durations', () => { + expect(formatDuration(1234)).to.eql('00:00:01') + expect(formatDuration(12345678)).to.eql('03:25:46') + }) + }) +}) From a3557083d5a0cf8567abda2f5a52893f39fdfcf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jim=20Br=C3=A4nnlund?= Date: Sun, 5 Mar 2023 14:33:47 +0100 Subject: [PATCH 59/60] Beyondevil/cleanup (#28) * chore: Cleanup branch before merge * chore: Fix duration and CI --- .github/workflows/actions.yml | 29 +- .github/workflows/nightly.yml | 3 +- .github/workflows/tests.yml | 176 +- .gitignore | 1 - docker-compose.tmpl.yml | 12 + package-lock.json | 6225 +++++++++++++++++ package.json | 1 + poetry.lock | 519 +- pyproject.toml | 25 +- src/pytest_html/html_report.py | 342 - src/pytest_html/nextgen.py | 116 +- src/pytest_html/outcome.py | 33 - src/pytest_html/plugin.py | 17 +- src/pytest_html/resources/index.jinja2 | 8 +- src/pytest_html/result.py | 287 - src/pytest_html/scripts/datamanager.js | 2 +- src/pytest_html/scripts/dom.js | 13 +- src/pytest_html/scripts/main.js | 5 +- src/pytest_html/scripts/utils.js | 13 +- start | 13 + testing/js_test_report.html | 64 - ...est_html.py => legacy_test_pytest_html.py} | 0 testing/test.js | 134 - testing/test_new.py | 484 ++ testing/unittest.js | 10 +- tox.ini | 18 +- 26 files changed, 7237 insertions(+), 1313 deletions(-) create mode 100644 docker-compose.tmpl.yml create mode 100644 package-lock.json delete mode 100644 src/pytest_html/html_report.py delete mode 100644 src/pytest_html/outcome.py delete mode 100644 src/pytest_html/result.py create mode 100755 start delete mode 100644 testing/js_test_report.html rename testing/{test_pytest_html.py => legacy_test_pytest_html.py} (100%) delete mode 100644 testing/test.js create mode 100644 testing/test_new.py diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index 6b4a4e36..ff71f093 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -18,20 +18,21 @@ jobs: name: Build Docs runs-on: ubuntu-latest steps: - - uses: actions/checkout@master + - uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: - python-version: 3.6 + python-version: '3.10' - name: Install tox run: python -m pip install --upgrade tox - name: Build docs with tox - run: python -m tox -e docs + run: tox -e docs tests: - uses: pytest-dev/pytest-html/.github/workflows/tests.yml@master + uses: ./.github/workflows/tests.yml publish: + if: github.repository_owner == 'pytest-dev' name: Publish to PyPI registry needs: - tests @@ -41,14 +42,16 @@ jobs: TOXENV: packaging steps: - - name: Switch to using Python 3.6 by default - uses: actions/setup-python@v2 + - name: Switch to using Python 3.10 by default + uses: actions/setup-python@v4 with: - python-version: 3.6 + python-version: '3.10' + - name: Install tox run: python -m pip install --user tox + - name: Check out src from Git - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: # Get shallow Git history (default) for tag creation events # but have a complete clone for any other workflows. @@ -63,6 +66,7 @@ jobs: ) && 1 || 0 }} + - name: Drop Git tags from HEAD for non-tag-create events if: >- github.event_name != 'create' || @@ -71,8 +75,10 @@ jobs: git tag --points-at HEAD | xargs git tag --delete + - name: Build dists run: python -m tox + - name: Publish to test.pypi.org if: >- ( @@ -85,14 +91,15 @@ jobs: github.event_name == 'create' && github.event.ref_type == 'tag' ) - uses: pypa/gh-action-pypi-publish@master + uses: pypa/gh-action-pypi-publish@v1 with: password: ${{ secrets.testpypi_password }} repository_url: https://test.pypi.org/legacy/ + - name: Publish to pypi.org if: >- # "create" workflows run separately from "push" & "pull_request" github.event_name == 'create' && github.event.ref_type == 'tag' - uses: pypa/gh-action-pypi-publish@master + uses: pypa/gh-action-pypi-publish@v1 with: password: ${{ secrets.pypi_password }} diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index d97d3156..252aa36c 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -6,4 +6,5 @@ on: jobs: tests: - uses: pytest-dev/pytest-html/.github/workflows/tests.yml@master + if: github.repository_owner == 'pytest-dev' + uses: ./.github/workflows/tests.yml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 14404ca4..ce3bd859 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -5,120 +5,132 @@ on: jobs: test_python: - name: ${{ matrix.name }} - runs-on: ${{ matrix.os }} + name: ${{ matrix.python-version }} + runs-on: ubuntu-latest strategy: matrix: include: - - os: ubuntu-latest - name: py36-ubuntu - python-version: 3.6 - - - os: windows-latest - name: py36-windows - python-version: 3.6 - - - os: macOS-latest - name: py36-mac - python-version: 3.6 + - tox-env: "py37" + python-version: "3.7" - - os: ubuntu-latest - name: py37-ubuntu - python-version: 3.7 +# https://github.com/pytest-dev/pytest-html/issues/585 +# - os: windows-latest +# name: py37-windows +# python-version: 3.7 - - os: windows-latest - name: py37-windows - python-version: 3.7 +# https://github.com/pytest-dev/pytest-html/issues/585 +# - os: macOS-latest +# name: py37-mac +# python-version: 3.7 - - os: macOS-latest - name: py37-mac - python-version: 3.7 + - tox-env: "py38" + python-version: "3.8" - - os: ubuntu-latest - name: py38-ubuntu - python-version: 3.8 +# https://github.com/pytest-dev/pytest-html/issues/585 +# - os: windows-latest +# name: py38-windows +# python-version: 3.8 - - os: windows-latest - name: py38-windows - python-version: 3.8 +# https://github.com/pytest-dev/pytest-html/issues/585 +# - os: macOS-latest +# name: py38-mac +# python-version: 3.8 - - os: macOS-latest - name: py38-mac - python-version: 3.8 + - tox-env: "py39" + python-version: "3.9" - - os: ubuntu-latest - name: py39-ubuntu - python-version: 3.9 + - tox-env: "py310" + python-version: "3.10" - - os: windows-latest - name: py39-windows - python-version: 3.9 +# https://github.com/pytest-dev/pytest-html/issues/585 +# - os: windows-latest +# name: py39-windows +# python-version: 3.9 - - os: macOS-latest - name: py39-mac - python-version: 3.9 +# https://github.com/pytest-dev/pytest-html/issues/585 +# - os: macOS-latest +# name: py39-mac +# python-version: 3.9 - - os: ubuntu-latest - name: pypy3-ubuntu - python-version: pypy3 + - tox-env: "pypy3" + python-version: "pypy3.9" + skip-coverage: true - - os: windows-latest - name: pypy3-windows - python-version: pypy3 +# https://github.com/pytest-dev/pytest-html/issues/585 +# - os: windows-latest +# name: pypy3-windows +# python-version: pypy3 # https://github.com/pytest-dev/pytest-html/issues/482 # - os: macOS-latest # name: pypy3-mac # python-version: pypy3 - - os: ubuntu-latest - name: devel-ubuntu - python-version: 3.9 + - tox-env: "devel" + python-version: "3.11-dev" steps: - name: Set Newline Behavior - run : git config --global core.autocrlf false - - uses: actions/checkout@master + run: git config --global core.autocrlf false + + - uses: actions/checkout@v3 + + - name: Start chrome + run: ./start + + - name: Use Node.js + uses: actions/setup-node@v3 + with: + node-version: '16.x' + + - name: Install Dependencies + run: npm ci + + - name: Build app + run: npm run build:ci + - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: - python-version: ${{ matrix['python-version'] }} + python-version: ${{ matrix.python-version }} + - name: Install tox run: python -m pip install --upgrade tox - - name: Get Tox Environment Name From Matrix Name - uses: rishabhgupta/split-by@v1 - id: split-matrix-name - with: - string: '${{ matrix.name }}' - split-by: '-' + - name: Test with coverage - if: "! contains(matrix.name, 'pypy3')" - run: python -m tox -e ${{ steps.split-matrix-name.outputs._0}}-cov + if: ${{ ! matrix.skip-coverage }} + run: tox -e ${{ matrix.tox-env }}-cov + - name: Test without coverage - if: "contains(matrix.name, 'pypy3')" - run: python -m tox -e ${{ steps.split-matrix-name.outputs._0}} - - # TODO: https://github.com/pytest-dev/pytest-html/issues/481 -# - name: Upload coverage to codecov -# if: github.event.schedule == '' -# uses: codecov/codecov-action@v2 -# with: -# fail_ci_if_error: true -# file: ./coverage.xml -# flags: tests -# name: ${{ matrix.py }} - ${{ matrix.os }} -# verbose: true + if: ${{ matrix.skip-coverage }} + run: tox -e ${{ matrix.tox-env }} + + # TODO: https://github.com/pytest-dev/pytest-html/issues/481 + - name: Upload coverage to codecov + if: >- + ${{ + ! github.event.schedule && + ! matrix.skip-coverage && + github.repository_owner == 'pytest-dev' + }} + uses: codecov/codecov-action@v3 + with: + fail_ci_if_error: true + files: ./coverage.xml + flags: tests + name: ${{ matrix.tox-env }} + verbose: true test_javascript: - name: grunt + name: mocha runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 + - uses: actions/checkout@v3 + - name: Use Node.js + uses: actions/setup-node@v3 with: - node-version: '12.x' + node-version: '16.x' - name: Install Dependencies - run: npm install - - name: QUnit Tests - run: npm test + run: npm ci + - name: Mocha Tests + run: npm run unit diff --git a/.gitignore b/.gitignore index e807fe14..3c76b5b9 100644 --- a/.gitignore +++ b/.gitignore @@ -35,7 +35,6 @@ local.properties # JS files/folders ## node / npm node_modules/ -package-lock.json # MacOS files .DS_Store diff --git a/docker-compose.tmpl.yml b/docker-compose.tmpl.yml new file mode 100644 index 00000000..57060e79 --- /dev/null +++ b/docker-compose.tmpl.yml @@ -0,0 +1,12 @@ +version: "3" + +services: + chrome: + image: seleniarm/standalone-chromium:110.0 + container_name: chrome + shm_size: '2gb' + ports: + - "4444:4444" + - "7900:7900" + volumes: + - "%%VOLUME%%:Z" diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..f951ac4c --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6225 @@ +{ + "name": "pytest-html", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "devDependencies": { + "browserify": "^17.0.0", + "chai": "^4.3.6", + "eslint": "^8.20.0", + "eslint-config-google": "^0.14.0", + "mocha": "^10.0.0", + "sass": "^1.52.3", + "sinon": "^14.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", + "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.4.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", + "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/@sinonjs/fake-timers/node_modules/@sinonjs/commons": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/samsam": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-7.0.1.tgz", + "integrity": "sha512-zsAk2Jkiq89mhZovB2LLOdTCxJF4hqqTToGP0ASWlhp4I1hqOjcfmZGafXntCN7MDC6yySH0mFHrYtHceOeLmw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^2.0.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } + }, + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", + "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-node": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", + "dev": true, + "dependencies": { + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" + } + }, + "node_modules/acorn-node/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dev": true, + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/asn1.js/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "dev": true, + "dependencies": { + "object-assign": "^4.1.1", + "util": "0.10.3" + } + }, + "node_modules/assert/node_modules/inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA==", + "dev": true + }, + "node_modules/assert/node_modules/util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha512-5KiHfsmkqacuKjkRkdV7SsfDJ2EGiPsK92s2MhNSY0craxjTdKTtqKsJaCWp4LW33ZZ0OPUv1WO/TFvNQRiQxQ==", + "dev": true, + "dependencies": { + "inherits": "2.0.1" + } + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "dev": true + }, + "node_modules/browser-pack": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.1.0.tgz", + "integrity": "sha512-erYug8XoqzU3IfcU8fUgyHqyOXqIE4tUTTQ+7mqUjQlvnXkOO6OlT9c/ZoJVHYoAaqGxr09CN53G7XIsO4KtWA==", + "dev": true, + "dependencies": { + "combine-source-map": "~0.8.0", + "defined": "^1.0.0", + "JSONStream": "^1.0.3", + "safe-buffer": "^5.1.1", + "through2": "^2.0.0", + "umd": "^3.0.0" + }, + "bin": { + "browser-pack": "bin/cmd.js" + } + }, + "node_modules/browser-resolve": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-2.0.0.tgz", + "integrity": "sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==", + "dev": true, + "dependencies": { + "resolve": "^1.17.0" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/browserify": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/browserify/-/browserify-17.0.0.tgz", + "integrity": "sha512-SaHqzhku9v/j6XsQMRxPyBrSP3gnwmE27gLJYZgMT2GeK3J0+0toN+MnuNYDfHwVGQfLiMZ7KSNSIXHemy905w==", + "dev": true, + "dependencies": { + "assert": "^1.4.0", + "browser-pack": "^6.0.1", + "browser-resolve": "^2.0.0", + "browserify-zlib": "~0.2.0", + "buffer": "~5.2.1", + "cached-path-relative": "^1.0.0", + "concat-stream": "^1.6.0", + "console-browserify": "^1.1.0", + "constants-browserify": "~1.0.0", + "crypto-browserify": "^3.0.0", + "defined": "^1.0.0", + "deps-sort": "^2.0.1", + "domain-browser": "^1.2.0", + "duplexer2": "~0.1.2", + "events": "^3.0.0", + "glob": "^7.1.0", + "has": "^1.0.0", + "htmlescape": "^1.1.0", + "https-browserify": "^1.0.0", + "inherits": "~2.0.1", + "insert-module-globals": "^7.2.1", + "JSONStream": "^1.0.3", + "labeled-stream-splicer": "^2.0.0", + "mkdirp-classic": "^0.5.2", + "module-deps": "^6.2.3", + "os-browserify": "~0.3.0", + "parents": "^1.0.1", + "path-browserify": "^1.0.0", + "process": "~0.11.0", + "punycode": "^1.3.2", + "querystring-es3": "~0.2.0", + "read-only-stream": "^2.0.0", + "readable-stream": "^2.0.2", + "resolve": "^1.1.4", + "shasum-object": "^1.0.0", + "shell-quote": "^1.6.1", + "stream-browserify": "^3.0.0", + "stream-http": "^3.0.0", + "string_decoder": "^1.1.1", + "subarg": "^1.0.0", + "syntax-error": "^1.1.1", + "through2": "^2.0.0", + "timers-browserify": "^1.0.1", + "tty-browserify": "0.0.1", + "url": "~0.11.0", + "util": "~0.12.0", + "vm-browserify": "^1.0.0", + "xtend": "^4.0.0" + }, + "bin": { + "browserify": "bin/cmd.js" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "node_modules/browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "dev": true, + "dependencies": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "node_modules/browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "dev": true, + "dependencies": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "node_modules/browserify-sign/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "dependencies": { + "pako": "~1.0.5" + } + }, + "node_modules/buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", + "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", + "dev": true, + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", + "dev": true + }, + "node_modules/builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==", + "dev": true + }, + "node_modules/cached-path-relative": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.1.0.tgz", + "integrity": "sha512-WF0LihfemtesFcJgO7xfOoOcnWzY/QHR4qeDqV44jPU3HTI54+LnfXK3SA27AVVGCdZFgjjFFaqUA9Jx7dMJZA==", + "dev": true + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chai": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", + "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^4.1.2", + "get-func-name": "^2.0.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "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/check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "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/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/combine-source-map": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz", + "integrity": "sha512-UlxQ9Vw0b/Bt/KYwCFqdEwsQ1eL8d1gibiFb7lxQJFdvTgc2hIZi6ugsg+kyhzhPV+QEpUiEIwInIAIrgoEkrg==", + "dev": true, + "dependencies": { + "convert-source-map": "~1.1.0", + "inline-source-map": "~0.6.0", + "lodash.memoize": "~3.0.3", + "source-map": "~0.5.3" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", + "dev": true + }, + "node_modules/constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", + "integrity": "sha512-Y8L5rp6jo+g9VEPgvqNfEopjTR4OTYct8lXlS8iVQdmnjDvbdbzYe9rjtFCB9egC86JoNCU61WRY+ScjkZpnIg==", + "dev": true + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "node_modules/create-ecdh/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "dependencies": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + }, + "engines": { + "node": "*" + } + }, + "node_modules/dash-ast": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-1.0.0.tgz", + "integrity": "sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA==", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/defined": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", + "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/deps-sort": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.1.tgz", + "integrity": "sha512-1orqXQr5po+3KI6kQb9A4jnXT1PBwggGl2d7Sq2xsnOeI9GPcE/tGcF9UiSZtZBM7MukY4cAh7MemS6tZYipfw==", + "dev": true, + "dependencies": { + "JSONStream": "^1.0.3", + "shasum-object": "^1.0.0", + "subarg": "^1.0.0", + "through2": "^2.0.0" + }, + "bin": { + "deps-sort": "bin/cmd.js" + } + }, + "node_modules/des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/detective": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", + "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", + "dev": true, + "dependencies": { + "acorn-node": "^1.8.2", + "defined": "^1.0.0", + "minimist": "^1.2.6" + }, + "bin": { + "detective": "bin/detective.js" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/diffie-hellman/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true, + "engines": { + "node": ">=0.4", + "npm": ">=1.2" + } + }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.34.0.tgz", + "integrity": "sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg==", + "dev": true, + "dependencies": { + "@eslint/eslintrc": "^1.4.1", + "@humanwhocodes/config-array": "^0.11.8", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.4.0", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-google": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/eslint-config-google/-/eslint-config-google-0.14.0.tgz", + "integrity": "sha512-WsbX4WbjuMvTdeVL6+J3rK1RGhCTqjsFjX7UMSMgZiyxxaNLkoJENbrGExzERFeoTpGw3F3FypTiWAP9ZXzkEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "eslint": ">=5.16.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/espree": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", + "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "dev": true, + "dependencies": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/get-assigned-identifiers": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz", + "integrity": "sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ==", + "dev": true + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash-base/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dev": true, + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/htmlescape": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz", + "integrity": "sha512-eVcrzgbR4tim7c7soKQKtxa/kQM4TzjnlU83rcZ9bHU6t31ehfV7SktN6McWgwPWg+JYMA/O3qpGxBvFq1z2Jg==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==", + "dev": true + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/immutable": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.2.4.tgz", + "integrity": "sha512-WDxL3Hheb1JkRN3sQkyujNlL/xRjAo3rJtaU5xeufUauG66JdMr32bLj4gF+vWl84DIA3Zxw7tiAjneYzRRw+w==", + "dev": true + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/inline-source-map": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz", + "integrity": "sha512-0mVWSSbNDvedDWIN4wxLsdPM4a7cIPcpyMxj3QZ406QRwQ6ePGB1YIHxVPjqpcUGbWQ5C+nHTwGNWAGvt7ggVA==", + "dev": true, + "dependencies": { + "source-map": "~0.5.3" + } + }, + "node_modules/insert-module-globals": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.2.1.tgz", + "integrity": "sha512-ufS5Qq9RZN+Bu899eA9QCAYThY+gGW7oRkmb0vC93Vlyu/CFGcH0OYPEjVkDXA5FEbTt1+VWzdoOD3Ny9N+8tg==", + "dev": true, + "dependencies": { + "acorn-node": "^1.5.2", + "combine-source-map": "^0.8.0", + "concat-stream": "^1.6.1", + "is-buffer": "^1.1.0", + "JSONStream": "^1.0.3", + "path-is-absolute": "^1.0.1", + "process": "~0.11.0", + "through2": "^2.0.0", + "undeclared-identifiers": "^1.1.2", + "xtend": "^4.0.0" + }, + "bin": { + "insert-module-globals": "bin/cmd.js" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/js-sdsl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", + "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ] + }, + "node_modules/JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/just-extend": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", + "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", + "dev": true + }, + "node_modules/labeled-stream-splicer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.2.tgz", + "integrity": "sha512-Ca4LSXFFZUjPScRaqOcFxneA0VpKZr4MMYCljyQr4LIewTLb3Y0IUTIsnBBsVubIeEfxeSZpSjSsRM8APEQaAw==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "stream-splicer": "^2.0.0" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "dev": true + }, + "node_modules/lodash.memoize": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", + "integrity": "sha512-eDn9kqrAmVUC1wmZvlQ6Uhde44n+tXpqPrN8olQJbttgh0oKclk+SF54P47VEGE9CEiMeRwAP8BaM7UHvBkz2A==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/loupe": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", + "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.0" + } + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/miller-rabin/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "dev": true + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true + }, + "node_modules/mocha": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", + "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", + "dev": true, + "dependencies": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/minimatch/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/module-deps": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.2.3.tgz", + "integrity": "sha512-fg7OZaQBcL4/L+AK5f4iVqf9OMbCclXfy/znXRxTVhJSeW5AIlS9AwheYwDaXM3lVW7OBeaeUEY3gbaC6cLlSA==", + "dev": true, + "dependencies": { + "browser-resolve": "^2.0.0", + "cached-path-relative": "^1.0.2", + "concat-stream": "~1.6.0", + "defined": "^1.0.0", + "detective": "^5.2.0", + "duplexer2": "^0.1.2", + "inherits": "^2.0.1", + "JSONStream": "^1.0.3", + "parents": "^1.0.0", + "readable-stream": "^2.0.2", + "resolve": "^1.4.0", + "stream-combiner2": "^1.1.1", + "subarg": "^1.0.0", + "through2": "^2.0.0", + "xtend": "^4.0.0" + }, + "bin": { + "module-deps": "bin/cmd.js" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/nise": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.4.tgz", + "integrity": "sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^2.0.0", + "@sinonjs/fake-timers": "^10.0.2", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "path-to-regexp": "^1.7.0" + } + }, + "node_modules/nise/node_modules/@sinonjs/fake-timers": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz", + "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^2.0.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", + "dev": true + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parents": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", + "integrity": "sha512-mXKF3xkoUt5td2DoxpLmtOmZvko9VfFpwRwkKDHSNvgmpLAeBo18YDhcPbBzJq+QLCHMbGOfzia2cX4U+0v9Mg==", + "dev": true, + "dependencies": { + "path-platform": "~0.11.15" + } + }, + "node_modules/parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "dev": true, + "dependencies": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-platform": { + "version": "0.11.15", + "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz", + "integrity": "sha512-Y30dB6rab1A/nfEKsZxmr01nUotHX0c/ZiIAsCTatEe1CmS5Pm5He7fZ195bPT7RdquoaL8lLxFCMQi/bS7IJg==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dev": true, + "dependencies": { + "isarray": "0.0.1" + } + }, + "node_modules/path-to-regexp/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dev": true, + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "dev": true + }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "node_modules/read-only-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz", + "integrity": "sha512-3ALe0bjBVZtkdWKIcThYpQCLbBMd/+Tbh2CDSrAIDO3UsZ4Xs+tnyjv2MjCOMMgBG+AsUOeuP1cgtY1INISc8w==", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/readable-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/sass": { + "version": "1.58.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.58.0.tgz", + "integrity": "sha512-PiMJcP33DdKtZ/1jSjjqVIKihoDc6yWmYr9K/4r3fVVIEDAluD0q7XZiRKrNJcPK3qkLRF/79DND1H5q1LBjgg==", + "dev": true, + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/shasum-object": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shasum-object/-/shasum-object-1.0.0.tgz", + "integrity": "sha512-Iqo5rp/3xVi6M4YheapzZhhGPVs0yZwHj7wvwQ1B9z8H6zk+FEnI7y3Teq7qwnekfEhu8WmG2z0z4iWZaxLWVg==", + "dev": true, + "dependencies": { + "fast-safe-stringify": "^2.0.7" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.0.tgz", + "integrity": "sha512-QHsz8GgQIGKlRi24yFc6a6lN69Idnx634w49ay6+jA5yFh7a1UY+4Rp6HPx/L/1zcEDPEij8cIsiqR6bQsE5VQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/sinon": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-14.0.2.tgz", + "integrity": "sha512-PDpV0ZI3ZCS3pEqx0vpNp6kzPhHrLx72wA0G+ZLaaJjLIYeE0n8INlgaohKuGy7hP0as5tbUd23QWu5U233t+w==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^2.0.0", + "@sinonjs/fake-timers": "^9.1.2", + "@sinonjs/samsam": "^7.0.1", + "diff": "^5.0.0", + "nise": "^5.1.2", + "supports-color": "^7.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stream-browserify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", + "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "dev": true, + "dependencies": { + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" + } + }, + "node_modules/stream-browserify/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/stream-combiner2": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "integrity": "sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==", + "dev": true, + "dependencies": { + "duplexer2": "~0.1.0", + "readable-stream": "^2.0.2" + } + }, + "node_modules/stream-http": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.2.0.tgz", + "integrity": "sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==", + "dev": true, + "dependencies": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "xtend": "^4.0.2" + } + }, + "node_modules/stream-http/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/stream-splicer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.1.tgz", + "integrity": "sha512-Xizh4/NPuYSyAXyT7g8IvdJ9HJpxIGL9PjyhtywCZvvP0OPIdqyrr4dMikeuvY8xahpdKEBlBTySe583totajg==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.2" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/subarg": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", + "integrity": "sha512-RIrIdRY0X1xojthNcVtgT9sjpOGagEUKpZdgBUi054OEPFo282yg+zE+t1Rj3+RqKq2xStL7uUHhY+AjbC4BXg==", + "dev": true, + "dependencies": { + "minimist": "^1.1.0" + } + }, + "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/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/syntax-error": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz", + "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==", + "dev": true, + "dependencies": { + "acorn-node": "^1.2.0" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/timers-browserify": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz", + "integrity": "sha512-PIxwAupJZiYU4JmVZYwXp9FKsHMXb5h0ZEFyuXTAn8WLHOlcij+FEcbrvDsom1o5dr1YggEtFbECvGCW2sT53Q==", + "dev": true, + "dependencies": { + "process": "~0.11.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tty-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", + "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "dev": true + }, + "node_modules/umd": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.3.tgz", + "integrity": "sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow==", + "dev": true, + "bin": { + "umd": "bin/cli.js" + } + }, + "node_modules/undeclared-identifiers": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/undeclared-identifiers/-/undeclared-identifiers-1.1.3.tgz", + "integrity": "sha512-pJOW4nxjlmfwKApE4zvxLScM/njmwj/DiUBv7EabwE4O8kRUy+HIwxQtZLBPll/jx1LJyBcqNfB3/cpv9EZwOw==", + "dev": true, + "dependencies": { + "acorn-node": "^1.3.0", + "dash-ast": "^1.0.0", + "get-assigned-identifiers": "^1.2.0", + "simple-concat": "^1.0.0", + "xtend": "^4.0.1" + }, + "bin": { + "undeclared-identifiers": "bin.js" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/uri-js/node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ==", + "dev": true, + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==", + "dev": true + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "dependencies": { + "@eslint/eslintrc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", + "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.4.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + } + }, + "@humanwhocodes/config-array": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + } + }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", + "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + }, + "dependencies": { + "@sinonjs/commons": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + } + } + }, + "@sinonjs/samsam": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-7.0.1.tgz", + "integrity": "sha512-zsAk2Jkiq89mhZovB2LLOdTCxJF4hqqTToGP0ASWlhp4I1hqOjcfmZGafXntCN7MDC6yySH0mFHrYtHceOeLmw==", + "dev": true, + "requires": { + "@sinonjs/commons": "^2.0.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } + }, + "@sinonjs/text-encoding": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", + "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", + "dev": true + }, + "acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, + "acorn-node": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", + "dev": true, + "requires": { + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + } + } + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "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" + } + }, + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "dev": true, + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA==", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha512-5KiHfsmkqacuKjkRkdV7SsfDJ2EGiPsK92s2MhNSY0craxjTdKTtqKsJaCWp4LW33ZZ0OPUv1WO/TFvNQRiQxQ==", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, + "available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "dev": true + }, + "browser-pack": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.1.0.tgz", + "integrity": "sha512-erYug8XoqzU3IfcU8fUgyHqyOXqIE4tUTTQ+7mqUjQlvnXkOO6OlT9c/ZoJVHYoAaqGxr09CN53G7XIsO4KtWA==", + "dev": true, + "requires": { + "combine-source-map": "~0.8.0", + "defined": "^1.0.0", + "JSONStream": "^1.0.3", + "safe-buffer": "^5.1.1", + "through2": "^2.0.0", + "umd": "^3.0.0" + } + }, + "browser-resolve": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-2.0.0.tgz", + "integrity": "sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==", + "dev": true, + "requires": { + "resolve": "^1.17.0" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "browserify": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/browserify/-/browserify-17.0.0.tgz", + "integrity": "sha512-SaHqzhku9v/j6XsQMRxPyBrSP3gnwmE27gLJYZgMT2GeK3J0+0toN+MnuNYDfHwVGQfLiMZ7KSNSIXHemy905w==", + "dev": true, + "requires": { + "assert": "^1.4.0", + "browser-pack": "^6.0.1", + "browser-resolve": "^2.0.0", + "browserify-zlib": "~0.2.0", + "buffer": "~5.2.1", + "cached-path-relative": "^1.0.0", + "concat-stream": "^1.6.0", + "console-browserify": "^1.1.0", + "constants-browserify": "~1.0.0", + "crypto-browserify": "^3.0.0", + "defined": "^1.0.0", + "deps-sort": "^2.0.1", + "domain-browser": "^1.2.0", + "duplexer2": "~0.1.2", + "events": "^3.0.0", + "glob": "^7.1.0", + "has": "^1.0.0", + "htmlescape": "^1.1.0", + "https-browserify": "^1.0.0", + "inherits": "~2.0.1", + "insert-module-globals": "^7.2.1", + "JSONStream": "^1.0.3", + "labeled-stream-splicer": "^2.0.0", + "mkdirp-classic": "^0.5.2", + "module-deps": "^6.2.3", + "os-browserify": "~0.3.0", + "parents": "^1.0.1", + "path-browserify": "^1.0.0", + "process": "~0.11.0", + "punycode": "^1.3.2", + "querystring-es3": "~0.2.0", + "read-only-stream": "^2.0.0", + "readable-stream": "^2.0.2", + "resolve": "^1.1.4", + "shasum-object": "^1.0.0", + "shell-quote": "^1.6.1", + "stream-browserify": "^3.0.0", + "stream-http": "^3.0.0", + "string_decoder": "^1.1.1", + "subarg": "^1.0.0", + "syntax-error": "^1.1.1", + "through2": "^2.0.0", + "timers-browserify": "^1.0.1", + "tty-browserify": "0.0.1", + "url": "~0.11.0", + "util": "~0.12.0", + "vm-browserify": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "dev": true, + "requires": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "dev": true, + "requires": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, + "buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", + "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==", + "dev": true + }, + "cached-path-relative": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.1.0.tgz", + "integrity": "sha512-WF0LihfemtesFcJgO7xfOoOcnWzY/QHR4qeDqV44jPU3HTI54+LnfXK3SA27AVVGCdZFgjjFFaqUA9Jx7dMJZA==", + "dev": true + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, + "chai": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", + "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^4.1.2", + "get-func-name": "^2.0.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + } + }, + "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" + } + }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", + "dev": true + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.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 + }, + "combine-source-map": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz", + "integrity": "sha512-UlxQ9Vw0b/Bt/KYwCFqdEwsQ1eL8d1gibiFb7lxQJFdvTgc2hIZi6ugsg+kyhzhPV+QEpUiEIwInIAIrgoEkrg==", + "dev": true, + "requires": { + "convert-source-map": "~1.1.0", + "inline-source-map": "~0.6.0", + "lodash.memoize": "~3.0.3", + "source-map": "~0.5.3" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", + "dev": true + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==", + "dev": true + }, + "convert-source-map": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", + "integrity": "sha512-Y8L5rp6jo+g9VEPgvqNfEopjTR4OTYct8lXlS8iVQdmnjDvbdbzYe9rjtFCB9egC86JoNCU61WRY+ScjkZpnIg==", + "dev": true + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "dash-ast": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-1.0.0.tgz", + "integrity": "sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA==", + "dev": true + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true + }, + "deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "defined": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", + "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==", + "dev": true + }, + "deps-sort": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.1.tgz", + "integrity": "sha512-1orqXQr5po+3KI6kQb9A4jnXT1PBwggGl2d7Sq2xsnOeI9GPcE/tGcF9UiSZtZBM7MukY4cAh7MemS6tZYipfw==", + "dev": true, + "requires": { + "JSONStream": "^1.0.3", + "shasum-object": "^1.0.0", + "subarg": "^1.0.0", + "through2": "^2.0.0" + } + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "detective": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", + "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", + "dev": true, + "requires": { + "acorn-node": "^1.8.2", + "defined": "^1.0.0", + "minimist": "^1.2.6" + } + }, + "diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true + }, + "duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "requires": { + "readable-stream": "^2.0.2" + } + }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "eslint": { + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.34.0.tgz", + "integrity": "sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg==", + "dev": true, + "requires": { + "@eslint/eslintrc": "^1.4.1", + "@humanwhocodes/config-array": "^0.11.8", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.4.0", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + } + }, + "eslint-config-google": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/eslint-config-google/-/eslint-config-google-0.14.0.tgz", + "integrity": "sha512-WsbX4WbjuMvTdeVL6+J3rK1RGhCTqjsFjX7UMSMgZiyxxaNLkoJENbrGExzERFeoTpGw3F3FypTiWAP9ZXzkEw==", + "dev": true, + "requires": {} + }, + "eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true + }, + "espree": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", + "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "dev": true, + "requires": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + } + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "dev": true + }, + "fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "requires": { + "is-callable": "^1.1.3" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "get-assigned-identifiers": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz", + "integrity": "sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "dev": true + }, + "get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.3" + } + }, + "grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dev": true, + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "htmlescape": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz", + "integrity": "sha512-eVcrzgbR4tim7c7soKQKtxa/kQM4TzjnlU83rcZ9bHU6t31ehfV7SktN6McWgwPWg+JYMA/O3qpGxBvFq1z2Jg==", + "dev": true + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==", + "dev": true + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true + }, + "ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true + }, + "immutable": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.2.4.tgz", + "integrity": "sha512-WDxL3Hheb1JkRN3sQkyujNlL/xRjAo3rJtaU5xeufUauG66JdMr32bLj4gF+vWl84DIA3Zxw7tiAjneYzRRw+w==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "inline-source-map": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz", + "integrity": "sha512-0mVWSSbNDvedDWIN4wxLsdPM4a7cIPcpyMxj3QZ406QRwQ6ePGB1YIHxVPjqpcUGbWQ5C+nHTwGNWAGvt7ggVA==", + "dev": true, + "requires": { + "source-map": "~0.5.3" + } + }, + "insert-module-globals": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.2.1.tgz", + "integrity": "sha512-ufS5Qq9RZN+Bu899eA9QCAYThY+gGW7oRkmb0vC93Vlyu/CFGcH0OYPEjVkDXA5FEbTt1+VWzdoOD3Ny9N+8tg==", + "dev": true, + "requires": { + "acorn-node": "^1.5.2", + "combine-source-map": "^0.8.0", + "concat-stream": "^1.6.1", + "is-buffer": "^1.1.0", + "JSONStream": "^1.0.3", + "path-is-absolute": "^1.0.1", + "process": "~0.11.0", + "through2": "^2.0.0", + "undeclared-identifiers": "^1.1.2", + "xtend": "^4.0.0" + } + }, + "is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true + }, + "is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, + "is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + } + }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "js-sdsl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", + "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true + }, + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "just-extend": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", + "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", + "dev": true + }, + "labeled-stream-splicer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.2.tgz", + "integrity": "sha512-Ca4LSXFFZUjPScRaqOcFxneA0VpKZr4MMYCljyQr4LIewTLb3Y0IUTIsnBBsVubIeEfxeSZpSjSsRM8APEQaAw==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "stream-splicer": "^2.0.0" + } + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "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, + "requires": { + "p-locate": "^5.0.0" + } + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "dev": true + }, + "lodash.memoize": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", + "integrity": "sha512-eDn9kqrAmVUC1wmZvlQ6Uhde44n+tXpqPrN8olQJbttgh0oKclk+SF54P47VEGE9CEiMeRwAP8BaM7UHvBkz2A==", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, + "loupe": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", + "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", + "dev": true, + "requires": { + "get-func-name": "^2.0.0" + } + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "dev": true + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true + }, + "mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true + }, + "mocha": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", + "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", + "dev": true, + "requires": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "dependencies": { + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "dependencies": { + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + } + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "module-deps": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.2.3.tgz", + "integrity": "sha512-fg7OZaQBcL4/L+AK5f4iVqf9OMbCclXfy/znXRxTVhJSeW5AIlS9AwheYwDaXM3lVW7OBeaeUEY3gbaC6cLlSA==", + "dev": true, + "requires": { + "browser-resolve": "^2.0.0", + "cached-path-relative": "^1.0.2", + "concat-stream": "~1.6.0", + "defined": "^1.0.0", + "detective": "^5.2.0", + "duplexer2": "^0.1.2", + "inherits": "^2.0.1", + "JSONStream": "^1.0.3", + "parents": "^1.0.0", + "readable-stream": "^2.0.2", + "resolve": "^1.4.0", + "stream-combiner2": "^1.1.1", + "subarg": "^1.0.0", + "through2": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "nise": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.4.tgz", + "integrity": "sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg==", + "dev": true, + "requires": { + "@sinonjs/commons": "^2.0.0", + "@sinonjs/fake-timers": "^10.0.2", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "path-to-regexp": "^1.7.0" + }, + "dependencies": { + "@sinonjs/fake-timers": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz", + "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==", + "dev": true, + "requires": { + "@sinonjs/commons": "^2.0.0" + } + } + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", + "dev": true + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parents": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", + "integrity": "sha512-mXKF3xkoUt5td2DoxpLmtOmZvko9VfFpwRwkKDHSNvgmpLAeBo18YDhcPbBzJq+QLCHMbGOfzia2cX4U+0v9Mg==", + "dev": true, + "requires": { + "path-platform": "~0.11.15" + } + }, + "parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "dev": true, + "requires": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "path-platform": { + "version": "0.11.15", + "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz", + "integrity": "sha512-Y30dB6rab1A/nfEKsZxmr01nUotHX0c/ZiIAsCTatEe1CmS5Pm5He7fZ195bPT7RdquoaL8lLxFCMQi/bS7IJg==", + "dev": true + }, + "path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dev": true, + "requires": { + "isarray": "0.0.1" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true + } + } + }, + "pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true + }, + "pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "dev": true + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==", + "dev": true + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "read-only-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz", + "integrity": "sha512-3ALe0bjBVZtkdWKIcThYpQCLbBMd/+Tbh2CDSrAIDO3UsZ4Xs+tnyjv2MjCOMMgBG+AsUOeuP1cgtY1INISc8w==", + "dev": true, + "requires": { + "readable-stream": "^2.0.2" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "sass": { + "version": "1.58.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.58.0.tgz", + "integrity": "sha512-PiMJcP33DdKtZ/1jSjjqVIKihoDc6yWmYr9K/4r3fVVIEDAluD0q7XZiRKrNJcPK3qkLRF/79DND1H5q1LBjgg==", + "dev": true, + "requires": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + } + }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shasum-object": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shasum-object/-/shasum-object-1.0.0.tgz", + "integrity": "sha512-Iqo5rp/3xVi6M4YheapzZhhGPVs0yZwHj7wvwQ1B9z8H6zk+FEnI7y3Teq7qwnekfEhu8WmG2z0z4iWZaxLWVg==", + "dev": true, + "requires": { + "fast-safe-stringify": "^2.0.7" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "shell-quote": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.0.tgz", + "integrity": "sha512-QHsz8GgQIGKlRi24yFc6a6lN69Idnx634w49ay6+jA5yFh7a1UY+4Rp6HPx/L/1zcEDPEij8cIsiqR6bQsE5VQ==", + "dev": true + }, + "simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true + }, + "sinon": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-14.0.2.tgz", + "integrity": "sha512-PDpV0ZI3ZCS3pEqx0vpNp6kzPhHrLx72wA0G+ZLaaJjLIYeE0n8INlgaohKuGy7hP0as5tbUd23QWu5U233t+w==", + "dev": true, + "requires": { + "@sinonjs/commons": "^2.0.0", + "@sinonjs/fake-timers": "^9.1.2", + "@sinonjs/samsam": "^7.0.1", + "diff": "^5.0.0", + "nise": "^5.1.2", + "supports-color": "^7.2.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dev": true + }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true + }, + "stream-browserify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", + "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "dev": true, + "requires": { + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "stream-combiner2": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "integrity": "sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==", + "dev": true, + "requires": { + "duplexer2": "~0.1.0", + "readable-stream": "^2.0.2" + } + }, + "stream-http": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.2.0.tgz", + "integrity": "sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "xtend": "^4.0.2" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "stream-splicer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.1.tgz", + "integrity": "sha512-Xizh4/NPuYSyAXyT7g8IvdJ9HJpxIGL9PjyhtywCZvvP0OPIdqyrr4dMikeuvY8xahpdKEBlBTySe583totajg==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.2" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "subarg": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", + "integrity": "sha512-RIrIdRY0X1xojthNcVtgT9sjpOGagEUKpZdgBUi054OEPFo282yg+zE+t1Rj3+RqKq2xStL7uUHhY+AjbC4BXg==", + "dev": true, + "requires": { + "minimist": "^1.1.0" + } + }, + "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" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, + "syntax-error": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz", + "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==", + "dev": true, + "requires": { + "acorn-node": "^1.2.0" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "timers-browserify": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz", + "integrity": "sha512-PIxwAupJZiYU4JmVZYwXp9FKsHMXb5h0ZEFyuXTAn8WLHOlcij+FEcbrvDsom1o5dr1YggEtFbECvGCW2sT53Q==", + "dev": true, + "requires": { + "process": "~0.11.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "tty-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", + "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", + "dev": true + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "dev": true + }, + "umd": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.3.tgz", + "integrity": "sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow==", + "dev": true + }, + "undeclared-identifiers": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/undeclared-identifiers/-/undeclared-identifiers-1.1.3.tgz", + "integrity": "sha512-pJOW4nxjlmfwKApE4zvxLScM/njmwj/DiUBv7EabwE4O8kRUy+HIwxQtZLBPll/jx1LJyBcqNfB3/cpv9EZwOw==", + "dev": true, + "requires": { + "acorn-node": "^1.3.0", + "dash-ast": "^1.0.0", + "get-assigned-identifiers": "^1.2.0", + "simple-concat": "^1.0.0", + "xtend": "^4.0.1" + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + }, + "dependencies": { + "punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true + } + } + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ==", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==", + "dev": true + } + } + }, + "util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true + }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + } + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + } + } +} diff --git a/package.json b/package.json index bb07108b..dd7b4856 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "scripts": { "unit": "mocha testing/**/unittest.js", + "build:ci": "npm run build:css && npm run build:jsapp", "build:css": "sass --no-source-map --no-error-css src/layout/css/style.scss src/pytest_html/resources/style.css", "build:jsapp": "browserify ./src/pytest_html/scripts/index.js > ./src/pytest_html/resources/app.js", "build": "npm run unit && npm run build:css && npm run build:jsapp" diff --git a/poetry.lock b/poetry.lock index f1a8dc4f..47302b52 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,36 +1,38 @@ [[package]] -name = "atomicwrites" -version = "1.4.0" -description = "Atomic file writes." -category = "main" +name = "assertpy" +version = "1.1" +description = "Simple assertion library for unit testing in python with a fluent API" +category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = "*" [[package]] name = "attrs" -version = "21.4.0" +version = "22.2.0" description = "Classes Without Boilerplate" category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.6" [package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] -docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] -tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] +cov = ["attrs[tests]", "coverage-enable-subprocess", "coverage[toml] (>=5.3)"] +dev = ["attrs[docs,tests]"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope.interface"] +tests = ["attrs[tests-no-zope]", "zope.interface"] +tests-no-zope = ["cloudpickle", "cloudpickle", "hypothesis", "hypothesis", "mypy (>=0.971,<0.990)", "mypy (>=0.971,<0.990)", "pympler", "pympler", "pytest (>=4.3.0)", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-mypy-plugins", "pytest-xdist[psutil]", "pytest-xdist[psutil]"] [[package]] name = "black" -version = "22.3.0" +version = "23.1.0" description = "The uncompromising code formatter." category = "dev" optional = false -python-versions = ">=3.6.2" +python-versions = ">=3.7" [package.dependencies] click = ">=8.0.0" mypy-extensions = ">=0.4.3" +packaging = ">=22.0" pathspec = ">=0.9.0" platformdirs = ">=2" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} @@ -65,20 +67,31 @@ importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [[package]] name = "colorama" -version = "0.4.4" +version = "0.4.6" description = "Cross-platform colored terminal text." category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" [[package]] name = "distlib" -version = "0.3.4" +version = "0.3.6" description = "Distribution utilities" category = "dev" optional = false python-versions = "*" +[[package]] +name = "exceptiongroup" +version = "1.1.0" +description = "Backport of PEP 654 (exception groups)" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +test = ["pytest (>=6)"] + [[package]] name = "execnet" version = "1.9.0" @@ -92,33 +105,33 @@ testing = ["pre-commit"] [[package]] name = "filelock" -version = "3.7.1" +version = "3.9.0" description = "A platform independent file lock." category = "dev" optional = false python-versions = ">=3.7" [package.extras] -docs = ["furo (>=2021.8.17b43)", "sphinx (>=4.1)", "sphinx-autodoc-typehints (>=1.12)"] -testing = ["covdefaults (>=1.2.0)", "coverage (>=4)", "pytest (>=4)", "pytest-cov", "pytest-timeout (>=1.4.2)"] +docs = ["furo (>=2022.12.7)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"] +testing = ["covdefaults (>=2.2.2)", "coverage (>=7.0.1)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-timeout (>=2.1)"] [[package]] name = "flake8" -version = "4.0.1" +version = "5.0.4" description = "the modular source code checker: pep8 pyflakes and co" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.6.1" [package.dependencies] -importlib-metadata = {version = "<4.3", markers = "python_version < \"3.8\""} -mccabe = ">=0.6.0,<0.7.0" -pycodestyle = ">=2.8.0,<2.9.0" -pyflakes = ">=2.4.0,<2.5.0" +importlib-metadata = {version = ">=1.1.0,<4.3", markers = "python_version < \"3.8\""} +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.9.0,<2.10.0" +pyflakes = ">=2.5.0,<2.6.0" [[package]] name = "identify" -version = "2.5.1" +version = "2.5.17" description = "File identification library for Python" category = "dev" optional = false @@ -140,16 +153,16 @@ typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} zipp = ">=0.5" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] +docs = ["jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "sphinx"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pep517", "pyfakefs", "pytest (>=4.6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy"] [[package]] name = "iniconfig" -version = "1.1.1" -description = "iniconfig: brain-dead simple config-ini parsing" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" category = "main" optional = false -python-versions = "*" +python-versions = ">=3.7" [[package]] name = "jinja2" @@ -167,7 +180,7 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "markupsafe" -version = "2.1.1" +version = "2.1.2" description = "Safely add untrusted strings to HTML/XML markup." category = "main" optional = false @@ -175,58 +188,61 @@ python-versions = ">=3.7" [[package]] name = "mccabe" -version = "0.6.1" +version = "0.7.0" description = "McCabe checker, plugin for flake8" category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.6" [[package]] name = "mypy-extensions" -version = "0.4.3" -description = "Experimental type system extensions for programs checked with the mypy typechecker." +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.5" [[package]] name = "nodeenv" -version = "1.6.0" +version = "1.7.0" description = "Node.js virtual environment builder" category = "dev" optional = false -python-versions = "*" +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" + +[package.dependencies] +setuptools = "*" [[package]] name = "packaging" -version = "21.3" +version = "23.0" description = "Core utilities for Python packages" category = "main" optional = false -python-versions = ">=3.6" - -[package.dependencies] -pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" +python-versions = ">=3.7" [[package]] name = "pathspec" -version = "0.9.0" +version = "0.11.0" description = "Utility library for gitignore style pattern matching of file paths." category = "dev" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +python-versions = ">=3.7" [[package]] name = "platformdirs" -version = "2.5.2" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +version = "2.6.2" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." category = "dev" optional = false python-versions = ">=3.7" +[package.dependencies] +typing-extensions = {version = ">=4.4", markers = "python_version < \"3.8\""} + [package.extras] -docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)", "sphinx (>=4)"] -test = ["appdirs (==1.4.4)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)", "pytest (>=6)"] +docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] [[package]] name = "pluggy" @@ -245,7 +261,7 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pre-commit" -version = "2.19.0" +version = "2.21.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." category = "dev" optional = false @@ -257,92 +273,67 @@ identify = ">=1.0.0" importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} nodeenv = ">=0.11.1" pyyaml = ">=5.1" -toml = "*" -virtualenv = ">=20.0.8" +virtualenv = ">=20.10.0" [[package]] name = "py" version = "1.11.0" description = "library with cross-python path, ini-parsing, io, code, log facilities" -category = "main" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "pycodestyle" -version = "2.8.0" +version = "2.9.1" description = "Python style guide checker" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.6" [[package]] name = "pyflakes" -version = "2.4.0" +version = "2.5.0" description = "passive checker of Python programs" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[[package]] -name = "pyparsing" -version = "3.0.9" -description = "pyparsing module - Classes and methods to define and execute parsing grammars" -category = "main" -optional = false -python-versions = ">=3.6.8" - -[package.extras] -diagrams = ["railroad-diagrams", "jinja2"] +python-versions = ">=3.6" [[package]] name = "pytest" -version = "6.2.5" +version = "7.2.1" description = "pytest: simple powerful testing with Python" category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] -atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} attrs = ">=19.2.0" colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<2.0" -py = ">=1.8.2" -toml = "*" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] -testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] - -[[package]] -name = "pytest-forked" -version = "1.4.0" -description = "run tests in isolated forked subprocesses" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -py = "*" -pytest = ">=3.10" +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] [[package]] -name = "pytest-localserver" -version = "0.5.1.post0" -description = "py.test plugin to test server connections locally." -category = "dev" +name = "pytest-metadata" +version = "2.0.4" +description = "pytest plugin for test session metadata" +category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +python-versions = ">=3.7,<4.0" [package.dependencies] -werkzeug = ">=0.10" +pytest = ">=3.0.0,<8.0.0" [[package]] name = "pytest-mock" -version = "3.7.0" +version = "3.10.0" description = "Thin-wrapper around the mock package for easier use with pytest" category = "dev" optional = false @@ -352,20 +343,19 @@ python-versions = ">=3.7" pytest = ">=5.0" [package.extras] -dev = ["pre-commit", "tox", "pytest-asyncio"] +dev = ["pre-commit", "pytest-asyncio", "tox"] [[package]] name = "pytest-xdist" -version = "2.5.0" -description = "pytest xdist plugin for distributed testing and loop-on-failing modes" +version = "3.2.0" +description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] execnet = ">=1.1" pytest = ">=6.2.0" -pytest-forked = "*" [package.extras] psutil = ["psutil (>=3.0)"] @@ -380,6 +370,19 @@ category = "dev" optional = false python-versions = ">=3.6" +[[package]] +name = "setuptools" +version = "67.2.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + [[package]] name = "six" version = "1.16.0" @@ -388,25 +391,17 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -[[package]] -name = "toml" -version = "0.10.2" -description = "Python Library for Tom's Obvious, Minimal Language" -category = "main" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" - [[package]] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" -category = "dev" +category = "main" optional = false python-versions = ">=3.7" [[package]] name = "tox" -version = "3.25.0" +version = "3.28.0" description = "tox is a generic virtualenv management and test command line tool" category = "dev" optional = false @@ -420,12 +415,12 @@ packaging = ">=14" pluggy = ">=0.12.0" py = ">=1.4.17" six = ">=1.14.0" -toml = ">=0.9.4" +tomli = {version = ">=2.0.1", markers = "python_version >= \"3.7\" and python_version < \"3.11\""} virtualenv = ">=16.0.0,<20.0.0 || >20.0.0,<20.0.1 || >20.0.1,<20.0.2 || >20.0.2,<20.0.3 || >20.0.3,<20.0.4 || >20.0.4,<20.0.5 || >20.0.5,<20.0.6 || >20.0.6,<20.0.7 || >20.0.7" [package.extras] docs = ["pygments-github-lexers (>=0.0.5)", "sphinx (>=2.0.0)", "sphinxcontrib-autoprogram (>=0.1.5)", "towncrier (>=18.5.0)"] -testing = ["flaky (>=3.4.0)", "freezegun (>=0.3.11)", "pytest (>=4.0.0)", "pytest-cov (>=2.5.1)", "pytest-mock (>=1.10.0)", "pytest-randomly (>=1.0.0)", "psutil (>=5.6.1)", "pathlib2 (>=2.3.3)"] +testing = ["flaky (>=3.4.0)", "freezegun (>=0.3.11)", "pathlib2 (>=2.3.3)", "psutil (>=5.6.1)", "pytest (>=4.0.0)", "pytest-cov (>=2.5.1)", "pytest-mock (>=1.10.0)", "pytest-randomly (>=1.0.0)"] [[package]] name = "typed-ast" @@ -437,7 +432,7 @@ python-versions = ">=3.6" [[package]] name = "typing-extensions" -version = "4.2.0" +version = "4.4.0" description = "Backported and Experimental Type Hints for Python 3.7+" category = "main" optional = false @@ -445,84 +440,73 @@ python-versions = ">=3.7" [[package]] name = "virtualenv" -version = "20.14.1" +version = "20.16.2" description = "Virtual Python Environment builder" category = "dev" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +python-versions = ">=3.6" [package.dependencies] distlib = ">=0.3.1,<1" filelock = ">=3.2,<4" importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} platformdirs = ">=2,<3" -six = ">=1.9.0,<2" [package.extras] docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=21.3)"] -testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)"] - -[[package]] -name = "werkzeug" -version = "2.1.2" -description = "The comprehensive WSGI web application library." -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.extras] -watchdog = ["watchdog"] +testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "packaging (>=20.0)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)"] [[package]] name = "zipp" -version = "3.8.0" +version = "3.13.0" description = "Backport of pathlib-compatible object wrapper for zip files" category = "main" optional = false python-versions = ">=3.7" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] [metadata] lock-version = "1.1" -python-versions = "^3.7" -content-hash = "752a2842a555ee3ca6663b191832cbb0d6b019f0c71c57ed250582bce477cc54" +python-versions = ">=3.7, <4.0.0" +content-hash = "288796311da5b8708fdc236a6a1e57c26f710715e13a2ff487beb9266eca38ac" [metadata.files] -atomicwrites = [ - {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, - {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, +assertpy = [ + {file = "assertpy-1.1.tar.gz", hash = "sha256:acc64329934ad71a3221de185517a43af33e373bb44dc05b5a9b174394ef4833"}, ] attrs = [ - {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, - {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, + {file = "attrs-22.2.0-py3-none-any.whl", hash = "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836"}, + {file = "attrs-22.2.0.tar.gz", hash = "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"}, ] black = [ - {file = "black-22.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2497f9c2386572e28921fa8bec7be3e51de6801f7459dffd6e62492531c47e09"}, - {file = "black-22.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5795a0375eb87bfe902e80e0c8cfaedf8af4d49694d69161e5bd3206c18618bb"}, - {file = "black-22.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e3556168e2e5c49629f7b0f377070240bd5511e45e25a4497bb0073d9dda776a"}, - {file = "black-22.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67c8301ec94e3bcc8906740fe071391bce40a862b7be0b86fb5382beefecd968"}, - {file = "black-22.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:fd57160949179ec517d32ac2ac898b5f20d68ed1a9c977346efbac9c2f1e779d"}, - {file = "black-22.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cc1e1de68c8e5444e8f94c3670bb48a2beef0e91dddfd4fcc29595ebd90bb9ce"}, - {file = "black-22.3.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2fc92002d44746d3e7db7cf9313cf4452f43e9ea77a2c939defce3b10b5c82"}, - {file = "black-22.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:a6342964b43a99dbc72f72812bf88cad8f0217ae9acb47c0d4f141a6416d2d7b"}, - {file = "black-22.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:328efc0cc70ccb23429d6be184a15ce613f676bdfc85e5fe8ea2a9354b4e9015"}, - {file = "black-22.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06f9d8846f2340dfac80ceb20200ea5d1b3f181dd0556b47af4e8e0b24fa0a6b"}, - {file = "black-22.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4efa5fad66b903b4a5f96d91461d90b9507a812b3c5de657d544215bb7877a"}, - {file = "black-22.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8477ec6bbfe0312c128e74644ac8a02ca06bcdb8982d4ee06f209be28cdf163"}, - {file = "black-22.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:637a4014c63fbf42a692d22b55d8ad6968a946b4a6ebc385c5505d9625b6a464"}, - {file = "black-22.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:863714200ada56cbc366dc9ae5291ceb936573155f8bf8e9de92aef51f3ad0f0"}, - {file = "black-22.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10dbe6e6d2988049b4655b2b739f98785a884d4d6b85bc35133a8fb9a2233176"}, - {file = "black-22.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:cee3e11161dde1b2a33a904b850b0899e0424cc331b7295f2a9698e79f9a69a0"}, - {file = "black-22.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5891ef8abc06576985de8fa88e95ab70641de6c1fca97e2a15820a9b69e51b20"}, - {file = "black-22.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:30d78ba6bf080eeaf0b7b875d924b15cd46fec5fd044ddfbad38c8ea9171043a"}, - {file = "black-22.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ee8f1f7228cce7dffc2b464f07ce769f478968bfb3dd1254a4c2eeed84928aad"}, - {file = "black-22.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ee227b696ca60dd1c507be80a6bc849a5a6ab57ac7352aad1ffec9e8b805f21"}, - {file = "black-22.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:9b542ced1ec0ceeff5b37d69838106a6348e60db7b8fdd245294dc1d26136265"}, - {file = "black-22.3.0-py3-none-any.whl", hash = "sha256:bc58025940a896d7e5356952228b68f793cf5fcb342be703c3a2669a1488cb72"}, - {file = "black-22.3.0.tar.gz", hash = "sha256:35020b8886c022ced9282b51b5a875b6d1ab0c387b31a065b84db7c33085ca79"}, + {file = "black-23.1.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:b6a92a41ee34b883b359998f0c8e6eb8e99803aa8bf3123bf2b2e6fec505a221"}, + {file = "black-23.1.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:57c18c5165c1dbe291d5306e53fb3988122890e57bd9b3dcb75f967f13411a26"}, + {file = "black-23.1.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:9880d7d419bb7e709b37e28deb5e68a49227713b623c72b2b931028ea65f619b"}, + {file = "black-23.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e6663f91b6feca5d06f2ccd49a10f254f9298cc1f7f49c46e498a0771b507104"}, + {file = "black-23.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:9afd3f493666a0cd8f8df9a0200c6359ac53940cbde049dcb1a7eb6ee2dd7074"}, + {file = "black-23.1.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:bfffba28dc52a58f04492181392ee380e95262af14ee01d4bc7bb1b1c6ca8d27"}, + {file = "black-23.1.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:c1c476bc7b7d021321e7d93dc2cbd78ce103b84d5a4cf97ed535fbc0d6660648"}, + {file = "black-23.1.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:382998821f58e5c8238d3166c492139573325287820963d2f7de4d518bd76958"}, + {file = "black-23.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bf649fda611c8550ca9d7592b69f0637218c2369b7744694c5e4902873b2f3a"}, + {file = "black-23.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:121ca7f10b4a01fd99951234abdbd97728e1240be89fde18480ffac16503d481"}, + {file = "black-23.1.0-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:a8471939da5e824b891b25751955be52ee7f8a30a916d570a5ba8e0f2eb2ecad"}, + {file = "black-23.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8178318cb74f98bc571eef19068f6ab5613b3e59d4f47771582f04e175570ed8"}, + {file = "black-23.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:a436e7881d33acaf2536c46a454bb964a50eff59b21b51c6ccf5a40601fbef24"}, + {file = "black-23.1.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:a59db0a2094d2259c554676403fa2fac3473ccf1354c1c63eccf7ae65aac8ab6"}, + {file = "black-23.1.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:0052dba51dec07ed029ed61b18183942043e00008ec65d5028814afaab9a22fd"}, + {file = "black-23.1.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:49f7b39e30f326a34b5c9a4213213a6b221d7ae9d58ec70df1c4a307cf2a1580"}, + {file = "black-23.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:162e37d49e93bd6eb6f1afc3e17a3d23a823042530c37c3c42eeeaf026f38468"}, + {file = "black-23.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:8b70eb40a78dfac24842458476135f9b99ab952dd3f2dab738c1881a9b38b753"}, + {file = "black-23.1.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:a29650759a6a0944e7cca036674655c2f0f63806ddecc45ed40b7b8aa314b651"}, + {file = "black-23.1.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:bb460c8561c8c1bec7824ecbc3ce085eb50005883a6203dcfb0122e95797ee06"}, + {file = "black-23.1.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:c91dfc2c2a4e50df0026f88d2215e166616e0c80e86004d0003ece0488db2739"}, + {file = "black-23.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a951cc83ab535d248c89f300eccbd625e80ab880fbcfb5ac8afb5f01a258ac9"}, + {file = "black-23.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:0680d4380db3719ebcfb2613f34e86c8e6d15ffeabcf8ec59355c5e7b85bb555"}, + {file = "black-23.1.0-py3-none-any.whl", hash = "sha256:7a0f701d314cfa0896b9001df70a530eb2472babb76086344e688829efd97d32"}, + {file = "black-23.1.0.tar.gz", hash = "sha256:b0bd97bea8903f5a2ba7219257a44e3f1f9d00073d6cc1add68f0beec69692ac"}, ] cfgv = [ {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, @@ -533,150 +517,156 @@ click = [ {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, ] colorama = [ - {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, - {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] distlib = [ - {file = "distlib-0.3.4-py2.py3-none-any.whl", hash = "sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b"}, - {file = "distlib-0.3.4.zip", hash = "sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579"}, + {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, + {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, +] +exceptiongroup = [ + {file = "exceptiongroup-1.1.0-py3-none-any.whl", hash = "sha256:327cbda3da756e2de031a3107b81ab7b3770a602c4d16ca618298c526f4bec1e"}, + {file = "exceptiongroup-1.1.0.tar.gz", hash = "sha256:bcb67d800a4497e1b404c2dd44fca47d3b7a5e5433dbab67f96c1a685cdfdf23"}, ] execnet = [ {file = "execnet-1.9.0-py2.py3-none-any.whl", hash = "sha256:a295f7cc774947aac58dde7fdc85f4aa00c42adf5d8f5468fc630c1acf30a142"}, {file = "execnet-1.9.0.tar.gz", hash = "sha256:8f694f3ba9cc92cab508b152dcfe322153975c29bda272e2fd7f3f00f36e47c5"}, ] filelock = [ - {file = "filelock-3.7.1-py3-none-any.whl", hash = "sha256:37def7b658813cda163b56fc564cdc75e86d338246458c4c28ae84cabefa2404"}, - {file = "filelock-3.7.1.tar.gz", hash = "sha256:3a0fd85166ad9dbab54c9aec96737b744106dc5f15c0b09a6744a445299fcf04"}, + {file = "filelock-3.9.0-py3-none-any.whl", hash = "sha256:f58d535af89bb9ad5cd4df046f741f8553a418c01a7856bf0d173bbc9f6bd16d"}, + {file = "filelock-3.9.0.tar.gz", hash = "sha256:7b319f24340b51f55a2bf7a12ac0755a9b03e718311dac567a0f4f7fabd2f5de"}, ] flake8 = [ - {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"}, - {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"}, + {file = "flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"}, + {file = "flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"}, ] identify = [ - {file = "identify-2.5.1-py2.py3-none-any.whl", hash = "sha256:0dca2ea3e4381c435ef9c33ba100a78a9b40c0bab11189c7cf121f75815efeaa"}, - {file = "identify-2.5.1.tar.gz", hash = "sha256:3d11b16f3fe19f52039fb7e39c9c884b21cb1b586988114fbe42671f03de3e82"}, + {file = "identify-2.5.17-py2.py3-none-any.whl", hash = "sha256:7d526dd1283555aafcc91539acc061d8f6f59adb0a7bba462735b0a318bff7ed"}, + {file = "identify-2.5.17.tar.gz", hash = "sha256:93cc61a861052de9d4c541a7acb7e3dcc9c11b398a2144f6e52ae5285f5f4f06"}, ] importlib-metadata = [ {file = "importlib_metadata-4.2.0-py3-none-any.whl", hash = "sha256:057e92c15bc8d9e8109738a48db0ccb31b4d9d5cfbee5a8670879a30be66304b"}, {file = "importlib_metadata-4.2.0.tar.gz", hash = "sha256:b7e52a1f8dec14a75ea73e0891f3060099ca1d8e6a462a4dff11c3e119ea1b31"}, ] iniconfig = [ - {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, - {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] jinja2 = [ {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, ] markupsafe = [ - {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, - {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-win32.whl", hash = "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-win32.whl", hash = "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-win32.whl", hash = "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-win32.whl", hash = "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-win32.whl", hash = "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed"}, + {file = "MarkupSafe-2.1.2.tar.gz", hash = "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d"}, ] mccabe = [ - {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, - {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, ] mypy-extensions = [ - {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, - {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] nodeenv = [ - {file = "nodeenv-1.6.0-py2.py3-none-any.whl", hash = "sha256:621e6b7076565ddcacd2db0294c0381e01fd28945ab36bcf00f41c5daf63bef7"}, - {file = "nodeenv-1.6.0.tar.gz", hash = "sha256:3ef13ff90291ba2a4a7a4ff9a979b63ffdd00a464dbe04acf0ea6471517a4c2b"}, + {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, + {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, ] packaging = [ - {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, - {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, + {file = "packaging-23.0-py3-none-any.whl", hash = "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2"}, + {file = "packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"}, ] pathspec = [ - {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, - {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, + {file = "pathspec-0.11.0-py3-none-any.whl", hash = "sha256:3a66eb970cbac598f9e5ccb5b2cf58930cd8e3ed86d393d541eaf2d8b1705229"}, + {file = "pathspec-0.11.0.tar.gz", hash = "sha256:64d338d4e0914e91c1792321e6907b5a593f1ab1851de7fc269557a21b30ebbc"}, ] platformdirs = [ - {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, - {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, + {file = "platformdirs-2.6.2-py3-none-any.whl", hash = "sha256:83c8f6d04389165de7c9b6f0c682439697887bca0aa2f1c87ef1826be3584490"}, + {file = "platformdirs-2.6.2.tar.gz", hash = "sha256:e1fea1fe471b9ff8332e229df3cb7de4f53eeea4998d3b6bfff542115e998bd2"}, ] pluggy = [ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] pre-commit = [ - {file = "pre_commit-2.19.0-py2.py3-none-any.whl", hash = "sha256:10c62741aa5704faea2ad69cb550ca78082efe5697d6f04e5710c3c229afdd10"}, - {file = "pre_commit-2.19.0.tar.gz", hash = "sha256:4233a1e38621c87d9dda9808c6606d7e7ba0e087cd56d3fe03202a01d2919615"}, + {file = "pre_commit-2.21.0-py2.py3-none-any.whl", hash = "sha256:e2f91727039fc39a92f58a588a25b87f936de6567eed4f0e673e0507edc75bad"}, + {file = "pre_commit-2.21.0.tar.gz", hash = "sha256:31ef31af7e474a8d8995027fefdfcf509b5c913ff31f2015b4ec4beb26a6f658"}, ] py = [ {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, ] pycodestyle = [ - {file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"}, - {file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"}, + {file = "pycodestyle-2.9.1-py2.py3-none-any.whl", hash = "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"}, + {file = "pycodestyle-2.9.1.tar.gz", hash = "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785"}, ] pyflakes = [ - {file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"}, - {file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"}, -] -pyparsing = [ - {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, - {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, + {file = "pyflakes-2.5.0-py2.py3-none-any.whl", hash = "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2"}, + {file = "pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"}, ] pytest = [ - {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, - {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, -] -pytest-forked = [ - {file = "pytest-forked-1.4.0.tar.gz", hash = "sha256:8b67587c8f98cbbadfdd804539ed5455b6ed03802203485dd2f53c1422d7440e"}, - {file = "pytest_forked-1.4.0-py3-none-any.whl", hash = "sha256:bbbb6717efc886b9d64537b41fb1497cfaf3c9601276be8da2cccfea5a3c8ad8"}, + {file = "pytest-7.2.1-py3-none-any.whl", hash = "sha256:c7c6ca206e93355074ae32f7403e8ea12163b1163c976fee7d4d84027c162be5"}, + {file = "pytest-7.2.1.tar.gz", hash = "sha256:d45e0952f3727241918b8fd0f376f5ff6b301cc0777c6f9a556935c92d8a7d42"}, ] -pytest-localserver = [ - {file = "pytest-localserver-0.5.1.post0.tar.gz", hash = "sha256:5ec7f8e6534cf03887af2cb59e577f169ac0e8b2fd2c3e3409280035f386d407"}, - {file = "pytest_localserver-0.5.1.post0-py3-none-any.whl", hash = "sha256:b3ff1b8bade571d54701bad3efd68ca1bb463ad88daa75da15cc8842809659ea"}, +pytest-metadata = [ + {file = "pytest_metadata-2.0.4-py3-none-any.whl", hash = "sha256:acb739f89fabb3d798c099e9e0c035003062367a441910aaaf2281bc1972ee14"}, + {file = "pytest_metadata-2.0.4.tar.gz", hash = "sha256:fcc653f65fe3035b478820b5284fbf0f52803622ee3f60a2faed7a7d3ba1f41e"}, ] pytest-mock = [ - {file = "pytest-mock-3.7.0.tar.gz", hash = "sha256:5112bd92cc9f186ee96e1a92efc84969ea494939c3aead39c50f421c4cc69534"}, - {file = "pytest_mock-3.7.0-py3-none-any.whl", hash = "sha256:6cff27cec936bf81dc5ee87f07132b807bcda51106b5ec4b90a04331cba76231"}, + {file = "pytest-mock-3.10.0.tar.gz", hash = "sha256:fbbdb085ef7c252a326fd8cdcac0aa3b1333d8811f131bdcc701002e1be7ed4f"}, + {file = "pytest_mock-3.10.0-py3-none-any.whl", hash = "sha256:f4c973eeae0282963eb293eb173ce91b091a79c1334455acfac9ddee8a1c784b"}, ] pytest-xdist = [ - {file = "pytest-xdist-2.5.0.tar.gz", hash = "sha256:4580deca3ff04ddb2ac53eba39d76cb5dd5edeac050cb6fbc768b0dd712b4edf"}, - {file = "pytest_xdist-2.5.0-py3-none-any.whl", hash = "sha256:6fe5c74fec98906deb8f2d2b616b5c782022744978e7bd4695d39c8f42d0ce65"}, + {file = "pytest-xdist-3.2.0.tar.gz", hash = "sha256:fa10f95a2564cd91652f2d132725183c3b590d9fdcdec09d3677386ecf4c1ce9"}, + {file = "pytest_xdist-3.2.0-py3-none-any.whl", hash = "sha256:336098e3bbd8193276867cc87db8b22903c3927665dff9d1ac8684c02f597b68"}, ] pyyaml = [ {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, @@ -686,6 +676,13 @@ pyyaml = [ {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, + {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, + {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, + {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, + {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, @@ -713,21 +710,21 @@ pyyaml = [ {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, ] +setuptools = [ + {file = "setuptools-67.2.0-py3-none-any.whl", hash = "sha256:16ccf598aab3b506593c17378473978908a2734d7336755a8769b480906bec1c"}, + {file = "setuptools-67.2.0.tar.gz", hash = "sha256:b440ee5f7e607bb8c9de15259dba2583dd41a38879a7abc1d43a71c59524da48"}, +] six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] -toml = [ - {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, - {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, -] tomli = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] tox = [ - {file = "tox-3.25.0-py2.py3-none-any.whl", hash = "sha256:0805727eb4d6b049de304977dfc9ce315a1938e6619c3ab9f38682bb04662a5a"}, - {file = "tox-3.25.0.tar.gz", hash = "sha256:37888f3092aa4e9f835fc8cc6dadbaaa0782651c41ef359e3a5743fcb0308160"}, + {file = "tox-3.28.0-py2.py3-none-any.whl", hash = "sha256:57b5ab7e8bb3074edc3c0c0b4b192a4f3799d3723b2c5b76f1fa9f2d40316eea"}, + {file = "tox-3.28.0.tar.gz", hash = "sha256:d0d28f3fe6d6d7195c27f8b054c3e99d5451952b54abdae673b71609a581f640"}, ] typed-ast = [ {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, @@ -756,18 +753,14 @@ typed-ast = [ {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, ] typing-extensions = [ - {file = "typing_extensions-4.2.0-py3-none-any.whl", hash = "sha256:6657594ee297170d19f67d55c05852a874e7eb634f4f753dbd667855e07c1708"}, - {file = "typing_extensions-4.2.0.tar.gz", hash = "sha256:f1c24655a0da0d1b67f07e17a5e6b2a105894e6824b92096378bb3668ef02376"}, + {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, + {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, ] virtualenv = [ - {file = "virtualenv-20.14.1-py2.py3-none-any.whl", hash = "sha256:e617f16e25b42eb4f6e74096b9c9e37713cf10bf30168fb4a739f3fa8f898a3a"}, - {file = "virtualenv-20.14.1.tar.gz", hash = "sha256:ef589a79795589aada0c1c5b319486797c03b67ac3984c48c669c0e4f50df3a5"}, -] -werkzeug = [ - {file = "Werkzeug-2.1.2-py3-none-any.whl", hash = "sha256:72a4b735692dd3135217911cbeaa1be5fa3f62bffb8745c5215420a03dc55255"}, - {file = "Werkzeug-2.1.2.tar.gz", hash = "sha256:1ce08e8093ed67d638d63879fd1ba3735817f7a80de3674d293f5984f25fb6e6"}, + {file = "virtualenv-20.16.2-py2.py3-none-any.whl", hash = "sha256:635b272a8e2f77cb051946f46c60a54ace3cb5e25568228bd6b57fc70eca9ff3"}, + {file = "virtualenv-20.16.2.tar.gz", hash = "sha256:0ef5be6d07181946891f5abc8047fda8bc2f0b4b9bf222c64e6e8963baee76db"}, ] zipp = [ - {file = "zipp-3.8.0-py3-none-any.whl", hash = "sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099"}, - {file = "zipp-3.8.0.tar.gz", hash = "sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad"}, + {file = "zipp-3.13.0-py3-none-any.whl", hash = "sha256:e8b2a36ea17df80ffe9e2c4fda3f693c3dad6df1697d3cd3af232db680950b0b"}, + {file = "zipp-3.13.0.tar.gz", hash = "sha256:23f70e964bc11a34cef175bc90ba2914e1e4545ea1e3e2f67c079671883f9cb6"}, ] diff --git a/pyproject.toml b/pyproject.toml index 008ce854..25792869 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,22 +37,25 @@ include = [ { path = "testing", format = "sdist" }, { path = "docs", format = "sdist" }, "src/pytest_html/resources", + "src/pytest_html/resources/app.js", ] [tool.poetry.dependencies] -python = "^3.7" -pytest = "^7.0.0" -pytest-metadata = "^2.0.2" -Jinja2 = "^3.0.0" +python = ">=3.7" +pytest = ">=7.0.0" +pytest-metadata = ">=2.0.2" +Jinja2 = ">=3.0.0" [tool.poetry.dev-dependencies] -assertpy = "^1.1" -black = "^22.1.0" -flake8 = "^4.0.1" -pre-commit = "^2.17.0" -pytest-xdist = "^2.4.0" -pytest-mock = "^3.7.0" -tox = "^3.24.5" +assertpy = ">=1.1" +beautifulsoup4 = ">=4.11.1" +black = ">=22.1.0" +flake8 = ">=4.0.1" +pre-commit = ">=2.17.0" +pytest-xdist = ">=2.4.0" +pytest-mock = ">=3.7.0" +selenium = ">=4.3.0" +tox = ">=3.24.5" [tool.poetry.plugins.pytest11] html = "pytest_html.plugin" diff --git a/src/pytest_html/html_report.py b/src/pytest_html/html_report.py deleted file mode 100644 index 28afe823..00000000 --- a/src/pytest_html/html_report.py +++ /dev/null @@ -1,342 +0,0 @@ -import bisect -import datetime -import json -import os -import re -import time -from collections import defaultdict -from collections import OrderedDict - -from py.xml import html -from py.xml import raw - -from . import __pypi_url__ -from . import __version__ -from .outcome import Outcome -from .result import TestResult -from .util import ansi_support - - -class HTMLReport: - def __init__(self, logfile, config): - logfile = os.path.expanduser(os.path.expandvars(logfile)) - self.logfile = os.path.abspath(logfile) - self.test_logs = [] - self.title = os.path.basename(self.logfile) - self.results = [] - self.errors = self.failed = 0 - self.passed = self.skipped = 0 - self.xfailed = self.xpassed = 0 - has_rerun = config.pluginmanager.hasplugin("rerunfailures") - self.rerun = 0 if has_rerun else None - self.self_contained = config.getoption("self_contained_html") - self.config = config - self.reports = defaultdict(list) - - def _appendrow(self, outcome, report): - result = TestResult(outcome, report, self.logfile, self.config) - if result.row_table is not None: - index = bisect.bisect_right(self.results, result) - self.results.insert(index, result) - tbody = html.tbody( - result.row_table, - class_="{} results-table-row".format(result.outcome.lower()), - ) - if result.row_extra is not None: - tbody.append(result.row_extra) - self.test_logs.insert(index, tbody) - - def append_passed(self, report): - if report.when == "call": - if hasattr(report, "wasxfail"): - self.xpassed += 1 - self._appendrow("XPassed", report) - else: - self.passed += 1 - self._appendrow("Passed", report) - - def append_failed(self, report): - if getattr(report, "when", None) == "call": - if hasattr(report, "wasxfail"): - # pytest < 3.0 marked xpasses as failures - self.xpassed += 1 - self._appendrow("XPassed", report) - else: - self.failed += 1 - self._appendrow("Failed", report) - else: - self.errors += 1 - self._appendrow("Error", report) - - def append_rerun(self, report): - self.rerun += 1 - self._appendrow("Rerun", report) - - def append_skipped(self, report): - if hasattr(report, "wasxfail"): - self.xfailed += 1 - self._appendrow("XFailed", report) - else: - self.skipped += 1 - self._appendrow("Skipped", report) - - def _generate_report(self, session): - suite_stop_time = time.time() - suite_time_delta = suite_stop_time - self.suite_start_time - numtests = self.passed + self.failed + self.xpassed + self.xfailed - generated = datetime.datetime.now() - - with open( - os.path.join(os.path.dirname(__file__), "resources", "style.css") - ) as style_css_fp: - self.style_css = style_css_fp.read() - - if ansi_support(): - ansi_css = [ - "\n/******************************", - " * ANSI2HTML STYLES", - " ******************************/\n", - ] - ansi_css.extend([str(r) for r in ansi_support().style.get_styles()]) - self.style_css += "\n".join(ansi_css) - - # Add user-provided CSS - for path in self.config.getoption("css"): - self.style_css += "\n/******************************" - self.style_css += "\n * CUSTOM CSS" - self.style_css += f"\n * {path}" - self.style_css += "\n ******************************/\n\n" - with open(path) as f: - self.style_css += f.read() - - css_href = "assets/style.css" - html_css = html.link(href=css_href, rel="stylesheet", type="text/css") - if self.self_contained: - html_css = html.style(raw(self.style_css)) - - session.config.hook.pytest_html_report_title(report=self) - - head = html.head(html.meta(charset="utf-8"), html.title(self.title), html_css) - - outcomes = [ - Outcome("passed", self.passed), - Outcome("skipped", self.skipped), - Outcome("failed", self.failed), - Outcome("error", self.errors, label="errors"), - Outcome("xfailed", self.xfailed, label="expected failures"), - Outcome("xpassed", self.xpassed, label="unexpected passes"), - ] - - if self.rerun is not None: - outcomes.append(Outcome("rerun", self.rerun)) - - summary = [ - html.p(f"{numtests} tests ran in {suite_time_delta:.2f} seconds. "), - html.p( - "(Un)check the boxes to filter the results.", - class_="filter", - hidden="true", - ), - ] - - for i, outcome in enumerate(outcomes, start=1): - summary.append(outcome.checkbox) - summary.append(outcome.summary_item) - if i < len(outcomes): - summary.append(", ") - - cells = [ - html.th("Result", class_="sortable result initial-sort", col="result"), - html.th("Test", class_="sortable", col="name"), - html.th("Duration", class_="sortable", col="duration"), - html.th("Links", class_="sortable links", col="links"), - ] - session.config.hook.pytest_html_results_table_header(cells=cells) - - results = [ - html.h2("Results"), - html.table( - [ - html.thead( - html.tr(cells), - html.tr( - [ - html.th( - "No results found. Try to check the filters", - colspan=len(cells), - ) - ], - id="not-found-message", - hidden="true", - ), - id="results-table-head", - ), - self.test_logs, - ], - id="results-table", - ), - ] - - with open( - os.path.join(os.path.dirname(__file__), "resources", "old_main.js") - ) as main_js_fp: - main_js = main_js_fp.read() - - body = html.body( - html.script(raw(main_js)), - html.h1(self.title), - html.p( - "Report generated on {} at {} by ".format( - generated.strftime("%d-%b-%Y"), generated.strftime("%H:%M:%S") - ), - html.a("pytest-html", href=__pypi_url__), - f" v{__version__}", - ), - onLoad="init()", - ) - - body.extend(self._generate_environment(session.config)) - - summary_prefix, summary_postfix = [], [] - session.config.hook.pytest_html_results_summary( - prefix=summary_prefix, summary=summary, postfix=summary_postfix - ) - body.extend([html.h2("Summary")] + summary_prefix + summary + summary_postfix) - - body.extend(results) - - doc = html.html(head, body) - - unicode_doc = "\n{}".format(doc.unicode(indent=2)) - - # Fix encoding issues, e.g. with surrogates - unicode_doc = unicode_doc.encode("utf-8", errors="xmlcharrefreplace") - return unicode_doc.decode("utf-8") - - def _generate_environment(self, config): - if not hasattr(config, "_metadata") or config._metadata is None: - return [] - - metadata = config._metadata - environment = [html.h2("Environment")] - rows = [] - - keys = [k for k in metadata.keys()] - if not isinstance(metadata, OrderedDict): - keys.sort() - - for key in keys: - value = metadata[key] - if self._is_redactable_environment_variable(key, config): - black_box_ascii_value = 0x2593 - value = "".join(chr(black_box_ascii_value) for char in str(value)) - - if isinstance(value, str) and value.startswith("http"): - value = html.a(value, href=value, target="_blank") - elif isinstance(value, (list, tuple, set)): - value = ", ".join(str(i) for i in sorted(map(str, value))) - elif isinstance(value, dict): - sorted_dict = {k: value[k] for k in sorted(value)} - value = json.dumps(sorted_dict) - raw_value_string = raw(str(value)) - rows.append(html.tr(html.td(key), html.td(raw_value_string))) - - environment.append(html.table(rows, id="environment")) - return environment - - def _is_redactable_environment_variable(self, environment_variable, config): - redactable_regexes = config.getini("environment_table_redact_list") - for redactable_regex in redactable_regexes: - if re.match(redactable_regex, environment_variable): - return True - - return False - - def _save_report(self, report_content): - dir_name = os.path.dirname(self.logfile) - assets_dir = os.path.join(dir_name, "assets") - - os.makedirs(dir_name, exist_ok=True) - if not self.self_contained: - os.makedirs(assets_dir, exist_ok=True) - - with open(self.logfile, "w", encoding="utf-8") as f: - f.write(report_content) - if not self.self_contained: - style_path = os.path.join(assets_dir, "style.css") - with open(style_path, "w", encoding="utf-8") as f: - f.write(self.style_css) - - def _post_process_reports(self): - for test_name, test_reports in self.reports.items(): - report_outcome = "passed" - wasxfail = False - failure_when = None - full_text = "" - extras = [] - duration = 0.0 - - # in theory the last one should have all logs so we just go - # through them all to figure out the outcome, xfail, duration, - # extras, and when it swapped from pass - for test_report in test_reports: - if test_report.outcome == "rerun": - # reruns are separate test runs for all intensive purposes - self.append_rerun(test_report) - else: - full_text += test_report.longreprtext - extras.extend(getattr(test_report, "extra", [])) - duration += getattr(test_report, "duration", 0.0) - - if ( - test_report.outcome not in ("passed", "rerun") - and report_outcome == "passed" - ): - report_outcome = test_report.outcome - failure_when = test_report.when - - if hasattr(test_report, "wasxfail"): - wasxfail = True - - # the following test_report. = settings come at the end of us - # looping through all test_reports that make up a single - # case. - - # outcome on the right comes from the outcome of the various - # test_reports that make up this test case - # we are just carrying it over to the final report. - test_report.outcome = report_outcome - test_report.when = "call" - test_report.nodeid = test_name - test_report.longrepr = full_text - test_report.extra = extras - test_report.duration = duration - - if wasxfail: - test_report.wasxfail = True - - if test_report.outcome == "passed": - self.append_passed(test_report) - elif test_report.outcome == "skipped": - self.append_skipped(test_report) - elif test_report.outcome == "failed": - test_report.when = failure_when - self.append_failed(test_report) - - def pytest_runtest_logreport(self, report): - self.reports[report.nodeid].append(report) - - def pytest_collectreport(self, report): - if report.failed: - self.append_failed(report) - - def pytest_sessionstart(self, session): - self.suite_start_time = time.time() - - def pytest_sessionfinish(self, session): - self._post_process_reports() - report_content = self._generate_report(session) - self._save_report(report_content) - - def pytest_terminal_summary(self, terminalreporter): - terminalreporter.write_sep("-", f"generated html file: file://{self.logfile}") diff --git a/src/pytest_html/nextgen.py b/src/pytest_html/nextgen.py index 2a344d28..031e1ee2 100644 --- a/src/pytest_html/nextgen.py +++ b/src/pytest_html/nextgen.py @@ -5,10 +5,10 @@ import os import pytest import re -import shutil import warnings from collections import defaultdict +from functools import partial from pathlib import Path from jinja2 import Environment from jinja2 import FileSystemLoader @@ -19,6 +19,21 @@ from .util import cleanup_unserializable +try: + from ansi2html import Ansi2HTMLConverter, style + + converter = Ansi2HTMLConverter( + inline=False, escaped=False + ) + _handle_ansi = partial(converter.convert, full=False) + _ansi_styles = style.get_styles() +except ImportError: + from _pytest.logging import _remove_ansi_escape_sequences + + _handle_ansi = _remove_ansi_escape_sequences + _ansi_styles = [] + + class BaseReport(object): class Cells(object): def __init__(self): @@ -59,22 +74,22 @@ def title(self): def title(self, title): self._data["title"] = title - def __init__(self, report_path, config): - self._report_path = Path(os.path.expandvars(report_path)).expanduser().resolve() + def __init__(self, report_path, config, default_css="style.css"): + self._report_path = Path(os.path.expandvars(report_path)).expanduser() self._report_path.parent.mkdir(parents=True, exist_ok=True) - self._resources_path = Path(__file__).parent.joinpath("resources") - self._config = config - self._css = None - self._template = None - self._template_filename = "index.jinja2" - + self._template = _read_template([self._resources_path]) + self._css = _process_css(Path(self._resources_path, default_css), self._config.getoption("css")) self._duration_format = config.getini("duration_format") self._max_asset_filename_length = int(config.getini("max_asset_filename_length")) - self._report = self.Report(self._report_path.name, self._duration_format) + @property + def css(self): + # implement in subclasses + return + def _asset_filename(self, test_id, extra_index, test_index, file_extension): return "{}_{}_{}.{}".format( re.sub(r"[^\w.]", "_", test_id), @@ -89,7 +104,7 @@ def _generate_report(self, self_contained=False): generated.strftime("%d-%b-%Y"), generated.strftime("%H:%M:%S"), __version__, - self._css, + self.css, self_contained=self_contained, test_data=cleanup_unserializable(self._report.data), prefix=self._report.data["additionalSummary"]["prefix"], @@ -148,15 +163,6 @@ def _process_extras(self, report, test_id): return report_extras - def _read_template(self, search_paths): - env = Environment( - loader=FileSystemLoader(search_paths), - autoescape=select_autoescape( - enabled_extensions=('jinja2',), - ), - ) - return env.get_template(self._template_filename) - def _render_html( self, date, @@ -212,7 +218,7 @@ def pytest_sessionfinish(self, session): @pytest.hookimpl(trylast=True) def pytest_terminal_summary(self, terminalreporter): - terminalreporter.write_sep("-", f"Generated html report: file://{self._report_path}") + terminalreporter.write_sep("-", f"Generated html report: file://{self._report_path.resolve()}") @pytest.hookimpl(trylast=True) def pytest_collection_finish(self, session): @@ -229,7 +235,9 @@ def pytest_runtest_logreport(self, report): test_id += f"::{report.when}" data["nodeid"] = test_id - data["longreprtext"] = report.longreprtext or "No log output captured." + # Order here matters! + log = report.longreprtext or report.capstdout or "No log output captured." + data["longreprtext"] = _handle_ansi(log) data["outcome"] = _process_outcome(report) @@ -249,17 +257,18 @@ def pytest_runtest_logreport(self, report): class NextGenReport(BaseReport): def __init__(self, report_path, config): super().__init__(report_path, config) - self._assets_path = Path("assets") + self._assets_path = Path(self._report_path.parent, "assets") self._assets_path.mkdir(parents=True, exist_ok=True) - self._default_css_path = Path(self._resources_path, "style.css") + self._css_path = Path(self._assets_path, "style.css") - self._template = self._read_template( - [self._resources_path, self._assets_path] - ) + with self._css_path.open("w", encoding="utf-8") as f: + f.write(self._css) - # Copy default css file (style.css) to assets directory - new_css_path = shutil.copy(self._default_css_path, self._assets_path) - self._css = [new_css_path] + self._config.getoption("css") + @property + def css(self): + # print("woot", Path(self._assets_path.name, "style.css")) + # print("waat", self._css_path.relative_to(self._report_path.parent)) + return Path(self._assets_path.name, "style.css") def _data_content(self, content, asset_name, *args, **kwargs): content = content.encode("utf-8") @@ -275,18 +284,17 @@ def _media_content(self, content, asset_name, *args, **kwargs): def _write_content(self, content, asset_name): content_relative_path = Path(self._assets_path, asset_name) - Path(self._report_path.parent, content_relative_path).write_bytes(content) - return str(content_relative_path) + content_relative_path.write_bytes(content) + return str(content_relative_path.relative_to(self._report_path.parent)) class NextGenSelfContainedReport(BaseReport): def __init__(self, report_path, config): super().__init__(report_path, config) - self._template = self._read_template( - [self._resources_path] - ) - self._css = ["style.css"] + self._config.getoption("css") + @property + def css(self): + return self._css def _data_content(self, content, mime_type, *args, **kwargs): charset = "utf-8" @@ -311,6 +319,32 @@ def _generate_report(self, *args, **kwargs): super()._generate_report(self_contained=True) +def _process_css(default_css, extra_css): + with open(default_css, encoding="utf-8") as f: + css = f.read() + + # Add user-provided CSS + for path in extra_css: + css += "\n/******************************" + css += "\n * CUSTOM CSS" + css += f"\n * {path}" + css += "\n ******************************/\n\n" + with open(path, encoding="utf-8") as f: + css += f.read() + + # ANSI support + if _ansi_styles: + ansi_css = [ + "\n/******************************", + " * ANSI2HTML STYLES", + " ******************************/\n", + ] + ansi_css.extend([str(r) for r in _ansi_styles]) + css += "\n".join(ansi_css) + + return css + + def _process_outcome(report): if report.when in ["setup", "teardown"] and report.outcome == "failed": return "Error" @@ -321,3 +355,13 @@ def _process_outcome(report): return "XFailed" return report.outcome.capitalize() + + +def _read_template(search_paths, template_name="index.jinja2"): + env = Environment( + loader=FileSystemLoader(search_paths), + autoescape=select_autoescape( + enabled_extensions=('jinja2',), + ), + ) + return env.get_template(template_name) diff --git a/src/pytest_html/outcome.py b/src/pytest_html/outcome.py deleted file mode 100644 index 1bb71acd..00000000 --- a/src/pytest_html/outcome.py +++ /dev/null @@ -1,33 +0,0 @@ -from py.xml import html - - -class Outcome: - def __init__(self, outcome, total=0, label=None, test_result=None, class_html=None): - self.outcome = outcome - self.label = label or outcome - self.class_html = class_html or outcome - self.total = total - self.test_result = test_result or outcome - - self.generate_checkbox() - self.generate_summary_item() - - def generate_checkbox(self): - checkbox_kwargs = {"data-test-result": self.test_result.lower()} - if self.total == 0: - checkbox_kwargs["disabled"] = "true" - - self.checkbox = html.input( - type="checkbox", - checked="true", - onChange="filterTable(this)", - name="filter_checkbox", - class_="filter", - hidden="true", - **checkbox_kwargs, - ) - - def generate_summary_item(self): - self.summary_item = html.span( - f"{self.total} {self.label}", class_=self.class_html - ) diff --git a/src/pytest_html/plugin.py b/src/pytest_html/plugin.py index 103be5d5..9a8c8ac5 100644 --- a/src/pytest_html/plugin.py +++ b/src/pytest_html/plugin.py @@ -6,7 +6,6 @@ from pathlib import Path from . import extras # noqa: F401 -from .html_report import HTMLReport from .nextgen import NextGenReport from .nextgen import NextGenSelfContainedReport @@ -43,12 +42,6 @@ def pytest_addoption(parser): default=[], help="append given css file content to report style file.", ) - group.addoption( - "--next-gen", - action="store_true", - default=False, - help="use next-gen report.", - ) parser.addini( "duration_format", default=None, @@ -91,14 +84,10 @@ def pytest_configure(config): if not hasattr(config, "workerinput"): # prevent opening html_path on worker nodes (xdist) - - if not config.getoption("next_gen"): - html = HTMLReport(html_path, config) + if config.getoption("self_contained_html"): + html = NextGenSelfContainedReport(html_path, config) else: - if config.getoption("self_contained_html"): - html = NextGenSelfContainedReport(html_path, config) - else: - html = NextGenReport(html_path, config) + html = NextGenReport(html_path, config) config.pluginmanager.register(html) diff --git a/src/pytest_html/resources/index.jinja2 b/src/pytest_html/resources/index.jinja2 index 1819f0a0..fef03bdb 100644 --- a/src/pytest_html/resources/index.jinja2 +++ b/src/pytest_html/resources/index.jinja2 @@ -5,14 +5,10 @@ {% if self_contained %} {% else %} - {% for style in styles -%} - - {% endfor -%} + {% endif %} diff --git a/src/pytest_html/result.py b/src/pytest_html/result.py deleted file mode 100644 index bb95812d..00000000 --- a/src/pytest_html/result.py +++ /dev/null @@ -1,287 +0,0 @@ -import json -import os -import re -import time -import warnings -from base64 import b64decode -from base64 import b64encode -from html import escape -from os.path import isfile - -from _pytest.logging import _remove_ansi_escape_sequences -from py.xml import html -from py.xml import raw - -from . import extras -from .util import ansi_support - - -class TestResult: - def __init__(self, outcome, report, logfile, config): - self.test_id = report.nodeid.encode("utf-8").decode("unicode_escape") - if getattr(report, "when", "call") != "call": - self.test_id = "::".join([report.nodeid, report.when]) - self.time = getattr(report, "duration", 0.0) - self.formatted_time = self._format_time(report) - self.outcome = outcome - self.additional_html = [] - self.links_html = [] - self.self_contained = config.getoption("self_contained_html") - self.max_asset_filename_length = int(config.getini("max_asset_filename_length")) - self.logfile = logfile - self.config = config - self.row_table = self.row_extra = None - - test_index = hasattr(report, "rerun") and report.rerun + 1 or 0 - - for extra_index, extra in enumerate(getattr(report, "extra", [])): - self.append_extra_html(extra, extra_index, test_index) - - self.append_log_html( - report, - self.additional_html, - config.option.capture, - config.option.showcapture, - ) - - cells = [ - html.td(self.outcome, class_="col-result"), - html.td(self.test_id, class_="col-name"), - html.td(self.formatted_time, class_="col-duration"), - html.td(self.links_html, class_="col-links"), - ] - - self.config.hook.pytest_html_results_table_row(report=report, cells=cells) - - self.config.hook.pytest_html_results_table_html( - report=report, data=self.additional_html - ) - - if len(cells) > 0: - tr_class = None - if self.config.getini("render_collapsed"): - tr_class = "collapsed" - self.row_table = html.tr(cells) - self.row_extra = html.tr( - html.td(self.additional_html, class_="extra", colspan=len(cells)), - class_=tr_class, - ) - - def __lt__(self, other): - order = ( - "Error", - "Failed", - "Rerun", - "XFailed", - "XPassed", - "Skipped", - "Passed", - ) - return order.index(self.outcome) < order.index(other.outcome) - - def create_asset(self, content, extra_index, test_index, file_extension, mode="w"): - asset_file_name = "{}_{}_{}.{}".format( - re.sub(r"[^\w\.]", "_", self.test_id), - str(extra_index), - str(test_index), - file_extension, - )[-self.max_asset_filename_length:] - asset_path = os.path.join( - os.path.dirname(self.logfile), "assets", asset_file_name - ) - - os.makedirs(os.path.dirname(asset_path), exist_ok=True) - - relative_path = f"assets/{asset_file_name}" - - kwargs = {"encoding": "utf-8"} if "b" not in mode else {} - with open(asset_path, mode, **kwargs) as f: - f.write(content) - return relative_path - - def append_extra_html(self, extra, extra_index, test_index): - href = None - if extra.get("format_type") == extras.FORMAT_IMAGE: - self._append_image(extra, extra_index, test_index) - - elif extra.get("format_type") == extras.FORMAT_HTML: - self.additional_html.append(html.div(raw(extra.get("content")))) - - elif extra.get("format_type") == extras.FORMAT_JSON: - content = json.dumps(extra.get("content")) - if self.self_contained: - href = self._data_uri(content, mime_type=extra.get("mime_type")) - else: - href = self.create_asset( - content, extra_index, test_index, extra.get("extension") - ) - - elif extra.get("format_type") == extras.FORMAT_TEXT: - content = extra.get("content") - if isinstance(content, bytes): - content = content.decode("utf-8") - if self.self_contained: - href = self._data_uri(content) - else: - href = self.create_asset( - content, extra_index, test_index, extra.get("extension") - ) - - elif extra.get("format_type") == extras.FORMAT_URL: - href = extra.get("content") - - elif extra.get("format_type") == extras.FORMAT_VIDEO: - self._append_video(extra, extra_index, test_index) - - if href is not None: - self.links_html.append( - html.a( - extra.get("name"), - class_=extra.get("format_type"), - href=href, - target="_blank", - ) - ) - self.links_html.append(" ") - - def _format_time(self, report): - # parse the report duration into its display version and return - # it to the caller - duration = getattr(report, "duration", None) - if duration is None: - return "" - - duration_formatter = getattr(report, "duration_formatter", None) - string_duration = str(duration) - if duration_formatter is None: - if "." in string_duration: - split_duration = string_duration.split(".") - split_duration[1] = split_duration[1][0:2] - - string_duration = ".".join(split_duration) - - return string_duration - else: - # support %f, since time.strftime doesn't support it out of the box - # keep a precision of 2 for legacy reasons - formatted_milliseconds = "00" - if "." in string_duration: - milliseconds = string_duration.split(".")[1] - formatted_milliseconds = milliseconds[0:2] - - duration_formatter = duration_formatter.replace( - "%f", formatted_milliseconds - ) - duration_as_gmtime = time.gmtime(report.duration) - return time.strftime(duration_formatter, duration_as_gmtime) - - def _populate_html_log_div(self, log, report): - if report.longrepr: - # longreprtext is only filled out on failure by pytest - # otherwise will be None. - # Use full_text if longreprtext is None-ish - # we added full_text elsewhere in this file. - text = report.longreprtext or report.full_text - for line in text.splitlines(): - separator = line.startswith("_ " * 10) - if separator: - log.append(line[:80]) - else: - exception = line.startswith("E ") - if exception: - log.append(html.span(raw(escape(line)), class_="error")) - else: - log.append(raw(escape(line))) - log.append(html.br()) - - for section in report.sections: - header, content = map(escape, section) - log.append(f" {header:-^80} ") - log.append(html.br()) - - if ansi_support(): - converter = ansi_support().Ansi2HTMLConverter( - inline=False, escaped=False - ) - content = converter.convert(content, full=False) - else: - content = _remove_ansi_escape_sequences(content) - - log.append(raw(content)) - log.append(html.br()) - - def append_log_html( - self, - report, - additional_html, - pytest_capture_value, - pytest_show_capture_value, - ): - log = html.div(class_="log") - - should_skip_captured_output = pytest_capture_value == "no" - if report.outcome == "failed" and not should_skip_captured_output: - should_skip_captured_output = pytest_show_capture_value == "no" - if not should_skip_captured_output: - self._populate_html_log_div(log, report) - - if len(log) == 0: - log = html.div(class_="empty log") - log.append("No log output captured.") - - additional_html.append(log) - - def _make_media_html_div( - self, extra, extra_index, test_index, base_extra_string, base_extra_class - ): - content = extra.get("content") - try: - is_uri_or_path = content.startswith(("file", "http")) or isfile(content) - except ValueError: - # On Windows, os.path.isfile throws this exception when - # passed a b64 encoded image. - is_uri_or_path = False - if is_uri_or_path: - if self.self_contained: - warnings.warn( - "Self-contained HTML report " - "includes link to external " - f"resource: {content}" - ) - - html_div = html.a( - raw(base_extra_string.format(extra.get("content"))), href=content - ) - elif self.self_contained: - src = f"data:{extra.get('mime_type')};base64,{content}" - html_div = raw(base_extra_string.format(src)) - else: - content = b64decode(content.encode("utf-8")) - href = src = self.create_asset( - content, extra_index, test_index, extra.get("extension"), "wb" - ) - html_div = html.a( - raw(base_extra_string.format(src)), - class_=base_extra_class, - target="_blank", - href=href, - ) - return html_div - - def _append_image(self, extra, extra_index, test_index): - image_base = '' - html_div = self._make_media_html_div( - extra, extra_index, test_index, image_base, "image" - ) - self.additional_html.append(html.div(html_div, class_="image")) - - def _append_video(self, extra, extra_index, test_index): - video_base = '' - html_div = self._make_media_html_div( - extra, extra_index, test_index, video_base, "video" - ) - self.additional_html.append(html.div(html_div, class_="video")) - - def _data_uri(self, content, mime_type="text/plain", charset="utf-8"): - data = b64encode(content.encode(charset)).decode("ascii") - return f"data:{mime_type};charset={charset};base64,{data}" diff --git a/src/pytest_html/scripts/datamanager.js b/src/pytest_html/scripts/datamanager.js index 20e3adfb..8a24e440 100644 --- a/src/pytest_html/scripts/datamanager.js +++ b/src/pytest_html/scripts/datamanager.js @@ -2,7 +2,7 @@ const { getCollapsedCategory } = require('./storage.js') class DataManager { setManager(data) { - const collapsedCategories = getCollapsedCategory() + const collapsedCategories = [...getCollapsedCategory(), 'passed'] const dataBlob = { ...data, tests: data.tests.map((test, index) => ({ ...test, id: `test_${index}`, diff --git a/src/pytest_html/scripts/dom.js b/src/pytest_html/scripts/dom.js index b8a4a9fa..529bc1b1 100644 --- a/src/pytest_html/scripts/dom.js +++ b/src/pytest_html/scripts/dom.js @@ -1,6 +1,6 @@ const storageModule = require('./storage.js') const { formatDuration } = require('./utils.js') -const mediaViewer = require('./mediaViewer.js') +const mediaViewer = require('./mediaviewer.js') const templateEnvRow = document.querySelector('#template_environment_row') const templateCollGroup = document.querySelector('#template_table-colgroup') const templateResult = document.querySelector('#template_results-table__tbody') @@ -69,19 +69,24 @@ const dom = { getColGroup: () => templateCollGroup.content.cloneNode(true), getResultTBody: ({ nodeid, id, longreprtext, duration, extras, resultsTableRow, tableHtml, outcome, collapsed }) => { const outcomeLower = outcome.toLowerCase() + let formattedDuration = formatDuration(duration) + formattedDuration = formatDuration < 1 ? formattedDuration.ms : formattedDuration.formatted const resultBody = templateResult.content.cloneNode(true) resultBody.querySelector('tbody').classList.add(outcomeLower) resultBody.querySelector('.col-result').innerText = outcome resultBody.querySelector('.col-result').classList.add(`${collapsed ? 'expander' : 'collapser'}`) resultBody.querySelector('.col-result').dataset.id = id resultBody.querySelector('.col-name').innerText = nodeid - resultBody.querySelector('.col-duration').innerText = formatDuration(duration) + + resultBody.querySelector('.col-duration').innerText = duration < 1 ? formatDuration(duration).ms : formatDuration(duration).formatted if (longreprtext) { - resultBody.querySelector('.log').innerText = longreprtext + // resultBody.querySelector('.log').innerText = longreprtext + resultBody.querySelector('.log').innerHTML = longreprtext } - if (collapsed || !longreprtext) { + // if (collapsed || !longreprtext) { + if (collapsed) { resultBody.querySelector('.extras-row').classList.add('hidden') } diff --git a/src/pytest_html/scripts/main.js b/src/pytest_html/scripts/main.js index c4d1787c..9ba64462 100644 --- a/src/pytest_html/scripts/main.js +++ b/src/pytest_html/scripts/main.js @@ -91,8 +91,9 @@ const renderDerived = (tests, collectedItems, isFinished) => { const accTime = tests.reduce((prev, { duration }) => prev + duration, 0) const formattedAccTime = formatDuration(accTime) const testWord = numberOfTests > 1 ? 'tests' : 'test' - const innerText = `${numberOfTests} ${testWord} ran in ${formattedAccTime} seconds.` - document.querySelector('.run-count').innerText = innerText + const durationText = formattedAccTime.hasOwnProperty('ms') ? formattedAccTime.ms : formattedAccTime.seconds + + document.querySelector('.run-count').innerText = `${numberOfTests} ${testWord} ran in ${durationText}.` document.querySelector('.summary__reload__button').classList.add('hidden') } else { document.querySelector('.run-count').innerText = `${numberOfTests} / ${collectedItems} tests done` diff --git a/src/pytest_html/scripts/utils.js b/src/pytest_html/scripts/utils.js index 3dd5e156..44549c1b 100644 --- a/src/pytest_html/scripts/utils.js +++ b/src/pytest_html/scripts/utils.js @@ -4,20 +4,21 @@ const formatedNumber = (number) => useGrouping: false, }) - -const formatDuration = ( ms ) => { - const totalSeconds = ms / 1000 - +const formatDuration = ( totalSeconds ) => { if (totalSeconds < 1) { - return `${ms}ms` + return {ms: `${Math.round(totalSeconds * 1000)} ms`} } + const hours = Math.floor(totalSeconds / 3600) let remainingSeconds = totalSeconds % 3600 const minutes = Math.floor(remainingSeconds / 60) remainingSeconds = remainingSeconds % 60 const seconds = Math.round(remainingSeconds) - return `${formatedNumber(hours)}:${formatedNumber(minutes)}:${formatedNumber(seconds)}` + return { + seconds: `${Math.round(totalSeconds)} seconds`, + formatted: `${formatedNumber(hours)}:${formatedNumber(minutes)}:${formatedNumber(seconds)}`, + } } module.exports = { formatDuration } diff --git a/start b/start new file mode 100755 index 00000000..3440913d --- /dev/null +++ b/start @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +if [[ $(uname) == "Darwin" ]]; then + volume="/private/var/folders:/reports/private/var/folders" +else + volume="${TMPDIR:-/tmp}:/reports${TMPDIR:-/tmp}" +fi + +if [[ "${1}" == "down" ]]; then + docker-compose -f <(sed -e "s;%%VOLUME%%;${volume};g" docker-compose.tmpl.yml) down +else + docker-compose -f <(sed -e "s;%%VOLUME%%;${volume};g" docker-compose.tmpl.yml) up -d +fi diff --git a/testing/js_test_report.html b/testing/js_test_report.html deleted file mode 100644 index 205fab29..00000000 --- a/testing/js_test_report.html +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - QUnit Pytest-HTML - - - -
-
- - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ResultTestDurationLinks
Rerunrerun.py::test_rexample_11.00
-
@pytest.mark.flaky(reruns=5)
def test_example():
import random
> assert random.choice([True, False])
E assert False
E + where False = <bound method Random.choice of <random.Random object at 0x7fe80b85f420>>([True, False])
E + where <bound method Random.choice of <random.Random object at 0x7fe80b85f420>> = <module 'random' from '/usr/local/Cellar/python/2.7.12/Frameworks/Python.framework/Versions/2.7/lib/python2.7/random.pyc'>.choice

rerun.py:6: AssertionError
Passedrerun.py::test_example_20.00
-
No log output captured.
-
Passedrerun.py::test_example_30.00
-
No log output captured.
-
-
- - diff --git a/testing/test_pytest_html.py b/testing/legacy_test_pytest_html.py similarity index 100% rename from testing/test_pytest_html.py rename to testing/legacy_test_pytest_html.py diff --git a/testing/test.js b/testing/test.js deleted file mode 100644 index 65dc2928..00000000 --- a/testing/test.js +++ /dev/null @@ -1,134 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - - if (!String.prototype.includes) { - String.prototype.includes = function() {'use strict'; - return String.prototype.indexOf.apply(this, arguments) !== -1; - }; - } - - QUnit.module( 'module', { - beforeEach: function( assert ) { - init(); - } - }); - - QUnit.test('sortColumn', function(assert){ - function sortColumnTest(col_re, first_element_then, first_element_now) { - assert.equal(findAll('.results-table-row')[0].className, first_element_then); - var row_sort = find(col_re); - sortColumn(row_sort); - assert.equal(findAll('.results-table-row')[0].className, first_element_now); - } - - //check col-name, tests should be in this order test-1 => (test-2 => test-3) on col-name - assert.equal(findAll('.col-name')[1].className, 'test-2 col-name'); - - //result - sortColumnTest('[col=result]', - 'rerun results-table-row', 'passed results-table-row'); - - //make sure sorting the result column does not change the tests order in the col-name - //tests should be in this order (test-2 => test-3) => test1 on col-name - assert.equal(findAll('.col-name')[0].className, 'test-2 col-name'); - - sortColumnTest('[col=result]', - 'passed results-table-row', 'rerun results-table-row'); - - - //name - sortColumnTest('[col=name]', - 'rerun results-table-row', 'passed results-table-row'); - sortColumnTest('[col=name]', - 'passed results-table-row', 'rerun results-table-row'); - - //duration - sortColumnTest('[col=duration]', - 'rerun results-table-row', 'passed results-table-row'); - sortColumnTest('[col=duration]', - 'passed results-table-row', 'rerun results-table-row'); - - //links - sortColumnTest('[col=links]', - 'rerun results-table-row', 'passed results-table-row'); - sortColumnTest('[col=links]', - 'passed results-table-row', 'rerun results-table-row'); - }); - -QUnit.test('filterTable', function(assert){ - function filterTableTest(outcome, checked) { - var filter_input = document.createElement('input'); - filter_input.setAttribute('data-test-result', outcome); - filter_input.checked = checked; - filterTable(filter_input); - - var outcomes = findAll('.' + outcome); - for(var i = 0; i < outcomes.length; i++) { - assert.equal(outcomes[i].hidden, !checked); - } - } - assert.equal(find('#not-found-message').hidden, true); - - filterTableTest('rerun', false); - filterTableTest('passed', false); - assert.equal(find('#not-found-message').hidden, false); - - filterTableTest('rerun', true); - assert.equal(find('#not-found-message').hidden, true); - - filterTableTest('passed', true); - -}); - -QUnit.test('showHideExtras', function(assert) { - function showExtrasTest(element){ - assert.equal(element.parentNode.nextElementSibling.className, 'collapsed'); - showExtras(element); - assert.notEqual(element.parentNode.nextElementSibling.className, 'collapsed'); - } - - function hideExtrasTest(element){ - assert.notEqual(element.parentNode.nextElementSibling.className, 'collapsed'); - hideExtras(element); - assert.equal(element.parentNode.nextElementSibling.className, 'collapsed'); - } - //Passed results have log collapsed by default - showExtrasTest(find('.passed').firstElementChild.firstElementChild); - hideExtrasTest(find('.passed').firstElementChild.firstElementChild); - - hideExtrasTest(find('.rerun').firstElementChild.firstElementChild); - showExtrasTest(find('.rerun').firstElementChild.firstElementChild); -}); - -QUnit.test('showHideAllExtras', function(assert) { - function showAllExtrasTest(){ - showAllExtras(); - var extras = findAll('.extra'); - for (var i = 0; i < extras.length; i++) { - assert.notEqual(extras[i].parentNode.className, 'collapsed'); - } - } - - function hideAllExtrasTest(){ - hideAllExtras(); - var extras = findAll('.extra'); - for (var i = 0; i < extras.length; i++) { - assert.equal(extras[i].parentNode.className, 'collapsed'); - } - } - - showAllExtrasTest(); - hideAllExtrasTest(); -}); - -QUnit.test('find', function (assert) { - assert.notEqual(find('#results-table-head'), null); - assert.notEqual(find('table#results-table'), null); - assert.equal(find('.not-in-table'), null); -}); - -QUnit.test('findAll', function(assert) { - assert.equal(findAll('.sortable').length, 4); - assert.equal(findAll('.not-in-table').length, 0); -}); diff --git a/testing/test_new.py b/testing/test_new.py new file mode 100644 index 00000000..064db922 --- /dev/null +++ b/testing/test_new.py @@ -0,0 +1,484 @@ +import base64 +import importlib.resources +import pkg_resources +import json +import os + +import pytest +import random +import re + +from pathlib import Path + +from assertpy import assert_that +from base64 import b64encode +from bs4 import BeautifulSoup +from selenium import webdriver + +pytest_plugins = ("pytester",) + +OUTCOMES = { + "passed": "Passed", + "skipped": "Skipped", + "failed": "Failed", + "error": "Errors", + "xfailed": "Unexpected failures", + "xpassed": "Unexpected passes", + "rerun": "Reruns", +} + + +def run(pytester, path="report.html", *args): + path = pytester.path.joinpath(path) + pytester.runpytest("-s", "--html", path, *args) + + chrome_options = webdriver.ChromeOptions() + # chrome_options.add_argument("--headless") + # chrome_options.add_argument("--window-size=1920x1080") + chrome_options.add_argument("--allow-file-access-from-files") + driver = webdriver.Remote( + command_executor='http://127.0.0.1:4444', + options=chrome_options + ) + try: + # Begin workaround + # See: https://github.com/pytest-dev/pytest/issues/10738 + path.chmod(0o755) + for parent in path.parents: + try: + os.chmod(parent, 0o755) + except PermissionError: + continue + # End workaround + # sleep(5) + driver.get(f"file:///reports{path}") + # sleep(5) + return BeautifulSoup(driver.page_source, "html.parser") + finally: + driver.quit() + + +def assert_results( + page, + passed=0, + skipped=0, + failed=0, + error=0, + xfailed=0, + xpassed=0, + rerun=0, + total_tests=None, +): + args = locals() + number_of_tests = 0 + for outcome, number in args.items(): + if outcome == "total_tests": + continue + if isinstance(number, int): + number_of_tests += number + result = get_text(page, f"span[class={outcome}]") + assert_that(result).is_equal_to(f"{number} {OUTCOMES[outcome]}") + + # if total_tests is not None: + # number_of_tests = total_tests + # total = get_text(page, "p[class='run-count']") + # expr = r"%d %s ran in \d+.\d+ seconds." % (number_of_tests, "tests" if number_of_tests > 1 else "test") + # assert_that(total).matches(expr) + + +def get_element(page, selector): + return page.select_one(selector) + + +def get_text(page, selector): + return get_element(page, selector).string + + +def get_log(page): + # TODO(jim) move to get_text (use .contents) + log = get_element(page, ".summary div[class='log']") + all_text = "" + for text in log.strings: + all_text += text + + return all_text + + +def file_content(): + try: + return importlib.resources.files("pytest_html") \ + .joinpath("resources", "style.css") \ + .read_bytes() \ + .decode("utf-8") \ + .strip() + except AttributeError: + # Needed for python < 3.9 + return pkg_resources.resource_string( + "pytest_html", os.path.join("resources", "style.css") + ).decode("utf-8") + + +class TestHTML: + @pytest.mark.parametrize("pause, expectation", + [ + (0.4, 400), + (1, r"^((?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d$)"), + ], + ) + def test_durations(self, pytester, pause, expectation): + pytester.makepyfile( + f""" + import time + def test_sleep(): + time.sleep({pause}) + """) + page = run(pytester) + duration = get_text(page, "#results-table td[class='col-duration']") + if pause < 1: + assert_that(int(duration.replace("ms", ""))).is_between(expectation, expectation * 2) + else: + assert_that(duration).matches(expectation) + + def test_pass(self, pytester): + pytester.makepyfile("def test_pass(): pass") + page = run(pytester) + assert_results(page, passed=1) + + def test_skip(self, pytester): + reason = str(random.random()) + pytester.makepyfile( + f""" + import pytest + def test_skip(): + pytest.skip('{reason}') + """ + ) + page = run(pytester) + assert_results(page, skipped=1, total_tests=0) + + log = get_text(page, ".summary div[class='log']") + assert_that(log).contains(reason) + + def test_fail(self, pytester): + pytester.makepyfile("def test_fail(): assert False") + page = run(pytester) + assert_results(page, failed=1) + assert_that(get_log(page)).contains("AssertionError") + + def test_xfail(self, pytester): + reason = str(random.random()) + pytester.makepyfile( + f""" + import pytest + def test_xfail(): + pytest.xfail('{reason}') + """ + ) + page = run(pytester) + assert_results(page, xfailed=1) + assert_that(get_log(page)).contains(reason) + + def test_xpass(self, pytester): + pytester.makepyfile( + """ + import pytest + @pytest.mark.xfail() + def test_xpass(): + pass + """ + ) + page = run(pytester) + assert_results(page, xpassed=1) + + def test_rerun(self, pytester): + pytester.makepyfile( + """ + import pytest + import time + + @pytest.mark.flaky(reruns=2) + def test_example(): + time.sleep(0.2) + assert False + """ + ) + + page = run(pytester) + assert_results(page, failed=1, rerun=2, total_tests=1) + + def test_conditional_xfails(self, pytester): + pytester.makepyfile( + """ + import pytest + @pytest.mark.xfail(False, reason='reason') + def test_fail(): assert False + @pytest.mark.xfail(False, reason='reason') + def test_pass(): pass + @pytest.mark.xfail(True, reason='reason') + def test_xfail(): assert False + @pytest.mark.xfail(True, reason='reason') + def test_xpass(): pass + """ + ) + page = run(pytester) + assert_results(page, passed=1, failed=1, xfailed=1, xpassed=1) + + def test_setup_error(self, pytester): + pytester.makepyfile( + """ + import pytest + @pytest.fixture + def arg(request): + raise ValueError() + def test_function(arg): + pass + """ + ) + page = run(pytester) + assert_results(page, error=1, total_tests=0) + + col_name = get_text(page, ".summary td[class='col-name']") + assert_that(col_name).contains("::setup") + assert_that(get_log(page)).contains("ValueError") + + @pytest.mark.parametrize( + "title", ["", "Special Report"] + ) + def test_report_title(self, pytester, title): + pytester.makepyfile("def test_pass(): pass") + + if title: + pytester.makeconftest( + f""" + import pytest + def pytest_html_report_title(report): + report.title = "{title}" + """ + ) + + expected_title = title if title else "report.html" + page = run(pytester) + assert_that(get_text(page, "#head-title")).is_equal_to(expected_title) + assert_that(get_text(page, "h1[id='title']")).is_equal_to(expected_title) + + def test_resources_inline_css(self, pytester): + pytester.makepyfile("def test_pass(): pass") + page = run(pytester, "report.html", "--self-contained-html") + + content = file_content() + + assert_that( + get_text(page, "head style").strip() + ).contains(content) + + def test_resources_css(self, pytester): + pytester.makepyfile("def test_pass(): pass") + page = run(pytester) + + assert_that( + page.select_one("head link")['href'] + ).is_equal_to( + str(Path("assets", "style.css")) + ) + + def test_custom_content_in_summary(self, pytester): + content = { + "prefix": str(random.random()), + "summary": str(random.random()), + "postfix": str(random.random()), + } + + pytester.makeconftest( + f""" + import pytest + + def pytest_html_results_summary(prefix, summary, postfix): + prefix.append(r"

prefix is {content['prefix']}

") + summary.extend([r"

summary is {content['summary']}

"]) + postfix.extend([r"

postfix is {content['postfix']}

"]) + """ + ) + + pytester.makepyfile("def test_pass(): pass") + page = run(pytester) + + elements = page.select(".summary__data p:not(.run-count):not(.filter)") + assert_that(elements).is_length(3) + for element in elements: + key = re.search(r"(\w+).*", element.string).group(1) + value = content.pop(key) + assert_that(element.string).contains(value) + + def test_extra_html(self, pytester): + content = str(random.random()) + pytester.makeconftest( + f""" + import pytest + + @pytest.hookimpl(hookwrapper=True) + def pytest_runtest_makereport(item, call): + outcome = yield + report = outcome.get_result() + if report.when == 'call': + from pytest_html import extras + report.extras = [extras.html('
{content}
')] + """ + ) + + pytester.makepyfile("def test_pass(): pass") + page = run(pytester) + + assert_that( + page.select_one(".summary .extraHTML").string + ).is_equal_to(content) + + @pytest.mark.parametrize( + "content, encoded", + [("u'\u0081'", "woE="), ("'foo'", "Zm9v"), ("b'\\xe2\\x80\\x93'", "4oCT")], + ) + def test_extra_text(self, pytester, content, encoded): + pytester.makeconftest( + f""" + import pytest + @pytest.hookimpl(hookwrapper=True) + def pytest_runtest_makereport(item, call): + outcome = yield + report = outcome.get_result() + if report.when == 'call': + from pytest_html import extras + report.extras = [extras.text({content})] + """ + ) + + pytester.makepyfile("def test_pass(): pass") + page = run(pytester, "report.html", "--self-contained-html") + + element = page.select_one(".summary a[class='col-links__extra text']") + assert_that(element.string).is_equal_to("Text") + assert_that( + element["href"] + ).is_equal_to( + f"data:text/plain;charset=utf-8;base64,{encoded}" + ) + + def test_extra_json(self, pytester): + content = {str(random.random()): str(random.random())} + pytester.makeconftest( + f""" + import pytest + + @pytest.hookimpl(hookwrapper=True) + def pytest_runtest_makereport(item, call): + outcome = yield + report = outcome.get_result() + if report.when == 'call': + from pytest_html import extras + report.extras = [extras.json({content})] + """ + ) + + pytester.makepyfile("def test_pass(): pass") + page = run(pytester, "report.html", "--self-contained-html") + + content_str = json.dumps(content) + data = b64encode(content_str.encode("utf-8")).decode("ascii") + + element = page.select_one(".summary a[class='col-links__extra json']") + assert_that(element.string).is_equal_to("JSON") + assert_that( + element["href"] + ).is_equal_to( + f"data:application/json;charset=utf-8;base64,{data}" + ) + + def test_extra_url(self, pytester): + content = str(random.random()) + pytester.makeconftest( + f""" + import pytest + + @pytest.hookimpl(hookwrapper=True) + def pytest_runtest_makereport(item, call): + outcome = yield + report = outcome.get_result() + if report.when == 'call': + from pytest_html import extras + report.extras = [extras.url('{content}')] + """ + ) + pytester.makepyfile("def test_pass(): pass") + page = run(pytester) + + element = page.select_one(".summary a[class='col-links__extra url']") + assert_that(element.string).is_equal_to("URL") + assert_that(element["href"]).is_equal_to(content) + + @pytest.mark.parametrize( + "mime_type, extension", + [ + ("image/png", "png"), + ("image/png", "image"), + ("image/jpeg", "jpg"), + ("image/svg+xml", "svg"), + ], + ) + def test_extra_image(self, pytester, mime_type, extension): + content = str(random.random()) + charset = "utf-8" + data = base64.b64encode(content.encode(charset)).decode(charset) + + pytester.makeconftest( + f""" + import pytest + + @pytest.hookimpl(hookwrapper=True) + def pytest_runtest_makereport(item, call): + outcome = yield + report = outcome.get_result() + if report.when == 'call': + from pytest_html import extras + report.extras = [extras.{extension}('{data}')] + """ + ) + pytester.makepyfile("def test_pass(): pass") + page = run(pytester, "report.html", "--self-contained-html") + + # element = page.select_one(".summary a[class='col-links__extra image']") + src = f"data:{mime_type};base64,{data}" + # assert_that(element.string).is_equal_to("Image") + # assert_that(element["href"]).is_equal_to(src) + + element = page.select_one(".summary .media img") + assert_that(str(element)).is_equal_to(f'') + + @pytest.mark.parametrize("mime_type, extension", [("video/mp4", "mp4")]) + def test_extra_video(self, pytester, mime_type, extension): + content = str(random.random()) + charset = "utf-8" + data = base64.b64encode(content.encode(charset)).decode(charset) + pytester.makeconftest( + f""" + import pytest + @pytest.hookimpl(hookwrapper=True) + def pytest_runtest_makereport(item, call): + outcome = yield + report = outcome.get_result() + if report.when == 'call': + from pytest_html import extras + report.extras = [extras.{extension}('{data}')] + """ + ) + pytester.makepyfile("def test_pass(): pass") + page = run(pytester, "report.html", "--self-contained-html") + + # element = page.select_one(".summary a[class='col-links__extra video']") + src = f"data:{mime_type};base64,{data}" + # assert_that(element.string).is_equal_to("Video") + # assert_that(element["href"]).is_equal_to(src) + + element = page.select_one(".summary .media video") + assert_that( + str(element) + ).is_equal_to( + f'' + ) diff --git a/testing/unittest.js b/testing/unittest.js index f3c46eee..d7ae69c0 100644 --- a/testing/unittest.js +++ b/testing/unittest.js @@ -146,13 +146,13 @@ describe('Sort tests', () => { describe('utils tests', () => { describe('formatDuration', () => { it('handles small durations', () => { - expect(formatDuration(123)).to.eql('123ms') - expect(formatDuration(0)).to.eql('0ms') - expect(formatDuration(999)).to.eql('999ms') + expect(formatDuration(0.123).ms).to.eql('123 ms') + expect(formatDuration(0).ms).to.eql('0 ms') + expect(formatDuration(0.999).ms).to.eql('999 ms') }) it('handles larger durations', () => { - expect(formatDuration(1234)).to.eql('00:00:01') - expect(formatDuration(12345678)).to.eql('03:25:46') + expect(formatDuration(1.234).formatted).to.eql('00:00:01') + expect(formatDuration(12345.678).formatted).to.eql('03:25:46') }) }) }) diff --git a/tox.ini b/tox.ini index 633d31bb..07325612 100644 --- a/tox.ini +++ b/tox.ini @@ -4,20 +4,24 @@ # and then run "tox" from this directory. [tox] -envlist = py{36,37,38,39,py3}, docs, linting +envlist = py{37,38,39,310,py3}, docs, linting isolated_build = True [testenv] -setenv = PYTHONDONTWRITEBYTECODE=1 +setenv = + PYTHONDONTWRITEBYTECODE=1 deps = + assertpy + beautifulsoup4 pytest-xdist pytest-rerunfailures pytest-mock + selenium ansi2html # soft-dependency cov: pytest-cov commands = - !cov: pytest -v -r a --color=yes --html={envlogdir}/report.html --self-contained-html {posargs} - cov: pytest -v -r a --color=yes --html={envlogdir}/report.html --self-contained-html --cov={envsitepackagesdir}/pytest_html --cov-report=term --cov-report=xml {posargs} + !cov: pytest -s -ra --color=yes --html={envlogdir}/report.html --self-contained-html {posargs} + cov: pytest -s -ra --color=yes --html={envlogdir}/report.html --self-contained-html --cov={envsitepackagesdir}/pytest_html --cov-report=term --cov-report=xml {posargs} [testenv:linting] skip_install = True @@ -35,12 +39,6 @@ deps = pytest-rerunfailures @ git+https://github.com/pytest-dev/pytest-rerunfailures.git pytest @ git+https://github.com/pytest-dev/pytest.git -[testenv:devel-cov] -description = Tests with unreleased deps and coverage -basepython = {[testenv:devel]basepython} -pip_pre = {[testenv:devel]pip_pre} -deps = {[testenv:devel]deps} - [testenv:docs] # NOTE: The command for doc building was taken from readthedocs documentation # See https://docs.readthedocs.io/en/stable/builds.html#understanding-what-s-going-on From de247c2f5052e9c6f2bf99b91f7565e2c0328957 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jim=20Br=C3=A4nnlund?= Date: Sun, 5 Mar 2023 15:38:05 +0100 Subject: [PATCH 60/60] Fix pre-commit issues --- .eslintrc.json | 6 +- .github/workflows/actions.yml | 16 +++-- .github/workflows/tests.yml | 4 +- .pre-commit-config.yaml | 10 ++- src/pytest_html/nextgen.py | 53 +++++++++------ src/pytest_html/plugin.py | 9 +-- src/pytest_html/resources/index.jinja2 | 4 +- testing/test_new.py | 89 +++++++++++--------------- tox.ini | 29 --------- 9 files changed, 103 insertions(+), 117 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 8e1f0724..02244542 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -47,9 +47,9 @@ "no-var": "error", "object-curly-spacing": [ "error", - "always", - { - "arraysInObjects": true + "always", + { + "arraysInObjects": true } ], "prefer-const": "error", diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index ff71f093..a57a1fcc 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -39,8 +39,6 @@ jobs: runs-on: ubuntu-latest env: PY_COLORS: 1 - TOXENV: packaging - steps: - name: Switch to using Python 3.10 by default uses: actions/setup-python@v4 @@ -76,8 +74,14 @@ jobs: | xargs git tag --delete - - name: Build dists - run: python -m tox + - name: Build and Check Package + uses: hynek/build-and-inspect-python-package@v1 + + - name: Download Package + uses: actions/download-artifact@v3 + with: + name: Packages + path: dist - name: Publish to test.pypi.org if: >- @@ -91,7 +95,7 @@ jobs: github.event_name == 'create' && github.event.ref_type == 'tag' ) - uses: pypa/gh-action-pypi-publish@v1 + uses: pypa/gh-action-pypi-publish@release/v1 with: password: ${{ secrets.testpypi_password }} repository_url: https://test.pypi.org/legacy/ @@ -100,6 +104,6 @@ jobs: if: >- # "create" workflows run separately from "push" & "pull_request" github.event_name == 'create' && github.event.ref_type == 'tag' - uses: pypa/gh-action-pypi-publish@v1 + uses: pypa/gh-action-pypi-publish@release/v1 with: password: ${{ secrets.pypi_password }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index aaf2e44a..21eea4a3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -109,8 +109,8 @@ jobs: # TODO: https://github.com/pytest-dev/pytest-html/issues/481 - name: Upload coverage to codecov if: >- - ${{ - ! github.event.schedule && + ${{ + ! github.event.schedule && ! matrix.skip-coverage && github.repository_owner == 'pytest-dev' }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index aa5c6711..2b24580c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,12 +3,14 @@ repos: rev: 22.3.0 hooks: - id: black - args: [--safe, --quiet] + args: [--safe, --quiet, --line-length=88] + - repo: https://github.com/asottile/blacken-docs rev: v1.12.1 hooks: - id: blacken-docs additional_dependencies: [black==22.3.0] + - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.1.0 hooks: @@ -19,6 +21,7 @@ repos: - id: check-yaml - id: debug-statements language_version: python3 + - repo: https://github.com/PyCQA/flake8 rev: 4.0.1 hooks: @@ -27,16 +30,19 @@ repos: additional_dependencies: - flake8-builtins==1.5.3 - flake8-typing-imports==1.12.0 + - repo: https://github.com/asottile/reorder_python_imports rev: v3.0.1 hooks: - id: reorder-python-imports args: ["--application-directories=.:src:testing", --py3-plus] + - repo: https://github.com/asottile/pyupgrade rev: v2.32.0 hooks: - id: pyupgrade args: [--py3-plus] + # - repo: https://github.com/pre-commit/mirrors-eslint # rev: v7.13.0 # hooks: @@ -44,6 +50,7 @@ repos: # additional_dependencies: # - eslint@7.13.0 # args: [src] + - repo: local hooks: - id: rst @@ -52,6 +59,7 @@ repos: files: ^(README.rst)$ language: python additional_dependencies: [pygments, restructuredtext_lint] + - repo: https://github.com/elidupuis/mirrors-sass-lint rev: "5cc45653263b423398e4af2561fae362903dd45d" hooks: diff --git a/src/pytest_html/nextgen.py b/src/pytest_html/nextgen.py index 031e1ee2..4e9d8c74 100644 --- a/src/pytest_html/nextgen.py +++ b/src/pytest_html/nextgen.py @@ -3,13 +3,13 @@ import datetime import json import os -import pytest import re import warnings - from collections import defaultdict from functools import partial from pathlib import Path + +import pytest from jinja2 import Environment from jinja2 import FileSystemLoader from jinja2 import select_autoescape @@ -22,9 +22,7 @@ try: from ansi2html import Ansi2HTMLConverter, style - converter = Ansi2HTMLConverter( - inline=False, escaped=False - ) + converter = Ansi2HTMLConverter(inline=False, escaped=False) _handle_ansi = partial(converter.convert, full=False) _ansi_styles = style.get_styles() except ImportError: @@ -34,8 +32,8 @@ _ansi_styles = [] -class BaseReport(object): - class Cells(object): +class BaseReport: + class Cells: def __init__(self): self._html = {} @@ -46,7 +44,7 @@ def html(self): def insert(self, index, html): self._html[index] = html - class Report(object): + class Report: def __init__(self, title, duration_format): self._data = { "title": title, @@ -80,9 +78,13 @@ def __init__(self, report_path, config, default_css="style.css"): self._resources_path = Path(__file__).parent.joinpath("resources") self._config = config self._template = _read_template([self._resources_path]) - self._css = _process_css(Path(self._resources_path, default_css), self._config.getoption("css")) + self._css = _process_css( + Path(self._resources_path, default_css), self._config.getoption("css") + ) self._duration_format = config.getini("duration_format") - self._max_asset_filename_length = int(config.getini("max_asset_filename_length")) + self._max_asset_filename_length = int( + config.getini("max_asset_filename_length") + ) self._report = self.Report(self._report_path.name, self._duration_format) @property @@ -96,7 +98,7 @@ def _asset_filename(self, test_id, extra_index, test_index, file_extension): str(extra_index), str(test_index), file_extension, - )[-self._max_asset_filename_length:] + )[-self._max_asset_filename_length :] def _generate_report(self, self_contained=False): generated = datetime.datetime.now() @@ -147,19 +149,28 @@ def _process_extras(self, report, test_id): test_id.encode("utf-8").decode("unicode_escape"), extra_index, test_index, - extra['extension'] + extra["extension"], ) if extra["format_type"] == extras.FORMAT_JSON: content = json.dumps(content) - extra["content"] = self._data_content(content, asset_name=asset_name, mime_type=extra["mime_type"]) + extra["content"] = self._data_content( + content, asset_name=asset_name, mime_type=extra["mime_type"] + ) if extra["format_type"] == extras.FORMAT_TEXT: if isinstance(content, bytes): content = content.decode("utf-8") - extra["content"] = self._data_content(content, asset_name=asset_name, mime_type=extra["mime_type"]) - - if extra["format_type"] == extras.FORMAT_IMAGE or extra["format_type"] == extras.FORMAT_VIDEO: - extra["content"] = self._media_content(content, asset_name=asset_name, mime_type=extra["mime_type"]) + extra["content"] = self._data_content( + content, asset_name=asset_name, mime_type=extra["mime_type"] + ) + + if ( + extra["format_type"] == extras.FORMAT_IMAGE + or extra["format_type"] == extras.FORMAT_VIDEO + ): + extra["content"] = self._media_content( + content, asset_name=asset_name, mime_type=extra["mime_type"] + ) return report_extras @@ -218,7 +229,9 @@ def pytest_sessionfinish(self, session): @pytest.hookimpl(trylast=True) def pytest_terminal_summary(self, terminalreporter): - terminalreporter.write_sep("-", f"Generated html report: file://{self._report_path.resolve()}") + terminalreporter.write_sep( + "-", f"Generated html report: file://{self._report_path.resolve()}" + ) @pytest.hookimpl(trylast=True) def pytest_collection_finish(self, session): @@ -307,7 +320,7 @@ def _media_content(self, content, mime_type, *args, **kwargs): base64.b64decode(content.encode("utf-8"), validate=True) return f"data:{mime_type};base64,{content}" except binascii.Error: - # if not base64 content, issue warning and just return as it's a file or link + # if not base64, issue warning and just return as it's a file or link warnings.warn( "Self-contained HTML report " "includes link to external " @@ -361,7 +374,7 @@ def _read_template(search_paths, template_name="index.jinja2"): env = Environment( loader=FileSystemLoader(search_paths), autoescape=select_autoescape( - enabled_extensions=('jinja2',), + enabled_extensions=("jinja2",), ), ) return env.get_template(template_name) diff --git a/src/pytest_html/plugin.py b/src/pytest_html/plugin.py index 9a8c8ac5..2f166c56 100644 --- a/src/pytest_html/plugin.py +++ b/src/pytest_html/plugin.py @@ -1,11 +1,11 @@ # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -import pytest import warnings from pathlib import Path -from . import extras # noqa: F401 +import pytest + from .nextgen import NextGenReport from .nextgen import NextGenSelfContainedReport @@ -121,8 +121,9 @@ def test_foo(extra): extra.append(pytest_html.extras.url("https://www.example.com/")) """ warnings.warn( - "The 'extra' fixture is deprecated and will be removed in a future release, use 'extras' instead.", - DeprecationWarning + "The 'extra' fixture is deprecated and will be removed in a future release" + ", use 'extras' instead.", + DeprecationWarning, ) pytestconfig.extras = [] yield pytestconfig.extras diff --git a/src/pytest_html/resources/index.jinja2 b/src/pytest_html/resources/index.jinja2 index fef03bdb..2d205d5a 100644 --- a/src/pytest_html/resources/index.jinja2 +++ b/src/pytest_html/resources/index.jinja2 @@ -49,8 +49,8 @@
>
-
-
+
+
diff --git a/testing/test_new.py b/testing/test_new.py index 064db922..481fe7e9 100644 --- a/testing/test_new.py +++ b/testing/test_new.py @@ -1,17 +1,15 @@ import base64 import importlib.resources -import pkg_resources import json import os - -import pytest import random import re - +from base64 import b64encode from pathlib import Path +import pkg_resources +import pytest from assertpy import assert_that -from base64 import b64encode from bs4 import BeautifulSoup from selenium import webdriver @@ -33,12 +31,11 @@ def run(pytester, path="report.html", *args): pytester.runpytest("-s", "--html", path, *args) chrome_options = webdriver.ChromeOptions() - # chrome_options.add_argument("--headless") - # chrome_options.add_argument("--window-size=1920x1080") - chrome_options.add_argument("--allow-file-access-from-files") + chrome_options.add_argument("--headless") + chrome_options.add_argument("--window-size=1920x1080") + # chrome_options.add_argument("--allow-file-access-from-files") driver = webdriver.Remote( - command_executor='http://127.0.0.1:4444', - options=chrome_options + command_executor="http://127.0.0.1:4444", options=chrome_options ) try: # Begin workaround @@ -50,24 +47,23 @@ def run(pytester, path="report.html", *args): except PermissionError: continue # End workaround - # sleep(5) + driver.get(f"file:///reports{path}") - # sleep(5) return BeautifulSoup(driver.page_source, "html.parser") finally: driver.quit() def assert_results( - page, - passed=0, - skipped=0, - failed=0, - error=0, - xfailed=0, - xpassed=0, - rerun=0, - total_tests=None, + page, + passed=0, + skipped=0, + failed=0, + error=0, + xfailed=0, + xpassed=0, + rerun=0, + total_tests=None, ): args = locals() number_of_tests = 0 @@ -82,7 +78,8 @@ def assert_results( # if total_tests is not None: # number_of_tests = total_tests # total = get_text(page, "p[class='run-count']") - # expr = r"%d %s ran in \d+.\d+ seconds." % (number_of_tests, "tests" if number_of_tests > 1 else "test") + # expr = r"%d %s ran in \d+.\d+ seconds." + # % (number_of_tests, "tests" if number_of_tests > 1 else "test") # assert_that(total).matches(expr) @@ -106,11 +103,13 @@ def get_log(page): def file_content(): try: - return importlib.resources.files("pytest_html") \ - .joinpath("resources", "style.css") \ - .read_bytes() \ - .decode("utf-8") \ + return ( + importlib.resources.files("pytest_html") + .joinpath("resources", "style.css") + .read_bytes() + .decode("utf-8") .strip() + ) except AttributeError: # Needed for python < 3.9 return pkg_resources.resource_string( @@ -119,7 +118,8 @@ def file_content(): class TestHTML: - @pytest.mark.parametrize("pause, expectation", + @pytest.mark.parametrize( + "pause, expectation", [ (0.4, 400), (1, r"^((?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d$)"), @@ -131,11 +131,14 @@ def test_durations(self, pytester, pause, expectation): import time def test_sleep(): time.sleep({pause}) - """) + """ + ) page = run(pytester) duration = get_text(page, "#results-table td[class='col-duration']") if pause < 1: - assert_that(int(duration.replace("ms", ""))).is_between(expectation, expectation * 2) + assert_that(int(duration.replace("ms", ""))).is_between( + expectation, expectation * 2 + ) else: assert_that(duration).matches(expectation) @@ -241,9 +244,7 @@ def test_function(arg): assert_that(col_name).contains("::setup") assert_that(get_log(page)).contains("ValueError") - @pytest.mark.parametrize( - "title", ["", "Special Report"] - ) + @pytest.mark.parametrize("title", ["", "Special Report"]) def test_report_title(self, pytester, title): pytester.makepyfile("def test_pass(): pass") @@ -267,17 +268,13 @@ def test_resources_inline_css(self, pytester): content = file_content() - assert_that( - get_text(page, "head style").strip() - ).contains(content) + assert_that(get_text(page, "head style").strip()).contains(content) def test_resources_css(self, pytester): pytester.makepyfile("def test_pass(): pass") page = run(pytester) - assert_that( - page.select_one("head link")['href'] - ).is_equal_to( + assert_that(page.select_one("head link")["href"]).is_equal_to( str(Path("assets", "style.css")) ) @@ -328,9 +325,7 @@ def pytest_runtest_makereport(item, call): pytester.makepyfile("def test_pass(): pass") page = run(pytester) - assert_that( - page.select_one(".summary .extraHTML").string - ).is_equal_to(content) + assert_that(page.select_one(".summary .extraHTML").string).is_equal_to(content) @pytest.mark.parametrize( "content, encoded", @@ -355,9 +350,7 @@ def pytest_runtest_makereport(item, call): element = page.select_one(".summary a[class='col-links__extra text']") assert_that(element.string).is_equal_to("Text") - assert_that( - element["href"] - ).is_equal_to( + assert_that(element["href"]).is_equal_to( f"data:text/plain;charset=utf-8;base64,{encoded}" ) @@ -385,9 +378,7 @@ def pytest_runtest_makereport(item, call): element = page.select_one(".summary a[class='col-links__extra json']") assert_that(element.string).is_equal_to("JSON") - assert_that( - element["href"] - ).is_equal_to( + assert_that(element["href"]).is_equal_to( f"data:application/json;charset=utf-8;base64,{data}" ) @@ -477,8 +468,6 @@ def pytest_runtest_makereport(item, call): # assert_that(element["href"]).is_equal_to(src) element = page.select_one(".summary .media video") - assert_that( - str(element) - ).is_equal_to( + assert_that(str(element)).is_equal_to( f'' ) diff --git a/tox.ini b/tox.ini index 31fdf82d..3016156a 100644 --- a/tox.ini +++ b/tox.ini @@ -47,35 +47,6 @@ changedir = docs deps = sphinx commands = sphinx-build -b html . _build/html -[testenv:packaging] -description = - Do packaging/distribution. If tag is not present or PEP440 compliant upload to - PYPI could fail -# `usedevelop = true` overrides `skip_install` instruction, it's unwanted -usedevelop = false -# don't install package in this env -skip_install = true -deps = - collective.checkdocs >= 0.2 - pep517 >= 0.8.2 - pip >= 20.2.2 - toml >= 0.10.1 - twine >= 3.2.0 -setenv = -commands = - rm -rfv {toxinidir}/dist/ - python -m pep517.build \ - --source \ - --binary \ - --out-dir {toxinidir}/dist/ \ - {toxinidir} - # metadata validation - python setup.py check - sh -c "python -m twine check {toxinidir}/dist/*" -whitelist_externals = - rm - sh - [flake8] max-line-length = 88 exclude = .eggs,.tox