diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 293d3e4de6e..b4804d01ad0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -23,6 +23,7 @@ env: FORCE_COLOR: "1" PYTHONDEVMODE: "1" # -X dev PYTHONWARNDEFAULTENCODING: "1" # -X warn_default_encoding + UV_SYSTEM_PYTHON: "1" # make uv do global installs jobs: ubuntu: @@ -124,6 +125,38 @@ jobs: DO_EPUBCHECK: "1" EPUBCHECK_PATH: "/tmp/epubcheck/epubcheck-5.1.0/epubcheck.jar" + oldest-supported: + runs-on: ubuntu-latest + name: Oldest supported + + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3" + - name: Check Python version + run: python --version --version + - name: Install graphviz + run: sudo apt-get install graphviz + - name: Install uv + run: > + curl + --location + --fail + --proto '=https' --tlsv1.2 + --silent --show-error + https://astral.sh/uv/install.sh + | sh + - name: Install dependencies + run: | + uv pip install .[test] --resolution lowest-direct + uv pip install alabaster==1.0.0 + - name: Test with pytest + run: python -m pytest -vv --durations 25 + env: + PYTHONWARNINGS: "error" # treat all warnings as errors + latex: runs-on: ubuntu-latest name: LaTeX diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index b7f6f21bd8d..fece8861873 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -6,7 +6,6 @@ on: - ".github/workflows/nodejs.yml" - "sphinx/themes/**.js" - "tests/js/**" - - "karma.conf.js" - "package.json" - "package-lock.json" pull_request: @@ -14,7 +13,6 @@ on: - ".github/workflows/nodejs.yml" - "sphinx/themes/**.js" - "tests/js/**" - - "karma.conf.js" - "package.json" - "package-lock.json" diff --git a/.ruff.toml b/.ruff.toml index 2b0e3434aa9..608c811da7c 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -1,5 +1,5 @@ target-version = "py310" # Pin Ruff to Python 3.10 -line-length = 95 +line-length = 88 output-format = "full" extend-exclude = [ @@ -427,6 +427,9 @@ select = [ "ANN", # utilities don't need annotations ] +[lint.pycodestyle] +max-line-length = 95 + [lint.flake8-quotes] inline-quotes = "single" @@ -482,18 +485,4 @@ exclude = [ "sphinx/transforms/*", "sphinx/util/*", "sphinx/writers/*", - "tests/*", - "tests/roots/*", - "tests/test_builders/*", - "tests/test_config/*", - "tests/test_directives/*", - "tests/test_domains/*", - "tests/test_environment/*", - "tests/test_extensions/*", - "tests/test_intl/*", - "tests/test_markup/*", - "tests/test_pycode/*", - "tests/test_transforms/*", - "tests/test_util/*", - "tests/test_writers/*", ] diff --git a/CHANGES.rst b/CHANGES.rst index e3c1e5d0e6c..b75cccaca27 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,15 +4,33 @@ Release 8.1.0 (in development) Dependencies ------------ +* #12756: Add lower-bounds to the ``sphinxcontrib-*`` dependencies. + Patch by Adam Turner. + Incompatible changes -------------------- +* #12763: Remove unused internal class ``sphinx.util.Tee``. + Patch by Adam Turner. + Deprecated ---------- +* #12762: Deprecate ``sphinx.util.import_object``. + Use :py:func:`importlib.import_module` instead. + Patch by Adam Turner. +* #12766: Deprecate ``sphinx.util.FilenameUniqDict`` + and ``sphinx.util.DownloadFiles``. + Patch by Adam Turner. + Features added -------------- +* #11328: Mention evaluation of templated content during production of static + output files. +* #12474: Support type-dependent search result highlighting via CSS. + Patch by Tim Hoffmann. + Bugs fixed ---------- @@ -23,12 +41,22 @@ Bugs fixed strings in a codeline when the problem has actually been solved thanks to :ref:`latexsphinxsetupforcewraps`. Patch by Jean-François B. - * #12730: The ``UnreferencedFootnotesDetector`` transform has been improved to more consistently detect unreferenced footnotes. Note, the priority of the transform has been changed from 200 to 622, so that it now runs after the docutils ``Footnotes`` resolution transform. Patch by Chris Sewell. +* #12587: Do not warn when potential ambiguity detected during Intersphinx + resolution occurs due to duplicate targets that differ case-insensitively. + Patch by James Addison. +* #12639: Fix singular and plural search results text. + Patch by Hugo van Kemenade. +* #12645: Correctly support custom gettext output templates. + Patch by Jeremy Bowman. Testing ------- + +* #12141: Migrate from the deprecated ``karma`` JavaScript test framework to + the actively-maintained ``jasmine`` framework. Test coverage is unaffected. + Patch by James Addison. diff --git a/doc/_themes/sphinx13/static/sphinx13.css b/doc/_themes/sphinx13/static/sphinx13.css index e008e2fa36b..b0501d34394 100644 --- a/doc/_themes/sphinx13/static/sphinx13.css +++ b/doc/_themes/sphinx13/static/sphinx13.css @@ -691,7 +691,28 @@ div.sphinx-feature > p.admonition-title::before { justify-content: center; gap: 10px; } - .sphinx-users-logos .headerlink { display: none; } + +/* -- search results -------------------------------------------------------- */ + +ul.search { + padding-left: 30px; +} +ul.search li { + padding: 5px 0 5px 10px; + list-style-type: "\25A1"; /* Unicode: White Square */ +} +ul.search li.context-index { + list-style-type: "\1F4D1"; /* Unicode: Bookmark Tabs */ +} +ul.search li.context-object { + list-style-type: "\1F4E6"; /* Unicode: Package */ +} +ul.search li.context-title { + list-style-type: "\1F4C4"; /* Unicode: Page Facing Up */ +} +ul.search li.context-text { + list-style-type: "\1F4C4"; /* Unicode: Page Facing Up */ +} diff --git a/doc/conf.py b/doc/conf.py index ffb9d19bd07..fc86fcd6d98 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -77,7 +77,14 @@ epub_description = 'Sphinx documentation generator system manual' latex_documents = [ - ('index', 'sphinx.tex', 'Sphinx Documentation', 'the Sphinx developers', 'manual', 1) + ( + 'index', + 'sphinx.tex', + 'Sphinx Documentation', + 'the Sphinx developers', + 'manual', + 1, + ) ] latex_logo = '_static/sphinx.png' latex_elements = { @@ -324,7 +331,9 @@ def setup(app: Sphinx) -> None: app.connect('autodoc-process-docstring', cut_lines(4, what=['module'])) app.connect('include-read', linkify_issues_in_changelog) app.connect('build-finished', build_redirects) - fdesc = GroupedField('parameter', label='Parameters', names=['param'], can_collapse=True) + fdesc = GroupedField( + 'parameter', label='Parameters', names=['param'], can_collapse=True + ) app.add_object_type( 'event', 'event', diff --git a/doc/development/html_themes/index.rst b/doc/development/html_themes/index.rst index 35a3b363a36..18a345d3f71 100644 --- a/doc/development/html_themes/index.rst +++ b/doc/development/html_themes/index.rst @@ -221,6 +221,64 @@ If your theme package contains two or more themes, please call ``sphinx.html_themes`` entry_points feature. +Styling with CSS +---------------- + +The :confval:`!stylesheets` setting can be used to add custom CSS files to a theme. + +.. caution:: + + The structure of the HTML elements and their classes are currently not a + well-defined public API. Please infer them from inspecting the built HTML + pages. While we cannot guarantee full stability, they tend to be fairly + stable. + +Styling search result entries by category +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 8.0 + +The search result items have classes indicating the context in which the +search term was found. You can use the CSS selectors: + +- ``ul.search li.context-index``: + For results in an index, such as the glossary +- ``ul.search li.context-object``: + For results in source code, like Python function definitions +- ``ul.search li.context-title``: + For results found in section headings +- ``ul.search li.context-text``: + For results found anywhere else in the documentation text + +As a base for inheritance by other themes, the ``basic`` theme is +intentionally minimal and does not define CSS rules using these. +Derived themes are encouraged to use these selectors as they see fit. +For example, the following stylesheet adds contextual icons to the +search result list: + +.. code-block:: css + + ul.search { + padding-left: 30px; + } + ul.search li { + padding: 5px 0 5px 10px; + list-style-type: "\25A1"; /* Unicode: White Square */ + } + ul.search li.context-index { + list-style-type: "\1F4D1"; /* Unicode: Bookmark Tabs */ + } + ul.search li.context-object { + list-style-type: "\1F4E6"; /* Unicode: Package */ + } + ul.search li.context-title { + list-style-type: "\1F4C4"; /* Unicode: Page Facing Up */ + } + ul.search li.context-text { + list-style-type: "\1F4C4"; /* Unicode: Page Facing Up */ + } + + Templating ---------- diff --git a/doc/development/tutorials/examples/recipe.py b/doc/development/tutorials/examples/recipe.py index baf85fe3e2c..9848629216a 100644 --- a/doc/development/tutorials/examples/recipe.py +++ b/doc/development/tutorials/examples/recipe.py @@ -62,7 +62,15 @@ def generate(self, docnames=None): for ingredient, recipe_names in ingredient_recipes.items(): for recipe_name in recipe_names: dispname, typ, docname, anchor = recipes[recipe_name] - content[ingredient].append((dispname, 0, docname, anchor, docname, '', typ)) + content[ingredient].append(( + dispname, + 0, + docname, + anchor, + docname, + '', + typ, + )) # convert the dict to the sorted list of tuples expected content = sorted(content.items()) @@ -153,7 +161,14 @@ def add_recipe(self, signature, ingredients): self.data['recipe_ingredients'][name] = ingredients # name, dispname, type, docname, anchor, priority - self.data['recipes'].append((name, signature, 'Recipe', self.env.docname, anchor, 0)) + self.data['recipes'].append(( + name, + signature, + 'Recipe', + self.env.docname, + anchor, + 0, + )) def setup(app: Sphinx) -> ExtensionMetadata: diff --git a/doc/development/tutorials/examples/todo.py b/doc/development/tutorials/examples/todo.py index 4e9dc66855e..25aedfb0543 100644 --- a/doc/development/tutorials/examples/todo.py +++ b/doc/development/tutorials/examples/todo.py @@ -57,7 +57,9 @@ def purge_todos(app, env, docname): if not hasattr(env, 'todo_all_todos'): return - env.todo_all_todos = [todo for todo in env.todo_all_todos if todo['docname'] != docname] + env.todo_all_todos = [ + todo for todo in env.todo_all_todos if todo['docname'] != docname + ] def merge_todos(app, env, docnames, other): @@ -98,7 +100,9 @@ def process_todo_nodes(app, doctree, fromdocname): newnode = nodes.reference('', '') innernode = nodes.emphasis(_('here'), _('here')) newnode['refdocname'] = todo_info['docname'] - newnode['refuri'] = app.builder.get_relative_uri(fromdocname, todo_info['docname']) + newnode['refuri'] = app.builder.get_relative_uri( + fromdocname, todo_info['docname'] + ) newnode['refuri'] += '#' + todo_info['target']['refid'] newnode.append(innernode) para += newnode diff --git a/doc/extdev/deprecated.rst b/doc/extdev/deprecated.rst index aa61768fb63..2a9cfb70601 100644 --- a/doc/extdev/deprecated.rst +++ b/doc/extdev/deprecated.rst @@ -22,6 +22,21 @@ The following is a list of deprecated interfaces. - Removed - Alternatives + * - ``sphinx.util.FilenameUniqDict`` + - 8.1 + - 10.0 + - N/A + + * - ``sphinx.util.DownloadFiles`` + - 8.1 + - 10.0 + - N/A + + * - ``sphinx.util.import_object`` + - 8.1 + - 10.0 + - ``importlib.import_module`` + * - ``sphinx.ext.intersphinx.normalize_intersphinx_mapping`` - 8.0 - 10.0 diff --git a/doc/extdev/testing.rst b/doc/extdev/testing.rst index 4153d445840..db03855bc43 100644 --- a/doc/extdev/testing.rst +++ b/doc/extdev/testing.rst @@ -28,5 +28,5 @@ Usage ----- If you want to know more detailed usage, -please refer to :file:`tests/conftest.py`` and other :file:`test_*.py` files +please refer to :file:`tests/conftest.py` and other :file:`test_*.py` files under the :file:`tests/` directory. diff --git a/doc/internals/contributing.rst b/doc/internals/contributing.rst index e2981c0a1e6..0f387341658 100644 --- a/doc/internals/contributing.rst +++ b/doc/internals/contributing.rst @@ -174,10 +174,10 @@ Style and type checks can be run as follows: Unit tests ~~~~~~~~~~ -Sphinx is tested using pytest_ for Python code and Karma_ for JavaScript. +Sphinx is tested using pytest_ for Python code and Jasmine_ for JavaScript. .. _pytest: https://docs.pytest.org/en/latest/ -.. _Karma: https://karma-runner.github.io +.. _Jasmine: https://jasmine.github.io/ To run Python unit tests, we recommend using :program:`tox`, which provides a number of targets and allows testing against multiple different Python environments: @@ -216,13 +216,10 @@ To run JavaScript tests, use :program:`npm`: .. tip:: - :program:`karma` requires a Firefox binary to use as a test browser. + :program:`jasmine` requires a Firefox binary to use as a test browser. - For Unix-based systems, you can specify the path to the Firefox binary using: - - .. code-block:: shell - - FIREFOX_BIN="/Applications/Firefox.app/Contents/MacOS/firefox" npm test + On Unix systems, you can check the presence and location of the ``firefox`` + binary at the command-line by running ``command -v firefox``. New unit tests should be included in the :file:`tests/` directory where necessary: diff --git a/doc/usage/advanced/intl.rst b/doc/usage/advanced/intl.rst index 590ef0356e0..4d52c34debe 100644 --- a/doc/usage/advanced/intl.rst +++ b/doc/usage/advanced/intl.rst @@ -119,6 +119,11 @@ section describe an easy way to translate with *sphinx-intl*. $ make gettext The generated pot files will be placed in the ``_build/gettext`` directory. + If you want to customize the output beyond what can be done via the + :ref:`intl-options`, the + :download:`default pot file template <../../../sphinx/templates/gettext/message.pot.jinja>` + can be replaced by a custom :file:`message.pot.jinja` file placed in any + directory listed in :confval:`templates_path`. #. Generate po files. diff --git a/doc/usage/restructuredtext/directives.rst b/doc/usage/restructuredtext/directives.rst index 19079d2cfed..c32d7a1c4e4 100644 --- a/doc/usage/restructuredtext/directives.rst +++ b/doc/usage/restructuredtext/directives.rst @@ -96,138 +96,156 @@ The ``toctree`` directive is the central element. You can also add external links, by giving an HTTP URL instead of a document name. - **Section numbering** + The special entry name ``self`` stands for the document containing the + toctree directive. This is useful if you want to generate a "sitemap" from + the toctree. - If you want to have section numbers even in HTML output, give the - **toplevel** toctree a ``numbered`` option. For example:: + In the end, all documents in the :term:`source directory` (or subdirectories) + must occur in some ``toctree`` directive; Sphinx will emit a warning if it + finds a file that is not included, because that means that this file will not + be reachable through standard navigation. - .. toctree:: - :numbered: + Use :confval:`exclude_patterns` to explicitly exclude documents or + directories from building completely. Use :ref:`the "orphan" metadata + ` to let a document be built, but notify Sphinx that it is not + reachable via a toctree. - foo - bar + The "root document" (selected by :confval:`root_doc`) is the "root" of the TOC + tree hierarchy. It can be used as the documentation's main page, or as a + "full table of contents" if you don't give a ``:maxdepth:`` option. - Numbering then starts at the heading of ``foo``. Sub-toctrees are - automatically numbered (don't give the ``numbered`` flag to those). + .. versionchanged:: 0.6 + Added support for external links and "self" references. - Numbering up to a specific depth is also possible, by giving the depth as a - numeric argument to ``numbered``. + .. rubric:: options - **Additional options** + .. rst:directive:option:: class: class names + :type: a list of class names, separated by spaces - You can use the ``caption`` option to provide a toctree caption and you can - use the ``name`` option to provide an implicit target name that can be - referenced by using :rst:role:`ref`:: + Assign `class attributes`_. + This is a :dudir:`common option `. + For example:: - .. toctree:: - :caption: Table of Contents - :name: mastertoc + .. toctree:: + :class: custom-toc - foo + .. _class attributes: https://docutils.sourceforge.io/docs/ref/doctree.html#classes - As with :dudir:`most directives `, - you can use the ``class`` option to assign `class attributes`_:: + .. versionadded:: 7.4 - .. toctree:: - :class: custom-toc + .. rst:directive:option:: name: label + :type: text - .. _class attributes: https://docutils.sourceforge.io/docs/ref/doctree.html#classes + An implicit target name that can be referenced using :rst:role:`ref`. + This is a :dudir:`common option `. + For example:: - If you want only the titles of documents in the tree to show up, not other - headings of the same level, you can use the ``titlesonly`` option:: + .. toctree:: + :name: mastertoc - .. toctree:: - :titlesonly: + foo - foo - bar + .. versionadded:: 1.3 - You can use "globbing" in toctree directives, by giving the ``glob`` flag - option. All entries are then matched against the list of available - documents, and matches are inserted into the list alphabetically. Example:: + .. rst:directive:option:: caption + :type: text - .. toctree:: - :glob: + Add a caption to the toctree. + For example:: - intro* - recipe/* - * + .. toctree:: + :caption: Table of Contents - This includes first all documents whose names start with ``intro``, then all - documents in the ``recipe`` folder, then all remaining documents (except the - one containing the directive, of course.) [#]_ + foo - The special entry name ``self`` stands for the document containing the - toctree directive. This is useful if you want to generate a "sitemap" from - the toctree. + .. versionadded:: 1.3 - You can use the ``reversed`` flag option to reverse the order of the entries - in the list. This can be useful when using the ``glob`` flag option to - reverse the ordering of the files. Example:: + .. rst:directive:option:: numbered + numbered: depth - .. toctree:: - :glob: - :reversed: + If you want to have section numbers even in HTML output, + add the ``:numbered:`` option to the *top-level* toctree. + For example:: - recipe/* + .. toctree:: + :numbered: - You can also give a "hidden" option to the directive, like this:: + foo + bar - .. toctree:: - :hidden: + Section numbering then starts at the heading of ``foo``. + Sub-toctrees are automatically numbered + (don't give the ``numbered`` flag to those). - doc_1 - doc_2 + Numbering up to a specific depth is also possible, + by giving the depth as a numeric argument to ``numbered``. - This will still notify Sphinx of the document hierarchy, but not insert links - into the document at the location of the directive -- this makes sense if you - intend to insert these links yourself, in a different style, or in the HTML - sidebar. + .. versionadded:: 0.6 - In cases where you want to have only one top-level toctree and hide all other - lower level toctrees you can add the "includehidden" option to the top-level - toctree entry:: + .. versionchanged:: 1.1 + Added the numeric *depth* argument. - .. toctree:: - :includehidden: + .. rst:directive:option:: titlesonly - doc_1 - doc_2 + Only list document titles, not other headings of the same level. + For example:: - All other toctree entries can then be eliminated by the "hidden" option. + .. toctree:: + :titlesonly: - In the end, all documents in the :term:`source directory` (or subdirectories) - must occur in some ``toctree`` directive; Sphinx will emit a warning if it - finds a file that is not included, because that means that this file will not - be reachable through standard navigation. + foo + bar - Use :confval:`exclude_patterns` to explicitly exclude documents or - directories from building completely. Use :ref:`the "orphan" metadata - ` to let a document be built, but notify Sphinx that it is not - reachable via a toctree. + .. versionadded:: 1.0 - The "root document" (selected by :confval:`root_doc`) is the "root" of the TOC - tree hierarchy. It can be used as the documentation's main page, or as a - "full table of contents" if you don't give a ``maxdepth`` option. + .. rst:directive:option:: glob - .. versionchanged:: 0.3 - Added "globbing" option. + Parse glob wildcards in toctree entries. + All entries are matched against the list of available documents, + and matches are inserted into the list alphabetically. + For example:: - .. versionchanged:: 0.6 - Added "numbered" and "hidden" options as well as external links and - support for "self" references. + .. toctree:: + :glob: - .. versionchanged:: 1.0 - Added "titlesonly" option. + intro* + recipe/* + * - .. versionchanged:: 1.1 - Added numeric argument to "numbered". + This includes first all documents whose names start with ``intro``, + then all documents in the ``recipe`` folder, then all remaining documents + (except the one containing the directive, of course.) [#]_ - .. versionchanged:: 1.2 - Added "includehidden" option. + .. versionadded:: 0.3 + + .. rst:directive:option:: reversed + + Reverse the order of the entries in the list. + This is particularly useful when using the ``:glob:`` option. + + .. versionadded:: 1.5 + + .. rst:directive:option:: hidden + + A hidden toctree only defines the document hierarchy. + It will not insert links into the document at the location of the directive. + + This makes sense if you have other means of navigation, + e.g. through manual links, HTML sidebar navigation, + or if you use the ``:includehidden:`` option on the top-level toctree. + + .. versionadded:: 0.6 + + .. rst:directive:option:: includehidden + + If you want one global table of contents showing the complete document structure, + you can add the ``:includehidden:`` option to the *top-level* toctree directive. + All other toctrees on child pages can then be made invisible + with the ``:hidden:`` option. + The top-level toctree with ``:includehidden:`` will then include their entries. + + .. versionadded:: 1.2 - .. versionchanged:: 1.3 - Added "caption" and "name" option. Special names ^^^^^^^^^^^^^ diff --git a/karma.conf.js b/karma.conf.js deleted file mode 100644 index 4f1b9c616e4..00000000000 --- a/karma.conf.js +++ /dev/null @@ -1,75 +0,0 @@ -// Karma configuration -// Generated on Sat Jul 21 2018 22:01:48 GMT+0200 (CEST) - -module.exports = function(config) { - config.set({ - - // base path that will be used to resolve all patterns (eg. files, exclude) - basePath: '', - - - // frameworks to use - // available frameworks: https://npmjs.org/browse/keyword/karma-adapter - frameworks: ['jasmine'], - - - // list of files / patterns to load in the browser - files: [ - { pattern: 'tests/js/fixtures/**/*.js', included: false, served: true }, - 'tests/js/documentation_options.js', - 'tests/js/language_data.js', - 'sphinx/themes/basic/static/doctools.js', - 'sphinx/themes/basic/static/searchtools.js', - 'sphinx/themes/basic/static/sphinx_highlight.js', - 'tests/js/*.js' - ], - - - // list of files / patterns to exclude - exclude: [ - ], - - - // preprocess matching files before serving them to the browser - // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor - preprocessors: { - }, - - - // test results reporter to use - // possible values: 'dots', 'progress' - // available reporters: https://npmjs.org/browse/keyword/karma-reporter - reporters: ['progress'], - - - // web server port - port: 9876, - - - // enable / disable colors in the output (reporters and logs) - colors: true, - - - // level of logging - // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG - logLevel: config.LOG_INFO, - - - // enable / disable watching file and executing tests whenever any file changes - autoWatch: true, - - - // start these browsers - // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher - browsers: ["Firefox"], - - - // Continuous Integration mode - // if true, Karma captures browsers, runs the tests and exits - singleRun: false, - - // Concurrency level - // how many browser should be started simultaneous - concurrency: Infinity - }) -} diff --git a/package-lock.json b/package-lock.json index 12c33c40a2f..3340f176ee8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,49 +6,120 @@ "": { "name": "sphinx", "devDependencies": { - "jasmine-core": "^3.4.0", - "karma": "^6.3.16", - "karma-firefox-launcher": "^2.0.0", - "karma-jasmine": "^4.0.0" + "jasmine-browser-runner": "^2.5.0", + "jasmine-core": "^5.2.0" } }, - "node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "node_modules/@bazel/runfiles": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@bazel/runfiles/-/runfiles-5.8.1.tgz", + "integrity": "sha512-NDdfpdQ6rZlylgv++iMn5FkObC/QlBQvipinGLSOguTYpRywmieOyJ29XHvUilspwTFSILWpoE9CqMGkHXug1g==", + "dev": true + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, "engines": { - "node": ">=0.1.90" + "node": ">=12" } }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", - "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", - "dev": true + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } }, - "node_modules/@types/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, - "node_modules/@types/cors": { - "version": "2.8.17", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", - "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "dependencies": { - "@types/node": "*" + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/@types/node": { - "version": "20.11.28", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.28.tgz", - "integrity": "sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==", + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, "dependencies": { - "undici-types": "~5.26.4" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" } }, "node_modules/accepts": { @@ -88,18 +159,17 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true + }, + "node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "dev": true }, "node_modules/balanced-match": { "version": "1.0.2", @@ -107,24 +177,6 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "node_modules/base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", - "dev": true, - "engines": { - "node": "^4.5.0 || >= 5.9" - } - }, - "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/body-parser": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", @@ -171,18 +223,6 @@ "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/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -211,36 +251,20 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "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": { - "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" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">= 8.10.0" + "node": ">=10" }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "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" + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/color-convert": { @@ -267,19 +291,16 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "node_modules/connect": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", - "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dev": true, "dependencies": { - "debug": "2.6.9", - "finalhandler": "1.1.2", - "parseurl": "~1.3.3", - "utils-merge": "1.0.1" + "safe-buffer": "5.2.1" }, "engines": { - "node": ">= 0.10.0" + "node": ">= 0.6" } }, "node_modules/content-type": { @@ -291,41 +312,30 @@ "node": ">= 0.6" } }, - "node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "dev": true, - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true }, - "node_modules/custom-event": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", - "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", + "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/date-format": { - "version": "4.0.14", - "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", - "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", + "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": ">=4.0" + "node": ">= 8" } }, "node_modules/debug": { @@ -373,30 +383,33 @@ "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/di": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", - "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, - "node_modules/dom-serialize": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", - "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", - "dev": true, - "dependencies": { - "custom-event": "~1.0.0", - "ent": "~2.2.0", - "extend": "^3.0.0", - "void-elements": "^2.0.0" - } - }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", "dev": true }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -412,65 +425,6 @@ "node": ">= 0.8" } }, - "node_modules/engine.io": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.4.tgz", - "integrity": "sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==", - "dev": true, - "dependencies": { - "@types/cookie": "^0.4.1", - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.4.1", - "cors": "~2.8.5", - "debug": "~4.3.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.11.0" - }, - "engines": { - "node": ">=10.2.0" - } - }, - "node_modules/engine.io-parser": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz", - "integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==", - "dev": true, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/engine.io/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/engine.io/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/ent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", - "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", - "dev": true - }, "node_modules/es-define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", @@ -492,121 +446,173 @@ "node": ">= 0.4" } }, - "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-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", "dev": true }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } }, - "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==", + "node_modules/express": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", "dev": true, "dependencies": { - "to-regex-range": "^5.0.1" + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.2", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.6.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" }, "engines": { - "node": ">=8" + "node": ">= 0.10.0" } }, - "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "node_modules/express/node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "dev": true, "dependencies": { "debug": "2.6.9", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", - "statuses": "~1.5.0", + "statuses": "2.0.1", "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8" } }, - "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", - "dev": true + "node_modules/express/node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } }, - "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "node_modules/express/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], "engines": { - "node": ">=4.0" + "node": ">= 0.8" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/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/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } + "engines": { + "node": ">=10" } }, - "node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", "dev": true, "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" }, "engines": { - "node": ">=6 <7 || >=8" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "engines": { + "node": ">= 0.6" + } }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">= 0.6" } }, "node_modules/function-bind": { @@ -618,15 +624,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "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-intrinsic": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", @@ -646,38 +643,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "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/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/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -690,11 +655,14 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true + "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-property-descriptors": { "version": "1.0.2", @@ -769,41 +737,23 @@ "node": ">= 0.8" } }, - "node_modules/http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": ">= 2.1.2 < 3" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true }, "node_modules/inherits": { "version": "2.0.4", @@ -811,40 +761,13 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "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==", + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, "engines": { - "node": ">=8" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true, - "engines": { - "node": ">=0.10.0" + "node": ">= 0.10" } }, "node_modules/is-fullwidth-code-point": { @@ -856,178 +779,144 @@ "node": ">=8" } }, - "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==", + "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": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, "dependencies": { - "is-extglob": "^2.1.1" + "@isaacs/cliui": "^8.0.2" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.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==", + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", "dev": true, + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, "engines": { - "node": ">=0.12.0" + "node": ">=10" } }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "node_modules/jasmine-browser-runner": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/jasmine-browser-runner/-/jasmine-browser-runner-2.5.0.tgz", + "integrity": "sha512-CzdvpeZunUu6x1u8G6/vPnfcKVpDaBFfk3tIvm1hoA+EfceQ8FRvsy4o8hEcKYyMt556XFRnP5PjYsxFU8z7Xw==", "dev": true, "dependencies": { - "is-docker": "^2.0.0" + "ejs": "^3.1.6", + "express": "^4.19.2", + "glob": "^10.0.0", + "selenium-webdriver": "^4.12.0" }, - "engines": { - "node": ">=8" + "bin": { + "jasmine-browser-runner": "bin/jasmine-browser-runner" + }, + "peerDependencies": { + "jasmine-core": "^5.0.0" } }, - "node_modules/isbinaryfile": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.8.tgz", - "integrity": "sha512-53h6XFniq77YdW+spoRrebh0mnmTxRPTlcuIArO57lmMdq4uBKFKaeTjnb92oYWrSn/LVL+LT+Hap2tFQj8V+w==", + "node_modules/jasmine-browser-runner/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, - "engines": { - "node": ">= 8.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/gjtorikian/" + "dependencies": { + "balanced-match": "^1.0.0" } }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "node_modules/jasmine-core": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.10.1.tgz", - "integrity": "sha512-ooZWSDVAdh79Rrj4/nnfklL3NQVra0BcuhcuWoAwwi+znLDoUeH87AFfeX8s+YeYi6xlv5nveRyaA1v7CintfA==", - "dev": true - }, - "node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "node_modules/jasmine-browser-runner/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/karma": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.3.tgz", - "integrity": "sha512-LuucC/RE92tJ8mlCwqEoRWXP38UMAqpnq98vktmS9SznSoUPPUJQbc91dHcxcunROvfQjdORVA/YFviH+Xci9Q==", - "dev": true, - "dependencies": { - "@colors/colors": "1.5.0", - "body-parser": "^1.19.0", - "braces": "^3.0.2", - "chokidar": "^3.5.1", - "connect": "^3.7.0", - "di": "^0.0.1", - "dom-serialize": "^2.2.1", - "glob": "^7.1.7", - "graceful-fs": "^4.2.6", - "http-proxy": "^1.18.1", - "isbinaryfile": "^4.0.8", - "lodash": "^4.17.21", - "log4js": "^6.4.1", - "mime": "^2.5.2", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.5", - "qjobs": "^1.2.0", - "range-parser": "^1.2.1", - "rimraf": "^3.0.2", - "socket.io": "^4.7.2", - "source-map": "^0.6.1", - "tmp": "^0.2.1", - "ua-parser-js": "^0.7.30", - "yargs": "^16.1.1" + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, "bin": { - "karma": "bin/karma" + "glob": "dist/esm/bin.mjs" }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/karma-firefox-launcher": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/karma-firefox-launcher/-/karma-firefox-launcher-2.1.2.tgz", - "integrity": "sha512-VV9xDQU1QIboTrjtGVD4NCfzIH7n01ZXqy/qpBhnOeGVOkG5JYPEm8kuSd7psHE6WouZaQ9Ool92g8LFweSNMA==", - "dev": true, - "dependencies": { - "is-wsl": "^2.2.0", - "which": "^2.0.1" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/karma-jasmine": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-4.0.1.tgz", - "integrity": "sha512-h8XDAhTiZjJKzfkoO1laMH+zfNlra+dEQHUAjpn5JV1zCPtOIVWGQjLBrqhnzQa/hrU2XrZwSyBa6XjEBzfXzw==", + "node_modules/jasmine-browser-runner/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "dependencies": { - "jasmine-core": "^3.6.0" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">= 10" + "node": ">=16 || 14 >=14.17" }, - "peerDependencies": { - "karma": "*" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "node_modules/jasmine-core": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.2.0.tgz", + "integrity": "sha512-tSAtdrvWybZkQmmaIoDgnvHG8ORUNw5kEVlO5CvrXj02Jjr9TZrmjFq7FUiOUzJiOP2wLGYT6PgrQgQF4R1xiw==", "dev": true }, - "node_modules/log4js": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz", - "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==", + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", "dev": true, "dependencies": { - "date-format": "^4.0.14", - "debug": "^4.3.4", - "flatted": "^3.2.7", - "rfdc": "^1.3.0", - "streamroller": "^3.1.5" - }, - "engines": { - "node": ">=8.0" + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" } }, - "node_modules/log4js/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", "dev": true, "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "immediate": "~3.0.5" } }, - "node_modules/log4js/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==", + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true }, "node_modules/media-typer": { @@ -1039,16 +928,19 @@ "node": ">= 0.6" } }, - "node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "dev": true + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "dev": true, - "bin": { - "mime": "cli.js" - }, "engines": { - "node": ">=4.0.0" + "node": ">= 0.6" } }, "node_modules/mime-db": { @@ -1084,25 +976,13 @@ "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": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" + "engines": { + "node": ">=16 || 14 >=14.17" } }, "node_modules/ms": { @@ -1120,24 +1000,6 @@ "node": ">= 0.6" } }, - "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/object-inspect": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", @@ -1147,26 +1009,17 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dev": true, - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } + "node_modules/package-json-from-dist": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", + "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", + "dev": true }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "dependencies": { - "wrappy": "1" - } + "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/parseurl": { "version": "1.3.3", @@ -1177,34 +1030,54 @@ "node": ">= 0.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": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "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": ">=0.10.0" + "node": ">=8" } }, - "node_modules/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, "engines": { - "node": ">=8.6" + "node": ">=16 || 14 >=14.18" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/qjobs": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", - "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "dev": true + }, + "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/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "dev": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, "engines": { - "node": ">=0.9" + "node": ">= 0.10" } }, "node_modules/qs": { @@ -1246,53 +1119,46 @@ "node": ">= 0.8" } }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" + "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/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", - "dev": true - }, - "node_modules/rfdc": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz", - "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==", + "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/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "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, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } + "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", @@ -1300,213 +1166,231 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "node_modules/selenium-webdriver": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.23.0.tgz", + "integrity": "sha512-DdvtInpnMt95Td8VApvmAw7oSydBD9twIRXqoMyRoGMvL1dAnMFxdrwnW6L0d/pF/uoNTjbVUarwGZ9wIGNStA==", "dev": true, "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" + "@bazel/runfiles": "^5.8.1", + "jszip": "^3.10.1", + "tmp": "^0.2.3", + "ws": "^8.17.1" }, "engines": { - "node": ">= 0.4" + "node": ">= 14.21.0" } }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true - }, - "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "node_modules/selenium-webdriver/node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" - }, "engines": { - "node": ">= 0.4" + "node": ">=10.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, - "node_modules/socket.io": { - "version": "4.7.5", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz", - "integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==", + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "dev": true, "dependencies": { - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "cors": "~2.8.5", - "debug": "~4.3.2", - "engine.io": "~6.5.2", - "socket.io-adapter": "~2.5.2", - "socket.io-parser": "~4.2.4" + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" }, "engines": { - "node": ">=10.2.0" + "node": ">= 0.8.0" } }, - "node_modules/socket.io-adapter": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.4.tgz", - "integrity": "sha512-wDNHGXGewWAjQPt3pyeYBtpWSq9cLE5UW1ZUPL/2eGK9jtse/FpXib7epSTsz0Q0m+6sg6Y4KtcFTlah1bdOVg==", + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "dev": true, - "dependencies": { - "debug": "~4.3.4", - "ws": "~8.11.0" + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" } }, - "node_modules/socket.io-adapter/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/send/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/send/node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dev": true, "dependencies": { - "ms": "2.1.2" + "ee-first": "1.1.1" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">= 0.8" } }, - "node_modules/socket.io-adapter/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/send/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } }, - "node_modules/socket.io-parser": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", - "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "dev": true, "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1" + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" }, "engines": { - "node": ">=10.0.0" + "node": ">= 0.8.0" } }, - "node_modules/socket.io-parser/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dev": true, "dependencies": { - "ms": "2.1.2" + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">= 0.4" } }, - "node_modules/socket.io-parser/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==", + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "dev": true }, - "node_modules/socket.io/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "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": { - "ms": "2.1.2" + "shebang-regex": "^3.0.0" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=8" } }, - "node_modules/socket.io/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/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "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": ">=0.10.0" + "node": ">=8" } }, - "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, "engines": { - "node": ">= 0.6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/streamroller": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", - "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==", + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, - "dependencies": { - "date-format": "^4.0.14", - "debug": "^4.3.4", - "fs-extra": "^8.1.0" - }, "engines": { - "node": ">=8.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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/streamroller/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/string_decoder/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/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": { - "ms": "2.1.2" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=8" } }, - "node_modules/streamroller/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/string-width": { + "node_modules/string-width-cjs": { + "name": "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==", @@ -1532,28 +1416,38 @@ "node": ">=8" } }, - "node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "node_modules/strip-ansi-cjs": { + "name": "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": { - "rimraf": "^3.0.0" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=8.17.0" + "node": ">=8" } }, - "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==", + "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": { - "is-number": "^7.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=8.0" + "node": ">=8" + } + }, + "node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "dev": true, + "engines": { + "node": ">=14.14" } }, "node_modules/toidentifier": { @@ -1578,44 +1472,6 @@ "node": ">= 0.6" } }, - "node_modules/ua-parser-js": { - "version": "0.7.37", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.37.tgz", - "integrity": "sha512-xV8kqRKM+jhMvcHWUKthV9fNebIzrNy//2O9ZwWcfiBFR5f25XVZPLlEajk/sf3Ra15V92isyQqnIEXRDaZWEA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/ua-parser-js" - }, - { - "type": "paypal", - "url": "https://paypal.me/faisalman" - }, - { - "type": "github", - "url": "https://github.com/sponsors/faisalman" - } - ], - "engines": { - "node": "*" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true - }, - "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -1625,6 +1481,12 @@ "node": ">= 0.8" } }, + "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/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -1643,15 +1505,6 @@ "node": ">= 0.8" } }, - "node_modules/void-elements": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", - "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -1667,7 +1520,8 @@ "node": ">= 8" } }, - "node_modules/wrap-ansi": { + "node_modules/wrap-ansi-cjs": { + "name": "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==", @@ -1683,107 +1537,86 @@ "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": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "node_modules/ws": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "dev": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "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.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "engines": { - "node": ">=10" - } } }, "dependencies": { - "@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "dev": true - }, - "@socket.io/component-emitter": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", - "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", + "@bazel/runfiles": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@bazel/runfiles/-/runfiles-5.8.1.tgz", + "integrity": "sha512-NDdfpdQ6rZlylgv++iMn5FkObC/QlBQvipinGLSOguTYpRywmieOyJ29XHvUilspwTFSILWpoE9CqMGkHXug1g==", "dev": true }, - "@types/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", - "dev": true - }, - "@types/cors": { - "version": "2.8.17", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", - "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, "requires": { - "@types/node": "*" + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + } + } } }, - "@types/node": { - "version": "20.11.28", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.28.tgz", - "integrity": "sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==", + "@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, - "requires": { - "undici-types": "~5.26.4" - } + "optional": true }, "accepts": { "version": "1.3.8", @@ -1810,15 +1643,17 @@ "color-convert": "^2.0.1" } }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true + }, + "async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "dev": true }, "balanced-match": { "version": "1.0.2", @@ -1826,18 +1661,6 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", - "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 - }, "body-parser": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", @@ -1879,15 +1702,6 @@ "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" - } - }, "bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -1907,31 +1721,14 @@ "set-function-length": "^1.2.1" } }, - "chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", - "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" - } - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "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": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, "color-convert": { @@ -1955,16 +1752,13 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "connect": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", - "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dev": true, "requires": { - "debug": "2.6.9", - "finalhandler": "1.1.2", - "parseurl": "~1.3.3", - "utils-merge": "1.0.1" + "safe-buffer": "5.2.1" } }, "content-type": { @@ -1973,34 +1767,29 @@ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "dev": true }, - "cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "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 }, - "cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "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": { - "object-assign": "^4", - "vary": "^1" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" } }, - "custom-event": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", - "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", - "dev": true - }, - "date-format": { - "version": "4.0.14", - "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", - "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", - "dev": true - }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -2033,30 +1822,27 @@ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", "dev": true }, - "di": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", - "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, - "dom-serialize": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", - "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", - "dev": true, - "requires": { - "custom-event": "~1.0.0", - "ent": "~2.2.0", - "extend": "^3.0.0", - "void-elements": "^2.0.0" - } - }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", "dev": true }, + "ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "requires": { + "jake": "^10.8.5" + } + }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -2069,53 +1855,6 @@ "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", "dev": true }, - "engine.io": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.4.tgz", - "integrity": "sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==", - "dev": true, - "requires": { - "@types/cookie": "^0.4.1", - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.4.1", - "cors": "~2.8.5", - "debug": "~4.3.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.11.0" - }, - "dependencies": { - "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" - } - }, - "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 - } - } - }, - "engine.io-parser": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz", - "integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==", - "dev": true - }, - "ent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", - "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", - "dev": true - }, "es-define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", @@ -2131,89 +1870,145 @@ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "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-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", "dev": true }, - "eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "dev": true }, - "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" - } - }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "express": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", "dev": true, "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.2", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.6.0", + "cookie-signature": "1.0.6", "debug": "2.6.9", + "depd": "2.0.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", - "on-finished": "~2.3.0", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "dev": true + }, + "finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + } + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true + } } }, - "flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", - "dev": true - }, - "follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", - "dev": true + "filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "requires": { + "minimatch": "^5.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" + } + }, + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } }, - "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", "dev": true, "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" } }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "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 + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true }, "function-bind": { "version": "1.1.2", @@ -2221,12 +2016,6 @@ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "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-intrinsic": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", @@ -2237,30 +2026,7 @@ "function-bind": "^1.1.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - } - }, - "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" - } - }, - "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" + "hasown": "^2.0.0" } }, "gopd": { @@ -2272,10 +2038,10 @@ "get-intrinsic": "^1.1.3" } }, - "graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "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-property-descriptors": { @@ -2329,17 +2095,6 @@ } } }, - "http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "dev": true, - "requires": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - } - }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -2349,15 +2104,11 @@ "safer-buffer": ">= 2.1.2 < 3" } }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true }, "inherits": { "version": "2.0.4", @@ -2365,25 +2116,10 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "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-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "dev": true }, "is-fullwidth-code-point": { @@ -2392,34 +2128,10 @@ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, - "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-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "requires": { - "is-docker": "^2.0.0" - } - }, - "isbinaryfile": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.8.tgz", - "integrity": "sha512-53h6XFniq77YdW+spoRrebh0mnmTxRPTlcuIArO57lmMdq4uBKFKaeTjnb92oYWrSn/LVL+LT+Hap2tFQj8V+w==", + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "dev": true }, "isexe": { @@ -2428,118 +2140,123 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, - "jasmine-core": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.10.1.tgz", - "integrity": "sha512-ooZWSDVAdh79Rrj4/nnfklL3NQVra0BcuhcuWoAwwi+znLDoUeH87AFfeX8s+YeYi6xlv5nveRyaA1v7CintfA==", - "dev": true - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "karma": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.3.tgz", - "integrity": "sha512-LuucC/RE92tJ8mlCwqEoRWXP38UMAqpnq98vktmS9SznSoUPPUJQbc91dHcxcunROvfQjdORVA/YFviH+Xci9Q==", - "dev": true, - "requires": { - "@colors/colors": "1.5.0", - "body-parser": "^1.19.0", - "braces": "^3.0.2", - "chokidar": "^3.5.1", - "connect": "^3.7.0", - "di": "^0.0.1", - "dom-serialize": "^2.2.1", - "glob": "^7.1.7", - "graceful-fs": "^4.2.6", - "http-proxy": "^1.18.1", - "isbinaryfile": "^4.0.8", - "lodash": "^4.17.21", - "log4js": "^6.4.1", - "mime": "^2.5.2", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.5", - "qjobs": "^1.2.0", - "range-parser": "^1.2.1", - "rimraf": "^3.0.2", - "socket.io": "^4.7.2", - "source-map": "^0.6.1", - "tmp": "^0.2.1", - "ua-parser-js": "^0.7.30", - "yargs": "^16.1.1" - } - }, - "karma-firefox-launcher": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/karma-firefox-launcher/-/karma-firefox-launcher-2.1.2.tgz", - "integrity": "sha512-VV9xDQU1QIboTrjtGVD4NCfzIH7n01ZXqy/qpBhnOeGVOkG5JYPEm8kuSd7psHE6WouZaQ9Ool92g8LFweSNMA==", + "jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, "requires": { - "is-wsl": "^2.2.0", - "which": "^2.0.1" + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" } }, - "karma-jasmine": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-4.0.1.tgz", - "integrity": "sha512-h8XDAhTiZjJKzfkoO1laMH+zfNlra+dEQHUAjpn5JV1zCPtOIVWGQjLBrqhnzQa/hrU2XrZwSyBa6XjEBzfXzw==", + "jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", "dev": true, "requires": { - "jasmine-core": "^3.6.0" + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" } }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "log4js": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz", - "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==", + "jasmine-browser-runner": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/jasmine-browser-runner/-/jasmine-browser-runner-2.5.0.tgz", + "integrity": "sha512-CzdvpeZunUu6x1u8G6/vPnfcKVpDaBFfk3tIvm1hoA+EfceQ8FRvsy4o8hEcKYyMt556XFRnP5PjYsxFU8z7Xw==", "dev": true, "requires": { - "date-format": "^4.0.14", - "debug": "^4.3.4", - "flatted": "^3.2.7", - "rfdc": "^1.3.0", - "streamroller": "^3.1.5" + "ejs": "^3.1.6", + "express": "^4.19.2", + "glob": "^10.0.0", + "selenium-webdriver": "^4.12.0" }, "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "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": { - "ms": "2.1.2" + "balanced-match": "^1.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 + "glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + } + }, + "minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } } } }, + "jasmine-core": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.2.0.tgz", + "integrity": "sha512-tSAtdrvWybZkQmmaIoDgnvHG8ORUNw5kEVlO5CvrXj02Jjr9TZrmjFq7FUiOUzJiOP2wLGYT6PgrQgQF4R1xiw==", + "dev": true + }, + "jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, + "requires": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "requires": { + "immediate": "~3.0.5" + } + }, + "lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", "dev": true }, - "mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "dev": true }, "mime-db": { @@ -2566,21 +2283,12 @@ "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==", + "minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true }, - "mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "requires": { - "minimist": "^1.2.6" - } - }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -2593,41 +2301,23 @@ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "dev": true }, - "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 - }, "object-inspect": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "dev": true }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dev": true, - "requires": { - "ee-first": "1.1.1" - } + "package-json-from-dist": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", + "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", + "dev": true }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true }, "parseurl": { "version": "1.3.3", @@ -2635,24 +2325,44 @@ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "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": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "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 }, - "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "requires": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + } + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", "dev": true }, - "qjobs": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", - "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "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 }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, "qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", @@ -2680,47 +2390,123 @@ "unpipe": "1.0.0" } }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "requires": { - "picomatch": "^2.2.1" + "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 + } } }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "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 }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, - "rfdc": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz", - "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==", - "dev": true + "selenium-webdriver": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.23.0.tgz", + "integrity": "sha512-DdvtInpnMt95Td8VApvmAw7oSydBD9twIRXqoMyRoGMvL1dAnMFxdrwnW6L0d/pF/uoNTjbVUarwGZ9wIGNStA==", + "dev": true, + "requires": { + "@bazel/runfiles": "^5.8.1", + "jszip": "^3.10.1", + "tmp": "^0.2.3", + "ws": "^8.17.1" + }, + "dependencies": { + "ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "dev": true, + "requires": {} + } + } }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "dev": true, "requires": { - "glob": "^7.1.3" + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "dependencies": { + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "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 + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "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 + "serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + } }, "set-function-length": { "version": "1.2.2", @@ -2736,12 +2522,33 @@ "has-property-descriptors": "^1.0.2" } }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, "setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "dev": true }, + "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 + }, "side-channel": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", @@ -2754,134 +2561,42 @@ "object-inspect": "^1.13.1" } }, - "socket.io": { - "version": "4.7.5", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz", - "integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==", - "dev": true, - "requires": { - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "cors": "~2.8.5", - "debug": "~4.3.2", - "engine.io": "~6.5.2", - "socket.io-adapter": "~2.5.2", - "socket.io-parser": "~4.2.4" - }, - "dependencies": { - "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" - } - }, - "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 - } - } - }, - "socket.io-adapter": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.4.tgz", - "integrity": "sha512-wDNHGXGewWAjQPt3pyeYBtpWSq9cLE5UW1ZUPL/2eGK9jtse/FpXib7epSTsz0Q0m+6sg6Y4KtcFTlah1bdOVg==", - "dev": true, - "requires": { - "debug": "~4.3.4", - "ws": "~8.11.0" - }, - "dependencies": { - "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" - } - }, - "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 - } - } + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true }, - "socket.io-parser": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", - "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "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": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1" + "safe-buffer": "~5.1.0" }, "dependencies": { - "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" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "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 } } }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "dev": true - }, - "streamroller": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", - "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==", + "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": { - "date-format": "^4.0.14", - "debug": "^4.3.4", - "fs-extra": "^8.1.0" - }, - "dependencies": { - "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" - } - }, - "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 - } + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" } }, - "string-width": { - "version": "4.2.3", + "string-width-cjs": { + "version": "npm:string-width@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, @@ -2900,24 +2615,30 @@ "ansi-regex": "^5.0.1" } }, - "tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { - "rimraf": "^3.0.0" + "ansi-regex": "^5.0.1" } }, - "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==", + "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": { - "is-number": "^7.0.0" + "has-flag": "^4.0.0" } }, + "tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "dev": true + }, "toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -2934,30 +2655,18 @@ "mime-types": "~2.1.24" } }, - "ua-parser-js": { - "version": "0.7.37", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.37.tgz", - "integrity": "sha512-xV8kqRKM+jhMvcHWUKthV9fNebIzrNy//2O9ZwWcfiBFR5f25XVZPLlEajk/sf3Ra15V92isyQqnIEXRDaZWEA==", - "dev": true - }, - "undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true - }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", "dev": true }, + "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 + }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -2970,12 +2679,6 @@ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "dev": true }, - "void-elements": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", - "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", - "dev": true - }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -2985,8 +2688,8 @@ "isexe": "^2.0.0" } }, - "wrap-ansi": { - "version": "7.0.0", + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, @@ -2995,46 +2698,6 @@ "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": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "ws": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "dev": true, - "requires": {} - }, - "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.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true } } } diff --git a/package.json b/package.json index 451f5e20a11..3a25f9bb3fd 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "sphinx", "scripts": { - "test": "./node_modules/.bin/karma start --browsers Firefox --single-run" + "test": "npx jasmine-browser-runner runSpecs --config=tests/js/jasmine-browser.mjs" }, "repository": { "type": "git", @@ -11,9 +11,7 @@ "url": "https://github.com/sphinx-doc/sphinx/issues" }, "devDependencies": { - "jasmine-core": "^3.4.0", - "karma": "^6.3.16", - "karma-firefox-launcher": "^2.0.0", - "karma-jasmine": "^4.0.0" + "jasmine-browser-runner": "^2.5.0", + "jasmine-core": "^5.2.0" } } diff --git a/pyproject.toml b/pyproject.toml index 1f45185dc24..ff26246c2a9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,12 +55,12 @@ classifiers = [ "Topic :: Utilities", ] dependencies = [ - "sphinxcontrib-applehelp", - "sphinxcontrib-devhelp", - "sphinxcontrib-jsmath", - "sphinxcontrib-htmlhelp>=2.0.0", + "sphinxcontrib-applehelp>=1.0.7", + "sphinxcontrib-devhelp>=1.0.6", + "sphinxcontrib-htmlhelp>=2.0.6", + "sphinxcontrib-jsmath>=1.0.1", + "sphinxcontrib-qthelp>=1.0.6", "sphinxcontrib-serializinghtml>=1.1.9", - "sphinxcontrib-qthelp", "Jinja2>=3.1", "Pygments>=2.17", "docutils>=0.20,<0.22", @@ -81,7 +81,7 @@ docs = [ ] lint = [ "flake8>=6.0", - "ruff==0.5.5", + "ruff==0.5.7", "mypy==1.11.1", "sphinx-lint>=0.9", "types-colorama==0.4.15.20240311", diff --git a/sphinx/__init__.py b/sphinx/__init__.py index 85397059f59..bf62f813e07 100644 --- a/sphinx/__init__.py +++ b/sphinx/__init__.py @@ -16,7 +16,10 @@ warnings.filterwarnings('default', category=RemovedInNextVersionWarning) warnings.filterwarnings( - 'ignore', 'The frontend.Option class .*', DeprecationWarning, module='docutils.frontend' + 'ignore', + 'The frontend.Option class .*', + DeprecationWarning, + module='docutils.frontend', ) #: Version info for better programmatic use. diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py index 151df6aeb18..3c5313afe41 100644 --- a/sphinx/builders/__init__.py +++ b/sphinx/builders/__init__.py @@ -16,14 +16,25 @@ from sphinx.environment.adapters.asset import ImageAdapter from sphinx.errors import SphinxError from sphinx.locale import __ -from sphinx.util import UnicodeDecodeErrorHandler, get_filetype, import_object, logging, rst +from sphinx.util import ( + UnicodeDecodeErrorHandler, + get_filetype, + logging, + rst, +) +from sphinx.util._importer import import_object from sphinx.util.build_phase import BuildPhase from sphinx.util.console import bold from sphinx.util.display import progress_message, status_iterator from sphinx.util.docutils import sphinx_domains from sphinx.util.i18n import CatalogInfo, CatalogRepository, docname_to_domain from sphinx.util.osutil import SEP, canon_path, ensuredir, relative_uri, relpath -from sphinx.util.parallel import ParallelTasks, SerialTasks, make_chunks, parallel_available +from sphinx.util.parallel import ( + ParallelTasks, + SerialTasks, + make_chunks, + parallel_available, +) # side effect: registers roles and directives from sphinx import directives # NoQA: F401 isort:skip @@ -128,8 +139,11 @@ def init(self) -> None: def create_template_bridge(self) -> None: """Return the template bridge configured.""" if self.config.template_bridge: - self.templates = import_object(self.config.template_bridge, - 'template_bridge setting')() + template_bridge_cls = import_object( + self.config.template_bridge, + source='template_bridge setting' + ) + self.templates = template_bridge_cls() else: from sphinx.jinja2glue import BuiltinTemplateLoader self.templates = BuiltinTemplateLoader() diff --git a/sphinx/builders/gettext.py b/sphinx/builders/gettext.py index f1f7d7fa920..8427fcbb49d 100644 --- a/sphinx/builders/gettext.py +++ b/sphinx/builders/gettext.py @@ -7,6 +7,7 @@ from codecs import open from collections import defaultdict from os import getenv, path, walk +from pathlib import Path from typing import TYPE_CHECKING, Any, Literal from uuid import uuid4 @@ -28,7 +29,7 @@ if TYPE_CHECKING: import os - from collections.abc import Iterable, Iterator + from collections.abc import Iterable, Iterator, Sequence from docutils.nodes import Element @@ -36,6 +37,8 @@ from sphinx.config import Config from sphinx.util.typing import ExtensionMetadata +DEFAULT_TEMPLATE_PATH = Path(package_dir, 'templates', 'gettext') + logger = logging.getLogger(__name__) @@ -91,13 +94,14 @@ def __init__(self, source: str, line: int) -> None: class GettextRenderer(SphinxRenderer): def __init__( - self, template_path: list[str | os.PathLike[str]] | None = None, + self, template_path: Sequence[str | os.PathLike[str]] | None = None, outdir: str | os.PathLike[str] | None = None, ) -> None: self.outdir = outdir if template_path is None: - template_path = [path.join(package_dir, 'templates', 'gettext')] - super().__init__(template_path) + super().__init__([DEFAULT_TEMPLATE_PATH]) + else: + super().__init__([*template_path, DEFAULT_TEMPLATE_PATH]) def escape(s: str) -> str: s = s.replace('\\', r'\\') @@ -287,7 +291,12 @@ def finish(self) -> None: ensuredir(path.join(self.outdir, path.dirname(textdomain))) context['messages'] = list(catalog) - content = GettextRenderer(outdir=self.outdir).render('message.pot.jinja', context) + template_path = [ + self.app.srcdir / rel_path + for rel_path in self.config.templates_path + ] + renderer = GettextRenderer(template_path, outdir=self.outdir) + content = renderer.render('message.pot.jinja', context) pofn = path.join(self.outdir, textdomain + '.pot') if should_write(pofn, content): diff --git a/sphinx/builders/html/__init__.py b/sphinx/builders/html/__init__.py index 06917232f6b..2fa5f360a95 100644 --- a/sphinx/builders/html/__init__.py +++ b/sphinx/builders/html/__init__.py @@ -26,7 +26,11 @@ from sphinx import __display_version__, package_dir from sphinx import version_info as sphinx_version from sphinx.builders import Builder -from sphinx.builders.html._assets import _CascadingStyleSheet, _file_checksum, _JavaScript +from sphinx.builders.html._assets import ( + _CascadingStyleSheet, + _file_checksum, + _JavaScript, +) from sphinx.config import ENUM, Config from sphinx.deprecation import _deprecation_warning from sphinx.domains import Domain, Index, IndexEntry @@ -40,6 +44,7 @@ from sphinx.theming import HTMLThemeFactory from sphinx.util import isurl, logging from sphinx.util._timestamps import _format_rfc3339_microseconds +from sphinx.util.console import bold from sphinx.util.display import progress_message, status_iterator from sphinx.util.docutils import new_document from sphinx.util.fileutil import copy_asset @@ -389,8 +394,9 @@ def math_renderer_name(self) -> str | None: return None def get_outdated_docs(self) -> Iterator[str]: + build_info_fname = self.outdir / '.buildinfo' try: - with open(path.join(self.outdir, '.buildinfo'), encoding="utf-8") as fp: + with open(build_info_fname, encoding="utf-8") as fp: buildinfo = BuildInfo.load(fp) if self.build_info != buildinfo: @@ -405,6 +411,21 @@ def get_outdated_docs(self) -> Iterator[str]: if self.templates: template_mtime = int(self.templates.newest_template_mtime() * 10**6) + try: + old_mtime = _last_modified_time(build_info_fname) + except Exception: + pass + else: + # Let users know they have a newer template + if template_mtime > old_mtime: + logger.info( + bold("building [html]: ") + + __( + "template %s has been changed since the previous build, " + "all docs will be rebuilt" + ), + self.templates.newest_template_name(), + ) else: template_mtime = 0 for docname in self.env.found_docs: @@ -887,7 +908,7 @@ def copy_html_favicon(self) -> None: def copy_static_files(self) -> None: try: - with progress_message(__('copying static files')): + with progress_message(__('copying static files'), nonl=False): ensuredir(self.outdir / '_static') # prepare context for templates @@ -908,7 +929,7 @@ def copy_static_files(self) -> None: def copy_extra_files(self) -> None: """Copy html_extra_path files.""" try: - with progress_message(__('copying extra files')): + with progress_message(__('copying extra files'), nonl=False): excluded = Matcher(self.config.exclude_patterns) for extra_path in self.config.html_extra_path: copy_asset( diff --git a/sphinx/builders/latex/__init__.py b/sphinx/builders/latex/__init__.py index 508c676312f..f19d66edbc9 100644 --- a/sphinx/builders/latex/__init__.py +++ b/sphinx/builders/latex/__init__.py @@ -12,7 +12,11 @@ import sphinx.builders.latex.nodes # NoQA: F401,E501 # Workaround: import this before writer to avoid ImportError from sphinx import addnodes, highlighting, package_dir from sphinx.builders import Builder -from sphinx.builders.latex.constants import ADDITIONAL_SETTINGS, DEFAULT_SETTINGS, SHORTHANDOFF +from sphinx.builders.latex.constants import ( + ADDITIONAL_SETTINGS, + DEFAULT_SETTINGS, + SHORTHANDOFF, +) from sphinx.builders.latex.theming import Theme, ThemeFactory from sphinx.builders.latex.util import ExtBabel from sphinx.config import ENUM, Config diff --git a/sphinx/deprecation.py b/sphinx/deprecation.py index 8605e418826..1baec85bf7a 100644 --- a/sphinx/deprecation.py +++ b/sphinx/deprecation.py @@ -40,7 +40,11 @@ def _deprecation_warning( # deprecated name -> (object to return, canonical path or empty string, removal version) _DEPRECATED_OBJECTS = { - 'deprecated_name': (object_to_return, 'fully_qualified_replacement_name', (9, 0)), + 'deprecated_name': ( + object_to_return, + 'fully_qualified_replacement_name', + (9, 0), + ), } @@ -65,7 +69,9 @@ def __getattr__(name: str) -> Any: qualname = f'{module}.{attribute}' if canonical_name: - message = f'The alias {qualname!r} is deprecated, use {canonical_name!r} instead.' + message = ( + f'The alias {qualname!r} is deprecated, use {canonical_name!r} instead.' + ) else: message = f'{qualname!r} is deprecated.' diff --git a/sphinx/directives/other.py b/sphinx/directives/other.py index f74f919cc3e..18fdff194ee 100644 --- a/sphinx/directives/other.py +++ b/sphinx/directives/other.py @@ -78,17 +78,19 @@ def run(self) -> list[Node]: subnode['numbered'] = self.options.get('numbered', 0) subnode['titlesonly'] = 'titlesonly' in self.options self.set_source_info(subnode) + self.parse_content(subnode) + wrappernode = nodes.compound( classes=['toctree-wrapper', *self.options.get('class', ())], ) wrappernode.append(subnode) self.add_name(wrappernode) + return [wrappernode] - ret = self.parse_content(subnode) - ret.append(wrappernode) - return ret - - def parse_content(self, toctree: addnodes.toctree) -> list[Node]: + def parse_content(self, toctree: addnodes.toctree) -> None: + """ + Populate ``toctree['entries']`` and ``toctree['includefiles']`` from content. + """ generated_docnames = frozenset(StandardDomain._virtual_doc_names) suffixes = self.config.source_suffix current_docname = self.env.docname @@ -99,7 +101,6 @@ def parse_content(self, toctree: addnodes.toctree) -> list[Node]: all_docnames.remove(current_docname) # remove current document frozen_all_docnames = frozenset(all_docnames) - ret: list[Node] = [] excluded = Matcher(self.config.exclude_patterns) for entry in self.content: if not entry: @@ -110,17 +111,20 @@ def parse_content(self, toctree: addnodes.toctree) -> list[Node]: url_match = url_re.match(entry) is not None if glob and glob_re.match(entry) and not explicit and not url_match: pat_name = docname_join(current_docname, entry) - doc_names = sorted(patfilter(all_docnames, pat_name)) + doc_names = sorted( + docname for docname in patfilter(all_docnames, pat_name) + # don't include generated documents in globs + if docname not in generated_docnames + ) + if not doc_names: + logger.warning( + __("toctree glob pattern %r didn't match any documents"), + entry, location=toctree) + for docname in doc_names: - if docname in generated_docnames: - # don't include generated documents in globs - continue all_docnames.remove(docname) # don't include it again toctree['entries'].append((None, docname)) toctree['includefiles'].append(docname) - if not doc_names: - logger.warning(__("toctree glob pattern %r didn't match any documents"), - entry, location=toctree) continue if explicit: @@ -170,8 +174,6 @@ def parse_content(self, toctree: addnodes.toctree) -> list[Node]: toctree['entries'] = list(reversed(toctree['entries'])) toctree['includefiles'] = list(reversed(toctree['includefiles'])) - return ret - class Author(SphinxDirective): """ diff --git a/sphinx/domains/c/_parser.py b/sphinx/domains/c/_parser.py index 42211988414..1d29c60832e 100644 --- a/sphinx/domains/c/_parser.py +++ b/sphinx/domains/c/_parser.py @@ -495,9 +495,11 @@ def _parse_nested_name(self) -> ASTNestedName: self.fail("Expected identifier in nested name, " "got keyword: %s" % identifier) if self.matched_text in self.config.c_extra_keywords: - msg = "Expected identifier, got user-defined keyword: %s." \ - + " Remove it from c_extra_keywords to allow it as identifier.\n" \ - + "Currently c_extra_keywords is %s." + msg = ( + 'Expected identifier, got user-defined keyword: %s.' + ' Remove it from c_extra_keywords to allow it as identifier.\n' + 'Currently c_extra_keywords is %s.' + ) self.fail(msg % (self.matched_text, str(self.config.c_extra_keywords))) ident = ASTIdentifier(identifier) @@ -670,9 +672,11 @@ def _parse_declarator_name_suffix( self.fail("Expected identifier, " "got keyword: %s" % self.matched_text) if self.matched_text in self.config.c_extra_keywords: - msg = "Expected identifier, got user-defined keyword: %s." \ - + " Remove it from c_extra_keywords to allow it as identifier.\n" \ - + "Currently c_extra_keywords is %s." + msg = ( + 'Expected identifier, got user-defined keyword: %s. ' + 'Remove it from c_extra_keywords to allow it as identifier.\n' + 'Currently c_extra_keywords is %s.' + ) self.fail(msg % (self.matched_text, str(self.config.c_extra_keywords))) identifier = ASTIdentifier(self.matched_text) diff --git a/sphinx/domains/cpp/_parser.py b/sphinx/domains/cpp/_parser.py index 452e4c9c8a7..d0d70221a45 100644 --- a/sphinx/domains/cpp/_parser.py +++ b/sphinx/domains/cpp/_parser.py @@ -1263,7 +1263,7 @@ def _parse_decl_specs_simple(self, outer: str, typed: bool) -> ASTDeclSpecsSimpl if self.skip_string('('): expr = self._parse_constant_expression(inTemplate=False) if not expr: - self.fail("Expected constant expression after '('" + + self.fail("Expected constant expression after '('" " in explicit specifier.") self.skip_ws() if not self.skip_string(')'): @@ -1972,13 +1972,14 @@ def _check_template_consistency(self, nestedName: ASTNestedName, if numArgs > numParams: numExtra = numArgs - numParams if not fullSpecShorthand and not isMemberInstantiation: - msg = "Too many template argument lists compared to parameter" \ - " lists. Argument lists: %d, Parameter lists: %d," \ - " Extra empty parameters lists prepended: %d." \ - % (numArgs, numParams, numExtra) - msg += " Declaration:\n\t" + msg = ( + f'Too many template argument lists compared to parameter lists. ' + f'Argument lists: {numArgs:d}, Parameter lists: {numParams:d}, ' + f'Extra empty parameters lists prepended: {numExtra:d}. ' + 'Declaration:\n\t' + ) if templatePrefix: - msg += "%s\n\t" % templatePrefix + msg += f"{templatePrefix}\n\t" msg += str(nestedName) self.warn(msg) diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py index 20bfd86bc75..637fe185f7f 100644 --- a/sphinx/environment/__init__.py +++ b/sphinx/environment/__init__.py @@ -12,10 +12,16 @@ from sphinx import addnodes from sphinx.environment.adapters import toctree as toctree_adapters -from sphinx.errors import BuildEnvironmentError, DocumentError, ExtensionError, SphinxError +from sphinx.errors import ( + BuildEnvironmentError, + DocumentError, + ExtensionError, + SphinxError, +) from sphinx.locale import __ from sphinx.transforms import SphinxTransformer -from sphinx.util import DownloadFiles, FilenameUniqDict, logging +from sphinx.util import logging +from sphinx.util._files import DownloadFiles, FilenameUniqDict from sphinx.util._timestamps import _format_rfc3339_microseconds from sphinx.util.docutils import LoggingReporter from sphinx.util.i18n import CatalogRepository, docname_to_domain diff --git a/sphinx/errors.py b/sphinx/errors.py index 44df3e8da95..c0339b4e998 100644 --- a/sphinx/errors.py +++ b/sphinx/errors.py @@ -45,7 +45,10 @@ class ExtensionError(SphinxError): """Extension error.""" def __init__( - self, message: str, orig_exc: Exception | None = None, modname: str | None = None + self, + message: str, + orig_exc: Exception | None = None, + modname: str | None = None, ) -> None: super().__init__(message) self.message = message diff --git a/sphinx/events.py b/sphinx/events.py index 4dc5131e7ee..df2fdf7048d 100644 --- a/sphinx/events.py +++ b/sphinx/events.py @@ -84,7 +84,10 @@ def disconnect(self, listener_id: int) -> None: listeners.remove(listener) def emit( - self, name: str, *args: Any, allowed_exceptions: tuple[type[Exception], ...] = () + self, + name: str, + *args: Any, + allowed_exceptions: tuple[type[Exception], ...] = (), ) -> list: """Emit a Sphinx event.""" # not every object likes to be repr()'d (think @@ -120,7 +123,10 @@ def emit( return results def emit_firstresult( - self, name: str, *args: Any, allowed_exceptions: tuple[type[Exception], ...] = () + self, + name: str, + *args: Any, + allowed_exceptions: tuple[type[Exception], ...] = (), ) -> Any: """Emit a Sphinx event and returns first result. diff --git a/sphinx/ext/apidoc.py b/sphinx/ext/apidoc.py index 53bc2ea370e..e08c0b1e91c 100644 --- a/sphinx/ext/apidoc.py +++ b/sphinx/ext/apidoc.py @@ -96,7 +96,10 @@ def write_file(name: str, text: str, opts: CliOptions) -> Path: def create_module_file( - package: str | None, basename: str, opts: CliOptions, user_template_dir: str | None = None + package: str | None, + basename: str, + opts: CliOptions, + user_template_dir: str | None = None, ) -> Path: """Build the text of the file and write the file.""" options = copy(OPTIONS) @@ -148,7 +151,9 @@ def create_package_file( if not is_skipped_module(Path(root, sub), opts, excludes) and not is_initpy(sub) ] submodules = sorted(set(submodules)) - submodules = [module_join(master_package, subroot, modname) for modname in submodules] + submodules = [ + module_join(master_package, subroot, modname) for modname in submodules + ] options = copy(OPTIONS) if opts.includeprivate and 'private-members' not in options: options.append('private-members') @@ -316,7 +321,9 @@ def recurse_tree( if is_pkg or is_namespace: # we are in a package with something to document if subs or len(files) > 1 or not is_skipped_package(root, opts): - subpackage = root[len(rootpath) :].lstrip(path.sep).replace(path.sep, '.') + subpackage = ( + root[len(rootpath) :].lstrip(path.sep).replace(path.sep, '.') + ) # if this is not a namespace or # a namespace and there is something there to document if not is_namespace or has_child_module(root, excludes, opts): @@ -342,7 +349,9 @@ def recurse_tree( if not is_skipped_module(Path(rootpath, py_file), opts, excludes): module = py_file.split('.')[0] written_files.append( - create_module_file(root_package, module, opts, user_template_dir) + create_module_file( + root_package, module, opts, user_template_dir + ) ) toplevels.append(module) @@ -361,7 +370,7 @@ def is_excluded(root: str | Path, excludes: Sequence[re.Pattern[str]]) -> bool: def get_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser( - usage='%(prog)s [OPTIONS] -o ' '[EXCLUDE_PATTERN, ...]', + usage='%(prog)s [OPTIONS] -o [EXCLUDE_PATTERN, ...]', epilog=__('For more information, visit .'), description=__(""" Look recursively in for Python modules and packages and create @@ -384,7 +393,9 @@ def get_parser() -> argparse.ArgumentParser: parser.add_argument( 'exclude_pattern', nargs='*', - help=__('fnmatch-style file and/or directory patterns ' 'to exclude from generation'), + help=__( + 'fnmatch-style file and/or directory patterns to exclude from generation' + ), ) parser.add_argument( @@ -408,10 +419,14 @@ def get_parser() -> argparse.ArgumentParser: dest='maxdepth', type=int, default=4, - help=__('maximum depth of submodules to show in the TOC ' '(default: 4)'), + help=__('maximum depth of submodules to show in the TOC (default: 4)'), ) parser.add_argument( - '-f', '--force', action='store_true', dest='force', help=__('overwrite existing files') + '-f', + '--force', + action='store_true', + dest='force', + help=__('overwrite existing files'), ) parser.add_argument( '-l', @@ -420,7 +435,7 @@ def get_parser() -> argparse.ArgumentParser: dest='followlinks', default=False, help=__( - 'follow symbolic links. Powerful when combined ' 'with collective.recipe.omelette.' + 'follow symbolic links. Powerful when combined with collective.recipe.omelette.' ), ) parser.add_argument( @@ -474,14 +489,14 @@ def get_parser() -> argparse.ArgumentParser: '--module-first', action='store_true', dest='modulefirst', - help=__('put module documentation before submodule ' 'documentation'), + help=__('put module documentation before submodule documentation'), ) parser.add_argument( '--implicit-namespaces', action='store_true', dest='implicit_namespaces', help=__( - 'interpret module paths according to PEP-0420 ' 'implicit namespaces specification' + 'interpret module paths according to PEP-0420 implicit namespaces specification' ), ) parser.add_argument( @@ -497,7 +512,9 @@ def get_parser() -> argparse.ArgumentParser: '--remove-old', action='store_true', dest='remove_old', - help=__('Remove existing files in the output directory that were not generated'), + help=__( + 'Remove existing files in the output directory that were not generated' + ), ) exclusive_group.add_argument( '-F', @@ -539,7 +556,9 @@ def get_parser() -> argparse.ArgumentParser: '--doc-release', action='store', dest='release', - help=__('project release, used when --full is given, ' 'defaults to --doc-version'), + help=__( + 'project release, used when --full is given, defaults to --doc-version' + ), ) group = parser.add_argument_group(__('extension options')) @@ -649,7 +668,11 @@ def main(argv: Sequence[str] = (), /) -> int: 'suffix': '.' + args.suffix, 'master': 'index', 'epub': True, - 'extensions': ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx.ext.todo'], + 'extensions': [ + 'sphinx.ext.autodoc', + 'sphinx.ext.viewcode', + 'sphinx.ext.todo', + ], 'makefile': True, 'batchfile': True, 'make_mode': True, @@ -670,7 +693,9 @@ def main(argv: Sequence[str] = (), /) -> int: d['extensions'].extend(ext.split(',')) if not args.dryrun: - qs.generate(d, silent=True, overwrite=args.force, templatedir=args.templatedir) + qs.generate( + d, silent=True, overwrite=args.force, templatedir=args.templatedir + ) elif args.tocfile: written_files.append( create_modules_toc_file(modules, args, args.tocfile, args.templatedir) diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py index 356043e1e68..bed3e5b8a37 100644 --- a/sphinx/ext/autosummary/generate.py +++ b/sphinx/ext/autosummary/generate.py @@ -131,7 +131,9 @@ def __init__(self, app: Sphinx) -> None: msg = 'Expected a Sphinx application object!' raise ValueError(msg) - system_templates_path = [os.path.join(package_dir, 'ext', 'autosummary', 'templates')] + system_templates_path = [ + os.path.join(package_dir, 'ext', 'autosummary', 'templates') + ] loader = SphinxTemplateLoader( app.srcdir, app.config.templates_path, system_templates_path ) @@ -297,7 +299,9 @@ def generate_autosummary_content( ns['members'] = scanner.scan(imported_members) respect_module_all = not app.config.autosummary_ignore_module_all - imported_members = imported_members or ('__all__' in dir(obj) and respect_module_all) + imported_members = imported_members or ( + '__all__' in dir(obj) and respect_module_all + ) ns['functions'], ns['all_functions'] = _get_members( doc, app, obj, {'function'}, imported=imported_members @@ -378,7 +382,9 @@ def generate_autosummary_content( def _skip_member(app: Sphinx, obj: Any, name: str, objtype: str) -> bool: try: - return app.emit_firstresult('autodoc-skip-member', objtype, name, obj, False, {}) + return app.emit_firstresult( + 'autodoc-skip-member', objtype, name, obj, False, {} + ) except Exception as exc: logger.warning( __( @@ -465,7 +471,11 @@ def _get_module_attrs(name: str, members: Any) -> tuple[list[str], list[str]]: def _get_modules( - obj: Any, *, skip: Sequence[str], name: str, public_members: Sequence[str] | None = None + obj: Any, + *, + skip: Sequence[str], + name: str, + public_members: Sequence[str] | None = None, ) -> tuple[list[str], list[str]]: items: list[str] = [] public: list[str] = [] @@ -511,7 +521,9 @@ def generate_autosummary_docs( showed_sources = sorted(sources) if len(showed_sources) > 20: showed_sources = showed_sources[:10] + ['...'] + showed_sources[-10:] - logger.info(__('[autosummary] generating autosummary for: %s'), ', '.join(showed_sources)) + logger.info( + __('[autosummary] generating autosummary for: %s'), ', '.join(showed_sources) + ) if output_dir: logger.info(__('[autosummary] writing to %s'), output_dir) @@ -736,7 +748,9 @@ def find_autosummary_in_lines( if m: current_module = m.group(1).strip() # recurse into the automodule docstring - documented.extend(find_autosummary_in_docstring(current_module, filename=filename)) + documented.extend( + find_autosummary_in_docstring(current_module, filename=filename) + ) continue m = module_re.match(line) @@ -789,7 +803,7 @@ def get_parser() -> argparse.ArgumentParser: action='store', dest='suffix', default='rst', - help=__('default suffix for files (default: ' '%(default)s)'), + help=__('default suffix for files (default: %(default)s)'), ) parser.add_argument( '-t', @@ -797,7 +811,7 @@ def get_parser() -> argparse.ArgumentParser: action='store', dest='templates', default=None, - help=__('custom template directory (default: ' '%(default)s)'), + help=__('custom template directory (default: %(default)s)'), ) parser.add_argument( '-i', @@ -805,7 +819,7 @@ def get_parser() -> argparse.ArgumentParser: action='store_true', dest='imported_members', default=False, - help=__('document imported members (default: ' '%(default)s)'), + help=__('document imported members (default: %(default)s)'), ) parser.add_argument( '-a', @@ -823,7 +837,9 @@ def get_parser() -> argparse.ArgumentParser: action='store_true', dest='remove_old', default=False, - help=__('Remove existing files in the output directory that were not generated'), + help=__( + 'Remove existing files in the output directory that were not generated' + ), ) return parser diff --git a/sphinx/ext/coverage.py b/sphinx/ext/coverage.py index f7ce3beaa65..5ecc1670959 100644 --- a/sphinx/ext/coverage.py +++ b/sphinx/ext/coverage.py @@ -455,7 +455,7 @@ def write_py_coverage(self) -> None: if self.app.quiet or self.app.warningiserror: for meth in methods: logger.warning( - __('undocumented python method:' + + __('undocumented python method:' ' %s :: %s :: %s'), name, class_name, meth) else: diff --git a/sphinx/ext/intersphinx/_resolve.py b/sphinx/ext/intersphinx/_resolve.py index 730385a7359..35a8c12bc7d 100644 --- a/sphinx/ext/intersphinx/_resolve.py +++ b/sphinx/ext/intersphinx/_resolve.py @@ -82,10 +82,16 @@ def _resolve_reference_in_domain_by_target( insensitive_matches = list(filter(lambda k: k.lower() == target_lower, inventory[objtype].keys())) if len(insensitive_matches) > 1: + data_items = {inventory[objtype][match] for match in insensitive_matches} inv_descriptor = inv_name or 'main_inventory' - LOGGER.warning(__("inventory '%s': multiple matches found for %s:%s"), - inv_descriptor, objtype, target, - type='intersphinx', subtype='external', location=node) + if len(data_items) == 1: # these are duplicates; relatively innocuous + LOGGER.debug(__("inventory '%s': duplicate matches found for %s:%s"), + inv_descriptor, objtype, target, + type='intersphinx', subtype='external', location=node) + else: + LOGGER.warning(__("inventory '%s': multiple matches found for %s:%s"), + inv_descriptor, objtype, target, + type='intersphinx', subtype='external', location=node) if insensitive_matches: data = inventory[objtype][insensitive_matches[0]] else: diff --git a/sphinx/highlighting.py b/sphinx/highlighting.py index 86bdd234430..c7ae156689d 100644 --- a/sphinx/highlighting.py +++ b/sphinx/highlighting.py @@ -48,7 +48,11 @@ } -escape_hl_chars = {ord('\\'): '\\PYGZbs{}', ord('{'): '\\PYGZob{}', ord('}'): '\\PYGZcb{}'} +escape_hl_chars = { + ord('\\'): '\\PYGZbs{}', + ord('{'): '\\PYGZob{}', + ord('}'): '\\PYGZcb{}', +} # used if Pygments is available # MEMO: no use of \protected here to avoid having to do hyperref extras, @@ -96,7 +100,10 @@ class PygmentsBridge: latex_formatter = LatexFormatter[str] def __init__( - self, dest: str = 'html', stylename: str = 'sphinx', latex_engine: str | None = None + self, + dest: str = 'html', + stylename: str = 'sphinx', + latex_engine: str | None = None, ) -> None: self.dest = dest self.latex_engine = latex_engine diff --git a/sphinx/jinja2glue.py b/sphinx/jinja2glue.py index ec75c6d96cb..0df58b52d4f 100644 --- a/sphinx/jinja2glue.py +++ b/sphinx/jinja2glue.py @@ -116,7 +116,9 @@ class SphinxFileSystemLoader(FileSystemLoader): template names. """ - def get_source(self, environment: Environment, template: str) -> tuple[str, str, Callable]: + def get_source( + self, environment: Environment, template: str + ) -> tuple[str, str, Callable]: for searchpath in self.searchpath: filename = path.join(searchpath, template) f = open_if_exists(filename) @@ -204,8 +206,14 @@ def render_string(self, source: str, context: dict) -> str: return self.environment.from_string(source).render(context) def newest_template_mtime(self) -> float: + return self._newest_template_mtime_name()[0] + + def newest_template_name(self) -> str: + return self._newest_template_mtime_name()[1] + + def _newest_template_mtime_name(self) -> tuple[float, str]: return max( - os.stat(os.path.join(root, sfile)).st_mtime_ns / 10**9 + (os.stat(os.path.join(root, sfile)).st_mtime_ns / 10**9, sfile) for dirname in self.pathchain for root, _dirs, files in os.walk(dirname) for sfile in files @@ -214,7 +222,9 @@ def newest_template_mtime(self) -> float: # Loader interface - def get_source(self, environment: Environment, template: str) -> tuple[str, str, Callable]: + def get_source( + self, environment: Environment, template: str + ) -> tuple[str, str, Callable]: loaders = self.loaders # exclamation mark starts search from theme if template.startswith('!'): diff --git a/sphinx/locale/__init__.py b/sphinx/locale/__init__.py index c0b4ee9adff..1d66c8e1e45 100644 --- a/sphinx/locale/__init__.py +++ b/sphinx/locale/__init__.py @@ -166,11 +166,15 @@ def init_console( return init([locale_dir], language, catalog, 'console') -def get_translator(catalog: str = 'sphinx', namespace: str = 'general') -> NullTranslations: +def get_translator( + catalog: str = 'sphinx', namespace: str = 'general' +) -> NullTranslations: return translators.get((namespace, catalog), NullTranslations()) -def is_translator_registered(catalog: str = 'sphinx', namespace: str = 'general') -> bool: +def is_translator_registered( + catalog: str = 'sphinx', namespace: str = 'general' +) -> bool: return (namespace, catalog) in translators diff --git a/sphinx/parsers.py b/sphinx/parsers.py index 955d59b3b79..cc10ce184b1 100644 --- a/sphinx/parsers.py +++ b/sphinx/parsers.py @@ -72,7 +72,9 @@ def parse(self, inputstring: str | StringList, document: nodes.document) -> None # preprocess inputstring if isinstance(inputstring, str): lines = docutils.statemachine.string2lines( - inputstring, tab_width=document.settings.tab_width, convert_whitespace=True + inputstring, + tab_width=document.settings.tab_width, + convert_whitespace=True, ) inputlines = StringList(lines, document.current_source) diff --git a/sphinx/project.py b/sphinx/project.py index f1d77a351a0..01642999306 100644 --- a/sphinx/project.py +++ b/sphinx/project.py @@ -23,7 +23,9 @@ class Project: """A project is the source code set of the Sphinx document(s).""" - def __init__(self, srcdir: str | os.PathLike[str], source_suffix: Iterable[str]) -> None: + def __init__( + self, srcdir: str | os.PathLike[str], source_suffix: Iterable[str] + ) -> None: #: Source directory. self.srcdir = _StrPath(srcdir) @@ -82,7 +84,9 @@ def discover( self._docname_to_path[docname] = path else: logger.warning( - __('Ignored unreadable document %r.'), filename, location=docname + __('Ignored unreadable document %r.'), + filename, + location=docname, ) return self.docnames diff --git a/sphinx/roles.py b/sphinx/roles.py index fbc322dcf5b..182e2c0da24 100644 --- a/sphinx/roles.py +++ b/sphinx/roles.py @@ -167,7 +167,11 @@ def process_link( return title, ws_re.sub(' ', target) def result_nodes( - self, document: nodes.document, env: BuildEnvironment, node: Element, is_ref: bool + self, + document: nodes.document, + env: BuildEnvironment, + node: Element, + is_ref: bool, ) -> tuple[list[Node], list[system_message]]: """Called before returning the finished nodes. *node* is the reference node if one was created (*is_ref* is then true), else the content node. @@ -211,7 +215,9 @@ def run(self) -> tuple[list[Node], list[system_message]]: try: refuri = self.build_uri() - reference = nodes.reference('', '', internal=False, refuri=refuri, classes=['pep']) + reference = nodes.reference( + '', '', internal=False, refuri=refuri, classes=['pep'] + ) if self.has_explicit_title: reference += nodes.strong(self.title, self.title) else: @@ -246,7 +252,9 @@ def run(self) -> tuple[list[Node], list[system_message]]: try: refuri = self.build_uri() - reference = nodes.reference('', '', internal=False, refuri=refuri, classes=['rfc']) + reference = nodes.reference( + '', '', internal=False, refuri=refuri, classes=['rfc'] + ) if self.has_explicit_title: reference += nodes.strong(self.title, self.title) else: diff --git a/sphinx/search/ja.py b/sphinx/search/ja.py index 5669155cb04..7ff663292df 100644 --- a/sphinx/search/ja.py +++ b/sphinx/search/ja.py @@ -29,7 +29,7 @@ from sphinx.errors import ExtensionError, SphinxError from sphinx.search import SearchLanguage -from sphinx.util import import_object +from sphinx.util._importer import import_object class BaseSplitter: @@ -505,12 +505,18 @@ class SearchJapanese(SearchLanguage): language_name = 'Japanese' def init(self, options: dict[str, str]) -> None: - dotted_path = options.get('type', 'sphinx.search.ja.DefaultSplitter') - try: - self.splitter = import_object(dotted_path)(options) - except ExtensionError as exc: - raise ExtensionError("Splitter module %r can't be imported" % - dotted_path) from exc + dotted_path = options.get('type') + if dotted_path is None: + self.splitter = DefaultSplitter(options) + else: + try: + splitter_cls = import_object( + dotted_path, "html_search_options['type'] setting" + ) + self.splitter = splitter_cls(options) + except ExtensionError as exc: + msg = f"Splitter module {dotted_path!r} can't be imported" + raise ExtensionError(msg) from exc def split(self, input: str) -> list[str]: return self.splitter.split(input) diff --git a/sphinx/testing/fixtures.py b/sphinx/testing/fixtures.py index 388e5f6e3cc..7e7811e8907 100644 --- a/sphinx/testing/fixtures.py +++ b/sphinx/testing/fixtures.py @@ -42,7 +42,7 @@ def pytest_configure(config: pytest.Config) -> None: @pytest.fixture(scope='session') -def rootdir() -> str | None: +def rootdir() -> Path | None: return None @@ -74,7 +74,7 @@ def app_params( test_params: dict[str, Any], shared_result: SharedResult, sphinx_test_tempdir: str, - rootdir: str, + rootdir: Path, ) -> _app_params: """ Parameters that are specified by 'pytest.mark.sphinx' for diff --git a/sphinx/themes/basic/static/basic.css.jinja b/sphinx/themes/basic/static/basic.css.jinja index 297b9bfaeff..53fbadb0b6a 100644 --- a/sphinx/themes/basic/static/basic.css.jinja +++ b/sphinx/themes/basic/static/basic.css.jinja @@ -115,15 +115,11 @@ img { /* -- search page ----------------------------------------------------------- */ ul.search { - margin: 10px 0 0 20px; - padding: 0; + margin-top: 10px; } ul.search li { - padding: 5px 0 5px 20px; - background-image: url(file.png); - background-repeat: no-repeat; - background-position: 0 7px; + padding: 5px 0; } ul.search li a { diff --git a/sphinx/themes/basic/static/searchtools.js b/sphinx/themes/basic/static/searchtools.js index b08d58c9b9b..2b058e13bec 100644 --- a/sphinx/themes/basic/static/searchtools.js +++ b/sphinx/themes/basic/static/searchtools.js @@ -20,7 +20,7 @@ if (typeof Scorer === "undefined") { // and returns the new score. /* score: result => { - const [docname, title, anchor, descr, score, filename] = result + const [docname, title, anchor, descr, score, filename, context] = result return score }, */ @@ -47,6 +47,14 @@ if (typeof Scorer === "undefined") { }; } +// Global search result kind enum, used by themes to style search results. +class SearchResultContext { + static get index() { return "index"; } + static get object() { return "object"; } + static get text() { return "text"; } + static get title() { return "title"; } +} + const _removeChildren = (element) => { while (element && element.lastChild) element.removeChild(element.lastChild); }; @@ -64,9 +72,13 @@ const _displayItem = (item, searchTerms, highlightTerms) => { const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; const contentRoot = document.documentElement.dataset.content_root; - const [docName, title, anchor, descr, score, _filename] = item; + const [docName, title, anchor, descr, score, _filename, context] = item; let listItem = document.createElement("li"); + // Add a class representing the item's type: + // can be used by a theme's CSS selector for styling + // See SearchResultContext for the class names. + listItem.classList.add(`context-${context}`); let requestUrl; let linkUrl; if (docBuilder === "dirhtml") { @@ -115,8 +127,10 @@ const _finishSearch = (resultCount) => { "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." ); else - Search.status.innerText = _( - "Search finished, found ${resultCount} page(s) matching the search query." + Search.status.innerText = Documentation.ngettext( + "Search finished, found one page matching the search query.", + "Search finished, found ${resultCount} pages matching the search query.", + resultCount, ).replace('${resultCount}', resultCount); }; const _displayNextItem = ( @@ -138,7 +152,7 @@ const _displayNextItem = ( else _finishSearch(resultCount); }; // Helper function used by query() to order search results. -// Each input is an array of [docname, title, anchor, descr, score, filename]. +// Each input is an array of [docname, title, anchor, descr, score, filename, context]. // Order the results by score (in opposite order of appearance, since the // `_displayNextItem` function uses pop() to retrieve items) and then alphabetically. const _orderResultsByScoreThenName = (a, b) => { @@ -248,6 +262,7 @@ const Search = { searchSummary.classList.add("search-summary"); searchSummary.innerText = ""; const searchList = document.createElement("ul"); + searchList.setAttribute("role", "list"); searchList.classList.add("search"); const out = document.getElementById("search-results"); @@ -318,7 +333,7 @@ const Search = { const indexEntries = Search._index.indexentries; // Collect multiple result groups to be sorted separately and then ordered. - // Each is an array of [docname, title, anchor, descr, score, filename]. + // Each is an array of [docname, title, anchor, descr, score, filename, context]. const normalResults = []; const nonMainIndexResults = []; @@ -337,6 +352,7 @@ const Search = { null, score + boost, filenames[file], + SearchResultContext.title, ]); } } @@ -354,6 +370,7 @@ const Search = { null, score, filenames[file], + SearchResultContext.index, ]; if (isMain) { normalResults.push(result); @@ -475,6 +492,7 @@ const Search = { descr, score, filenames[match[0]], + SearchResultContext.object, ]); }; Object.keys(objects).forEach((prefix) => @@ -585,6 +603,7 @@ const Search = { null, score, filenames[file], + SearchResultContext.text, ]); } return results; diff --git a/sphinx/theming.py b/sphinx/theming.py index b132c8bcd24..6d9986ba3c2 100644 --- a/sphinx/theming.py +++ b/sphinx/theming.py @@ -293,7 +293,9 @@ def _load_theme_with_ancestors( return themes, theme_dirs, tmp_dirs -def _load_theme(name: str, theme_path: str, /) -> tuple[str, str, str | None, _ConfigFile]: +def _load_theme( + name: str, theme_path: str, / +) -> tuple[str, str, str | None, _ConfigFile]: if path.isdir(theme_path): # already a directory, do nothing tmp_dir = None @@ -371,7 +373,9 @@ def _convert_theme_toml(cfg: _ThemeToml, /) -> _ConfigFile: pygments_table = theme.get('pygments_style', {}) if isinstance(pygments_table, str): hint = f'pygments_style = {{ default = "{pygments_table}" }}' - msg = __('The "theme.pygments_style" setting must be a table. Hint: "%s"') % hint + msg = ( + __('The "theme.pygments_style" setting must be a table. Hint: "%s"') % hint + ) raise ThemeError(msg) pygments_style_default: str | None = pygments_table.get('default') pygments_style_dark: str | None = pygments_table.get('dark') @@ -401,15 +405,23 @@ def _validate_theme_conf(cfg: configparser.RawConfigParser, name: str) -> str: def _convert_theme_conf(cfg: configparser.RawConfigParser, /) -> _ConfigFile: if stylesheet := cfg.get('theme', 'stylesheet', fallback=''): - stylesheets: tuple[str, ...] | None = tuple(map(str.strip, stylesheet.split(','))) + stylesheets: tuple[str, ...] | None = tuple( + map(str.strip, stylesheet.split(',')) + ) else: stylesheets = None if sidebar := cfg.get('theme', 'sidebars', fallback=''): - sidebar_templates: tuple[str, ...] | None = tuple(map(str.strip, sidebar.split(','))) + sidebar_templates: tuple[str, ...] | None = tuple( + map(str.strip, sidebar.split(',')) + ) else: sidebar_templates = None - pygments_style_default: str | None = cfg.get('theme', 'pygments_style', fallback=None) - pygments_style_dark: str | None = cfg.get('theme', 'pygments_dark_style', fallback=None) + pygments_style_default: str | None = cfg.get( + 'theme', 'pygments_style', fallback=None + ) + pygments_style_dark: str | None = cfg.get( + 'theme', 'pygments_dark_style', fallback=None + ) options = dict(cfg.items('options')) if cfg.has_section('options') else {} return _ConfigFile( stylesheets=stylesheets, diff --git a/sphinx/transforms/i18n.py b/sphinx/transforms/i18n.py index 35e1a086abb..8b67836764a 100644 --- a/sphinx/transforms/i18n.py +++ b/sphinx/transforms/i18n.py @@ -202,7 +202,7 @@ def list_replace_or_append(lst: list[N], old: N, new: N) -> None: old_foot_refs = list(is_autofootnote_ref.findall(self.node)) new_foot_refs = list(is_autofootnote_ref.findall(self.patch)) self.compare_references(old_foot_refs, new_foot_refs, - __('inconsistent footnote references in translated message.' + + __('inconsistent footnote references in translated message.' ' original: {0}, translated: {1}')) old_foot_namerefs: dict[str, list[nodes.footnote_reference]] = {} for r in old_foot_refs: @@ -242,7 +242,7 @@ def update_refnamed_references(self) -> None: old_refs = list(is_refnamed_ref.findall(self.node)) new_refs = list(is_refnamed_ref.findall(self.patch)) self.compare_references(old_refs, new_refs, - __('inconsistent references in translated message.' + + __('inconsistent references in translated message.' ' original: {0}, translated: {1}')) old_ref_names = [r['refname'] for r in old_refs] new_ref_names = [r['refname'] for r in new_refs] @@ -267,7 +267,7 @@ def update_refnamed_footnote_references(self) -> None: new_foot_refs = list(is_refnamed_footnote_ref.findall(self.patch)) refname_ids_map: dict[str, list[str]] = {} self.compare_references(old_foot_refs, new_foot_refs, - __('inconsistent footnote references in translated message.' + + __('inconsistent footnote references in translated message.' ' original: {0}, translated: {1}')) for oldf in old_foot_refs: refname_ids_map.setdefault(oldf["refname"], []).append(oldf["ids"]) @@ -282,7 +282,7 @@ def update_citation_references(self) -> None: old_cite_refs = list(is_citation_ref.findall(self.node)) new_cite_refs = list(is_citation_ref.findall(self.patch)) self.compare_references(old_cite_refs, new_cite_refs, - __('inconsistent citation references in translated message.' + + __('inconsistent citation references in translated message.' ' original: {0}, translated: {1}')) refname_ids_map: dict[str, list[str]] = {} for oldc in old_cite_refs: @@ -299,7 +299,7 @@ def update_pending_xrefs(self) -> None: old_xrefs = [*self.node.findall(addnodes.pending_xref)] new_xrefs = [*self.patch.findall(addnodes.pending_xref)] self.compare_references(old_xrefs, new_xrefs, - __('inconsistent term references in translated message.' + + __('inconsistent term references in translated message.' ' original: {0}, translated: {1}')) xref_reftarget_map: dict[tuple[str, str, str] | None, dict[str, Any]] = {} diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py index 4271f908930..6dd54cb5e76 100644 --- a/sphinx/util/__init__.py +++ b/sphinx/util/__init__.py @@ -6,15 +6,13 @@ import os import posixpath import re -from importlib import import_module -from os import path -from typing import IO, Any +from typing import Any from urllib.parse import parse_qsl, quote_plus, urlencode, urlsplit, urlunsplit -from sphinx.errors import ExtensionError, FiletypeNotFoundError +from sphinx.errors import FiletypeNotFoundError from sphinx.locale import __ +from sphinx.util import _files, _importer, logging from sphinx.util import index_entries as _index_entries -from sphinx.util import logging from sphinx.util.console import strip_colors # NoQA: F401 from sphinx.util.matching import patfilter # NoQA: F401 from sphinx.util.nodes import ( # NoQA: F401 @@ -56,49 +54,6 @@ def get_filetype(source_suffix: dict[str, str], filename: str | os.PathLike) -> raise FiletypeNotFoundError -class FilenameUniqDict(dict): - """ - A dictionary that automatically generates unique names for its keys, - interpreted as filenames, and keeps track of a set of docnames they - appear in. Used for images and downloadable files in the environment. - """ - - def __init__(self) -> None: - self._existing: set[str] = set() - - def add_file(self, docname: str, newfile: str) -> str: - if newfile in self: - self[newfile][0].add(docname) - return self[newfile][1] - uniquename = path.basename(newfile) - base, ext = path.splitext(uniquename) - i = 0 - while uniquename in self._existing: - i += 1 - uniquename = f'{base}{i}{ext}' - self[newfile] = ({docname}, uniquename) - self._existing.add(uniquename) - return uniquename - - def purge_doc(self, docname: str) -> None: - for filename, (docs, unique) in list(self.items()): - docs.discard(docname) - if not docs: - del self[filename] - self._existing.discard(unique) - - def merge_other(self, docnames: set[str], other: dict[str, tuple[set[str], Any]]) -> None: - for filename, (docs, _unique) in other.items(): - for doc in docs & set(docnames): - self.add_file(doc, filename) - - def __getstate__(self) -> set[str]: - return self._existing - - def __setstate__(self, state: set[str]) -> None: - self._existing = state - - def _md5(data: bytes = b'', **_kw: Any) -> hashlib._Hash: """Deprecated wrapper around hashlib.md5 @@ -115,38 +70,6 @@ def _sha1(data: bytes = b'', **_kw: Any) -> hashlib._Hash: return hashlib.sha1(data, usedforsecurity=False) -class DownloadFiles(dict): - """A special dictionary for download files. - - .. important:: This class would be refactored in nearly future. - Hence don't hack this directly. - """ - - def add_file(self, docname: str, filename: str) -> str: - if filename not in self: - digest = hashlib.md5(filename.encode(), usedforsecurity=False).hexdigest() - dest = f'{digest}/{os.path.basename(filename)}' - self[filename] = (set(), dest) - - self[filename][0].add(docname) - return self[filename][1] - - def purge_doc(self, docname: str) -> None: - for filename, (docs, _dest) in list(self.items()): - docs.discard(docname) - if not docs: - del self[filename] - - def merge_other(self, docnames: set[str], other: dict[str, tuple[set[str], Any]]) -> None: - for filename, (docs, _dest) in other.items(): - for docname in docs & set(docnames): - self.add_file(docname, filename) - - -# a regex to recognize coding cookies -_coding_re = re.compile(r'coding[:=]\s*([-\w.]+)') - - class UnicodeDecodeErrorHandler: """Custom error handler for open() that warns and replaces.""" @@ -169,26 +92,6 @@ def __call__(self, error: UnicodeDecodeError) -> tuple[str, int]: # Low-level utility functions and classes. -class Tee: - """ - File-like object writing to two streams. - """ - - def __init__(self, stream1: IO, stream2: IO) -> None: - self.stream1 = stream1 - self.stream2 = stream2 - - def write(self, text: str) -> None: - self.stream1.write(text) - self.stream2.write(text) - - def flush(self) -> None: - if hasattr(self.stream1, 'flush'): - self.stream1.flush() - if hasattr(self.stream2, 'flush'): - self.stream2.flush() - - def parselinenos(spec: str, total: int) -> list[int]: """Parse a line number spec (such as "1,2,4-6") and return a list of wanted line numbers. @@ -217,27 +120,6 @@ def parselinenos(spec: str, total: int) -> list[int]: return items -def import_object(objname: str, source: str | None = None) -> Any: - """Import python object by qualname.""" - try: - objpath = objname.split('.') - modname = objpath.pop(0) - obj = import_module(modname) - for name in objpath: - modname += '.' + name - try: - obj = getattr(obj, name) - except AttributeError: - obj = import_module(modname) - - return obj - except (AttributeError, ImportError) as exc: - if source: - raise ExtensionError('Could not import %s (needed for %s)' % - (objname, source), exc) from exc - raise ExtensionError('Could not import %s' % objname, exc) from exc - - def encode_uri(uri: str) -> str: split = list(urlsplit(uri)) split[1] = split[1].encode('idna').decode('ascii') @@ -262,6 +144,9 @@ def isurl(url: str) -> bool: (9, 0)), 'md5': (_md5, '', (9, 0)), 'sha1': (_sha1, '', (9, 0)), + 'import_object': (_importer.import_object, '', (10, 0)), + 'FilenameUniqDict': (_files.FilenameUniqDict, '', (10, 0)), + 'DownloadFiles': (_files.DownloadFiles, '', (10, 0)), } diff --git a/sphinx/util/_files.py b/sphinx/util/_files.py new file mode 100644 index 00000000000..9252002411f --- /dev/null +++ b/sphinx/util/_files.py @@ -0,0 +1,76 @@ +from __future__ import annotations + +import hashlib +import os.path +from typing import Any + + +class FilenameUniqDict(dict[str, tuple[set[str], str]]): + """ + A dictionary that automatically generates unique names for its keys, + interpreted as filenames, and keeps track of a set of docnames they + appear in. Used for images and downloadable files in the environment. + """ + + def __init__(self) -> None: + self._existing: set[str] = set() + + def add_file(self, docname: str, newfile: str) -> str: + if newfile in self: + self[newfile][0].add(docname) + return self[newfile][1] + uniquename = os.path.basename(newfile) + base, ext = os.path.splitext(uniquename) + i = 0 + while uniquename in self._existing: + i += 1 + uniquename = f'{base}{i}{ext}' + self[newfile] = ({docname}, uniquename) + self._existing.add(uniquename) + return uniquename + + def purge_doc(self, docname: str) -> None: + for filename, (docs, unique) in list(self.items()): + docs.discard(docname) + if not docs: + del self[filename] + self._existing.discard(unique) + + def merge_other(self, docnames: set[str], other: dict[str, tuple[set[str], Any]]) -> None: + for filename, (docs, _unique) in other.items(): + for doc in docs & set(docnames): + self.add_file(doc, filename) + + def __getstate__(self) -> set[str]: + return self._existing + + def __setstate__(self, state: set[str]) -> None: + self._existing = state + + +class DownloadFiles(dict[str, tuple[set[str], str]]): + """A special dictionary for download files. + + .. important:: This class would be refactored in nearly future. + Hence don't hack this directly. + """ + + def add_file(self, docname: str, filename: str) -> str: + if filename not in self: + digest = hashlib.md5(filename.encode(), usedforsecurity=False).hexdigest() + dest = f'{digest}/{os.path.basename(filename)}' + self[filename] = (set(), dest) + + self[filename][0].add(docname) + return self[filename][1] + + def purge_doc(self, docname: str) -> None: + for filename, (docs, _dest) in list(self.items()): + docs.discard(docname) + if not docs: + del self[filename] + + def merge_other(self, docnames: set[str], other: dict[str, tuple[set[str], Any]]) -> None: + for filename, (docs, _dest) in other.items(): + for docname in docs & set(docnames): + self.add_file(docname, filename) diff --git a/sphinx/util/_importer.py b/sphinx/util/_importer.py new file mode 100644 index 00000000000..915750d2d88 --- /dev/null +++ b/sphinx/util/_importer.py @@ -0,0 +1,27 @@ +from __future__ import annotations + +from importlib import import_module +from typing import Any + +from sphinx.errors import ExtensionError + + +def import_object(object_name: str, /, source: str = '') -> Any: + """Import python object by qualname.""" + obj_path = object_name.split('.') + module_name = obj_path.pop(0) + try: + obj = import_module(module_name) + for name in obj_path: + module_name += '.' + name + try: + obj = getattr(obj, name) + except AttributeError: + obj = import_module(module_name) + except (AttributeError, ImportError) as exc: + if source: + msg = f'Could not import {object_name} (needed for {source})' + raise ExtensionError(msg, exc) from exc + msg = f'Could not import {object_name}' + raise ExtensionError(msg, exc) from exc + return obj diff --git a/sphinx/util/fileutil.py b/sphinx/util/fileutil.py index 1996a7af846..259a2af1952 100644 --- a/sphinx/util/fileutil.py +++ b/sphinx/util/fileutil.py @@ -81,6 +81,9 @@ def copy_asset_file(source: str | os.PathLike[str], destination: str | os.PathLi destination = _template_basename(destination) or destination with open(destination, 'w', encoding='utf-8') as fdst: + msg = __('Writing evaluated template result to %s') + logger.info(msg, os.fsdecode(destination), type='misc', + subtype='template_evaluation') fdst.write(rendered_template) else: copyfile(source, destination, force=force) diff --git a/sphinx/versioning.py b/sphinx/versioning.py index 8756fabe4ba..506d7b5753d 100644 --- a/sphinx/versioning.py +++ b/sphinx/versioning.py @@ -45,7 +45,9 @@ def add_uids(doctree: Node, condition: Callable[[Node], bool]) -> Iterator[Node] yield node -def merge_doctrees(old: Node, new: Node, condition: Callable[[Node], bool]) -> Iterator[Node]: +def merge_doctrees( + old: Node, new: Node, condition: Callable[[Node], bool] +) -> Iterator[Node]: """Merge the `old` doctree with the `new` one while looking at nodes matching the `condition`. diff --git a/tests/conftest.py b/tests/conftest.py index 391cb4dc562..6e3b83b1397 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -19,7 +19,8 @@ def _init_console( - locale_dir: str | None = sphinx.locale._LOCALE_DIR, catalog: str = 'sphinx', + locale_dir: str | None = sphinx.locale._LOCALE_DIR, + catalog: str = 'sphinx', ) -> tuple[gettext.NullTranslations, bool]: """Monkeypatch ``init_console`` to skip its action. @@ -46,9 +47,9 @@ def rootdir() -> Path: def pytest_report_header(config: pytest.Config) -> str: - header = f"libraries: Sphinx-{sphinx.__display_version__}, docutils-{docutils.__version__}" + header = f'libraries: Sphinx-{sphinx.__display_version__}, docutils-{docutils.__version__}' if hasattr(config, '_tmp_path_factory'): - header += f"\nbase tmp_path: {config._tmp_path_factory.getbasetemp()}" + header += f'\nbase tmp_path: {config._tmp_path_factory.getbasetemp()}' return header diff --git a/tests/js/fixtures/partial/searchindex.js b/tests/js/fixtures/partial/searchindex.js index dc6bc1cddbb..b650c366e94 100644 --- a/tests/js/fixtures/partial/searchindex.js +++ b/tests/js/fixtures/partial/searchindex.js @@ -1 +1 @@ -Search.setIndex({"alltitles": {"sphinx_utils module": [[0, null]]}, "docnames": ["index"], "envversion": {"sphinx": 63, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst"], "indexentries": {}, "objects": {}, "objnames": {}, "objtypes": {}, "terms": {"also": 0, "ar": 0, "built": 0, "confirm": 0, "document": 0, "function": 0, "html": 0, "i": 0, "includ": 0, "input": 0, "javascript": 0, "known": 0, "match": 0, "partial": 0, "possibl": 0, "prefix": 0, "project": 0, "provid": 0, "restructuredtext": 0, "sampl": 0, "search": 0, "should": 0, "thi": 0, "titl": 0, "us": 0, "when": 0}, "titles": ["sphinx_utils module"], "titleterms": {"modul": 0, "sphinx_util": 0}}) \ No newline at end of file +Search.setIndex({"alltitles": {"sphinx_utils module": [[0, null]]}, "docnames": ["index"], "envversion": {"sphinx": 63, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst"], "indexentries": {}, "objects": {}, "objnames": {}, "objtypes": {}, "terms": {"ar": 0, "both": 0, "built": 0, "confirm": 0, "document": 0, "function": 0, "html": 0, "i": 0, "includ": 0, "input": 0, "javascript": 0, "match": 0, "partial": 0, "possibl": 0, "project": 0, "provid": 0, "restructuredtext": 0, "sampl": 0, "search": 0, "should": 0, "term": 0, "thi": 0, "titl": 0, "us": 0, "when": 0}, "titles": ["sphinx_utils module"], "titleterms": {"modul": 0, "sphinx_util": 0}}) \ No newline at end of file diff --git a/tests/js/fixtures/titles/searchindex.js b/tests/js/fixtures/titles/searchindex.js index f503bf35df9..a084952248a 100644 --- a/tests/js/fixtures/titles/searchindex.js +++ b/tests/js/fixtures/titles/searchindex.js @@ -1 +1 @@ -Search.setIndex({"alltitles": {"Main Page": [[0, null]], "Relevance": [[0, "relevance"], [1, null]]}, "docnames": ["index", "relevance"], "envversion": {"sphinx": 63, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst", "relevance.rst"], "indexentries": {"example (class in relevance)": [[0, "relevance.Example", false]], "module": [[0, "module-relevance", false]], "relevance": [[0, "module-relevance", false]], "relevance (relevance.example attribute)": [[0, "relevance.Example.relevance", false]]}, "objects": {"": [[0, 0, 0, "-", "relevance"]], "relevance": [[0, 1, 1, "", "Example"]], "relevance.Example": [[0, 2, 1, "", "relevance"]]}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "class", "Python class"], "2": ["py", "attribute", "Python attribute"]}, "objtypes": {"0": "py:module", "1": "py:class", "2": "py:attribute"}, "terms": {"": [0, 1], "A": 1, "For": 1, "In": [0, 1], "against": 0, "also": 1, "an": 0, "answer": 0, "appear": 1, "ar": 1, "area": 0, "ask": 0, "attribut": 0, "built": 1, "can": [0, 1], "class": 0, "code": [0, 1], "consid": 1, "contain": 0, "context": 0, "corpu": 1, "could": 1, "demonstr": 0, "describ": 1, "detail": 1, "determin": 1, "docstr": 0, "document": [0, 1], "domain": 1, "engin": 0, "exampl": [0, 1], "extract": 0, "find": 0, "found": 0, "from": 0, "function": 1, "ha": 1, "handl": 0, "happen": 1, "head": 0, "help": 0, "highli": 1, "how": 0, "i": [0, 1], "improv": 0, "inform": 0, "intend": 0, "issu": 1, "itself": 1, "knowledg": 0, "languag": 1, "less": 1, "like": [0, 1], "match": 0, "mention": 1, "name": [0, 1], "object": 0, "one": 1, "onli": 1, "other": 0, "page": 1, "part": 1, "particular": 0, "printf": 1, "program": 1, "project": 0, "queri": [0, 1], "question": 0, "re": 0, "rel": 0, "research": 0, "result": 1, "sai": 0, "same": 1, "score": 0, "search": [0, 1], "seem": 0, "softwar": 1, "some": 1, "sphinx": 0, "straightforward": 1, "subject": 0, "subsect": 0, "term": [0, 1], "test": 0, "text": 0, "than": 1, "thei": 0, "them": 0, "thi": 0, "titl": 0, "user": [0, 1], "we": [0, 1], "when": 0, "whether": 1, "within": 0, "would": 1}, "titles": ["Main Page", "Relevance"], "titleterms": {"main": 0, "page": 0, "relev": [0, 1]}}) \ No newline at end of file +Search.setIndex({"alltitles": {"Main Page": [[0, null]], "Relevance": [[0, "relevance"], [1, null]], "Result Scoring": [[0, "result-scoring"]]}, "docnames": ["index", "relevance"], "envversion": {"sphinx": 63, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst", "relevance.rst"], "indexentries": {"example (class in relevance)": [[0, "relevance.Example", false]], "module": [[0, "module-relevance", false]], "relevance": [[0, "index-1", false], [0, "module-relevance", false]], "relevance (relevance.example attribute)": [[0, "relevance.Example.relevance", false]], "scoring": [[0, "index-0", true]]}, "objects": {"": [[0, 0, 0, "-", "relevance"]], "relevance": [[0, 1, 1, "", "Example"]], "relevance.Example": [[0, 2, 1, "", "relevance"]]}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "class", "Python class"], "2": ["py", "attribute", "Python attribute"]}, "objtypes": {"0": "py:module", "1": "py:class", "2": "py:attribute"}, "terms": {"": [0, 1], "A": 1, "By": 0, "For": [0, 1], "In": [0, 1], "against": 0, "align": 0, "also": 1, "an": 0, "answer": 0, "appear": 1, "ar": 1, "area": 0, "ask": 0, "assign": 0, "attempt": 0, "attribut": 0, "both": 0, "built": 1, "can": [0, 1], "class": 0, "code": [0, 1], "collect": 0, "consid": 1, "contain": 0, "context": 0, "corpu": 1, "could": 1, "demonstr": 0, "describ": 1, "detail": 1, "determin": [0, 1], "docstr": 0, "document": [0, 1], "domain": 1, "dure": 0, "engin": 0, "evalu": 0, "exampl": [0, 1], "extract": 0, "feedback": 0, "find": 0, "found": 0, "from": 0, "function": 1, "ha": 1, "handl": 0, "happen": 1, "head": 0, "help": 0, "highli": [0, 1], "how": 0, "i": [0, 1], "improv": 0, "inform": 0, "intend": 0, "issu": [0, 1], "itself": 1, "knowledg": 0, "languag": 1, "less": 1, "like": [0, 1], "mani": 0, "match": 0, "mention": 1, "more": 0, "name": [0, 1], "numer": 0, "object": 0, "often": 0, "one": [0, 1], "onli": [0, 1], "order": 0, "other": 0, "over": 0, "page": 1, "part": 1, "particular": 0, "present": 0, "printf": 1, "program": 1, "project": 0, "queri": [0, 1], "question": 0, "re": 0, "rel": 0, "research": 0, "result": 1, "retriev": 0, "sai": 0, "same": 1, "search": [0, 1], "seem": 0, "softwar": 1, "some": 1, "sphinx": 0, "straightforward": 1, "subject": 0, "subsect": 0, "term": [0, 1], "test": 0, "text": 0, "than": [0, 1], "thei": 0, "them": 0, "thi": 0, "time": 0, "titl": 0, "two": 0, "typic": 0, "us": 0, "user": [0, 1], "we": [0, 1], "when": 0, "whether": 1, "which": 0, "within": 0, "word": 0, "would": [0, 1]}, "titles": ["Main Page", "Relevance"], "titleterms": {"main": 0, "page": 0, "relev": [0, 1], "result": 0, "score": 0}}) \ No newline at end of file diff --git a/tests/js/jasmine-browser.mjs b/tests/js/jasmine-browser.mjs new file mode 100644 index 00000000000..7cc8610dd76 --- /dev/null +++ b/tests/js/jasmine-browser.mjs @@ -0,0 +1,28 @@ +export default { + srcDir: ".", + srcFiles: [ + 'sphinx/themes/basic/static/doctools.js', + 'sphinx/themes/basic/static/searchtools.js', + 'sphinx/themes/basic/static/sphinx_highlight.js', + 'tests/js/fixtures/**/*.js', + 'tests/js/documentation_options.js', + 'tests/js/language_data.js', + ], + specDir: "tests/js", + specFiles: [ + '**/*.spec.js', + ], + helpers: [], + env: { + stopSpecOnExpectationFailure: false, + stopOnSpecFailure: false, + random: true + }, + + listenAddress: "127.0.0.1", + hostname: "127.0.0.1", + + browser: { + name: "firefox" + } +}; diff --git a/tests/js/roots/partial/index.rst b/tests/js/roots/partial/index.rst index 6a9561b3994..23fba6dd3eb 100644 --- a/tests/js/roots/partial/index.rst +++ b/tests/js/roots/partial/index.rst @@ -1,7 +1,7 @@ sphinx_utils module =================== -Partial (also known as "prefix") matches on document titles should be possible +Partial matches on document titles and document terms should both be possible using the JavaScript search functionality included when HTML documentation projects are built. diff --git a/tests/js/roots/titles/index.rst b/tests/js/roots/titles/index.rst index 464cd954b5c..c401c0b95f5 100644 --- a/tests/js/roots/titles/index.rst +++ b/tests/js/roots/titles/index.rst @@ -18,3 +18,17 @@ the subject area they're researching. .. automodule:: relevance :members: + +Result Scoring +-------------- + +Many search engines assign a numeric score to documents during retrieval of +results - and this score is often used to determine the order in which they +will be presented to the user. + +For example, if a user issues a query for a two words, then documents that +contain both of the words would typically be scored more highly than documents +which only contain one of them. + +By evaluating search results and collecting user feedback over time, we can +attempt to align document :index:`!scoring` with :index:`relevance`. diff --git a/tests/js/searchtools.js b/tests/js/searchtools.spec.js similarity index 80% rename from tests/js/searchtools.js rename to tests/js/searchtools.spec.js index c82c6f1f968..cfe5fdcf7ed 100644 --- a/tests/js/searchtools.js +++ b/tests/js/searchtools.spec.js @@ -2,7 +2,7 @@ describe('Basic html theme search', function() { function loadFixture(name) { req = new XMLHttpRequest(); - req.open("GET", `base/tests/js/fixtures/${name}`, false); + req.open("GET", `__src__/tests/js/fixtures/${name}`, false); req.send(null); return req.responseText; } @@ -38,7 +38,8 @@ describe('Basic html theme search', function() { "", null, 5, - "index.rst" + "index.rst", + "text" ]]; expect(Search.performTermsSearch(searchterms, excluded)).toEqual(hits); }); @@ -53,7 +54,9 @@ describe('Basic html theme search', function() { '', null, 15, - 'index.rst']]; + 'index.rst', + 'text' + ]]; expect(Search.performTermsSearch(searchterms, excluded)).toEqual(hits); }); @@ -68,11 +71,31 @@ describe('Basic html theme search', function() { "", null, 7, - "index.rst" + "index.rst", + "text" ]]; expect(Search.performTermsSearch(searchterms, excluded)).toEqual(hits); }); + it('should partially-match within "possible" when in term index', function() { + eval(loadFixture("partial/searchindex.js")); + + [_searchQuery, searchterms, excluded, ..._remainingItems] = Search._parseQuery('ossibl'); + terms = Search._index.terms; + titleterms = Search._index.titleterms; + + hits = [[ + "index", + "sphinx_utils module", + "", + null, + 2, + "index.rst", + "text" + ]]; + expect(Search.performTermsSearch(searchterms, excluded, terms, titleterms)).toEqual(hits); + }); + }); describe('aggregation of search results', function() { @@ -89,7 +112,8 @@ describe('Basic html theme search', function() { '', null, 16, - 'index.rst' + 'index.rst', + 'title' ] ]; expect(Search._performSearch(...searchParameters)).toEqual(hits); @@ -141,6 +165,34 @@ describe('Basic html theme search', function() { checkRanking(expectedRanking, results); }); + it('should score a title match above a standard index entry match', function() { + eval(loadFixture("titles/searchindex.js")); + + expectedRanking = [ + ['relevance', 'Relevance', ''], /* title */ + ['index', 'Main Page', '#index-1'], /* index entry */ + ]; + + searchParameters = Search._parseQuery('relevance'); + results = Search._performSearch(...searchParameters); + + checkRanking(expectedRanking, results); + }); + + it('should score a priority index entry match above a title match', function() { + eval(loadFixture("titles/searchindex.js")); + + expectedRanking = [ + ['index', 'Main Page', '#index-0'], /* index entry */ + ['index', 'Main Page > Result Scoring', '#result-scoring'], /* title */ + ]; + + searchParameters = Search._parseQuery('scoring'); + results = Search._performSearch(...searchParameters); + + checkRanking(expectedRanking, results); + }); + it('should score a main-title match above a subheading-title match', function() { eval(loadFixture("titles/searchindex.js")); diff --git a/tests/js/sphinx_highlight.js b/tests/js/sphinx_highlight.spec.js similarity index 100% rename from tests/js/sphinx_highlight.js rename to tests/js/sphinx_highlight.spec.js diff --git a/tests/roots/test-ext-autodoc/target/abstractmethods.py b/tests/roots/test-ext-autodoc/target/abstractmethods.py index a4396d5c93e..d860495e69d 100644 --- a/tests/roots/test-ext-autodoc/target/abstractmethods.py +++ b/tests/roots/test-ext-autodoc/target/abstractmethods.py @@ -1,7 +1,7 @@ from abc import abstractmethod -class Base(): +class Base: def meth(self): pass diff --git a/tests/roots/test-ext-autodoc/target/callable.py b/tests/roots/test-ext-autodoc/target/callable.py index 6fcd5053ee0..93a2c178f7b 100644 --- a/tests/roots/test-ext-autodoc/target/callable.py +++ b/tests/roots/test-ext-autodoc/target/callable.py @@ -1,4 +1,4 @@ -class Callable(): +class Callable: """A callable object that behaves like a function.""" def __call__(self, arg1, arg2, **kwargs): diff --git a/tests/roots/test-ext-autodoc/target/methods.py b/tests/roots/test-ext-autodoc/target/methods.py index ad5a6a952ca..4900f7989d7 100644 --- a/tests/roots/test-ext-autodoc/target/methods.py +++ b/tests/roots/test-ext-autodoc/target/methods.py @@ -1,7 +1,7 @@ from functools import partialmethod -class Base(): +class Base: def meth(self): pass diff --git a/tests/roots/test-ext-autosummary/autosummary_class_module.py b/tests/roots/test-ext-autosummary/autosummary_class_module.py index f13de17032f..2b1f40419d6 100644 --- a/tests/roots/test-ext-autosummary/autosummary_class_module.py +++ b/tests/roots/test-ext-autosummary/autosummary_class_module.py @@ -1,2 +1,2 @@ -class Class(): +class Class: pass diff --git a/tests/roots/test-gettext-custom-output-template/_templates/message.pot.jinja b/tests/roots/test-gettext-custom-output-template/_templates/message.pot.jinja new file mode 100644 index 00000000000..5cc2975f710 --- /dev/null +++ b/tests/roots/test-gettext-custom-output-template/_templates/message.pot.jinja @@ -0,0 +1,21 @@ +# EVEN MORE DESCRIPTIVE TITLE. +# Copyright (C) {{ copyright }} +# This file is distributed under the same license as the {{ project }} package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: {{ project|e }} {{ version|e }}\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: {{ ctime|e }}\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: {{ last_translator|e }}\n" +"Language-Team: {{ language_team|e }}\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +{% for message in messages %} +msgid "{{ message.text|e }}" +msgstr "" +{% endfor -%} diff --git a/tests/roots/test-gettext-custom-output-template/conf.py b/tests/roots/test-gettext-custom-output-template/conf.py new file mode 100644 index 00000000000..3f793b7cfcc --- /dev/null +++ b/tests/roots/test-gettext-custom-output-template/conf.py @@ -0,0 +1 @@ +templates_path = ['_templates'] diff --git a/tests/roots/test-gettext-custom-output-template/index.rst b/tests/roots/test-gettext-custom-output-template/index.rst new file mode 100644 index 00000000000..efe96e02895 --- /dev/null +++ b/tests/roots/test-gettext-custom-output-template/index.rst @@ -0,0 +1,7 @@ +CONTENTS +======== + +.. toctree:: + :maxdepth: 2 + :numbered: + :caption: Table of Contents diff --git a/tests/test_application.py b/tests/test_application.py index ee7e850e7c2..d30a9b12463 100644 --- a/tests/test_application.py +++ b/tests/test_application.py @@ -1,4 +1,5 @@ """Test the Sphinx class.""" + from __future__ import annotations import shutil @@ -51,26 +52,27 @@ def test_instantiation( def test_events(app): def empty(): pass + with pytest.raises(ExtensionError) as excinfo: - app.connect("invalid", empty) - assert "Unknown event name: invalid" in str(excinfo.value) + app.connect('invalid', empty) + assert 'Unknown event name: invalid' in str(excinfo.value) - app.add_event("my_event") + app.add_event('my_event') with pytest.raises(ExtensionError) as excinfo: - app.add_event("my_event") + app.add_event('my_event') assert "Event 'my_event' already present" in str(excinfo.value) def mock_callback(a_app, *args): assert a_app is app assert emit_args == args - return "ret" - emit_args = (1, 3, "string") - listener_id = app.connect("my_event", mock_callback) - assert app.emit("my_event", *emit_args) == ["ret"], "Callback not called" + return 'ret' + + emit_args = (1, 3, 'string') + listener_id = app.connect('my_event', mock_callback) + assert app.emit('my_event', *emit_args) == ['ret'], 'Callback not called' app.disconnect(listener_id) - assert app.emit("my_event", *emit_args) == [], \ - "Callback called when disconnected" + assert app.emit('my_event', *emit_args) == [], 'Callback called when disconnected' def test_emit_with_nonascii_name_node(app): @@ -121,14 +123,18 @@ def test_add_is_parallel_allowed(app): app.setup_extension('write_parallel') assert app.is_parallel_allowed('read') is False assert app.is_parallel_allowed('write') is True - assert ("the write_parallel extension does not declare if it is safe " - "for parallel reading, assuming it isn't - please ") in app.warning.getvalue() + assert ( + 'the write_parallel extension does not declare if it is safe ' + "for parallel reading, assuming it isn't - please " + ) in app.warning.getvalue() app.extensions.pop('write_parallel') app.warning.truncate(0) # reset warnings app.setup_extension('read_serial') assert app.is_parallel_allowed('read') is False - assert "the read_serial extension is not safe for parallel reading" in app.warning.getvalue() + assert ( + 'the read_serial extension is not safe for parallel reading' + ) in app.warning.getvalue() app.warning.truncate(0) # reset warnings assert app.is_parallel_allowed('write') is True assert app.warning.getvalue() == '' @@ -137,8 +143,10 @@ def test_add_is_parallel_allowed(app): app.setup_extension('write_serial') assert app.is_parallel_allowed('read') is False assert app.is_parallel_allowed('write') is False - assert ("the write_serial extension does not declare if it is safe " - "for parallel reading, assuming it isn't - please ") in app.warning.getvalue() + assert ( + 'the write_serial extension does not declare if it is safe ' + "for parallel reading, assuming it isn't - please " + ) in app.warning.getvalue() app.extensions.pop('write_serial') app.warning.truncate(0) # reset warnings @@ -146,17 +154,21 @@ def test_add_is_parallel_allowed(app): @pytest.mark.sphinx('dummy', testroot='root') def test_build_specific(app): app.builder.build = Mock() - filenames = [app.srcdir / 'index.txt', # normal - app.srcdir / 'images', # without suffix - app.srcdir / 'notfound.txt', # not found - app.srcdir / 'img.png', # unknown suffix - '/index.txt', # external file - app.srcdir / 'subdir', # directory - app.srcdir / 'subdir/includes.txt', # file on subdir - app.srcdir / 'subdir/../subdir/excluded.txt'] # not normalized + filenames = [ + app.srcdir / 'index.txt', # normal + app.srcdir / 'images', # without suffix + app.srcdir / 'notfound.txt', # not found + app.srcdir / 'img.png', # unknown suffix + '/index.txt', # external file + app.srcdir / 'subdir', # directory + app.srcdir / 'subdir/includes.txt', # file on subdir + app.srcdir / 'subdir/../subdir/excluded.txt', # not normalized + ] # fmt: skip app.build(False, filenames) expected = ['index', 'subdir/includes', 'subdir/excluded'] - app.builder.build.assert_called_with(expected, - method='specific', - summary='3 source files given on command line') + app.builder.build.assert_called_with( + expected, + method='specific', + summary='3 source files given on command line', + ) diff --git a/tests/test_builders/test_build.py b/tests/test_builders/test_build.py index 009eb97e86d..5ccef02c943 100644 --- a/tests/test_builders/test_build.py +++ b/tests/test_builders/test_build.py @@ -31,35 +31,44 @@ def nonascii_srcdir(request, rootdir, sphinx_test_tempdir): shutil.copytree(rootdir / 'test-root', srcdir) # add a doc with a non-ASCII file name to the source dir - (srcdir / (test_name + '.txt')).write_text(""" + (srcdir / (test_name + '.txt')).write_text( + """ nonascii file name page ======================= -""", encoding='utf8') +""", + encoding='utf8', + ) root_doc = srcdir / 'index.txt' - root_doc.write_text(root_doc.read_text(encoding='utf8') + f""" + root_doc.write_text( + root_doc.read_text(encoding='utf8') + + f""" .. toctree:: {test_name}/{test_name} -""", encoding='utf8') +""", + encoding='utf8', + ) return srcdir # note: this test skips building docs for some builders because they have independent testcase. # (html, changes, epub, latex, texinfo and manpage) @pytest.mark.parametrize( - "buildername", + 'buildername', ['dirhtml', 'singlehtml', 'text', 'xml', 'pseudoxml', 'linkcheck'], ) -@mock.patch('sphinx.builders.linkcheck.requests.head', - side_effect=request_session_head) +@mock.patch( + 'sphinx.builders.linkcheck.requests.head', + side_effect=request_session_head, +) def test_build_all(requests_head, make_app, nonascii_srcdir, buildername): app = make_app(buildername, srcdir=nonascii_srcdir) app.build() def test_root_doc_not_found(tmp_path, make_app): - (tmp_path / 'conf.py').write_text('', encoding='utf8') + (tmp_path / 'conf.py').touch() assert os.listdir(tmp_path) == ['conf.py'] app = make_app('dummy', srcdir=tmp_path) @@ -67,31 +76,31 @@ def test_root_doc_not_found(tmp_path, make_app): app.build(force_all=True) # no index.rst -@pytest.mark.sphinx(buildername='text', testroot='circular') +@pytest.mark.sphinx('text', testroot='circular') def test_circular_toctree(app): app.build(force_all=True) warnings = app.warning.getvalue() assert ( - 'circular toctree references detected, ignoring: ' - 'sub <- index <- sub') in warnings + 'circular toctree references detected, ignoring: sub <- index <- sub' + ) in warnings assert ( - 'circular toctree references detected, ignoring: ' - 'index <- sub <- index') in warnings + 'circular toctree references detected, ignoring: index <- sub <- index' + ) in warnings -@pytest.mark.sphinx(buildername='text', testroot='numbered-circular') +@pytest.mark.sphinx('text', testroot='numbered-circular') def test_numbered_circular_toctree(app): app.build(force_all=True) warnings = app.warning.getvalue() assert ( - 'circular toctree references detected, ignoring: ' - 'sub <- index <- sub') in warnings + 'circular toctree references detected, ignoring: sub <- index <- sub' + ) in warnings assert ( - 'circular toctree references detected, ignoring: ' - 'index <- sub <- index') in warnings + 'circular toctree references detected, ignoring: index <- sub <- index' + ) in warnings -@pytest.mark.sphinx(buildername='dummy', testroot='images') +@pytest.mark.sphinx('dummy', testroot='images') def test_image_glob(app): app.build(force_all=True) @@ -108,16 +117,20 @@ def test_image_glob(app): assert doctree[0][2][0]['uri'] == 'rimg.png' assert isinstance(doctree[0][3], nodes.image) - assert doctree[0][3]['candidates'] == {'application/pdf': 'img.pdf', - 'image/gif': 'img.gif', - 'image/png': 'img.png'} + assert doctree[0][3]['candidates'] == { + 'application/pdf': 'img.pdf', + 'image/gif': 'img.gif', + 'image/png': 'img.png', + } assert doctree[0][3]['uri'] == 'img.*' assert isinstance(doctree[0][4], nodes.figure) assert isinstance(doctree[0][4][0], nodes.image) - assert doctree[0][4][0]['candidates'] == {'application/pdf': 'img.pdf', - 'image/gif': 'img.gif', - 'image/png': 'img.png'} + assert doctree[0][4][0]['candidates'] == { + 'application/pdf': 'img.pdf', + 'image/gif': 'img.gif', + 'image/png': 'img.png', + } assert doctree[0][4][0]['uri'] == 'img.*' # subdir/index.rst @@ -128,14 +141,18 @@ def test_image_glob(app): assert doctree[0][1]['uri'] == 'subdir/rimg.png' assert isinstance(doctree[0][2], nodes.image) - assert doctree[0][2]['candidates'] == {'application/pdf': 'subdir/svgimg.pdf', - 'image/svg+xml': 'subdir/svgimg.svg'} + assert doctree[0][2]['candidates'] == { + 'application/pdf': 'subdir/svgimg.pdf', + 'image/svg+xml': 'subdir/svgimg.svg', + } assert doctree[0][2]['uri'] == 'subdir/svgimg.*' assert isinstance(doctree[0][3], nodes.figure) assert isinstance(doctree[0][3][0], nodes.image) - assert doctree[0][3][0]['candidates'] == {'application/pdf': 'subdir/svgimg.pdf', - 'image/svg+xml': 'subdir/svgimg.svg'} + assert doctree[0][3][0]['candidates'] == { + 'application/pdf': 'subdir/svgimg.pdf', + 'image/svg+xml': 'subdir/svgimg.svg', + } assert doctree[0][3][0]['uri'] == 'subdir/svgimg.*' diff --git a/tests/test_builders/test_build_changes.py b/tests/test_builders/test_build_changes.py index 6d070d76855..69db01ba3e5 100644 --- a/tests/test_builders/test_build_changes.py +++ b/tests/test_builders/test_build_changes.py @@ -15,18 +15,23 @@ def test_build(app): path_html = ( 'Path: deprecated: Deprecated since version 0.6:' - ' So, that was a bad idea it turns out.') + ' So, that was a bad idea it turns out.' + ) assert path_html in htmltext malloc_html = ( 'void *Test_Malloc(size_t n): changed: Changed in version 0.6:' - ' Can now be replaced with a different allocator.') + ' Can now be replaced with a different allocator.' + ) assert malloc_html in htmltext @pytest.mark.sphinx( - 'changes', testroot='changes', srcdir='changes-none', - confoverrides={'version': '0.7', 'release': '0.7b1'}) + 'changes', + testroot='changes', + srcdir='changes-none', + confoverrides={'version': '0.7', 'release': '0.7b1'}, +) def test_no_changes(app): app.build() diff --git a/tests/test_builders/test_build_dirhtml.py b/tests/test_builders/test_build_dirhtml.py index e80d2a384e5..3e78f1ed0c8 100644 --- a/tests/test_builders/test_build_dirhtml.py +++ b/tests/test_builders/test_build_dirhtml.py @@ -7,7 +7,7 @@ from sphinx.util.inventory import InventoryFile -@pytest.mark.sphinx(buildername='dirhtml', testroot='builder-dirhtml') +@pytest.mark.sphinx('dirhtml', testroot='builder-dirhtml') def test_dirhtml(app): app.build() @@ -31,10 +31,25 @@ def test_dirhtml(app): assert invdata['std:doc']['index'] == ('Project name not set', '', 'path/to/', '-') assert 'foo/index' in invdata.get('std:doc', {}) - assert invdata['std:doc']['foo/index'] == ('Project name not set', '', 'path/to/foo/', '-') + assert invdata['std:doc']['foo/index'] == ( + 'Project name not set', + '', + 'path/to/foo/', + '-', + ) assert 'index' in invdata.get('std:label', {}) - assert invdata['std:label']['index'] == ('Project name not set', '', 'path/to/#index', '-') + assert invdata['std:label']['index'] == ( + 'Project name not set', + '', + 'path/to/#index', + '-', + ) assert 'foo' in invdata.get('std:label', {}) - assert invdata['std:label']['foo'] == ('Project name not set', '', 'path/to/foo/#foo', 'foo/index') + assert invdata['std:label']['foo'] == ( + 'Project name not set', + '', + 'path/to/foo/#foo', + 'foo/index', + ) diff --git a/tests/test_builders/test_build_epub.py b/tests/test_builders/test_build_epub.py index c0f653c2b48..263fefe8090 100644 --- a/tests/test_builders/test_build_epub.py +++ b/tests/test_builders/test_build_epub.py @@ -62,69 +62,87 @@ def __iter__(self): @pytest.mark.sphinx('epub', testroot='basic') def test_build_epub(app): app.build(force_all=True) - assert (app.outdir / 'mimetype').read_text(encoding='utf8') == 'application/epub+zip' + assert (app.outdir / 'mimetype').read_text( + encoding='utf8' + ) == 'application/epub+zip' assert (app.outdir / 'META-INF' / 'container.xml').exists() # toc.ncx - toc = EPUBElementTree.fromstring((app.outdir / 'toc.ncx').read_text(encoding='utf8')) - assert toc.find("./ncx:docTitle/ncx:text").text == 'Project name not set' + toc = EPUBElementTree.fromstring( + (app.outdir / 'toc.ncx').read_text(encoding='utf8') + ) + assert toc.find('./ncx:docTitle/ncx:text').text == 'Project name not set' # toc.ncx / head - meta = list(toc.find("./ncx:head")) + meta = list(toc.find('./ncx:head')) assert meta[0].attrib == {'name': 'dtb:uid', 'content': 'unknown'} assert meta[1].attrib == {'name': 'dtb:depth', 'content': '1'} assert meta[2].attrib == {'name': 'dtb:totalPageCount', 'content': '0'} assert meta[3].attrib == {'name': 'dtb:maxPageNumber', 'content': '0'} # toc.ncx / navMap - navpoints = toc.findall("./ncx:navMap/ncx:navPoint") + navpoints = toc.findall('./ncx:navMap/ncx:navPoint') assert len(navpoints) == 1 assert navpoints[0].attrib == {'id': 'navPoint1', 'playOrder': '1'} - assert navpoints[0].find("./ncx:content").attrib == {'src': 'index.xhtml'} + assert navpoints[0].find('./ncx:content').attrib == {'src': 'index.xhtml'} - navlabel = navpoints[0].find("./ncx:navLabel/ncx:text") + navlabel = navpoints[0].find('./ncx:navLabel/ncx:text') assert navlabel.text == 'The basic Sphinx documentation for testing' # content.opf - opf = EPUBElementTree.fromstring((app.outdir / 'content.opf').read_text(encoding='utf8')) + opf = EPUBElementTree.fromstring( + (app.outdir / 'content.opf').read_text(encoding='utf8') + ) # content.opf / metadata - metadata = opf.find("./idpf:metadata") - assert metadata.find("./dc:language").text == 'en' - assert metadata.find("./dc:title").text == 'Project name not set' - assert metadata.find("./dc:description").text == 'unknown' - assert metadata.find("./dc:creator").text == 'Author name not set' - assert metadata.find("./dc:contributor").text == 'unknown' - assert metadata.find("./dc:publisher").text == 'Author name not set' - assert metadata.find("./dc:rights").text is None + metadata = opf.find('./idpf:metadata') + assert metadata.find('./dc:language').text == 'en' + assert metadata.find('./dc:title').text == 'Project name not set' + assert metadata.find('./dc:description').text == 'unknown' + assert metadata.find('./dc:creator').text == 'Author name not set' + assert metadata.find('./dc:contributor').text == 'unknown' + assert metadata.find('./dc:publisher').text == 'Author name not set' + assert metadata.find('./dc:rights').text is None assert metadata.find("./idpf:meta[@property='ibooks:version']").text is None - assert metadata.find("./idpf:meta[@property='ibooks:specified-fonts']").text == 'true' + assert ( + metadata.find("./idpf:meta[@property='ibooks:specified-fonts']").text == 'true' + ) assert metadata.find("./idpf:meta[@property='ibooks:binding']").text == 'true' - assert metadata.find("./idpf:meta[@property='ibooks:scroll-axis']").text == 'vertical' + assert ( + metadata.find("./idpf:meta[@property='ibooks:scroll-axis']").text == 'vertical' + ) # content.opf / manifest - manifest = opf.find("./idpf:manifest") + manifest = opf.find('./idpf:manifest') items = list(manifest) - assert items[0].attrib == {'id': 'ncx', - 'href': 'toc.ncx', - 'media-type': 'application/x-dtbncx+xml'} - assert items[1].attrib == {'id': 'nav', - 'href': 'nav.xhtml', - 'media-type': 'application/xhtml+xml', - 'properties': 'nav'} - assert items[2].attrib == {'id': 'epub-0', - 'href': 'genindex.xhtml', - 'media-type': 'application/xhtml+xml'} - assert items[3].attrib == {'id': 'epub-1', - 'href': 'index.xhtml', - 'media-type': 'application/xhtml+xml'} + assert items[0].attrib == { + 'id': 'ncx', + 'href': 'toc.ncx', + 'media-type': 'application/x-dtbncx+xml', + } + assert items[1].attrib == { + 'id': 'nav', + 'href': 'nav.xhtml', + 'media-type': 'application/xhtml+xml', + 'properties': 'nav', + } + assert items[2].attrib == { + 'id': 'epub-0', + 'href': 'genindex.xhtml', + 'media-type': 'application/xhtml+xml', + } + assert items[3].attrib == { + 'id': 'epub-1', + 'href': 'index.xhtml', + 'media-type': 'application/xhtml+xml', + } for i, item in enumerate(items[2:]): # items are named as epub-NN assert item.get('id') == 'epub-%d' % i # content.opf / spine - spine = opf.find("./idpf:spine") + spine = opf.find('./idpf:spine') itemrefs = list(spine) assert spine.get('toc') == 'ncx' assert spine.get('page-progression-direction') == 'ltr' @@ -132,34 +150,45 @@ def test_build_epub(app): assert itemrefs[1].get('idref') == 'epub-0' # content.opf / guide - reference = opf.find("./idpf:guide/idpf:reference") + reference = opf.find('./idpf:guide/idpf:reference') assert reference.get('type') == 'toc' assert reference.get('title') == 'Table of Contents' assert reference.get('href') == 'index.xhtml' # nav.xhtml - nav = EPUBElementTree.fromstring((app.outdir / 'nav.xhtml').read_text(encoding='utf8')) - assert nav.attrib == {'lang': 'en', - '{http://www.w3.org/XML/1998/namespace}lang': 'en'} - assert nav.find("./xhtml:head/xhtml:title").text == 'Table of Contents' + nav = EPUBElementTree.fromstring( + (app.outdir / 'nav.xhtml').read_text(encoding='utf8') + ) + assert nav.attrib == { + 'lang': 'en', + '{http://www.w3.org/XML/1998/namespace}lang': 'en', + } + assert nav.find('./xhtml:head/xhtml:title').text == 'Table of Contents' # nav.xhtml / nav - navlist = nav.find("./xhtml:body/xhtml:nav") - toc = navlist.findall("./xhtml:ol/xhtml:li") - assert navlist.find("./xhtml:h1").text == 'Table of Contents' + navlist = nav.find('./xhtml:body/xhtml:nav') + toc = navlist.findall('./xhtml:ol/xhtml:li') + assert navlist.find('./xhtml:h1').text == 'Table of Contents' assert len(toc) == 1 - assert toc[0].find("./xhtml:a").get("href") == 'index.xhtml' - assert toc[0].find("./xhtml:a").text == 'The basic Sphinx documentation for testing' + assert toc[0].find('./xhtml:a').get('href') == 'index.xhtml' + assert toc[0].find('./xhtml:a').text == 'The basic Sphinx documentation for testing' -@pytest.mark.sphinx('epub', testroot='footnotes', - confoverrides={'epub_cover': ('_images/rimg.png', None)}) +@pytest.mark.sphinx( + 'epub', + testroot='footnotes', + confoverrides={'epub_cover': ('_images/rimg.png', None)}, +) def test_epub_cover(app): app.build() # content.opf / metadata - opf = EPUBElementTree.fromstring((app.outdir / 'content.opf').read_text(encoding='utf8')) - cover_image = opf.find("./idpf:manifest/idpf:item[@href='%s']" % app.config.epub_cover[0]) + opf = EPUBElementTree.fromstring( + (app.outdir / 'content.opf').read_text(encoding='utf8') + ) + cover_image = opf.find( + "./idpf:manifest/idpf:item[@href='%s']" % app.config.epub_cover[0] + ) cover = opf.find("./idpf:metadata/idpf:meta[@name='cover']") assert cover assert cover.get('content') == cover_image.get('id') @@ -171,24 +200,27 @@ def test_nested_toc(app): # toc.ncx toc = EPUBElementTree.fromstring((app.outdir / 'toc.ncx').read_bytes()) - assert toc.find("./ncx:docTitle/ncx:text").text == 'Project name not set' + assert toc.find('./ncx:docTitle/ncx:text').text == 'Project name not set' # toc.ncx / navPoint def navinfo(elem: EPUBElementTree): - label = elem.find("./ncx:navLabel/ncx:text") - content = elem.find("./ncx:content") - return (elem.get('id'), elem.get('playOrder'), - content.get('src'), label.text) + label = elem.find('./ncx:navLabel/ncx:text') + content = elem.find('./ncx:content') + return elem.get('id'), elem.get('playOrder'), content.get('src'), label.text - navpoints = toc.findall("./ncx:navMap/ncx:navPoint") + navpoints = toc.findall('./ncx:navMap/ncx:navPoint') assert len(navpoints) == 4 - assert navinfo(navpoints[0]) == ('navPoint1', '1', 'index.xhtml', - "Welcome to Sphinx Tests’s documentation!") - assert navpoints[0].findall("./ncx:navPoint") == [] + assert navinfo(navpoints[0]) == ( + 'navPoint1', + '1', + 'index.xhtml', + 'Welcome to Sphinx Tests’s documentation!', + ) + assert navpoints[0].findall('./ncx:navPoint') == [] # toc.ncx / nested navPoints assert navinfo(navpoints[1]) == ('navPoint2', '2', 'foo.xhtml', 'foo') - navchildren = navpoints[1].findall("./ncx:navPoint") + navchildren = navpoints[1].findall('./ncx:navPoint') assert len(navchildren) == 4 assert navinfo(navchildren[0]) == ('navPoint3', '2', 'foo.xhtml', 'foo') assert navinfo(navchildren[1]) == ('navPoint4', '3', 'quux.xhtml', 'quux') @@ -197,25 +229,27 @@ def navinfo(elem: EPUBElementTree): # nav.xhtml / nav def navinfo(elem): - anchor = elem.find("./xhtml:a") - return (anchor.get('href'), anchor.text) + anchor = elem.find('./xhtml:a') + return anchor.get('href'), anchor.text nav = EPUBElementTree.fromstring((app.outdir / 'nav.xhtml').read_bytes()) - toc = nav.findall("./xhtml:body/xhtml:nav/xhtml:ol/xhtml:li") + toc = nav.findall('./xhtml:body/xhtml:nav/xhtml:ol/xhtml:li') assert len(toc) == 4 - assert navinfo(toc[0]) == ('index.xhtml', - "Welcome to Sphinx Tests’s documentation!") - assert toc[0].findall("./xhtml:ol") == [] + assert navinfo(toc[0]) == ( + 'index.xhtml', + 'Welcome to Sphinx Tests’s documentation!', + ) + assert toc[0].findall('./xhtml:ol') == [] # nav.xhtml / nested toc assert navinfo(toc[1]) == ('foo.xhtml', 'foo') - tocchildren = toc[1].findall("./xhtml:ol/xhtml:li") + tocchildren = toc[1].findall('./xhtml:ol/xhtml:li') assert len(tocchildren) == 3 assert navinfo(tocchildren[0]) == ('quux.xhtml', 'quux') assert navinfo(tocchildren[1]) == ('foo.xhtml#foo-1', 'foo.1') assert navinfo(tocchildren[2]) == ('foo.xhtml#foo-2', 'foo.2') - grandchild = tocchildren[1].findall("./xhtml:ol/xhtml:li") + grandchild = tocchildren[1].findall('./xhtml:ol/xhtml:li') assert len(grandchild) == 1 assert navinfo(grandchild[0]) == ('foo.xhtml#foo-1-1', 'foo.1-1') @@ -226,24 +260,27 @@ def test_escaped_toc(app): # toc.ncx toc = EPUBElementTree.fromstring((app.outdir / 'toc.ncx').read_bytes()) - assert toc.find("./ncx:docTitle/ncx:text").text == 'need "escaped" project' + assert toc.find('./ncx:docTitle/ncx:text').text == 'need "escaped" project' # toc.ncx / navPoint def navinfo(elem): - label = elem.find("./ncx:navLabel/ncx:text") - content = elem.find("./ncx:content") - return (elem.get('id'), elem.get('playOrder'), - content.get('src'), label.text) + label = elem.find('./ncx:navLabel/ncx:text') + content = elem.find('./ncx:content') + return elem.get('id'), elem.get('playOrder'), content.get('src'), label.text - navpoints = toc.findall("./ncx:navMap/ncx:navPoint") + navpoints = toc.findall('./ncx:navMap/ncx:navPoint') assert len(navpoints) == 4 - assert navinfo(navpoints[0]) == ('navPoint1', '1', 'index.xhtml', - "Welcome to Sphinx Tests's documentation!") - assert navpoints[0].findall("./ncx:navPoint") == [] + assert navinfo(navpoints[0]) == ( + 'navPoint1', + '1', + 'index.xhtml', + "Welcome to Sphinx Tests's documentation!", + ) + assert navpoints[0].findall('./ncx:navPoint') == [] # toc.ncx / nested navPoints assert navinfo(navpoints[1]) == ('navPoint2', '2', 'foo.xhtml', '') - navchildren = navpoints[1].findall("./ncx:navPoint") + navchildren = navpoints[1].findall('./ncx:navPoint') assert len(navchildren) == 4 assert navinfo(navchildren[0]) == ('navPoint3', '2', 'foo.xhtml', '') assert navinfo(navchildren[1]) == ('navPoint4', '3', 'quux.xhtml', 'quux') @@ -252,25 +289,27 @@ def navinfo(elem): # nav.xhtml / nav def navinfo(elem): - anchor = elem.find("./xhtml:a") - return (anchor.get('href'), anchor.text) + anchor = elem.find('./xhtml:a') + return anchor.get('href'), anchor.text nav = EPUBElementTree.fromstring((app.outdir / 'nav.xhtml').read_bytes()) - toc = nav.findall("./xhtml:body/xhtml:nav/xhtml:ol/xhtml:li") + toc = nav.findall('./xhtml:body/xhtml:nav/xhtml:ol/xhtml:li') assert len(toc) == 4 - assert navinfo(toc[0]) == ('index.xhtml', - "Welcome to Sphinx Tests's documentation!") - assert toc[0].findall("./xhtml:ol") == [] + assert navinfo(toc[0]) == ( + 'index.xhtml', + "Welcome to Sphinx Tests's documentation!", + ) + assert toc[0].findall('./xhtml:ol') == [] # nav.xhtml / nested toc assert navinfo(toc[1]) == ('foo.xhtml', '') - tocchildren = toc[1].findall("./xhtml:ol/xhtml:li") + tocchildren = toc[1].findall('./xhtml:ol/xhtml:li') assert len(tocchildren) == 3 assert navinfo(tocchildren[0]) == ('quux.xhtml', 'quux') assert navinfo(tocchildren[1]) == ('foo.xhtml#foo-1', 'foo “1”') assert navinfo(tocchildren[2]) == ('foo.xhtml#foo-2', 'foo.2') - grandchild = tocchildren[1].findall("./xhtml:ol/xhtml:li") + grandchild = tocchildren[1].findall('./xhtml:ol/xhtml:li') assert len(grandchild) == 1 assert navinfo(grandchild[0]) == ('foo.xhtml#foo-1-1', 'foo.1-1') @@ -281,12 +320,16 @@ def test_epub_writing_mode(app): app.build(force_all=True) # horizontal / page-progression-direction - opf = EPUBElementTree.fromstring((app.outdir / 'content.opf').read_text(encoding='utf8')) - assert opf.find("./idpf:spine").get('page-progression-direction') == 'ltr' + opf = EPUBElementTree.fromstring( + (app.outdir / 'content.opf').read_text(encoding='utf8') + ) + assert opf.find('./idpf:spine').get('page-progression-direction') == 'ltr' # horizontal / ibooks:scroll-axis - metadata = opf.find("./idpf:metadata") - assert metadata.find("./idpf:meta[@property='ibooks:scroll-axis']").text == 'vertical' + metadata = opf.find('./idpf:metadata') + assert ( + metadata.find("./idpf:meta[@property='ibooks:scroll-axis']").text == 'vertical' + ) # horizontal / writing-mode (CSS) css = (app.outdir / '_static' / 'epub.css').read_text(encoding='utf8') @@ -298,12 +341,17 @@ def test_epub_writing_mode(app): app.build() # vertical / page-progression-direction - opf = EPUBElementTree.fromstring((app.outdir / 'content.opf').read_text(encoding='utf8')) - assert opf.find("./idpf:spine").get('page-progression-direction') == 'rtl' + opf = EPUBElementTree.fromstring( + (app.outdir / 'content.opf').read_text(encoding='utf8') + ) + assert opf.find('./idpf:spine').get('page-progression-direction') == 'rtl' # vertical / ibooks:scroll-axis - metadata = opf.find("./idpf:metadata") - assert metadata.find("./idpf:meta[@property='ibooks:scroll-axis']").text == 'horizontal' + metadata = opf.find('./idpf:metadata') + assert ( + metadata.find("./idpf:meta[@property='ibooks:scroll-axis']").text + == 'horizontal' + ) # vertical / writing-mode (CSS) css = (app.outdir / '_static' / 'epub.css').read_text(encoding='utf8') @@ -315,11 +363,13 @@ def test_epub_anchor_id(app): app.build() html = (app.outdir / 'index.xhtml').read_text(encoding='utf8') - assert ('

' - 'blah blah blah

' in html) - assert ('' - '

blah blah blah

' in html) - assert 'see ' in html + assert '

blah blah blah

' in html + assert ( + '

blah blah blah

' + ) in html + assert ( + 'see
' + ) in html @pytest.mark.sphinx('epub', testroot='html_assets') @@ -328,26 +378,37 @@ def test_epub_assets(app): # epub_sytlesheets (same as html_css_files) content = (app.outdir / 'index.xhtml').read_text(encoding='utf8') - assert ('' - in content) - assert ('' in content) - - -@pytest.mark.sphinx('epub', testroot='html_assets', - confoverrides={'epub_css_files': ['css/epub.css']}) + assert ( + '' + ) in content + assert ( + '' + ) in content + + +@pytest.mark.sphinx( + 'epub', + testroot='html_assets', + confoverrides={'epub_css_files': ['css/epub.css']}, +) def test_epub_css_files(app): app.build(force_all=True) # epub_css_files content = (app.outdir / 'index.xhtml').read_text(encoding='utf8') - assert '' in content + assert ( + '' + ) in content # files in html_css_files are not outputted - assert ('' - not in content) - assert ('' not in content) + assert ( + '' + ) not in content + assert ( + '' + ) not in content @pytest.mark.sphinx('epub', testroot='roles-download') @@ -356,14 +417,20 @@ def test_html_download_role(app): assert not (app.outdir / '_downloads' / 'dummy.dat').exists() content = (app.outdir / 'index.xhtml').read_text(encoding='utf8') - assert ('
  • ' - 'dummy.dat

  • ' in content) - assert ('
  • ' - 'not_found.dat

  • ' in content) - assert ('
  • ' - 'Sphinx logo' - ' [https://www.sphinx-doc.org/en/master' - '/_static/sphinx-logo.svg]

  • ' in content) + assert ( + '
  • ' + 'dummy.dat

  • ' + ) in content + assert ( + '
  • ' + 'not_found.dat

  • ' + ) in content + assert ( + '
  • ' + 'Sphinx logo' + ' [https://www.sphinx-doc.org/en/master' + '/_static/sphinx-logo.svg]

  • ' + ) in content @pytest.mark.sphinx('epub', testroot='toctree-duplicated') @@ -372,22 +439,27 @@ def test_duplicated_toctree_entry(app): assert 'WARNING: duplicated ToC entry found: foo.xhtml' in app.warning.getvalue() -@pytest.mark.skipif('DO_EPUBCHECK' not in os.environ, - reason='Skipped because DO_EPUBCHECK is not set') +@pytest.mark.skipif( + 'DO_EPUBCHECK' not in os.environ, + reason='Skipped because DO_EPUBCHECK is not set', +) @pytest.mark.sphinx('epub') def test_run_epubcheck(app): app.build() if not runnable(['java', '-version']): - pytest.skip("Unable to run Java; skipping test") + pytest.skip('Unable to run Java; skipping test') epubcheck = os.environ.get('EPUBCHECK_PATH', '/usr/share/java/epubcheck.jar') if not os.path.exists(epubcheck): - pytest.skip("Could not find epubcheck; skipping test") + pytest.skip('Could not find epubcheck; skipping test') try: - subprocess.run(['java', '-jar', epubcheck, app.outdir / 'SphinxTests.epub'], - capture_output=True, check=True) + subprocess.run( + ['java', '-jar', epubcheck, app.outdir / 'SphinxTests.epub'], + capture_output=True, + check=True, + ) except CalledProcessError as exc: print(exc.stdout.decode('utf-8')) print(exc.stderr.decode('utf-8')) diff --git a/tests/test_builders/test_build_gettext.py b/tests/test_builders/test_build_gettext.py index dc8f4c9dcbd..905875fe1ef 100644 --- a/tests/test_builders/test_build_gettext.py +++ b/tests/test_builders/test_build_gettext.py @@ -36,14 +36,19 @@ def test_Catalog_duplicated_message(): msg1, msg2 = list(catalog) assert msg1.text == 'hello' - assert msg1.locations == [('/path/to/filename', 1), - ('/path/to/filename', 2), - ('/path/to/yetanother', 1)] + assert msg1.locations == [ + ('/path/to/filename', 1), + ('/path/to/filename', 2), + ('/path/to/yetanother', 1), + ] assert msg2.text == 'world' assert msg2.locations == [('/path/to/filename', 1)] -@pytest.mark.sphinx('gettext', srcdir='root-gettext') +@pytest.mark.sphinx( + 'gettext', + srcdir='root-gettext', +) def test_build_gettext(app): # Generic build; should fail only when the builder is horribly broken. app.build(force_all=True) @@ -59,14 +64,24 @@ def test_build_gettext(app): assert 'msgid "something, something else, something more"' in catalog -@pytest.mark.sphinx('gettext', srcdir='root-gettext') +@pytest.mark.sphinx( + 'gettext', + srcdir='root-gettext', +) def test_msgfmt(app): app.build(force_all=True) (app.outdir / 'en' / 'LC_MESSAGES').mkdir(parents=True, exist_ok=True) with chdir(app.outdir): try: - args = ['msginit', '--no-translator', '-i', 'markup.pot', '--locale', 'en_US'] + args = [ + 'msginit', + '--no-translator', + '-i', + 'markup.pot', + '--locale', + 'en_US', + ] subprocess.run(args, capture_output=True, check=True) except OSError: pytest.skip() # most likely msginit was not found @@ -78,8 +93,12 @@ def test_msgfmt(app): assert (app.outdir / 'en_US.po').is_file(), 'msginit failed' try: - args = ['msgfmt', 'en_US.po', - '-o', os.path.join('en', 'LC_MESSAGES', 'test_root.mo')] + args = [ + 'msgfmt', + 'en_US.po', + '-o', + os.path.join('en', 'LC_MESSAGES', 'test_root.mo'), + ] subprocess.run(args, capture_output=True, check=True) except OSError: pytest.skip() # most likely msgfmt was not found @@ -93,12 +112,15 @@ def test_msgfmt(app): assert mo.is_file(), 'msgfmt failed' _ = gettext.translation('test_root', app.outdir, languages=['en']).gettext - assert _("Testing various markup") == "Testing various markup" + assert _('Testing various markup') == 'Testing various markup' @pytest.mark.sphinx( - 'gettext', testroot='intl', srcdir='gettext', - confoverrides={'gettext_compact': False}) + 'gettext', + testroot='intl', + srcdir='gettext', + confoverrides={'gettext_compact': False}, +) def test_gettext_index_entries(app): # regression test for #976 app.build(filenames=[app.srcdir / 'index_entries.txt']) @@ -107,26 +129,28 @@ def test_gettext_index_entries(app): msg_ids = get_msgids(pot) assert msg_ids == [ - "i18n with index entries", - "index target section", - "this is :index:`Newsletter` target paragraph.", - "various index entries", + 'i18n with index entries', + 'index target section', + 'this is :index:`Newsletter` target paragraph.', + 'various index entries', "That's all.", - "Mailing List", - "Newsletter", - "Recipients List", - "First", - "Second", - "Third", - "Entry", - "See", + 'Mailing List', + 'Newsletter', + 'Recipients List', + 'First', + 'Second', + 'Third', + 'Entry', + 'See', ] @pytest.mark.sphinx( - 'gettext', testroot='intl', srcdir='gettext', - confoverrides={'gettext_compact': False, - 'gettext_additional_targets': []}) + 'gettext', + testroot='intl', + srcdir='gettext', + confoverrides={'gettext_compact': False, 'gettext_additional_targets': []}, +) def test_gettext_disable_index_entries(app): # regression test for #976 app.env._pickled_doctree_cache.clear() # clear cache @@ -136,23 +160,27 @@ def test_gettext_disable_index_entries(app): msg_ids = get_msgids(pot) assert msg_ids == [ - "i18n with index entries", - "index target section", - "this is :index:`Newsletter` target paragraph.", - "various index entries", + 'i18n with index entries', + 'index target section', + 'this is :index:`Newsletter` target paragraph.', + 'various index entries', "That's all.", ] -@pytest.mark.sphinx('gettext', testroot='intl', srcdir='gettext') +@pytest.mark.sphinx( + 'gettext', + testroot='intl', + srcdir='gettext', +) def test_gettext_template(app): app.build(force_all=True) assert (app.outdir / 'sphinx.pot').is_file() result = (app.outdir / 'sphinx.pot').read_text(encoding='utf8') - assert "Welcome" in result - assert "Sphinx %(version)s" in result + assert 'Welcome' in result + assert 'Sphinx %(version)s' in result @pytest.mark.sphinx('gettext', testroot='gettext-template') @@ -162,17 +190,31 @@ def test_gettext_template_msgid_order_in_sphinxpot(app): result = (app.outdir / 'sphinx.pot').read_text(encoding='utf8') assert re.search( - ('msgid "Template 1".*' - 'msgid "This is Template 1\\.".*' - 'msgid "Template 2".*' - 'msgid "This is Template 2\\.".*'), + ( + 'msgid "Template 1".*' + 'msgid "This is Template 1\\.".*' + 'msgid "Template 2".*' + 'msgid "This is Template 2\\.".*' + ), result, - flags=re.DOTALL) + flags=re.DOTALL, + ) + + +@pytest.mark.sphinx('gettext', testroot='gettext-custom-output-template') +def test_gettext_custom_output_template(app): + app.build(force_all=True) + assert (app.outdir / 'index.pot').is_file() + + result = (app.outdir / 'index.pot').read_text(encoding='utf8') + assert 'EVEN MORE DESCRIPTIVE TITLE' in result @pytest.mark.sphinx( - 'gettext', srcdir='root-gettext', - confoverrides={'gettext_compact': 'documentation'}) + 'gettext', + srcdir='root-gettext', + confoverrides={'gettext_compact': 'documentation'}, +) def test_build_single_pot(app): app.build(force_all=True) @@ -180,20 +222,23 @@ def test_build_single_pot(app): result = (app.outdir / 'documentation.pot').read_text(encoding='utf8') assert re.search( - ('msgid "Todo".*' - 'msgid "Like footnotes.".*' - 'msgid "The minute.".*' - 'msgid "Generated section".*'), + ( + 'msgid "Todo".*' + 'msgid "Like footnotes.".*' + 'msgid "The minute.".*' + 'msgid "Generated section".*' + ), result, - flags=re.DOTALL) + flags=re.DOTALL, + ) @pytest.mark.sphinx( 'gettext', testroot='intl_substitution_definitions', srcdir='gettext-subst', - confoverrides={'gettext_compact': False, - 'gettext_additional_targets': ['image']}) + confoverrides={'gettext_compact': False, 'gettext_additional_targets': ['image']}, +) def test_gettext_prolog_epilog_substitution(app): app.build(force_all=True) @@ -202,15 +247,15 @@ def test_gettext_prolog_epilog_substitution(app): msg_ids = get_msgids(pot) assert msg_ids == [ - "i18n with prologue and epilogue substitutions", - "This is content that contains |subst_prolog_1|.", - "Substituted image |subst_prolog_2| here.", - "subst_prolog_2", - ".. image:: /img.png", - "This is content that contains |subst_epilog_1|.", - "Substituted image |subst_epilog_2| here.", - "subst_epilog_2", - ".. image:: /i18n.png", + 'i18n with prologue and epilogue substitutions', + 'This is content that contains |subst_prolog_1|.', + 'Substituted image |subst_prolog_2| here.', + 'subst_prolog_2', + '.. image:: /img.png', + 'This is content that contains |subst_epilog_1|.', + 'Substituted image |subst_epilog_2| here.', + 'subst_epilog_2', + '.. image:: /i18n.png', ] @@ -218,26 +263,32 @@ def test_gettext_prolog_epilog_substitution(app): 'gettext', testroot='intl_substitution_definitions', srcdir='gettext-subst', - confoverrides={'gettext_compact': False, - 'gettext_additional_targets': ['image']}) + confoverrides={'gettext_compact': False, 'gettext_additional_targets': ['image']}, +) def test_gettext_prolog_epilog_substitution_excluded(app): # regression test for #9428 app.build(force_all=True) assert (app.outdir / 'prolog_epilog_substitution_excluded.pot').is_file() - pot = (app.outdir / 'prolog_epilog_substitution_excluded.pot').read_text(encoding='utf8') + pot = (app.outdir / 'prolog_epilog_substitution_excluded.pot').read_text( + encoding='utf8' + ) msg_ids = get_msgids(pot) assert msg_ids == [ - "i18n without prologue and epilogue substitutions", - "This is content that does not include prologue and epilogue substitutions.", + 'i18n without prologue and epilogue substitutions', + 'This is content that does not include prologue and epilogue substitutions.', ] @pytest.mark.sphinx( - 'gettext', srcdir='gettext', - confoverrides={'gettext_compact': False, - 'gettext_additional_targets': ['literal-block', 'doctest-block']}) + 'gettext', + srcdir='gettext', + confoverrides={ + 'gettext_compact': False, + 'gettext_additional_targets': ['literal-block', 'doctest-block'], + }, +) def test_gettext_literalblock_additional(app): app.build(force_all=True) diff --git a/tests/test_builders/test_build_html.py b/tests/test_builders/test_build_html.py index 847565e8e48..93e770cca9c 100644 --- a/tests/test_builders/test_build_html.py +++ b/tests/test_builders/test_build_html.py @@ -27,49 +27,103 @@ def test_html_sidebars_error(make_app, tmp_path): with pytest.raises( ConfigError, match="Values in 'html_sidebars' must be a list of strings. " - "At least one pattern has a string value: 'index'. " - r"Change to `html_sidebars = \{'index': \['searchbox.html'\]\}`.", + "At least one pattern has a string value: 'index'. " + r"Change to `html_sidebars = \{'index': \['searchbox.html'\]\}`.", ): make_app( - buildername='html', + 'html', srcdir=tmp_path, confoverrides={'html_sidebars': {'index': 'searchbox.html'}}, ) def test_html4_error(make_app, tmp_path): - (tmp_path / 'conf.py').write_text('', encoding='utf-8') + (tmp_path / 'conf.py').touch() with pytest.raises( ConfigError, match='HTML 4 is no longer supported by Sphinx', ): make_app( - buildername='html', + 'html', srcdir=tmp_path, confoverrides={'html4_writer': True}, ) -@pytest.mark.parametrize(("fname", "path", "check"), [ - ('index.html', ".//div[@class='citation']/span", r'Ref1'), - ('index.html', ".//div[@class='citation']/span", r'Ref_1'), - - ('footnote.html', ".//a[@class='footnote-reference brackets'][@href='#id9'][@id='id1']", r"1"), - ('footnote.html', ".//a[@class='footnote-reference brackets'][@href='#id10'][@id='id2']", r"2"), - ('footnote.html', ".//a[@class='footnote-reference brackets'][@href='#foo'][@id='id3']", r"3"), - ('footnote.html', ".//a[@class='reference internal'][@href='#bar'][@id='id4']/span", r"\[bar\]"), - ('footnote.html', ".//a[@class='reference internal'][@href='#baz-qux'][@id='id5']/span", r"\[baz_qux\]"), - ('footnote.html', ".//a[@class='footnote-reference brackets'][@href='#id11'][@id='id6']", r"4"), - ('footnote.html', ".//a[@class='footnote-reference brackets'][@href='#id12'][@id='id7']", r"5"), - ('footnote.html', ".//aside[@class='footnote brackets']/span/a[@href='#id1']", r"1"), - ('footnote.html', ".//aside[@class='footnote brackets']/span/a[@href='#id2']", r"2"), - ('footnote.html', ".//aside[@class='footnote brackets']/span/a[@href='#id3']", r"3"), - ('footnote.html', ".//div[@class='citation']/span/a[@href='#id4']", r"bar"), - ('footnote.html', ".//div[@class='citation']/span/a[@href='#id5']", r"baz_qux"), - ('footnote.html', ".//aside[@class='footnote brackets']/span/a[@href='#id6']", r"4"), - ('footnote.html', ".//aside[@class='footnote brackets']/span/a[@href='#id7']", r"5"), - ('footnote.html', ".//aside[@class='footnote brackets']/span/a[@href='#id8']", r"6"), -]) +@pytest.mark.parametrize( + ('fname', 'path', 'check'), + [ + ('index.html', ".//div[@class='citation']/span", r'Ref1'), + ('index.html', ".//div[@class='citation']/span", r'Ref_1'), + ( + 'footnote.html', + ".//a[@class='footnote-reference brackets'][@href='#id9'][@id='id1']", + r'1', + ), + ( + 'footnote.html', + ".//a[@class='footnote-reference brackets'][@href='#id10'][@id='id2']", + r'2', + ), + ( + 'footnote.html', + ".//a[@class='footnote-reference brackets'][@href='#foo'][@id='id3']", + r'3', + ), + ( + 'footnote.html', + ".//a[@class='reference internal'][@href='#bar'][@id='id4']/span", + r'\[bar\]', + ), + ( + 'footnote.html', + ".//a[@class='reference internal'][@href='#baz-qux'][@id='id5']/span", + r'\[baz_qux\]', + ), + ( + 'footnote.html', + ".//a[@class='footnote-reference brackets'][@href='#id11'][@id='id6']", + r'4', + ), + ( + 'footnote.html', + ".//a[@class='footnote-reference brackets'][@href='#id12'][@id='id7']", + r'5', + ), + ( + 'footnote.html', + ".//aside[@class='footnote brackets']/span/a[@href='#id1']", + r'1', + ), + ( + 'footnote.html', + ".//aside[@class='footnote brackets']/span/a[@href='#id2']", + r'2', + ), + ( + 'footnote.html', + ".//aside[@class='footnote brackets']/span/a[@href='#id3']", + r'3', + ), + ('footnote.html', ".//div[@class='citation']/span/a[@href='#id4']", r'bar'), + ('footnote.html', ".//div[@class='citation']/span/a[@href='#id5']", r'baz_qux'), + ( + 'footnote.html', + ".//aside[@class='footnote brackets']/span/a[@href='#id6']", + r'4', + ), + ( + 'footnote.html', + ".//aside[@class='footnote brackets']/span/a[@href='#id7']", + r'5', + ), + ( + 'footnote.html', + ".//aside[@class='footnote brackets']/span/a[@href='#id8']", + r'6', + ), + ], +) @pytest.mark.sphinx('html') @pytest.mark.test_params(shared_result='test_build_html_output_docutils18') def test_docutils_output(app, cached_etree_parse, fname, path, check): @@ -77,7 +131,10 @@ def test_docutils_output(app, cached_etree_parse, fname, path, check): check_xpath(cached_etree_parse(app.outdir / fname), fname, path, check) -@pytest.mark.sphinx('html', parallel=2) +@pytest.mark.sphinx( + 'html', + parallel=2, +) def test_html_parallel(app): app.build() @@ -88,44 +145,66 @@ def test_html_translator(app): assert app.builder.docwriter.visitor.depart_with_node == 10 -@pytest.mark.parametrize("expect", [ - (FIGURE_CAPTION + "//span[@class='caption-number']", "Fig. 1", True), - (FIGURE_CAPTION + "//span[@class='caption-number']", "Fig. 2", True), - (FIGURE_CAPTION + "//span[@class='caption-number']", "Fig. 3", True), - (".//div//span[@class='caption-number']", "No.1 ", True), - (".//div//span[@class='caption-number']", "No.2 ", True), - (".//li/p/a/span", 'Fig. 1', True), - (".//li/p/a/span", 'Fig. 2', True), - (".//li/p/a/span", 'Fig. 3', True), - (".//li/p/a/span", 'No.1', True), - (".//li/p/a/span", 'No.2', True), -]) -@pytest.mark.sphinx('html', testroot='add_enumerable_node', - srcdir='test_enumerable_node') +@pytest.mark.parametrize( + 'expect', + [ + (FIGURE_CAPTION + "//span[@class='caption-number']", 'Fig. 1', True), + (FIGURE_CAPTION + "//span[@class='caption-number']", 'Fig. 2', True), + (FIGURE_CAPTION + "//span[@class='caption-number']", 'Fig. 3', True), + (".//div//span[@class='caption-number']", 'No.1 ', True), + (".//div//span[@class='caption-number']", 'No.2 ', True), + ('.//li/p/a/span', 'Fig. 1', True), + ('.//li/p/a/span', 'Fig. 2', True), + ('.//li/p/a/span', 'Fig. 3', True), + ('.//li/p/a/span', 'No.1', True), + ('.//li/p/a/span', 'No.2', True), + ], +) +@pytest.mark.sphinx( + 'html', + testroot='add_enumerable_node', + srcdir='test_enumerable_node', +) def test_enumerable_node(app, cached_etree_parse, expect): app.build() check_xpath(cached_etree_parse(app.outdir / 'index.html'), 'index.html', *expect) -@pytest.mark.sphinx('html', testroot='basic', confoverrides={'html_copy_source': False}) +@pytest.mark.sphinx( + 'html', + testroot='basic', + confoverrides={'html_copy_source': False}, +) def test_html_copy_source(app): app.build(force_all=True) assert not (app.outdir / '_sources' / 'index.rst.txt').exists() -@pytest.mark.sphinx('html', testroot='basic', confoverrides={'html_sourcelink_suffix': '.txt'}) +@pytest.mark.sphinx( + 'html', + testroot='basic', + confoverrides={'html_sourcelink_suffix': '.txt'}, +) def test_html_sourcelink_suffix(app): app.build(force_all=True) assert (app.outdir / '_sources' / 'index.rst.txt').exists() -@pytest.mark.sphinx('html', testroot='basic', confoverrides={'html_sourcelink_suffix': '.rst'}) +@pytest.mark.sphinx( + 'html', + testroot='basic', + confoverrides={'html_sourcelink_suffix': '.rst'}, +) def test_html_sourcelink_suffix_same(app): app.build(force_all=True) assert (app.outdir / '_sources' / 'index.rst').exists() -@pytest.mark.sphinx('html', testroot='basic', confoverrides={'html_sourcelink_suffix': ''}) +@pytest.mark.sphinx( + 'html', + testroot='basic', + confoverrides={'html_sourcelink_suffix': ''}, +) def test_html_sourcelink_suffix_empty(app): app.build(force_all=True) assert (app.outdir / '_sources' / 'index.rst').exists() @@ -148,40 +227,57 @@ def test_html_inventory(app): invdata = InventoryFile.load(f, 'https://www.google.com', posixpath.join) assert set(invdata.keys()) == {'std:label', 'std:doc'} - assert set(invdata['std:label'].keys()) == {'modindex', - 'py-modindex', - 'genindex', - 'search'} - assert invdata['std:label']['modindex'] == ('Project name not set', - '', - 'https://www.google.com/py-modindex.html', - 'Module Index') - assert invdata['std:label']['py-modindex'] == ('Project name not set', - '', - 'https://www.google.com/py-modindex.html', - 'Python Module Index') - assert invdata['std:label']['genindex'] == ('Project name not set', - '', - 'https://www.google.com/genindex.html', - 'Index') - assert invdata['std:label']['search'] == ('Project name not set', - '', - 'https://www.google.com/search.html', - 'Search Page') + assert set(invdata['std:label'].keys()) == { + 'modindex', + 'py-modindex', + 'genindex', + 'search', + } + assert invdata['std:label']['modindex'] == ( + 'Project name not set', + '', + 'https://www.google.com/py-modindex.html', + 'Module Index', + ) + assert invdata['std:label']['py-modindex'] == ( + 'Project name not set', + '', + 'https://www.google.com/py-modindex.html', + 'Python Module Index', + ) + assert invdata['std:label']['genindex'] == ( + 'Project name not set', + '', + 'https://www.google.com/genindex.html', + 'Index', + ) + assert invdata['std:label']['search'] == ( + 'Project name not set', + '', + 'https://www.google.com/search.html', + 'Search Page', + ) assert set(invdata['std:doc'].keys()) == {'index'} - assert invdata['std:doc']['index'] == ('Project name not set', - '', - 'https://www.google.com/index.html', - 'The basic Sphinx documentation for testing') + assert invdata['std:doc']['index'] == ( + 'Project name not set', + '', + 'https://www.google.com/index.html', + 'The basic Sphinx documentation for testing', + ) -@pytest.mark.sphinx('html', testroot='images', confoverrides={'html_sourcelink_suffix': ''}) +@pytest.mark.sphinx( + 'html', + testroot='images', + confoverrides={'html_sourcelink_suffix': ''}, +) def test_html_anchor_for_figure(app): app.build(force_all=True) content = (app.outdir / 'index.html').read_text(encoding='utf8') - assert ('
    \n

    The caption of pic' - '

    \n
    ' - in content) + assert ( + '
    \n

    The caption of pic' + '

    \n
    ' + ) in content @pytest.mark.sphinx('html', testroot='directives-raw') @@ -198,28 +294,56 @@ def test_html_raw_directive(app): assert '

    LaTeX: abc ghi

    ' in result -@pytest.mark.parametrize("expect", [ - (".//link[@href='_static/persistent.css']" - "[@rel='stylesheet']", '', True), - (".//link[@href='_static/default.css']" - "[@rel='stylesheet']" - "[@title='Default']", '', True), - (".//link[@href='_static/alternate1.css']" - "[@rel='alternate stylesheet']" - "[@title='Alternate']", '', True), - (".//link[@href='_static/alternate2.css']" - "[@rel='alternate stylesheet']", '', True), - (".//link[@href='_static/more_persistent.css']" - "[@rel='stylesheet']", '', True), - (".//link[@href='_static/more_default.css']" - "[@rel='stylesheet']" - "[@title='Default']", '', True), - (".//link[@href='_static/more_alternate1.css']" - "[@rel='alternate stylesheet']" - "[@title='Alternate']", '', True), - (".//link[@href='_static/more_alternate2.css']" - "[@rel='alternate stylesheet']", '', True), -]) +@pytest.mark.parametrize( + 'expect', + [ + (".//link[@href='_static/persistent.css'][@rel='stylesheet']", '', True), + ( + ".//link[@href='_static/default.css']" + "[@rel='stylesheet']" + "[@title='Default']", + '', + True, + ), + ( + ".//link[@href='_static/alternate1.css']" + "[@rel='alternate stylesheet']" + "[@title='Alternate']", + '', + True, + ), + ( + ".//link[@href='_static/alternate2.css'][@rel='alternate stylesheet']", + '', + True, + ), + ( + ".//link[@href='_static/more_persistent.css'][@rel='stylesheet']", + '', + True, + ), + ( + ".//link[@href='_static/more_default.css']" + "[@rel='stylesheet']" + "[@title='Default']", + '', + True, + ), + ( + ".//link[@href='_static/more_alternate1.css']" + "[@rel='alternate stylesheet']" + "[@title='Alternate']", + '', + True, + ), + ( + ".//link[@href='_static/more_alternate2.css']" + "[@rel='alternate stylesheet']", + '', + True, + ), + ], +) @pytest.mark.sphinx('html', testroot='stylesheets') def test_alternate_stylesheets(app, cached_etree_parse, expect): app.build() @@ -230,22 +354,29 @@ def test_alternate_stylesheets(app, cached_etree_parse, expect): def test_html_style(app): app.build() result = (app.outdir / 'index.html').read_text(encoding='utf8') - assert '' in result - assert ('' - not in result) + assert ( + '' + ) in result + assert ( + '' + ) not in result @pytest.mark.sphinx( 'html', testroot='basic', # alabaster changed default sidebars in 1.0.0 - confoverrides={'html_sidebars': {'**': [ - 'about.html', - 'navigation.html', - 'relations.html', - 'searchbox.html', - 'donate.html', - ]}}, + confoverrides={ + 'html_sidebars': { + '**': [ + 'about.html', + 'navigation.html', + 'relations.html', + 'searchbox.html', + 'donate.html', + ] + } + }, ) def test_html_sidebar(app): ctx: dict[str, Any] = {} @@ -253,23 +384,26 @@ def test_html_sidebar(app): # default for alabaster app.build(force_all=True) result = (app.outdir / 'index.html').read_text(encoding='utf8') - assert ('