pax_global_header00006660000000000000000000000064147567003260014524gustar00rootroot0000000000000052 comment=8c35af88ac6b9488d5d3298e15ee18757289486b autosuspend-7.2.0/000077500000000000000000000000001475670032600141045ustar00rootroot00000000000000autosuspend-7.2.0/.editorconfig000066400000000000000000000002511475670032600165570ustar00rootroot00000000000000root = true [*] end_of_line = lf insert_final_newline = true charset = utf-8 indent_style = space [*.py] indent_size = 4 max_line_length = 88 [*.rst] indent_size = 3 autosuspend-7.2.0/.github/000077500000000000000000000000001475670032600154445ustar00rootroot00000000000000autosuspend-7.2.0/.github/workflows/000077500000000000000000000000001475670032600175015ustar00rootroot00000000000000autosuspend-7.2.0/.github/workflows/ci.yml000066400000000000000000000112421475670032600206170ustar00rootroot00000000000000name: CI build on: push: branches: - main pull_request: {} schedule: - cron: '0 0 * * 0' jobs: lint-commits: runs-on: ubuntu-latest if: ${{ github.event_name == 'pull_request' }} steps: - name: install cairo run: sudo apt-get update && sudo apt-get install -y libcairo2-dev - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: wagoid/commitlint-github-action@v6 lint-code: runs-on: ubuntu-latest steps: - name: Clone repo uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.13" - name: Install native dependencies run: sudo apt-get update && sudo apt-get -y install libdbus-1-dev libgirepository1.0-dev libcairo2-dev - name: Cache Python packages uses: actions/cache@v4 with: path: ~/.cache/pip key: lint-code-${{ hashFiles('setup.py') }}-${{ hashFiles('tox.ini') }} - name: Install tox run: | python -m pip install --upgrade pip pip install tox - name: Lint with tox run: tox -e check docs: runs-on: ubuntu-latest steps: - name: Clone repo uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.13" - name: Install native dependencies run: sudo apt-get update && sudo apt-get -y install libdbus-1-dev libgirepository1.0-dev plantuml libcairo2-dev - name: Cache Python packages uses: actions/cache@v4 with: path: ~/.cache/pip key: docs-${{ hashFiles('setup.py') }}-${{ hashFiles('tox.ini') }}-${{ hashFiles('requirements-doc.txt') }} - name: Install tox run: | python -m pip install --upgrade pip pip install tox - name: Build Sphinx docs run: tox -e docs test-mindeps: runs-on: ubuntu-latest steps: - name: Clone repo uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.10" - name: Cache Python packages uses: actions/cache@v4 with: path: ~/.cache/pip key: test-mindeps-${{ hashFiles('setup.py') }}-${{ hashFiles('tox.ini') }} - name: Install tox run: | python -m pip install --upgrade pip pip install tox - name: Test execution with minimal dependencies run: tox -e mindeps test: runs-on: ubuntu-latest strategy: max-parallel: 4 matrix: python-version: ["3.10", "3.11", "3.12", "3.13"] steps: - name: Clone repo uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install native dependencies run: sudo apt-get update && sudo apt-get -y install libdbus-1-dev libgirepository1.0-dev libcairo2-dev - name: Cache Python packages uses: actions/cache@v4 with: path: ~/.cache/pip key: test-${{ hashFiles('setup.py') }}-${{ hashFiles('tox.ini') }}-${{ matrix.python-version }} - name: Install Python dependencies run: | python -m pip install --upgrade pip pip install coverage tox tox-gh-actions - name: Test with tox run: | tox coverage xml --rcfile=setup.cfg - name: Publish coverage to codecov.io uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} release: runs-on: ubuntu-latest if: ${{ github.ref == 'refs/heads/main' }} needs: - lint-code - test-mindeps - test - docs steps: - name: 'Generate token' id: generate_token uses: tibdex/github-app-token@v2 with: app_id: ${{ secrets.RELEASE_APP_ID }} private_key: ${{ secrets.RELEASE_PRIVATE_KEY }} - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 token: ${{ steps.generate_token.outputs.token }} - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: 22 - name: Cache Node packages uses: actions/cache@v4 with: path: node_modules key: release-${{ hashFiles('package.json') }}-${{ hashFiles('package-lock.json') }} - name: Install dependencies run: npm ci - name: Release run: npx semantic-release env: GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} autosuspend-7.2.0/.gitignore000066400000000000000000000003221475670032600160710ustar00rootroot00000000000000/.cache /.coverage* *.egg-info /.eggs /build /dist /htmlcov /tags __pycache__ /pytestdebug.log /doc/build/ /env/ /.ropeproject/ /.mypy_cache/ /.pytest_cache/ /.python-version /.tox/ /Session.vim /node_modules/ autosuspend-7.2.0/.readthedocs.yaml000066400000000000000000000003201475670032600173260ustar00rootroot00000000000000--- version: 2 sphinx: configuration: doc/source/conf.py build: os: ubuntu-22.04 tools: python: "3.11" python: install: - requirements: requirements-doc.txt - method: pip path: . autosuspend-7.2.0/.releaserc.json000066400000000000000000000014011475670032600170160ustar00rootroot00000000000000{ "branches": [ "main" ], "plugins": [ [ "@semantic-release/commit-analyzer", { "preset": "conventionalcommits" } ], [ "@semantic-release/release-notes-generator", { "preset": "angular" } ], [ "@semantic-release/changelog", { "changelogFile": "doc/source/generated_changelog.md" } ], [ "@semantic-release/exec", { "prepareCmd": "echo $(echo ${nextRelease.version} | cut -d '.' -f 1-2)'\n${nextRelease.version}' > ./VERSION" } ], [ "@semantic-release/git", { "assets": [ "VERSION", "doc/source/generated_changelog.md" ] } ], "@semantic-release/github" ] } autosuspend-7.2.0/LICENSE.txt000066400000000000000000000355621475670032600157420ustar00rootroot00000000000000GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS autosuspend-7.2.0/MANIFEST.in000066400000000000000000000000201475670032600156320ustar00rootroot00000000000000include VERSION autosuspend-7.2.0/README.md000066400000000000000000000020031475670032600153560ustar00rootroot00000000000000# autosuspend [![Actions Status](https://github.com/languitar/autosuspend/workflows/CI%20build/badge.svg)](https://github.com/languitar/autosuspend/actions) [![codecov](https://codecov.io/gh/languitar/autosuspend/branch/main/graph/badge.svg)](https://codecov.io/gh/languitar/autosuspend) [![Documentation Status](https://readthedocs.org/projects/autosuspend/badge/?version=latest)](http://autosuspend.readthedocs.io/en/latest/?badge=latest) `autosuspend` is a python daemon that suspends a system if certain conditions are met, or not met. This enables a server to sleep in case of inactivity without depending on the X infrastructure usually used by normal desktop environments. Documentation is [available here](https://autosuspend.readthedocs.io). ## Packages [![Packaging status](https://repology.org/badge/vertical-allrepos/autosuspend.svg)](https://repology.org/project/autosuspend/versions) ## License This software is licensed using the [GPL2 license](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html). autosuspend-7.2.0/VERSION000066400000000000000000000000121475670032600151450ustar00rootroot000000000000007.2 7.2.0 autosuspend-7.2.0/commitlint.config.js000066400000000000000000000001011475670032600200550ustar00rootroot00000000000000module.exports = {extends: ['@commitlint/config-conventional']}; autosuspend-7.2.0/cosmic-ray.toml000066400000000000000000000004271475670032600170520ustar00rootroot00000000000000[cosmic-ray] module-path = "src/autosuspend" python-version = "" timeout = 20.0 excluded-modules = [] test-command = "env PYTHONPATH=`pwd`/src pytest -x" [cosmic-ray.execution-engine] name = "local" [cosmic-ray.cloning] method = "copy" commands = [ "pip install .[test]" ] autosuspend-7.2.0/data/000077500000000000000000000000001475670032600150155ustar00rootroot00000000000000autosuspend-7.2.0/data/autosuspend-detect-suspend.service000066400000000000000000000004441475670032600237000ustar00rootroot00000000000000[Unit] Description=Notifies autosuspend about suspension Documentation=https://autosuspend.readthedocs.io/en/latest/systemd_integration.html Before=sleep.target [Service] Type=simple ExecStart=/usr/bin/autosuspend -l /etc/autosuspend-logging.conf presuspend [Install] WantedBy=sleep.target autosuspend-7.2.0/data/autosuspend-logging.conf000066400000000000000000000010331475670032600216570ustar00rootroot00000000000000[loggers] keys=root,autosuspend,checks [handlers] keys=consoleHandler [formatters] keys=simpleFormatter [logger_root] level=INFO handlers=consoleHandler [logger_autosuspend] qualname=autosuspend propagate=0 level=INFO handlers=consoleHandler [logger_checks] qualname=autosuspend.checks propagate=0 level=INFO handlers=consoleHandler [handler_consoleHandler] class=StreamHandler level=DEBUG formatter=simpleFormatter args=(sys.stdout,) [formatter_simpleFormatter] format=%(asctime)s - %(name)s - %(levelname)s - %(message)s datefmt= autosuspend-7.2.0/data/autosuspend.conf000066400000000000000000000034011475670032600202340ustar00rootroot00000000000000## This is an exemplary documentation file that mainly serves as a syntax explanation. ## For a list of available options and checks, please refer to `man autosuspend.conf` or the online documentation. [general] interval = 30 idle_time = 900 suspend_cmd = /usr/bin/systemctl suspend wakeup_cmd = sh -c 'echo 0 > /sys/class/rtc/rtc0/wakealarm && echo {timestamp:.0f} > /sys/class/rtc/rtc0/wakealarm' woke_up_file = /var/run/autosuspend-just-woke-up lock_file = /var/lock/autosuspend.lock lock_timeout = 30 # Can be used to call a command before suspending, either with scheduled wake up or not. # notify_cmd_wakeup = su myuser -c notify-send -a autosuspend 'Suspending the system. Wake up at {iso}' # notify_cmd_no_wakeup = su myuser -c notify-send -a autosuspend 'Suspending the system.' # Basic activity check configuration. # The check class name is derived from the section header (Ping in this case). # Remember to enable desired checks. They are disabled by default. [check.Ping] enabled = true hosts = 192.168.0.7 # This check is disabled. [check.Smb] enabled = false # Example for a custom check name. # This will use the Users check with the custom name RemoteUsers. # Custom names are necessary in case a check class is used multiple times. # Custom names can also be used for clarification. [check.RemoteUsers] class = Users enabled = true name = .* terminal = .* host = [0-9].* # Here the Users activity check is used again with different settings and a different name [check.LocalUsers] class = Users enabled = true name = .* terminal = .* host = localhost # Checks to determine the next scheduled wakeup are prefixed with 'wakeup'. [wakeup.Calendar] enabled = true url = http://example.org/test.ics # Apart from this, wake up checks reuse the same configuration mechanism. autosuspend-7.2.0/data/autosuspend.service000066400000000000000000000005221475670032600207500ustar00rootroot00000000000000[Unit] Description=A daemon to suspend your server in case of inactivity Documentation=https://autosuspend.readthedocs.io/en/latest/systemd_integration.html After=network.target [Service] ExecStart=/usr/bin/autosuspend -l /etc/autosuspend-logging.conf daemon [Install] WantedBy=multi-user.target Also=autosuspend-detect-suspend.service autosuspend-7.2.0/doc/000077500000000000000000000000001475670032600146515ustar00rootroot00000000000000autosuspend-7.2.0/doc/source/000077500000000000000000000000001475670032600161515ustar00rootroot00000000000000autosuspend-7.2.0/doc/source/api.rst000066400000000000000000000004421475670032600174540ustar00rootroot00000000000000Python API documentation ######################## In case custom checks are required, the following classes have to be subclassed. .. autoclass:: autosuspend.checks.Activity :members: :inherited-members: .. autoclass:: autosuspend.checks.Wakeup :members: :inherited-members: autosuspend-7.2.0/doc/source/available_checks.rst000066400000000000000000000365721475670032600221600ustar00rootroot00000000000000.. _available-checks: Available activity checks ######################### The following checks for activity are currently implemented. Each of the is described with its available configuration options and required optional dependencies. .. _check-active-calendar-event: ActiveCalendarEvent ******************* .. program:: check-active-calendar-event Checks an online `iCalendar`_ file for events that are currently running. If so, this indicates activity and prevents suspending the system. Thus, a calendar can be provided with times at which the system should not go to sleep. If this calendar resides on an online service like a groupware it might even be possible to invite the system. Options ======= .. option:: url The URL to query for the iCalendar file .. option:: timeout Timeout for executed requests in seconds. Default: 5. .. option:: username Optional user name to use for authenticating at a server requiring authentication. If used, also a password must be provided. .. option:: password Optional password to use for authenticating at a server requiring authentication. If used, also a user name must be provided. Requirements ============ * `requests`_ * `icalendar `_ * `dateutil`_ * `tzlocal`_ .. _check-active-connection: ActiveConnection **************** .. program:: check-active-connection Checks whether there is currently a client connected to a TCP server at certain ports. Can be used to e.g. block suspending the system in case SSH users are connected or a web server is used by clients. Options ======= .. option:: ports list of comma-separated port numbers Requirements ============ .. _check-external-command: ExternalCommand *************** .. program:: check-external-command Executes an arbitrary command. In case this command returns 0, the system is assumed to be active. The command is executed as is using shell execution. Beware of malicious commands in obtained configuration files. .. seealso:: * :ref:`external-command-activity-scripts` for a collection of user-provided scripts for some common use cases. Options ======= .. option:: command The command to execute including all arguments Requirements ============ .. _check-jsonpath: JsonPath ******** .. program:: check-jsonpath A generic check which queries a configured URL and expects the reply to contain JSON data. The returned JSON document is checked against a configured `JSONPath`_ expression and in case the expression matches, the system is assumed to be active. Options ======= .. option:: url The URL to query for the XML reply. .. option:: jsonpath The `JSONPath`_ query to execute. In case it returns a result, the system is assumed to be active. .. option:: timeout Timeout for executed requests in seconds. Default: 5. .. option:: username Optional user name to use for authenticating at a server requiring authentication. If used, also a password must be provided. .. option:: password Optional password to use for authenticating at a server requiring authentication. If used, also a user name must be provided. Requirements ============ - `requests`_ - `jsonpath-ng`_ .. _check-kodi: Kodi **** .. program:: check-kodi Checks whether an instance of `Kodi`_ is currently playing. Options ======= .. option:: url Base URL of the JSON RPC API of the Kodi instance, default: ``http://localhost:8080/jsonrpc`` .. option:: timeout Request timeout in seconds, default: ``5`` .. option:: username Optional user name to use for authenticating at a server requiring authentication. If used, also a password must be provided. .. option:: password Optional password to use for authenticating at a server requiring authentication. If used, also a user name must be provided. .. option:: suspend_while_paused Also suspend the system when media playback is paused instead of only suspending when playback is stopped. Default: ``false`` Requirements ============ - `requests`_ .. _check-kodi-idle-time: KodiIdleTime ************ .. program:: check-kodi-idle-time Checks whether there has been interaction with the Kodi user interface recently. This prevents suspending the system in case someone is currently browsing collections etc. This check is redundant to :ref:`check-xidletime` on systems using an X server, but might be necessary in case Kodi is used standalone. It does not replace the :ref:`check-kodi` check, as the idle time is not updated when media is playing. Options ======= .. option:: idle_time Marks the system active in case a user interaction has appeared within the this amount of seconds until now. Default: ``120`` .. option:: url Base URL of the JSON RPC API of the Kodi instance, default: ``http://localhost:8080/jsonrpc`` .. option:: timeout Request timeout in seconds, default: ``5`` .. option:: username Optional user name to use for authenticating at a server requiring authentication. If used, also a password must be provided. .. option:: password Optional password to use for authenticating at a server requiring authentication. If used, also a user name must be provided. Requirements ============ - `requests`_ .. _check-last-log-activity: LastLogActivity *************** .. program:: check-last-log-activity Parses a log file and uses the most recent time contained in the file to determine activity. For this purpose, the log file lines are iterated from the back until a line matching a configurable regular expression is found. This expression is used to extract the contained timestamp in that log line, which is then compared to the current time with an allowed delta. The check only looks at the first line from the back that contains a timestamp. Further lines are ignored. A typical use case for this check would be a web server access log file. This check supports all date formats that are supported by the `dateutil parser `_. Options ======= .. option:: log_file path to the log file that should be analyzed .. option:: pattern A regular expression used to determine whether a line of the log file contains a timestamp to look at. The expression must contain exactly one matching group. For instance, ``^\[(.*)\] .*$`` might be used to find dates in square brackets at line beginnings. .. option:: minutes The number of minutes to allow log file timestamps to be in the past for detecting activity. If a timestamp is older than `` - `` no activity is detected. default: 10 .. option:: encoding The encoding with which to parse the log file. default: ascii .. option:: timezone The timezone to assume in case a timestamp extracted from the log file has not associated timezone information. Timezones are expressed using the names from the Olson timezone database (e.g. ``Europe/Berlin``). default: ``UTC`` Requirements ============ * `dateutil`_ * `pytz`_ .. _check-load: Load **** .. program:: check-load Checks whether the `system load 5 `__ is below a certain value. Options ======= .. option:: threshold a float for the maximum allowed load value, default: 2.5 Requirements ============ .. _check-logind-session-idle: LogindSessionsIdle ****************** .. program:: check-logind-session-idle Prevents suspending in case ``IdleHint`` for one of the running sessions `logind`_ sessions is set to ``no``. Support for setting this hint currently varies greatly across display managers, screen lockers etc. Thus, check exactly whether the hint is set on your system via ``loginctl show-session``. Options ======= .. option:: types A comma-separated list of sessions types to inspect for activity. The check ignores sessions of other types. Default: ``tty``, ``x11``, ``wayland`` .. option:: states A comma-separated list of session states to inspect. For instance, ``lingering`` sessions used for background programs might not be of interest. Default: ``active``, ``online`` .. option:: classes A comma-separated list of session classes to inspect. For instance, ``greeter`` sessions used by greeters such as lightdm might not be of interest. Default: ``user`` Requirements ============ - `dbus-python`_ .. _check-mpd: Mpd *** .. program:: check-mpd Checks whether an instance of `MPD`_ is currently playing music. Options ======= .. option:: host Host containing the MPD daemon, default: ``localhost`` .. option:: port Port to connect to the MPD daemon, default: ``6600`` .. option:: timeout .. _mpd-timeout: Request timeout in seconds, default: ``5`` Requirements ============ - `python-mpd2`_ .. _check-network-bandwidth: NetworkBandwidth **************** .. program:: check-network-bandwidth Checks whether more network bandwidth is currently being used than specified. A set of specified interfaces is checked in this regard, each of the individually, based on the average bandwidth on that interface. This average is based on the global checking interval specified in the configuration file via the :option:`interval ` option. .. note:: This check assumes stable network interface names. If this is not the case for your system, consider adding the required udev rules to ensure persistent device names. The `Archlinux Wiki page on network configuration `__ explains the necessary configuration steps. Options ======= .. option:: interfaces Comma-separated list of network interfaces to check .. option:: threshold_send If the average sending bandwidth of one of the specified interfaces is above this threshold, then activity is detected. Specified in bytes/s, default: ``100`` .. option:: threshold_receive If the average receive bandwidth of one of the specified interfaces is above this threshold, then activity is detected. Specified in bytes/s, default: ``100`` Requirements ============ .. _check-ping: Ping **** .. program:: check-ping Checks whether one or more hosts answer to ICMP requests. Options ======= .. option:: hosts Comma-separated list of host names or IPs. Requirements ============ .. _check-processes: Processes ********* .. program:: check-processes If currently running processes match an expression, the suspend will be blocked. You might use this to hinder the system from suspending when for example your rsync runs. Options ======= .. option:: processes list of comma-separated process names to check for Requirements ============ .. _check-smb: Smb *** .. program:: check-smb Any active Samba connection will block suspend. Options ======= .. option:: smbstatus executable needs to be present. Requirements ============ .. _check-users: Users ***** .. program:: check-users Checks whether a user currently logged in at the system matches several criteria. All provided criteria must match to indicate activity on the host. To find the applicable values for a given scenario on a system, use the following command: .. code-block:: console $ python3 -c "import psutil; print(psutil.users())" [suser(name='someone', terminal='tty7', host='', started=1670269568.0, pid=77179)] Options ======= All regular expressions are applied against the full string. Capturing substrings needs to be explicitly enabled using wildcard matching. .. option:: name A regular expression specifying which users to capture, default: ``.*``. .. option:: terminal A regular expression specifying the terminal on which the user needs to be logged in, default: ``.*``. .. option:: host A regular expression specifying the host from which a user needs to be logged in. Users logged in locally on the machine are usually reported with an empty string as the host value. In case this check should only match local users, use ``^$`` as the value for this option. default: ``.*`` (i.e. accept users from any host). Requirements ============ .. _check-xidletime: XIdleTime ********* .. program:: check-xidletime Checks whether all active local X displays have been idle for a sufficiently long time. Determining which X11 sessions currently exist on a running system is a harder problem than one might expect. Sometimes, the server runs as root, sometimes under the real user, and many other configuration variants exist. Thus, multiple sources for active X serer instances are implemented for this check, each of them having different requirements and limitations. They can be changed using the provided configuration option. Options ======= .. option:: timeout required idle time in seconds .. option:: method The method to use for acquiring running X sessions. Valid options are ``sockets`` and ``logind``. The default is ``sockets``. ``sockets`` Uses the X server sockets files found in :file:`/tmp/.X11-unix`. This method requires that all X server instances run with user permissions and not as root. ``logind`` Uses `logind`_ to obtain the running X server instances. This does not support manually started servers. .. option:: ignore_if_process A regular expression to match against the process names executed by each X session owner. In case the use has a running process that matches this expression, the X idle time is ignored and the check continues as if there was no activity. This can be useful in case of processes which inevitably tinker with the idle time. .. option:: ignore_users Do not check sessions of users matching this regular expressions. Requirements ============ * `dbus-python`_ for the ``logind`` method .. _check-xpath: XPath ***** .. program:: check-xpath A generic check which queries a configured URL and expects the reply to contain XML data. The returned XML document is checked against a configured `XPath`_ expression and in case the expression matches, the system is assumed to be active. Some common applications and their respective configuration are: `tvheadend`_ The required URL for `tvheadend`_ is (if running on the same host):: http://127.0.0.1:9981/status.xml In case you want to prevent suspending in case there are active subscriptions or recordings, use the following XPath:: /currentload/subscriptions[number(.) > 0] | /currentload/recordings/recording/start If you have a permantently running subscriber like `Kodi`_, increase the ``0`` to ``1``. `Plex`_ For `Plex`_, use the following URL (if running on the same host):: http://127.0.0.1:32400/status/sessions/?X-Plex-Token={TOKEN} Where acquiring the token is `documented here `_. If suspending should be prevented in case of any activity, this simple `XPath`_ expression will suffice:: /MediaContainer[@size > 2] Options ======= .. option:: url The URL to query for the XML reply. .. option:: xpath The XPath query to execute. In case it returns a result, the system is assumed to be active. .. option:: timeout Timeout for executed requests in seconds. Default: 5. .. option:: username Optional user name to use for authenticating at a server requiring authentication. If used, also a password must be provided. .. option:: password Optional password to use for authenticating at a server requiring authentication. If used, also a user name must be provided. Requirements ============ * `requests`_ * `lxml`_ autosuspend-7.2.0/doc/source/available_wakeups.rst000066400000000000000000000120671475670032600223700ustar00rootroot00000000000000.. _available-wakeups: Available wake up checks ######################## The following checks for wake up times are currently implemented. Each of the checks is described with its available configuration options and required optional dependencies. .. _wakeup-calendar: Calendar ******** .. program:: wakeup-calendar Determines next wake up time from an `iCalendar`_ file. The next event that starts after the current time is chosen as the next wake up time. Remember that updates to the calendar can only be reflected in case the system currently running. Changes to the calendar made while the system is sleeping will obviously not trigger an earlier wake up. Options ======= .. option:: url The URL to query for the XML reply. .. option:: username Optional user name to use for authenticating at a server requiring authentication. If used, also a password must be provided. .. option:: password Optional password to use for authenticating at a server requiring authentication. If used, also a user name must be provided. .. option:: xpath The XPath query to execute. Must always return number strings or nothing. .. option:: timeout Timeout for executed requests in seconds. Default: 5. Requirements ============ * `requests`_ * `icalendar `_ * `dateutil`_ * `tzlocal`_ .. _wakeup-command: Command ******* .. program:: wakeup-command Determines the wake up time by calling an external command The command always has to succeed. If something is printed on stdout by the command, this has to be the next wake up time in UTC seconds. The command is executed as is using shell execution. Beware of malicious commands in obtained configuration files. Options ======= .. option:: command The command to execute including all arguments .. _wakeup-file: File **** .. program:: wakeup-file Determines the wake up time by reading a file from a configured location. The file has to contains the planned wake up time as an int or float in seconds UTC. Options ======= .. option:: path path of the file to read in case it is present .. _wakeup-periodic: Periodic ******** .. program:: wakeup-periodic Always schedules a wake up at a specified delta from now on. Can be used to let the system wake up every once in a while, for instance, to refresh the calendar used in the :ref:`wakeup-calendar` check. Options ======= .. option:: unit A string indicating in which unit the delta is specified. Valid options are: ``microseconds``, ``milliseconds``, ``seconds``, ``minutes``, ``hours``, ``days``, ``weeks``. .. option:: value The value of the delta as an int. .. _wakeup-systemd-timer: SystemdTimer ************ .. program:: wakeup-systemd-timer Ensures that the system is active when a `systemd`_ timer is scheduled to run next. Options ======= .. option:: match A regular expression selecting the `systemd`_ timers to check. This expression matches against the names of the timer units, for instance ``logrotate.timer``. Use ``systemctl list-timers`` to find out which timers exists. .. _wakeup-xpath: XPath ***** .. program:: wakeup-xpath A generic check which queries a configured URL and expects the reply to contain XML data. The returned XML document is parsed using a configured `XPath`_ expression that has to return timestamps UTC (as strings, not elements). These are interpreted as the wake up times. In case multiple entries exist, the soonest one is used. Options ======= .. option:: url The URL to query for the XML reply. .. option:: xpath The XPath query to execute. Must always return number strings or nothing. .. option:: timeout Timeout for executed requests in seconds. Default: 5. .. option:: username Optional user name to use for authenticating at a server requiring authentication. If used, also a password must be provided. .. option:: password Optional password to use for authenticating at a server requiring authentication. If used, also a user name must be provided. .. _wakeup-xpath-delta: XPathDelta ********** .. program:: wakeup-xpath-delta Comparable to :ref:`wakeup-xpath`, but expects that the returned results represent the wake up time as a delta to the current time in a configurable unit. This check can for instance be used for `tvheadend`_ with the following expression:: //recording/next/text() Options ======= .. option:: url The URL to query for the XML reply. .. option:: username Optional user name to use for authenticating at a server requiring authentication. If used, also a password must be provided. .. option:: password Optional password to use for authenticating at a server requiring authentication. If used, also a user name must be provided. .. option:: xpath The XPath query to execute. Must always return number strings or nothing. .. option:: timeout Timeout for executed requests in seconds. Default: 5. .. option:: unit A string indicating in which unit the delta is specified. Valid options are: ``microseconds``, ``milliseconds``, ``seconds``, ``minutes``, ``hours``, ``days``, ``weeks``. Default: minutes autosuspend-7.2.0/doc/source/changelog.rst000066400000000000000000000004641475670032600206360ustar00rootroot00000000000000Changelog ######### |project| follows `Semantic Versioning `_. Hence, any breaking change to the configuration, command line interface, `systemd`_ interface, etc. will result in a new major release of |project|. .. toctree:: :maxdepth: 1 generated_changelog old_changelog autosuspend-7.2.0/doc/source/conf.py000066400000000000000000000063071475670032600174560ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- import os import os.path # needs_sphinx = '1.0' extensions = [ "sphinx.ext.ifconfig", "sphinx.ext.intersphinx", "sphinx.ext.napoleon", "sphinx.ext.autodoc", "sphinx_autodoc_typehints", "sphinxcontrib.plantuml", "sphinx_issues", "recommonmark", ] templates_path = ['_templates'] source_suffix = '.rst' master_doc = 'index' project = 'autosuspend' copyright = '2017, Johannes Wienke' author = 'Johannes Wienke' with open(os.path.join( os.path.abspath(os.path.dirname(os.path.realpath(__file__))), '../..', 'VERSION'), 'r') as version_file: lines = version_file.readlines() version = lines[0].strip() release = lines[1].strip() language = "en" exclude_patterns = [] pygments_style = 'sphinx' todo_include_todos = False rst_epilog = ''' .. _autosuspend: https://github.com/languitar/autosuspend .. _Python 3: https://docs.python.org/3/ .. _Python: https://docs.python.org/3/ .. _setuptools: https://setuptools.readthedocs.io .. _configparser: https://docs.python.org/3/library/configparser.html .. _psutil: https://github.com/giampaolo/psutil .. _lxml: http://lxml.de/ .. _MPD: http://www.musicpd.org/ .. _python-mpd2: https://pypi.python.org/pypi/python-mpd2 .. _dbus-python: https://cgit.freedesktop.org/dbus/dbus-python/ .. _Kodi: https://kodi.tv/ .. _requests: https://pypi.python.org/pypi/requests .. _systemd: https://www.freedesktop.org/wiki/Software/systemd/ .. _systemd service files: http://www.freedesktop.org/software/systemd/man/systemd.service.html .. _broadcast-logging: https://github.com/languitar/broadcast-logging .. _tvheadend: https://tvheadend.org/ .. _XPath: https://www.w3.org/TR/xpath/ .. _logind: https://www.freedesktop.org/wiki/Software/systemd/logind/ .. _iCalendar: https://tools.ietf.org/html/rfc5545 .. _dateutil: https://dateutil.readthedocs.io .. _python-icalendar: https://icalendar.readthedocs.io .. _tzlocal: https://pypi.org/project/tzlocal/ .. _requests-file: https://github.com/dashea/requests-file .. _Plex: https://www.plex.tv/ .. _portalocker: https://portalocker.readthedocs.io .. _jsonpath-ng: https://github.com/h2non/jsonpath-ng .. _JSONPath: https://goessner.net/articles/JsonPath/ .. _pytz: https://pythonhosted.org/pytz/ .. |project| replace:: {project} .. |project_bold| replace:: **{project}** .. |project_program| replace:: :program:`{project}`'''.format(project=project) # Intersphinx intersphinx_mapping = {'python': ('https://docs.python.org/3.7', None)} # HTML options html_theme = 'furo' # html_theme_options = {} # html_static_path = ['_static'] html_sidebars = { } # MANPAGE options man_pages = [ ('man_command', 'autosuspend', 'autosuspend Documentation', [author], 1), ('man_config', 'autosuspend.conf', 'autosuspend config file Documentation', [author], 5), ] man_show_urls = True # issues issues_github_path = 'languitar/autosuspend' # napoleon napoleon_google_docstring = True napoleon_numpye_docstring = False napoleon_include_init_with_doc = True typehints_fully_qualified = True def setup(app): app.add_config_value( 'is_preview', os.environ.get('READTHEDOCS_VERSION', '') == 'latest', 'env', ) autosuspend-7.2.0/doc/source/configuration_file.inc000066400000000000000000000130051475670032600225110ustar00rootroot00000000000000Syntax ~~~~~~ The |project_program| configuration file uses INI syntax and needs to be processable by the Python `configparser`_ module. A simple configuration file could look like: .. code-block:: ini [general] interval = 30 idle_time = 900 suspend_cmd = /usr/bin/systemctl suspend wakeup_cmd = echo {timestamp:.0f} > /sys/class/rtc/rtc0/wakealarm notify_cmd_wakeup = su myuser -c notify-send -a autosuspend 'Suspending the system. Wake up at {iso}' notify_cmd_no_wakeup = su myuser -c notify-send -a autosuspend 'Suspending the system.' lock_file = /var/lock/autosuspend.lock lock_timeout = 30 [check.Ping] enabled = false hosts = 192.168.0.7 [check.RemoteUsers] class = Users enabled = true name = .* terminal = .* host = [0-9].* [wakeup.File] enabled = True path = /var/run/autosuspend/wakeup The configuration file consists of a ``[general]`` section, which specifies general processing options, and multiple sections of the format ``[check.*]`` and ``[wakeup.*]``. These sections describe the activity and wake up checks to execute. General configuration ~~~~~~~~~~~~~~~~~~~~~ .. program:: config-general The ``[general]`` section contains options controlling the overall behavior of the |project_program| daemon. These are: .. option:: interval The time to wait after executing all checks in seconds. .. option:: idle_time The required amount of time in seconds with no detected activity before the host will be suspended. Default: 300 seconds .. option:: min_sleep_time The minimal amount of time in seconds the system has to sleep for actually triggering suspension. If a scheduled wake up results in an effective time below this value, the system will not sleep. Default: 1200 seconds .. option:: wakeup_delta Wake up the system this amount of seconds earlier than the time that was determined for an event that requires the system to be up. This value adds a safety margin for the time a the wake up effectively takes. Default: 30 seconds .. option:: suspend_cmd The command to execute in case the host shall be suspended. This line can contain additional command line arguments to the command to execute. .. option:: wakeup_cmd The command to execute for scheduling a wake up of the system. The given string is processed using Python's :meth:`str.format` and a format argument called ``timestamp`` encodes the UTC timestamp of the planned wake up time (float). Additionally ``iso`` can be used to acquire the timestamp in ISO 8601 format. .. option:: notify_cmd_wakeup A command to execute before the system is going to suspend for the purpose of notifying interested clients. This command is only called in case a wake up is scheduled. The given string is processed using Python's :meth:`str.format` and a format argument called ``timestamp`` encodes the UTC timestamp of the planned wake up time (float). Additionally ``iso`` can be used to acquire the timestamp in ISO 8601 format. If empty or not specified, no command will be called. .. option:: notify_cmd_no_wakeup A command to execute before the system is going to suspend for the purpose of notifying interested clients. This command is only called in case NO wake up is scheduled. Hence, no string formatting options are available. If empty or not specified, no command will be called. .. option:: woke_up_file Location of a file that indicates to |project_program| that the computer has suspended since the last time checks were executed. This file is usually created by a `systemd`_ service. Thus, changing the location also requires adapting the respective service. Refer to :ref:`systemd-integration` for further details. .. option:: lock_file Location of a file that is used to synchronize the continuously running daemon and the systemd callback. .. option:: lock_timeout Timeout in seconds used when trying to acquire the lock. This should be longer than the maximum run time of all configured checks. In the worst cases, suspending the system is delayed by this amount of time because ``presuspend`` hook has to wait before all checks have passed. Activity check configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. program:: config-check For each activity check to execute, a section with the name format ``[check.*]`` needs to be created. Each check has a name and an executing class which implements the behavior. The fraction of the section name ``check.`` determines the name, and in case no class option is given inside the section, also the class which implements the check. In case the :option:`class` option is specified, the name is completely user-defined and the same check can even be instantiated multiple times with differing names. For each check, these generic options can be specified: .. option:: class Name of the class implementing the check. If the name does not contain a dot (``.``), this is assumed to be one of the checks provided by |project| internally. Otherwise, this can be used to pull in third-party checks. If this option is not specified, the section name must represent a valid internal check class. .. option:: enabled Needs to be ``true`` for a check to actually execute. ``false`` is assumed if not specified. Furthermore, each check might have custom options. Wake up check configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Wake up checks uses the same configuration logic as the previously described activity checks. However, the configuration file sections start with ``wakeup.`` instead of ``check.``. autosuspend-7.2.0/doc/source/configuration_file.rst000066400000000000000000000002641475670032600225530ustar00rootroot00000000000000Configuration file ################## .. include:: configuration_file.inc For options of individual checks, please refer to :ref:`available-checks` and :ref:`available-wakeups`. autosuspend-7.2.0/doc/source/debugging.rst000066400000000000000000000026111475670032600206360ustar00rootroot00000000000000Debugging ######### In case you need to track configuration issues to understand why a system suspends or does not, the extensive logging output of |project_program| might be used. Each iteration of the daemon logs exactly which condition detected activity or not. So you should be able to find out what is going on. The command line flag :option:`autosuspend -l` allows specifying a Python logging configuration file which specifies what to log. The provided `systemd`_ service files (see :ref:`systemd-integration`) already use :file:`/etc/autosuspend-logging.conf` as the standard location and a default file is usually installed. If you launch |project_program| manually from the console, the command line flag :option:`autosuspend -d` might also be used to get full logging to the console instead. In case one of the conditions you monitor prevents suspending the system if an external connection is established (logged-in users, open TCP port), then the logging configuration file can be changed to use the `broadcast-logging`_ package. This way, the server will broadcast new log messages on the network and external clients on the same network can listen to these messages without creating an explicit connection. Please refer to the documentation of the `broadcast-logging`_ package on how to enable and use it. Additionally, one might also examine the ``journalctl`` for |project_program| after the fact. autosuspend-7.2.0/doc/source/description.inc000066400000000000000000000016561475670032600211770ustar00rootroot00000000000000|project_program| is a daemon that periodically suspends a system on inactivity and wakes it up again automatically in case it is needed. For this purpose, |project_program| periodically iterates a number of user-configurable activity checks, which indicate whether an activity on the host is currently present that should prevent the host from suspending. In case one of the checks indicates such activity, no action is taken and periodic checking continues. Otherwise, in case no activity can be detected, this state needs to be present for a specified amount of time before the host is suspended by |project_program|. In addition to the activity checks, wake up checks are used to determine planned future activities of the system (for instance, a TV recording or a periodic backup). In case such activities are known before suspending, |project_program| triggers a command to wake up the system automatically before the soonest activity. autosuspend-7.2.0/doc/source/external_command_activity_scripts.rst000066400000000000000000000017531475670032600257140ustar00rootroot00000000000000.. _external-command-activity-scripts: External command scripts for activity detection ############################################### A collection of user-provided scripts to use with the :ref:`check-external-command` check for activity detection. pyLoad ****** `pyLoad `_ uses an uncommon login theme for its API and hence two separate requests are required to query for active downloads. Use something along the following lines to query `pyLoad`_. .. code-block:: bash #!/bin/bash SessionID=$(curl -s "http://127.0.0.1:8000/api/login" -g -H "Host: 127.0.0.1:8000" -H "Content-Type: application/x-www-form-urlencoded" --data "username=user&password=password" | jq -r) SessionStatus=$(curl -s "http://127.0.0.1:8000/api/statusServer" -g -H "Host: 127.0.0.1:8000" -H "Content-Type: application/x-www-form-urlencoded" --data "session=$SessionID" | jq -r '.active') if [ $SessionStatus -eq 1 ] then exit 0 else exit 1 fi Source: :issue:`102` autosuspend-7.2.0/doc/source/faq.rst000066400000000000000000000110051475670032600174470ustar00rootroot00000000000000.. _faq: Frequently Asked Questions ########################## Usage ***** How to check unsupported software? ================================== In case you want to detect if some piece of software running on your system that is not officially supported is performing relevant activity you have two options: * Use a script with the :ref:`check-external-command` check. * Implement a Python module with you check being a subclass of :class:`autosuspend.checks.Activity` or :class:`autosuspend.checks.Wakeup` and install it alongside |project|. The custom check class can then be referenced in the config with its full dotted path, for instance, ``mymodule.MyCheck``, in the `class` field. How do I wake up my system if needed? ===================================== |project_bold| itself only handles wake ups for events that were foreseeable at the time the system was put into sleep mode. In case the system also has to be used on-demand, a simple way to wake up the system is to enable `Wake on LAN `_. Here, a special network packet can be used to wake up the system again. Multiple front-ends exist to send these magic packets. The typical usage scenario with this approach is to manually send the magic packet when the system is needed, wait a few seconds, and then to perform the intended tasks with the system. Wake on LAN needs to be specifically enabled on the system. Typically, the documentation of common Linux distributions explains how to enable Wake on LAN: * `Archlinux `__ * `Debian `__ * `Ubuntu `__ A set of front-ends for various platforms allows sending the magic packets. For instance: * `gWakeOnLan `__: GTK GUI, Linux * `wol `__: command line, Linux * `Wake On Lan `__: GUI, Windows * `Wake On Lan `__: Android * `Wake On Lan `__: Android, open-source * `Kore (Kodi remote control) `__: Android, for Kodi users * `Mocha WOL `__: iOS How do I keep a system active at daytime ======================================== Imagine you want to have a NAS that is always available between 7 a.m. and 8 p.m. After 8 p.m. the system should go to sleep in case no one else is using it. Every morning at 7 a.m. it should wake up automatically. This workflow can be realized using the :ref:`wakeup-calendar` wakeup check and the :ref:`check-active-calendar-event` activity check based on an `iCalendar`_ file residing on the local file system of the NAS. The former check ensures that the system wakes up at the desired time of the day while the latter ensure that it stays active at daytime. The first step is to create the `iCalendar`_ file, which can conveniently and graphically be edited with `Thunderbird Lightning `_ or any other calendar frontend. Essentially, the ``*.ics`` may look like this:: BEGIN:VCALENDAR PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN VERSION:2.0 BEGIN:VEVENT CREATED:20180602T151701Z LAST-MODIFIED:20180602T152732Z DTSTAMP:20180602T152732Z UID:0ef23894-702e-40ac-ab09-94fa8c9c51fd SUMMARY:keep active RRULE:FREQ=DAILY DTSTART:20180612T070000 DTEND:20180612T200000 TRANSP:OPAQUE SEQUENCE:3 END:VEVENT END:VCALENDAR Afterwards, edit ``autosuspend.conf`` to contain the two aforementioned checks based on the created ``ics`` file. This will end up with at least this config: .. code-block:: ini [general] interval = 30 suspend_cmd = /usr/bin/systemctl suspend wakeup_cmd = echo {timestamp:.0f} > /sys/class/rtc/rtc0/wakealarm woke_up_file = /var/run/autosuspend-just-woke-up [check.ActiveCalendarEvent] enabled = true url = file:///path/to/your.ics [wakeup.Calendar] enabled = true url = file:///path/to/your.ics Adding other activity checks will ensure that the system stays awake event after 8 p.m. if it is still used. Error messages ************** No connection adapters were found for '\file://\*' ================================================== You need to install the `requests-file`_ package for ``file://`` URIs to work. autosuspend-7.2.0/doc/source/generated_changelog.md000066400000000000000000000157401475670032600224470ustar00rootroot00000000000000# [7.2.0](https://github.com/languitar/autosuspend/compare/v7.1.0...v7.2.0) (2025-02-23) ### Features * **systemd:** automatically enable/disable suspend hook ([772797a](https://github.com/languitar/autosuspend/commit/772797a8e4d9b3db7f609d20e73a0e66805ba2f1)), closes [#625](https://github.com/languitar/autosuspend/issues/625) # [7.1.0](https://github.com/languitar/autosuspend/compare/v7.0.3...v7.1.0) (2025-01-12) ### Features * official Python 3.13 support ([a8ea72d](https://github.com/languitar/autosuspend/commit/a8ea72d414621a13ff7705330bd731ffe94eeef8)) ## [7.0.3](https://github.com/languitar/autosuspend/compare/v7.0.2...v7.0.3) (2024-11-19) ### Bug Fixes * treat temporary failures as activity ([8c96853](https://github.com/languitar/autosuspend/commit/8c968530f011dad814df8c55794b058f3c751e8d)), closes [#589](https://github.com/languitar/autosuspend/issues/589) ## [7.0.2](https://github.com/languitar/autosuspend/compare/v7.0.1...v7.0.2) (2024-10-13) ### Bug Fixes * **icalendar:** support icalendar v6 ([49bc89f](https://github.com/languitar/autosuspend/commit/49bc89fb2461758dd4d4f07016e88c8458192161)) ## [7.0.1](https://github.com/languitar/autosuspend/compare/v7.0.0...v7.0.1) (2024-09-22) ### Bug Fixes * **kodi-idle-time:** Send proper request ([8bb6dad](https://github.com/languitar/autosuspend/commit/8bb6dad7f325024d011008fc1f0e3d52a0b9f222)) # [7.0.0](https://github.com/languitar/autosuspend/compare/v6.1.1...v7.0.0) (2024-04-25) * build!: drop Python 3.9 support ([3c4ae32](https://github.com/languitar/autosuspend/commit/3c4ae32c8e52f022f41e94c3a49dd89b9d02dcf2)) ### Features * officially support Python 3.12 ([de2f180](https://github.com/languitar/autosuspend/commit/de2f18010d166eb86fe15665aa7769f2105b02aa)) ### BREAKING CHANGES * Python 3.9 is not supported officially anymore. Python 3.10 is the supported minimum version. ## [6.1.1](https://github.com/languitar/autosuspend/compare/v6.1.0...v6.1.1) (2024-02-12) ### Bug Fixes * **docs:** add missing docs for new version subcommand ([fb248f7](https://github.com/languitar/autosuspend/commit/fb248f7a5706f81c20f7e88907e22cbd5c895cbb)) # [6.1.0](https://github.com/languitar/autosuspend/compare/v6.0.0...v6.1.0) (2024-02-11) ### Features * **cli:** provide a version subcommand ([d51d836](https://github.com/languitar/autosuspend/commit/d51d836564a53b0dd5017fcd801e43b117542ebc)), closes [#482](https://github.com/languitar/autosuspend/issues/482) # [6.0.0](https://github.com/languitar/autosuspend/compare/v5.0.0...v6.0.0) (2023-09-18) * build!: modernize supported Python version ([31c8ccc](https://github.com/languitar/autosuspend/commit/31c8cccb503218691ffb045142b1297133ce5340)) ### BREAKING CHANGES * Python 3.8 has been deprecated and is not officially supported anymore. # [5.0.0](https://github.com/languitar/autosuspend/compare/v4.3.3...v5.0.0) (2023-08-13) * feat(logind)!: configure which session classes to process ([986e558](https://github.com/languitar/autosuspend/commit/986e558c2913bf30ebbab87025fe9722d5997aa7)), closes [#366](https://github.com/languitar/autosuspend/issues/366) ### BREAKING CHANGES * LogindSessionIdle now only processes sessions of type "user" by default. Use the new configuration option classes to also include other types in case you need to include them in the checks. ## [4.3.3](https://github.com/languitar/autosuspend/compare/v4.3.2...v4.3.3) (2023-08-10) ### Bug Fixes * **systemd:** handle timers without next execution time ([9fb83ea](https://github.com/languitar/autosuspend/commit/9fb83eac7d6cbe981e2ebfc1ec3c3b54fca19804)), closes [#403](https://github.com/languitar/autosuspend/issues/403) ## [4.3.2](https://github.com/languitar/autosuspend/compare/v4.3.1...v4.3.2) (2023-06-05) ### Bug Fixes * release for sphinx 7 support ([569dfa5](https://github.com/languitar/autosuspend/commit/569dfa5954617929ae11529ece84f32810e10bee)) ## [4.3.1](https://github.com/languitar/autosuspend/compare/v4.3.0...v4.3.1) (2023-05-16) ### Bug Fixes * **ical:** support all versions of tzlocal ([9eb0b95](https://github.com/languitar/autosuspend/commit/9eb0b9549e11b612d47d007777cb83eac4c53f31)) # [4.3.0](https://github.com/languitar/autosuspend/compare/v4.2.0...v4.3.0) (2022-12-08) ### Features * add seconds since the system became idle to logs ([cba13db](https://github.com/languitar/autosuspend/commit/cba13db8c50a5fbab05447c3f6ce74cf85898100)), closes [#281](https://github.com/languitar/autosuspend/issues/281) # [4.2.0](https://github.com/languitar/autosuspend/compare/v4.1.1...v4.2.0) (2022-07-24) ### Features * **wakeup:** add a systemd timer wakeup check ([7c687a2](https://github.com/languitar/autosuspend/commit/7c687a23f705d46c65ef400332483a32ff6eaa79)) ## [4.1.1](https://github.com/languitar/autosuspend/compare/v4.1.0...v4.1.1) (2022-03-10) ### Bug Fixes * allow tzlocal version >= 4 ([58e8634](https://github.com/languitar/autosuspend/commit/58e8634347cc5bf25cbfbfccfe874d05420bb995)) # [4.1.0](https://github.com/languitar/autosuspend/compare/v4.0.1...v4.1.0) (2021-12-28) ### Features * add official Python 3.10 support ([e5b2e49](https://github.com/languitar/autosuspend/commit/e5b2e494986d13ac29a06cfac0c5a6601c372671)) ## [4.0.1](https://github.com/languitar/autosuspend/compare/v4.0.0...v4.0.1) (2021-10-26) ### Bug Fixes * **activity:** detect ipv4 mapped ipv6 connections ([a81e456](https://github.com/languitar/autosuspend/commit/a81e456aa89737a0a2f03ec5af5ffaf2e7738073)), closes [#116](https://github.com/languitar/autosuspend/issues/116) # [4.0.0](https://github.com/languitar/autosuspend/compare/v3.1.4...v4.0.0) (2021-09-20) * chore(build)!: drop tests on Python 3.7 ([06dce98](https://github.com/languitar/autosuspend/commit/06dce98882d5c8fa4d5e90623660c43d006eefa0)) ### BREAKING CHANGES * Python 3.7 isn't used anymore on any LTS Ubuntu or Debian release. No need to support such an old version anymore. ## [3.1.4](https://github.com/languitar/autosuspend/compare/v3.1.3...v3.1.4) (2021-09-20) ### Bug Fixes * **ical:** limit tzlocal to version <3 ([623cd37](https://github.com/languitar/autosuspend/commit/623cd371df03a6fe3305eca4cf9e57c4d76b5c8a)) ## [3.1.3](https://github.com/languitar/autosuspend/compare/v3.1.2...v3.1.3) (2021-03-29) ## [3.1.2](https://github.com/languitar/autosuspend/compare/v3.1.1...v3.1.2) (2021-03-29) ## [3.1.1](https://github.com/languitar/autosuspend/compare/v3.1.0...v3.1.1) (2021-03-28) ### Bug Fixes * fix automatic version file generation ([aeb601d](https://github.com/languitar/autosuspend/commit/aeb601d523791780e5da592476b365bbc4b3f4c5)) ## [3.1.0](https://github.com/languitar/autosuspend/compare/v3.0.1...v3.1.0) (2021-03-28) ### Features * add semantic-release for automatic releases ([ac5ec86](https://github.com/languitar/autosuspend/commit/ac5ec8617681b537714f8eb8fef4ce0872989f2a)) ### Bug Fixes * use jsonpath ext to support filter expressions ([24d1be1](https://github.com/languitar/autosuspend/commit/24d1be1fcbd59d8e29a1bbfdc162e253e2f239c4)), closes [#102](https://github.com/languitar/autosuspend/issues/102) autosuspend-7.2.0/doc/source/index.rst000066400000000000000000000033431475670032600200150ustar00rootroot00000000000000|project| - a daemon to automatically suspend and wake up a system ################################################################## .. ifconfig:: is_preview .. warning:: This is the documentation for an unreleased preview version of |project|. .. include:: description.inc The following diagram visualizes the periodic processing performed by |project|. .. uml:: @startuml skinparam shadowing false skinparam backgroundcolor #eeeeee skinparam Padding 8 skinparam ActivityBackgroundColor #FFFFFF skinparam ActivityDiamondBackgroundColor #FFFFFF skinparam ActivityBorderColor #333333 skinparam ActivityDiamondBorderColor #333333 skinparam ArrowColor #333333 start :Execute activity checks; if (Is the system active?) then (no) if (Was the system idle before?) then (no) :Remember current time as start of system inactivity; else (yes) endif if (Is system idle long enough?) then (yes) :Execute wake up checks; if (Is a wake up required soon?) then (yes) stop else if (Is any wake up required?) then (yes) #BBFFBB:Schedule the earliest wake up; else (no) endif endif #BBFFBB:Suspend the system; else (no) stop endif else (yes) :Forget start of system inactivity; stop endif stop @enduml .. toctree:: :maxdepth: 2 :caption: Usage installation options configuration_file available_checks available_wakeups systemd_integration external_command_activity_scripts api .. toctree:: :maxdepth: 2 :caption: Support faq debugging support changelog Indices and tables ################## * :ref:`genindex` autosuspend-7.2.0/doc/source/installation.rst000066400000000000000000000053221475670032600214060ustar00rootroot00000000000000.. _installation: Installation instructions ######################### |project_program| is designed for Python **3** and does not work with Python 2. .. note:: After installation, do not forget to enable and start |project| vis `systemd`_ as described in :ref:`systemd-integration`. Requirements ************ The minimal requirements are. * `Python 3`_ >= 3.7 * `psutil`_ * `portalocker`_ Additionally, the some checks need further dependencies to function properly. Please refer to :ref:`available-checks` for individual requirements. If checks using URLs to load data should support ``file://`` URLs, `requests-file`_ is needed. Binary packages *************** .. image:: https://repology.org/badge/vertical-allrepos/autosuspend.svg :target: https://repology.org/project/autosuspend/versions Debian ====== Installation from official package sources:: apt-get install autosuspend Archlinux (AUR) =============== |project| is available as an `Archlinux AUR package `_. Installation via some `AUR helpers ` such as :program:`paru`:: paru -S autosuspend Other AUR helpers may be used, too. Gentoo ====== Patrick Holthaus has provided an ebuild for Gentoo in `his overlay `_. You can use it as follows:: eselect repository enable pholthaus-overlay emaint sync -r pholthaus-overlay emerge sys-apps/autosuspend Other distributions =================== In case you want to generate a package for a different Linux distribution, I'd be glad to hear about that. Manual installation ******************* |project_program| is a usual Python_ package and hence can be installed using the common Python_ packaging tools. Briefly, the following steps can be used to install |project_program| from source in a system-wide location (as ``root`` user): .. code-block:: bash python3 -m venv /opt/autosuspend /opt/autosuspend/bin/pip install git+https://github.com/languitar/autosuspend.git@#egg=autosuspend[all] .. note:: Replace the angle brackets with desired Git tag or branch. Use ``main`` for the latest development release. .. note:: The ``all`` in the square brackets ensures that |project_program| is installed with all optional dependencies. That way all available checks can be used. In case you only need a subset of optional requirements, replace ``all`` with a comma-separated list of package extras. The names of these extras can be found in :file:`setup.py`. Afterwards, copy the systemd_ unit files found in ``/opt/autosuspend/lib/systemd/system/`` to ``/etc/systemd`` and adapt the contained paths to the installation location. autosuspend-7.2.0/doc/source/man_command.rst000066400000000000000000000013301475670032600211510ustar00rootroot00000000000000:orphan: .. _man-command: |project| ######### Synopsis ******** |project_bold| [*options*] **daemon|presuspend|version** [*subcommand options*] Description *********** .. include:: description.inc If not specified via a command line argument, |project_program| looks for a default configuration at :file:`/etc/autosuspend.conf`. :manpage:`autosuspend.conf(5)` describes the configuration file, the available checks, and their configuration options. Options ******* .. toctree:: options Bugs **** Please report bugs at the project repository at https://github.com/languitar/autosuspend. See also ******** :manpage:`autosuspend.conf(5)`, online documentation including FAQs at https://autosuspend.readthedocs.io/ autosuspend-7.2.0/doc/source/man_config.rst000066400000000000000000000005741475670032600210110ustar00rootroot00000000000000:orphan: |project|.conf ############## Synopsis ******** :file:`/etc/autosuspend.conf` General Configuration ********************* Configures the |project_program| daemon. .. toctree:: configuration_file Available Activity Check ************************ .. toctree:: available_checks Available Wakeup Check ********************** .. toctree:: available_wakeups autosuspend-7.2.0/doc/source/old_changelog.rst000066400000000000000000000140171475670032600214730ustar00rootroot000000000000003.1 *** New features ============ New activity checks ------------------- * :ref:`check-jsonpath`: Similar to the existing :ref`check-xpath`, the new checks requests a JSON URL and evaluates it against a `JSONPath`_ expression to determine activity (:issue:`81`, :issue:`103`). * :ref:`check-last-log-activity`: Check log files for most recent contained timestamps (:issue:`98`, :issue:`99`). Fixed bugs ========== * Connection errors are now properly handled by :ref:`check-mpd` (:issue:`77`). Notable changes =============== * The required `Python`_ version is now declared in the :ref:`installation` and :file:`setup.py` (:issue:`76`) * Python 3.9 is officially supported and tested (:issue:`89`). * Some code cleanup work has been performed (:issue:`93` and :issue:`92`). * The daemon now better distinguished between temporary and permanent issues, for instance, by terminating in case a required program is not installed (:issue:`78`). 3.0 *** This version splits the executable into two distinct subcommands, one for activity checking and one for scheduling wake ups. This way, the wake up scheduling mechanism can be hooked into system tools such as `systemd`_ to ensure that wake ups are scheduled correctly every time the system suspends. This increases the reliability of the mechanism but also changes the way |project_program| has to be called. You now need to enable two `systemd`_ units as describe in :ref:`systemd-integration` and the command line interface has changed. New features ============ * The :ref:`check-kodi-idle-time` activity check can now be parameterized whether to indicate activity on a paused player or not (:issue:`59`, :issue:`60`). * New structure as described above in the version introduction (:issue:`43`). Fixed bugs ========== * Documented default URL for the ``Kodi*`` checks did not actually exist in code, which has been fixed now (:issue:`58`, :issue:`61`). * A bug in :ref:`check-logind-session-idle` has been fixed (:issue:`71`, :issue:`72`). Notable changes =============== * The executable now uses subcommands. The previous behavior as a long-running daemon is now available under the ``daemon`` subcommand. * The command line flags for logging have changed. The previous ``-l`` flag, which combined boolean behavior and file reading, has been split into two distinct flags: ``-d`` is a boolean switch to enable full debug logging to console, whereas the old ``-l`` is now only used for reading logging configuration files. This change prevents nasty subtleties and issues when parsing the command line and became mandatory to support subcommands after the general configuration arguments such as logging. * Dropped support for Python 3.6 and included Python 3.8 in CI infrastructure. Everything works on Python 3.8. * The documentation has been restructured and improved. For instance, there is now a :ref:`faq` section. * Some build and test dependencies have changed. * CI-builds have been converted to Github Actions. 2.0.4 ***** This is a minor bug fix release. Fixed bugs ========== * :ref:`check-active-connection` did not handle local IPv6 addresses with scope such as ``fe80::5193:518c:5c69:aedb%enp3s0`` (:issue:`50`) 2.0.3 ***** This is a minor bug fix release. Fixed bugs ========== * :ref:`check-network-bandwidth` did not update its internal state and therefore did not work as documented (:issue:`49`) 2.0.2 ***** This is a minor bug fix release. Fixed bugs ========== * :ref:`check-kodi` and :ref:`check-kodi-idle-time` checks now catch ``JSONDecodeErrors`` (:issue:`45`) * :ref:`check-kodi` and :ref:`check-kodi-idle-time` checks now support authentication (:issue:`47`) 2.0 *** This version adds scheduled wake ups as its main features. In addition to checks for activity, a set of checks for future activities can now be configured to determine times at which the systems needs to be online again. The daemon will start suspending in case the next detected wake up time is far enough in the future and schedule an automatic system wake up at the closest determined wake up time. This can, for instance, be used to ensure that the system is up again when a TV show has to be recorded to disk. Below is a detailed list of notable changes. New features ============ * Scheduled wake ups (:issue:`9`). * Ability to call configurable user commands before suspending for notification purposes (:issue:`25`). * Checks using network requests now support authentication (:issue:`32`). * Checks using network requests now support ``file://`` URIs (:issue:`36`). New activity checks ------------------- * :ref:`check-active-calendar-event`: Uses an `iCalendar`_ file (via network request) to prevent suspending in case an event in the calendar is currently active (:issue:`24`). * :ref:`check-kodi-idle-time`: Checks the idle time of `Kodi`_ to prevent suspending in case the menu is used (:issue:`33`). New wakeup checks ----------------- * :ref:`wakeup-calendar`: Wake up the system at the next event in an `iCalendar`_ file (requested via network, :issue:`30`). * :ref:`wakeup-command`: Call an external command to determine the next wake up time (:issue:`26`). * :ref:`wakeup-file`: Read the next wake up time from a file (:issue:`9`). * :ref:`wakeup-periodic`: Wake up at a defined interval, for instance, to refresh calendars for the :ref:`wakeup-calendar` check (:issue:`34`). * :ref:`wakeup-xpath` and :ref:`wakeup-xpath-delta`: Request an XML document and use `XPath`_ to extract the next wakeup time. Fixed bugs ========== * `XPath`_ checks now support responses with explicit encodings (:issue:`29`). Notable changes =============== * The namespace of the logging systems has been rearranged (:issue:`38`). Existing logging configurations might require changes. * The default configuration file has been reduced to explain the syntax and semantics. For a list of all available checks, refer the manual instead (:issue:`39`). For a complete list of all addressed issues and new features, please refer to the respective `Github milestone `_. autosuspend-7.2.0/doc/source/options.rst000066400000000000000000000031401475670032600203740ustar00rootroot00000000000000Command line options #################### General syntax: |project_bold| [*options*] **daemon|presuspend|version** [*subcommand options*] General options *************** .. program:: autosuspend .. option:: -h, --help Displays an online help. .. option:: -c FILE, --config FILE Specifies an alternate config file to use instead of the default on at :file:`/etc/autosuspend.conf`. .. option:: -l FILE, --logging FILE Configure the logging system with the provided logging file. This file needs to follow the conventions for :ref:`Python logging files `. .. option:: -d Configure full debug logging in the command line. Mutually exclusive to :option:`autosuspend -l`. Subcommand ``daemon`` ********************* Starts the continuously running daemon. .. program:: autosuspend daemon .. option:: -a, --allchecks Usually, |project_program| stops checks in each iteration as soon as the first matching check indicates system activity. If this flag is set, all subsequent checks are still executed. Useful mostly for debugging purposes. .. option:: -r SECONDS, --runfor SECONDS If specified, do not run endlessly. Instead, operate only for the specified amount of seconds, then exit. Useful mostly for debugging purposes. Subcommand ``presuspend`` ************************* Should be called by the system before suspending. .. program:: autosuspend presuspend No options Subcommand ``version`` ************************* Outputs the currently installed version of |project_program| to stdout. .. program:: autosuspend version No options autosuspend-7.2.0/doc/source/support.rst000066400000000000000000000006371475670032600204250ustar00rootroot00000000000000Support requests ################ For questions, please first consult the issue tracker at the `Github project `_ for existing issues and questions. Questions are marked with the `question` tag. If your question is not answered, open a new issue with the question. In case you have found a bug or you want to request a new feature, please also open an issue at the `Github project `_. autosuspend-7.2.0/doc/source/systemd_integration.rst000066400000000000000000000022221475670032600227740ustar00rootroot00000000000000.. _systemd-integration: systemd integration ################### Even though it is possible to run |project_program| manually (cf. :ref:`the manpage `), in production use cases, the daemon will usually be run from `systemd`_. For this purpose, the package ships with `service definition files `_ for `systemd`_, so that you should be able to manage |project_program| via `systemd`_. These files need to be installed in the appropriate locations for such service files, which depend on the Linux distribution. Some common locations are: * :file:`/usr/lib/systemd/system` (e.g. Archlinux packaged service files) * :file:`/lib/systemd/system` (e.g. Debian packaged service files) * :file:`/etc/systemd/system` (e.g. Archlinux manually added service files) Binary installation packages for Linux distributions should have installed the service files at the appropriate locations already. To start |project_program| via `systemd`_, execute: .. code-block:: bash systemctl enable autosuspend.service To start |project_program| automatically at system start, execute: .. code-block:: bash systemctl start autosuspend.service autosuspend-7.2.0/package-lock.json000066400000000000000000015372751475670032600173440ustar00rootroot00000000000000{ "name": "autosuspend", "lockfileVersion": 2, "requires": true, "packages": { "": { "devDependencies": { "@commitlint/cli": "19.7.1", "@commitlint/config-conventional": "19.7.1", "@semantic-release/changelog": "6.0.3", "@semantic-release/exec": "7.0.3", "@semantic-release/git": "10.0.1", "semantic-release": "24.2.3" } }, "node_modules/@babel/code-frame": { "version": "7.22.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dev": true, "dependencies": { "@babel/highlight": "^7.22.13", "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/code-frame/node_modules/ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "dependencies": { "color-convert": "^1.9.0" }, "engines": { "node": ">=4" } }, "node_modules/@babel/code-frame/node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" }, "engines": { "node": ">=4" } }, "node_modules/@babel/code-frame/node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "dependencies": { "color-name": "1.1.3" } }, "node_modules/@babel/code-frame/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "engines": { "node": ">=0.8.0" } }, "node_modules/@babel/code-frame/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/@babel/code-frame/node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "dependencies": { "has-flag": "^3.0.0" }, "engines": { "node": ">=4" } }, "node_modules/@babel/helper-validator-identifier": { "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight/node_modules/ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "dependencies": { "color-convert": "^1.9.0" }, "engines": { "node": ">=4" } }, "node_modules/@babel/highlight/node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" }, "engines": { "node": ">=4" } }, "node_modules/@babel/highlight/node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "dependencies": { "color-name": "1.1.3" } }, "node_modules/@babel/highlight/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, "node_modules/@babel/highlight/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "engines": { "node": ">=0.8.0" } }, "node_modules/@babel/highlight/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/@babel/highlight/node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "dependencies": { "has-flag": "^3.0.0" }, "engines": { "node": ">=4" } }, "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==", "dev": true, "optional": true, "engines": { "node": ">=0.1.90" } }, "node_modules/@commitlint/cli": { "version": "19.7.1", "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-19.7.1.tgz", "integrity": "sha512-iObGjR1tE/PfDtDTEfd+tnRkB3/HJzpQqRTyofS2MPPkDn1mp3DBC8SoPDayokfAy+xKhF8+bwRCJO25Nea0YQ==", "dev": true, "dependencies": { "@commitlint/format": "^19.5.0", "@commitlint/lint": "^19.7.1", "@commitlint/load": "^19.6.1", "@commitlint/read": "^19.5.0", "@commitlint/types": "^19.5.0", "tinyexec": "^0.3.0", "yargs": "^17.0.0" }, "bin": { "commitlint": "cli.js" }, "engines": { "node": ">=v18" } }, "node_modules/@commitlint/config-conventional": { "version": "19.7.1", "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-19.7.1.tgz", "integrity": "sha512-fsEIF8zgiI/FIWSnykdQNj/0JE4av08MudLTyYHm4FlLWemKoQvPNUYU2M/3tktWcCEyq7aOkDDgtjrmgWFbvg==", "dev": true, "dependencies": { "@commitlint/types": "^19.5.0", "conventional-changelog-conventionalcommits": "^7.0.2" }, "engines": { "node": ">=v18" } }, "node_modules/@commitlint/config-validator": { "version": "19.5.0", "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-19.5.0.tgz", "integrity": "sha512-CHtj92H5rdhKt17RmgALhfQt95VayrUo2tSqY9g2w+laAXyk7K/Ef6uPm9tn5qSIwSmrLjKaXK9eiNuxmQrDBw==", "dev": true, "dependencies": { "@commitlint/types": "^19.5.0", "ajv": "^8.11.0" }, "engines": { "node": ">=v18" } }, "node_modules/@commitlint/ensure": { "version": "19.5.0", "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-19.5.0.tgz", "integrity": "sha512-Kv0pYZeMrdg48bHFEU5KKcccRfKmISSm9MvgIgkpI6m+ohFTB55qZlBW6eYqh/XDfRuIO0x4zSmvBjmOwWTwkg==", "dev": true, "dependencies": { "@commitlint/types": "^19.5.0", "lodash.camelcase": "^4.3.0", "lodash.kebabcase": "^4.1.1", "lodash.snakecase": "^4.1.1", "lodash.startcase": "^4.4.0", "lodash.upperfirst": "^4.3.1" }, "engines": { "node": ">=v18" } }, "node_modules/@commitlint/execute-rule": { "version": "19.5.0", "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-19.5.0.tgz", "integrity": "sha512-aqyGgytXhl2ejlk+/rfgtwpPexYyri4t8/n4ku6rRJoRhGZpLFMqrZ+YaubeGysCP6oz4mMA34YSTaSOKEeNrg==", "dev": true, "engines": { "node": ">=v18" } }, "node_modules/@commitlint/format": { "version": "19.5.0", "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-19.5.0.tgz", "integrity": "sha512-yNy088miE52stCI3dhG/vvxFo9e4jFkU1Mj3xECfzp/bIS/JUay4491huAlVcffOoMK1cd296q0W92NlER6r3A==", "dev": true, "dependencies": { "@commitlint/types": "^19.5.0", "chalk": "^5.3.0" }, "engines": { "node": ">=v18" } }, "node_modules/@commitlint/format/node_modules/chalk": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/@commitlint/is-ignored": { "version": "19.7.1", "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-19.7.1.tgz", "integrity": "sha512-3IaOc6HVg2hAoGleRK3r9vL9zZ3XY0rf1RsUf6jdQLuaD46ZHnXBiOPTyQ004C4IvYjSWqJwlh0/u2P73aIE3g==", "dev": true, "dependencies": { "@commitlint/types": "^19.5.0", "semver": "^7.6.0" }, "engines": { "node": ">=v18" } }, "node_modules/@commitlint/lint": { "version": "19.7.1", "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-19.7.1.tgz", "integrity": "sha512-LhcPfVjcOcOZA7LEuBBeO00o3MeZa+tWrX9Xyl1r9PMd5FWsEoZI9IgnGqTKZ0lZt5pO3ZlstgnRyY1CJJc9Xg==", "dev": true, "dependencies": { "@commitlint/is-ignored": "^19.7.1", "@commitlint/parse": "^19.5.0", "@commitlint/rules": "^19.6.0", "@commitlint/types": "^19.5.0" }, "engines": { "node": ">=v18" } }, "node_modules/@commitlint/load": { "version": "19.6.1", "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-19.6.1.tgz", "integrity": "sha512-kE4mRKWWNju2QpsCWt428XBvUH55OET2N4QKQ0bF85qS/XbsRGG1MiTByDNlEVpEPceMkDr46LNH95DtRwcsfA==", "dev": true, "dependencies": { "@commitlint/config-validator": "^19.5.0", "@commitlint/execute-rule": "^19.5.0", "@commitlint/resolve-extends": "^19.5.0", "@commitlint/types": "^19.5.0", "chalk": "^5.3.0", "cosmiconfig": "^9.0.0", "cosmiconfig-typescript-loader": "^6.1.0", "lodash.isplainobject": "^4.0.6", "lodash.merge": "^4.6.2", "lodash.uniq": "^4.5.0" }, "engines": { "node": ">=v18" } }, "node_modules/@commitlint/load/node_modules/chalk": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/@commitlint/message": { "version": "19.5.0", "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-19.5.0.tgz", "integrity": "sha512-R7AM4YnbxN1Joj1tMfCyBryOC5aNJBdxadTZkuqtWi3Xj0kMdutq16XQwuoGbIzL2Pk62TALV1fZDCv36+JhTQ==", "dev": true, "engines": { "node": ">=v18" } }, "node_modules/@commitlint/parse": { "version": "19.5.0", "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-19.5.0.tgz", "integrity": "sha512-cZ/IxfAlfWYhAQV0TwcbdR1Oc0/r0Ik1GEessDJ3Lbuma/MRO8FRQX76eurcXtmhJC//rj52ZSZuXUg0oIX0Fw==", "dev": true, "dependencies": { "@commitlint/types": "^19.5.0", "conventional-changelog-angular": "^7.0.0", "conventional-commits-parser": "^5.0.0" }, "engines": { "node": ">=v18" } }, "node_modules/@commitlint/read": { "version": "19.5.0", "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-19.5.0.tgz", "integrity": "sha512-TjS3HLPsLsxFPQj6jou8/CZFAmOP2y+6V4PGYt3ihbQKTY1Jnv0QG28WRKl/d1ha6zLODPZqsxLEov52dhR9BQ==", "dev": true, "dependencies": { "@commitlint/top-level": "^19.5.0", "@commitlint/types": "^19.5.0", "git-raw-commits": "^4.0.0", "minimist": "^1.2.8", "tinyexec": "^0.3.0" }, "engines": { "node": ">=v18" } }, "node_modules/@commitlint/resolve-extends": { "version": "19.5.0", "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-19.5.0.tgz", "integrity": "sha512-CU/GscZhCUsJwcKTJS9Ndh3AKGZTNFIOoQB2n8CmFnizE0VnEuJoum+COW+C1lNABEeqk6ssfc1Kkalm4bDklA==", "dev": true, "dependencies": { "@commitlint/config-validator": "^19.5.0", "@commitlint/types": "^19.5.0", "global-directory": "^4.0.1", "import-meta-resolve": "^4.0.0", "lodash.mergewith": "^4.6.2", "resolve-from": "^5.0.0" }, "engines": { "node": ">=v18" } }, "node_modules/@commitlint/rules": { "version": "19.6.0", "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-19.6.0.tgz", "integrity": "sha512-1f2reW7lbrI0X0ozZMesS/WZxgPa4/wi56vFuJENBmed6mWq5KsheN/nxqnl/C23ioxpPO/PL6tXpiiFy5Bhjw==", "dev": true, "dependencies": { "@commitlint/ensure": "^19.5.0", "@commitlint/message": "^19.5.0", "@commitlint/to-lines": "^19.5.0", "@commitlint/types": "^19.5.0" }, "engines": { "node": ">=v18" } }, "node_modules/@commitlint/to-lines": { "version": "19.5.0", "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-19.5.0.tgz", "integrity": "sha512-R772oj3NHPkodOSRZ9bBVNq224DOxQtNef5Pl8l2M8ZnkkzQfeSTr4uxawV2Sd3ui05dUVzvLNnzenDBO1KBeQ==", "dev": true, "engines": { "node": ">=v18" } }, "node_modules/@commitlint/top-level": { "version": "19.5.0", "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-19.5.0.tgz", "integrity": "sha512-IP1YLmGAk0yWrImPRRc578I3dDUI5A2UBJx9FbSOjxe9sTlzFiwVJ+zeMLgAtHMtGZsC8LUnzmW1qRemkFU4ng==", "dev": true, "dependencies": { "find-up": "^7.0.0" }, "engines": { "node": ">=v18" } }, "node_modules/@commitlint/types": { "version": "19.5.0", "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-19.5.0.tgz", "integrity": "sha512-DSHae2obMSMkAtTBSOulg5X7/z+rGLxcXQIkg3OmWvY6wifojge5uVMydfhUvs7yQj+V7jNmRZ2Xzl8GJyqRgg==", "dev": true, "dependencies": { "@types/conventional-commits-parser": "^5.0.0", "chalk": "^5.3.0" }, "engines": { "node": ">=v18" } }, "node_modules/@commitlint/types/node_modules/chalk": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" }, "engines": { "node": ">= 8" } }, "node_modules/@nodelib/fs.stat": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, "engines": { "node": ">= 8" } }, "node_modules/@nodelib/fs.walk": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" }, "engines": { "node": ">= 8" } }, "node_modules/@octokit/auth-token": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.0.1.tgz", "integrity": "sha512-RTmWsLfig8SBoiSdgvCht4BXl1CHU89Co5xiQ5JF19my/sIRDFCQ1RPrmK0exgqUZuNm39C/bV8+/83+MJEjGg==", "dev": true, "engines": { "node": ">= 18" } }, "node_modules/@octokit/core": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.0.1.tgz", "integrity": "sha512-MIpPQXu8Y8GjHwXM81JLveiV+DHJZtLMcB5nKekBGOl3iAtk0HT3i12Xl8Biybu+bCS1+k4qbuKEq5d0RxNRnQ==", "dev": true, "dependencies": { "@octokit/auth-token": "^5.0.0", "@octokit/graphql": "^8.0.0", "@octokit/request": "^9.0.0", "@octokit/request-error": "^6.0.1", "@octokit/types": "^12.0.0", "before-after-hook": "^3.0.2", "universal-user-agent": "^7.0.0" }, "engines": { "node": ">= 18" } }, "node_modules/@octokit/endpoint": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.0.0.tgz", "integrity": "sha512-emBcNDxBdC1y3+knJonS5zhUB/CG6TihubxM2U1/pG/Z1y3a4oV0Gzz3lmkCvWWQI6h3tqBAX9MgCBFp+M68Jw==", "dev": true, "dependencies": { "@octokit/types": "^12.0.0", "universal-user-agent": "^7.0.2" }, "engines": { "node": ">= 18" } }, "node_modules/@octokit/graphql": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.0.1.tgz", "integrity": "sha512-lLDb6LhC1gBj2CxEDa5Xk10+H/boonhs+3Mi6jpRyetskDKNHe6crMeKmUE2efoLofMP8ruannLlCUgpTFmVzQ==", "dev": true, "dependencies": { "@octokit/request": "^9.0.0", "@octokit/types": "^12.0.0", "universal-user-agent": "^7.0.0" }, "engines": { "node": ">= 18" } }, "node_modules/@octokit/openapi-types": { "version": "20.0.0", "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz", "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==", "dev": true }, "node_modules/@octokit/plugin-paginate-rest": { "version": "11.3.5", "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.3.5.tgz", "integrity": "sha512-cgwIRtKrpwhLoBi0CUNuY83DPGRMaWVjqVI/bGKsLJ4PzyWZNaEmhHroI2xlrVXkk6nFv0IsZpOp+ZWSWUS2AQ==", "dev": true, "dependencies": { "@octokit/types": "^13.6.0" }, "engines": { "node": ">= 18" }, "peerDependencies": { "@octokit/core": ">=6" } }, "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": { "version": "22.2.0", "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz", "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==", "dev": true }, "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": { "version": "13.6.1", "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.6.1.tgz", "integrity": "sha512-PHZE9Z+kWXb23Ndik8MKPirBPziOc0D2/3KH1P+6jK5nGWe96kadZuE4jev2/Jq7FvIfTlT2Ltg8Fv2x1v0a5g==", "dev": true, "dependencies": { "@octokit/openapi-types": "^22.2.0" } }, "node_modules/@octokit/plugin-retry": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-7.0.3.tgz", "integrity": "sha512-T9l5Z7XnDZ7dkyNmhJPSUq0YjbqUT/xn4yQbhcSuv4WGC/LqM73/mKwkl68VDPoLw20e8oz4L7qQopWt9v6sow==", "dev": true, "dependencies": { "@octokit/request-error": "^6.0.0", "@octokit/types": "^12.0.0", "bottleneck": "^2.15.3" }, "engines": { "node": ">= 18" }, "peerDependencies": { "@octokit/core": ">=6" } }, "node_modules/@octokit/plugin-throttling": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-9.0.3.tgz", "integrity": "sha512-DReKamrLBJOzld73dmmxV2H137QKJfsxszAczEZXeAJQ/Po6bzQacKajPdodA6T1jfmP9+waImus+d/R2j+R7Q==", "dev": true, "dependencies": { "@octokit/types": "^12.6.0", "bottleneck": "^2.15.3" }, "engines": { "node": ">= 18" }, "peerDependencies": { "@octokit/core": "^6.0.0" } }, "node_modules/@octokit/request": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.0.1.tgz", "integrity": "sha512-kL+cAcbSl3dctYLuJmLfx6Iku2MXXy0jszhaEIjQNaCp4zjHXrhVAHeuaRdNvJjW9qjl3u1MJ72+OuBP0YW/pg==", "dev": true, "dependencies": { "@octokit/endpoint": "^10.0.0", "@octokit/request-error": "^6.0.1", "@octokit/types": "^12.0.0", "universal-user-agent": "^7.0.2" }, "engines": { "node": ">= 18" } }, "node_modules/@octokit/request-error": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.0.2.tgz", "integrity": "sha512-WtRVpoHcNXs84+s9s/wqfHaxM68NGMg8Av7h59B50OVO0PwwMx+2GgQ/OliUd0iQBSNWgR6N8afi/KjSHbXHWw==", "dev": true, "dependencies": { "@octokit/types": "^12.0.0" }, "engines": { "node": ">= 18" } }, "node_modules/@octokit/types": { "version": "12.6.0", "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz", "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", "dev": true, "dependencies": { "@octokit/openapi-types": "^20.0.0" } }, "node_modules/@pnpm/network.ca-file": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", "dev": true, "dependencies": { "graceful-fs": "4.2.10" }, "engines": { "node": ">=12.22.0" } }, "node_modules/@pnpm/npm-conf": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-1.0.5.tgz", "integrity": "sha512-hD8ml183638O3R6/Txrh0L8VzGOrFXgRtRDG4qQC4tONdZ5Z1M+tlUUDUvrjYdmK6G+JTBTeaCLMna11cXzi8A==", "dev": true, "dependencies": { "@pnpm/network.ca-file": "^1.0.1", "config-chain": "^1.1.11" }, "engines": { "node": ">=12" } }, "node_modules/@sec-ant/readable-stream": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", "dev": true }, "node_modules/@semantic-release/changelog": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/@semantic-release/changelog/-/changelog-6.0.3.tgz", "integrity": "sha512-dZuR5qByyfe3Y03TpmCvAxCyTnp7r5XwtHRf/8vD9EAn4ZWbavUX8adMtXYzE86EVh0gyLA7lm5yW4IV30XUag==", "dev": true, "dependencies": { "@semantic-release/error": "^3.0.0", "aggregate-error": "^3.0.0", "fs-extra": "^11.0.0", "lodash": "^4.17.4" }, "engines": { "node": ">=14.17" }, "peerDependencies": { "semantic-release": ">=18.0.0" } }, "node_modules/@semantic-release/commit-analyzer": { "version": "13.0.0", "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-13.0.0.tgz", "integrity": "sha512-KtXWczvTAB1ZFZ6B4O+w8HkfYm/OgQb1dUGNFZtDgQ0csggrmkq8sTxhd+lwGF8kMb59/RnG9o4Tn7M/I8dQ9Q==", "dev": true, "dependencies": { "conventional-changelog-angular": "^8.0.0", "conventional-changelog-writer": "^8.0.0", "conventional-commits-filter": "^5.0.0", "conventional-commits-parser": "^6.0.0", "debug": "^4.0.0", "import-from-esm": "^1.0.3", "lodash-es": "^4.17.21", "micromatch": "^4.0.2" }, "engines": { "node": ">=20.8.1" }, "peerDependencies": { "semantic-release": ">=20.1.0" } }, "node_modules/@semantic-release/commit-analyzer/node_modules/conventional-changelog-angular": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-8.0.0.tgz", "integrity": "sha512-CLf+zr6St0wIxos4bmaKHRXWAcsCXrJU6F4VdNDrGRK3B8LDLKoX3zuMV5GhtbGkVR/LohZ6MT6im43vZLSjmA==", "dev": true, "dependencies": { "compare-func": "^2.0.0" }, "engines": { "node": ">=18" } }, "node_modules/@semantic-release/commit-analyzer/node_modules/conventional-commits-parser": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.0.0.tgz", "integrity": "sha512-TbsINLp48XeMXR8EvGjTnKGsZqBemisPoyWESlpRyR8lif0lcwzqz+NMtYSj1ooF/WYjSuu7wX0CtdeeMEQAmA==", "dev": true, "dependencies": { "meow": "^13.0.0" }, "bin": { "conventional-commits-parser": "dist/cli/index.js" }, "engines": { "node": ">=18" } }, "node_modules/@semantic-release/commit-analyzer/node_modules/meow": { "version": "13.2.0", "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", "dev": true, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@semantic-release/error": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-3.0.0.tgz", "integrity": "sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw==", "dev": true, "engines": { "node": ">=14.17" } }, "node_modules/@semantic-release/exec": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/@semantic-release/exec/-/exec-7.0.3.tgz", "integrity": "sha512-uNWwPNtWi3WTcTm3fWfFQEuj8otOvwoS5m9yo2jSVHuvqdZNsOWmuL0/FqcVyZnCI32fxyYV0G7PPb/TzCH6jw==", "dev": true, "dependencies": { "@semantic-release/error": "^4.0.0", "aggregate-error": "^3.0.0", "debug": "^4.0.0", "execa": "^9.0.0", "lodash-es": "^4.17.21", "parse-json": "^8.0.0" }, "engines": { "node": ">=20.8.1" }, "peerDependencies": { "semantic-release": ">=24.1.0" } }, "node_modules/@semantic-release/exec/node_modules/@semantic-release/error": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz", "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==", "dev": true, "engines": { "node": ">=18" } }, "node_modules/@semantic-release/exec/node_modules/@sindresorhus/merge-streams": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", "dev": true, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@semantic-release/exec/node_modules/execa": { "version": "9.5.2", "resolved": "https://registry.npmjs.org/execa/-/execa-9.5.2.tgz", "integrity": "sha512-EHlpxMCpHWSAh1dgS6bVeoLAXGnJNdR93aabr4QCGbzOM73o5XmRfM/e5FUqsw3aagP8S8XEWUWFAxnRBnAF0Q==", "dev": true, "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.3", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.0", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.0.0" }, "engines": { "node": "^18.19.0 || >=20.5.0" }, "funding": { "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, "node_modules/@semantic-release/exec/node_modules/get-stream": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", "dev": true, "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@semantic-release/exec/node_modules/human-signals": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.0.tgz", "integrity": "sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA==", "dev": true, "engines": { "node": ">=18.18.0" } }, "node_modules/@semantic-release/exec/node_modules/is-stream": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", "dev": true, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@semantic-release/exec/node_modules/npm-run-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", "dev": true, "dependencies": { "path-key": "^4.0.0", "unicorn-magic": "^0.3.0" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@semantic-release/exec/node_modules/parse-json": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.1.0.tgz", "integrity": "sha512-rum1bPifK5SSar35Z6EKZuYPJx85pkNaFrxBK3mwdfSJ1/WKbYrjoW/zTPSjRRamfmVX1ACBIdFAO0VRErW/EA==", "dev": true, "dependencies": { "@babel/code-frame": "^7.22.13", "index-to-position": "^0.1.2", "type-fest": "^4.7.1" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@semantic-release/exec/node_modules/path-key": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@semantic-release/exec/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, "engines": { "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/@semantic-release/exec/node_modules/strip-final-newline": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", "dev": true, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@semantic-release/exec/node_modules/unicorn-magic": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", "dev": true, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@semantic-release/git": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/@semantic-release/git/-/git-10.0.1.tgz", "integrity": "sha512-eWrx5KguUcU2wUPaO6sfvZI0wPafUKAMNC18aXY4EnNcrZL86dEmpNVnC9uMpGZkmZJ9EfCVJBQx4pV4EMGT1w==", "dev": true, "dependencies": { "@semantic-release/error": "^3.0.0", "aggregate-error": "^3.0.0", "debug": "^4.0.0", "dir-glob": "^3.0.0", "execa": "^5.0.0", "lodash": "^4.17.4", "micromatch": "^4.0.0", "p-reduce": "^2.0.0" }, "engines": { "node": ">=14.17" }, "peerDependencies": { "semantic-release": ">=18.0.0" } }, "node_modules/@semantic-release/github": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-11.0.0.tgz", "integrity": "sha512-Uon6G6gJD8U1JNvPm7X0j46yxNRJ8Ui6SgK4Zw5Ktu8RgjEft3BGn+l/RX1TTzhhO3/uUcKuqM+/9/ETFxWS/Q==", "dev": true, "dependencies": { "@octokit/core": "^6.0.0", "@octokit/plugin-paginate-rest": "^11.0.0", "@octokit/plugin-retry": "^7.0.0", "@octokit/plugin-throttling": "^9.0.0", "@semantic-release/error": "^4.0.0", "aggregate-error": "^5.0.0", "debug": "^4.3.4", "dir-glob": "^3.0.1", "globby": "^14.0.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", "issue-parser": "^7.0.0", "lodash-es": "^4.17.21", "mime": "^4.0.0", "p-filter": "^4.0.0", "url-join": "^5.0.0" }, "engines": { "node": ">=20.8.1" }, "peerDependencies": { "semantic-release": ">=24.1.0" } }, "node_modules/@semantic-release/github/node_modules/@semantic-release/error": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz", "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==", "dev": true, "engines": { "node": ">=18" } }, "node_modules/@semantic-release/github/node_modules/aggregate-error": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz", "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==", "dev": true, "dependencies": { "clean-stack": "^5.2.0", "indent-string": "^5.0.0" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@semantic-release/github/node_modules/clean-stack": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.2.0.tgz", "integrity": "sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==", "dev": true, "dependencies": { "escape-string-regexp": "5.0.0" }, "engines": { "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@semantic-release/github/node_modules/indent-string": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", "dev": true, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@semantic-release/npm": { "version": "12.0.0", "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-12.0.0.tgz", "integrity": "sha512-72TVYQCH9NvVsO/y13eF8vE4bNnfls518+4KcFwJUKi7AtA/ZXoNgSg9gTTfw5eMZMkiH0izUrpGXgZE/cSQhA==", "dev": true, "dependencies": { "@semantic-release/error": "^4.0.0", "aggregate-error": "^5.0.0", "execa": "^8.0.0", "fs-extra": "^11.0.0", "lodash-es": "^4.17.21", "nerf-dart": "^1.0.0", "normalize-url": "^8.0.0", "npm": "^10.5.0", "rc": "^1.2.8", "read-pkg": "^9.0.0", "registry-auth-token": "^5.0.0", "semver": "^7.1.2", "tempy": "^3.0.0" }, "engines": { "node": ">=20.8.1" }, "peerDependencies": { "semantic-release": ">=20.1.0" } }, "node_modules/@semantic-release/npm/node_modules/@semantic-release/error": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz", "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==", "dev": true, "engines": { "node": ">=18" } }, "node_modules/@semantic-release/npm/node_modules/aggregate-error": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz", "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==", "dev": true, "dependencies": { "clean-stack": "^5.2.0", "indent-string": "^5.0.0" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@semantic-release/npm/node_modules/clean-stack": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.2.0.tgz", "integrity": "sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==", "dev": true, "dependencies": { "escape-string-regexp": "5.0.0" }, "engines": { "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@semantic-release/npm/node_modules/execa": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" }, "engines": { "node": ">=16.17" }, "funding": { "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, "node_modules/@semantic-release/npm/node_modules/get-stream": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", "dev": true, "engines": { "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@semantic-release/npm/node_modules/human-signals": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", "dev": true, "engines": { "node": ">=16.17.0" } }, "node_modules/@semantic-release/npm/node_modules/indent-string": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", "dev": true, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@semantic-release/npm/node_modules/is-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@semantic-release/npm/node_modules/mimic-fn": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", "dev": true, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@semantic-release/npm/node_modules/npm-run-path": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", "dev": true, "dependencies": { "path-key": "^4.0.0" }, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@semantic-release/npm/node_modules/onetime": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", "dev": true, "dependencies": { "mimic-fn": "^4.0.0" }, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@semantic-release/npm/node_modules/path-key": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@semantic-release/npm/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, "engines": { "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/@semantic-release/npm/node_modules/strip-final-newline": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", "dev": true, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@semantic-release/release-notes-generator": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-14.0.0.tgz", "integrity": "sha512-XRxwr4e46yUMaXT8KGFBlRJlp5+NOMaufdq8qaEWlcJ7cT4Pn/iRmDGglZ2TgDe6GVP+u1boXFEnSs7N8Yzhng==", "dev": true, "dependencies": { "conventional-changelog-angular": "^8.0.0", "conventional-changelog-writer": "^8.0.0", "conventional-commits-filter": "^5.0.0", "conventional-commits-parser": "^6.0.0", "debug": "^4.0.0", "get-stream": "^7.0.0", "import-from-esm": "^1.0.3", "into-stream": "^7.0.0", "lodash-es": "^4.17.21", "read-pkg-up": "^11.0.0" }, "engines": { "node": ">=20.8.1" }, "peerDependencies": { "semantic-release": ">=20.1.0" } }, "node_modules/@semantic-release/release-notes-generator/node_modules/conventional-changelog-angular": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-8.0.0.tgz", "integrity": "sha512-CLf+zr6St0wIxos4bmaKHRXWAcsCXrJU6F4VdNDrGRK3B8LDLKoX3zuMV5GhtbGkVR/LohZ6MT6im43vZLSjmA==", "dev": true, "dependencies": { "compare-func": "^2.0.0" }, "engines": { "node": ">=18" } }, "node_modules/@semantic-release/release-notes-generator/node_modules/conventional-commits-parser": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.0.0.tgz", "integrity": "sha512-TbsINLp48XeMXR8EvGjTnKGsZqBemisPoyWESlpRyR8lif0lcwzqz+NMtYSj1ooF/WYjSuu7wX0CtdeeMEQAmA==", "dev": true, "dependencies": { "meow": "^13.0.0" }, "bin": { "conventional-commits-parser": "dist/cli/index.js" }, "engines": { "node": ">=18" } }, "node_modules/@semantic-release/release-notes-generator/node_modules/get-stream": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-7.0.1.tgz", "integrity": "sha512-3M8C1EOFN6r8AMUhwUAACIoXZJEOufDU5+0gFFN5uNs6XYOralD2Pqkl7m046va6x77FwposWXbAhPPIOus7mQ==", "dev": true, "engines": { "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@semantic-release/release-notes-generator/node_modules/meow": { "version": "13.2.0", "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", "dev": true, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@sindresorhus/is": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", "dev": true, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sindresorhus/is?sponsor=1" } }, "node_modules/@sindresorhus/merge-streams": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", "dev": true, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@types/conventional-commits-parser": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@types/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", "integrity": "sha512-loB369iXNmAZglwWATL+WRe+CRMmmBPtpolYzIebFaX4YA3x+BEfLqhUAV9WanycKI3TG1IMr5bMJDajDKLlUQ==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/node": { "version": "18.18.7", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.7.tgz", "integrity": "sha512-bw+lEsxis6eqJYW8Ql6+yTqkE6RuFtsQPSe5JxXbqYRFQEER5aJA9a5UH9igqDWm3X4iLHIKOHlnAXLM4mi7uQ==", "dev": true, "dependencies": { "undici-types": "~5.26.4" } }, "node_modules/@types/normalize-package-data": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.3.tgz", "integrity": "sha512-ehPtgRgaULsFG8x0NeYJvmyH1hmlfsNLujHe9dQEia/7MAJYdzMSi19JtchUHjmBA6XC/75dK55mzZH+RyieSg==", "dev": true }, "node_modules/@types/semver": { "version": "7.5.8", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, "node_modules/agent-base": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", "dev": true, "dependencies": { "debug": "^4.3.4" }, "engines": { "node": ">= 14" } }, "node_modules/aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/ajv": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" }, "funding": { "type": "github", "url": "https://github.com/sponsors/epoberezkin" } }, "node_modules/ansi-escapes": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.0.tgz", "integrity": "sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==", "dev": true, "dependencies": { "type-fest": "^3.0.0" }, "engines": { "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/ansi-escapes/node_modules/type-fest": { "version": "3.13.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", "dev": true, "engines": { "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { "color-convert": "^2.0.1" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", "dev": true }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, "node_modules/argv-formatter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/argv-formatter/-/argv-formatter-1.0.0.tgz", "integrity": "sha512-F2+Hkm9xFaRg+GkaNnbwXNDV5O6pnCFEmqyhvfC/Ic5LbgOWjJh3L+mN/s91rxVL3znE7DYVpW0GJFT+4YBgWw==", "dev": true }, "node_modules/array-ify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", "dev": true }, "node_modules/before-after-hook": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz", "integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==", "dev": true }, "node_modules/bottleneck": { "version": "2.19.5", "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", "dev": true }, "node_modules/braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { "fill-range": "^7.1.1" }, "engines": { "node": ">=8" } }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/char-regex": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true, "engines": { "node": ">=10" } }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/cli-highlight": { "version": "2.1.11", "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", "dev": true, "dependencies": { "chalk": "^4.0.0", "highlight.js": "^10.7.1", "mz": "^2.4.0", "parse5": "^5.1.1", "parse5-htmlparser2-tree-adapter": "^6.0.0", "yargs": "^16.0.0" }, "bin": { "highlight": "bin/highlight" }, "engines": { "node": ">=8.0.0", "npm": ">=5.0.0" } }, "node_modules/cli-highlight/node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" } }, "node_modules/cli-highlight/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/cli-table3": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", "dev": true, "dependencies": { "string-width": "^4.2.0" }, "engines": { "node": "10.* || >= 12.*" }, "optionalDependencies": { "@colors/colors": "1.5.0" } }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" }, "engines": { "node": ">=12" } }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { "color-name": "~1.1.4" }, "engines": { "node": ">=7.0.0" } }, "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, "node_modules/compare-func": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", "dev": true, "dependencies": { "array-ify": "^1.0.0", "dot-prop": "^5.1.0" } }, "node_modules/config-chain": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", "dev": true, "dependencies": { "ini": "^1.3.4", "proto-list": "~1.2.1" } }, "node_modules/conventional-changelog-angular": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz", "integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==", "dev": true, "dependencies": { "compare-func": "^2.0.0" }, "engines": { "node": ">=16" } }, "node_modules/conventional-changelog-conventionalcommits": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-7.0.2.tgz", "integrity": "sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==", "dev": true, "dependencies": { "compare-func": "^2.0.0" }, "engines": { "node": ">=16" } }, "node_modules/conventional-changelog-writer": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-8.0.0.tgz", "integrity": "sha512-TQcoYGRatlAnT2qEWDON/XSfnVG38JzA7E0wcGScu7RElQBkg9WWgZd1peCWFcWDh1xfb2CfsrcvOn1bbSzztA==", "dev": true, "dependencies": { "@types/semver": "^7.5.5", "conventional-commits-filter": "^5.0.0", "handlebars": "^4.7.7", "meow": "^13.0.0", "semver": "^7.5.2" }, "bin": { "conventional-changelog-writer": "dist/cli/index.js" }, "engines": { "node": ">=18" } }, "node_modules/conventional-changelog-writer/node_modules/meow": { "version": "13.2.0", "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", "dev": true, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/conventional-commits-filter": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-5.0.0.tgz", "integrity": "sha512-tQMagCOC59EVgNZcC5zl7XqO30Wki9i9J3acbUvkaosCT6JX3EeFwJD7Qqp4MCikRnzS18WXV3BLIQ66ytu6+Q==", "dev": true, "engines": { "node": ">=18" } }, "node_modules/conventional-commits-parser": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==", "dev": true, "dependencies": { "is-text-path": "^2.0.0", "JSONStream": "^1.3.5", "meow": "^12.0.1", "split2": "^4.0.0" }, "bin": { "conventional-commits-parser": "cli.mjs" }, "engines": { "node": ">=16" } }, "node_modules/convert-hrtime": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/convert-hrtime/-/convert-hrtime-5.0.0.tgz", "integrity": "sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==", "dev": true, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "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/cosmiconfig": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", "dev": true, "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0" }, "engines": { "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/d-fischer" }, "peerDependencies": { "typescript": ">=4.9.5" }, "peerDependenciesMeta": { "typescript": { "optional": true } } }, "node_modules/cosmiconfig-typescript-loader": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.1.0.tgz", "integrity": "sha512-tJ1w35ZRUiM5FeTzT7DtYWAFFv37ZLqSRkGi2oeCK1gPhvaWjkAtfXvLmvE1pRfxxp9aQo6ba/Pvg1dKj05D4g==", "dev": true, "dependencies": { "jiti": "^2.4.1" }, "engines": { "node": ">=v18" }, "peerDependencies": { "@types/node": "*", "cosmiconfig": ">=9", "typescript": ">=5" } }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" }, "engines": { "node": ">= 8" } }, "node_modules/crypto-random-string": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", "dev": true, "dependencies": { "type-fest": "^1.0.1" }, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/crypto-random-string/node_modules/type-fest": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", "dev": true, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/dargs": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/dargs/-/dargs-8.1.0.tgz", "integrity": "sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==", "dev": true, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "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/deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true, "engines": { "node": ">=4.0.0" } }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, "dependencies": { "path-type": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/dot-prop": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", "dev": true, "dependencies": { "is-obj": "^2.0.0" }, "engines": { "node": ">=8" } }, "node_modules/duplexer2": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", "dev": true, "dependencies": { "readable-stream": "^2.0.2" } }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, "node_modules/emojilib": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==", "dev": true }, "node_modules/env-ci": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-11.0.0.tgz", "integrity": "sha512-apikxMgkipkgTvMdRT9MNqWx5VLOci79F4VBd7Op/7OPjjoanjdAvn6fglMCCEf/1bAh8eOiuEVCUs4V3qP3nQ==", "dev": true, "dependencies": { "execa": "^8.0.0", "java-properties": "^1.0.2" }, "engines": { "node": "^18.17 || >=20.6.1" } }, "node_modules/env-ci/node_modules/execa": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" }, "engines": { "node": ">=16.17" }, "funding": { "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, "node_modules/env-ci/node_modules/get-stream": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", "dev": true, "engines": { "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/env-ci/node_modules/human-signals": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", "dev": true, "engines": { "node": ">=16.17.0" } }, "node_modules/env-ci/node_modules/is-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/env-ci/node_modules/mimic-fn": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", "dev": true, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/env-ci/node_modules/npm-run-path": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", "dev": true, "dependencies": { "path-key": "^4.0.0" }, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/env-ci/node_modules/onetime": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", "dev": true, "dependencies": { "mimic-fn": "^4.0.0" }, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/env-ci/node_modules/path-key": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/env-ci/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, "engines": { "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/env-ci/node_modules/strip-final-newline": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", "dev": true, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/env-paths": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, "dependencies": { "is-arrayish": "^0.2.1" } }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/escape-string-regexp": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", "dev": true, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, "node_modules/fast-glob": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" }, "engines": { "node": ">=8.6.0" } }, "node_modules/fast-uri": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==", "dev": true }, "node_modules/fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dev": true, "dependencies": { "reusify": "^1.0.4" } }, "node_modules/figures": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", "dev": true, "dependencies": { "is-unicode-supported": "^2.0.0" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/find-up": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz", "integrity": "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==", "dev": true, "dependencies": { "locate-path": "^7.2.0", "path-exists": "^5.0.0", "unicorn-magic": "^0.1.0" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/find-up-simple": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.0.tgz", "integrity": "sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw==", "dev": true, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/find-versions": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-6.0.0.tgz", "integrity": "sha512-2kCCtc+JvcZ86IGAz3Z2Y0A1baIz9fL31pH/0S1IqZr9Iwnjq8izfPtrCyQKO6TLMPELLsQMre7VDqeIKCsHkA==", "dev": true, "dependencies": { "semver-regex": "^4.0.5", "super-regex": "^1.0.0" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/from2": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", "dev": true, "dependencies": { "inherits": "^2.0.1", "readable-stream": "^2.0.0" } }, "node_modules/fs-extra": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz", "integrity": "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==", "dev": true, "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" }, "engines": { "node": ">=14.14" } }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, "node_modules/function-timeout": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/function-timeout/-/function-timeout-1.0.1.tgz", "integrity": "sha512-6yPMImFFuaMPNaTMTBuolA8EanHJWF5Vju0NHpObRURT105J6x1Mf2a7J4P7Sqk2xDxv24N5L0RatEhTBhNmdA==", "dev": true, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "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-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/git-log-parser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/git-log-parser/-/git-log-parser-1.2.0.tgz", "integrity": "sha512-rnCVNfkTL8tdNryFuaY0fYiBWEBcgF748O6ZI61rslBvr2o7U65c2/6npCRqH40vuAhtgtDiqLTJjBVdrejCzA==", "dev": true, "dependencies": { "argv-formatter": "~1.0.0", "spawn-error-forwarder": "~1.0.0", "split2": "~1.0.0", "stream-combiner2": "~1.1.1", "through2": "~2.0.0", "traverse": "~0.6.6" } }, "node_modules/git-log-parser/node_modules/split2": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/split2/-/split2-1.0.0.tgz", "integrity": "sha512-NKywug4u4pX/AZBB1FCPzZ6/7O+Xhz1qMVbzTvvKvikjO99oPN87SkK08mEY9P63/5lWjK+wgOOgApnTg5r6qg==", "dev": true, "dependencies": { "through2": "~2.0.0" } }, "node_modules/git-log-parser/node_modules/through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, "dependencies": { "readable-stream": "~2.3.6", "xtend": "~4.0.1" } }, "node_modules/git-raw-commits": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-4.0.0.tgz", "integrity": "sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==", "dev": true, "dependencies": { "dargs": "^8.0.0", "meow": "^12.0.1", "split2": "^4.0.0" }, "bin": { "git-raw-commits": "cli.mjs" }, "engines": { "node": ">=16" } }, "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/global-directory": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", "dev": true, "dependencies": { "ini": "4.1.1" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/global-directory/node_modules/ini": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", "dev": true, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/globby": { "version": "14.0.1", "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.1.tgz", "integrity": "sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ==", "dev": true, "dependencies": { "@sindresorhus/merge-streams": "^2.1.0", "fast-glob": "^3.3.2", "ignore": "^5.2.4", "path-type": "^5.0.0", "slash": "^5.1.0", "unicorn-magic": "^0.1.0" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/globby/node_modules/path-type": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==", "dev": true, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, "node_modules/handlebars": { "version": "4.7.8", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", "dev": true, "dependencies": { "minimist": "^1.2.5", "neo-async": "^2.6.2", "source-map": "^0.6.1", "wordwrap": "^1.0.0" }, "bin": { "handlebars": "bin/handlebars" }, "engines": { "node": ">=0.4.7" }, "optionalDependencies": { "uglify-js": "^3.1.4" } }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "dependencies": { "function-bind": "^1.1.1" }, "engines": { "node": ">= 0.4.0" } }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/highlight.js": { "version": "10.7.3", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", "dev": true, "engines": { "node": "*" } }, "node_modules/hook-std": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-3.0.0.tgz", "integrity": "sha512-jHRQzjSDzMtFy34AGj1DN+vq54WVuhSvKgrHf0OMiFQTwDD4L/qqofVEWjLOBMTn5+lCD3fPg32W9yOfnEJTTw==", "dev": true, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/hosted-git-info": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.1.tgz", "integrity": "sha512-+K84LB1DYwMHoHSgaOY/Jfhw3ucPmSET5v98Ke/HdNSw4a0UktWzyW1mjhjpuxxTqOOsfWT/7iVshHmVZ4IpOA==", "dev": true, "dependencies": { "lru-cache": "^10.0.1" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/hosted-git-info/node_modules/lru-cache": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", "dev": true, "engines": { "node": "14 || >=16.14" } }, "node_modules/http-proxy-agent": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", "dev": true, "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" }, "engines": { "node": ">= 14" } }, "node_modules/https-proxy-agent": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.0.tgz", "integrity": "sha512-0euwPCRyAPSgGdzD1IVN9nJYHtBhJwb6XPfbpQcYbPCwrBidX6GzxmchnaF4sfF/jPb74Ojx5g4yTg3sixlyPw==", "dev": true, "dependencies": { "agent-base": "^7.0.2", "debug": "4" }, "engines": { "node": ">= 14" } }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true, "engines": { "node": ">=10.17.0" } }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true, "engines": { "node": ">= 4" } }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" }, "engines": { "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/import-fresh/node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/import-from-esm": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/import-from-esm/-/import-from-esm-1.3.3.tgz", "integrity": "sha512-U3Qt/CyfFpTUv6LOP2jRTLYjphH6zg3okMfHbyqRa/W2w6hr8OsJWVggNlR4jxuojQy81TgTJTxgSkyoteRGMQ==", "dev": true, "dependencies": { "debug": "^4.3.4", "import-meta-resolve": "^4.0.0" }, "engines": { "node": ">=16.20" } }, "node_modules/import-meta-resolve": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.0.0.tgz", "integrity": "sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA==", "dev": true, "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, "node_modules/indent-string": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/index-to-position": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-0.1.2.tgz", "integrity": "sha512-MWDKS3AS1bGCHLBA2VLImJz42f7bJh8wQsTGCzI3j519/CASStoDONUBVz2I/VID0MpiX3SGSnbOD2xUalbE5g==", "dev": true, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, "node_modules/ini": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, "node_modules/into-stream": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-7.0.0.tgz", "integrity": "sha512-2dYz766i9HprMBasCMvHMuazJ7u4WzhJwo5kb3iPSiW/iRYV6uPari3zHoqZlnuaR7V1bEiNMxikhp37rdBXbw==", "dev": true, "dependencies": { "from2": "^2.3.0", "p-is-promise": "^3.0.0" }, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, "node_modules/is-core-module": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", "dev": true, "dependencies": { "has": "^1.0.3" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, "engines": { "node": ">=0.10.0" } }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, "engines": { "node": ">=0.12.0" } }, "node_modules/is-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/is-plain-obj": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", "dev": true, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-text-path": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz", "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==", "dev": true, "dependencies": { "text-extensions": "^2.0.0" }, "engines": { "node": ">=8" } }, "node_modules/is-unicode-supported": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.0.0.tgz", "integrity": "sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q==", "dev": true, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "dev": true }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "node_modules/issue-parser": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-7.0.0.tgz", "integrity": "sha512-jgAw78HO3gs9UrKqJNQvfDj9Ouy8Mhu40fbEJ8yXff4MW8+/Fcn9iFjyWUQ6SKbX8ipPk3X5A3AyfYHRu6uVLw==", "dev": true, "dependencies": { "lodash.capitalize": "^4.2.1", "lodash.escaperegexp": "^4.1.2", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.uniqby": "^4.7.0" }, "engines": { "node": "^18.17 || >=20.6.1" } }, "node_modules/java-properties": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/java-properties/-/java-properties-1.0.2.tgz", "integrity": "sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ==", "dev": true, "engines": { "node": ">= 0.6.0" } }, "node_modules/jiti": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.1.tgz", "integrity": "sha512-yPBThwecp1wS9DmoA4x4KR2h3QoslacnDR8ypuFM962kI4/456Iy1oHx2RAgh4jfZNdn0bctsdadceiBUgpU1g==", "dev": true, "bin": { "jiti": "lib/jiti-cli.mjs" } }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "node_modules/json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "dev": true }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, "node_modules/jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "node_modules/jsonparse": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", "dev": true, "engines": [ "node >= 0.2.0" ] }, "node_modules/JSONStream": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", "dev": true, "dependencies": { "jsonparse": "^1.2.0", "through": ">=2.2.7 <3" }, "bin": { "JSONStream": "bin.js" }, "engines": { "node": "*" } }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, "node_modules/load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", "dev": true, "dependencies": { "graceful-fs": "^4.1.2", "parse-json": "^4.0.0", "pify": "^3.0.0", "strip-bom": "^3.0.0" }, "engines": { "node": ">=4" } }, "node_modules/load-json-file/node_modules/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", "dev": true, "dependencies": { "error-ex": "^1.3.1", "json-parse-better-errors": "^1.0.1" }, "engines": { "node": ">=4" } }, "node_modules/locate-path": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", "dev": true, "dependencies": { "p-locate": "^6.0.0" }, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, "node_modules/lodash-es": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", "dev": true }, "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", "dev": true }, "node_modules/lodash.capitalize": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", "integrity": "sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw==", "dev": true }, "node_modules/lodash.escaperegexp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", "dev": true }, "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", "dev": true }, "node_modules/lodash.isstring": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", "dev": true }, "node_modules/lodash.kebabcase": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", "dev": true }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, "node_modules/lodash.mergewith": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", "dev": true }, "node_modules/lodash.snakecase": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", "dev": true }, "node_modules/lodash.startcase": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", "dev": true }, "node_modules/lodash.uniq": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", "dev": true }, "node_modules/lodash.uniqby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==", "dev": true }, "node_modules/lodash.upperfirst": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==", "dev": true }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "dependencies": { "yallist": "^4.0.0" }, "engines": { "node": ">=10" } }, "node_modules/marked": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/marked/-/marked-12.0.1.tgz", "integrity": "sha512-Y1/V2yafOcOdWQCX0XpAKXzDakPOpn6U0YLxTJs3cww6VxOzZV1BTOOYWLvH3gX38cq+iLwljHHTnMtlDfg01Q==", "dev": true, "bin": { "marked": "bin/marked.js" }, "engines": { "node": ">= 18" } }, "node_modules/marked-terminal": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-7.0.0.tgz", "integrity": "sha512-sNEx8nn9Ktcm6pL0TnRz8tnXq/mSS0Q1FRSwJOAqw4lAB4l49UeDf85Gm1n9RPFm5qurCPjwi1StAQT2XExhZw==", "dev": true, "dependencies": { "ansi-escapes": "^6.2.0", "chalk": "^5.3.0", "cli-highlight": "^2.1.11", "cli-table3": "^0.6.3", "node-emoji": "^2.1.3", "supports-hyperlinks": "^3.0.0" }, "engines": { "node": ">=16.0.0" }, "peerDependencies": { "marked": ">=1 <13" } }, "node_modules/marked-terminal/node_modules/chalk": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/meow": { "version": "12.1.1", "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", "dev": true, "engines": { "node": ">=16.10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, "engines": { "node": ">= 8" } }, "node_modules/micromatch": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "dependencies": { "braces": "^3.0.2", "picomatch": "^2.3.1" }, "engines": { "node": ">=8.6" } }, "node_modules/mime": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.1.tgz", "integrity": "sha512-5lZ5tyrIfliMXzFtkYyekWbtRXObT9OWa8IwQ5uxTBDHucNNwniRqo0yInflj+iYi5CBa6qxadGzGarDfuEOxA==", "dev": true, "funding": [ "https://github.com/sponsors/broofa" ], "bin": { "mime": "bin/cli.js" }, "engines": { "node": ">=16" } }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, "engines": { "node": ">=6" } }, "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/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/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", "dev": true, "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, "node_modules/nerf-dart": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/nerf-dart/-/nerf-dart-1.0.0.tgz", "integrity": "sha512-EZSPZB70jiVsivaBLYDCyntd5eH8NTSMOn3rB+HxwdmKThGELLdYv8qVIMWvZEFy9w8ZZpW9h9OB32l1rGtj7g==", "dev": true }, "node_modules/node-emoji": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.1.3.tgz", "integrity": "sha512-E2WEOVsgs7O16zsURJ/eH8BqhF029wGpEOnv7Urwdo2wmQanOACwJQh0devF9D9RhoZru0+9JXIS0dBXIAz+lA==", "dev": true, "dependencies": { "@sindresorhus/is": "^4.6.0", "char-regex": "^1.0.2", "emojilib": "^2.4.0", "skin-tone": "^2.0.0" }, "engines": { "node": ">=18" } }, "node_modules/normalize-package-data": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.0.tgz", "integrity": "sha512-UL7ELRVxYBHBgYEtZCXjxuD5vPxnmvMGq0jp/dGPKKrN7tfsBh2IY7TlJ15WWwdjRWD3RJbnsygUurTK3xkPkg==", "dev": true, "dependencies": { "hosted-git-info": "^7.0.0", "is-core-module": "^2.8.1", "semver": "^7.3.5", "validate-npm-package-license": "^3.0.4" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/normalize-url": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz", "integrity": "sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==", "dev": true, "engines": { "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/npm": { "version": "10.5.0", "resolved": "https://registry.npmjs.org/npm/-/npm-10.5.0.tgz", "integrity": "sha512-Ejxwvfh9YnWVU2yA5FzoYLTW52vxHCz+MHrOFg9Cc8IFgF/6f5AGPAvb5WTay5DIUP1NIfN3VBZ0cLlGO0Ys+A==", "bundleDependencies": [ "@isaacs/string-locale-compare", "@npmcli/arborist", "@npmcli/config", "@npmcli/fs", "@npmcli/map-workspaces", "@npmcli/package-json", "@npmcli/promise-spawn", "@npmcli/run-script", "@sigstore/tuf", "abbrev", "archy", "cacache", "chalk", "ci-info", "cli-columns", "cli-table3", "columnify", "fastest-levenshtein", "fs-minipass", "glob", "graceful-fs", "hosted-git-info", "ini", "init-package-json", "is-cidr", "json-parse-even-better-errors", "libnpmaccess", "libnpmdiff", "libnpmexec", "libnpmfund", "libnpmhook", "libnpmorg", "libnpmpack", "libnpmpublish", "libnpmsearch", "libnpmteam", "libnpmversion", "make-fetch-happen", "minimatch", "minipass", "minipass-pipeline", "ms", "node-gyp", "nopt", "normalize-package-data", "npm-audit-report", "npm-install-checks", "npm-package-arg", "npm-pick-manifest", "npm-profile", "npm-registry-fetch", "npm-user-validate", "npmlog", "p-map", "pacote", "parse-conflict-json", "proc-log", "qrcode-terminal", "read", "semver", "spdx-expression-parse", "ssri", "supports-color", "tar", "text-table", "tiny-relative-date", "treeverse", "validate-npm-package-name", "which", "write-file-atomic" ], "dev": true, "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", "@npmcli/arborist": "^7.2.1", "@npmcli/config": "^8.0.2", "@npmcli/fs": "^3.1.0", "@npmcli/map-workspaces": "^3.0.4", "@npmcli/package-json": "^5.0.0", "@npmcli/promise-spawn": "^7.0.1", "@npmcli/run-script": "^7.0.4", "@sigstore/tuf": "^2.3.1", "abbrev": "^2.0.0", "archy": "~1.0.0", "cacache": "^18.0.2", "chalk": "^5.3.0", "ci-info": "^4.0.0", "cli-columns": "^4.0.0", "cli-table3": "^0.6.3", "columnify": "^1.6.0", "fastest-levenshtein": "^1.0.16", "fs-minipass": "^3.0.3", "glob": "^10.3.10", "graceful-fs": "^4.2.11", "hosted-git-info": "^7.0.1", "ini": "^4.1.1", "init-package-json": "^6.0.0", "is-cidr": "^5.0.3", "json-parse-even-better-errors": "^3.0.1", "libnpmaccess": "^8.0.1", "libnpmdiff": "^6.0.3", "libnpmexec": "^7.0.4", "libnpmfund": "^5.0.1", "libnpmhook": "^10.0.0", "libnpmorg": "^6.0.1", "libnpmpack": "^6.0.3", "libnpmpublish": "^9.0.2", "libnpmsearch": "^7.0.0", "libnpmteam": "^6.0.0", "libnpmversion": "^5.0.1", "make-fetch-happen": "^13.0.0", "minimatch": "^9.0.3", "minipass": "^7.0.4", "minipass-pipeline": "^1.2.4", "ms": "^2.1.2", "node-gyp": "^10.0.1", "nopt": "^7.2.0", "normalize-package-data": "^6.0.0", "npm-audit-report": "^5.0.0", "npm-install-checks": "^6.3.0", "npm-package-arg": "^11.0.1", "npm-pick-manifest": "^9.0.0", "npm-profile": "^9.0.0", "npm-registry-fetch": "^16.1.0", "npm-user-validate": "^2.0.0", "npmlog": "^7.0.1", "p-map": "^4.0.0", "pacote": "^17.0.6", "parse-conflict-json": "^3.0.1", "proc-log": "^3.0.0", "qrcode-terminal": "^0.12.0", "read": "^2.1.0", "semver": "^7.6.0", "spdx-expression-parse": "^3.0.1", "ssri": "^10.0.5", "supports-color": "^9.4.0", "tar": "^6.2.0", "text-table": "~0.2.0", "tiny-relative-date": "^1.3.0", "treeverse": "^3.0.0", "validate-npm-package-name": "^5.0.0", "which": "^4.0.0", "write-file-atomic": "^5.0.1" }, "bin": { "npm": "bin/npm-cli.js", "npx": "bin/npx-cli.js" }, "engines": { "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, "dependencies": { "path-key": "^3.0.0" }, "engines": { "node": ">=8" } }, "node_modules/npm/node_modules/@colors/colors": { "version": "1.5.0", "dev": true, "inBundle": true, "license": "MIT", "optional": true, "engines": { "node": ">=0.1.90" } }, "node_modules/npm/node_modules/@isaacs/cliui": { "version": "8.0.2", "dev": true, "inBundle": true, "license": "ISC", "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": ">=12" } }, "node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { "version": "6.0.1", "dev": true, "inBundle": true, "license": "MIT", "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, "node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": { "version": "9.2.2", "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", "dev": true, "inBundle": true, "license": "MIT", "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/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { "version": "7.1.0", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" }, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/npm/node_modules/@isaacs/string-locale-compare": { "version": "1.1.0", "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/@npmcli/agent": { "version": "2.2.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "agent-base": "^7.1.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.1", "lru-cache": "^10.0.1", "socks-proxy-agent": "^8.0.1" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@npmcli/arborist": { "version": "7.4.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", "@npmcli/fs": "^3.1.0", "@npmcli/installed-package-contents": "^2.0.2", "@npmcli/map-workspaces": "^3.0.2", "@npmcli/metavuln-calculator": "^7.0.0", "@npmcli/name-from-folder": "^2.0.0", "@npmcli/node-gyp": "^3.0.0", "@npmcli/package-json": "^5.0.0", "@npmcli/query": "^3.1.0", "@npmcli/run-script": "^7.0.2", "bin-links": "^4.0.1", "cacache": "^18.0.0", "common-ancestor-path": "^1.0.1", "hosted-git-info": "^7.0.1", "json-parse-even-better-errors": "^3.0.0", "json-stringify-nice": "^1.1.4", "minimatch": "^9.0.0", "nopt": "^7.0.0", "npm-install-checks": "^6.2.0", "npm-package-arg": "^11.0.1", "npm-pick-manifest": "^9.0.0", "npm-registry-fetch": "^16.0.0", "npmlog": "^7.0.1", "pacote": "^17.0.4", "parse-conflict-json": "^3.0.0", "proc-log": "^3.0.0", "promise-all-reject-late": "^1.0.0", "promise-call-limit": "^3.0.1", "read-package-json-fast": "^3.0.2", "semver": "^7.3.7", "ssri": "^10.0.5", "treeverse": "^3.0.0", "walk-up-path": "^3.0.1" }, "bin": { "arborist": "bin/index.js" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@npmcli/config": { "version": "8.2.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/map-workspaces": "^3.0.2", "ci-info": "^4.0.0", "ini": "^4.1.0", "nopt": "^7.0.0", "proc-log": "^3.0.0", "read-package-json-fast": "^3.0.2", "semver": "^7.3.5", "walk-up-path": "^3.0.1" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@npmcli/disparity-colors": { "version": "3.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "ansi-styles": "^4.3.0" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@npmcli/disparity-colors/node_modules/ansi-styles": { "version": "4.3.0", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/npm/node_modules/@npmcli/fs": { "version": "3.1.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "semver": "^7.3.5" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@npmcli/git": { "version": "5.0.4", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/promise-spawn": "^7.0.0", "lru-cache": "^10.0.1", "npm-pick-manifest": "^9.0.0", "proc-log": "^3.0.0", "promise-inflight": "^1.0.1", "promise-retry": "^2.0.1", "semver": "^7.3.5", "which": "^4.0.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@npmcli/installed-package-contents": { "version": "2.0.2", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "npm-bundled": "^3.0.0", "npm-normalize-package-bin": "^3.0.0" }, "bin": { "installed-package-contents": "lib/index.js" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@npmcli/map-workspaces": { "version": "3.0.4", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/name-from-folder": "^2.0.0", "glob": "^10.2.2", "minimatch": "^9.0.0", "read-package-json-fast": "^3.0.0" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { "version": "7.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "cacache": "^18.0.0", "json-parse-even-better-errors": "^3.0.0", "pacote": "^17.0.0", "semver": "^7.3.5" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@npmcli/name-from-folder": { "version": "2.0.0", "dev": true, "inBundle": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@npmcli/node-gyp": { "version": "3.0.0", "dev": true, "inBundle": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@npmcli/package-json": { "version": "5.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/git": "^5.0.0", "glob": "^10.2.2", "hosted-git-info": "^7.0.0", "json-parse-even-better-errors": "^3.0.0", "normalize-package-data": "^6.0.0", "proc-log": "^3.0.0", "semver": "^7.5.3" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@npmcli/promise-spawn": { "version": "7.0.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "which": "^4.0.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@npmcli/query": { "version": "3.1.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "postcss-selector-parser": "^6.0.10" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@npmcli/run-script": { "version": "7.0.4", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/node-gyp": "^3.0.0", "@npmcli/package-json": "^5.0.0", "@npmcli/promise-spawn": "^7.0.0", "node-gyp": "^10.0.0", "which": "^4.0.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@pkgjs/parseargs": { "version": "0.11.0", "dev": true, "inBundle": true, "license": "MIT", "optional": true, "engines": { "node": ">=14" } }, "node_modules/npm/node_modules/@sigstore/bundle": { "version": "2.2.0", "dev": true, "inBundle": true, "license": "Apache-2.0", "dependencies": { "@sigstore/protobuf-specs": "^0.3.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@sigstore/core": { "version": "1.0.0", "dev": true, "inBundle": true, "license": "Apache-2.0", "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@sigstore/protobuf-specs": { "version": "0.3.0", "dev": true, "inBundle": true, "license": "Apache-2.0", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@sigstore/sign": { "version": "2.2.3", "dev": true, "inBundle": true, "license": "Apache-2.0", "dependencies": { "@sigstore/bundle": "^2.2.0", "@sigstore/core": "^1.0.0", "@sigstore/protobuf-specs": "^0.3.0", "make-fetch-happen": "^13.0.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@sigstore/tuf": { "version": "2.3.1", "dev": true, "inBundle": true, "license": "Apache-2.0", "dependencies": { "@sigstore/protobuf-specs": "^0.3.0", "tuf-js": "^2.2.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@sigstore/verify": { "version": "1.1.0", "dev": true, "inBundle": true, "license": "Apache-2.0", "dependencies": { "@sigstore/bundle": "^2.2.0", "@sigstore/core": "^1.0.0", "@sigstore/protobuf-specs": "^0.3.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@tufjs/canonical-json": { "version": "2.0.0", "dev": true, "inBundle": true, "license": "MIT", "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@tufjs/models": { "version": "2.0.0", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "@tufjs/canonical-json": "2.0.0", "minimatch": "^9.0.3" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/abbrev": { "version": "2.0.0", "dev": true, "inBundle": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/agent-base": { "version": "7.1.0", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "debug": "^4.3.4" }, "engines": { "node": ">= 14" } }, "node_modules/npm/node_modules/aggregate-error": { "version": "3.1.0", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/npm/node_modules/ansi-regex": { "version": "5.0.1", "dev": true, "inBundle": true, "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/npm/node_modules/ansi-styles": { "version": "6.2.1", "dev": true, "inBundle": true, "license": "MIT", "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/npm/node_modules/aproba": { "version": "2.0.0", "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/archy": { "version": "1.0.0", "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/are-we-there-yet": { "version": "4.0.2", "dev": true, "inBundle": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/balanced-match": { "version": "1.0.2", "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/bin-links": { "version": "4.0.3", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "cmd-shim": "^6.0.0", "npm-normalize-package-bin": "^3.0.0", "read-cmd-shim": "^4.0.0", "write-file-atomic": "^5.0.0" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/binary-extensions": { "version": "2.2.0", "dev": true, "inBundle": true, "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/npm/node_modules/brace-expansion": { "version": "2.0.1", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/npm/node_modules/builtins": { "version": "5.0.1", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "semver": "^7.0.0" } }, "node_modules/npm/node_modules/cacache": { "version": "18.0.2", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/fs": "^3.1.0", "fs-minipass": "^3.0.0", "glob": "^10.2.2", "lru-cache": "^10.0.1", "minipass": "^7.0.3", "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "p-map": "^4.0.0", "ssri": "^10.0.0", "tar": "^6.1.11", "unique-filename": "^3.0.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/chalk": { "version": "5.3.0", "dev": true, "inBundle": true, "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/npm/node_modules/chownr": { "version": "2.0.0", "dev": true, "inBundle": true, "license": "ISC", "engines": { "node": ">=10" } }, "node_modules/npm/node_modules/ci-info": { "version": "4.0.0", "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/sibiraj-s" } ], "inBundle": true, "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/npm/node_modules/cidr-regex": { "version": "4.0.3", "dev": true, "inBundle": true, "license": "BSD-2-Clause", "dependencies": { "ip-regex": "^5.0.0" }, "engines": { "node": ">=14" } }, "node_modules/npm/node_modules/clean-stack": { "version": "2.2.0", "dev": true, "inBundle": true, "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/npm/node_modules/cli-columns": { "version": "4.0.0", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "string-width": "^4.2.3", "strip-ansi": "^6.0.1" }, "engines": { "node": ">= 10" } }, "node_modules/npm/node_modules/cli-table3": { "version": "0.6.3", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "string-width": "^4.2.0" }, "engines": { "node": "10.* || >= 12.*" }, "optionalDependencies": { "@colors/colors": "1.5.0" } }, "node_modules/npm/node_modules/clone": { "version": "1.0.4", "dev": true, "inBundle": true, "license": "MIT", "engines": { "node": ">=0.8" } }, "node_modules/npm/node_modules/cmd-shim": { "version": "6.0.2", "dev": true, "inBundle": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/color-convert": { "version": "2.0.1", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, "engines": { "node": ">=7.0.0" } }, "node_modules/npm/node_modules/color-name": { "version": "1.1.4", "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/color-support": { "version": "1.1.3", "dev": true, "inBundle": true, "license": "ISC", "bin": { "color-support": "bin.js" } }, "node_modules/npm/node_modules/columnify": { "version": "1.6.0", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "strip-ansi": "^6.0.1", "wcwidth": "^1.0.0" }, "engines": { "node": ">=8.0.0" } }, "node_modules/npm/node_modules/common-ancestor-path": { "version": "1.0.1", "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/console-control-strings": { "version": "1.1.0", "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/cross-spawn": { "version": "7.0.3", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" }, "engines": { "node": ">= 8" } }, "node_modules/npm/node_modules/cross-spawn/node_modules/which": { "version": "2.0.2", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" }, "engines": { "node": ">= 8" } }, "node_modules/npm/node_modules/cssesc": { "version": "3.0.0", "dev": true, "inBundle": true, "license": "MIT", "bin": { "cssesc": "bin/cssesc" }, "engines": { "node": ">=4" } }, "node_modules/npm/node_modules/debug": { "version": "4.3.4", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "ms": "2.1.2" }, "engines": { "node": ">=6.0" }, "peerDependenciesMeta": { "supports-color": { "optional": true } } }, "node_modules/npm/node_modules/debug/node_modules/ms": { "version": "2.1.2", "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/defaults": { "version": "1.0.4", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "clone": "^1.0.2" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/npm/node_modules/diff": { "version": "5.2.0", "dev": true, "inBundle": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } }, "node_modules/npm/node_modules/eastasianwidth": { "version": "0.2.0", "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/emoji-regex": { "version": "8.0.0", "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/encoding": { "version": "0.1.13", "dev": true, "inBundle": true, "license": "MIT", "optional": true, "dependencies": { "iconv-lite": "^0.6.2" } }, "node_modules/npm/node_modules/env-paths": { "version": "2.2.1", "dev": true, "inBundle": true, "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/npm/node_modules/err-code": { "version": "2.0.3", "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/exponential-backoff": { "version": "3.1.1", "dev": true, "inBundle": true, "license": "Apache-2.0" }, "node_modules/npm/node_modules/fastest-levenshtein": { "version": "1.0.16", "dev": true, "inBundle": true, "license": "MIT", "engines": { "node": ">= 4.9.1" } }, "node_modules/npm/node_modules/foreground-child": { "version": "3.1.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" }, "engines": { "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/npm/node_modules/fs-minipass": { "version": "3.0.3", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "minipass": "^7.0.3" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/function-bind": { "version": "1.1.2", "dev": true, "inBundle": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/npm/node_modules/gauge": { "version": "5.0.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "aproba": "^1.0.3 || ^2.0.0", "color-support": "^1.1.3", "console-control-strings": "^1.1.0", "has-unicode": "^2.0.1", "signal-exit": "^4.0.1", "string-width": "^4.2.3", "strip-ansi": "^6.0.1", "wide-align": "^1.1.5" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/glob": { "version": "10.3.10", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^2.3.5", "minimatch": "^9.0.1", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", "path-scurry": "^1.10.1" }, "bin": { "glob": "dist/esm/bin.mjs" }, "engines": { "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/npm/node_modules/graceful-fs": { "version": "4.2.11", "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/has-unicode": { "version": "2.0.1", "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/hasown": { "version": "2.0.1", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, "engines": { "node": ">= 0.4" } }, "node_modules/npm/node_modules/hosted-git-info": { "version": "7.0.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "lru-cache": "^10.0.1" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/http-cache-semantics": { "version": "4.1.1", "dev": true, "inBundle": true, "license": "BSD-2-Clause" }, "node_modules/npm/node_modules/http-proxy-agent": { "version": "7.0.2", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" }, "engines": { "node": ">= 14" } }, "node_modules/npm/node_modules/https-proxy-agent": { "version": "7.0.4", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "agent-base": "^7.0.2", "debug": "4" }, "engines": { "node": ">= 14" } }, "node_modules/npm/node_modules/iconv-lite": { "version": "0.6.3", "dev": true, "inBundle": true, "license": "MIT", "optional": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" } }, "node_modules/npm/node_modules/ignore-walk": { "version": "6.0.4", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "minimatch": "^9.0.0" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/imurmurhash": { "version": "0.1.4", "dev": true, "inBundle": true, "license": "MIT", "engines": { "node": ">=0.8.19" } }, "node_modules/npm/node_modules/indent-string": { "version": "4.0.0", "dev": true, "inBundle": true, "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/npm/node_modules/ini": { "version": "4.1.1", "dev": true, "inBundle": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/init-package-json": { "version": "6.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "npm-package-arg": "^11.0.0", "promzard": "^1.0.0", "read": "^2.0.0", "read-package-json": "^7.0.0", "semver": "^7.3.5", "validate-npm-package-license": "^3.0.4", "validate-npm-package-name": "^5.0.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/ip-address": { "version": "9.0.5", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "jsbn": "1.1.0", "sprintf-js": "^1.1.3" }, "engines": { "node": ">= 12" } }, "node_modules/npm/node_modules/ip-address/node_modules/sprintf-js": { "version": "1.1.3", "dev": true, "inBundle": true, "license": "BSD-3-Clause" }, "node_modules/npm/node_modules/ip-regex": { "version": "5.0.0", "dev": true, "inBundle": true, "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/npm/node_modules/is-cidr": { "version": "5.0.3", "dev": true, "inBundle": true, "license": "BSD-2-Clause", "dependencies": { "cidr-regex": "4.0.3" }, "engines": { "node": ">=14" } }, "node_modules/npm/node_modules/is-core-module": { "version": "2.13.1", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/npm/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "dev": true, "inBundle": true, "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/npm/node_modules/is-lambda": { "version": "1.0.1", "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/isexe": { "version": "2.0.0", "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/jackspeak": { "version": "2.3.6", "dev": true, "inBundle": true, "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, "engines": { "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/isaacs" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "node_modules/npm/node_modules/jsbn": { "version": "1.1.0", "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/json-parse-even-better-errors": { "version": "3.0.1", "dev": true, "inBundle": true, "license": "MIT", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/json-stringify-nice": { "version": "1.1.4", "dev": true, "inBundle": true, "license": "ISC", "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/npm/node_modules/jsonparse": { "version": "1.3.1", "dev": true, "engines": [ "node >= 0.2.0" ], "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/just-diff": { "version": "6.0.2", "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/just-diff-apply": { "version": "5.5.0", "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/libnpmaccess": { "version": "8.0.2", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "npm-package-arg": "^11.0.1", "npm-registry-fetch": "^16.0.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmdiff": { "version": "6.0.7", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/arborist": "^7.2.1", "@npmcli/disparity-colors": "^3.0.0", "@npmcli/installed-package-contents": "^2.0.2", "binary-extensions": "^2.2.0", "diff": "^5.1.0", "minimatch": "^9.0.0", "npm-package-arg": "^11.0.1", "pacote": "^17.0.4", "tar": "^6.2.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmexec": { "version": "7.0.8", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/arborist": "^7.2.1", "@npmcli/run-script": "^7.0.2", "ci-info": "^4.0.0", "npm-package-arg": "^11.0.1", "npmlog": "^7.0.1", "pacote": "^17.0.4", "proc-log": "^3.0.0", "read": "^2.0.0", "read-package-json-fast": "^3.0.2", "semver": "^7.3.7", "walk-up-path": "^3.0.1" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmfund": { "version": "5.0.5", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/arborist": "^7.2.1" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmhook": { "version": "10.0.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "aproba": "^2.0.0", "npm-registry-fetch": "^16.0.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmorg": { "version": "6.0.2", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "aproba": "^2.0.0", "npm-registry-fetch": "^16.0.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmpack": { "version": "6.0.7", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/arborist": "^7.2.1", "@npmcli/run-script": "^7.0.2", "npm-package-arg": "^11.0.1", "pacote": "^17.0.4" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmpublish": { "version": "9.0.4", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "ci-info": "^4.0.0", "normalize-package-data": "^6.0.0", "npm-package-arg": "^11.0.1", "npm-registry-fetch": "^16.0.0", "proc-log": "^3.0.0", "semver": "^7.3.7", "sigstore": "^2.2.0", "ssri": "^10.0.5" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmsearch": { "version": "7.0.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "npm-registry-fetch": "^16.0.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmteam": { "version": "6.0.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "aproba": "^2.0.0", "npm-registry-fetch": "^16.0.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmversion": { "version": "5.0.2", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/git": "^5.0.3", "@npmcli/run-script": "^7.0.2", "json-parse-even-better-errors": "^3.0.0", "proc-log": "^3.0.0", "semver": "^7.3.7" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/lru-cache": { "version": "10.2.0", "dev": true, "inBundle": true, "license": "ISC", "engines": { "node": "14 || >=16.14" } }, "node_modules/npm/node_modules/make-fetch-happen": { "version": "13.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/agent": "^2.0.0", "cacache": "^18.0.0", "http-cache-semantics": "^4.1.1", "is-lambda": "^1.0.1", "minipass": "^7.0.2", "minipass-fetch": "^3.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^0.6.3", "promise-retry": "^2.0.1", "ssri": "^10.0.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/minimatch": { "version": "9.0.3", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/npm/node_modules/minipass": { "version": "7.0.4", "dev": true, "inBundle": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" } }, "node_modules/npm/node_modules/minipass-collect": { "version": "2.0.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "minipass": "^7.0.3" }, "engines": { "node": ">=16 || 14 >=14.17" } }, "node_modules/npm/node_modules/minipass-fetch": { "version": "3.0.4", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "minipass": "^7.0.3", "minipass-sized": "^1.0.3", "minizlib": "^2.1.2" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" }, "optionalDependencies": { "encoding": "^0.1.13" } }, "node_modules/npm/node_modules/minipass-flush": { "version": "1.0.5", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "minipass": "^3.0.0" }, "engines": { "node": ">= 8" } }, "node_modules/npm/node_modules/minipass-flush/node_modules/minipass": { "version": "3.3.6", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/npm/node_modules/minipass-json-stream": { "version": "1.0.1", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "jsonparse": "^1.3.1", "minipass": "^3.0.0" } }, "node_modules/npm/node_modules/minipass-json-stream/node_modules/minipass": { "version": "3.3.6", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/npm/node_modules/minipass-pipeline": { "version": "1.2.4", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "minipass": "^3.0.0" }, "engines": { "node": ">=8" } }, "node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": { "version": "3.3.6", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/npm/node_modules/minipass-sized": { "version": "1.0.3", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "minipass": "^3.0.0" }, "engines": { "node": ">=8" } }, "node_modules/npm/node_modules/minipass-sized/node_modules/minipass": { "version": "3.3.6", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/npm/node_modules/minizlib": { "version": "2.1.2", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" }, "engines": { "node": ">= 8" } }, "node_modules/npm/node_modules/minizlib/node_modules/minipass": { "version": "3.3.6", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/npm/node_modules/mkdirp": { "version": "1.0.4", "dev": true, "inBundle": true, "license": "MIT", "bin": { "mkdirp": "bin/cmd.js" }, "engines": { "node": ">=10" } }, "node_modules/npm/node_modules/ms": { "version": "2.1.3", "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/mute-stream": { "version": "1.0.0", "dev": true, "inBundle": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/negotiator": { "version": "0.6.3", "dev": true, "inBundle": true, "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/npm/node_modules/node-gyp": { "version": "10.0.1", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", "glob": "^10.3.10", "graceful-fs": "^4.2.6", "make-fetch-happen": "^13.0.0", "nopt": "^7.0.0", "proc-log": "^3.0.0", "semver": "^7.3.5", "tar": "^6.1.2", "which": "^4.0.0" }, "bin": { "node-gyp": "bin/node-gyp.js" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/nopt": { "version": "7.2.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "abbrev": "^2.0.0" }, "bin": { "nopt": "bin/nopt.js" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/normalize-package-data": { "version": "6.0.0", "dev": true, "inBundle": true, "license": "BSD-2-Clause", "dependencies": { "hosted-git-info": "^7.0.0", "is-core-module": "^2.8.1", "semver": "^7.3.5", "validate-npm-package-license": "^3.0.4" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/npm-audit-report": { "version": "5.0.0", "dev": true, "inBundle": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/npm-bundled": { "version": "3.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "npm-normalize-package-bin": "^3.0.0" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/npm-install-checks": { "version": "6.3.0", "dev": true, "inBundle": true, "license": "BSD-2-Clause", "dependencies": { "semver": "^7.1.1" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/npm-normalize-package-bin": { "version": "3.0.1", "dev": true, "inBundle": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/npm-package-arg": { "version": "11.0.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "hosted-git-info": "^7.0.0", "proc-log": "^3.0.0", "semver": "^7.3.5", "validate-npm-package-name": "^5.0.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/npm-packlist": { "version": "8.0.2", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "ignore-walk": "^6.0.4" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/npm-pick-manifest": { "version": "9.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "npm-install-checks": "^6.0.0", "npm-normalize-package-bin": "^3.0.0", "npm-package-arg": "^11.0.0", "semver": "^7.3.5" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/npm-profile": { "version": "9.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "npm-registry-fetch": "^16.0.0", "proc-log": "^3.0.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/npm-registry-fetch": { "version": "16.1.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "make-fetch-happen": "^13.0.0", "minipass": "^7.0.2", "minipass-fetch": "^3.0.0", "minipass-json-stream": "^1.0.1", "minizlib": "^2.1.2", "npm-package-arg": "^11.0.0", "proc-log": "^3.0.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/npm-user-validate": { "version": "2.0.0", "dev": true, "inBundle": true, "license": "BSD-2-Clause", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/npmlog": { "version": "7.0.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "are-we-there-yet": "^4.0.0", "console-control-strings": "^1.1.0", "gauge": "^5.0.0", "set-blocking": "^2.0.0" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/p-map": { "version": "4.0.0", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "aggregate-error": "^3.0.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/npm/node_modules/pacote": { "version": "17.0.6", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/git": "^5.0.0", "@npmcli/installed-package-contents": "^2.0.1", "@npmcli/promise-spawn": "^7.0.0", "@npmcli/run-script": "^7.0.0", "cacache": "^18.0.0", "fs-minipass": "^3.0.0", "minipass": "^7.0.2", "npm-package-arg": "^11.0.0", "npm-packlist": "^8.0.0", "npm-pick-manifest": "^9.0.0", "npm-registry-fetch": "^16.0.0", "proc-log": "^3.0.0", "promise-retry": "^2.0.1", "read-package-json": "^7.0.0", "read-package-json-fast": "^3.0.0", "sigstore": "^2.2.0", "ssri": "^10.0.0", "tar": "^6.1.11" }, "bin": { "pacote": "lib/bin.js" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/parse-conflict-json": { "version": "3.0.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "json-parse-even-better-errors": "^3.0.0", "just-diff": "^6.0.0", "just-diff-apply": "^5.2.0" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/path-key": { "version": "3.1.1", "dev": true, "inBundle": true, "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/npm/node_modules/path-scurry": { "version": "1.10.1", "dev": true, "inBundle": true, "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^9.1.1 || ^10.0.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/npm/node_modules/postcss-selector-parser": { "version": "6.0.15", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" }, "engines": { "node": ">=4" } }, "node_modules/npm/node_modules/proc-log": { "version": "3.0.0", "dev": true, "inBundle": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/promise-all-reject-late": { "version": "1.0.1", "dev": true, "inBundle": true, "license": "ISC", "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/npm/node_modules/promise-call-limit": { "version": "3.0.1", "dev": true, "inBundle": true, "license": "ISC", "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/npm/node_modules/promise-inflight": { "version": "1.0.1", "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/promise-retry": { "version": "2.0.1", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "err-code": "^2.0.2", "retry": "^0.12.0" }, "engines": { "node": ">=10" } }, "node_modules/npm/node_modules/promzard": { "version": "1.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "read": "^2.0.0" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/qrcode-terminal": { "version": "0.12.0", "dev": true, "inBundle": true, "bin": { "qrcode-terminal": "bin/qrcode-terminal.js" } }, "node_modules/npm/node_modules/read": { "version": "2.1.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "mute-stream": "~1.0.0" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/read-cmd-shim": { "version": "4.0.0", "dev": true, "inBundle": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/read-package-json": { "version": "7.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "glob": "^10.2.2", "json-parse-even-better-errors": "^3.0.0", "normalize-package-data": "^6.0.0", "npm-normalize-package-bin": "^3.0.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/read-package-json-fast": { "version": "3.0.2", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "json-parse-even-better-errors": "^3.0.0", "npm-normalize-package-bin": "^3.0.0" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/retry": { "version": "0.12.0", "dev": true, "inBundle": true, "license": "MIT", "engines": { "node": ">= 4" } }, "node_modules/npm/node_modules/safer-buffer": { "version": "2.1.2", "dev": true, "inBundle": true, "license": "MIT", "optional": true }, "node_modules/npm/node_modules/semver": { "version": "7.6.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "lru-cache": "^6.0.0" }, "bin": { "semver": "bin/semver.js" }, "engines": { "node": ">=10" } }, "node_modules/npm/node_modules/semver/node_modules/lru-cache": { "version": "6.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, "engines": { "node": ">=10" } }, "node_modules/npm/node_modules/set-blocking": { "version": "2.0.0", "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/shebang-command": { "version": "2.0.0", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, "engines": { "node": ">=8" } }, "node_modules/npm/node_modules/shebang-regex": { "version": "3.0.0", "dev": true, "inBundle": true, "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/npm/node_modules/signal-exit": { "version": "4.1.0", "dev": true, "inBundle": true, "license": "ISC", "engines": { "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/npm/node_modules/sigstore": { "version": "2.2.2", "dev": true, "inBundle": true, "license": "Apache-2.0", "dependencies": { "@sigstore/bundle": "^2.2.0", "@sigstore/core": "^1.0.0", "@sigstore/protobuf-specs": "^0.3.0", "@sigstore/sign": "^2.2.3", "@sigstore/tuf": "^2.3.1", "@sigstore/verify": "^1.1.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/smart-buffer": { "version": "4.2.0", "dev": true, "inBundle": true, "license": "MIT", "engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0" } }, "node_modules/npm/node_modules/socks": { "version": "2.8.0", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "ip-address": "^9.0.5", "smart-buffer": "^4.2.0" }, "engines": { "node": ">= 16.0.0", "npm": ">= 3.0.0" } }, "node_modules/npm/node_modules/socks-proxy-agent": { "version": "8.0.2", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "agent-base": "^7.0.2", "debug": "^4.3.4", "socks": "^2.7.1" }, "engines": { "node": ">= 14" } }, "node_modules/npm/node_modules/spdx-correct": { "version": "3.2.0", "dev": true, "inBundle": true, "license": "Apache-2.0", "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" } }, "node_modules/npm/node_modules/spdx-exceptions": { "version": "2.5.0", "dev": true, "inBundle": true, "license": "CC-BY-3.0" }, "node_modules/npm/node_modules/spdx-expression-parse": { "version": "3.0.1", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "node_modules/npm/node_modules/spdx-license-ids": { "version": "3.0.17", "dev": true, "inBundle": true, "license": "CC0-1.0" }, "node_modules/npm/node_modules/ssri": { "version": "10.0.5", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "minipass": "^7.0.3" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/string-width": { "version": "4.2.3", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" } }, "node_modules/npm/node_modules/string-width-cjs": { "name": "string-width", "version": "4.2.3", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" } }, "node_modules/npm/node_modules/strip-ansi": { "version": "6.0.1", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/npm/node_modules/strip-ansi-cjs": { "name": "strip-ansi", "version": "6.0.1", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/npm/node_modules/supports-color": { "version": "9.4.0", "dev": true, "inBundle": true, "license": "MIT", "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/chalk/supports-color?sponsor=1" } }, "node_modules/npm/node_modules/tar": { "version": "6.2.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" }, "engines": { "node": ">=10" } }, "node_modules/npm/node_modules/tar/node_modules/fs-minipass": { "version": "2.1.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "minipass": "^3.0.0" }, "engines": { "node": ">= 8" } }, "node_modules/npm/node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { "version": "3.3.6", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/npm/node_modules/tar/node_modules/minipass": { "version": "5.0.0", "dev": true, "inBundle": true, "license": "ISC", "engines": { "node": ">=8" } }, "node_modules/npm/node_modules/text-table": { "version": "0.2.0", "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/tiny-relative-date": { "version": "1.3.0", "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/treeverse": { "version": "3.0.0", "dev": true, "inBundle": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/tuf-js": { "version": "2.2.0", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "@tufjs/models": "2.0.0", "debug": "^4.3.4", "make-fetch-happen": "^13.0.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/unique-filename": { "version": "3.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "unique-slug": "^4.0.0" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/unique-slug": { "version": "4.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/util-deprecate": { "version": "1.0.2", "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/validate-npm-package-license": { "version": "3.0.4", "dev": true, "inBundle": true, "license": "Apache-2.0", "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" } }, "node_modules/npm/node_modules/validate-npm-package-name": { "version": "5.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "builtins": "^5.0.0" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/walk-up-path": { "version": "3.0.1", "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/wcwidth": { "version": "1.0.1", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "defaults": "^1.0.3" } }, "node_modules/npm/node_modules/which": { "version": "4.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" }, "engines": { "node": "^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/which/node_modules/isexe": { "version": "3.1.1", "dev": true, "inBundle": true, "license": "ISC", "engines": { "node": ">=16" } }, "node_modules/npm/node_modules/wide-align": { "version": "1.1.5", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "string-width": "^1.0.2 || 2 || 3 || 4" } }, "node_modules/npm/node_modules/wrap-ansi": { "version": "8.1.0", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "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/npm/node_modules/wrap-ansi-cjs": { "name": "wrap-ansi", "version": "7.0.0", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/npm/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { "version": "4.3.0", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { "version": "6.0.1", "dev": true, "inBundle": true, "license": "MIT", "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, "node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": { "version": "9.2.2", "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": { "version": "5.1.2", "dev": true, "inBundle": true, "license": "MIT", "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/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { "version": "7.1.0", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" }, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/npm/node_modules/write-file-atomic": { "version": "5.0.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^4.0.1" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/yallist": { "version": "4.0.0", "dev": true, "inBundle": true, "license": "ISC" }, "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/onetime": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "dependencies": { "mimic-fn": "^2.1.0" }, "engines": { "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-each-series": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-3.0.0.tgz", "integrity": "sha512-lastgtAdoH9YaLyDa5i5z64q+kzOcQHsQ5SsZJD3q0VEyI8mq872S3geuNbRUQLVAE9siMfgKrpj7MloKFHruw==", "dev": true, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-filter": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-4.1.0.tgz", "integrity": "sha512-37/tPdZ3oJwHaS3gNJdenCDB3Tz26i9sjhnguBtvN0vYlRIiDNnvTWkuh+0hETV9rLPdJ3rlL3yVOYPIAnM8rw==", "dev": true, "dependencies": { "p-map": "^7.0.1" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-is-promise": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/p-limit": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", "dev": true, "dependencies": { "yocto-queue": "^1.0.0" }, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-locate": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", "dev": true, "dependencies": { "p-limit": "^4.0.0" }, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-map": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.1.tgz", "integrity": "sha512-2wnaR0XL/FDOj+TgpDuRb2KTjLnu3Fma6b1ZUwGY7LcqenMcvP/YFpjpbPKY6WVGsbuJZRuoUz8iPrt8ORnAFw==", "dev": true, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-reduce": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz", "integrity": "sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/p-try": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "dependencies": { "callsites": "^3.0.0" }, "engines": { "node": ">=6" } }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/parse-ms": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", "dev": true, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/parse5": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", "dev": true }, "node_modules/parse5-htmlparser2-tree-adapter": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", "dev": true, "dependencies": { "parse5": "^6.0.1" } }, "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", "dev": true }, "node_modules/path-exists": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", "dev": true, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "engines": { "node": ">=8.6" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/pkg-conf": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", "integrity": "sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==", "dev": true, "dependencies": { "find-up": "^2.0.0", "load-json-file": "^4.0.0" }, "engines": { "node": ">=4" } }, "node_modules/pkg-conf/node_modules/find-up": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", "dev": true, "dependencies": { "locate-path": "^2.0.0" }, "engines": { "node": ">=4" } }, "node_modules/pkg-conf/node_modules/locate-path": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", "dev": true, "dependencies": { "p-locate": "^2.0.0", "path-exists": "^3.0.0" }, "engines": { "node": ">=4" } }, "node_modules/pkg-conf/node_modules/p-limit": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", "dev": true, "dependencies": { "p-try": "^1.0.0" }, "engines": { "node": ">=4" } }, "node_modules/pkg-conf/node_modules/p-locate": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", "dev": true, "dependencies": { "p-limit": "^1.1.0" }, "engines": { "node": ">=4" } }, "node_modules/pkg-conf/node_modules/path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/pretty-ms": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.0.0.tgz", "integrity": "sha512-E9e9HJ9R9NasGOgPaPE8VMeiPKAyWR5jcFpNnwIejslIhWqdqOrb2wShBsncMPUb+BcCd2OPYfh7p2W6oemTng==", "dev": true, "dependencies": { "parse-ms": "^4.0.0" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "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/proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", "dev": true }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/feross" }, { "type": "patreon", "url": "https://www.patreon.com/feross" }, { "type": "consulting", "url": "https://feross.org/support" } ] }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "cli.js" } }, "node_modules/read-package-up": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-11.0.0.tgz", "integrity": "sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ==", "dev": true, "dependencies": { "find-up-simple": "^1.0.0", "read-pkg": "^9.0.0", "type-fest": "^4.6.0" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/read-pkg": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", "dev": true, "dependencies": { "@types/normalize-package-data": "^2.4.3", "normalize-package-data": "^6.0.0", "parse-json": "^8.0.0", "type-fest": "^4.6.0", "unicorn-magic": "^0.1.0" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/read-pkg-up": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-11.0.0.tgz", "integrity": "sha512-LOVbvF1Q0SZdjClSefZ0Nz5z8u+tIE7mV5NibzmE9VYmDe9CaBbAVtz1veOSZbofrdsilxuDAYnFenukZVp8/Q==", "deprecated": "Renamed to read-package-up", "dev": true, "dependencies": { "find-up-simple": "^1.0.0", "read-pkg": "^9.0.0", "type-fest": "^4.6.0" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/read-pkg/node_modules/parse-json": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.1.0.tgz", "integrity": "sha512-rum1bPifK5SSar35Z6EKZuYPJx85pkNaFrxBK3mwdfSJ1/WKbYrjoW/zTPSjRRamfmVX1ACBIdFAO0VRErW/EA==", "dev": true, "dependencies": { "@babel/code-frame": "^7.22.13", "index-to-position": "^0.1.2", "type-fest": "^4.7.1" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "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": { "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/registry-auth-token": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.1.tgz", "integrity": "sha512-UfxVOj8seK1yaIOiieV4FIP01vfBDLsY0H9sQzi9EbbUdJiuuBjJgLa1DpImXMNPnVkBD4eVxTEXcrZA6kfpJA==", "dev": true, "dependencies": { "@pnpm/npm-conf": "^1.0.4" }, "engines": { "node": ">=14" } }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true, "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" } }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/feross" }, { "type": "patreon", "url": "https://www.patreon.com/feross" }, { "type": "consulting", "url": "https://feross.org/support" } ], "dependencies": { "queue-microtask": "^1.2.2" } }, "node_modules/safe-buffer": { "version": "5.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/semantic-release": { "version": "24.2.3", "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-24.2.3.tgz", "integrity": "sha512-KRhQG9cUazPavJiJEFIJ3XAMjgfd0fcK3B+T26qOl8L0UG5aZUjeRfREO0KM5InGtYwxqiiytkJrbcYoLDEv0A==", "dev": true, "dependencies": { "@semantic-release/commit-analyzer": "^13.0.0-beta.1", "@semantic-release/error": "^4.0.0", "@semantic-release/github": "^11.0.0", "@semantic-release/npm": "^12.0.0", "@semantic-release/release-notes-generator": "^14.0.0-beta.1", "aggregate-error": "^5.0.0", "cosmiconfig": "^9.0.0", "debug": "^4.0.0", "env-ci": "^11.0.0", "execa": "^9.0.0", "figures": "^6.0.0", "find-versions": "^6.0.0", "get-stream": "^6.0.0", "git-log-parser": "^1.2.0", "hook-std": "^3.0.0", "hosted-git-info": "^8.0.0", "import-from-esm": "^2.0.0", "lodash-es": "^4.17.21", "marked": "^12.0.0", "marked-terminal": "^7.0.0", "micromatch": "^4.0.2", "p-each-series": "^3.0.0", "p-reduce": "^3.0.0", "read-package-up": "^11.0.0", "resolve-from": "^5.0.0", "semver": "^7.3.2", "semver-diff": "^4.0.0", "signale": "^1.2.1", "yargs": "^17.5.1" }, "bin": { "semantic-release": "bin/semantic-release.js" }, "engines": { "node": ">=20.8.1" } }, "node_modules/semantic-release/node_modules/@semantic-release/error": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz", "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==", "dev": true, "engines": { "node": ">=18" } }, "node_modules/semantic-release/node_modules/@sindresorhus/merge-streams": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", "dev": true, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/semantic-release/node_modules/aggregate-error": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz", "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==", "dev": true, "dependencies": { "clean-stack": "^5.2.0", "indent-string": "^5.0.0" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/semantic-release/node_modules/clean-stack": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.2.0.tgz", "integrity": "sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==", "dev": true, "dependencies": { "escape-string-regexp": "5.0.0" }, "engines": { "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/semantic-release/node_modules/execa": { "version": "9.5.2", "resolved": "https://registry.npmjs.org/execa/-/execa-9.5.2.tgz", "integrity": "sha512-EHlpxMCpHWSAh1dgS6bVeoLAXGnJNdR93aabr4QCGbzOM73o5XmRfM/e5FUqsw3aagP8S8XEWUWFAxnRBnAF0Q==", "dev": true, "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.3", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.0", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.0.0" }, "engines": { "node": "^18.19.0 || >=20.5.0" }, "funding": { "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, "node_modules/semantic-release/node_modules/execa/node_modules/get-stream": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", "dev": true, "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/semantic-release/node_modules/hosted-git-info": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.0.2.tgz", "integrity": "sha512-sYKnA7eGln5ov8T8gnYlkSOxFJvywzEx9BueN6xo/GKO8PGiI6uK6xx+DIGe45T3bdVjLAQDQW1aicT8z8JwQg==", "dev": true, "dependencies": { "lru-cache": "^10.0.1" }, "engines": { "node": "^18.17.0 || >=20.5.0" } }, "node_modules/semantic-release/node_modules/human-signals": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.0.tgz", "integrity": "sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA==", "dev": true, "engines": { "node": ">=18.18.0" } }, "node_modules/semantic-release/node_modules/import-from-esm": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/import-from-esm/-/import-from-esm-2.0.0.tgz", "integrity": "sha512-YVt14UZCgsX1vZQ3gKjkWVdBdHQ6eu3MPU1TBgL1H5orXe2+jWD006WCPPtOuwlQm10NuzOW5WawiF1Q9veW8g==", "dev": true, "dependencies": { "debug": "^4.3.4", "import-meta-resolve": "^4.0.0" }, "engines": { "node": ">=18.20" } }, "node_modules/semantic-release/node_modules/indent-string": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", "dev": true, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/semantic-release/node_modules/is-stream": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", "dev": true, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/semantic-release/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/semantic-release/node_modules/npm-run-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", "dev": true, "dependencies": { "path-key": "^4.0.0", "unicorn-magic": "^0.3.0" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/semantic-release/node_modules/p-reduce": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-3.0.0.tgz", "integrity": "sha512-xsrIUgI0Kn6iyDYm9StOpOeK29XM1aboGji26+QEortiFST1hGZaUQOLhtEbqHErPpGW/aSz6allwK2qcptp0Q==", "dev": true, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/semantic-release/node_modules/path-key": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/semantic-release/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, "engines": { "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/semantic-release/node_modules/strip-final-newline": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", "dev": true, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/semantic-release/node_modules/unicorn-magic": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", "dev": true, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/semver": { "version": "7.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, "bin": { "semver": "bin/semver.js" }, "engines": { "node": ">=10" } }, "node_modules/semver-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", "dev": true, "dependencies": { "semver": "^7.3.5" }, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/semver-regex": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-4.0.5.tgz", "integrity": "sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==", "dev": true, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, "engines": { "node": ">=8" } }, "node_modules/shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, "node_modules/signale": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/signale/-/signale-1.4.0.tgz", "integrity": "sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==", "dev": true, "dependencies": { "chalk": "^2.3.2", "figures": "^2.0.0", "pkg-conf": "^2.1.0" }, "engines": { "node": ">=6" } }, "node_modules/signale/node_modules/ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "dependencies": { "color-convert": "^1.9.0" }, "engines": { "node": ">=4" } }, "node_modules/signale/node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" }, "engines": { "node": ">=4" } }, "node_modules/signale/node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "dependencies": { "color-name": "1.1.3" } }, "node_modules/signale/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, "node_modules/signale/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "engines": { "node": ">=0.8.0" } }, "node_modules/signale/node_modules/figures": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", "dev": true, "dependencies": { "escape-string-regexp": "^1.0.5" }, "engines": { "node": ">=4" } }, "node_modules/signale/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/signale/node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "dependencies": { "has-flag": "^3.0.0" }, "engines": { "node": ">=4" } }, "node_modules/skin-tone": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==", "dev": true, "dependencies": { "unicode-emoji-modifier-base": "^1.0.0" }, "engines": { "node": ">=8" } }, "node_modules/slash": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", "dev": true, "engines": { "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/spawn-error-forwarder": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz", "integrity": "sha512-gRjMgK5uFjbCvdibeGJuy3I5OYz6VLoVdsOJdA6wV0WlfQVLFueoqMxwwYD9RODdgb6oUIvlRlsyFSiQkMKu0g==", "dev": true }, "node_modules/spdx-correct": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", "dev": true, "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" } }, "node_modules/spdx-exceptions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", "dev": true }, "node_modules/spdx-expression-parse": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "dev": true, "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "node_modules/spdx-license-ids": { "version": "3.0.12", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", "dev": true }, "node_modules/split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", "dev": true, "engines": { "node": ">= 10.x" } }, "node_modules/stream-combiner2": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", "integrity": "sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==", "dev": true, "dependencies": { "duplexer2": "~0.1.0", "readable-stream": "^2.0.2" } }, "node_modules/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/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" } }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/super-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/super-regex/-/super-regex-1.0.0.tgz", "integrity": "sha512-CY8u7DtbvucKuquCmOFEKhr9Besln7n9uN8eFbwcoGYWXOMW07u2o8njWaiXt11ylS3qoGF55pILjRmPlbodyg==", "dev": true, "dependencies": { "function-timeout": "^1.0.1", "time-span": "^5.1.0" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { "has-flag": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/supports-hyperlinks": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.0.0.tgz", "integrity": "sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==", "dev": true, "dependencies": { "has-flag": "^4.0.0", "supports-color": "^7.0.0" }, "engines": { "node": ">=14.18" } }, "node_modules/temp-dir": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/tempy": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/tempy/-/tempy-3.0.0.tgz", "integrity": "sha512-B2I9X7+o2wOaW4r/CWMkpOO9mdiTRCxXNgob6iGvPmfPWgH/KyUD6Uy5crtWBxIBe3YrNZKR2lSzv1JJKWD4vA==", "dev": true, "dependencies": { "is-stream": "^3.0.0", "temp-dir": "^2.0.0", "type-fest": "^2.12.2", "unique-string": "^3.0.0" }, "engines": { "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/tempy/node_modules/is-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/tempy/node_modules/type-fest": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", "dev": true, "engines": { "node": ">=12.20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/text-extensions": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz", "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==", "dev": true, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", "dev": true, "dependencies": { "any-promise": "^1.0.0" } }, "node_modules/thenify-all": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", "dev": true, "dependencies": { "thenify": ">= 3.1.0 < 4" }, "engines": { "node": ">=0.8" } }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, "node_modules/time-span": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/time-span/-/time-span-5.1.0.tgz", "integrity": "sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==", "dev": true, "dependencies": { "convert-hrtime": "^5.0.0" }, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/tinyexec": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.0.tgz", "integrity": "sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==", "dev": true }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "dependencies": { "is-number": "^7.0.0" }, "engines": { "node": ">=8.0" } }, "node_modules/traverse": { "version": "0.6.7", "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.7.tgz", "integrity": "sha512-/y956gpUo9ZNCb99YjxG7OaslxZWHfCHAUUfshwqOXmxUIvqLjVO581BT+gM59+QV9tFe6/CGG53tsA1Y7RSdg==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/type-fest": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.12.0.tgz", "integrity": "sha512-5Y2/pp2wtJk8o08G0CMkuFPCO354FGwk/vbidxrdhRGZfd0tFnb4Qb8anp9XxXriwBgVPjdWbKpGl4J9lJY2jQ==", "dev": true, "engines": { "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/typescript": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "dev": true, "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { "node": ">=14.17" } }, "node_modules/uglify-js": { "version": "3.17.4", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", "dev": true, "optional": true, "bin": { "uglifyjs": "bin/uglifyjs" }, "engines": { "node": ">=0.8.0" } }, "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/unicode-emoji-modifier-base": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz", "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/unicorn-magic": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", "dev": true, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/unique-string": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", "dev": true, "dependencies": { "crypto-random-string": "^4.0.0" }, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/universal-user-agent": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz", "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==", "dev": true }, "node_modules/universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", "dev": true, "engines": { "node": ">= 10.0.0" } }, "node_modules/url-join": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz", "integrity": "sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==", "dev": true, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, "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/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" } }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" }, "engines": { "node": ">= 8" } }, "node_modules/wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", "dev": true }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", "dev": true, "engines": { "node": ">=0.4" } }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, "engines": { "node": ">=10" } }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, "node_modules/yargs": { "version": "17.7.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", "dev": true, "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" }, "engines": { "node": ">=12" } }, "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" } }, "node_modules/yargs/node_modules/yargs-parser": { "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, "engines": { "node": ">=12" } }, "node_modules/yocto-queue": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", "dev": true, "engines": { "node": ">=12.20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/yoctocolors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.0.0.tgz", "integrity": "sha512-esbDnt0Z1zI1KgvOZU90hJbL6BkoUbrP9yy7ArNZ6TmxBxydMJTYMf9FZjmwwcA8ZgEQzriQ3hwZ0NYXhlFo8Q==", "dev": true, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } } }, "dependencies": { "@babel/code-frame": { "version": "7.22.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dev": true, "requires": { "@babel/highlight": "^7.22.13", "chalk": "^2.4.2" }, "dependencies": { "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { "color-convert": "^1.9.0" } }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "requires": { "color-name": "1.1.3" } }, "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { "has-flag": "^3.0.0" } } } }, "@babel/helper-validator-identifier": { "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true }, "@babel/highlight": { "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "dependencies": { "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { "color-convert": "^1.9.0" } }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "requires": { "color-name": "1.1.3" } }, "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { "has-flag": "^3.0.0" } } } }, "@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, "optional": true }, "@commitlint/cli": { "version": "19.7.1", "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-19.7.1.tgz", "integrity": "sha512-iObGjR1tE/PfDtDTEfd+tnRkB3/HJzpQqRTyofS2MPPkDn1mp3DBC8SoPDayokfAy+xKhF8+bwRCJO25Nea0YQ==", "dev": true, "requires": { "@commitlint/format": "^19.5.0", "@commitlint/lint": "^19.7.1", "@commitlint/load": "^19.6.1", "@commitlint/read": "^19.5.0", "@commitlint/types": "^19.5.0", "tinyexec": "^0.3.0", "yargs": "^17.0.0" } }, "@commitlint/config-conventional": { "version": "19.7.1", "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-19.7.1.tgz", "integrity": "sha512-fsEIF8zgiI/FIWSnykdQNj/0JE4av08MudLTyYHm4FlLWemKoQvPNUYU2M/3tktWcCEyq7aOkDDgtjrmgWFbvg==", "dev": true, "requires": { "@commitlint/types": "^19.5.0", "conventional-changelog-conventionalcommits": "^7.0.2" } }, "@commitlint/config-validator": { "version": "19.5.0", "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-19.5.0.tgz", "integrity": "sha512-CHtj92H5rdhKt17RmgALhfQt95VayrUo2tSqY9g2w+laAXyk7K/Ef6uPm9tn5qSIwSmrLjKaXK9eiNuxmQrDBw==", "dev": true, "requires": { "@commitlint/types": "^19.5.0", "ajv": "^8.11.0" } }, "@commitlint/ensure": { "version": "19.5.0", "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-19.5.0.tgz", "integrity": "sha512-Kv0pYZeMrdg48bHFEU5KKcccRfKmISSm9MvgIgkpI6m+ohFTB55qZlBW6eYqh/XDfRuIO0x4zSmvBjmOwWTwkg==", "dev": true, "requires": { "@commitlint/types": "^19.5.0", "lodash.camelcase": "^4.3.0", "lodash.kebabcase": "^4.1.1", "lodash.snakecase": "^4.1.1", "lodash.startcase": "^4.4.0", "lodash.upperfirst": "^4.3.1" } }, "@commitlint/execute-rule": { "version": "19.5.0", "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-19.5.0.tgz", "integrity": "sha512-aqyGgytXhl2ejlk+/rfgtwpPexYyri4t8/n4ku6rRJoRhGZpLFMqrZ+YaubeGysCP6oz4mMA34YSTaSOKEeNrg==", "dev": true }, "@commitlint/format": { "version": "19.5.0", "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-19.5.0.tgz", "integrity": "sha512-yNy088miE52stCI3dhG/vvxFo9e4jFkU1Mj3xECfzp/bIS/JUay4491huAlVcffOoMK1cd296q0W92NlER6r3A==", "dev": true, "requires": { "@commitlint/types": "^19.5.0", "chalk": "^5.3.0" }, "dependencies": { "chalk": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true } } }, "@commitlint/is-ignored": { "version": "19.7.1", "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-19.7.1.tgz", "integrity": "sha512-3IaOc6HVg2hAoGleRK3r9vL9zZ3XY0rf1RsUf6jdQLuaD46ZHnXBiOPTyQ004C4IvYjSWqJwlh0/u2P73aIE3g==", "dev": true, "requires": { "@commitlint/types": "^19.5.0", "semver": "^7.6.0" } }, "@commitlint/lint": { "version": "19.7.1", "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-19.7.1.tgz", "integrity": "sha512-LhcPfVjcOcOZA7LEuBBeO00o3MeZa+tWrX9Xyl1r9PMd5FWsEoZI9IgnGqTKZ0lZt5pO3ZlstgnRyY1CJJc9Xg==", "dev": true, "requires": { "@commitlint/is-ignored": "^19.7.1", "@commitlint/parse": "^19.5.0", "@commitlint/rules": "^19.6.0", "@commitlint/types": "^19.5.0" } }, "@commitlint/load": { "version": "19.6.1", "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-19.6.1.tgz", "integrity": "sha512-kE4mRKWWNju2QpsCWt428XBvUH55OET2N4QKQ0bF85qS/XbsRGG1MiTByDNlEVpEPceMkDr46LNH95DtRwcsfA==", "dev": true, "requires": { "@commitlint/config-validator": "^19.5.0", "@commitlint/execute-rule": "^19.5.0", "@commitlint/resolve-extends": "^19.5.0", "@commitlint/types": "^19.5.0", "chalk": "^5.3.0", "cosmiconfig": "^9.0.0", "cosmiconfig-typescript-loader": "^6.1.0", "lodash.isplainobject": "^4.0.6", "lodash.merge": "^4.6.2", "lodash.uniq": "^4.5.0" }, "dependencies": { "chalk": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true } } }, "@commitlint/message": { "version": "19.5.0", "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-19.5.0.tgz", "integrity": "sha512-R7AM4YnbxN1Joj1tMfCyBryOC5aNJBdxadTZkuqtWi3Xj0kMdutq16XQwuoGbIzL2Pk62TALV1fZDCv36+JhTQ==", "dev": true }, "@commitlint/parse": { "version": "19.5.0", "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-19.5.0.tgz", "integrity": "sha512-cZ/IxfAlfWYhAQV0TwcbdR1Oc0/r0Ik1GEessDJ3Lbuma/MRO8FRQX76eurcXtmhJC//rj52ZSZuXUg0oIX0Fw==", "dev": true, "requires": { "@commitlint/types": "^19.5.0", "conventional-changelog-angular": "^7.0.0", "conventional-commits-parser": "^5.0.0" } }, "@commitlint/read": { "version": "19.5.0", "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-19.5.0.tgz", "integrity": "sha512-TjS3HLPsLsxFPQj6jou8/CZFAmOP2y+6V4PGYt3ihbQKTY1Jnv0QG28WRKl/d1ha6zLODPZqsxLEov52dhR9BQ==", "dev": true, "requires": { "@commitlint/top-level": "^19.5.0", "@commitlint/types": "^19.5.0", "git-raw-commits": "^4.0.0", "minimist": "^1.2.8", "tinyexec": "^0.3.0" } }, "@commitlint/resolve-extends": { "version": "19.5.0", "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-19.5.0.tgz", "integrity": "sha512-CU/GscZhCUsJwcKTJS9Ndh3AKGZTNFIOoQB2n8CmFnizE0VnEuJoum+COW+C1lNABEeqk6ssfc1Kkalm4bDklA==", "dev": true, "requires": { "@commitlint/config-validator": "^19.5.0", "@commitlint/types": "^19.5.0", "global-directory": "^4.0.1", "import-meta-resolve": "^4.0.0", "lodash.mergewith": "^4.6.2", "resolve-from": "^5.0.0" } }, "@commitlint/rules": { "version": "19.6.0", "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-19.6.0.tgz", "integrity": "sha512-1f2reW7lbrI0X0ozZMesS/WZxgPa4/wi56vFuJENBmed6mWq5KsheN/nxqnl/C23ioxpPO/PL6tXpiiFy5Bhjw==", "dev": true, "requires": { "@commitlint/ensure": "^19.5.0", "@commitlint/message": "^19.5.0", "@commitlint/to-lines": "^19.5.0", "@commitlint/types": "^19.5.0" } }, "@commitlint/to-lines": { "version": "19.5.0", "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-19.5.0.tgz", "integrity": "sha512-R772oj3NHPkodOSRZ9bBVNq224DOxQtNef5Pl8l2M8ZnkkzQfeSTr4uxawV2Sd3ui05dUVzvLNnzenDBO1KBeQ==", "dev": true }, "@commitlint/top-level": { "version": "19.5.0", "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-19.5.0.tgz", "integrity": "sha512-IP1YLmGAk0yWrImPRRc578I3dDUI5A2UBJx9FbSOjxe9sTlzFiwVJ+zeMLgAtHMtGZsC8LUnzmW1qRemkFU4ng==", "dev": true, "requires": { "find-up": "^7.0.0" } }, "@commitlint/types": { "version": "19.5.0", "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-19.5.0.tgz", "integrity": "sha512-DSHae2obMSMkAtTBSOulg5X7/z+rGLxcXQIkg3OmWvY6wifojge5uVMydfhUvs7yQj+V7jNmRZ2Xzl8GJyqRgg==", "dev": true, "requires": { "@types/conventional-commits-parser": "^5.0.0", "chalk": "^5.3.0" }, "dependencies": { "chalk": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true } } }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "requires": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "@nodelib/fs.stat": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true }, "@nodelib/fs.walk": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "requires": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "@octokit/auth-token": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.0.1.tgz", "integrity": "sha512-RTmWsLfig8SBoiSdgvCht4BXl1CHU89Co5xiQ5JF19my/sIRDFCQ1RPrmK0exgqUZuNm39C/bV8+/83+MJEjGg==", "dev": true }, "@octokit/core": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.0.1.tgz", "integrity": "sha512-MIpPQXu8Y8GjHwXM81JLveiV+DHJZtLMcB5nKekBGOl3iAtk0HT3i12Xl8Biybu+bCS1+k4qbuKEq5d0RxNRnQ==", "dev": true, "requires": { "@octokit/auth-token": "^5.0.0", "@octokit/graphql": "^8.0.0", "@octokit/request": "^9.0.0", "@octokit/request-error": "^6.0.1", "@octokit/types": "^12.0.0", "before-after-hook": "^3.0.2", "universal-user-agent": "^7.0.0" } }, "@octokit/endpoint": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.0.0.tgz", "integrity": "sha512-emBcNDxBdC1y3+knJonS5zhUB/CG6TihubxM2U1/pG/Z1y3a4oV0Gzz3lmkCvWWQI6h3tqBAX9MgCBFp+M68Jw==", "dev": true, "requires": { "@octokit/types": "^12.0.0", "universal-user-agent": "^7.0.2" } }, "@octokit/graphql": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.0.1.tgz", "integrity": "sha512-lLDb6LhC1gBj2CxEDa5Xk10+H/boonhs+3Mi6jpRyetskDKNHe6crMeKmUE2efoLofMP8ruannLlCUgpTFmVzQ==", "dev": true, "requires": { "@octokit/request": "^9.0.0", "@octokit/types": "^12.0.0", "universal-user-agent": "^7.0.0" } }, "@octokit/openapi-types": { "version": "20.0.0", "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz", "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==", "dev": true }, "@octokit/plugin-paginate-rest": { "version": "11.3.5", "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.3.5.tgz", "integrity": "sha512-cgwIRtKrpwhLoBi0CUNuY83DPGRMaWVjqVI/bGKsLJ4PzyWZNaEmhHroI2xlrVXkk6nFv0IsZpOp+ZWSWUS2AQ==", "dev": true, "requires": { "@octokit/types": "^13.6.0" }, "dependencies": { "@octokit/openapi-types": { "version": "22.2.0", "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz", "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==", "dev": true }, "@octokit/types": { "version": "13.6.1", "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.6.1.tgz", "integrity": "sha512-PHZE9Z+kWXb23Ndik8MKPirBPziOc0D2/3KH1P+6jK5nGWe96kadZuE4jev2/Jq7FvIfTlT2Ltg8Fv2x1v0a5g==", "dev": true, "requires": { "@octokit/openapi-types": "^22.2.0" } } } }, "@octokit/plugin-retry": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-7.0.3.tgz", "integrity": "sha512-T9l5Z7XnDZ7dkyNmhJPSUq0YjbqUT/xn4yQbhcSuv4WGC/LqM73/mKwkl68VDPoLw20e8oz4L7qQopWt9v6sow==", "dev": true, "requires": { "@octokit/request-error": "^6.0.0", "@octokit/types": "^12.0.0", "bottleneck": "^2.15.3" } }, "@octokit/plugin-throttling": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-9.0.3.tgz", "integrity": "sha512-DReKamrLBJOzld73dmmxV2H137QKJfsxszAczEZXeAJQ/Po6bzQacKajPdodA6T1jfmP9+waImus+d/R2j+R7Q==", "dev": true, "requires": { "@octokit/types": "^12.6.0", "bottleneck": "^2.15.3" } }, "@octokit/request": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.0.1.tgz", "integrity": "sha512-kL+cAcbSl3dctYLuJmLfx6Iku2MXXy0jszhaEIjQNaCp4zjHXrhVAHeuaRdNvJjW9qjl3u1MJ72+OuBP0YW/pg==", "dev": true, "requires": { "@octokit/endpoint": "^10.0.0", "@octokit/request-error": "^6.0.1", "@octokit/types": "^12.0.0", "universal-user-agent": "^7.0.2" } }, "@octokit/request-error": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.0.2.tgz", "integrity": "sha512-WtRVpoHcNXs84+s9s/wqfHaxM68NGMg8Av7h59B50OVO0PwwMx+2GgQ/OliUd0iQBSNWgR6N8afi/KjSHbXHWw==", "dev": true, "requires": { "@octokit/types": "^12.0.0" } }, "@octokit/types": { "version": "12.6.0", "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz", "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", "dev": true, "requires": { "@octokit/openapi-types": "^20.0.0" } }, "@pnpm/network.ca-file": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", "dev": true, "requires": { "graceful-fs": "4.2.10" } }, "@pnpm/npm-conf": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-1.0.5.tgz", "integrity": "sha512-hD8ml183638O3R6/Txrh0L8VzGOrFXgRtRDG4qQC4tONdZ5Z1M+tlUUDUvrjYdmK6G+JTBTeaCLMna11cXzi8A==", "dev": true, "requires": { "@pnpm/network.ca-file": "^1.0.1", "config-chain": "^1.1.11" } }, "@sec-ant/readable-stream": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", "dev": true }, "@semantic-release/changelog": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/@semantic-release/changelog/-/changelog-6.0.3.tgz", "integrity": "sha512-dZuR5qByyfe3Y03TpmCvAxCyTnp7r5XwtHRf/8vD9EAn4ZWbavUX8adMtXYzE86EVh0gyLA7lm5yW4IV30XUag==", "dev": true, "requires": { "@semantic-release/error": "^3.0.0", "aggregate-error": "^3.0.0", "fs-extra": "^11.0.0", "lodash": "^4.17.4" } }, "@semantic-release/commit-analyzer": { "version": "13.0.0", "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-13.0.0.tgz", "integrity": "sha512-KtXWczvTAB1ZFZ6B4O+w8HkfYm/OgQb1dUGNFZtDgQ0csggrmkq8sTxhd+lwGF8kMb59/RnG9o4Tn7M/I8dQ9Q==", "dev": true, "requires": { "conventional-changelog-angular": "^8.0.0", "conventional-changelog-writer": "^8.0.0", "conventional-commits-filter": "^5.0.0", "conventional-commits-parser": "^6.0.0", "debug": "^4.0.0", "import-from-esm": "^1.0.3", "lodash-es": "^4.17.21", "micromatch": "^4.0.2" }, "dependencies": { "conventional-changelog-angular": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-8.0.0.tgz", "integrity": "sha512-CLf+zr6St0wIxos4bmaKHRXWAcsCXrJU6F4VdNDrGRK3B8LDLKoX3zuMV5GhtbGkVR/LohZ6MT6im43vZLSjmA==", "dev": true, "requires": { "compare-func": "^2.0.0" } }, "conventional-commits-parser": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.0.0.tgz", "integrity": "sha512-TbsINLp48XeMXR8EvGjTnKGsZqBemisPoyWESlpRyR8lif0lcwzqz+NMtYSj1ooF/WYjSuu7wX0CtdeeMEQAmA==", "dev": true, "requires": { "meow": "^13.0.0" } }, "meow": { "version": "13.2.0", "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", "dev": true } } }, "@semantic-release/error": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-3.0.0.tgz", "integrity": "sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw==", "dev": true }, "@semantic-release/exec": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/@semantic-release/exec/-/exec-7.0.3.tgz", "integrity": "sha512-uNWwPNtWi3WTcTm3fWfFQEuj8otOvwoS5m9yo2jSVHuvqdZNsOWmuL0/FqcVyZnCI32fxyYV0G7PPb/TzCH6jw==", "dev": true, "requires": { "@semantic-release/error": "^4.0.0", "aggregate-error": "^3.0.0", "debug": "^4.0.0", "execa": "^9.0.0", "lodash-es": "^4.17.21", "parse-json": "^8.0.0" }, "dependencies": { "@semantic-release/error": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz", "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==", "dev": true }, "@sindresorhus/merge-streams": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", "dev": true }, "execa": { "version": "9.5.2", "resolved": "https://registry.npmjs.org/execa/-/execa-9.5.2.tgz", "integrity": "sha512-EHlpxMCpHWSAh1dgS6bVeoLAXGnJNdR93aabr4QCGbzOM73o5XmRfM/e5FUqsw3aagP8S8XEWUWFAxnRBnAF0Q==", "dev": true, "requires": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.3", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.0", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.0.0" } }, "get-stream": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", "dev": true, "requires": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "human-signals": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.0.tgz", "integrity": "sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA==", "dev": true }, "is-stream": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", "dev": true }, "npm-run-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", "dev": true, "requires": { "path-key": "^4.0.0", "unicorn-magic": "^0.3.0" } }, "parse-json": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.1.0.tgz", "integrity": "sha512-rum1bPifK5SSar35Z6EKZuYPJx85pkNaFrxBK3mwdfSJ1/WKbYrjoW/zTPSjRRamfmVX1ACBIdFAO0VRErW/EA==", "dev": true, "requires": { "@babel/code-frame": "^7.22.13", "index-to-position": "^0.1.2", "type-fest": "^4.7.1" } }, "path-key": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true }, "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 }, "strip-final-newline": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", "dev": true }, "unicorn-magic": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", "dev": true } } }, "@semantic-release/git": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/@semantic-release/git/-/git-10.0.1.tgz", "integrity": "sha512-eWrx5KguUcU2wUPaO6sfvZI0wPafUKAMNC18aXY4EnNcrZL86dEmpNVnC9uMpGZkmZJ9EfCVJBQx4pV4EMGT1w==", "dev": true, "requires": { "@semantic-release/error": "^3.0.0", "aggregate-error": "^3.0.0", "debug": "^4.0.0", "dir-glob": "^3.0.0", "execa": "^5.0.0", "lodash": "^4.17.4", "micromatch": "^4.0.0", "p-reduce": "^2.0.0" } }, "@semantic-release/github": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-11.0.0.tgz", "integrity": "sha512-Uon6G6gJD8U1JNvPm7X0j46yxNRJ8Ui6SgK4Zw5Ktu8RgjEft3BGn+l/RX1TTzhhO3/uUcKuqM+/9/ETFxWS/Q==", "dev": true, "requires": { "@octokit/core": "^6.0.0", "@octokit/plugin-paginate-rest": "^11.0.0", "@octokit/plugin-retry": "^7.0.0", "@octokit/plugin-throttling": "^9.0.0", "@semantic-release/error": "^4.0.0", "aggregate-error": "^5.0.0", "debug": "^4.3.4", "dir-glob": "^3.0.1", "globby": "^14.0.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", "issue-parser": "^7.0.0", "lodash-es": "^4.17.21", "mime": "^4.0.0", "p-filter": "^4.0.0", "url-join": "^5.0.0" }, "dependencies": { "@semantic-release/error": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz", "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==", "dev": true }, "aggregate-error": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz", "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==", "dev": true, "requires": { "clean-stack": "^5.2.0", "indent-string": "^5.0.0" } }, "clean-stack": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.2.0.tgz", "integrity": "sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==", "dev": true, "requires": { "escape-string-regexp": "5.0.0" } }, "indent-string": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", "dev": true } } }, "@semantic-release/npm": { "version": "12.0.0", "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-12.0.0.tgz", "integrity": "sha512-72TVYQCH9NvVsO/y13eF8vE4bNnfls518+4KcFwJUKi7AtA/ZXoNgSg9gTTfw5eMZMkiH0izUrpGXgZE/cSQhA==", "dev": true, "requires": { "@semantic-release/error": "^4.0.0", "aggregate-error": "^5.0.0", "execa": "^8.0.0", "fs-extra": "^11.0.0", "lodash-es": "^4.17.21", "nerf-dart": "^1.0.0", "normalize-url": "^8.0.0", "npm": "^10.5.0", "rc": "^1.2.8", "read-pkg": "^9.0.0", "registry-auth-token": "^5.0.0", "semver": "^7.1.2", "tempy": "^3.0.0" }, "dependencies": { "@semantic-release/error": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz", "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==", "dev": true }, "aggregate-error": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz", "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==", "dev": true, "requires": { "clean-stack": "^5.2.0", "indent-string": "^5.0.0" } }, "clean-stack": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.2.0.tgz", "integrity": "sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==", "dev": true, "requires": { "escape-string-regexp": "5.0.0" } }, "execa": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, "requires": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "get-stream": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", "dev": true }, "human-signals": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", "dev": true }, "indent-string": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", "dev": true }, "is-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true }, "mimic-fn": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", "dev": true }, "npm-run-path": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", "dev": true, "requires": { "path-key": "^4.0.0" } }, "onetime": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", "dev": true, "requires": { "mimic-fn": "^4.0.0" } }, "path-key": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true }, "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 }, "strip-final-newline": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", "dev": true } } }, "@semantic-release/release-notes-generator": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-14.0.0.tgz", "integrity": "sha512-XRxwr4e46yUMaXT8KGFBlRJlp5+NOMaufdq8qaEWlcJ7cT4Pn/iRmDGglZ2TgDe6GVP+u1boXFEnSs7N8Yzhng==", "dev": true, "requires": { "conventional-changelog-angular": "^8.0.0", "conventional-changelog-writer": "^8.0.0", "conventional-commits-filter": "^5.0.0", "conventional-commits-parser": "^6.0.0", "debug": "^4.0.0", "get-stream": "^7.0.0", "import-from-esm": "^1.0.3", "into-stream": "^7.0.0", "lodash-es": "^4.17.21", "read-pkg-up": "^11.0.0" }, "dependencies": { "conventional-changelog-angular": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-8.0.0.tgz", "integrity": "sha512-CLf+zr6St0wIxos4bmaKHRXWAcsCXrJU6F4VdNDrGRK3B8LDLKoX3zuMV5GhtbGkVR/LohZ6MT6im43vZLSjmA==", "dev": true, "requires": { "compare-func": "^2.0.0" } }, "conventional-commits-parser": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.0.0.tgz", "integrity": "sha512-TbsINLp48XeMXR8EvGjTnKGsZqBemisPoyWESlpRyR8lif0lcwzqz+NMtYSj1ooF/WYjSuu7wX0CtdeeMEQAmA==", "dev": true, "requires": { "meow": "^13.0.0" } }, "get-stream": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-7.0.1.tgz", "integrity": "sha512-3M8C1EOFN6r8AMUhwUAACIoXZJEOufDU5+0gFFN5uNs6XYOralD2Pqkl7m046va6x77FwposWXbAhPPIOus7mQ==", "dev": true }, "meow": { "version": "13.2.0", "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", "dev": true } } }, "@sindresorhus/is": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", "dev": true }, "@sindresorhus/merge-streams": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", "dev": true }, "@types/conventional-commits-parser": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@types/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", "integrity": "sha512-loB369iXNmAZglwWATL+WRe+CRMmmBPtpolYzIebFaX4YA3x+BEfLqhUAV9WanycKI3TG1IMr5bMJDajDKLlUQ==", "dev": true, "requires": { "@types/node": "*" } }, "@types/node": { "version": "18.18.7", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.7.tgz", "integrity": "sha512-bw+lEsxis6eqJYW8Ql6+yTqkE6RuFtsQPSe5JxXbqYRFQEER5aJA9a5UH9igqDWm3X4iLHIKOHlnAXLM4mi7uQ==", "dev": true, "requires": { "undici-types": "~5.26.4" } }, "@types/normalize-package-data": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.3.tgz", "integrity": "sha512-ehPtgRgaULsFG8x0NeYJvmyH1hmlfsNLujHe9dQEia/7MAJYdzMSi19JtchUHjmBA6XC/75dK55mzZH+RyieSg==", "dev": true }, "@types/semver": { "version": "7.5.8", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, "agent-base": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", "dev": true, "requires": { "debug": "^4.3.4" } }, "aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, "requires": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" } }, "ajv": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "requires": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "ansi-escapes": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.0.tgz", "integrity": "sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==", "dev": true, "requires": { "type-fest": "^3.0.0" }, "dependencies": { "type-fest": { "version": "3.13.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", "dev": true } } }, "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { "color-convert": "^2.0.1" } }, "any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", "dev": true }, "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, "argv-formatter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/argv-formatter/-/argv-formatter-1.0.0.tgz", "integrity": "sha512-F2+Hkm9xFaRg+GkaNnbwXNDV5O6pnCFEmqyhvfC/Ic5LbgOWjJh3L+mN/s91rxVL3znE7DYVpW0GJFT+4YBgWw==", "dev": true }, "array-ify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", "dev": true }, "before-after-hook": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz", "integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==", "dev": true }, "bottleneck": { "version": "2.19.5", "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", "dev": true }, "braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "requires": { "fill-range": "^7.1.1" } }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "char-regex": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true }, "clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true }, "cli-highlight": { "version": "2.1.11", "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", "dev": true, "requires": { "chalk": "^4.0.0", "highlight.js": "^10.7.1", "mz": "^2.4.0", "parse5": "^5.1.1", "parse5-htmlparser2-tree-adapter": "^6.0.0", "yargs": "^16.0.0" }, "dependencies": { "cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, "requires": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" } }, "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" } } } }, "cli-table3": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", "dev": true, "requires": { "@colors/colors": "1.5.0", "string-width": "^4.2.0" } }, "cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "requires": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { "color-name": "~1.1.4" } }, "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, "compare-func": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", "dev": true, "requires": { "array-ify": "^1.0.0", "dot-prop": "^5.1.0" } }, "config-chain": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", "dev": true, "requires": { "ini": "^1.3.4", "proto-list": "~1.2.1" } }, "conventional-changelog-angular": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz", "integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==", "dev": true, "requires": { "compare-func": "^2.0.0" } }, "conventional-changelog-conventionalcommits": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-7.0.2.tgz", "integrity": "sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==", "dev": true, "requires": { "compare-func": "^2.0.0" } }, "conventional-changelog-writer": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-8.0.0.tgz", "integrity": "sha512-TQcoYGRatlAnT2qEWDON/XSfnVG38JzA7E0wcGScu7RElQBkg9WWgZd1peCWFcWDh1xfb2CfsrcvOn1bbSzztA==", "dev": true, "requires": { "@types/semver": "^7.5.5", "conventional-commits-filter": "^5.0.0", "handlebars": "^4.7.7", "meow": "^13.0.0", "semver": "^7.5.2" }, "dependencies": { "meow": { "version": "13.2.0", "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", "dev": true } } }, "conventional-commits-filter": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-5.0.0.tgz", "integrity": "sha512-tQMagCOC59EVgNZcC5zl7XqO30Wki9i9J3acbUvkaosCT6JX3EeFwJD7Qqp4MCikRnzS18WXV3BLIQ66ytu6+Q==", "dev": true }, "conventional-commits-parser": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==", "dev": true, "requires": { "is-text-path": "^2.0.0", "JSONStream": "^1.3.5", "meow": "^12.0.1", "split2": "^4.0.0" } }, "convert-hrtime": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/convert-hrtime/-/convert-hrtime-5.0.0.tgz", "integrity": "sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==", "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 }, "cosmiconfig": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", "dev": true, "requires": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0" } }, "cosmiconfig-typescript-loader": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.1.0.tgz", "integrity": "sha512-tJ1w35ZRUiM5FeTzT7DtYWAFFv37ZLqSRkGi2oeCK1gPhvaWjkAtfXvLmvE1pRfxxp9aQo6ba/Pvg1dKj05D4g==", "dev": true, "requires": { "jiti": "^2.4.1" } }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "requires": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "crypto-random-string": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", "dev": true, "requires": { "type-fest": "^1.0.1" }, "dependencies": { "type-fest": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", "dev": true } } }, "dargs": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/dargs/-/dargs-8.1.0.tgz", "integrity": "sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==", "dev": true }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { "ms": "2.1.2" } }, "deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true }, "dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, "requires": { "path-type": "^4.0.0" } }, "dot-prop": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", "dev": true, "requires": { "is-obj": "^2.0.0" } }, "duplexer2": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", "dev": true, "requires": { "readable-stream": "^2.0.2" } }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, "emojilib": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==", "dev": true }, "env-ci": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-11.0.0.tgz", "integrity": "sha512-apikxMgkipkgTvMdRT9MNqWx5VLOci79F4VBd7Op/7OPjjoanjdAvn6fglMCCEf/1bAh8eOiuEVCUs4V3qP3nQ==", "dev": true, "requires": { "execa": "^8.0.0", "java-properties": "^1.0.2" }, "dependencies": { "execa": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, "requires": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "get-stream": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", "dev": true }, "human-signals": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", "dev": true }, "is-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true }, "mimic-fn": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", "dev": true }, "npm-run-path": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", "dev": true, "requires": { "path-key": "^4.0.0" } }, "onetime": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", "dev": true, "requires": { "mimic-fn": "^4.0.0" } }, "path-key": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true }, "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 }, "strip-final-newline": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", "dev": true } } }, "env-paths": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "dev": true }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, "requires": { "is-arrayish": "^0.2.1" } }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true }, "escape-string-regexp": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", "dev": true }, "execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, "requires": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, "fast-glob": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" } }, "fast-uri": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==", "dev": true }, "fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dev": true, "requires": { "reusify": "^1.0.4" } }, "figures": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", "dev": true, "requires": { "is-unicode-supported": "^2.0.0" } }, "fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "requires": { "to-regex-range": "^5.0.1" } }, "find-up": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz", "integrity": "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==", "dev": true, "requires": { "locate-path": "^7.2.0", "path-exists": "^5.0.0", "unicorn-magic": "^0.1.0" } }, "find-up-simple": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.0.tgz", "integrity": "sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw==", "dev": true }, "find-versions": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-6.0.0.tgz", "integrity": "sha512-2kCCtc+JvcZ86IGAz3Z2Y0A1baIz9fL31pH/0S1IqZr9Iwnjq8izfPtrCyQKO6TLMPELLsQMre7VDqeIKCsHkA==", "dev": true, "requires": { "semver-regex": "^4.0.5", "super-regex": "^1.0.0" } }, "from2": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", "dev": true, "requires": { "inherits": "^2.0.1", "readable-stream": "^2.0.0" } }, "fs-extra": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz", "integrity": "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==", "dev": true, "requires": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, "function-timeout": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/function-timeout/-/function-timeout-1.0.1.tgz", "integrity": "sha512-6yPMImFFuaMPNaTMTBuolA8EanHJWF5Vju0NHpObRURT105J6x1Mf2a7J4P7Sqk2xDxv24N5L0RatEhTBhNmdA==", "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-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true }, "git-log-parser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/git-log-parser/-/git-log-parser-1.2.0.tgz", "integrity": "sha512-rnCVNfkTL8tdNryFuaY0fYiBWEBcgF748O6ZI61rslBvr2o7U65c2/6npCRqH40vuAhtgtDiqLTJjBVdrejCzA==", "dev": true, "requires": { "argv-formatter": "~1.0.0", "spawn-error-forwarder": "~1.0.0", "split2": "~1.0.0", "stream-combiner2": "~1.1.1", "through2": "~2.0.0", "traverse": "~0.6.6" }, "dependencies": { "split2": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/split2/-/split2-1.0.0.tgz", "integrity": "sha512-NKywug4u4pX/AZBB1FCPzZ6/7O+Xhz1qMVbzTvvKvikjO99oPN87SkK08mEY9P63/5lWjK+wgOOgApnTg5r6qg==", "dev": true, "requires": { "through2": "~2.0.0" } }, "through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, "requires": { "readable-stream": "~2.3.6", "xtend": "~4.0.1" } } } }, "git-raw-commits": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-4.0.0.tgz", "integrity": "sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==", "dev": true, "requires": { "dargs": "^8.0.0", "meow": "^12.0.1", "split2": "^4.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" } }, "global-directory": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", "dev": true, "requires": { "ini": "4.1.1" }, "dependencies": { "ini": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", "dev": true } } }, "globby": { "version": "14.0.1", "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.1.tgz", "integrity": "sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ==", "dev": true, "requires": { "@sindresorhus/merge-streams": "^2.1.0", "fast-glob": "^3.3.2", "ignore": "^5.2.4", "path-type": "^5.0.0", "slash": "^5.1.0", "unicorn-magic": "^0.1.0" }, "dependencies": { "path-type": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==", "dev": true } } }, "graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, "handlebars": { "version": "4.7.8", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", "dev": true, "requires": { "minimist": "^1.2.5", "neo-async": "^2.6.2", "source-map": "^0.6.1", "uglify-js": "^3.1.4", "wordwrap": "^1.0.0" } }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "requires": { "function-bind": "^1.1.1" } }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, "highlight.js": { "version": "10.7.3", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", "dev": true }, "hook-std": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-3.0.0.tgz", "integrity": "sha512-jHRQzjSDzMtFy34AGj1DN+vq54WVuhSvKgrHf0OMiFQTwDD4L/qqofVEWjLOBMTn5+lCD3fPg32W9yOfnEJTTw==", "dev": true }, "hosted-git-info": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.1.tgz", "integrity": "sha512-+K84LB1DYwMHoHSgaOY/Jfhw3ucPmSET5v98Ke/HdNSw4a0UktWzyW1mjhjpuxxTqOOsfWT/7iVshHmVZ4IpOA==", "dev": true, "requires": { "lru-cache": "^10.0.1" }, "dependencies": { "lru-cache": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", "dev": true } } }, "http-proxy-agent": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", "dev": true, "requires": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "https-proxy-agent": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.0.tgz", "integrity": "sha512-0euwPCRyAPSgGdzD1IVN9nJYHtBhJwb6XPfbpQcYbPCwrBidX6GzxmchnaF4sfF/jPb74Ojx5g4yTg3sixlyPw==", "dev": true, "requires": { "agent-base": "^7.0.2", "debug": "4" } }, "human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true }, "ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true }, "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, "requires": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" }, "dependencies": { "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true } } }, "import-from-esm": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/import-from-esm/-/import-from-esm-1.3.3.tgz", "integrity": "sha512-U3Qt/CyfFpTUv6LOP2jRTLYjphH6zg3okMfHbyqRa/W2w6hr8OsJWVggNlR4jxuojQy81TgTJTxgSkyoteRGMQ==", "dev": true, "requires": { "debug": "^4.3.4", "import-meta-resolve": "^4.0.0" } }, "import-meta-resolve": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.0.0.tgz", "integrity": "sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA==", "dev": true }, "indent-string": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true }, "index-to-position": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-0.1.2.tgz", "integrity": "sha512-MWDKS3AS1bGCHLBA2VLImJz42f7bJh8wQsTGCzI3j519/CASStoDONUBVz2I/VID0MpiX3SGSnbOD2xUalbE5g==", "dev": true }, "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, "ini": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, "into-stream": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-7.0.0.tgz", "integrity": "sha512-2dYz766i9HprMBasCMvHMuazJ7u4WzhJwo5kb3iPSiW/iRYV6uPari3zHoqZlnuaR7V1bEiNMxikhp37rdBXbw==", "dev": true, "requires": { "from2": "^2.3.0", "p-is-promise": "^3.0.0" } }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, "is-core-module": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", "dev": true, "requires": { "has": "^1.0.3" } }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true }, "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, "is-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-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", "dev": true }, "is-plain-obj": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", "dev": true }, "is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true }, "is-text-path": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz", "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==", "dev": true, "requires": { "text-extensions": "^2.0.0" } }, "is-unicode-supported": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.0.0.tgz", "integrity": "sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q==", "dev": true }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "dev": true }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "issue-parser": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-7.0.0.tgz", "integrity": "sha512-jgAw78HO3gs9UrKqJNQvfDj9Ouy8Mhu40fbEJ8yXff4MW8+/Fcn9iFjyWUQ6SKbX8ipPk3X5A3AyfYHRu6uVLw==", "dev": true, "requires": { "lodash.capitalize": "^4.2.1", "lodash.escaperegexp": "^4.1.2", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.uniqby": "^4.7.0" } }, "java-properties": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/java-properties/-/java-properties-1.0.2.tgz", "integrity": "sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ==", "dev": true }, "jiti": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.1.tgz", "integrity": "sha512-yPBThwecp1wS9DmoA4x4KR2h3QoslacnDR8ypuFM962kI4/456Iy1oHx2RAgh4jfZNdn0bctsdadceiBUgpU1g==", "dev": true }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, "js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "requires": { "argparse": "^2.0.1" } }, "json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "dev": true }, "json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, "json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, "jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "requires": { "graceful-fs": "^4.1.6", "universalify": "^2.0.0" } }, "jsonparse": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", "dev": true }, "JSONStream": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", "dev": true, "requires": { "jsonparse": "^1.2.0", "through": ">=2.2.7 <3" } }, "lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, "load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", "dev": true, "requires": { "graceful-fs": "^4.1.2", "parse-json": "^4.0.0", "pify": "^3.0.0", "strip-bom": "^3.0.0" }, "dependencies": { "parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", "dev": true, "requires": { "error-ex": "^1.3.1", "json-parse-better-errors": "^1.0.1" } } } }, "locate-path": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", "dev": true, "requires": { "p-locate": "^6.0.0" } }, "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, "lodash-es": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", "dev": true }, "lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", "dev": true }, "lodash.capitalize": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", "integrity": "sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw==", "dev": true }, "lodash.escaperegexp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", "dev": true }, "lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", "dev": true }, "lodash.isstring": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", "dev": true }, "lodash.kebabcase": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", "dev": true }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, "lodash.mergewith": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", "dev": true }, "lodash.snakecase": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", "dev": true }, "lodash.startcase": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", "dev": true }, "lodash.uniq": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", "dev": true }, "lodash.uniqby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==", "dev": true }, "lodash.upperfirst": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==", "dev": true }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "requires": { "yallist": "^4.0.0" } }, "marked": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/marked/-/marked-12.0.1.tgz", "integrity": "sha512-Y1/V2yafOcOdWQCX0XpAKXzDakPOpn6U0YLxTJs3cww6VxOzZV1BTOOYWLvH3gX38cq+iLwljHHTnMtlDfg01Q==", "dev": true }, "marked-terminal": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-7.0.0.tgz", "integrity": "sha512-sNEx8nn9Ktcm6pL0TnRz8tnXq/mSS0Q1FRSwJOAqw4lAB4l49UeDf85Gm1n9RPFm5qurCPjwi1StAQT2XExhZw==", "dev": true, "requires": { "ansi-escapes": "^6.2.0", "chalk": "^5.3.0", "cli-highlight": "^2.1.11", "cli-table3": "^0.6.3", "node-emoji": "^2.1.3", "supports-hyperlinks": "^3.0.0" }, "dependencies": { "chalk": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true } } }, "meow": { "version": "12.1.1", "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", "dev": true }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true }, "micromatch": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "requires": { "braces": "^3.0.2", "picomatch": "^2.3.1" } }, "mime": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.1.tgz", "integrity": "sha512-5lZ5tyrIfliMXzFtkYyekWbtRXObT9OWa8IwQ5uxTBDHucNNwniRqo0yInflj+iYi5CBa6qxadGzGarDfuEOxA==", "dev": true }, "mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true }, "minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true }, "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 }, "mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", "dev": true, "requires": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, "nerf-dart": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/nerf-dart/-/nerf-dart-1.0.0.tgz", "integrity": "sha512-EZSPZB70jiVsivaBLYDCyntd5eH8NTSMOn3rB+HxwdmKThGELLdYv8qVIMWvZEFy9w8ZZpW9h9OB32l1rGtj7g==", "dev": true }, "node-emoji": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.1.3.tgz", "integrity": "sha512-E2WEOVsgs7O16zsURJ/eH8BqhF029wGpEOnv7Urwdo2wmQanOACwJQh0devF9D9RhoZru0+9JXIS0dBXIAz+lA==", "dev": true, "requires": { "@sindresorhus/is": "^4.6.0", "char-regex": "^1.0.2", "emojilib": "^2.4.0", "skin-tone": "^2.0.0" } }, "normalize-package-data": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.0.tgz", "integrity": "sha512-UL7ELRVxYBHBgYEtZCXjxuD5vPxnmvMGq0jp/dGPKKrN7tfsBh2IY7TlJ15WWwdjRWD3RJbnsygUurTK3xkPkg==", "dev": true, "requires": { "hosted-git-info": "^7.0.0", "is-core-module": "^2.8.1", "semver": "^7.3.5", "validate-npm-package-license": "^3.0.4" } }, "normalize-url": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz", "integrity": "sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==", "dev": true }, "npm": { "version": "10.5.0", "resolved": "https://registry.npmjs.org/npm/-/npm-10.5.0.tgz", "integrity": "sha512-Ejxwvfh9YnWVU2yA5FzoYLTW52vxHCz+MHrOFg9Cc8IFgF/6f5AGPAvb5WTay5DIUP1NIfN3VBZ0cLlGO0Ys+A==", "dev": true, "requires": { "@isaacs/string-locale-compare": "^1.1.0", "@npmcli/arborist": "^7.2.1", "@npmcli/config": "^8.0.2", "@npmcli/fs": "^3.1.0", "@npmcli/map-workspaces": "^3.0.4", "@npmcli/package-json": "^5.0.0", "@npmcli/promise-spawn": "^7.0.1", "@npmcli/run-script": "^7.0.4", "@sigstore/tuf": "^2.3.1", "abbrev": "^2.0.0", "archy": "~1.0.0", "cacache": "^18.0.2", "chalk": "^5.3.0", "ci-info": "^4.0.0", "cli-columns": "^4.0.0", "cli-table3": "^0.6.3", "columnify": "^1.6.0", "fastest-levenshtein": "^1.0.16", "fs-minipass": "^3.0.3", "glob": "^10.3.10", "graceful-fs": "^4.2.11", "hosted-git-info": "^7.0.1", "ini": "^4.1.1", "init-package-json": "^6.0.0", "is-cidr": "^5.0.3", "json-parse-even-better-errors": "^3.0.1", "libnpmaccess": "^8.0.1", "libnpmdiff": "^6.0.3", "libnpmexec": "^7.0.4", "libnpmfund": "^5.0.1", "libnpmhook": "^10.0.0", "libnpmorg": "^6.0.1", "libnpmpack": "^6.0.3", "libnpmpublish": "^9.0.2", "libnpmsearch": "^7.0.0", "libnpmteam": "^6.0.0", "libnpmversion": "^5.0.1", "make-fetch-happen": "^13.0.0", "minimatch": "^9.0.3", "minipass": "^7.0.4", "minipass-pipeline": "^1.2.4", "ms": "^2.1.2", "node-gyp": "^10.0.1", "nopt": "^7.2.0", "normalize-package-data": "^6.0.0", "npm-audit-report": "^5.0.0", "npm-install-checks": "^6.3.0", "npm-package-arg": "^11.0.1", "npm-pick-manifest": "^9.0.0", "npm-profile": "^9.0.0", "npm-registry-fetch": "^16.1.0", "npm-user-validate": "^2.0.0", "npmlog": "^7.0.1", "p-map": "^4.0.0", "pacote": "^17.0.6", "parse-conflict-json": "^3.0.1", "proc-log": "^3.0.0", "qrcode-terminal": "^0.12.0", "read": "^2.1.0", "semver": "^7.6.0", "spdx-expression-parse": "^3.0.1", "ssri": "^10.0.5", "supports-color": "^9.4.0", "tar": "^6.2.0", "text-table": "~0.2.0", "tiny-relative-date": "^1.3.0", "treeverse": "^3.0.0", "validate-npm-package-name": "^5.0.0", "which": "^4.0.0", "write-file-atomic": "^5.0.1" }, "dependencies": { "@colors/colors": { "version": "1.5.0", "bundled": true, "dev": true, "optional": true }, "@isaacs/cliui": { "version": "8.0.2", "bundled": true, "dev": true, "requires": { "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", "bundled": true, "dev": true }, "emoji-regex": { "version": "9.2.2", "bundled": true, "dev": true }, "string-width": { "version": "5.1.2", "bundled": true, "dev": true, "requires": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "strip-ansi": { "version": "7.1.0", "bundled": true, "dev": true, "requires": { "ansi-regex": "^6.0.1" } } } }, "@isaacs/string-locale-compare": { "version": "1.1.0", "bundled": true, "dev": true }, "@npmcli/agent": { "version": "2.2.1", "bundled": true, "dev": true, "requires": { "agent-base": "^7.1.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.1", "lru-cache": "^10.0.1", "socks-proxy-agent": "^8.0.1" } }, "@npmcli/arborist": { "version": "7.4.0", "bundled": true, "dev": true, "requires": { "@isaacs/string-locale-compare": "^1.1.0", "@npmcli/fs": "^3.1.0", "@npmcli/installed-package-contents": "^2.0.2", "@npmcli/map-workspaces": "^3.0.2", "@npmcli/metavuln-calculator": "^7.0.0", "@npmcli/name-from-folder": "^2.0.0", "@npmcli/node-gyp": "^3.0.0", "@npmcli/package-json": "^5.0.0", "@npmcli/query": "^3.1.0", "@npmcli/run-script": "^7.0.2", "bin-links": "^4.0.1", "cacache": "^18.0.0", "common-ancestor-path": "^1.0.1", "hosted-git-info": "^7.0.1", "json-parse-even-better-errors": "^3.0.0", "json-stringify-nice": "^1.1.4", "minimatch": "^9.0.0", "nopt": "^7.0.0", "npm-install-checks": "^6.2.0", "npm-package-arg": "^11.0.1", "npm-pick-manifest": "^9.0.0", "npm-registry-fetch": "^16.0.0", "npmlog": "^7.0.1", "pacote": "^17.0.4", "parse-conflict-json": "^3.0.0", "proc-log": "^3.0.0", "promise-all-reject-late": "^1.0.0", "promise-call-limit": "^3.0.1", "read-package-json-fast": "^3.0.2", "semver": "^7.3.7", "ssri": "^10.0.5", "treeverse": "^3.0.0", "walk-up-path": "^3.0.1" } }, "@npmcli/config": { "version": "8.2.0", "bundled": true, "dev": true, "requires": { "@npmcli/map-workspaces": "^3.0.2", "ci-info": "^4.0.0", "ini": "^4.1.0", "nopt": "^7.0.0", "proc-log": "^3.0.0", "read-package-json-fast": "^3.0.2", "semver": "^7.3.5", "walk-up-path": "^3.0.1" } }, "@npmcli/disparity-colors": { "version": "3.0.0", "bundled": true, "dev": true, "requires": { "ansi-styles": "^4.3.0" }, "dependencies": { "ansi-styles": { "version": "4.3.0", "bundled": true, "dev": true, "requires": { "color-convert": "^2.0.1" } } } }, "@npmcli/fs": { "version": "3.1.0", "bundled": true, "dev": true, "requires": { "semver": "^7.3.5" } }, "@npmcli/git": { "version": "5.0.4", "bundled": true, "dev": true, "requires": { "@npmcli/promise-spawn": "^7.0.0", "lru-cache": "^10.0.1", "npm-pick-manifest": "^9.0.0", "proc-log": "^3.0.0", "promise-inflight": "^1.0.1", "promise-retry": "^2.0.1", "semver": "^7.3.5", "which": "^4.0.0" } }, "@npmcli/installed-package-contents": { "version": "2.0.2", "bundled": true, "dev": true, "requires": { "npm-bundled": "^3.0.0", "npm-normalize-package-bin": "^3.0.0" } }, "@npmcli/map-workspaces": { "version": "3.0.4", "bundled": true, "dev": true, "requires": { "@npmcli/name-from-folder": "^2.0.0", "glob": "^10.2.2", "minimatch": "^9.0.0", "read-package-json-fast": "^3.0.0" } }, "@npmcli/metavuln-calculator": { "version": "7.0.0", "bundled": true, "dev": true, "requires": { "cacache": "^18.0.0", "json-parse-even-better-errors": "^3.0.0", "pacote": "^17.0.0", "semver": "^7.3.5" } }, "@npmcli/name-from-folder": { "version": "2.0.0", "bundled": true, "dev": true }, "@npmcli/node-gyp": { "version": "3.0.0", "bundled": true, "dev": true }, "@npmcli/package-json": { "version": "5.0.0", "bundled": true, "dev": true, "requires": { "@npmcli/git": "^5.0.0", "glob": "^10.2.2", "hosted-git-info": "^7.0.0", "json-parse-even-better-errors": "^3.0.0", "normalize-package-data": "^6.0.0", "proc-log": "^3.0.0", "semver": "^7.5.3" } }, "@npmcli/promise-spawn": { "version": "7.0.1", "bundled": true, "dev": true, "requires": { "which": "^4.0.0" } }, "@npmcli/query": { "version": "3.1.0", "bundled": true, "dev": true, "requires": { "postcss-selector-parser": "^6.0.10" } }, "@npmcli/run-script": { "version": "7.0.4", "bundled": true, "dev": true, "requires": { "@npmcli/node-gyp": "^3.0.0", "@npmcli/package-json": "^5.0.0", "@npmcli/promise-spawn": "^7.0.0", "node-gyp": "^10.0.0", "which": "^4.0.0" } }, "@pkgjs/parseargs": { "version": "0.11.0", "bundled": true, "dev": true, "optional": true }, "@sigstore/bundle": { "version": "2.2.0", "bundled": true, "dev": true, "requires": { "@sigstore/protobuf-specs": "^0.3.0" } }, "@sigstore/core": { "version": "1.0.0", "bundled": true, "dev": true }, "@sigstore/protobuf-specs": { "version": "0.3.0", "bundled": true, "dev": true }, "@sigstore/sign": { "version": "2.2.3", "bundled": true, "dev": true, "requires": { "@sigstore/bundle": "^2.2.0", "@sigstore/core": "^1.0.0", "@sigstore/protobuf-specs": "^0.3.0", "make-fetch-happen": "^13.0.0" } }, "@sigstore/tuf": { "version": "2.3.1", "bundled": true, "dev": true, "requires": { "@sigstore/protobuf-specs": "^0.3.0", "tuf-js": "^2.2.0" } }, "@sigstore/verify": { "version": "1.1.0", "bundled": true, "dev": true, "requires": { "@sigstore/bundle": "^2.2.0", "@sigstore/core": "^1.0.0", "@sigstore/protobuf-specs": "^0.3.0" } }, "@tufjs/canonical-json": { "version": "2.0.0", "bundled": true, "dev": true }, "@tufjs/models": { "version": "2.0.0", "bundled": true, "dev": true, "requires": { "@tufjs/canonical-json": "2.0.0", "minimatch": "^9.0.3" } }, "abbrev": { "version": "2.0.0", "bundled": true, "dev": true }, "agent-base": { "version": "7.1.0", "bundled": true, "dev": true, "requires": { "debug": "^4.3.4" } }, "aggregate-error": { "version": "3.1.0", "bundled": true, "dev": true, "requires": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" } }, "ansi-regex": { "version": "5.0.1", "bundled": true, "dev": true }, "ansi-styles": { "version": "6.2.1", "bundled": true, "dev": true }, "aproba": { "version": "2.0.0", "bundled": true, "dev": true }, "archy": { "version": "1.0.0", "bundled": true, "dev": true }, "are-we-there-yet": { "version": "4.0.2", "bundled": true, "dev": true }, "balanced-match": { "version": "1.0.2", "bundled": true, "dev": true }, "bin-links": { "version": "4.0.3", "bundled": true, "dev": true, "requires": { "cmd-shim": "^6.0.0", "npm-normalize-package-bin": "^3.0.0", "read-cmd-shim": "^4.0.0", "write-file-atomic": "^5.0.0" } }, "binary-extensions": { "version": "2.2.0", "bundled": true, "dev": true }, "brace-expansion": { "version": "2.0.1", "bundled": true, "dev": true, "requires": { "balanced-match": "^1.0.0" } }, "builtins": { "version": "5.0.1", "bundled": true, "dev": true, "requires": { "semver": "^7.0.0" } }, "cacache": { "version": "18.0.2", "bundled": true, "dev": true, "requires": { "@npmcli/fs": "^3.1.0", "fs-minipass": "^3.0.0", "glob": "^10.2.2", "lru-cache": "^10.0.1", "minipass": "^7.0.3", "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "p-map": "^4.0.0", "ssri": "^10.0.0", "tar": "^6.1.11", "unique-filename": "^3.0.0" } }, "chalk": { "version": "5.3.0", "bundled": true, "dev": true }, "chownr": { "version": "2.0.0", "bundled": true, "dev": true }, "ci-info": { "version": "4.0.0", "bundled": true, "dev": true }, "cidr-regex": { "version": "4.0.3", "bundled": true, "dev": true, "requires": { "ip-regex": "^5.0.0" } }, "clean-stack": { "version": "2.2.0", "bundled": true, "dev": true }, "cli-columns": { "version": "4.0.0", "bundled": true, "dev": true, "requires": { "string-width": "^4.2.3", "strip-ansi": "^6.0.1" } }, "cli-table3": { "version": "0.6.3", "bundled": true, "dev": true, "requires": { "@colors/colors": "1.5.0", "string-width": "^4.2.0" } }, "clone": { "version": "1.0.4", "bundled": true, "dev": true }, "cmd-shim": { "version": "6.0.2", "bundled": true, "dev": true }, "color-convert": { "version": "2.0.1", "bundled": true, "dev": true, "requires": { "color-name": "~1.1.4" } }, "color-name": { "version": "1.1.4", "bundled": true, "dev": true }, "color-support": { "version": "1.1.3", "bundled": true, "dev": true }, "columnify": { "version": "1.6.0", "bundled": true, "dev": true, "requires": { "strip-ansi": "^6.0.1", "wcwidth": "^1.0.0" } }, "common-ancestor-path": { "version": "1.0.1", "bundled": true, "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, "dev": true }, "cross-spawn": { "version": "7.0.3", "bundled": true, "dev": true, "requires": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" }, "dependencies": { "which": { "version": "2.0.2", "bundled": true, "dev": true, "requires": { "isexe": "^2.0.0" } } } }, "cssesc": { "version": "3.0.0", "bundled": true, "dev": true }, "debug": { "version": "4.3.4", "bundled": true, "dev": true, "requires": { "ms": "2.1.2" }, "dependencies": { "ms": { "version": "2.1.2", "bundled": true, "dev": true } } }, "defaults": { "version": "1.0.4", "bundled": true, "dev": true, "requires": { "clone": "^1.0.2" } }, "diff": { "version": "5.2.0", "bundled": true, "dev": true }, "eastasianwidth": { "version": "0.2.0", "bundled": true, "dev": true }, "emoji-regex": { "version": "8.0.0", "bundled": true, "dev": true }, "encoding": { "version": "0.1.13", "bundled": true, "dev": true, "optional": true, "requires": { "iconv-lite": "^0.6.2" } }, "env-paths": { "version": "2.2.1", "bundled": true, "dev": true }, "err-code": { "version": "2.0.3", "bundled": true, "dev": true }, "exponential-backoff": { "version": "3.1.1", "bundled": true, "dev": true }, "fastest-levenshtein": { "version": "1.0.16", "bundled": true, "dev": true }, "foreground-child": { "version": "3.1.1", "bundled": true, "dev": true, "requires": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" } }, "fs-minipass": { "version": "3.0.3", "bundled": true, "dev": true, "requires": { "minipass": "^7.0.3" } }, "function-bind": { "version": "1.1.2", "bundled": true, "dev": true }, "gauge": { "version": "5.0.1", "bundled": true, "dev": true, "requires": { "aproba": "^1.0.3 || ^2.0.0", "color-support": "^1.1.3", "console-control-strings": "^1.1.0", "has-unicode": "^2.0.1", "signal-exit": "^4.0.1", "string-width": "^4.2.3", "strip-ansi": "^6.0.1", "wide-align": "^1.1.5" } }, "glob": { "version": "10.3.10", "bundled": true, "dev": true, "requires": { "foreground-child": "^3.1.0", "jackspeak": "^2.3.5", "minimatch": "^9.0.1", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", "path-scurry": "^1.10.1" } }, "graceful-fs": { "version": "4.2.11", "bundled": true, "dev": true }, "has-unicode": { "version": "2.0.1", "bundled": true, "dev": true }, "hasown": { "version": "2.0.1", "bundled": true, "dev": true, "requires": { "function-bind": "^1.1.2" } }, "hosted-git-info": { "version": "7.0.1", "bundled": true, "dev": true, "requires": { "lru-cache": "^10.0.1" } }, "http-cache-semantics": { "version": "4.1.1", "bundled": true, "dev": true }, "http-proxy-agent": { "version": "7.0.2", "bundled": true, "dev": true, "requires": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "https-proxy-agent": { "version": "7.0.4", "bundled": true, "dev": true, "requires": { "agent-base": "^7.0.2", "debug": "4" } }, "iconv-lite": { "version": "0.6.3", "bundled": true, "dev": true, "optional": true, "requires": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "ignore-walk": { "version": "6.0.4", "bundled": true, "dev": true, "requires": { "minimatch": "^9.0.0" } }, "imurmurhash": { "version": "0.1.4", "bundled": true, "dev": true }, "indent-string": { "version": "4.0.0", "bundled": true, "dev": true }, "ini": { "version": "4.1.1", "bundled": true, "dev": true }, "init-package-json": { "version": "6.0.0", "bundled": true, "dev": true, "requires": { "npm-package-arg": "^11.0.0", "promzard": "^1.0.0", "read": "^2.0.0", "read-package-json": "^7.0.0", "semver": "^7.3.5", "validate-npm-package-license": "^3.0.4", "validate-npm-package-name": "^5.0.0" } }, "ip-address": { "version": "9.0.5", "bundled": true, "dev": true, "requires": { "jsbn": "1.1.0", "sprintf-js": "^1.1.3" }, "dependencies": { "sprintf-js": { "version": "1.1.3", "bundled": true, "dev": true } } }, "ip-regex": { "version": "5.0.0", "bundled": true, "dev": true }, "is-cidr": { "version": "5.0.3", "bundled": true, "dev": true, "requires": { "cidr-regex": "4.0.3" } }, "is-core-module": { "version": "2.13.1", "bundled": true, "dev": true, "requires": { "hasown": "^2.0.0" } }, "is-fullwidth-code-point": { "version": "3.0.0", "bundled": true, "dev": true }, "is-lambda": { "version": "1.0.1", "bundled": true, "dev": true }, "isexe": { "version": "2.0.0", "bundled": true, "dev": true }, "jackspeak": { "version": "2.3.6", "bundled": true, "dev": true, "requires": { "@isaacs/cliui": "^8.0.2", "@pkgjs/parseargs": "^0.11.0" } }, "jsbn": { "version": "1.1.0", "bundled": true, "dev": true }, "json-parse-even-better-errors": { "version": "3.0.1", "bundled": true, "dev": true }, "json-stringify-nice": { "version": "1.1.4", "bundled": true, "dev": true }, "jsonparse": { "version": "1.3.1", "bundled": true, "dev": true }, "just-diff": { "version": "6.0.2", "bundled": true, "dev": true }, "just-diff-apply": { "version": "5.5.0", "bundled": true, "dev": true }, "libnpmaccess": { "version": "8.0.2", "bundled": true, "dev": true, "requires": { "npm-package-arg": "^11.0.1", "npm-registry-fetch": "^16.0.0" } }, "libnpmdiff": { "version": "6.0.7", "bundled": true, "dev": true, "requires": { "@npmcli/arborist": "^7.2.1", "@npmcli/disparity-colors": "^3.0.0", "@npmcli/installed-package-contents": "^2.0.2", "binary-extensions": "^2.2.0", "diff": "^5.1.0", "minimatch": "^9.0.0", "npm-package-arg": "^11.0.1", "pacote": "^17.0.4", "tar": "^6.2.0" } }, "libnpmexec": { "version": "7.0.8", "bundled": true, "dev": true, "requires": { "@npmcli/arborist": "^7.2.1", "@npmcli/run-script": "^7.0.2", "ci-info": "^4.0.0", "npm-package-arg": "^11.0.1", "npmlog": "^7.0.1", "pacote": "^17.0.4", "proc-log": "^3.0.0", "read": "^2.0.0", "read-package-json-fast": "^3.0.2", "semver": "^7.3.7", "walk-up-path": "^3.0.1" } }, "libnpmfund": { "version": "5.0.5", "bundled": true, "dev": true, "requires": { "@npmcli/arborist": "^7.2.1" } }, "libnpmhook": { "version": "10.0.1", "bundled": true, "dev": true, "requires": { "aproba": "^2.0.0", "npm-registry-fetch": "^16.0.0" } }, "libnpmorg": { "version": "6.0.2", "bundled": true, "dev": true, "requires": { "aproba": "^2.0.0", "npm-registry-fetch": "^16.0.0" } }, "libnpmpack": { "version": "6.0.7", "bundled": true, "dev": true, "requires": { "@npmcli/arborist": "^7.2.1", "@npmcli/run-script": "^7.0.2", "npm-package-arg": "^11.0.1", "pacote": "^17.0.4" } }, "libnpmpublish": { "version": "9.0.4", "bundled": true, "dev": true, "requires": { "ci-info": "^4.0.0", "normalize-package-data": "^6.0.0", "npm-package-arg": "^11.0.1", "npm-registry-fetch": "^16.0.0", "proc-log": "^3.0.0", "semver": "^7.3.7", "sigstore": "^2.2.0", "ssri": "^10.0.5" } }, "libnpmsearch": { "version": "7.0.1", "bundled": true, "dev": true, "requires": { "npm-registry-fetch": "^16.0.0" } }, "libnpmteam": { "version": "6.0.1", "bundled": true, "dev": true, "requires": { "aproba": "^2.0.0", "npm-registry-fetch": "^16.0.0" } }, "libnpmversion": { "version": "5.0.2", "bundled": true, "dev": true, "requires": { "@npmcli/git": "^5.0.3", "@npmcli/run-script": "^7.0.2", "json-parse-even-better-errors": "^3.0.0", "proc-log": "^3.0.0", "semver": "^7.3.7" } }, "lru-cache": { "version": "10.2.0", "bundled": true, "dev": true }, "make-fetch-happen": { "version": "13.0.0", "bundled": true, "dev": true, "requires": { "@npmcli/agent": "^2.0.0", "cacache": "^18.0.0", "http-cache-semantics": "^4.1.1", "is-lambda": "^1.0.1", "minipass": "^7.0.2", "minipass-fetch": "^3.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^0.6.3", "promise-retry": "^2.0.1", "ssri": "^10.0.0" } }, "minimatch": { "version": "9.0.3", "bundled": true, "dev": true, "requires": { "brace-expansion": "^2.0.1" } }, "minipass": { "version": "7.0.4", "bundled": true, "dev": true }, "minipass-collect": { "version": "2.0.1", "bundled": true, "dev": true, "requires": { "minipass": "^7.0.3" } }, "minipass-fetch": { "version": "3.0.4", "bundled": true, "dev": true, "requires": { "encoding": "^0.1.13", "minipass": "^7.0.3", "minipass-sized": "^1.0.3", "minizlib": "^2.1.2" } }, "minipass-flush": { "version": "1.0.5", "bundled": true, "dev": true, "requires": { "minipass": "^3.0.0" }, "dependencies": { "minipass": { "version": "3.3.6", "bundled": true, "dev": true, "requires": { "yallist": "^4.0.0" } } } }, "minipass-json-stream": { "version": "1.0.1", "bundled": true, "dev": true, "requires": { "jsonparse": "^1.3.1", "minipass": "^3.0.0" }, "dependencies": { "minipass": { "version": "3.3.6", "bundled": true, "dev": true, "requires": { "yallist": "^4.0.0" } } } }, "minipass-pipeline": { "version": "1.2.4", "bundled": true, "dev": true, "requires": { "minipass": "^3.0.0" }, "dependencies": { "minipass": { "version": "3.3.6", "bundled": true, "dev": true, "requires": { "yallist": "^4.0.0" } } } }, "minipass-sized": { "version": "1.0.3", "bundled": true, "dev": true, "requires": { "minipass": "^3.0.0" }, "dependencies": { "minipass": { "version": "3.3.6", "bundled": true, "dev": true, "requires": { "yallist": "^4.0.0" } } } }, "minizlib": { "version": "2.1.2", "bundled": true, "dev": true, "requires": { "minipass": "^3.0.0", "yallist": "^4.0.0" }, "dependencies": { "minipass": { "version": "3.3.6", "bundled": true, "dev": true, "requires": { "yallist": "^4.0.0" } } } }, "mkdirp": { "version": "1.0.4", "bundled": true, "dev": true }, "ms": { "version": "2.1.3", "bundled": true, "dev": true }, "mute-stream": { "version": "1.0.0", "bundled": true, "dev": true }, "negotiator": { "version": "0.6.3", "bundled": true, "dev": true }, "node-gyp": { "version": "10.0.1", "bundled": true, "dev": true, "requires": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", "glob": "^10.3.10", "graceful-fs": "^4.2.6", "make-fetch-happen": "^13.0.0", "nopt": "^7.0.0", "proc-log": "^3.0.0", "semver": "^7.3.5", "tar": "^6.1.2", "which": "^4.0.0" } }, "nopt": { "version": "7.2.0", "bundled": true, "dev": true, "requires": { "abbrev": "^2.0.0" } }, "normalize-package-data": { "version": "6.0.0", "bundled": true, "dev": true, "requires": { "hosted-git-info": "^7.0.0", "is-core-module": "^2.8.1", "semver": "^7.3.5", "validate-npm-package-license": "^3.0.4" } }, "npm-audit-report": { "version": "5.0.0", "bundled": true, "dev": true }, "npm-bundled": { "version": "3.0.0", "bundled": true, "dev": true, "requires": { "npm-normalize-package-bin": "^3.0.0" } }, "npm-install-checks": { "version": "6.3.0", "bundled": true, "dev": true, "requires": { "semver": "^7.1.1" } }, "npm-normalize-package-bin": { "version": "3.0.1", "bundled": true, "dev": true }, "npm-package-arg": { "version": "11.0.1", "bundled": true, "dev": true, "requires": { "hosted-git-info": "^7.0.0", "proc-log": "^3.0.0", "semver": "^7.3.5", "validate-npm-package-name": "^5.0.0" } }, "npm-packlist": { "version": "8.0.2", "bundled": true, "dev": true, "requires": { "ignore-walk": "^6.0.4" } }, "npm-pick-manifest": { "version": "9.0.0", "bundled": true, "dev": true, "requires": { "npm-install-checks": "^6.0.0", "npm-normalize-package-bin": "^3.0.0", "npm-package-arg": "^11.0.0", "semver": "^7.3.5" } }, "npm-profile": { "version": "9.0.0", "bundled": true, "dev": true, "requires": { "npm-registry-fetch": "^16.0.0", "proc-log": "^3.0.0" } }, "npm-registry-fetch": { "version": "16.1.0", "bundled": true, "dev": true, "requires": { "make-fetch-happen": "^13.0.0", "minipass": "^7.0.2", "minipass-fetch": "^3.0.0", "minipass-json-stream": "^1.0.1", "minizlib": "^2.1.2", "npm-package-arg": "^11.0.0", "proc-log": "^3.0.0" } }, "npm-user-validate": { "version": "2.0.0", "bundled": true, "dev": true }, "npmlog": { "version": "7.0.1", "bundled": true, "dev": true, "requires": { "are-we-there-yet": "^4.0.0", "console-control-strings": "^1.1.0", "gauge": "^5.0.0", "set-blocking": "^2.0.0" } }, "p-map": { "version": "4.0.0", "bundled": true, "dev": true, "requires": { "aggregate-error": "^3.0.0" } }, "pacote": { "version": "17.0.6", "bundled": true, "dev": true, "requires": { "@npmcli/git": "^5.0.0", "@npmcli/installed-package-contents": "^2.0.1", "@npmcli/promise-spawn": "^7.0.0", "@npmcli/run-script": "^7.0.0", "cacache": "^18.0.0", "fs-minipass": "^3.0.0", "minipass": "^7.0.2", "npm-package-arg": "^11.0.0", "npm-packlist": "^8.0.0", "npm-pick-manifest": "^9.0.0", "npm-registry-fetch": "^16.0.0", "proc-log": "^3.0.0", "promise-retry": "^2.0.1", "read-package-json": "^7.0.0", "read-package-json-fast": "^3.0.0", "sigstore": "^2.2.0", "ssri": "^10.0.0", "tar": "^6.1.11" } }, "parse-conflict-json": { "version": "3.0.1", "bundled": true, "dev": true, "requires": { "json-parse-even-better-errors": "^3.0.0", "just-diff": "^6.0.0", "just-diff-apply": "^5.2.0" } }, "path-key": { "version": "3.1.1", "bundled": true, "dev": true }, "path-scurry": { "version": "1.10.1", "bundled": true, "dev": true, "requires": { "lru-cache": "^9.1.1 || ^10.0.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "postcss-selector-parser": { "version": "6.0.15", "bundled": true, "dev": true, "requires": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "proc-log": { "version": "3.0.0", "bundled": true, "dev": true }, "promise-all-reject-late": { "version": "1.0.1", "bundled": true, "dev": true }, "promise-call-limit": { "version": "3.0.1", "bundled": true, "dev": true }, "promise-inflight": { "version": "1.0.1", "bundled": true, "dev": true }, "promise-retry": { "version": "2.0.1", "bundled": true, "dev": true, "requires": { "err-code": "^2.0.2", "retry": "^0.12.0" } }, "promzard": { "version": "1.0.0", "bundled": true, "dev": true, "requires": { "read": "^2.0.0" } }, "qrcode-terminal": { "version": "0.12.0", "bundled": true, "dev": true }, "read": { "version": "2.1.0", "bundled": true, "dev": true, "requires": { "mute-stream": "~1.0.0" } }, "read-cmd-shim": { "version": "4.0.0", "bundled": true, "dev": true }, "read-package-json": { "version": "7.0.0", "bundled": true, "dev": true, "requires": { "glob": "^10.2.2", "json-parse-even-better-errors": "^3.0.0", "normalize-package-data": "^6.0.0", "npm-normalize-package-bin": "^3.0.0" } }, "read-package-json-fast": { "version": "3.0.2", "bundled": true, "dev": true, "requires": { "json-parse-even-better-errors": "^3.0.0", "npm-normalize-package-bin": "^3.0.0" } }, "retry": { "version": "0.12.0", "bundled": true, "dev": true }, "safer-buffer": { "version": "2.1.2", "bundled": true, "dev": true, "optional": true }, "semver": { "version": "7.6.0", "bundled": true, "dev": true, "requires": { "lru-cache": "^6.0.0" }, "dependencies": { "lru-cache": { "version": "6.0.0", "bundled": true, "dev": true, "requires": { "yallist": "^4.0.0" } } } }, "set-blocking": { "version": "2.0.0", "bundled": true, "dev": true }, "shebang-command": { "version": "2.0.0", "bundled": true, "dev": true, "requires": { "shebang-regex": "^3.0.0" } }, "shebang-regex": { "version": "3.0.0", "bundled": true, "dev": true }, "signal-exit": { "version": "4.1.0", "bundled": true, "dev": true }, "sigstore": { "version": "2.2.2", "bundled": true, "dev": true, "requires": { "@sigstore/bundle": "^2.2.0", "@sigstore/core": "^1.0.0", "@sigstore/protobuf-specs": "^0.3.0", "@sigstore/sign": "^2.2.3", "@sigstore/tuf": "^2.3.1", "@sigstore/verify": "^1.1.0" } }, "smart-buffer": { "version": "4.2.0", "bundled": true, "dev": true }, "socks": { "version": "2.8.0", "bundled": true, "dev": true, "requires": { "ip-address": "^9.0.5", "smart-buffer": "^4.2.0" } }, "socks-proxy-agent": { "version": "8.0.2", "bundled": true, "dev": true, "requires": { "agent-base": "^7.0.2", "debug": "^4.3.4", "socks": "^2.7.1" } }, "spdx-correct": { "version": "3.2.0", "bundled": true, "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" } }, "spdx-exceptions": { "version": "2.5.0", "bundled": true, "dev": true }, "spdx-expression-parse": { "version": "3.0.1", "bundled": true, "dev": true, "requires": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "spdx-license-ids": { "version": "3.0.17", "bundled": true, "dev": true }, "ssri": { "version": "10.0.5", "bundled": true, "dev": true, "requires": { "minipass": "^7.0.3" } }, "string-width": { "version": "4.2.3", "bundled": true, "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "string-width-cjs": { "version": "npm:string-width@4.2.3", "bundled": true, "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "strip-ansi": { "version": "6.0.1", "bundled": true, "dev": true, "requires": { "ansi-regex": "^5.0.1" } }, "strip-ansi-cjs": { "version": "npm:strip-ansi@6.0.1", "bundled": true, "dev": true, "requires": { "ansi-regex": "^5.0.1" } }, "supports-color": { "version": "9.4.0", "bundled": true, "dev": true }, "tar": { "version": "6.2.0", "bundled": true, "dev": true, "requires": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" }, "dependencies": { "fs-minipass": { "version": "2.1.0", "bundled": true, "dev": true, "requires": { "minipass": "^3.0.0" }, "dependencies": { "minipass": { "version": "3.3.6", "bundled": true, "dev": true, "requires": { "yallist": "^4.0.0" } } } }, "minipass": { "version": "5.0.0", "bundled": true, "dev": true } } }, "text-table": { "version": "0.2.0", "bundled": true, "dev": true }, "tiny-relative-date": { "version": "1.3.0", "bundled": true, "dev": true }, "treeverse": { "version": "3.0.0", "bundled": true, "dev": true }, "tuf-js": { "version": "2.2.0", "bundled": true, "dev": true, "requires": { "@tufjs/models": "2.0.0", "debug": "^4.3.4", "make-fetch-happen": "^13.0.0" } }, "unique-filename": { "version": "3.0.0", "bundled": true, "dev": true, "requires": { "unique-slug": "^4.0.0" } }, "unique-slug": { "version": "4.0.0", "bundled": true, "dev": true, "requires": { "imurmurhash": "^0.1.4" } }, "util-deprecate": { "version": "1.0.2", "bundled": true, "dev": true }, "validate-npm-package-license": { "version": "3.0.4", "bundled": true, "dev": true, "requires": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" } }, "validate-npm-package-name": { "version": "5.0.0", "bundled": true, "dev": true, "requires": { "builtins": "^5.0.0" } }, "walk-up-path": { "version": "3.0.1", "bundled": true, "dev": true }, "wcwidth": { "version": "1.0.1", "bundled": true, "dev": true, "requires": { "defaults": "^1.0.3" } }, "which": { "version": "4.0.0", "bundled": true, "dev": true, "requires": { "isexe": "^3.1.1" }, "dependencies": { "isexe": { "version": "3.1.1", "bundled": true, "dev": true } } }, "wide-align": { "version": "1.1.5", "bundled": true, "dev": true, "requires": { "string-width": "^1.0.2 || 2 || 3 || 4" } }, "wrap-ansi": { "version": "8.1.0", "bundled": true, "dev": true, "requires": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" }, "dependencies": { "ansi-regex": { "version": "6.0.1", "bundled": true, "dev": true }, "emoji-regex": { "version": "9.2.2", "bundled": true, "dev": true }, "string-width": { "version": "5.1.2", "bundled": true, "dev": true, "requires": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "strip-ansi": { "version": "7.1.0", "bundled": true, "dev": true, "requires": { "ansi-regex": "^6.0.1" } } } }, "wrap-ansi-cjs": { "version": "npm:wrap-ansi@7.0.0", "bundled": true, "dev": true, "requires": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" }, "dependencies": { "ansi-styles": { "version": "4.3.0", "bundled": true, "dev": true, "requires": { "color-convert": "^2.0.1" } } } }, "write-file-atomic": { "version": "5.0.1", "bundled": true, "dev": true, "requires": { "imurmurhash": "^0.1.4", "signal-exit": "^4.0.1" } }, "yallist": { "version": "4.0.0", "bundled": true, "dev": true } } }, "npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, "requires": { "path-key": "^3.0.0" } }, "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 }, "onetime": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "requires": { "mimic-fn": "^2.1.0" } }, "p-each-series": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-3.0.0.tgz", "integrity": "sha512-lastgtAdoH9YaLyDa5i5z64q+kzOcQHsQ5SsZJD3q0VEyI8mq872S3geuNbRUQLVAE9siMfgKrpj7MloKFHruw==", "dev": true }, "p-filter": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-4.1.0.tgz", "integrity": "sha512-37/tPdZ3oJwHaS3gNJdenCDB3Tz26i9sjhnguBtvN0vYlRIiDNnvTWkuh+0hETV9rLPdJ3rlL3yVOYPIAnM8rw==", "dev": true, "requires": { "p-map": "^7.0.1" } }, "p-is-promise": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==", "dev": true }, "p-limit": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", "dev": true, "requires": { "yocto-queue": "^1.0.0" } }, "p-locate": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", "dev": true, "requires": { "p-limit": "^4.0.0" } }, "p-map": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.1.tgz", "integrity": "sha512-2wnaR0XL/FDOj+TgpDuRb2KTjLnu3Fma6b1ZUwGY7LcqenMcvP/YFpjpbPKY6WVGsbuJZRuoUz8iPrt8ORnAFw==", "dev": true }, "p-reduce": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz", "integrity": "sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==", "dev": true }, "p-try": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", "dev": true }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "requires": { "callsites": "^3.0.0" } }, "parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "parse-ms": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", "dev": true }, "parse5": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", "dev": true }, "parse5-htmlparser2-tree-adapter": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", "dev": true, "requires": { "parse5": "^6.0.1" }, "dependencies": { "parse5": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", "dev": true } } }, "path-exists": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", "dev": true }, "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", "dev": true }, "pkg-conf": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", "integrity": "sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==", "dev": true, "requires": { "find-up": "^2.0.0", "load-json-file": "^4.0.0" }, "dependencies": { "find-up": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", "dev": true, "requires": { "locate-path": "^2.0.0" } }, "locate-path": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", "dev": true, "requires": { "p-locate": "^2.0.0", "path-exists": "^3.0.0" } }, "p-limit": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", "dev": true, "requires": { "p-try": "^1.0.0" } }, "p-locate": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", "dev": true, "requires": { "p-limit": "^1.1.0" } }, "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", "dev": true } } }, "pretty-ms": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.0.0.tgz", "integrity": "sha512-E9e9HJ9R9NasGOgPaPE8VMeiPKAyWR5jcFpNnwIejslIhWqdqOrb2wShBsncMPUb+BcCd2OPYfh7p2W6oemTng==", "dev": true, "requires": { "parse-ms": "^4.0.0" } }, "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 }, "proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", "dev": true }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, "rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, "requires": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" } }, "read-package-up": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-11.0.0.tgz", "integrity": "sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ==", "dev": true, "requires": { "find-up-simple": "^1.0.0", "read-pkg": "^9.0.0", "type-fest": "^4.6.0" } }, "read-pkg": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", "dev": true, "requires": { "@types/normalize-package-data": "^2.4.3", "normalize-package-data": "^6.0.0", "parse-json": "^8.0.0", "type-fest": "^4.6.0", "unicorn-magic": "^0.1.0" }, "dependencies": { "parse-json": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.1.0.tgz", "integrity": "sha512-rum1bPifK5SSar35Z6EKZuYPJx85pkNaFrxBK3mwdfSJ1/WKbYrjoW/zTPSjRRamfmVX1ACBIdFAO0VRErW/EA==", "dev": true, "requires": { "@babel/code-frame": "^7.22.13", "index-to-position": "^0.1.2", "type-fest": "^4.7.1" } } } }, "read-pkg-up": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-11.0.0.tgz", "integrity": "sha512-LOVbvF1Q0SZdjClSefZ0Nz5z8u+tIE7mV5NibzmE9VYmDe9CaBbAVtz1veOSZbofrdsilxuDAYnFenukZVp8/Q==", "dev": true, "requires": { "find-up-simple": "^1.0.0", "read-pkg": "^9.0.0", "type-fest": "^4.6.0" } }, "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": { "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" } }, "registry-auth-token": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.1.tgz", "integrity": "sha512-UfxVOj8seK1yaIOiieV4FIP01vfBDLsY0H9sQzi9EbbUdJiuuBjJgLa1DpImXMNPnVkBD4eVxTEXcrZA6kfpJA==", "dev": true, "requires": { "@pnpm/npm-conf": "^1.0.4" } }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, "require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true }, "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true }, "run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "requires": { "queue-microtask": "^1.2.2" } }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, "semantic-release": { "version": "24.2.3", "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-24.2.3.tgz", "integrity": "sha512-KRhQG9cUazPavJiJEFIJ3XAMjgfd0fcK3B+T26qOl8L0UG5aZUjeRfREO0KM5InGtYwxqiiytkJrbcYoLDEv0A==", "dev": true, "requires": { "@semantic-release/commit-analyzer": "^13.0.0-beta.1", "@semantic-release/error": "^4.0.0", "@semantic-release/github": "^11.0.0", "@semantic-release/npm": "^12.0.0", "@semantic-release/release-notes-generator": "^14.0.0-beta.1", "aggregate-error": "^5.0.0", "cosmiconfig": "^9.0.0", "debug": "^4.0.0", "env-ci": "^11.0.0", "execa": "^9.0.0", "figures": "^6.0.0", "find-versions": "^6.0.0", "get-stream": "^6.0.0", "git-log-parser": "^1.2.0", "hook-std": "^3.0.0", "hosted-git-info": "^8.0.0", "import-from-esm": "^2.0.0", "lodash-es": "^4.17.21", "marked": "^12.0.0", "marked-terminal": "^7.0.0", "micromatch": "^4.0.2", "p-each-series": "^3.0.0", "p-reduce": "^3.0.0", "read-package-up": "^11.0.0", "resolve-from": "^5.0.0", "semver": "^7.3.2", "semver-diff": "^4.0.0", "signale": "^1.2.1", "yargs": "^17.5.1" }, "dependencies": { "@semantic-release/error": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz", "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==", "dev": true }, "@sindresorhus/merge-streams": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", "dev": true }, "aggregate-error": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz", "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==", "dev": true, "requires": { "clean-stack": "^5.2.0", "indent-string": "^5.0.0" } }, "clean-stack": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.2.0.tgz", "integrity": "sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==", "dev": true, "requires": { "escape-string-regexp": "5.0.0" } }, "execa": { "version": "9.5.2", "resolved": "https://registry.npmjs.org/execa/-/execa-9.5.2.tgz", "integrity": "sha512-EHlpxMCpHWSAh1dgS6bVeoLAXGnJNdR93aabr4QCGbzOM73o5XmRfM/e5FUqsw3aagP8S8XEWUWFAxnRBnAF0Q==", "dev": true, "requires": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.3", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.0", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.0.0" }, "dependencies": { "get-stream": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", "dev": true, "requires": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } } } }, "hosted-git-info": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.0.2.tgz", "integrity": "sha512-sYKnA7eGln5ov8T8gnYlkSOxFJvywzEx9BueN6xo/GKO8PGiI6uK6xx+DIGe45T3bdVjLAQDQW1aicT8z8JwQg==", "dev": true, "requires": { "lru-cache": "^10.0.1" } }, "human-signals": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.0.tgz", "integrity": "sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA==", "dev": true }, "import-from-esm": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/import-from-esm/-/import-from-esm-2.0.0.tgz", "integrity": "sha512-YVt14UZCgsX1vZQ3gKjkWVdBdHQ6eu3MPU1TBgL1H5orXe2+jWD006WCPPtOuwlQm10NuzOW5WawiF1Q9veW8g==", "dev": true, "requires": { "debug": "^4.3.4", "import-meta-resolve": "^4.0.0" } }, "indent-string": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", "dev": true }, "is-stream": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", "dev": true }, "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 }, "npm-run-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", "dev": true, "requires": { "path-key": "^4.0.0", "unicorn-magic": "^0.3.0" } }, "p-reduce": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-3.0.0.tgz", "integrity": "sha512-xsrIUgI0Kn6iyDYm9StOpOeK29XM1aboGji26+QEortiFST1hGZaUQOLhtEbqHErPpGW/aSz6allwK2qcptp0Q==", "dev": true }, "path-key": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true }, "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 }, "strip-final-newline": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", "dev": true }, "unicorn-magic": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", "dev": true } } }, "semver": { "version": "7.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, "requires": { "lru-cache": "^6.0.0" } }, "semver-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", "dev": true, "requires": { "semver": "^7.3.5" } }, "semver-regex": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-4.0.5.tgz", "integrity": "sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==", "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 }, "signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, "signale": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/signale/-/signale-1.4.0.tgz", "integrity": "sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==", "dev": true, "requires": { "chalk": "^2.3.2", "figures": "^2.0.0", "pkg-conf": "^2.1.0" }, "dependencies": { "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { "color-convert": "^1.9.0" } }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "requires": { "color-name": "1.1.3" } }, "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true }, "figures": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", "dev": true, "requires": { "escape-string-regexp": "^1.0.5" } }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { "has-flag": "^3.0.0" } } } }, "skin-tone": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==", "dev": true, "requires": { "unicode-emoji-modifier-base": "^1.0.0" } }, "slash": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", "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 }, "spawn-error-forwarder": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz", "integrity": "sha512-gRjMgK5uFjbCvdibeGJuy3I5OYz6VLoVdsOJdA6wV0WlfQVLFueoqMxwwYD9RODdgb6oUIvlRlsyFSiQkMKu0g==", "dev": true }, "spdx-correct": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" } }, "spdx-exceptions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", "dev": true }, "spdx-expression-parse": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "dev": true, "requires": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "spdx-license-ids": { "version": "3.0.12", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", "dev": true }, "split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", "dev": true }, "stream-combiner2": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", "integrity": "sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==", "dev": true, "requires": { "duplexer2": "~0.1.0", "readable-stream": "^2.0.2" } }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" } }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { "ansi-regex": "^5.0.1" } }, "strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true }, "strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "dev": true }, "super-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/super-regex/-/super-regex-1.0.0.tgz", "integrity": "sha512-CY8u7DtbvucKuquCmOFEKhr9Besln7n9uN8eFbwcoGYWXOMW07u2o8njWaiXt11ylS3qoGF55pILjRmPlbodyg==", "dev": true, "requires": { "function-timeout": "^1.0.1", "time-span": "^5.1.0" } }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { "has-flag": "^4.0.0" } }, "supports-hyperlinks": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.0.0.tgz", "integrity": "sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==", "dev": true, "requires": { "has-flag": "^4.0.0", "supports-color": "^7.0.0" } }, "temp-dir": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", "dev": true }, "tempy": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/tempy/-/tempy-3.0.0.tgz", "integrity": "sha512-B2I9X7+o2wOaW4r/CWMkpOO9mdiTRCxXNgob6iGvPmfPWgH/KyUD6Uy5crtWBxIBe3YrNZKR2lSzv1JJKWD4vA==", "dev": true, "requires": { "is-stream": "^3.0.0", "temp-dir": "^2.0.0", "type-fest": "^2.12.2", "unique-string": "^3.0.0" }, "dependencies": { "is-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true }, "type-fest": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", "dev": true } } }, "text-extensions": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz", "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==", "dev": true }, "thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", "dev": true, "requires": { "any-promise": "^1.0.0" } }, "thenify-all": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", "dev": true, "requires": { "thenify": ">= 3.1.0 < 4" } }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, "time-span": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/time-span/-/time-span-5.1.0.tgz", "integrity": "sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==", "dev": true, "requires": { "convert-hrtime": "^5.0.0" } }, "tinyexec": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.0.tgz", "integrity": "sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==", "dev": true }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "requires": { "is-number": "^7.0.0" } }, "traverse": { "version": "0.6.7", "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.7.tgz", "integrity": "sha512-/y956gpUo9ZNCb99YjxG7OaslxZWHfCHAUUfshwqOXmxUIvqLjVO581BT+gM59+QV9tFe6/CGG53tsA1Y7RSdg==", "dev": true }, "type-fest": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.12.0.tgz", "integrity": "sha512-5Y2/pp2wtJk8o08G0CMkuFPCO354FGwk/vbidxrdhRGZfd0tFnb4Qb8anp9XxXriwBgVPjdWbKpGl4J9lJY2jQ==", "dev": true }, "typescript": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "dev": true, "peer": true }, "uglify-js": { "version": "3.17.4", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", "dev": true, "optional": 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 }, "unicode-emoji-modifier-base": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz", "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==", "dev": true }, "unicorn-magic": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", "dev": true }, "unique-string": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", "dev": true, "requires": { "crypto-random-string": "^4.0.0" } }, "universal-user-agent": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz", "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==", "dev": true }, "universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", "dev": true }, "url-join": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz", "integrity": "sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==", "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 }, "validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, "requires": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" } }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "requires": { "isexe": "^2.0.0" } }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", "dev": true }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "requires": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", "dev": true }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, "yargs": { "version": "17.7.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", "dev": true, "requires": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" }, "dependencies": { "yargs-parser": { "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true } } }, "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 }, "yocto-queue": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", "dev": true }, "yoctocolors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.0.0.tgz", "integrity": "sha512-esbDnt0Z1zI1KgvOZU90hJbL6BkoUbrP9yy7ArNZ6TmxBxydMJTYMf9FZjmwwcA8ZgEQzriQ3hwZ0NYXhlFo8Q==", "dev": true } } } autosuspend-7.2.0/package.json000066400000000000000000000004141475670032600163710ustar00rootroot00000000000000{ "devDependencies": { "@commitlint/cli": "19.7.1", "@commitlint/config-conventional": "19.7.1", "@semantic-release/changelog": "6.0.3", "@semantic-release/exec": "7.0.3", "@semantic-release/git": "10.0.1", "semantic-release": "24.2.3" } } autosuspend-7.2.0/pyproject.toml000066400000000000000000000033621475670032600170240ustar00rootroot00000000000000[build-system] requires = ["setuptools", "wheel"] [tool.ruff] src = ["src"] target-version = "py310" [tool.ruff.lint] select = [ "E", "F", "D", "ANN", "S", # "BLE", "B", "A", "C4", "T10", "DTZ", "EXE", "ISC", "G", "PIE", "T20", "PT", "Q", "RET", "SLF", "SIM", "TID", "TCH", "ARG", "PTH", "ERA", "TRY", "RUF", "UP", ] ignore = [ # We do this deliberately when extending modules with functionality "A005", # Not available in all supported Python versions "B905", # Black will handle this "E501", # Do not require docstrings everywhere "D1", # No need to add type annotation to self and cls "ANN10", # Allow Any "ANN401", # We use assert only for documentation purposes and debugging. "S101", # We want this as a feature of the configuration. The user is warned. "S602", # This one is hard to get around here "S603", # Required to be location-independent "S607", # I don't like this style "TRY300", # Gives some readability sometimes, No need to prevent this style "RET505", # This is the style used in this project. "TID252", # Will be fixed lated. "TRY003", "TRY301", ] [tool.ruff.lint.per-file-ignores] "tests/**" = [ # Allow hard-coded passwords in tests "S105", "S106", # Allow potentially insecure temp directory access "S108", # shell access in tests it ok "S604", # Sometimes needed for the current tests "SLF", ] "src/autosuspend/checks/ical.py" = [ # Terrible hack accessing internal members required to handle rrules correctly. "SLF001", ] [tool.ruff.lint.pydocstyle] convention = "google" autosuspend-7.2.0/renovate.json000066400000000000000000000011151475670032600166200ustar00rootroot00000000000000{ "extends": [ "config:recommended" ], "pip_requirements": { "fileMatch": [ "^requirements.*\\.txt$" ] }, "customManagers": [ { "customType": "regex", "fileMatch": [ "^\\.github/.*\\.ya?ml$" ], "matchStrings": [ "node-version: (?.*)" ], "depNameTemplate": "node-version", "datasourceTemplate": "node-version" } ], "packageRules": [ { "matchUpdateTypes": [ "minor", "patch", "pin", "digest" ], "automerge": true } ] } autosuspend-7.2.0/requirements-check.txt000066400000000000000000000002021475670032600204350ustar00rootroot00000000000000ruff==0.9.7 black==25.1.0 isort==6.0.0 mypy==1.15.0 types-tzlocal types-requests types-pytz types-freezegun types-python-dateutil autosuspend-7.2.0/requirements-doc.txt000066400000000000000000000002031475670032600201260ustar00rootroot00000000000000sphinx-issues==5.0.0 sphinx==8.2.1 furo==2024.8.6 sphinxcontrib-plantuml==0.30 sphinx-autodoc-typehints==3.1.0 recommonmark==0.7.1 autosuspend-7.2.0/setup.cfg000066400000000000000000000017321475670032600157300ustar00rootroot00000000000000[build_sphinx] source-dir = doc/source build-dir = doc/build [mypy] ignore_missing_imports = True disallow_untyped_defs = True check_untyped_defs = True no_implicit_optional = True warn_unused_configs = True warn_unused_ignores = True [tool:pytest] log_level = DEBUG markers = integration: longer-running integration tests filterwarnings = ignore::DeprecationWarning default::DeprecationWarning:autosuspend addopts = --cov-config=setup.cfg [coverage:run] branch = True source = autosuspend [coverage:paths] source = src/ */site-packages/ [coverage:report] exclude_lines = pragma: no cover def __repr__ if __name__ == "__main__": if TYPE_CHECKING: @abc.abstractmethod [isort] profile = google known_local_folder = tests case_sensitive = false combine_as_imports = true force_single_line = false multi_line_output = 3 include_trailing_comma = true lines_after_imports = 2 line_length = 88 force_grid_wrap = false reverse_relative = true autosuspend-7.2.0/setup.py000066400000000000000000000033771475670032600156300ustar00rootroot00000000000000from pathlib import Path from setuptools import find_packages, setup name = "autosuspend" version_file = Path(__file__).absolute().parent / "VERSION" lines = version_file.read_text().splitlines() release = lines[1].strip() extras_require = { "Mpd": ["python-mpd2"], "Kodi": ["requests"], "XPath": ["lxml", "requests"], "JSONPath": ["jsonpath-ng", "requests"], "Logind": ["dbus-python"], "ical": ["requests", "icalendar", "python-dateutil", "tzlocal"], "localfiles": ["requests-file"], "logactivity": ["python-dateutil", "pytz"], "test": [ "pytest", "pytest-cov", "pytest-mock", "freezegun", "python-dbusmock", "PyGObject", "pytest-datadir", "pytest-httpserver", ], } extras_require["test"].extend( {dep for k, v in extras_require.items() if k != "test" for dep in v}, ) extras_require["all"] = list( {dep for k, v in extras_require.items() if k != "test" for dep in v}, ) setup( name=name, version=release, description="A daemon to suspend your server in case of inactivity", author="Johannes Wienke", author_email="languitar@semipol.de", license="GPL2", zip_safe=False, python_requires=">=3.7", install_requires=[ "psutil>=5.0", "portalocker", ], extras_require=extras_require, package_dir={ "": "src", }, packages=find_packages("src"), entry_points={ "console_scripts": [ "autosuspend = autosuspend:main", ], }, data_files=[ ("etc", ["data/autosuspend.conf", "data/autosuspend-logging.conf"]), ( "lib/systemd/system", ["data/autosuspend.service", "data/autosuspend-detect-suspend.service"], ), ], ) autosuspend-7.2.0/src/000077500000000000000000000000001475670032600146735ustar00rootroot00000000000000autosuspend-7.2.0/src/autosuspend/000077500000000000000000000000001475670032600172455ustar00rootroot00000000000000autosuspend-7.2.0/src/autosuspend/__init__.py000077500000000000000000000624401475670032600213670ustar00rootroot00000000000000#!/usr/bin/env python3 """A daemon to suspend a system on inactivity.""" import argparse from collections.abc import Callable, Iterable, Sequence import configparser from contextlib import suppress from datetime import datetime, timedelta, timezone import functools from importlib.metadata import version import logging import logging.config from pathlib import Path import subprocess import time from typing import IO import portalocker from .checks import Activity, CheckType, ConfigurationError, TemporaryCheckError, Wakeup from .util import logger_by_class_instance # pylint: disable=invalid-name _logger = logging.getLogger("autosuspend") # pylint: enable=invalid-name def execute_suspend( command: str | Sequence[str], wakeup_at: datetime | None, ) -> None: """Suspend the system by calling the specified command. Args: command: The command to execute, which will be executed using shell execution wakeup_at: potential next wakeup time. Only informative. """ _logger.info( "Suspending using command: %s with next wake up at %s", command, wakeup_at ) try: subprocess.check_call(command, shell=True) except subprocess.CalledProcessError: _logger.warning("Unable to execute suspend command: %s", command, exc_info=True) def notify_suspend( command_wakeup_template: str | None, command_no_wakeup: str | None, wakeup_at: datetime | None, ) -> None: """Call a command to notify on suspending. Args: command_wakeup_template: A template for the command to execute in case a wakeup is scheduled. It will be executed using shell execution. The template is processed with string formatting to include information on a potentially scheduled wakeup. Notifications can be disable by providing ``None`` here. command_no_wakeup: Command to execute for notification in case no wake up is scheduled. Will be executed using shell execution. wakeup_at: if not ``None``, this is the time the system will wake up again """ def safe_exec(command: str) -> None: _logger.info("Notifying using command: %s", command) try: subprocess.check_call(command, shell=True) except subprocess.CalledProcessError: _logger.warning( "Unable to execute notification command: %s", command, exc_info=True ) if wakeup_at and command_wakeup_template: command = command_wakeup_template.format( timestamp=wakeup_at.timestamp(), iso=wakeup_at.isoformat() ) safe_exec(command) elif not wakeup_at and command_no_wakeup: safe_exec(command_no_wakeup) else: _logger.info("No suitable notification command configured.") def notify_and_suspend( suspend_cmd: str | Sequence[str], notify_cmd_wakeup_template: str | None, notify_cmd_no_wakeup: str | None, wakeup_at: datetime | None, ) -> None: notify_suspend(notify_cmd_wakeup_template, notify_cmd_no_wakeup, wakeup_at) execute_suspend(suspend_cmd, wakeup_at) def schedule_wakeup(command_template: str, wakeup_at: datetime) -> None: command = command_template.format( timestamp=wakeup_at.timestamp(), iso=wakeup_at.isoformat() ) _logger.info("Scheduling wakeup using command: %s", command) try: subprocess.check_call(command, shell=True) except subprocess.CalledProcessError: _logger.warning( "Unable to execute wakeup scheduling command: %s", command, exc_info=True ) def _safe_execute_activity(check: Activity, logger: logging.Logger) -> str | None: try: return check.check() except TemporaryCheckError: logger.warning("Check %s failed. Ignoring...", check, exc_info=True) return f"Check {check.name} failed temporarily" def execute_checks( checks: Iterable[Activity], all_checks: bool, logger: logging.Logger ) -> bool: """Execute the provided checks sequentially. Args: checks: the checks to execute all_checks: if ``True``, execute all checks even if a previous one already matched. logger: the logger instance to use Return: ``True`` if a check matched """ matched = False for check in checks: logger.debug("Executing check %s", check.name) result = _safe_execute_activity(check, logger) if result is not None: logger.info("Check %s matched. Reason: %s", check.name, result) matched = True if not all_checks: logger.debug("Skipping further checks") break return matched def _safe_execute_wakeup( check: Wakeup, timestamp: datetime, logger: logging.Logger ) -> datetime | None: try: return check.check(timestamp) except TemporaryCheckError: logger.warning("Wakeup %s failed. Ignoring...", check, exc_info=True) return None def execute_wakeups( wakeups: Iterable[Wakeup], timestamp: datetime, logger: logging.Logger ) -> datetime | None: wakeup_at = None for wakeup in wakeups: this_at = _safe_execute_wakeup(wakeup, timestamp, logger) # sanity checks if this_at is None: continue if this_at <= timestamp: logger.warning( "Wakeup %s returned a scheduled wakeup at %s, " "which is earlier than the current time %s. " "Ignoring.", wakeup, this_at, timestamp, ) continue # determine the earliest wake up point in time wakeup_at = min(this_at, wakeup_at or this_at) return wakeup_at class Processor: """Implements the logic for triggering suspension. Args: activities: the activity checks to execute wakeups: the wakeup checks to execute idle_time: the required amount of time the system has to be idle before suspension is triggered in seconds min_sleep_time: the minimum time the system has to sleep before it is woken up again in seconds. wakeup_delta: wake up this amount of seconds before the scheduled wake up time. sleep_fn: a callable that triggers suspension wakeup_fn: a callable that schedules the wakeup at the specified time in UTC seconds notify_fn: a callable that is called before suspending. One argument gives the scheduled wakeup time or ``None``. all_activities: if ``True``, execute all activity checks even if a previous one already matched. """ def __init__( self, activities: Iterable[Activity], wakeups: Iterable[Wakeup], idle_time: float, min_sleep_time: float, wakeup_delta: float, sleep_fn: Callable, wakeup_fn: Callable[[datetime], None], all_activities: bool, ) -> None: self._logger = logger_by_class_instance(self) self._activities = activities self._wakeups = wakeups self._idle_time = idle_time self._min_sleep_time = min_sleep_time self._wakeup_delta = wakeup_delta self._sleep_fn = sleep_fn self._wakeup_fn = wakeup_fn self._all_activities = all_activities self._idle_since = None # type: datetime | None def _reset_state(self, reason: str) -> None: self._logger.info("%s. Resetting state", reason) self._idle_since = None def _set_idle(self, since: datetime) -> datetime: """Set the idle since marker to the given dt if not already set earlier.""" self._idle_since = min(since, self._idle_since or since) return self._idle_since def iteration(self, timestamp: datetime, just_woke_up: bool) -> None: self._logger.info("Starting new check iteration") # exit in case something prevents suspension if just_woke_up: self._reset_state("Just woke up from suspension.") return # determine system activity active = execute_checks(self._activities, self._all_activities, self._logger) self._logger.debug("All activity checks have been executed. Active: %s", active) if active: self._reset_state("System is active") return # set idle timestamp if required idle_since = self._set_idle(timestamp) self._logger.info("System is idle since %s", idle_since) # determine if systems is idle long enough idle_seconds = (timestamp - idle_since).total_seconds() self._logger.debug("Idle seconds: %s", idle_seconds) if idle_seconds <= self._idle_time: self._logger.info( "Desired idle time of %s s not reached yet. Currently idle since %s s", self._idle_time, idle_seconds, ) return self._logger.info("System is idle long enough.") # determine potential wake ups wakeup_at = execute_wakeups(self._wakeups, timestamp, self._logger) if wakeup_at is None: self._logger.debug("No automatic wakeup required") else: self._logger.debug("System wakeup required at %s", wakeup_at) # Apply configured wakeup delta wakeup_at -= timedelta(seconds=self._wakeup_delta) self._logger.debug( "With delta applied, system should wake up at %s", wakeup_at, ) wakeup_in = wakeup_at - timestamp if wakeup_in.total_seconds() < self._min_sleep_time: self._logger.info( "Would wake up in %s seconds, which is " "below the minimum amount of %s s. " "Not suspending.", wakeup_in.total_seconds(), self._min_sleep_time, ) return # schedule wakeup self._logger.info("Scheduling wakeup at %s", wakeup_at) self._wakeup_fn(wakeup_at) self._reset_state("Going to suspend") self._sleep_fn(wakeup_at) def _continue_looping(run_for: int | None, start_time: datetime) -> bool: return (run_for is None) or ( datetime.now(timezone.utc) < (start_time + timedelta(seconds=run_for)) ) def _do_loop_iteration( processor: Processor, woke_up_file: Path, lock_file: Path, lock_timeout: float, ) -> None: try: _logger.debug("New iteration, trying to acquire lock") with portalocker.Lock(lock_file, timeout=lock_timeout): _logger.debug("Acquired lock") just_woke_up = woke_up_file.is_file() if just_woke_up: _logger.debug("Removing woke up file at %s", woke_up_file) try: woke_up_file.unlink() except FileNotFoundError: _logger.warning("Just woke up file disappeared", exc_info=True) processor.iteration(datetime.now(timezone.utc), just_woke_up) except portalocker.LockException: _logger.warning("Failed to acquire lock, skipping iteration", exc_info=True) def loop( processor: Processor, interval: float, run_for: int | None, woke_up_file: Path, lock_file: Path, lock_timeout: float, ) -> None: """Run the main loop of the daemon. Args: processor: the processor to use for handling the suspension computations interval: the length of one iteration of the main loop in seconds run_for: if specified, run the main loop for the specified amount of seconds before terminating (approximately) woke_up_file: path of a file that marks that the system was sleeping since the last processing iterations lock_file: path of a file used for locking modifications to the `woke_up_file` to ensure consistency lock_timeout: time in seconds to wait for acquiring the lock file """ start_time = datetime.now(timezone.utc) while _continue_looping(run_for, start_time): _do_loop_iteration(processor, woke_up_file, lock_file, lock_timeout) time.sleep(interval) def config_section_string(section: configparser.SectionProxy) -> str: data = {k: v if k != "password" else "" for k, v in section.items()} return f"{data}" def _determine_check_class_and_module( class_name: str, internal_module: str ) -> tuple[str, str]: """Determine module and class of a check depending on whether it is internal.""" if "." in class_name: # dot in class name means external class import_module, import_class = class_name.rsplit(".", maxsplit=1) else: # no dot means internal class import_module = f"autosuspend.checks.{internal_module}" import_class = class_name return import_module, import_class def _determine_check_class_name(name: str, section: configparser.SectionProxy) -> str: # if there is an explicit class, use that one with higher priority # else, use the legacy method to determine the check name from the section header return section.get("class", name) def _set_up_single_check( section: configparser.SectionProxy, prefix: str, internal_module: str, target_class: type[CheckType], ) -> CheckType: name = section.name[len(f"{prefix}.") :] class_name = _determine_check_class_name(name, section) # try to find the required class import_module, import_class = _determine_check_class_and_module( class_name, internal_module ) _logger.info( "Configuring check %s with class %s from module %s " "using config parameters %s", name, import_class, import_module, config_section_string(section), ) try: klass = getattr( __import__(import_module, fromlist=[import_class]), import_class ) except AttributeError as error: raise ConfigurationError( f"Cannot create built-in check named {class_name}: Class does not exist" ) from error check = klass.create(name, section) if not isinstance(check, target_class): raise ConfigurationError( "Check %s is not a correct %s instance", check, target_class.__name__ ) _logger.debug("Created check instance %s with options %s", check, check.options()) return check def set_up_checks( config: configparser.ConfigParser, prefix: str, internal_module: str, target_class: type[CheckType], error_none: bool = False, ) -> list[CheckType]: """Set up :py.class:`Check` instances from a given configuration. Args: config: the configuration to use prefix: The prefix of sections in the configuration file to use for creating instances. internal_module: Name of the submodule of ``autosuspend.checks`` to use for discovering internal check classes. target_class: the base class to check new instance against error_none: Raise an error if nothing was configured? """ configured_checks = [] check_section = [s for s in config.sections() if s.startswith(f"{prefix}.")] for section_name in check_section: section = config[section_name] if not section.getboolean("enabled", fallback=False): _logger.debug("Skipping disabled check %s", section_name) continue configured_checks.append( _set_up_single_check(section, prefix, internal_module, target_class) ) if not configured_checks and error_none: raise ConfigurationError("No checks enabled") return configured_checks def parse_config(config_file: Iterable[str]) -> configparser.ConfigParser: """Parse the configuration file. Args: config_file: The file to parse """ _logger.debug("Reading config file %s", config_file) config = configparser.ConfigParser( interpolation=configparser.ExtendedInterpolation() ) config.read_file(config_file) _logger.debug("Parsed config file: %s", config) return config def parse_arguments(args: Sequence[str] | None) -> argparse.Namespace: """Parse command line arguments. Args: args: if specified, use the provided arguments instead of the default ones determined via the :module:`sys` module. """ parser = argparse.ArgumentParser( description="Automatically suspends a server based on several criteria", formatter_class=argparse.ArgumentDefaultsHelpFormatter, ) default_config: IO[str] | None = None with suppress(FileNotFoundError, IsADirectoryError, PermissionError): # The open file is required after this function finishes inside the argparse # result. Therefore, a context manager is not easily usable here. default_config = Path("/etc/autosuspend.conf").open("r") # noqa: SIM115 parser.add_argument( "-c", "--config", dest="config_file", type=argparse.FileType("r"), default=default_config, required=default_config is None, metavar="FILE", help="The config file to use", ) logging_group = parser.add_mutually_exclusive_group() logging_group.add_argument( "-l", "--logging", type=argparse.FileType("r"), default=None, metavar="FILE", help="Configures the python logging system from the specified " "configuration file.", ) logging_group.add_argument( "-d", "--debug", action="store_true", default=False, help="Configures the logging system to provide full debug output on stdout.", ) subparsers = parser.add_subparsers(title="subcommands", dest="subcommand") subparsers.required = True parser_version = subparsers.add_parser( "version", help="Outputs the program version" ) parser_version.set_defaults(func=main_version) parser_daemon = subparsers.add_parser( "daemon", help="Execute the continuously operating daemon" ) parser_daemon.set_defaults(func=main_daemon) parser_daemon.add_argument( "-a", "--allchecks", dest="all_checks", default=False, action="store_true", help="Execute all checks even if one has already prevented " "the system from going to sleep. Useful to debug individual " "checks.", ) parser_daemon.add_argument( "-r", "--runfor", dest="run_for", type=float, default=None, metavar="SEC", help="If set, run for the specified amount of seconds before exiting " "instead of endless execution.", ) parser_hook = subparsers.add_parser( "presuspend", help="Hook method to be called before suspending" ) parser_hook.set_defaults(func=main_hook) result = parser.parse_args(args) _logger.debug("Parsed command line arguments %s", result) return result def configure_logging(config_file: IO | None, debug: bool) -> None: """Configure the python :mod:`logging` system. Assumes that either a config file is provided, or debugging is enabled. Both together are not possible. Args: config_file: a configuration file pointed by a :ref:`file object ` debug: if ``True``, enable debug logging """ if config_file: try: logging.config.fileConfig(config_file) except Exception: # probably ok for main-like function # at least configure warnings logging.basicConfig(level=logging.WARNING) _logger.warning( "Unable to configure logging from file %s. " "Falling back to warning level.", config_file, exc_info=True, ) else: if debug: logging.basicConfig(level=logging.DEBUG) else: # at least configure warnings logging.basicConfig(level=logging.WARNING) def get_notify_and_suspend_func(config: configparser.ConfigParser) -> Callable: return functools.partial( notify_and_suspend, config.get("general", "suspend_cmd"), config.get( "general", "notify_cmd_wakeup", fallback=None, ), config.get( "general", "notify_cmd_no_wakeup", fallback=None, ), ) def get_schedule_wakeup_func( config: configparser.ConfigParser, ) -> Callable[[datetime], None]: return functools.partial(schedule_wakeup, config.get("general", "wakeup_cmd")) def get_woke_up_file(config: configparser.ConfigParser) -> Path: return Path( config.get( "general", "woke_up_file", fallback="/var/run/autosuspend-just-woke-up" ) ) def get_lock_file(config: configparser.ConfigParser) -> Path: return Path( config.get("general", "lock_file", fallback="/var/lock/autosuspend.lock") ) def get_lock_timeout(config: configparser.ConfigParser) -> float: return config.getfloat("general", "lock_timeout", fallback=30.0) def get_wakeup_delta(config: configparser.ConfigParser) -> float: return config.getfloat("general", "wakeup_delta", fallback=30) def configure_processor( args: argparse.Namespace, config: configparser.ConfigParser, checks: Iterable[Activity], wakeups: Iterable[Wakeup], ) -> Processor: return Processor( checks, wakeups, config.getfloat("general", "idle_time", fallback=300), config.getfloat("general", "min_sleep_time", fallback=1200), get_wakeup_delta(config), get_notify_and_suspend_func(config), get_schedule_wakeup_func(config), all_activities=args.all_checks, ) def hook( wakeups: list[Wakeup], wakeup_delta: float, wakeup_fn: Callable[[datetime], None], woke_up_file: Path, lock_file: Path, lock_timeout: float, ) -> None: """Installs wake ups and notifies the daemon before suspending. Args: wakeups: set of wakeup checks to use for determining the wake up time wakeup_delta: The amount of time in seconds to wake up before an event wakeup_fn: function to call with the next wake up time woke_up_file: location of the file that instructs the daemon that the system just woke up lock_file: path of a file used for locking modifications to the `woke_up_file` to ensure consistency lock_timeout: time in seconds to wait for acquiring the lock file """ _logger.info("Pre-suspend hook starting, trying to acquire lock") try: with portalocker.Lock(lock_file, timeout=lock_timeout): _logger.debug("Hook acquired lock") _logger.debug("Hook executing with configured wake ups: %s", wakeups) wakeup_at = execute_wakeups(wakeups, datetime.now(timezone.utc), _logger) _logger.debug("Hook next wake up at %s", wakeup_at) if wakeup_at: wakeup_at -= timedelta(seconds=wakeup_delta) _logger.info("Scheduling next wake up at %s", wakeup_at) wakeup_fn(wakeup_at) else: _logger.info("No wake up required. Terminating") # create the just woke up file woke_up_file.touch() except portalocker.LockException: _logger.warning( "Hook unable to acquire lock. Not informing daemon.", exc_info=True ) def main_version( args: argparse.Namespace, config: configparser.ConfigParser # noqa: ARG001 ) -> None: print(version("autosuspend")) # noqa: T201 def main_hook( args: argparse.Namespace, config: configparser.ConfigParser # noqa: ARG001 ) -> None: wakeups = set_up_checks( config, "wakeup", "wakeup", Wakeup, # type: ignore # python/mypy#5374 ) hook( wakeups, get_wakeup_delta(config), get_schedule_wakeup_func(config), get_woke_up_file(config), get_lock_file(config), get_lock_timeout(config), ) def main_daemon(args: argparse.Namespace, config: configparser.ConfigParser) -> None: """Run the daemon.""" checks = set_up_checks( config, "check", "activity", Activity, # type: ignore error_none=True, ) wakeups = set_up_checks( config, "wakeup", "wakeup", Wakeup, # type: ignore ) processor = configure_processor(args, config, checks, wakeups) loop( processor, config.getfloat("general", "interval", fallback=60), run_for=args.run_for, woke_up_file=get_woke_up_file(config), lock_file=get_lock_file(config), lock_timeout=get_lock_timeout(config), ) def main(argv: Sequence[str] | None = None) -> None: """Run the daemon.""" args = parse_arguments(argv) configure_logging(args.logging, args.debug) config = parse_config(args.config_file) args.func(args, config) if __name__ == "__main__": main() autosuspend-7.2.0/src/autosuspend/checks/000077500000000000000000000000001475670032600205055ustar00rootroot00000000000000autosuspend-7.2.0/src/autosuspend/checks/__init__.py000066400000000000000000000066471475670032600226330ustar00rootroot00000000000000"""Provides the basic types used for checks.""" import abc from collections.abc import Mapping import configparser from datetime import datetime from typing import Any, TypeVar from autosuspend.util import logger_by_class_instance class ConfigurationError(RuntimeError): """Indicates an error in the configuration of a :class:`Check`.""" class TemporaryCheckError(RuntimeError): """Indicates a temporary error while performing a check. Such an error can be ignored for some time since it might recover automatically. """ class SevereCheckError(RuntimeError): """Indicates a sever check error that will probably not recover. There is no hope this situation recovers. """ CheckType = TypeVar("CheckType", bound="Check") class Check(abc.ABC): """Base class for all kinds of checks. Subclasses must call this class' ``__init__`` method. Args: name (str): Configured name of the check """ @classmethod @abc.abstractmethod def create( cls: type[CheckType], name: str, config: configparser.SectionProxy ) -> CheckType: """Create a new check instance from the provided configuration. Args: name: user-defined name for the check config: config parser section with the configuration for this check Raises: ConfigurationError: Configuration for this check is inappropriate """ def __init__(self, name: str | None = None) -> None: if name: self.name = name else: self.name = self.__class__.__name__ self.logger = logger_by_class_instance(self, name) def options(self) -> Mapping[str, Any]: """Return the configured options as a mapping. This is used for debugging purposes only. """ return { k: v for k, v in self.__dict__.items() if not callable(v) and k != "logger" } def __str__(self) -> str: return f"{self.name}[class={self.__class__.__name__}]" class Activity(Check): """Base class for activity checks. Subclasses must call this class' __init__ method. """ @abc.abstractmethod def check(self) -> str | None: """Determine if system activity exists that prevents suspending. Returns: A string describing which condition currently prevents sleep, else ``None``. Raises: TemporaryCheckError: Check execution currently fails but might recover later SevereCheckError: Check executions fails severely """ def __str__(self) -> str: return f"{self.name}[class={self.__class__.__name__}]" class Wakeup(Check): """Represents a check for potential wake up points.""" @abc.abstractmethod def check(self, timestamp: datetime) -> datetime | None: """Indicate if a wakeup has to be scheduled for this check. Args: timestamp: the time at which the call to the wakeup check is made Returns: a datetime describing when the system needs to be running again or ``None`` if no wakeup is required. Use timezone aware datetimes. Raises: TemporaryCheckError: Check execution currently fails but might recover later SevereCheckError: Check executions fails severely """ autosuspend-7.2.0/src/autosuspend/checks/activity.py000066400000000000000000000015201475670032600227110ustar00rootroot00000000000000from contextlib import suppress # isort: off from .command import CommandActivity as ExternalCommand # noqa from .linux import ( # noqa ActiveConnection, Load, NetworkBandwidth, Ping, Processes, Users, ) from .smb import Smb # noqa from .xorg import XIdleTime # noqa with suppress(ModuleNotFoundError): from .ical import ActiveCalendarEvent # noqa with suppress(ModuleNotFoundError): from .json import JsonPath # noqa with suppress(ModuleNotFoundError): from .logs import LastLogActivity # noqa with suppress(ModuleNotFoundError): from .xpath import XPathActivity as XPath # noqa with suppress(ModuleNotFoundError): from .systemd import LogindSessionsIdle # noqa with suppress(ModuleNotFoundError): from .mpd import Mpd # noqa from .kodi import Kodi, KodiIdleTime # noqa # isort: on autosuspend-7.2.0/src/autosuspend/checks/command.py000066400000000000000000000052511475670032600225000ustar00rootroot00000000000000import configparser from datetime import datetime, timezone import subprocess from . import ( Activity, Check, CheckType, ConfigurationError, SevereCheckError, TemporaryCheckError, Wakeup, ) def raise_severe_if_command_not_found(error: subprocess.CalledProcessError) -> None: if error.returncode == 127: # see http://tldp.org/LDP/abs/html/exitcodes.html raise SevereCheckError(f"Command '{' '.join(error.cmd)}' does not exist") class CommandMixin(Check): """Mixin for configuring checks based on external commands.""" @classmethod def create( cls: type[CheckType], name: str, config: configparser.SectionProxy ) -> CheckType: try: return cls(name, config["command"].strip()) # type: ignore except KeyError as error: raise ConfigurationError("Missing command specification") from error def __init__(self, command: str) -> None: self._command = command class CommandActivity(CommandMixin, Activity): def __init__(self, name: str, command: str) -> None: CommandMixin.__init__(self, command) Activity.__init__(self, name) def check(self) -> str | None: try: subprocess.check_call(self._command, shell=True) return f"Command {self._command} succeeded" except subprocess.CalledProcessError as error: raise_severe_if_command_not_found(error) return None class CommandWakeup(CommandMixin, Wakeup): """Determine wake up times based on an external command. The called command must return a timestamp in UTC or nothing in case no wake up is planned. """ def __init__(self, name: str, command: str) -> None: CommandMixin.__init__(self, command) Wakeup.__init__(self, name) def check(self, timestamp: datetime) -> datetime | None: # noqa: ARG002 try: output = subprocess.check_output( self._command, shell=True, ).splitlines()[0] self.logger.debug( "Command %s succeeded with output %s", self._command, output ) if output.strip(): return datetime.fromtimestamp(float(output.strip()), timezone.utc) else: return None except subprocess.CalledProcessError as error: raise_severe_if_command_not_found(error) raise TemporaryCheckError( "Unable to call the configured command" ) from error except ValueError as error: raise TemporaryCheckError( "Return value cannot be interpreted as a timestamp" ) from error autosuspend-7.2.0/src/autosuspend/checks/ical.py000066400000000000000000000247371475670032600220040ustar00rootroot00000000000000from collections.abc import Iterable, Sequence from contextlib import suppress from dataclasses import dataclass from datetime import date, datetime, timedelta, timezone, tzinfo from io import BytesIO from typing import Any, cast, IO, TypeVar from dateutil.rrule import rrule, rruleset, rrulestr import icalendar import icalendar.cal import pytz import tzlocal from . import Activity, Wakeup from .util import NetworkMixin from ..util.datetime import is_aware, to_tz_unaware # Make v6 behave as if it were an older version to support a wider range. with suppress(AttributeError): icalendar.use_pytz() @dataclass class CalendarEvent: summary: str start: datetime | date end: datetime | date def __str__(self) -> str: return ( f"CalendarEvent[summary={self.summary}, start={self.start}, end={self.end}]" ) def _expand_rrule_all_day( rrule: str, start: date, exclusions: Iterable, start_at: datetime, end_at: datetime ) -> Iterable[date]: """Expand an rrule for all-day events. To my mind, these events cannot have changes, just exclusions, because changes only affect the time, which doesn't exist for all-day events. """ rules = cast(rruleset, rrulestr(rrule, dtstart=start, ignoretz=True, forceset=True)) # add exclusions if exclusions: for xdate in exclusions: rules.exdate(datetime.combine(xdate.dts[0].dt, datetime.min.time())) dates = [] # reduce start and end to datetimes without timezone that just represent a # date at midnight. for candidate in rules.between( datetime.combine(start_at.date(), datetime.min.time()), datetime.combine(end_at.date(), datetime.min.time()), inc=True, ): dates.append(candidate.date()) return dates def _prepare_rruleset_for_expanding( rule: str, start: datetime, exclusions: Iterable, changes: Iterable[icalendar.cal.Event], tz: tzinfo | None, ) -> rruleset: """Prepare an rruleset for expanding. Every timestamp is converted to a single timezone and then made unaware to avoid DST issues. """ start = to_tz_unaware(start, tz) rules = rruleset() first_rule = cast( rrule, rrulestr(rule, dtstart=start, ignoretz=True, forceset=False) ) # apply the same timezone logic for the until part of the rule after # parsing it. if first_rule._until: # type: ignore first_rule._until = to_tz_unaware( # type: ignore pytz.utc.localize(first_rule._until), # type: ignore tz, ) rules.rrule(first_rule) # add exclusions if exclusions: for xdate in exclusions: with suppress(AttributeError): # also in this case, unify and strip the timezone rules.exdate(xdate.dts[0].dt.astimezone(tz).replace(tzinfo=None)) # add events that were changed for change in changes: # same timezone mangling applies here rules.exdate(to_tz_unaware(change.get("recurrence-id").dt, tz)) return rules def _expand_rrule( rrule: str, start: datetime, instance_duration: timedelta, exclusions: Iterable, changes: Iterable[icalendar.cal.Event], start_at: datetime, end_at: datetime, ) -> Sequence[datetime]: # unify everything to a single timezone and then strip it to handle DST # changes correctly orig_tz = start.tzinfo start_at = to_tz_unaware(start_at, orig_tz) end_at = to_tz_unaware(end_at, orig_tz) rules = _prepare_rruleset_for_expanding(rrule, start, exclusions, changes, orig_tz) # expand the rrule dates = [] for candidate in rules.between(start_at - instance_duration, end_at, inc=True): localized = _localize(candidate, orig_tz) dates.append(localized) return dates ChangeMapping = dict[str, list[icalendar.cal.Event]] def _collect_recurrence_changes(calendar: icalendar.Calendar) -> ChangeMapping: recurring_changes: ChangeMapping = {} for component in calendar.walk("VEVENT"): if component.get("recurrence-id"): if component.get("uid") not in recurring_changes: recurring_changes[component.get("uid")] = [] recurring_changes[component.get("uid")].append(component) return recurring_changes def _get_recurrence_exclusions_as_list(component: dict) -> list: exclusions = component.get("exdate") if exclusions and not isinstance(exclusions, list): exclusions = [exclusions] return exclusions DateType = TypeVar("DateType", date, datetime) def _extract_events_from_recurring_component( component: icalendar.Event, component_start: DateType, component_end: DateType, start_at: datetime, end_at: datetime, recurring_changes: ChangeMapping, ) -> list[CalendarEvent]: summary = component.get("summary") rrule = component.get("rrule").to_ical().decode("utf-8") exclusions = _get_recurrence_exclusions_as_list(component) length = component_end - component_start changes = recurring_changes.get(component.get("uid"), []) events = [] if isinstance(component_start, datetime): # complex processing in case of normal events for local_start in _expand_rrule( rrule, component_start, length, exclusions, changes, start_at, end_at ): events.append( CalendarEvent(str(summary), local_start, local_start + length) ) else: # simplified processing for all-day events for local_start_date in _expand_rrule_all_day( rrule, component_start, exclusions, start_at, end_at ): events.append( CalendarEvent( str(summary), local_start_date, local_start_date + timedelta(days=1) ) ) return events def _extract_events_from_single_component( component: icalendar.Event, component_start: DateType, component_end: DateType, start_at: datetime, end_at: datetime, ) -> list[CalendarEvent]: summary = component.get("summary") events = [] # distinction between usual events and all-day events if isinstance(component_start, datetime): # single events if component_end > start_at and component_start < end_at: events.append(CalendarEvent(str(summary), component_start, component_end)) else: # all-day events if component_end > start_at.date() and component_start <= end_at.date(): events.append(CalendarEvent(str(summary), component_start, component_end)) return events def _localize(dt: datetime, tz: Any) -> datetime: """Localizes a datetime with the provided timezone. This method handles the different return types of tzlocal in different versions. """ try: return tz.localize(dt) except AttributeError: return dt.astimezone(tz) def _extract_events_from_component( component: icalendar.Event, recurring_changes: ChangeMapping, start_at: datetime, end_at: datetime, ) -> list[CalendarEvent]: start = component.get("dtstart").dt end = component.get("dtend").dt # Check whether dates are floating and localize with local time if so. # Only works in case of non-all-day events, which are dates, not # datetimes. if isinstance(start, datetime) and not is_aware(start): assert not is_aware(end) local_time = tzlocal.get_localzone() start = _localize(start, local_time) end = _localize(end, local_time) if component.get("rrule"): return _extract_events_from_recurring_component( component, start, end, start_at, end_at, recurring_changes ) else: return _extract_events_from_single_component( component, start, end, start_at, end_at ) def list_calendar_events( data: IO[bytes], start_at: datetime, end_at: datetime ) -> Sequence[CalendarEvent]: """List all relevant calendar events in the provided interval. Args: data: A stream with icalendar data start_at: include events overlapping with this time (inclusive) end_at: do not include events that start after or exactly at this time """ # some useful notes: # * end times and dates are non-inclusive for ical events # * start and end are dates for all-day events calendar: icalendar.Calendar = icalendar.Calendar.from_ical(data.read()) # Do a first pass through the calendar to collect all exclusions to # recurring events so that they can be handled when expanding recurrences. recurring_changes = _collect_recurrence_changes(calendar) events = [] for component in calendar.walk("VEVENT"): events.extend( _extract_events_from_component( component, recurring_changes, start_at, end_at ) ) return sorted(events, key=lambda e: e.start) class ActiveCalendarEvent(NetworkMixin, Activity): """Determines activity by checking against events in an icalendar file.""" def __init__(self, name: str, **kwargs: Any) -> None: NetworkMixin.__init__(self, **kwargs) Activity.__init__(self, name) def check(self) -> str | None: response = self.request() start = datetime.now(timezone.utc) end = start + timedelta(minutes=1) events = list_calendar_events(BytesIO(response.content), start, end) self.logger.debug( "Listing active events between %s and %s returned %s events", start, end, len(events), ) if events: return f"Calendar event {events[0]} is active" else: return None class Calendar(NetworkMixin, Wakeup): """Uses an ical calendar to wake up on the next scheduled event.""" def __init__(self, name: str, **kwargs: Any) -> None: NetworkMixin.__init__(self, **kwargs) Wakeup.__init__(self, name) def check(self, timestamp: datetime) -> datetime | None: response = self.request() end = timestamp + timedelta(weeks=6 * 4) events = list_calendar_events(BytesIO(response.content), timestamp, end) # Filter out currently active events. They are not our business. events = [e for e in events if e.start >= timestamp] if events: candidate = events[0] if isinstance(candidate.start, datetime): return candidate.start else: return datetime.combine(candidate.start, datetime.min.time()) else: return None autosuspend-7.2.0/src/autosuspend/checks/json.py000066400000000000000000000031571475670032600220360ustar00rootroot00000000000000import configparser import json from textwrap import shorten from typing import Any from jsonpath_ng import JSONPath import requests import requests.exceptions from . import Activity, ConfigurationError, TemporaryCheckError from .util import NetworkMixin class JsonPath(NetworkMixin, Activity): """Requests a URL and evaluates whether a JSONPath expression matches.""" @classmethod def collect_init_args(cls, config: configparser.SectionProxy) -> dict[str, Any]: from jsonpath_ng.ext import parse try: args = NetworkMixin.collect_init_args(config) args["jsonpath"] = parse(config["jsonpath"]) return args except KeyError as error: raise ConfigurationError("Property jsonpath is missing") from error except Exception as error: raise ConfigurationError(f"JSONPath error {error}") from error def __init__(self, name: str, jsonpath: JSONPath, **kwargs: Any) -> None: Activity.__init__(self, name) NetworkMixin.__init__(self, accept="application/json", **kwargs) self._jsonpath = jsonpath def check(self) -> str | None: try: reply = self.request().json() matched = self._jsonpath.find(reply) if matched: # shorten to avoid excessive logging output return f"JSONPath {self._jsonpath} found elements " + shorten( str(matched), 24 ) return None except (json.JSONDecodeError, requests.exceptions.RequestException) as error: raise TemporaryCheckError(error) from error autosuspend-7.2.0/src/autosuspend/checks/kodi.py000066400000000000000000000072511475670032600220120ustar00rootroot00000000000000import configparser import json from typing import Any from . import Activity, ConfigurationError, TemporaryCheckError from .util import NetworkMixin def _add_default_kodi_url(config: configparser.SectionProxy) -> None: if "url" not in config: config["url"] = "http://localhost:8080/jsonrpc" class Kodi(NetworkMixin, Activity): @classmethod def collect_init_args(cls, config: configparser.SectionProxy) -> dict[str, Any]: try: _add_default_kodi_url(config) args = NetworkMixin.collect_init_args(config) args["suspend_while_paused"] = config.getboolean( "suspend_while_paused", fallback=False ) return args except ValueError as error: raise ConfigurationError(f"Configuration error {error}") from error @classmethod def create(cls, name: str, config: configparser.SectionProxy) -> "Kodi": return cls(name, **cls.collect_init_args(config)) def __init__( self, name: str, url: str, suspend_while_paused: bool = False, **kwargs: Any ) -> None: self._suspend_while_paused = suspend_while_paused if self._suspend_while_paused: request = url + ( '?request={"jsonrpc": "2.0", "id": 1, ' '"method": "XBMC.GetInfoBooleans",' '"params": {"booleans": ["Player.Playing"]} }' ) else: request = url + ( '?request={"jsonrpc": "2.0", "id": 1, ' '"method": "Player.GetActivePlayers"}' ) NetworkMixin.__init__(self, url=request, **kwargs) Activity.__init__(self, name) def _safe_request_result(self) -> dict: try: return self.request().json()["result"] except (KeyError, TypeError, json.JSONDecodeError) as error: raise TemporaryCheckError("Unable to get or parse Kodi state") from error def check(self) -> str | None: reply = self._safe_request_result() if self._suspend_while_paused: return ( "Kodi actively playing media" if reply.get("Player.Playing") else None ) else: return "Kodi currently playing" if reply else None class KodiIdleTime(NetworkMixin, Activity): @classmethod def collect_init_args(cls, config: configparser.SectionProxy) -> dict[str, Any]: try: _add_default_kodi_url(config) args = NetworkMixin.collect_init_args(config) args["idle_time"] = config.getint("idle_time", fallback=120) return args except ValueError as error: raise ConfigurationError("Configuration error " + str(error)) from error @classmethod def create(cls, name: str, config: configparser.SectionProxy) -> "KodiIdleTime": return cls(name, **cls.collect_init_args(config)) def __init__(self, name: str, url: str, idle_time: int, **kwargs: Any) -> None: request = url + ( '?request={"jsonrpc": "2.0", "id": 1, ' '"method": "XBMC.GetInfoBooleans",' f'"params": {{"booleans": ["System.IdleTime({idle_time})"]}}}}' ) NetworkMixin.__init__(self, url=request, **kwargs) Activity.__init__(self, name) self._idle_time = idle_time def check(self) -> str | None: try: reply = self.request().json() if not reply["result"][f"System.IdleTime({self._idle_time})"]: return "Someone interacts with Kodi" else: return None except (KeyError, TypeError, json.JSONDecodeError) as error: raise TemporaryCheckError("Unable to get or parse Kodi state") from error autosuspend-7.2.0/src/autosuspend/checks/linux.py000066400000000000000000000304041475670032600222170ustar00rootroot00000000000000"""Contains checks directly using the Linux operating system concepts.""" from collections.abc import Iterable import configparser from contextlib import suppress from datetime import datetime, timezone import os from pathlib import Path import re from re import Pattern import socket import subprocess import time import warnings import psutil from . import ( Activity, ConfigurationError, SevereCheckError, TemporaryCheckError, Wakeup, ) class ActiveConnection(Activity): """Checks if a client connection exists on specified ports.""" @classmethod def create( cls, name: str, config: configparser.SectionProxy, ) -> "ActiveConnection": try: split_ports = config["ports"].split(",") ports = {int(p.strip()) for p in split_ports} return cls(name, ports) except KeyError as error: raise ConfigurationError("Missing option ports") from error except ValueError as error: raise ConfigurationError("Ports must be integers") from error def __init__(self, name: str, ports: Iterable[int]) -> None: Activity.__init__(self, name) self._ports = ports def normalize_address( self, family: socket.AddressFamily, address: str ) -> tuple[socket.AddressFamily, str]: if family == socket.AF_INET6: # strip scope return family, address.split("%")[0] elif family == socket.AF_INET: # convert to IPv6 to handle cases where an IPv4 address is targeted via IPv6 # to IPv4 mapping return socket.AF_INET6, f"::ffff:{address}" else: return family, address def check(self) -> str | None: # Find the addresses of the system own_addresses = [ self.normalize_address(item.family, item.address) for sublist in psutil.net_if_addrs().values() for item in sublist ] # Find established connections to target ports connected = [ connection.laddr[1] for connection in psutil.net_connections() if ( self.normalize_address(connection.family, connection.laddr[0]) in own_addresses and connection.status == "ESTABLISHED" and connection.laddr[1] in self._ports ) ] if connected: return f"Ports {connected} are connected" else: return None class Load(Activity): @classmethod def create(cls, name: str, config: configparser.SectionProxy) -> "Load": try: return cls(name, config.getfloat("threshold", fallback=2.5)) except ValueError as error: raise ConfigurationError( f"Unable to parse threshold as float: {error}" ) from error def __init__(self, name: str, threshold: float) -> None: Activity.__init__(self, name) self._threshold = threshold def check(self) -> str | None: loadcurrent = os.getloadavg()[1] self.logger.debug("Load: %s", loadcurrent) if loadcurrent > self._threshold: return f"Load {loadcurrent} > threshold {self._threshold}" else: return None class NetworkBandwidth(Activity): @classmethod def _ensure_interfaces_exist(cls, interfaces: Iterable[str]) -> None: host_interfaces = psutil.net_if_addrs().keys() for interface in interfaces: if interface not in host_interfaces: raise ConfigurationError( f"Network interface {interface} does not exist" ) @classmethod def _extract_interfaces(cls, config: configparser.SectionProxy) -> list[str]: interfaces = config["interfaces"].split(",") interfaces = [i.strip() for i in interfaces if i.strip()] if not interfaces: raise ConfigurationError("No interfaces configured") cls._ensure_interfaces_exist(interfaces) return interfaces @classmethod def create( cls, name: str, config: configparser.SectionProxy, ) -> "NetworkBandwidth": try: interfaces = cls._extract_interfaces(config) threshold_send = config.getfloat("threshold_send", fallback=100) threshold_receive = config.getfloat("threshold_receive", fallback=100) return cls(name, interfaces, threshold_send, threshold_receive) except KeyError as error: raise ConfigurationError(f"Missing configuration key: {error}") from error except ValueError as error: raise ConfigurationError(f"Threshold in wrong format: {error}") from error def __init__( self, name: str, interfaces: Iterable[str], threshold_send: float, threshold_receive: float, ) -> None: Activity.__init__(self, name) self._interfaces = interfaces self._threshold_send = threshold_send self._threshold_receive = threshold_receive self._previous_values = psutil.net_io_counters(pernic=True) self._previous_time = time.time() @classmethod def _rate(cls, new: float, old: float, new_time: float, old_time: float) -> float: delta = new - old return delta / (new_time - old_time) class _InterfaceActive(RuntimeError): pass def _check_interface( self, interface: str, new: psutil._common.snetio, old: psutil._common.snetio, new_time: float, old_time: float, ) -> None: # send direction rate_send = self._rate(new.bytes_sent, old.bytes_sent, new_time, old_time) if rate_send > self._threshold_send: raise self._InterfaceActive( f"Interface {interface} sending rate {rate_send} byte/s " f"higher than threshold {self._threshold_send}" ) # receive direction rate_receive = self._rate(new.bytes_recv, old.bytes_recv, new_time, old_time) if rate_receive > self._threshold_receive: raise self._InterfaceActive( f"Interface {interface} receive rate {rate_receive} byte/s " f"higher than threshold {self._threshold_receive}" ) def check(self) -> str | None: # acquire the previous state and preserve it old_values = self._previous_values old_time = self._previous_time # read new values and store them for the next iteration new_values = psutil.net_io_counters(pernic=True) self._previous_values = new_values new_time = time.time() if new_time <= self._previous_time: raise TemporaryCheckError("Called too fast, no time between calls") self._previous_time = new_time for interface in self._interfaces: if interface not in new_values or interface not in self._previous_values: raise TemporaryCheckError(f"Interface {interface} is missing") try: self._check_interface( interface, new_values[interface], old_values[interface], new_time, old_time, ) except self._InterfaceActive as e: return str(e) return None class Ping(Activity): """Check if one or several hosts are reachable via ping.""" @classmethod def create(cls, name: str, config: configparser.SectionProxy) -> "Ping": try: hosts = config["hosts"].split(",") hosts = [h.strip() for h in hosts] return cls(name, hosts) except KeyError as error: raise ConfigurationError( f"Unable to determine hosts to ping: {error}" ) from error def __init__(self, name: str, hosts: Iterable[str]) -> None: Activity.__init__(self, name) self._hosts = hosts def check(self) -> str | None: try: for host in self._hosts: cmd = ["ping", "-q", "-c", "1", host] if ( subprocess.call( # we know the input from the config cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) == 0 ): self.logger.debug("host %s appears to be up", host) return f"Host {host} is up" return None except FileNotFoundError as error: raise SevereCheckError("Binary ping cannot be found") from error class Processes(Activity): @classmethod def create(cls, name: str, config: configparser.SectionProxy) -> "Processes": try: processes = config["processes"].split(",") processes = [p.strip() for p in processes] return cls(name, processes) except KeyError as error: raise ConfigurationError("No processes to check specified") from error def __init__(self, name: str, processes: Iterable[str]) -> None: Activity.__init__(self, name) self._processes = processes def check(self) -> str | None: for proc in psutil.process_iter(): with suppress(psutil.NoSuchProcess): pinfo = proc.name() if pinfo in self._processes: return f"Process {pinfo} is running" return None class Users(Activity): @classmethod def create(cls, name: str, config: configparser.SectionProxy) -> "Users": with warnings.catch_warnings(): warnings.simplefilter("ignore", FutureWarning) try: user_regex = re.compile(config.get("name", fallback=r".*")) # type: ignore terminal_regex = re.compile(config.get("terminal", fallback=r".*")) # type: ignore host_regex = re.compile(config.get("host", fallback=r".*")) # type: ignore return cls(name, user_regex, terminal_regex, host_regex) except re.error as error: raise ConfigurationError( f"Regular expression is invalid: {error}", ) from error def __init__( self, name: str, user_regex: Pattern, terminal_regex: Pattern, host_regex: Pattern, ) -> None: Activity.__init__(self, name) self._user_regex = user_regex self._terminal_regex = terminal_regex self._host_regex = host_regex def check(self) -> str | None: for entry in psutil.users(): if ( self._user_regex.fullmatch(entry.name) is not None and self._terminal_regex.fullmatch(entry.terminal) is not None and self._host_regex.fullmatch(entry.host) is not None ): self.logger.debug( "User %s on terminal %s from host %s matches criteria.", entry.name, entry.terminal, entry.host, ) return ( f"User {entry.name} is logged in on terminal {entry.terminal} " f"from {entry.host} since {entry.started}" ) return None class File(Wakeup): """Determines scheduled wake ups from the contents of a file on disk. File contents are interpreted as a Unix timestamp in seconds UTC. """ @classmethod def create(cls, name: str, config: configparser.SectionProxy) -> "File": try: path = Path(config["path"]) return cls(name, path) except KeyError as error: raise ConfigurationError("Missing option path") from error def __init__(self, name: str, path: Path) -> None: Wakeup.__init__(self, name) self._path = path def check(self, timestamp: datetime) -> datetime | None: # noqa: ARG002 try: first_line = self._path.read_text().splitlines()[0] return datetime.fromtimestamp(float(first_line.strip()), timezone.utc) except FileNotFoundError: # this is ok return None except (OSError, ValueError) as error: raise TemporaryCheckError( "Next wakeup time cannot be read despite a file being present" ) from error autosuspend-7.2.0/src/autosuspend/checks/logs.py000066400000000000000000000071451475670032600220320ustar00rootroot00000000000000from collections.abc import Iterable import configparser from datetime import datetime, timedelta, timezone from pathlib import Path import re from re import Pattern from dateutil.parser import parse from dateutil.utils import default_tzinfo import pytz from . import Activity, ConfigurationError, TemporaryCheckError class LastLogActivity(Activity): @classmethod def create(cls, name: str, config: configparser.SectionProxy) -> "LastLogActivity": try: return cls( name, Path(config["log_file"]), re.compile(config["pattern"]), timedelta(minutes=config.getint("minutes", fallback=10)), config.get("encoding", "ascii"), pytz.timezone(config.get("timezone", "UTC")), # type: ignore ) except KeyError as error: raise ConfigurationError( f"Missing config key {error}", ) from error except re.error as error: raise ConfigurationError( f"Regular expression is invalid: {error}", ) from error except ValueError as error: raise ConfigurationError( f"Unable to parse configuration: {error}", ) from error def __init__( self, name: str, log_file: Path, pattern: Pattern, delta: timedelta, encoding: str, default_timezone: timezone, ) -> None: if delta.total_seconds() < 0: raise ValueError("Given delta must be positive") if pattern.groups != 1: raise ValueError("Given pattern must have exactly one capture group") super().__init__(name=name) self.log_file = log_file self.pattern = pattern self.delta = delta self.encoding = encoding self.default_timezone = default_timezone def _safe_parse_date(self, match: str, now: datetime) -> datetime: try: match_date = default_tzinfo(parse(match), self.default_timezone) if match_date > now: raise TemporaryCheckError( f"Detected date {match_date} is in the future" ) return match_date except ValueError as error: raise TemporaryCheckError( f"Detected date {match} cannot be parsed as a date" ) from error except OverflowError as error: raise TemporaryCheckError( f"Detected date {match} is out of the valid range" ) from error def _file_lines_reversed(self) -> Iterable[str]: try: # Probably not the most effective solution for large log files. Might need # optimizations later on. return reversed( self.log_file.read_text(encoding=self.encoding).splitlines() ) except OSError as error: raise TemporaryCheckError( f"Cannot access log file {self.log_file}" ) from error def check(self) -> str | None: lines = self._file_lines_reversed() now = datetime.now(tz=timezone.utc) for line in lines: match = self.pattern.match(line) if not match: continue match_date = self._safe_parse_date(match.group(1), now) # Only check the first line (reverse order) that has a match, not all if (now - match_date) < self.delta: return f"Log activity in {self.log_file} at {match_date}" else: return None # No line matched at all return None autosuspend-7.2.0/src/autosuspend/checks/mpd.py000066400000000000000000000027711475670032600216460ustar00rootroot00000000000000import configparser import socket from mpd import MPDClient, MPDError from . import Activity, Check, ConfigurationError, TemporaryCheckError class Mpd(Activity): @classmethod def create(cls, name: str, config: configparser.SectionProxy) -> "Mpd": try: host = config.get("host", fallback="localhost") port = config.getint("port", fallback=6600) timeout = config.getint("timeout", fallback=5) return cls(name, host, port, timeout) # type: ignore except ValueError as error: raise ConfigurationError( f"Host port or timeout configuration wrong: {error}" ) from error def __init__(self, name: str, host: str, port: int, timeout: float) -> None: Check.__init__(self, name) self._host = host self._port = port self._timeout = timeout def _get_state(self) -> dict: client = MPDClient() client.timeout = self._timeout client.connect(self._host, self._port) state = client.status() client.close() client.disconnect() return state def check(self) -> str | None: try: state = self._get_state() if state["state"] == "play": return "MPD currently playing" else: return None except (TimeoutError, MPDError, ConnectionError, socket.gaierror) as error: raise TemporaryCheckError("Unable to get the current MPD state") from error autosuspend-7.2.0/src/autosuspend/checks/smb.py000066400000000000000000000023261475670032600216430ustar00rootroot00000000000000import configparser import subprocess from . import Activity, SevereCheckError, TemporaryCheckError class Smb(Activity): @classmethod def create( cls, name: str, config: configparser.SectionProxy | None # noqa: ARG003 ) -> "Smb": return cls(name) def _safe_get_status(self) -> str: try: return subprocess.check_output(["smbstatus", "-b"]).decode("utf-8") except FileNotFoundError as error: raise SevereCheckError("smbstatus binary not found") from error except subprocess.CalledProcessError as error: raise TemporaryCheckError("Unable to execute smbstatus") from error def check(self) -> str | None: status_output = self._safe_get_status() self.logger.debug("Received status output:\n%s", status_output) connections = [] start_seen = False for line in status_output.splitlines(): if start_seen: connections.append(line) else: if line.startswith("----"): start_seen = True if connections: return "SMB clients are connected:\n{}".format("\n".join(connections)) else: return None autosuspend-7.2.0/src/autosuspend/checks/stub.py000066400000000000000000000015021475670032600220320ustar00rootroot00000000000000import configparser from datetime import datetime, timedelta from . import ConfigurationError, Wakeup class Periodic(Wakeup): """Always indicates a wake up after a specified delta of time from now on. Use this to periodically wake up a system. """ @classmethod def create(cls, name: str, config: configparser.SectionProxy) -> "Periodic": try: kwargs = {config["unit"]: float(config["value"])} return cls(name, timedelta(**kwargs)) except (ValueError, KeyError, TypeError) as error: raise ConfigurationError(str(error)) from error def __init__(self, name: str, delta: timedelta) -> None: Wakeup.__init__(self, name) self._delta = delta def check(self, timestamp: datetime) -> datetime | None: return timestamp + self._delta autosuspend-7.2.0/src/autosuspend/checks/systemd.py000066400000000000000000000117351475670032600225560ustar00rootroot00000000000000from collections.abc import Iterable import configparser from datetime import datetime, timedelta, timezone import re from re import Pattern from typing import Any import dbus from . import Activity, ConfigurationError, TemporaryCheckError, Wakeup from ..util.systemd import list_logind_sessions, LogindDBusException _UINT64_MAX = 18446744073709551615 def next_timer_executions() -> dict[str, datetime]: bus = dbus.SystemBus() systemd = bus.get_object("org.freedesktop.systemd1", "/org/freedesktop/systemd1") units = systemd.ListUnits(dbus_interface="org.freedesktop.systemd1.Manager") timers = [unit for unit in units if unit[0].endswith(".timer")] def get_if_set(props: dict[str, Any], key: str) -> int | None: # For timers running after boot, next execution time might not be available. In # this case, the expected keys are all set to uint64 max. if props[key] and props[key] != _UINT64_MAX: return props[key] else: return None result: dict[str, datetime] = {} for timer in timers: obj = bus.get_object("org.freedesktop.systemd1", timer[6]) properties_interface = dbus.Interface(obj, "org.freedesktop.DBus.Properties") props = properties_interface.GetAll("org.freedesktop.systemd1.Timer") realtime = get_if_set(props, "NextElapseUSecRealtime") monotonic = get_if_set(props, "NextElapseUSecMonotonic") next_time: datetime | None = None if realtime is not None: next_time = datetime.fromtimestamp( realtime / 1000000, tz=timezone.utc, ) elif monotonic is not None: next_time = datetime.now(tz=timezone.utc) + timedelta( seconds=monotonic / 1000000 ) if next_time: result[str(timer[0])] = next_time return result class SystemdTimer(Wakeup): """Ensures that the system is active when some selected SystemD timers will run.""" @classmethod def create(cls, name: str, config: configparser.SectionProxy) -> "SystemdTimer": try: return cls(name, re.compile(config["match"])) except (re.error, ValueError, KeyError, TypeError) as error: raise ConfigurationError(str(error)) from error def __init__(self, name: str, match: Pattern) -> None: Wakeup.__init__(self, name) self._match = match def check(self, timestamp: datetime) -> datetime | None: # noqa: ARG002 executions = next_timer_executions() matching_executions = [ next_run for name, next_run in executions.items() if self._match.match(name) ] try: return min(matching_executions) except ValueError: return None class LogindSessionsIdle(Activity): """Prevents suspending in case a logind session is marked not idle. The decision is based on the ``IdleHint`` property of logind sessions. """ @classmethod def create( cls, name: str, config: configparser.SectionProxy, ) -> "LogindSessionsIdle": types = config.get("types", fallback="tty,x11,wayland").split(",") # type: ignore types = [t.strip() for t in types] states = config.get("states", fallback="active,online").split(",") # type: ignore states = [t.strip() for t in states] classes = config.get("classes", fallback="user").split(",") # type: ignore classes = [t.strip() for t in classes] return cls(name, types, states, classes) def __init__( self, name: str, types: Iterable[str], states: Iterable[str], classes: Iterable[str] = ("user"), ) -> None: Activity.__init__(self, name) self._types = types self._states = states self._classes = classes @staticmethod def _list_logind_sessions() -> Iterable[tuple[str, dict]]: try: return list_logind_sessions() except LogindDBusException as error: raise TemporaryCheckError(error) from error def check(self) -> str | None: for session_id, properties in self._list_logind_sessions(): self.logger.debug("Session %s properties: %s", session_id, properties) if properties["Type"] not in self._types: self.logger.debug( "Ignoring session of wrong type %s", properties["Type"] ) continue if properties["State"] not in self._states: self.logger.debug( "Ignoring session because its state is %s", properties["State"] ) continue if properties["Class"] not in self._classes: self.logger.debug( "Ignoring session because its class is %s", properties["Class"] ) continue if not properties["IdleHint"]: return f"Login session {session_id} is not idle" return None autosuspend-7.2.0/src/autosuspend/checks/util.py000066400000000000000000000074601475670032600220430ustar00rootroot00000000000000import configparser from contextlib import suppress from typing import Any, TYPE_CHECKING from . import ( Check, CheckType, ConfigurationError, SevereCheckError, TemporaryCheckError, ) if TYPE_CHECKING: import requests import requests.models class NetworkMixin(Check): @staticmethod def _ensure_credentials_consistent(args: dict[str, Any]) -> None: if (args["username"] is None) != (args["password"] is None): raise ConfigurationError("Username and password must be set") @classmethod def collect_init_args( cls, config: configparser.SectionProxy, ) -> dict[str, Any]: try: args: dict[str, Any] = {} args["timeout"] = config.getint("timeout", fallback=5) args["url"] = config["url"] args["username"] = config.get("username") args["password"] = config.get("password") cls._ensure_credentials_consistent(args) return args except ValueError as error: raise ConfigurationError("Configuration error " + str(error)) from error except KeyError as error: raise ConfigurationError("Lacks " + str(error) + " config entry") from error @classmethod def create( cls: type[CheckType], name: str, config: configparser.SectionProxy ) -> CheckType: return cls(name, **cls.collect_init_args(config)) # type: ignore def __init__( self, url: str, timeout: int, username: str | None = None, password: str | None = None, accept: str | None = None, ) -> None: self._url = url self._timeout = timeout self._username = username self._password = password self._accept = accept @staticmethod def _create_session() -> "requests.Session": import requests session = requests.Session() with suppress(ImportError): from requests_file import FileAdapter session.mount("file://", FileAdapter()) return session def _request_headers(self) -> dict[str, str] | None: if self._accept: return {"Accept": self._accept} else: return None def _create_auth_from_failed_request( self, reply: "requests.models.Response", username: str, password: str, ) -> Any: from requests.auth import HTTPBasicAuth, HTTPDigestAuth auth_map = { "basic": HTTPBasicAuth, "digest": HTTPDigestAuth, } auth_scheme = reply.headers["WWW-Authenticate"].split(" ")[0].lower() if auth_scheme not in auth_map: raise SevereCheckError(f"Unsupported authentication scheme {auth_scheme}") return auth_map[auth_scheme](username, password) def request(self) -> "requests.models.Response": import requests import requests.exceptions session = self._create_session() try: reply = session.get( self._url, timeout=self._timeout, headers=self._request_headers() ) # replace reply with an authenticated version if credentials are # available and the server has requested authentication if self._username and self._password and reply.status_code == 401: reply = session.get( self._url, timeout=self._timeout, auth=self._create_auth_from_failed_request( reply, self._username, self._password ), headers=self._request_headers(), ) reply.raise_for_status() return reply except requests.exceptions.RequestException as error: raise TemporaryCheckError(error) from error autosuspend-7.2.0/src/autosuspend/checks/wakeup.py000066400000000000000000000007521475670032600223570ustar00rootroot00000000000000from contextlib import suppress # isort: off from .command import CommandWakeup as Command # noqa from .linux import File # noqa from .stub import Periodic # noqa with suppress(ModuleNotFoundError): from .ical import Calendar # noqa with suppress(ModuleNotFoundError): from .xpath import XPathWakeup as XPath # noqa from .xpath import XPathDeltaWakeup as XPathDelta # noqa with suppress(ModuleNotFoundError): from .systemd import SystemdTimer # noqa # isort: on autosuspend-7.2.0/src/autosuspend/checks/xorg.py000066400000000000000000000174111475670032600220420ustar00rootroot00000000000000from collections.abc import Callable import configparser from contextlib import suppress import copy from dataclasses import dataclass import logging import os from pathlib import Path import re from re import Pattern import subprocess import warnings import psutil from . import Activity, ConfigurationError, SevereCheckError, TemporaryCheckError from ..util.systemd import list_logind_sessions, LogindDBusException @dataclass class XorgSession: display: int user: str _logger = logging.getLogger(__name__) def list_sessions_sockets(socket_path: Path | None = None) -> list[XorgSession]: """List running X sessions by iterating the X sockets. This method assumes that X servers are run under the users using the server. """ folder = socket_path or Path("/tmp/.X11-unix/") # noqa: S108 expected default path sockets = folder.glob("X*") _logger.debug("Found sockets: %s", sockets) results = [] for sock in sockets: # determine the number of the X display by stripping the X prefix try: display = int(sock.name[1:]) except ValueError: _logger.warning( "Cannot parse display number from socket %s. Skipping.", sock, exc_info=True, ) continue # determine the user of the display try: user = sock.owner() except (FileNotFoundError, KeyError): _logger.warning( "Cannot get the owning user from socket %s. Skipping.", sock, exc_info=True, ) continue results.append(XorgSession(display, user)) return results def list_sessions_logind() -> list[XorgSession]: """List running X sessions using logind. This method assumes that a ``Display`` variable is set in the logind sessions. Raises: LogindDBusException: cannot connect or extract sessions """ results = [] for session_id, properties in list_logind_sessions(): if "Name" not in properties or "Display" not in properties: _logger.debug( "Skipping session %s because it does not contain " "a user name and a display", session_id, ) continue try: results.append( XorgSession( int(properties["Display"].replace(":", "")), str(properties["Name"]), ) ) except ValueError: _logger.warning( "Unable to parse display from session properties %s", properties, exc_info=True, ) return results class XIdleTime(Activity): """Check that local X display have been idle long enough.""" @classmethod def create(cls, name: str, config: configparser.SectionProxy) -> "XIdleTime": with warnings.catch_warnings(): warnings.simplefilter("ignore", FutureWarning) try: return cls( name, config.getint("timeout", fallback=600), config.get("method", fallback="sockets"), # type: ignore re.compile(config.get("ignore_if_process", fallback=r"a^")), re.compile(config.get("ignore_users", fallback=r"a^")), ) except re.error as error: raise ConfigurationError( f"Regular expression is invalid: {error}", ) from error except ValueError as error: raise ConfigurationError( f"Unable to parse configuration: {error}", ) from error @staticmethod def _get_session_method(method: str) -> Callable[[], list[XorgSession]]: if method == "sockets": return list_sessions_sockets elif method == "logind": return list_sessions_logind else: raise ValueError(f"Unknown session discovery method {method}") def __init__( self, name: str, timeout: float, method: str, ignore_process_re: Pattern, ignore_users_re: Pattern, ) -> None: Activity.__init__(self, name) self._timeout = timeout self._provide_sessions: Callable[[], list[XorgSession]] self._provide_sessions = self._get_session_method(method) self._ignore_process_re = ignore_process_re self._ignore_users_re = ignore_users_re @staticmethod def _get_user_processes(user: str) -> list[psutil.Process]: user_processes = [] for process in psutil.process_iter(): with suppress( psutil.NoSuchProcess, psutil.ZombieProcess, psutil.AccessDenied ): if process.username() == user: user_processes.append(process.name()) return user_processes def _is_skip_process_running(self, user: str) -> bool: for process in self._get_user_processes(user): if self._ignore_process_re.match(process) is not None: self.logger.debug( "Process %s with pid %s matches the ignore regex '%s'." " Skipping idle time check for this user.", process.name(), process.pid, self._ignore_process_re, ) return True return False def _safe_provide_sessions(self) -> list[XorgSession]: try: return self._provide_sessions() except LogindDBusException as error: raise TemporaryCheckError(error) from error def _get_idle_time(self, session: XorgSession) -> float: env = copy.deepcopy(os.environ) env["DISPLAY"] = f":{session.display}" env["XAUTHORITY"] = str(Path("~" + session.user).expanduser() / ".Xauthority") try: idle_time_output = subprocess.check_output( ["sudo", "-u", session.user, "xprintidle"], env=env ) return float(idle_time_output.strip()) / 1000.0 except FileNotFoundError as error: raise SevereCheckError("sudo executable not found") from error except (subprocess.CalledProcessError, ValueError) as error: self.logger.warning( "Unable to determine the idle time for display %s.", session.display, exc_info=True, ) raise TemporaryCheckError("Unable to call xprintidle") from error def check(self) -> str | None: for session in self._safe_provide_sessions(): self.logger.info("Checking session %s", session) # check whether this users should be ignored completely if self._ignore_users_re.match(session.user) is not None: self.logger.debug("Skipping user '%s' due to request", session.user) continue # check whether any of the running processes of this user matches # the ignore regular expression. In that case we skip idletime # checking because we assume the user has a process running that # inevitably tampers with the idle time. if self._is_skip_process_running(session.user): continue idle_time = self._get_idle_time(session) self.logger.debug( "Idle time for display %s of user %s is %s seconds.", session.display, session.user, idle_time, ) if idle_time < self._timeout: return ( f"X session {session.display} of user {session.user} " f"has idle time {idle_time} < threshold {self._timeout}" ) return None autosuspend-7.2.0/src/autosuspend/checks/xpath.py000066400000000000000000000104041475670032600222020ustar00rootroot00000000000000from collections.abc import Sequence import configparser from datetime import datetime, timedelta, timezone from typing import Any from lxml import etree # using safe parser from lxml.etree import XPath, XPathSyntaxError # our input import requests import requests.exceptions from . import Activity, CheckType, ConfigurationError, TemporaryCheckError, Wakeup from .util import NetworkMixin class XPathMixin(NetworkMixin): @classmethod def collect_init_args(cls, config: configparser.SectionProxy) -> dict[str, Any]: try: args = NetworkMixin.collect_init_args(config) args["xpath"] = config["xpath"].strip() # validate the expression try: XPath(args["xpath"]) except XPathSyntaxError as error: raise ConfigurationError( "Invalid xpath expression: " + args["xpath"] ) from error return args except KeyError as error: raise ConfigurationError("Lacks " + str(error) + " config entry") from error @classmethod def create( cls: type[CheckType], name: str, config: configparser.SectionProxy ) -> CheckType: return cls(name, **cls.collect_init_args(config)) # type: ignore def __init__(self, xpath: str, **kwargs: Any) -> None: NetworkMixin.__init__(self, **kwargs) self._xpath = xpath self._parser = etree.XMLParser(resolve_entities=False) def evaluate(self) -> Sequence[Any]: try: reply = self.request().content root = etree.fromstring(reply, parser=self._parser) # noqa: S320 return root.xpath(self._xpath) except requests.exceptions.RequestException as error: raise TemporaryCheckError(error) from error except etree.XMLSyntaxError as error: raise TemporaryCheckError(error) from error class XPathActivity(XPathMixin, Activity): def __init__(self, name: str, **kwargs: Any) -> None: Activity.__init__(self, name) XPathMixin.__init__(self, **kwargs) def check(self) -> str | None: if self.evaluate(): return "XPath matches for url " + self._url else: return None class XPathWakeup(XPathMixin, Wakeup): """Determine wake up times from a network resource using XPath expressions. The matched results are expected to represent timestamps in seconds UTC. """ def __init__(self, name: str, **kwargs: Any) -> None: Wakeup.__init__(self, name) XPathMixin.__init__(self, **kwargs) def convert_result( self, result: str, timestamp: datetime, # noqa: ARG002 ) -> datetime: return datetime.fromtimestamp(float(result), timezone.utc) def check(self, timestamp: datetime) -> datetime | None: matches = self.evaluate() try: if matches: return min(self.convert_result(m, timestamp) for m in matches) else: return None except TypeError as error: raise TemporaryCheckError( "XPath returned a result that is not a string: " + str(error) ) from None except ValueError as error: raise TemporaryCheckError( "Result cannot be parsed: " + str(error) ) from error class XPathDeltaWakeup(XPathWakeup): UNITS = ( "days", "seconds", "microseconds", "milliseconds", "minutes", "hours", "weeks", ) @classmethod def create(cls, name: str, config: configparser.SectionProxy) -> "XPathDeltaWakeup": try: args = XPathWakeup.collect_init_args(config) args["unit"] = config.get("unit", fallback="minutes") return cls(name, **args) except ValueError as error: raise ConfigurationError(str(error)) from error def __init__(self, name: str, unit: str, **kwargs: Any) -> None: if unit not in self.UNITS: raise ValueError("Unsupported unit") XPathWakeup.__init__(self, name, **kwargs) self._unit = unit def convert_result(self, result: str, timestamp: datetime) -> datetime: kwargs = {self._unit: float(result)} return timestamp + timedelta(**kwargs) autosuspend-7.2.0/src/autosuspend/util/000077500000000000000000000000001475670032600202225ustar00rootroot00000000000000autosuspend-7.2.0/src/autosuspend/util/__init__.py000066400000000000000000000007371475670032600223420ustar00rootroot00000000000000import logging from typing import Any def logger_by_class(klass: type, name: str | None = None) -> logging.Logger: return logging.getLogger( "{module}.{klass}{name}".format( module=klass.__module__, klass=klass.__name__, name=f".{name}" if name else "", ) ) def logger_by_class_instance( instance: Any, name: str | None = None, ) -> logging.Logger: return logger_by_class(instance.__class__, name=name) autosuspend-7.2.0/src/autosuspend/util/datetime.py000066400000000000000000000004111475670032600223640ustar00rootroot00000000000000from datetime import datetime, tzinfo def is_aware(dt: datetime) -> bool: return dt.tzinfo is not None and dt.tzinfo.utcoffset(dt) is not None def to_tz_unaware(dt: datetime, tz: tzinfo | None) -> datetime: return dt.astimezone(tz).replace(tzinfo=None) autosuspend-7.2.0/src/autosuspend/util/systemd.py000066400000000000000000000025241475670032600222670ustar00rootroot00000000000000from collections.abc import Iterable from typing import TYPE_CHECKING if TYPE_CHECKING: import dbus def _get_bus() -> "dbus.SystemBus": import dbus return dbus.SystemBus() class LogindDBusException(RuntimeError): """Indicates an error communicating to Logind via DBus.""" def list_logind_sessions() -> Iterable[tuple[str, dict]]: """List running logind sessions and their properties. Returns: list of (session_id, properties dict): A list with tuples of sessions ids and their associated properties represented as dicts. """ import dbus try: bus = _get_bus() login1 = bus.get_object("org.freedesktop.login1", "/org/freedesktop/login1") sessions = login1.ListSessions(dbus_interface="org.freedesktop.login1.Manager") results = [] for session_id, path in [(s[0], s[4]) for s in sessions]: session = bus.get_object("org.freedesktop.login1", path) properties_interface = dbus.Interface( session, "org.freedesktop.DBus.Properties" ) properties = properties_interface.GetAll("org.freedesktop.login1.Session") results.append((session_id, properties)) except dbus.exceptions.DBusException as error: raise LogindDBusException(error) from error return results autosuspend-7.2.0/tests/000077500000000000000000000000001475670032600152465ustar00rootroot00000000000000autosuspend-7.2.0/tests/__init__.py000066400000000000000000000004641475670032600173630ustar00rootroot00000000000000import abc from autosuspend.checks import Check class CheckTest(abc.ABC): @abc.abstractmethod def create_instance(self, name: str) -> Check: pass def test_the_configured_name_is_used(self) -> None: name = "checktestname" assert self.create_instance(name).name == name autosuspend-7.2.0/tests/conftest.py000066400000000000000000000055101475670032600174460ustar00rootroot00000000000000from collections.abc import Callable, Iterable from pathlib import Path from typing import Any from dbus import Bus from dbus.proxies import ProxyObject import dbusmock from dbusmock.pytest_fixtures import dbusmock_system, PrivateDBus # noqa: F401 import pytest from pytest_httpserver import HTTPServer from werkzeug.wrappers import Request, Response from autosuspend.util import systemd as util_systemd @pytest.fixture def serve_file(httpserver: HTTPServer) -> Callable[[Path], str]: """Serve a file via HTTP. Returns: A callable that expected the file path to server. It returns the URL to use for accessing the file. """ def serve(the_file: Path) -> str: path = f"/{the_file.name}" httpserver.expect_request(path).respond_with_data(the_file.read_bytes()) return httpserver.url_for(path) return serve @pytest.fixture def serve_protected(httpserver: HTTPServer) -> Callable[[Path], tuple[str, str, str]]: """Serve a file behind basic authentication. Returns: A callable that accepts the file path to serve. It returns as a tuple the URL to use for the file, valid username and password """ realm = "the_realm" username = "the_user" password = "the_password" # only for testing def serve(the_file: Path) -> tuple[str, str, str]: def handler(request: Request) -> Response: auth = request.authorization if not auth or not ( auth.username == username and auth.password == password ): return Response( "Authentication required", 401, {"WWW-Authenticate": f"Basic realm={realm}"}, ) else: return Response(the_file.read_bytes()) path = f"/{the_file.name}" httpserver.expect_request(path).respond_with_handler(handler) return (httpserver.url_for(path), username, password) return serve @pytest.fixture def logind( monkeypatch: Any, dbusmock_system: PrivateDBus, # noqa ) -> Iterable[ProxyObject]: pytest.importorskip("dbus") pytest.importorskip("gi") with dbusmock.SpawnedMock.spawn_with_template("logind") as server: def get_bus() -> Bus: return dbusmock_system.bustype.get_connection() monkeypatch.setattr(util_systemd, "_get_bus", get_bus) yield server.obj @pytest.fixture def _logind_dbus_error( monkeypatch: Any, dbusmock_system: PrivateDBus # noqa ) -> Iterable[None]: pytest.importorskip("dbus") pytest.importorskip("gi") with dbusmock.SpawnedMock.spawn_with_template("logind"): def get_bus() -> Bus: import dbus raise dbus.exceptions.ValidationException("Test") monkeypatch.setattr(util_systemd, "_get_bus", get_bus) yield autosuspend-7.2.0/tests/data/000077500000000000000000000000001475670032600161575ustar00rootroot00000000000000autosuspend-7.2.0/tests/data/mindeps-test.conf000066400000000000000000000004411475670032600214410ustar00rootroot00000000000000[general] interval = 5 idle_time = 900 suspend_cmd = /usr/bin/systemctl suspend wakeup_cmd = echo {timestamp:.0f} > /sys/class/rtc/rtc0/wakealarm woke_up_file = /var/run/autosuspend-just-woke-up lock_file = /tmp/autosuspend-test-mindeps.lock [check.Ping] enabled = true hosts = localhost autosuspend-7.2.0/tests/test_autosuspend.py000066400000000000000000000570511475670032600212410ustar00rootroot00000000000000import argparse import configparser from datetime import datetime, timedelta, timezone import logging import subprocess from typing import Any import dateutil.parser import pytest from pytest_mock import MockerFixture import autosuspend class TestExecuteSuspend: def test_smoke(self, mocker: MockerFixture) -> None: mock = mocker.patch("subprocess.check_call") command = ["foo", "bar"] autosuspend.execute_suspend(command, None) mock.assert_called_once_with(command, shell=True) def test_call_exception(self, mocker: MockerFixture) -> None: mock = mocker.patch("subprocess.check_call") command = ["foo", "bar"] mock.side_effect = subprocess.CalledProcessError(2, command) spy = mocker.spy(autosuspend._logger, "warning") autosuspend.execute_suspend(command, None) mock.assert_called_once_with(command, shell=True) assert spy.call_count == 1 class TestScheduleWakeup: def test_smoke(self, mocker: MockerFixture) -> None: mock = mocker.patch("subprocess.check_call") dt = datetime.fromtimestamp(1525270801, timezone(timedelta(hours=4))) autosuspend.schedule_wakeup("echo {timestamp:.0f} {iso}", dt) mock.assert_called_once_with( "echo 1525270801 2018-05-02T18:20:01+04:00", shell=True ) def test_call_exception(self, mocker: MockerFixture) -> None: mock = mocker.patch("subprocess.check_call") mock.side_effect = subprocess.CalledProcessError(2, "foo") spy = mocker.spy(autosuspend._logger, "warning") autosuspend.schedule_wakeup("foo", datetime.now(timezone.utc)) mock.assert_called_once_with("foo", shell=True) assert spy.call_count == 1 class TestConfigureLogging: def test_debug(self, mocker: MockerFixture) -> None: mock = mocker.patch("logging.basicConfig") autosuspend.configure_logging(None, True) mock.assert_called_once_with(level=logging.DEBUG) def test_standard(self, mocker: MockerFixture) -> None: mock = mocker.patch("logging.basicConfig") autosuspend.configure_logging(None, False) mock.assert_called_once_with(level=logging.WARNING) def test_file(self, mocker: MockerFixture) -> None: mock = mocker.patch("logging.config.fileConfig") # anything that is not a boolean is treated like a file autosuspend.configure_logging(42, False) # type: ignore mock.assert_called_once_with(42) def test_file_fallback(self, mocker: MockerFixture) -> None: mock = mocker.patch("logging.config.fileConfig", side_effect=RuntimeError()) mock_basic = mocker.patch("logging.basicConfig") # anything that is not a boolean is treated like a file autosuspend.configure_logging(42, False) # type: ignore mock.assert_called_once_with(42) mock_basic.assert_called_once_with(level=logging.WARNING) class TestSetUpChecks: def test_smoke(self, mocker: MockerFixture) -> None: mock_class = mocker.patch("autosuspend.checks.activity.Mpd") mock_class.create.return_value = mocker.MagicMock( spec=autosuspend.checks.Activity ) parser = configparser.ConfigParser() parser.read_string( """ [check.Foo] class = Mpd enabled = True """ ) autosuspend.set_up_checks( parser, "check", "activity", autosuspend.Activity # type: ignore ) mock_class.create.assert_called_once_with("Foo", parser["check.Foo"]) def test_external_class(self, mocker: MockerFixture) -> None: mock_class = mocker.patch("os.path.TestCheck", create=True) mock_class.create.return_value = mocker.MagicMock( spec=autosuspend.checks.Activity ) parser = configparser.ConfigParser() parser.read_string( """ [check.Foo] class = os.path.TestCheck enabled = True """ ) autosuspend.set_up_checks( parser, "check", "activity", autosuspend.Activity # type: ignore ) mock_class.create.assert_called_once_with("Foo", parser["check.Foo"]) def test_not_enabled(self, mocker: MockerFixture) -> None: mock_class = mocker.patch("autosuspend.checks.activity.Mpd") mock_class.create.return_value = mocker.MagicMock(spec=autosuspend.Activity) parser = configparser.ConfigParser() parser.read_string( """ [check.Foo] class = Mpd enabled = False """ ) assert not autosuspend.set_up_checks( parser, "check", "activity", autosuspend.Activity, # type: ignore ) with pytest.raises(autosuspend.ConfigurationError): autosuspend.set_up_checks( parser, "check", "activity", autosuspend.Activity, # type: ignore error_none=True, ) def test_not_enabled_continues_with_next(self, mocker: MockerFixture) -> None: mock_mpd = mocker.patch("autosuspend.checks.activity.Mpd") mock_mpd.create.return_value = mocker.MagicMock(spec=autosuspend.Activity) mock_xidletime = mocker.patch("autosuspend.checks.activity.XIdleTime") mock_xidletime.create.return_value = mocker.MagicMock(spec=autosuspend.Activity) parser = configparser.ConfigParser() parser.read_string( """ [check.Foo] class = Mpd enabled = False [check.Bar] class = XIdleTime enabled = True """ ) assert ( len( autosuspend.set_up_checks( parser, "check", "activity", autosuspend.Activity, # type: ignore ) ) == 1 ) def test_no_such_class(self) -> None: parser = configparser.ConfigParser() parser.read_string( """ [check.Foo] class = FooBarr enabled = True """ ) with pytest.raises(autosuspend.ConfigurationError): autosuspend.set_up_checks( parser, "check", "activity", autosuspend.Activity # type: ignore ) def test_not_a_check(self, mocker: MockerFixture) -> None: mock_class = mocker.patch("autosuspend.checks.activity.Mpd") mock_class.create.return_value = mocker.MagicMock() parser = configparser.ConfigParser() parser.read_string( """ [check.Foo] class = Mpd enabled = True """ ) with pytest.raises(autosuspend.ConfigurationError): autosuspend.set_up_checks( parser, "check", "activity", autosuspend.Activity # type: ignore ) mock_class.create.assert_called_once_with("Foo", parser["check.Foo"]) def test_passwords_redacted(self, mocker: MockerFixture, caplog: Any) -> None: mock_class = mocker.patch("autosuspend.checks.activity.Mpd") mock_class.create.return_value = mocker.MagicMock( spec=autosuspend.checks.Activity ) parser = configparser.ConfigParser() parser.read_string( """ [check.Foo] class = Mpd enabled = True password = THEPASS """ ) with caplog.at_level(logging.DEBUG): autosuspend.set_up_checks( parser, "check", "activity", autosuspend.Activity # type: ignore ) assert "THEPASS" not in caplog.text class TestExecuteChecks: def test_no_checks(self, mocker: MockerFixture) -> None: assert autosuspend.execute_checks([], False, mocker.MagicMock()) is False def test_matches(self, mocker: MockerFixture) -> None: matching_check = mocker.MagicMock(spec=autosuspend.Activity) matching_check.name = "foo" matching_check.check.return_value = "matches" assert ( autosuspend.execute_checks([matching_check], False, mocker.MagicMock()) is True ) matching_check.check.assert_called_once_with() def test_only_first_called(self, mocker: MockerFixture) -> None: matching_check = mocker.MagicMock(spec=autosuspend.Activity) matching_check.name = "foo" matching_check.check.return_value = "matches" second_check = mocker.MagicMock() second_check.name = "bar" second_check.check.return_value = "matches" assert ( autosuspend.execute_checks( [matching_check, second_check], False, mocker.MagicMock() ) is True ) matching_check.check.assert_called_once_with() second_check.check.assert_not_called() def test_all_called(self, mocker: MockerFixture) -> None: matching_check = mocker.MagicMock(spec=autosuspend.Activity) matching_check.name = "foo" matching_check.check.return_value = "matches" second_check = mocker.MagicMock() second_check.name = "bar" second_check.check.return_value = "matches" assert ( autosuspend.execute_checks( [matching_check, second_check], True, mocker.MagicMock() ) is True ) matching_check.check.assert_called_once_with() second_check.check.assert_called_once_with() def test_treat_temporary_errors_as_activity(self, mocker: MockerFixture) -> None: matching_check = mocker.MagicMock(spec=autosuspend.Activity) matching_check.name = "foo" matching_check.check.side_effect = autosuspend.TemporaryCheckError() assert ( autosuspend.execute_checks([matching_check], False, mocker.MagicMock()) is True ) matching_check.check.assert_called_once_with() class TestExecuteWakeups: def test_no_wakeups(self, mocker: MockerFixture) -> None: assert ( autosuspend.execute_wakeups( [], datetime.now(timezone.utc), mocker.MagicMock() ) is None ) def test_all_none(self, mocker: MockerFixture) -> None: wakeup = mocker.MagicMock(spec=autosuspend.Wakeup) wakeup.check.return_value = None assert ( autosuspend.execute_wakeups( [wakeup], datetime.now(timezone.utc), mocker.MagicMock() ) is None ) @pytest.mark.parametrize( "illegal", [None, dateutil.parser.parse("20040605T090000Z")], ) def test_skips_none_outdated_and_continues( self, mocker: MockerFixture, illegal: datetime | None ) -> None: wakeup_none = mocker.MagicMock(spec=autosuspend.Wakeup) wakeup_none.check.return_value = illegal now = dateutil.parser.parse("20040705T090000Z") wake_up_at = now + timedelta(minutes=10) wakeup_real = mocker.MagicMock(spec=autosuspend.Wakeup) wakeup_real.check.return_value = wake_up_at assert ( autosuspend.execute_wakeups( [wakeup_none, wakeup_real], now, mocker.MagicMock(), ) == wake_up_at ) assert wakeup_none.check.called def test_basic_return(self, mocker: MockerFixture) -> None: wakeup = mocker.MagicMock(spec=autosuspend.Wakeup) now = datetime.now(timezone.utc) wakeup_time = now + timedelta(seconds=10) wakeup.check.return_value = wakeup_time assert ( autosuspend.execute_wakeups([wakeup], now, mocker.MagicMock()) == wakeup_time ) def test_soonest_taken(self, mocker: MockerFixture) -> None: reference = datetime.now(timezone.utc) wakeup = mocker.MagicMock(spec=autosuspend.Wakeup) wakeup.check.return_value = reference + timedelta(seconds=20) earlier = reference + timedelta(seconds=10) wakeup_earlier = mocker.MagicMock(spec=autosuspend.Wakeup) wakeup_earlier.check.return_value = earlier in_between = reference + timedelta(seconds=15) wakeup_later = mocker.MagicMock(spec=autosuspend.Wakeup) wakeup_later.check.return_value = in_between assert ( autosuspend.execute_wakeups( [wakeup, wakeup_earlier, wakeup_later], reference, mocker.MagicMock() ) == earlier ) def test_ignore_temporary_errors(self, mocker: MockerFixture) -> None: now = datetime.now(timezone.utc) wakeup = mocker.MagicMock(spec=autosuspend.Wakeup) wakeup.check.return_value = now + timedelta(seconds=20) wakeup_error = mocker.MagicMock(spec=autosuspend.Wakeup) wakeup_error.check.side_effect = autosuspend.TemporaryCheckError() wakeup_earlier = mocker.MagicMock(spec=autosuspend.Wakeup) wakeup_earlier.check.return_value = now + timedelta(seconds=10) assert autosuspend.execute_wakeups( [wakeup, wakeup_error, wakeup_earlier], now, mocker.MagicMock() ) == now + timedelta(seconds=10) def test_ignore_too_early(self, mocker: MockerFixture) -> None: now = datetime.now(timezone.utc) wakeup = mocker.MagicMock(spec=autosuspend.Wakeup) wakeup.check.return_value = now assert autosuspend.execute_wakeups([wakeup], now, mocker.MagicMock()) is None assert ( autosuspend.execute_wakeups( [wakeup], now + timedelta(seconds=1), mocker.MagicMock() ) is None ) class TestNotifySuspend: def test_date(self, mocker: MockerFixture) -> None: mock = mocker.patch("subprocess.check_call") dt = datetime.fromtimestamp(1525270801, timezone(timedelta(hours=4))) autosuspend.notify_suspend("echo {timestamp:.0f} {iso}", "not this", dt) mock.assert_called_once_with( "echo 1525270801 2018-05-02T18:20:01+04:00", shell=True ) def test_date_no_command(self, mocker: MockerFixture) -> None: mock = mocker.patch("subprocess.check_call") dt = datetime.fromtimestamp(1525270801, timezone(timedelta(hours=4))) autosuspend.notify_suspend(None, "not this", dt) mock.assert_not_called() def test_no_date(self, mocker: MockerFixture) -> None: mock = mocker.patch("subprocess.check_call") autosuspend.notify_suspend("echo {timestamp:.0f} {iso}", "echo nothing", None) mock.assert_called_once_with("echo nothing", shell=True) def test_no_date_no_command(self, mocker: MockerFixture) -> None: mock = mocker.patch("subprocess.check_call") autosuspend.notify_suspend("echo {timestamp:.0f} {iso}", None, None) mock.assert_not_called() def test_ignore_execution_errors(self, mocker: MockerFixture, caplog: Any) -> None: mock = mocker.patch("subprocess.check_call") mock.side_effect = subprocess.CalledProcessError(2, "cmd") dt = datetime.fromtimestamp(1525270801, timezone(timedelta(hours=4))) with caplog.at_level(logging.WARNING): autosuspend.notify_suspend("wakeup", "nowakeup", dt) assert "Unable to execute" in caplog.text assert mock.called def test_info_no_command(self, caplog: Any) -> None: with caplog.at_level(logging.INFO): autosuspend.notify_suspend(None, None, datetime.now(tz=timezone.utc)) assert "suitable" in caplog.text class TestConfigureProcessor: def test_minimal_config(self, mocker: MockerFixture) -> None: parser = configparser.ConfigParser() parser.read_string( """ [general] suspend_cmd = suspend wakeup_cmd = wakeup """ ) args = mocker.MagicMock(spec=argparse.Namespace) type(args).all_checks = mocker.PropertyMock(return_value=True) processor = autosuspend.configure_processor(args, parser, [], []) assert processor._idle_time == 300 assert processor._min_sleep_time == 1200 assert processor._wakeup_delta == 30 assert processor._all_activities def test_notify_and_suspend(mocker: MockerFixture) -> None: mock = mocker.patch("subprocess.check_call") dt = datetime.fromtimestamp(1525270801, timezone(timedelta(hours=4))) autosuspend.notify_and_suspend( "echo suspend", "echo notify {timestamp:.0f} {iso}", "not this", dt ) mock.assert_has_calls( [ mocker.call("echo notify 1525270801 2018-05-02T18:20:01+04:00", shell=True), mocker.call("echo suspend", shell=True), ] ) class _StubCheck(autosuspend.Activity): @classmethod def create(cls, name: str, config: configparser.SectionProxy) -> "_StubCheck": raise NotImplementedError() def __init__(self, name: str, match: str | None) -> None: autosuspend.Activity.__init__(self, name) self.match = match def check(self) -> str | None: return self.match class SleepFn: def __init__(self) -> None: self.called = False self.call_arg: float | None = None def reset(self) -> None: self.called = False self.call_arg = None def __call__(self, arg: float) -> None: self.called = True self.call_arg = arg @pytest.fixture def sleep_fn() -> SleepFn: return SleepFn() class WakeupFn: def __init__(self) -> None: self.call_arg: datetime | None = None def reset(self) -> None: self.call_arg = None def __call__(self, arg: datetime) -> None: self.call_arg = arg @pytest.fixture def wakeup_fn() -> WakeupFn: return WakeupFn() class TestProcessor: def test_smoke(self, sleep_fn: SleepFn, wakeup_fn: WakeupFn) -> None: processor = autosuspend.Processor( [_StubCheck("stub", None)], [], 2, 0, 0, sleep_fn, wakeup_fn, False ) # should init the timestamp initially start = datetime.now(timezone.utc) processor.iteration(start, False) assert not sleep_fn.called # not yet reached processor.iteration(start + timedelta(seconds=1), False) assert not sleep_fn.called # time must be greater, not equal processor.iteration(start + timedelta(seconds=2), False) assert not sleep_fn.called # go to sleep processor.iteration(start + timedelta(seconds=3), False) assert sleep_fn.called assert sleep_fn.call_arg is None sleep_fn.reset() # second iteration to check that the idle time got reset processor.iteration(start + timedelta(seconds=4), False) assert not sleep_fn.called # go to sleep again processor.iteration(start + timedelta(seconds=6, milliseconds=2), False) assert sleep_fn.called assert wakeup_fn.call_arg is None def test_just_woke_up_handling( self, sleep_fn: SleepFn, wakeup_fn: WakeupFn ) -> None: processor = autosuspend.Processor( [_StubCheck("stub", None)], [], 2, 0, 0, sleep_fn, wakeup_fn, False ) # should init the timestamp initially start = datetime.now(timezone.utc) processor.iteration(start, False) assert not sleep_fn.called # should go to sleep but we just woke up processor.iteration(start + timedelta(seconds=3), True) assert not sleep_fn.called # start over again processor.iteration(start + timedelta(seconds=4), False) assert not sleep_fn.called # not yet sleeping processor.iteration(start + timedelta(seconds=6), False) assert not sleep_fn.called # now go to sleep processor.iteration(start + timedelta(seconds=7), False) assert sleep_fn.called assert wakeup_fn.call_arg is None def test_wakeup_blocks_sleep( self, mocker: MockerFixture, sleep_fn: SleepFn, wakeup_fn: WakeupFn ) -> None: start = datetime.now(timezone.utc) wakeup = mocker.MagicMock(spec=autosuspend.Wakeup) wakeup.check.return_value = start + timedelta(seconds=6) processor = autosuspend.Processor( [_StubCheck("stub", None)], [wakeup], 2, 3.1, 0, sleep_fn, wakeup_fn, False ) # init iteration processor.iteration(start, False) # no activity and enough time passed to start sleeping processor.iteration(start + timedelta(seconds=3), False) assert not sleep_fn.called assert wakeup_fn.call_arg is None def test_wakeup_exact_hit_does_not_block( self, mocker: MockerFixture, sleep_fn: SleepFn, wakeup_fn: WakeupFn, ) -> None: start = datetime.now(timezone.utc) wakeup = mocker.MagicMock(spec=autosuspend.Wakeup) wakeup.check.return_value = start + timedelta(seconds=6) processor = autosuspend.Processor( [_StubCheck("stub", None)], [wakeup], 2, 3, 0, sleep_fn, wakeup_fn, False ) # init iteration processor.iteration(start, False) # no activity and enough time passed to start sleeping processor.iteration(start + timedelta(seconds=3), False) assert sleep_fn.called assert wakeup_fn.call_arg is not None def test_wakeup_scheduled( self, mocker: MockerFixture, sleep_fn: SleepFn, wakeup_fn: WakeupFn ) -> None: start = datetime.now(timezone.utc) wakeup = mocker.MagicMock(spec=autosuspend.Wakeup) wakeup.check.return_value = start + timedelta(seconds=25) processor = autosuspend.Processor( [_StubCheck("stub", None)], [wakeup], 2, 10, 0, sleep_fn, wakeup_fn, False ) # init iteration processor.iteration(start, False) # no activity and enough time passed to start sleeping processor.iteration(start + timedelta(seconds=3), False) assert sleep_fn.called assert sleep_fn.call_arg == start + timedelta(seconds=25) assert wakeup_fn.call_arg == start + timedelta(seconds=25) sleep_fn.reset() wakeup_fn.reset() # ensure that wake up is not scheduled again processor.iteration(start + timedelta(seconds=25), False) assert wakeup_fn.call_arg is None def test_wakeup_delta_blocks( self, mocker: MockerFixture, sleep_fn: SleepFn, wakeup_fn: WakeupFn ) -> None: start = datetime.now(timezone.utc) wakeup = mocker.MagicMock(spec=autosuspend.Wakeup) wakeup.check.return_value = start + timedelta(seconds=25) processor = autosuspend.Processor( [_StubCheck("stub", None)], [wakeup], 2, 10, 22, sleep_fn, wakeup_fn, False ) # init iteration processor.iteration(start, False) # no activity and enough time passed to start sleeping processor.iteration(start + timedelta(seconds=3), False) assert not sleep_fn.called def test_wakeup_delta_applied( self, mocker: MockerFixture, sleep_fn: SleepFn, wakeup_fn: WakeupFn ) -> None: start = datetime.now(timezone.utc) wakeup = mocker.MagicMock(spec=autosuspend.Wakeup) wakeup.check.return_value = start + timedelta(seconds=25) processor = autosuspend.Processor( [_StubCheck("stub", None)], [wakeup], 2, 10, 4, sleep_fn, wakeup_fn, False ) # init iteration processor.iteration(start, False) # no activity and enough time passed to start sleeping processor.iteration(start + timedelta(seconds=3), False) assert sleep_fn.called assert wakeup_fn.call_arg == start + timedelta(seconds=21) autosuspend-7.2.0/tests/test_checks.py000066400000000000000000000012241475670032600201160ustar00rootroot00000000000000import configparser from autosuspend.checks import Check class DummyCheck(Check): @classmethod def create(cls, name: str, config: configparser.SectionProxy) -> "DummyCheck": raise NotImplementedError() def check(self) -> str | None: pass class TestCheck: class TestName: def test_returns_the_provided_name(self) -> None: name = "test" assert DummyCheck(name).name == name def test_has_a_sensible_default(self) -> None: assert DummyCheck().name is not None def test_has_a_string_representation(self) -> None: assert isinstance(str(DummyCheck("test")), str) autosuspend-7.2.0/tests/test_checks_activity.py000066400000000000000000000012261475670032600220340ustar00rootroot00000000000000import pytest from autosuspend.checks import Activity @pytest.mark.parametrize( "name", [ "ActiveCalendarEvent", "ActiveConnection", "ExternalCommand", "JsonPath", "Kodi", "KodiIdleTime", "LastLogActivity", "Load", "LogindSessionsIdle", "Mpd", "NetworkBandwidth", "Ping", "Processes", "Smb", "Users", "XIdleTime", "XPath", ], ) def test_legacy_check_names_are_available(name: str) -> None: res = __import__("autosuspend.checks.activity", fromlist=[name]) assert issubclass(getattr(res, name), Activity) autosuspend-7.2.0/tests/test_checks_command.py000066400000000000000000000105061475670032600216170ustar00rootroot00000000000000from datetime import datetime, timezone import subprocess import pytest from pytest_mock import MockerFixture from autosuspend.checks import ( Activity, Check, ConfigurationError, SevereCheckError, TemporaryCheckError, ) from autosuspend.checks.command import CommandActivity, CommandMixin, CommandWakeup from . import CheckTest from .utils import config_section class _CommandMixinSub(CommandMixin, Activity): def __init__(self, name: str, command: str) -> None: Activity.__init__(self, name) CommandMixin.__init__(self, command) def check(self) -> str | None: pass class TestCommandMixin: class TestCreate: def test_it_works(self) -> None: section = config_section({"command": "narf bla"}) check: _CommandMixinSub = _CommandMixinSub.create( "name", section, ) assert check._command == "narf bla" def test_throws_if_no_command_is_configured(self) -> None: with pytest.raises(ConfigurationError): _CommandMixinSub.create("name", config_section()) class TestCommandActivity(CheckTest): def create_instance(self, name: str) -> Check: return CommandActivity(name, "asdfasdf") def test_reports_activity_if_the_command_succeeds( self, mocker: MockerFixture ) -> None: mock = mocker.patch("subprocess.check_call") assert ( CommandActivity.create( "name", config_section({"command": "foo bar"}) ).check() is not None ) mock.assert_called_once_with("foo bar", shell=True) def test_reports_no_activity_if_the_command_fails( self, mocker: MockerFixture ) -> None: mock = mocker.patch("subprocess.check_call") mock.side_effect = subprocess.CalledProcessError(2, "foo bar") assert ( CommandActivity.create( "name", config_section({"command": "foo bar"}) ).check() is None ) mock.assert_called_once_with("foo bar", shell=True) def test_reports_missing_commands(self) -> None: with pytest.raises(SevereCheckError): CommandActivity.create( "name", config_section({"command": "thisreallydoesnotexist"}) ).check() class TestCommandWakeup(CheckTest): def create_instance(self, name: str) -> Check: return CommandWakeup(name, "asdf") def test_reports_the_wakup_time_received_from_the_command(self) -> None: check = CommandWakeup("test", "echo 1234") assert check.check(datetime.now(timezone.utc)) == datetime.fromtimestamp( 1234, timezone.utc ) def test_reports_no_wakeup_without_command_output(self) -> None: check = CommandWakeup("test", "echo") assert check.check(datetime.now(timezone.utc)) is None def test_raises_an_error_if_the_command_output_cannot_be_parsed(self) -> None: check = CommandWakeup("test", "echo asdfasdf") with pytest.raises(TemporaryCheckError): check.check(datetime.now(timezone.utc)) def test_uses_only_the_first_output_line(self, mocker: MockerFixture) -> None: mock = mocker.patch("subprocess.check_output") mock.return_value = "1234\nignore\n" check = CommandWakeup("test", "echo bla") assert check.check(datetime.now(timezone.utc)) == datetime.fromtimestamp( 1234, timezone.utc ) def test_uses_only_the_first_line_even_if_empty( self, mocker: MockerFixture ) -> None: mock = mocker.patch("subprocess.check_output") mock.return_value = " \nignore\n" check = CommandWakeup("test", "echo bla") assert check.check(datetime.now(timezone.utc)) is None def test_raises_if_the_called_command_fails(self, mocker: MockerFixture) -> None: mock = mocker.patch("subprocess.check_output") mock.side_effect = subprocess.CalledProcessError(2, "foo bar") check = CommandWakeup("test", "echo bla") with pytest.raises(TemporaryCheckError): check.check(datetime.now(timezone.utc)) def test_reports_missing_executables(self) -> None: check = CommandWakeup("test", "reallydoesntexist bla") with pytest.raises(SevereCheckError): check.check(datetime.now(timezone.utc)) autosuspend-7.2.0/tests/test_checks_ical.py000066400000000000000000000523431475670032600211160ustar00rootroot00000000000000from collections.abc import Callable from datetime import timedelta from pathlib import Path from dateutil import parser from dateutil.tz import tzlocal from freezegun import freeze_time from autosuspend.checks import Check from autosuspend.checks.ical import ( ActiveCalendarEvent, Calendar, CalendarEvent, list_calendar_events, ) from . import CheckTest from .utils import config_section class TestCalendarEvent: def test_str(self) -> None: start = parser.parse("2018-06-11 02:00:00 UTC") end = start + timedelta(hours=1) event = CalendarEvent("summary", start, end) assert "summary" in str(event) class TestListCalendarEvents: def test_simple_recurring(self, datadir: Path) -> None: """Tests for basic recurrence. Events are collected with the same DST setting as their original creation. """ with (datadir / "simple-recurring.ics").open("rb") as f: start = parser.parse("2018-06-18 04:00:00 UTC") end = start + timedelta(weeks=2) events = list_calendar_events(f, start, end) expected_start_times = [ parser.parse("2018-06-18 07:00:00 UTC"), parser.parse("2018-06-19 07:00:00 UTC"), parser.parse("2018-06-20 07:00:00 UTC"), parser.parse("2018-06-21 07:00:00 UTC"), parser.parse("2018-06-22 07:00:00 UTC"), parser.parse("2018-06-25 07:00:00 UTC"), parser.parse("2018-06-26 07:00:00 UTC"), parser.parse("2018-06-27 07:00:00 UTC"), parser.parse("2018-06-28 07:00:00 UTC"), parser.parse("2018-06-29 07:00:00 UTC"), ] expected_end_times = [ parser.parse("2018-06-18 16:00:00 UTC"), parser.parse("2018-06-19 16:00:00 UTC"), parser.parse("2018-06-20 16:00:00 UTC"), parser.parse("2018-06-21 16:00:00 UTC"), parser.parse("2018-06-22 16:00:00 UTC"), parser.parse("2018-06-25 16:00:00 UTC"), parser.parse("2018-06-26 16:00:00 UTC"), parser.parse("2018-06-27 16:00:00 UTC"), parser.parse("2018-06-28 16:00:00 UTC"), parser.parse("2018-06-29 16:00:00 UTC"), ] assert expected_start_times == [e.start for e in events] assert expected_end_times == [e.end for e in events] def test_recurrence_different_dst(self, datadir: Path) -> None: with (datadir / "simple-recurring.ics").open("rb") as f: start = parser.parse("2018-11-19 04:00:00 UTC") end = start + timedelta(weeks=2) events = list_calendar_events(f, start, end) expected_start_times = [ parser.parse("2018-11-19 08:00:00 UTC"), parser.parse("2018-11-20 08:00:00 UTC"), parser.parse("2018-11-21 08:00:00 UTC"), parser.parse("2018-11-22 08:00:00 UTC"), parser.parse("2018-11-23 08:00:00 UTC"), parser.parse("2018-11-26 08:00:00 UTC"), parser.parse("2018-11-27 08:00:00 UTC"), parser.parse("2018-11-28 08:00:00 UTC"), parser.parse("2018-11-29 08:00:00 UTC"), parser.parse("2018-11-30 08:00:00 UTC"), ] assert expected_start_times == [e.start for e in events] def test_all_day_events(self, datadir: Path) -> None: with (datadir / "all-day-events.ics").open("rb") as f: start = parser.parse("2018-06-11 02:00:00 UTC") end = start + timedelta(weeks=1) events = list_calendar_events(f, start, end) assert len(events) == 3 expected_summaries = ["start", "between", "end"] assert [e.summary for e in events] == expected_summaries def test_normal_events(self, datadir: Path) -> None: with (datadir / "normal-events-corner-cases.ics").open("rb") as f: start = parser.parse("2018-06-04 00:00:00 +0200") end = start + timedelta(weeks=1) events = list_calendar_events(f, start, end) expected = [ ( "overlapping", parser.parse("2018-06-02 20:00:00 +0200"), parser.parse("2018-06-12 23:00:00 +0200"), ), ( "before include", parser.parse("2018-06-03 21:00:00 +0200"), parser.parse("2018-06-04 02:00:00 +0200"), ), ( "direct start", parser.parse("2018-06-04 00:00:00 +0200"), parser.parse("2018-06-04 03:00:00 +0200"), ), ( "in between", parser.parse("2018-06-07 04:00:00 +0200"), parser.parse("2018-06-07 09:00:00 +0200"), ), ( "end overlap", parser.parse("2018-06-10 21:00:00 +0200"), parser.parse("2018-06-11 02:00:00 +0200"), ), ( "direct end", parser.parse("2018-06-10 22:00:00 +0200"), parser.parse("2018-06-11 00:00:00 +0200"), ), ] assert [(e.summary, e.start, e.end) for e in events] == expected def test_floating_time(self, datadir: Path) -> None: with (datadir / "floating.ics").open("rb") as f: start = parser.parse("2018-06-09 00:00:00 +0200") end = start + timedelta(weeks=1) events = list_calendar_events(f, start, end) tzinfo = {"LOCAL": tzlocal()} expected = [ ( "floating", parser.parse("2018-06-10 15:00:00 LOCAL", tzinfos=tzinfo), parser.parse("2018-06-10 17:00:00 LOCAL", tzinfos=tzinfo), ), ( "floating recurring", parser.parse("2018-06-12 18:00:00 LOCAL", tzinfos=tzinfo), parser.parse("2018-06-12 20:00:00 LOCAL", tzinfos=tzinfo), ), ( "floating recurring", parser.parse("2018-06-13 18:00:00 LOCAL", tzinfos=tzinfo), parser.parse("2018-06-13 20:00:00 LOCAL", tzinfos=tzinfo), ), ( "floating recurring", parser.parse("2018-06-14 18:00:00 LOCAL", tzinfos=tzinfo), parser.parse("2018-06-14 20:00:00 LOCAL", tzinfos=tzinfo), ), ( "floating recurring", parser.parse("2018-06-15 18:00:00 LOCAL", tzinfos=tzinfo), parser.parse("2018-06-15 20:00:00 LOCAL", tzinfos=tzinfo), ), ] assert [(e.summary, e.start, e.end) for e in events] == expected def test_floating_time_other_dst(self, datadir: Path) -> None: with (datadir / "floating.ics").open("rb") as f: start = parser.parse("2018-12-09 00:00:00 +0200") end = start + timedelta(weeks=1) events = list_calendar_events(f, start, end) tzinfo = {"LOCAL": tzlocal()} expected = [ ( "floating recurring", parser.parse("2018-12-09 18:00:00 LOCAL", tzinfos=tzinfo), parser.parse("2018-12-09 20:00:00 LOCAL", tzinfos=tzinfo), ), ( "floating recurring", parser.parse("2018-12-10 18:00:00 LOCAL", tzinfos=tzinfo), parser.parse("2018-12-10 20:00:00 LOCAL", tzinfos=tzinfo), ), ( "floating recurring", parser.parse("2018-12-11 18:00:00 LOCAL", tzinfos=tzinfo), parser.parse("2018-12-11 20:00:00 LOCAL", tzinfos=tzinfo), ), ( "floating recurring", parser.parse("2018-12-12 18:00:00 LOCAL", tzinfos=tzinfo), parser.parse("2018-12-12 20:00:00 LOCAL", tzinfos=tzinfo), ), ( "floating recurring", parser.parse("2018-12-13 18:00:00 LOCAL", tzinfos=tzinfo), parser.parse("2018-12-13 20:00:00 LOCAL", tzinfos=tzinfo), ), ( "floating recurring", parser.parse("2018-12-14 18:00:00 LOCAL", tzinfos=tzinfo), parser.parse("2018-12-14 20:00:00 LOCAL", tzinfos=tzinfo), ), ( "floating recurring", parser.parse("2018-12-15 18:00:00 LOCAL", tzinfos=tzinfo), parser.parse("2018-12-15 20:00:00 LOCAL", tzinfos=tzinfo), ), ] assert [(e.summary, e.start, e.end) for e in events] == expected def test_exclusions(self, datadir: Path) -> None: with (datadir / "exclusions.ics").open("rb") as f: start = parser.parse("2018-06-09 04:00:00 UTC") end = start + timedelta(weeks=2) events = list_calendar_events(f, start, end) expected_start_times = [ parser.parse("2018-06-11 12:00:00 UTC"), parser.parse("2018-06-12 12:00:00 UTC"), parser.parse("2018-06-13 12:00:00 UTC"), parser.parse("2018-06-15 12:00:00 UTC"), parser.parse("2018-06-16 12:00:00 UTC"), parser.parse("2018-06-17 12:00:00 UTC"), ] assert expected_start_times == [e.start for e in events] def test_reucrring_single_changes(self, datadir: Path) -> None: with (datadir / "single-change.ics").open("rb") as f: start = parser.parse("2018-06-11 00:00:00 UTC") end = start + timedelta(weeks=1) events = list_calendar_events(f, start, end) expected_start_times = [ parser.parse("2018-06-11 11:00:00 UTC"), parser.parse("2018-06-12 11:00:00 UTC"), parser.parse("2018-06-13 14:00:00 UTC"), parser.parse("2018-06-14 11:00:00 UTC"), parser.parse("2018-06-15 09:00:00 UTC"), parser.parse("2018-06-16 11:00:00 UTC"), parser.parse("2018-06-17 11:00:00 UTC"), ] assert expected_start_times == [e.start for e in events] def test_reucrring_change_dst(self, datadir: Path) -> None: with (datadir / "recurring-change-dst.ics").open("rb") as f: start = parser.parse("2018-12-10 00:00:00 UTC") end = start + timedelta(weeks=1) events = list_calendar_events(f, start, end) expected_start_times = [ parser.parse("2018-12-10 13:00:00 UTC"), parser.parse("2018-12-11 13:00:00 UTC"), parser.parse("2018-12-12 10:00:00 UTC"), parser.parse("2018-12-13 13:00:00 UTC"), parser.parse("2018-12-15 13:00:00 UTC"), parser.parse("2018-12-16 13:00:00 UTC"), ] assert expected_start_times == [e.start for e in events] def test_recurring_start_and_end_inclusive(self, datadir: Path) -> None: with (datadir / "issue-41.ics").open("rb") as f: start = parser.parse("2018-06-26 15:13:51 UTC") end = start + timedelta(weeks=1) events = list_calendar_events(f, start, end) expected_start_times = [ parser.parse("2018-06-26 15:00:00 UTC"), parser.parse("2018-06-27 15:00:00 UTC"), parser.parse("2018-06-28 15:00:00 UTC"), parser.parse("2018-06-29 15:00:00 UTC"), parser.parse("2018-06-30 15:00:00 UTC"), parser.parse("2018-07-01 15:00:00 UTC"), parser.parse("2018-07-02 15:00:00 UTC"), parser.parse("2018-07-03 15:00:00 UTC"), ] assert expected_start_times == [e.start for e in events] def test_single_start_end_inclusive(self, datadir: Path) -> None: with (datadir / "old-event.ics").open("rb") as f: start = parser.parse("2004-06-05 11:15:00 UTC") end = start + timedelta(hours=1) events = list_calendar_events(f, start, end) expected_start_times = [ parser.parse("2004-06-05 11:00:00 UTC"), ] assert expected_start_times == [e.start for e in events] def test_single_all_day_start_end_inclusive(self, datadir: Path) -> None: with (datadir / "all-day-starts.ics").open("rb") as f: start = parser.parse("2018-06-25 10:00:00 UTC") end = start + timedelta(hours=2) events = list_calendar_events(f, start, end) expected_start_times = [ parser.parse("2018-06-25 02:00:00 UTC").date(), ] assert expected_start_times == [e.start for e in events] expected_end_times = [ parser.parse("2018-06-26 02:00:00 UTC").date(), ] assert expected_end_times == [e.end for e in events] def test_longer_single_all_day_start_end_inclusive(self, datadir: Path) -> None: with (datadir / "all-day-starts.ics").open("rb") as f: start = parser.parse("2018-06-29 10:00:00 UTC") end = start + timedelta(hours=2) events = list_calendar_events(f, start, end) expected_start_times = [ parser.parse("2018-06-28 02:00:00 UTC").date(), ] assert expected_start_times == [e.start for e in events] def test_recurring_all_day_start_end_inclusive(self, datadir: Path) -> None: with (datadir / "all-day-recurring.ics").open("rb") as f: start = parser.parse("2018-06-29 10:00:00 UTC") end = start + timedelta(hours=2) events = list_calendar_events(f, start, end) expected_start_times = [ parser.parse("2018-06-29 02:00:00 UTC").date(), ] assert expected_start_times == [e.start for e in events] expected_end_times = [ parser.parse("2018-06-30 02:00:00 UTC").date(), ] assert expected_end_times == [e.end for e in events] def test_recurring_all_day_start_in_between(self, datadir: Path) -> None: with (datadir / "all-day-recurring.ics").open("rb") as f: start = parser.parse("2018-06-29 00:00:00 UTC") end = start + timedelta(days=1) events = list_calendar_events(f, start, end) expected_start_times = [ parser.parse("2018-06-29 00:00:00 UTC").date(), parser.parse("2018-06-30 00:00:00 UTC").date(), ] assert expected_start_times == [e.start for e in events] def test_recurring_all_day_exclusions(self, datadir: Path) -> None: with (datadir / "all-day-recurring-exclusions.ics").open("rb") as f: start = parser.parse("2018-06-27 00:00:00 UTC") end = start + timedelta(days=4) events = list_calendar_events(f, start, end) expected_start_times = [ parser.parse("2018-06-27 00:00:00 UTC").date(), parser.parse("2018-06-28 00:00:00 UTC").date(), parser.parse("2018-06-29 00:00:00 UTC").date(), parser.parse("2018-07-01 00:00:00 UTC").date(), ] assert expected_start_times == [e.start for e in events] def test_recurring_all_day_exclusions_end(self, datadir: Path) -> None: with (datadir / "all-day-recurring-exclusions.ics").open("rb") as f: start = parser.parse("2018-06-26 00:00:00 UTC") end = start + timedelta(days=4) events = list_calendar_events(f, start, end) expected_start_times = [ parser.parse("2018-06-26 00:00:00 UTC").date(), parser.parse("2018-06-27 00:00:00 UTC").date(), parser.parse("2018-06-28 00:00:00 UTC").date(), parser.parse("2018-06-29 00:00:00 UTC").date(), ] assert expected_start_times == [e.start for e in events] class TestActiveCalendarEvent(CheckTest): def create_instance(self, name: str) -> Check: return ActiveCalendarEvent(name, url="asdfasdf", timeout=5) def test_smoke(self, datadir: Path, serve_file: Callable[[Path], str]) -> None: result = ActiveCalendarEvent( "test", url=serve_file(datadir / "long-event.ics"), timeout=3, ).check() assert result is not None assert "long-event" in result def test_exact_range( self, datadir: Path, serve_file: Callable[[Path], str] ) -> None: with freeze_time("2016-06-05 13:00:00", tz_offset=-2): result = ActiveCalendarEvent( "test", url=serve_file(datadir / "long-event.ics"), timeout=3, ).check() assert result is not None assert "long-event" in result def test_before_exact_range( self, datadir: Path, serve_file: Callable[[Path], str] ) -> None: with freeze_time("2016-06-05 12:58:00", tz_offset=-2): result = ActiveCalendarEvent( "test", url=serve_file(datadir / "long-event.ics"), timeout=3, ).check() assert result is None def test_no_event(self, datadir: Path, serve_file: Callable[[Path], str]) -> None: assert ( ActiveCalendarEvent( "test", url=serve_file(datadir / "old-event.ics"), timeout=3, ).check() is None ) def test_create(self) -> None: check: ActiveCalendarEvent = ActiveCalendarEvent.create( "name", config_section( { "url": "foobar", "username": "user", "password": "pass", "timeout": "3", } ), ) assert check._url == "foobar" assert check._username == "user" assert check._password == "pass" assert check._timeout == 3 class TestCalendar(CheckTest): def create_instance(self, name: str) -> Calendar: return Calendar(name, url="file:///asdf", timeout=3) def test_create(self) -> None: section = config_section( { "url": "url", "username": "user", "password": "pass", "timeout": "42", } ) check: Calendar = Calendar.create( "name", section, ) assert check._url == "url" assert check._username == "user" assert check._password == "pass" assert check._timeout == 42 def test_empty(self, datadir: Path, serve_file: Callable[[Path], str]) -> None: timestamp = parser.parse("20050605T130000Z") assert ( Calendar( "test", url=serve_file(datadir / "old-event.ics"), timeout=3, ).check(timestamp) is None ) def test_smoke(self, datadir: Path, serve_file: Callable[[Path], str]) -> None: timestamp = parser.parse("20040605T090000Z") desired_start = parser.parse("20040605T110000Z") assert ( Calendar( "test", url=serve_file(datadir / "old-event.ics"), timeout=3, ).check(timestamp) == desired_start ) def test_select_earliest( self, datadir: Path, serve_file: Callable[[Path], str] ) -> None: timestamp = parser.parse("20040401T090000Z") desired_start = parser.parse("20040405T110000Z") assert ( Calendar( "test", url=serve_file(datadir / "multiple.ics"), timeout=3, ).check(timestamp) == desired_start ) def test_ignore_running( self, datadir: Path, serve_file: Callable[[Path], str] ) -> None: url = serve_file(datadir / "old-event.ics") timestamp = parser.parse("20040605T110000Z") # events are taken if start hits exactly the current time assert Calendar("test", url=url, timeout=3).check(timestamp) is not None timestamp = timestamp + timedelta(seconds=1) assert Calendar("test", url=url, timeout=3).check(timestamp) is None def test_limited_horizon( self, datadir: Path, serve_file: Callable[[Path], str] ) -> None: timestamp = parser.parse("20040101T000000Z") assert ( Calendar( "test", url=serve_file(datadir / "after-horizon.ics"), timeout=3, ).check(timestamp) is None ) assert ( Calendar( "test", url=serve_file(datadir / "before-horizon.ics"), timeout=3, ).check(timestamp) is not None ) autosuspend-7.2.0/tests/test_checks_ical/000077500000000000000000000000001475670032600205355ustar00rootroot00000000000000autosuspend-7.2.0/tests/test_checks_ical/after-horizon.ics000066400000000000000000000012731475670032600240270ustar00rootroot00000000000000BEGIN:VCALENDAR PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN VERSION:2.0 BEGIN:VTIMEZONE TZID:Europe/Berlin BEGIN:DAYLIGHT TZOFFSETFROM:+0100 TZOFFSETTO:+0200 TZNAME:CEST DTSTART:19700329T020000 RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3 END:DAYLIGHT BEGIN:STANDARD TZOFFSETFROM:+0200 TZOFFSETTO:+0100 TZNAME:CET DTSTART:19701025T030000 RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 END:STANDARD END:VTIMEZONE BEGIN:VEVENT CREATED:20180603T194125Z LAST-MODIFIED:20180603T194144Z DTSTAMP:20180603T194144Z UID:6ff13ee1-e548-41b1-8e08-d7725423743a SUMMARY:long-event DTSTART;TZID=Europe/Berlin:20040618T000000 DTEND;TZID=Europe/Berlin:20040618T150000 TRANSP:OPAQUE SEQUENCE:1 END:VEVENT END:VCALENDAR autosuspend-7.2.0/tests/test_checks_ical/all-day-events.ics000066400000000000000000000024261475670032600240660ustar00rootroot00000000000000BEGIN:VCALENDAR PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN VERSION:2.0 BEGIN:VEVENT CREATED:20180601T194043Z LAST-MODIFIED:20180601T194050Z DTSTAMP:20180601T194050Z UID:0f82aa78-1478-4093-85c5-16d754f362f6 SUMMARY:between DTSTART;VALUE=DATE:20180613 DTEND;VALUE=DATE:20180615 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20180601T194002Z LAST-MODIFIED:20180601T194303Z DTSTAMP:20180601T194303Z UID:630f3b71-865e-4125-977d-a2fd0009ce7d SUMMARY:start DTSTART;VALUE=DATE:20180609 DTEND;VALUE=DATE:20180612 TRANSP:TRANSPARENT X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20180601T194054Z LAST-MODIFIED:20180601T194307Z DTSTAMP:20180601T194307Z UID:dc1c0bfc-633c-4d34-8de4-f6e9bcdb5fc6 SUMMARY:end DTSTART;VALUE=DATE:20180617 DTEND;VALUE=DATE:20180620 TRANSP:TRANSPARENT X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20180601T194313Z LAST-MODIFIED:20180601T194317Z DTSTAMP:20180601T194317Z UID:5095407e-5e63-4609-93a0-5dcd45ed5bf5 SUMMARY:after DTSTART;VALUE=DATE:20180619 DTEND;VALUE=DATE:20180620 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20180601T195811Z LAST-MODIFIED:20180601T195814Z DTSTAMP:20180601T195814Z UID:550119de-eef7-4820-9843-d260515807d2 SUMMARY:before DTSTART;VALUE=DATE:20180605 DTEND;VALUE=DATE:20180606 TRANSP:TRANSPARENT END:VEVENT END:VCALENDAR autosuspend-7.2.0/tests/test_checks_ical/all-day-recurring-exclusions.ics000066400000000000000000000006051475670032600267510ustar00rootroot00000000000000BEGIN:VCALENDAR PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN VERSION:2.0 BEGIN:VEVENT CREATED:20180627T111330Z LAST-MODIFIED:20180627T111340Z DTSTAMP:20180627T111340Z UID:ccf1c6b9-44c4-4fdb-8a98-0165e6f2e369 SUMMARY:single all day DTSTART;VALUE=DATE:20180625 DTEND;VALUE=DATE:20180626 EXDATE:20180630 RRULE:FREQ=DAILY TRANSP:TRANSPARENT END:VEVENT END:VCALENDAR autosuspend-7.2.0/tests/test_checks_ical/all-day-recurring.ics000066400000000000000000000005641475670032600245630ustar00rootroot00000000000000BEGIN:VCALENDAR PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN VERSION:2.0 BEGIN:VEVENT CREATED:20180627T111330Z LAST-MODIFIED:20180627T111340Z DTSTAMP:20180627T111340Z UID:ccf1c6b9-44c4-4fdb-8a98-0165e6f2e369 SUMMARY:single all day DTSTART;VALUE=DATE:20180625 DTEND;VALUE=DATE:20180626 RRULE:FREQ=DAILY TRANSP:TRANSPARENT END:VEVENT END:VCALENDAR autosuspend-7.2.0/tests/test_checks_ical/all-day-starts.ics000066400000000000000000000013161475670032600240770ustar00rootroot00000000000000BEGIN:VCALENDAR PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN VERSION:2.0 BEGIN:VEVENT CREATED:20180627T111330Z LAST-MODIFIED:20180627T111340Z DTSTAMP:20180627T111340Z UID:ccf1c6b9-44c4-4fdb-8a98-0165e6f2e369 SUMMARY:single all day DTSTART;VALUE=DATE:20180625 DTEND;VALUE=DATE:20180626 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20180627T111347Z LAST-MODIFIED:20180627T111357Z DTSTAMP:20180627T111357Z UID:a2dab4dd-1ede-4733-af8e-90cff0e26f79 SUMMARY:two all days DTSTART;VALUE=DATE:20180628 DTEND;VALUE=DATE:20180630 TRANSP:TRANSPARENT BEGIN:VALARM ACTION:DISPLAY TRIGGER;VALUE=DURATION:-PT15M DESCRIPTION:Default Mozilla Description END:VALARM END:VEVENT END:VCALENDAR autosuspend-7.2.0/tests/test_checks_ical/before-horizon.ics000066400000000000000000000012731475670032600241700ustar00rootroot00000000000000BEGIN:VCALENDAR PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN VERSION:2.0 BEGIN:VTIMEZONE TZID:Europe/Berlin BEGIN:DAYLIGHT TZOFFSETFROM:+0100 TZOFFSETTO:+0200 TZNAME:CEST DTSTART:19700329T020000 RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3 END:DAYLIGHT BEGIN:STANDARD TZOFFSETFROM:+0200 TZOFFSETTO:+0100 TZNAME:CET DTSTART:19701025T030000 RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 END:STANDARD END:VTIMEZONE BEGIN:VEVENT CREATED:20180603T194125Z LAST-MODIFIED:20180603T194144Z DTSTAMP:20180603T194144Z UID:6ff13ee1-e548-41b1-8e08-d7725423743a SUMMARY:long-event DTSTART;TZID=Europe/Berlin:20040617T000000 DTEND;TZID=Europe/Berlin:20040617T150000 TRANSP:OPAQUE SEQUENCE:1 END:VEVENT END:VCALENDAR autosuspend-7.2.0/tests/test_checks_ical/exclusions.ics000066400000000000000000000014151475670032600234320ustar00rootroot00000000000000BEGIN:VCALENDAR PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN VERSION:2.0 BEGIN:VTIMEZONE TZID:Europe/Berlin BEGIN:DAYLIGHT TZOFFSETFROM:+0100 TZOFFSETTO:+0200 TZNAME:CEST DTSTART:19700329T020000 RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3 END:DAYLIGHT BEGIN:STANDARD TZOFFSETFROM:+0200 TZOFFSETTO:+0100 TZNAME:CET DTSTART:19701025T030000 RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 END:STANDARD END:VTIMEZONE BEGIN:VEVENT CREATED:20180602T160606Z LAST-MODIFIED:20180602T160632Z DTSTAMP:20180602T160632Z UID:a40c5b76-e3f5-4259-92f5-26692f99f131 SUMMARY:recurring RRULE:FREQ=DAILY;UNTIL=20180617T120000Z EXDATE:20180614T120000Z DTSTART;TZID=Europe/Berlin:20180611T140000 DTEND;TZID=Europe/Berlin:20180611T160000 TRANSP:OPAQUE X-MOZ-GENERATION:4 SEQUENCE:2 END:VEVENT END:VCALENDAR autosuspend-7.2.0/tests/test_checks_ical/floating.ics000066400000000000000000000011671475670032600230450ustar00rootroot00000000000000BEGIN:VCALENDAR PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN VERSION:2.0 BEGIN:VEVENT CREATED:20180602T151629Z LAST-MODIFIED:20180602T152512Z DTSTAMP:20180602T152512Z UID:f0028400-24e2-4f10-81a0-032372781443 SUMMARY:floating DTSTART:20180610T150000 DTEND:20180610T170000 TRANSP:OPAQUE SEQUENCE:5 X-MOZ-GENERATION:3 END:VEVENT BEGIN:VEVENT CREATED:20180602T151701Z LAST-MODIFIED:20180602T152732Z DTSTAMP:20180602T152732Z UID:0ef23894-702e-40ac-ab09-94fa8c9c51fd SUMMARY:floating recurring RRULE:FREQ=DAILY DTSTART:20180612T180000 DTEND:20180612T200000 TRANSP:OPAQUE X-MOZ-GENERATION:5 SEQUENCE:3 END:VEVENT END:VCALENDAR autosuspend-7.2.0/tests/test_checks_ical/issue-41.ics000066400000000000000000000014011475670032600226030ustar00rootroot00000000000000BEGIN:VCALENDAR PRODID:-//Inverse inc./SOGo 4.0.0//EN VERSION:2.0 BEGIN:VTIMEZONE TZID:Europe/Berlin X-LIC-LOCATION:Europe/Berlin BEGIN:DAYLIGHT TZOFFSETFROM:+0100 TZOFFSETTO:+0200 TZNAME:CEST DTSTART:19700329T020000 RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU END:DAYLIGHT BEGIN:STANDARD TZOFFSETFROM:+0200 TZOFFSETTO:+0100 TZNAME:CET DTSTART:19701025T030000 RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU END:STANDARD END:VTIMEZONE BEGIN:VEVENT UID:2C-5B315480-3-4D014C80 SUMMARY:StayAlive LOCATION:Home CLASS:PUBLIC X-SOGO-SEND-APPOINTMENT-NOTIFICATIONS:NO RRULE:FREQ=DAILY TRANSP:OPAQUE DTSTART;TZID=Europe/Berlin:20180626T170000 DTEND;TZID=Europe/Berlin:20180626T210000 CREATED:20180625T204700Z DTSTAMP:20180625T204700Z LAST-MODIFIED:20180625T204700Z END:VEVENT END:VCALENDAR autosuspend-7.2.0/tests/test_checks_ical/long-event.ics000066400000000000000000000012731475670032600233160ustar00rootroot00000000000000BEGIN:VCALENDAR PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN VERSION:2.0 BEGIN:VTIMEZONE TZID:Europe/Berlin BEGIN:DAYLIGHT TZOFFSETFROM:+0100 TZOFFSETTO:+0200 TZNAME:CEST DTSTART:19700329T020000 RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3 END:DAYLIGHT BEGIN:STANDARD TZOFFSETFROM:+0200 TZOFFSETTO:+0100 TZNAME:CET DTSTART:19701025T030000 RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 END:STANDARD END:VTIMEZONE BEGIN:VEVENT CREATED:20180603T194125Z LAST-MODIFIED:20180603T194144Z DTSTAMP:20180603T194144Z UID:6ff13ee1-e548-41b1-8e08-d7725423743a SUMMARY:long-event DTSTART;TZID=Europe/Berlin:20160605T130000 DTEND;TZID=Europe/Berlin:20260605T150000 TRANSP:OPAQUE SEQUENCE:1 END:VEVENT END:VCALENDAR autosuspend-7.2.0/tests/test_checks_ical/multiple.ics000066400000000000000000000017161475670032600230750ustar00rootroot00000000000000BEGIN:VCALENDAR PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN VERSION:2.0 BEGIN:VTIMEZONE TZID:Europe/Berlin BEGIN:DAYLIGHT TZOFFSETFROM:+0100 TZOFFSETTO:+0200 TZNAME:CEST DTSTART:19700329T020000 RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3 END:DAYLIGHT BEGIN:STANDARD TZOFFSETFROM:+0200 TZOFFSETTO:+0100 TZNAME:CET DTSTART:19701025T030000 RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 END:STANDARD END:VTIMEZONE BEGIN:VEVENT CREATED:20180603T194125Z LAST-MODIFIED:20180603T194144Z DTSTAMP:20180603T194144Z UID:6ff13ee1-e548-41b1-8e08-d7725423743a SUMMARY:long-event DTSTART;TZID=Europe/Berlin:20040605T130000 DTEND;TZID=Europe/Berlin:20040605T150000 TRANSP:OPAQUE SEQUENCE:1 END:VEVENT BEGIN:VEVENT CREATED:20180403T194125Z LAST-MODIFIED:20180403T194144Z DTSTAMP:20180403T194144Z UID:6ff13ee1-e548-41b1-8e08-d7725423743b SUMMARY:early-event DTSTART;TZID=Europe/Berlin:20040405T130000 DTEND;TZID=Europe/Berlin:20040405T150000 TRANSP:OPAQUE SEQUENCE:1 END:VEVENT END:VCALENDAR autosuspend-7.2.0/tests/test_checks_ical/normal-events-corner-cases.ics000066400000000000000000000053151475670032600264150ustar00rootroot00000000000000BEGIN:VCALENDAR PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN VERSION:2.0 BEGIN:VTIMEZONE TZID:Europe/Berlin BEGIN:DAYLIGHT TZOFFSETFROM:+0100 TZOFFSETTO:+0200 TZNAME:CEST DTSTART:19700329T020000 RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3 END:DAYLIGHT BEGIN:STANDARD TZOFFSETFROM:+0200 TZOFFSETTO:+0100 TZNAME:CET DTSTART:19701025T030000 RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 END:STANDARD END:VTIMEZONE BEGIN:VEVENT CREATED:20180601T200433Z LAST-MODIFIED:20180601T200455Z DTSTAMP:20180601T200455Z UID:1c056498-9c83-4e0f-bb77-777c967c9a54 SUMMARY:before include DTSTART;TZID=Europe/Berlin:20180603T210000 DTEND;TZID=Europe/Berlin:20180604T020000 TRANSP:OPAQUE X-MOZ-GENERATION:2 SEQUENCE:1 END:VEVENT BEGIN:VEVENT CREATED:20180601T200328Z LAST-MODIFIED:20180601T200511Z DTSTAMP:20180601T200511Z UID:db4b1c02-6ac2-4def-bfb0-9a96b510387e SUMMARY:direct start DTSTART;TZID=Europe/Berlin:20180604T000000 DTEND;TZID=Europe/Berlin:20180604T030000 TRANSP:OPAQUE X-MOZ-GENERATION:2 SEQUENCE:1 END:VEVENT BEGIN:VEVENT CREATED:20180601T200518Z LAST-MODIFIED:20180601T200531Z DTSTAMP:20180601T200531Z UID:06622f56-d945-490b-9fd7-0fe5015f3188 SUMMARY:in between DTSTART;TZID=Europe/Berlin:20180607T040000 DTEND;TZID=Europe/Berlin:20180607T090000 TRANSP:OPAQUE X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20180601T200351Z LAST-MODIFIED:20180601T200555Z DTSTAMP:20180601T200555Z UID:48d1debe-e457-4bde-9bea-ab18be136d4a SUMMARY:before do not include DTSTART;TZID=Europe/Berlin:20180603T220000 DTEND;TZID=Europe/Berlin:20180604T000000 TRANSP:OPAQUE X-MOZ-GENERATION:4 SEQUENCE:2 END:VEVENT BEGIN:VEVENT CREATED:20180601T200531Z LAST-MODIFIED:20180601T200615Z DTSTAMP:20180601T200615Z UID:0a36a2e8-fac3-4337-8464-f52e5cf17bd5 SUMMARY:direct end DTSTART;TZID=Europe/Berlin:20180610T220000 DTEND;TZID=Europe/Berlin:20180611T000000 TRANSP:OPAQUE X-MOZ-GENERATION:4 SEQUENCE:1 END:VEVENT BEGIN:VEVENT CREATED:20180601T200619Z LAST-MODIFIED:20180601T200633Z DTSTAMP:20180601T200633Z UID:19bf0d84-3286-44d8-8376-67549a419001 SUMMARY:end overlap DTSTART;TZID=Europe/Berlin:20180610T210000 DTEND;TZID=Europe/Berlin:20180611T020000 TRANSP:OPAQUE X-MOZ-GENERATION:2 SEQUENCE:1 END:VEVENT BEGIN:VEVENT CREATED:20180601T200643Z LAST-MODIFIED:20180601T200651Z DTSTAMP:20180601T200651Z UID:ae376911-eab5-45fe-bb5b-14e9fd904b44 SUMMARY:end after DTSTART;TZID=Europe/Berlin:20180611T000000 DTEND;TZID=Europe/Berlin:20180611T030000 TRANSP:OPAQUE X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20180602T144323Z LAST-MODIFIED:20180602T144338Z DTSTAMP:20180602T144338Z UID:f52ee7b1-810f-4b08-bf28-80e8ae226ac3 SUMMARY:overlapping DTSTART;TZID=Europe/Berlin:20180602T200000 DTEND;TZID=Europe/Berlin:20180612T230000 TRANSP:OPAQUE X-MOZ-GENERATION:2 SEQUENCE:1 END:VEVENT END:VCALENDAR autosuspend-7.2.0/tests/test_checks_ical/old-event.ics000066400000000000000000000012731475670032600231350ustar00rootroot00000000000000BEGIN:VCALENDAR PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN VERSION:2.0 BEGIN:VTIMEZONE TZID:Europe/Berlin BEGIN:DAYLIGHT TZOFFSETFROM:+0100 TZOFFSETTO:+0200 TZNAME:CEST DTSTART:19700329T020000 RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3 END:DAYLIGHT BEGIN:STANDARD TZOFFSETFROM:+0200 TZOFFSETTO:+0100 TZNAME:CET DTSTART:19701025T030000 RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 END:STANDARD END:VTIMEZONE BEGIN:VEVENT CREATED:20180603T194125Z LAST-MODIFIED:20180603T194144Z DTSTAMP:20180603T194144Z UID:6ff13ee1-e548-41b1-8e08-d7725423743a SUMMARY:long-event DTSTART;TZID=Europe/Berlin:20040605T130000 DTEND;TZID=Europe/Berlin:20040605T150000 TRANSP:OPAQUE SEQUENCE:1 END:VEVENT END:VCALENDAR autosuspend-7.2.0/tests/test_checks_ical/recurring-change-dst.ics000066400000000000000000000026401475670032600252520ustar00rootroot00000000000000BEGIN:VCALENDAR PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN VERSION:2.0 BEGIN:VTIMEZONE TZID:Europe/Berlin BEGIN:DAYLIGHT TZOFFSETFROM:+0100 TZOFFSETTO:+0200 TZNAME:CEST DTSTART:19700329T020000 RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3 END:DAYLIGHT BEGIN:STANDARD TZOFFSETFROM:+0200 TZOFFSETTO:+0100 TZNAME:CET DTSTART:19701025T030000 RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 END:STANDARD END:VTIMEZONE BEGIN:VEVENT CREATED:20180603T200159Z LAST-MODIFIED:20180603T200414Z DTSTAMP:20180603T200414Z UID:d083699e-6f37-4a85-b20d-f03750aa6691 SUMMARY:recurring RRULE:FREQ=DAILY EXDATE:20181214T130000Z DTSTART;TZID=Europe/Berlin:20180606T140000 DTEND;TZID=Europe/Berlin:20180606T160000 TRANSP:OPAQUE X-MOZ-GENERATION:4 SEQUENCE:2 END:VEVENT BEGIN:VEVENT CREATED:20180603T200213Z LAST-MODIFIED:20180603T200243Z DTSTAMP:20180603T200243Z UID:d083699e-6f37-4a85-b20d-f03750aa6691 SUMMARY:recurring RECURRENCE-ID;TZID=Europe/Berlin:20180612T140000 DTSTART;TZID=Europe/Berlin:20180612T140000 DTEND;TZID=Europe/Berlin:20180612T160000 SEQUENCE:5 TRANSP:OPAQUE X-MOZ-GENERATION:4 END:VEVENT BEGIN:VEVENT CREATED:20180603T200401Z LAST-MODIFIED:20180603T200407Z DTSTAMP:20180603T200407Z UID:d083699e-6f37-4a85-b20d-f03750aa6691 SUMMARY:recurring RECURRENCE-ID;TZID=Europe/Berlin:20181212T140000 DTSTART;TZID=Europe/Berlin:20181212T110000 DTEND;TZID=Europe/Berlin:20181212T130000 SEQUENCE:2 TRANSP:OPAQUE X-MOZ-GENERATION:4 END:VEVENT END:VCALENDAR autosuspend-7.2.0/tests/test_checks_ical/simple-recurring.ics000066400000000000000000000013651475670032600245310ustar00rootroot00000000000000BEGIN:VCALENDAR PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN VERSION:2.0 BEGIN:VTIMEZONE TZID:Europe/Berlin BEGIN:DAYLIGHT TZOFFSETFROM:+0100 TZOFFSETTO:+0200 TZNAME:CEST DTSTART:19700329T020000 RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3 END:DAYLIGHT BEGIN:STANDARD TZOFFSETFROM:+0200 TZOFFSETTO:+0100 TZNAME:CET DTSTART:19701025T030000 RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 END:STANDARD END:VTIMEZONE BEGIN:VEVENT CREATED:20180601T182719Z LAST-MODIFIED:20180601T182803Z DTSTAMP:20180601T182803Z UID:74c93379-f763-439b-9d11-eca4d431bfc7 SUMMARY:Stay awake RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR DTSTART;TZID=Europe/Berlin:20180327T090000 DTEND;TZID=Europe/Berlin:20180327T180000 TRANSP:OPAQUE X-MOZ-GENERATION:2 SEQUENCE:1 END:VEVENT END:VCALENDAR autosuspend-7.2.0/tests/test_checks_ical/single-change.ics000066400000000000000000000026101475670032600237400ustar00rootroot00000000000000BEGIN:VCALENDAR PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN VERSION:2.0 BEGIN:VTIMEZONE TZID:Europe/Berlin BEGIN:DAYLIGHT TZOFFSETFROM:+0100 TZOFFSETTO:+0200 TZNAME:CEST DTSTART:19700329T020000 RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3 END:DAYLIGHT BEGIN:STANDARD TZOFFSETFROM:+0200 TZOFFSETTO:+0100 TZNAME:CET DTSTART:19701025T030000 RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 END:STANDARD END:VTIMEZONE BEGIN:VEVENT CREATED:20180603T194125Z LAST-MODIFIED:20180603T194144Z DTSTAMP:20180603T194144Z UID:6ff13ee1-e548-41b1-8e08-d7725423743a SUMMARY:recurring RRULE:FREQ=DAILY DTSTART;TZID=Europe/Berlin:20180605T130000 DTEND;TZID=Europe/Berlin:20180605T150000 TRANSP:OPAQUE X-MOZ-GENERATION:4 SEQUENCE:1 END:VEVENT BEGIN:VEVENT CREATED:20180603T194138Z LAST-MODIFIED:20180603T194140Z DTSTAMP:20180603T194140Z UID:6ff13ee1-e548-41b1-8e08-d7725423743a SUMMARY:recurring RECURRENCE-ID;TZID=Europe/Berlin:20180613T130000 DTSTART;TZID=Europe/Berlin:20180613T160000 DTEND;TZID=Europe/Berlin:20180613T180000 SEQUENCE:2 TRANSP:OPAQUE X-MOZ-GENERATION:4 END:VEVENT BEGIN:VEVENT CREATED:20180603T194141Z LAST-MODIFIED:20180603T194144Z DTSTAMP:20180603T194144Z UID:6ff13ee1-e548-41b1-8e08-d7725423743a SUMMARY:recurring RECURRENCE-ID;TZID=Europe/Berlin:20180615T130000 DTSTART;TZID=Europe/Berlin:20180615T110000 DTEND;TZID=Europe/Berlin:20180615T130000 SEQUENCE:2 TRANSP:OPAQUE X-MOZ-GENERATION:4 END:VEVENT END:VCALENDAR autosuspend-7.2.0/tests/test_checks_json.py000066400000000000000000000110211475670032600211430ustar00rootroot00000000000000from collections.abc import Callable from pathlib import Path from typing import Any from jsonpath_ng.ext import parse import pytest from pytest_mock import MockerFixture from autosuspend.checks import ConfigurationError, TemporaryCheckError from autosuspend.checks.json import JsonPath from . import CheckTest from .utils import config_section class TestJsonPath(CheckTest): def create_instance(self, name: str) -> JsonPath: return JsonPath( name=name, url="url", timeout=5, username="userx", password="pass", jsonpath=parse("b"), ) @staticmethod @pytest.fixture def json_get_mock(mocker: MockerFixture) -> Any: mock_reply = mocker.MagicMock() mock_reply.json.return_value = {"a": {"b": 42, "c": "ignore"}} return mocker.patch("requests.Session.get", return_value=mock_reply) def test_matching(self, json_get_mock: Any) -> None: url = "nourl" assert ( JsonPath("foo", jsonpath=parse("a.b"), url=url, timeout=5).check() is not None ) json_get_mock.assert_called_once_with( url, timeout=5, headers={"Accept": "application/json"} ) json_get_mock().json.assert_called_once() def test_filter_expressions_work(self, json_get_mock: Any) -> None: url = "nourl" assert ( JsonPath( "foo", jsonpath=parse("$[?(@.c=='ignore')]"), url=url, timeout=5 ).check() is not None ) json_get_mock.assert_called_once_with( url, timeout=5, headers={"Accept": "application/json"} ) json_get_mock().json.assert_called_once() def test_not_matching(self, json_get_mock: Any) -> None: url = "nourl" assert ( JsonPath("foo", jsonpath=parse("not.there"), url=url, timeout=5).check() is None ) json_get_mock.assert_called_once_with( url, timeout=5, headers={"Accept": "application/json"} ) json_get_mock().json.assert_called_once() def test_network_errors_are_passed( self, datadir: Path, serve_protected: Callable[[Path], tuple[str, str, str]] ) -> None: with pytest.raises(TemporaryCheckError): JsonPath( name="name", url=serve_protected(datadir / "data.txt")[0], timeout=5, username="wrong", password="wrong", jsonpath=parse("b"), ).check() def test_not_json(self, datadir: Path, serve_file: Callable[[Path], str]) -> None: with pytest.raises(TemporaryCheckError): JsonPath( name="name", url=serve_file(datadir / "invalid.json"), timeout=5, jsonpath=parse("b"), ).check() class TestCreate: def test_it_works(self) -> None: check: JsonPath = JsonPath.create( "name", config_section( { "url": "url", "jsonpath": "a.b", "username": "user", "password": "pass", "timeout": "42", } ), ) assert check._jsonpath == parse("a.b") assert check._url == "url" assert check._username == "user" assert check._password == "pass" assert check._timeout == 42 def test_raises_on_missing_json_path(self) -> None: with pytest.raises(ConfigurationError): JsonPath.create( "name", config_section( { "url": "url", "username": "user", "password": "pass", "timeout": "42", } ), ) def test_raises_on_invalid_json_path(self) -> None: with pytest.raises(ConfigurationError): JsonPath.create( "name", config_section( { "url": "url", "jsonpath": ",.asdfjasdklf", "username": "user", "password": "pass", "timeout": "42", } ), ) autosuspend-7.2.0/tests/test_checks_json/000077500000000000000000000000001475670032600205765ustar00rootroot00000000000000autosuspend-7.2.0/tests/test_checks_json/invalid.json000066400000000000000000000000111475670032600231070ustar00rootroot00000000000000{"broken autosuspend-7.2.0/tests/test_checks_kodi.py000066400000000000000000000177361475670032600211430ustar00rootroot00000000000000import json import pytest from pytest_mock import MockerFixture import requests.exceptions from autosuspend.checks import Check, ConfigurationError, TemporaryCheckError from autosuspend.checks.kodi import Kodi, KodiIdleTime from . import CheckTest from .utils import config_section class TestKodi(CheckTest): def create_instance(self, name: str) -> Check: return Kodi(name, url="url", timeout=10) def test_playing(self, mocker: MockerFixture) -> None: mock_reply = mocker.MagicMock() mock_reply.json.return_value = { "id": 1, "jsonrpc": "2.0", "result": [{"playerid": 0, "type": "audio"}], } mocker.patch("requests.Session.get", return_value=mock_reply) assert Kodi("foo", url="url", timeout=10).check() is not None mock_reply.json.assert_called_once_with() def test_not_playing(self, mocker: MockerFixture) -> None: mock_reply = mocker.MagicMock() mock_reply.json.return_value = {"id": 1, "jsonrpc": "2.0", "result": []} mocker.patch("requests.Session.get", return_value=mock_reply) assert Kodi("foo", url="url", timeout=10).check() is None mock_reply.json.assert_called_once_with() def test_playing_suspend_while_paused(self, mocker: MockerFixture) -> None: mock_reply = mocker.MagicMock() mock_reply.json.return_value = { "id": 1, "jsonrpc": "2.0", "result": {"Player.Playing": True}, } mocker.patch("requests.Session.get", return_value=mock_reply) assert ( Kodi("foo", url="url", timeout=10, suspend_while_paused=True).check() is not None ) mock_reply.json.assert_called_once_with() def test_not_playing_suspend_while_paused(self, mocker: MockerFixture) -> None: mock_reply = mocker.MagicMock() mock_reply.json.return_value = { "id": 1, "jsonrpc": "2.0", "result": {"Player.Playing": False}, } mocker.patch("requests.Session.get", return_value=mock_reply) assert ( Kodi("foo", url="url", timeout=10, suspend_while_paused=True).check() is None ) mock_reply.json.assert_called_once_with() def test_assertion_no_result(self, mocker: MockerFixture) -> None: mock_reply = mocker.MagicMock() mock_reply.json.return_value = {"id": 1, "jsonrpc": "2.0"} mocker.patch("requests.Session.get", return_value=mock_reply) with pytest.raises(TemporaryCheckError): Kodi("foo", url="url", timeout=10).check() def test_request_error(self, mocker: MockerFixture) -> None: mocker.patch( "requests.Session.get", side_effect=requests.exceptions.RequestException() ) with pytest.raises(TemporaryCheckError): Kodi("foo", url="url", timeout=10).check() def test_json_error(self, mocker: MockerFixture) -> None: mock_reply = mocker.MagicMock() mock_reply.json.side_effect = json.JSONDecodeError("test", "test", 42) mocker.patch("requests.Session.get", return_value=mock_reply) with pytest.raises(TemporaryCheckError): Kodi("foo", url="url", timeout=10).check() def test_create(self) -> None: check = Kodi.create( "name", config_section( { "url": "anurl", "timeout": "12", } ), ) assert check._url.startswith("anurl") assert check._timeout == 12 assert not check._suspend_while_paused def test_create_default_url(self) -> None: check = Kodi.create("name", config_section()) assert check._url.split("?")[0] == "http://localhost:8080/jsonrpc" def test_create_timeout_no_number(self) -> None: with pytest.raises(ConfigurationError): Kodi.create("name", config_section({"url": "anurl", "timeout": "string"})) def test_create_suspend_while_paused(self) -> None: check = Kodi.create( "name", config_section({"url": "anurl", "suspend_while_paused": "True"}) ) assert check._url.startswith("anurl") assert check._suspend_while_paused class TestKodiIdleTime(CheckTest): def create_instance(self, name: str) -> Check: return KodiIdleTime(name, url="url", timeout=10, idle_time=10) def test_create(self) -> None: check = KodiIdleTime.create( "name", config_section({"url": "anurl", "timeout": "12", "idle_time": "42"}) ) assert check._url.startswith("anurl") assert check._timeout == 12 assert check._idle_time == 42 def test_create_default_url(self) -> None: check = KodiIdleTime.create("name", config_section()) assert check._url.split("?")[0] == "http://localhost:8080/jsonrpc" def test_create_timeout_no_number(self) -> None: with pytest.raises(ConfigurationError): KodiIdleTime.create( "name", config_section({"url": "anurl", "timeout": "string"}) ) def test_create_idle_time_no_number(self) -> None: with pytest.raises(ConfigurationError): KodiIdleTime.create( "name", config_section({"url": "anurl", "idle_time": "string"}) ) def test_no_result(self, mocker: MockerFixture) -> None: mock_reply = mocker.MagicMock() mock_reply.json.return_value = {"id": 1, "jsonrpc": "2.0"} mocker.patch("requests.Session.get", return_value=mock_reply) with pytest.raises(TemporaryCheckError): KodiIdleTime("foo", url="url", timeout=10, idle_time=42).check() def test_result_is_list(self, mocker: MockerFixture) -> None: mock_reply = mocker.MagicMock() mock_reply.json.return_value = {"id": 1, "jsonrpc": "2.0", "result": []} mocker.patch("requests.Session.get", return_value=mock_reply) with pytest.raises(TemporaryCheckError): KodiIdleTime("foo", url="url", timeout=10, idle_time=42).check() def test_result_no_entry(self, mocker: MockerFixture) -> None: mock_reply = mocker.MagicMock() mock_reply.json.return_value = {"id": 1, "jsonrpc": "2.0", "result": {}} mocker.patch("requests.Session.get", return_value=mock_reply) with pytest.raises(TemporaryCheckError): KodiIdleTime("foo", url="url", timeout=10, idle_time=42).check() def test_result_wrong_entry(self, mocker: MockerFixture) -> None: mock_reply = mocker.MagicMock() mock_reply.json.return_value = { "id": 1, "jsonrpc": "2.0", "result": {"narf": True}, } mocker.patch("requests.Session.get", return_value=mock_reply) with pytest.raises(TemporaryCheckError): KodiIdleTime("foo", url="url", timeout=10, idle_time=42).check() def test_active(self, mocker: MockerFixture) -> None: mock_reply = mocker.MagicMock() mock_reply.json.return_value = { "id": 1, "jsonrpc": "2.0", "result": {"System.IdleTime(42)": False}, } mocker.patch("requests.Session.get", return_value=mock_reply) assert ( KodiIdleTime("foo", url="url", timeout=10, idle_time=42).check() is not None ) def test_inactive(self, mocker: MockerFixture) -> None: mock_reply = mocker.MagicMock() mock_reply.json.return_value = { "id": 1, "jsonrpc": "2.0", "result": {"System.IdleTime(42)": True}, } mocker.patch("requests.Session.get", return_value=mock_reply) assert KodiIdleTime("foo", url="url", timeout=10, idle_time=42).check() is None def test_request_error(self, mocker: MockerFixture) -> None: mocker.patch( "requests.Session.get", side_effect=requests.exceptions.RequestException() ) with pytest.raises(TemporaryCheckError): KodiIdleTime("foo", url="url", timeout=10, idle_time=42).check() autosuspend-7.2.0/tests/test_checks_linux.py000066400000000000000000000512211475670032600213370ustar00rootroot00000000000000from collections import namedtuple from collections.abc import Mapping from datetime import datetime, timezone from pathlib import Path import re import socket import sys from typing import Any from freezegun import freeze_time import psutil import pytest from pytest_httpserver import HTTPServer from pytest_mock import MockerFixture import requests from autosuspend.checks import ( Check, ConfigurationError, SevereCheckError, TemporaryCheckError, ) from autosuspend.checks.linux import ( ActiveConnection, File, Load, NetworkBandwidth, Ping, Processes, Users, ) from . import CheckTest from tests.utils import config_section class TestUsers(CheckTest): def create_instance(self, name: str) -> Check: return Users(name, re.compile(".*"), re.compile(".*"), re.compile(".*")) @staticmethod def create_suser( name: str, terminal: str, host: str, started: float, pid: int ) -> psutil._common.suser: return psutil._common.suser(name, terminal, host, started, pid) def test_reports_no_activity_without_users(self, mocker: MockerFixture) -> None: mocker.patch("psutil.users").return_value = [] assert ( Users("users", re.compile(".*"), re.compile(".*"), re.compile(".*")).check() is None ) def test_matching_users(self, mocker: MockerFixture) -> None: mocker.patch("psutil.users").return_value = [ self.create_suser("foo", "pts1", "host", 12345, 12345) ] assert ( Users("users", re.compile(".*"), re.compile(".*"), re.compile(".*")).check() is not None ) def test_detect_no_activity_if_no_matching_user_exists( self, mocker: MockerFixture ) -> None: mocker.patch("psutil.users").return_value = [ self.create_suser("foo", "pts1", "host", 12345, 12345) ] assert ( Users( "users", re.compile("narf"), re.compile(".*"), re.compile(".*") ).check() is None ) class TestCreate: def test_it_works_with_a_valid_config(self) -> None: check = Users.create( "name", config_section( { "name": "name.*name", "terminal": "term.*term", "host": "host.*host", } ), ) assert check._user_regex == re.compile("name.*name") assert check._terminal_regex == re.compile("term.*term") assert check._host_regex == re.compile("host.*host") def test_raises_with_invalid_expression(self) -> None: with pytest.raises(ConfigurationError): Users.create( "name", config_section( { "name": "name.*name", "terminal": "term.[[a-9]term", "host": "host.*host", } ), ) class TestProcesses(CheckTest): def create_instance(self, name: str) -> Check: return Processes(name, ["foo"]) class StubProcess: def __init__(self, name: str) -> None: self._name = name def name(self) -> str: return self._name class RaisingProcess: def name(self) -> str: raise psutil.NoSuchProcess(42) def test_detects_activity_with_matching_process( self, mocker: MockerFixture ) -> None: mocker.patch("psutil.process_iter").return_value = [ self.StubProcess("blubb"), self.StubProcess("nonmatching"), ] assert Processes("foo", ["dummy", "blubb", "other"]).check() is not None def test_ignores_no_such_process_errors(self, mocker: MockerFixture) -> None: mocker.patch("psutil.process_iter").return_value = [self.RaisingProcess()] Processes("foo", ["dummy"]).check() def test_detect_no_activity_for_non_matching_processes( self, mocker: MockerFixture ) -> None: mocker.patch("psutil.process_iter").return_value = [ self.StubProcess("asdfasdf"), self.StubProcess("nonmatching"), ] assert Processes("foo", ["dummy", "blubb", "other"]).check() is None class TestCreate: def test_it_works_with_a_valid_config(self) -> None: assert Processes.create( "name", config_section({"processes": "foo, bar, narf"}) )._processes == [ "foo", "bar", "narf", ] def test_raises_if_no_processes_are_configured(self) -> None: with pytest.raises(ConfigurationError): Processes.create("name", config_section()) snic = namedtuple("snic", ["family", "address", "netmask", "broadcast", "ptp"]) class TestActiveConnection(CheckTest): MY_PORT = 22 MY_ADDRESS = "123.456.123.456" MY_ADDRESS_IPV6 = "fe80::5193:518c:5c69:aedb" # this might sometimes happen: # https://superuser.com/a/99753/227177 MY_ADDRESS_IPV6_SCOPED = "fe80::5193:518c:5c69:cccc%eth0" def create_instance(self, name: str) -> Check: return ActiveConnection(name, [10]) @pytest.mark.parametrize( "connection", [ # ipv4 psutil._common.sconn( -1, socket.AF_INET, socket.SOCK_STREAM, (MY_ADDRESS, MY_PORT), ("42.42.42.42", 42), "ESTABLISHED", None, ), # ipv6 psutil._common.sconn( -1, socket.AF_INET6, socket.SOCK_STREAM, (MY_ADDRESS_IPV6, MY_PORT), ("42.42.42.42", 42), "ESTABLISHED", None, ), # ipv6 where local address has scope psutil._common.sconn( -1, socket.AF_INET6, socket.SOCK_STREAM, (MY_ADDRESS_IPV6_SCOPED.split("%")[0], MY_PORT), ("42.42.42.42", 42), "ESTABLISHED", None, ), # ipv6 with mapping to ipv4 # https://github.com/languitar/autosuspend/issues/116 psutil._common.sconn( -1, socket.AF_INET6, socket.SOCK_STREAM, (f"::ffff:{MY_ADDRESS}", MY_PORT), ("42.42.42.42", 42), "ESTABLISHED", None, ), ], ) def test_detect_activity_if_port_is_connected( self, mocker: MockerFixture, connection: psutil._common.sconn ) -> None: mocker.patch("psutil.net_if_addrs").return_value = { "dummy": [ snic(socket.AF_INET, self.MY_ADDRESS, "255.255.255.0", None, None), snic( socket.AF_INET6, self.MY_ADDRESS_IPV6, "ffff:ffff:ffff:ffff::", None, None, ), snic( socket.AF_INET6, self.MY_ADDRESS_IPV6_SCOPED, "ffff:ffff:ffff:ffff::", None, None, ), ], } mocker.patch("psutil.net_connections").return_value = [connection] assert ActiveConnection("foo", [10, self.MY_PORT, 30]).check() is not None @pytest.mark.parametrize( "connection", [ # not my port psutil._common.sconn( -1, socket.AF_INET, socket.SOCK_STREAM, (MY_ADDRESS, 32), ("42.42.42.42", 42), "ESTABLISHED", None, ), # not my local address psutil._common.sconn( -1, socket.AF_INET, socket.SOCK_STREAM, ("33.33.33.33", MY_PORT), ("42.42.42.42", 42), "ESTABLISHED", None, ), # not established psutil._common.sconn( -1, socket.AF_INET, socket.SOCK_STREAM, (MY_ADDRESS, MY_PORT), ("42.42.42.42", 42), "NARF", None, ), # I am the client psutil._common.sconn( -1, socket.AF_INET, socket.SOCK_STREAM, ("42.42.42.42", 42), (MY_ADDRESS, MY_PORT), "NARF", None, ), ], ) def test_detects_no_activity_if_port_is_not_connected( self, mocker: MockerFixture, connection: psutil._common.sconn ) -> None: mocker.patch("psutil.net_if_addrs").return_value = { "dummy": [ snic(socket.AF_INET, self.MY_ADDRESS, "255.255.255.0", None, None) ] } mocker.patch("psutil.net_connections").return_value = [connection] assert ActiveConnection("foo", [10, self.MY_PORT, 30]).check() is None class TestCreate: def test_it_works_with_a_valid_config(self) -> None: assert ActiveConnection.create( "name", config_section({"ports": "10,20,30"}) )._ports == {10, 20, 30} def test_raises_if_no_ports_are_configured(self) -> None: with pytest.raises(ConfigurationError): ActiveConnection.create("name", config_section()) def test_raises_if_ports_are_not_numeric(self) -> None: with pytest.raises(ConfigurationError): ActiveConnection.create("name", config_section({"ports": "10,20xx,30"})) class TestLoad(CheckTest): def create_instance(self, name: str) -> Check: return Load(name, 0.4) def test_detects_no_activity_below_threshold(self, mocker: Any) -> None: threshold = 1.34 mocker.patch("os.getloadavg").return_value = [0, threshold - 0.2, 0] assert Load("foo", threshold).check() is None def test_detects_activity_above_threshold(self, mocker: MockerFixture) -> None: threshold = 1.34 mocker.patch("os.getloadavg").return_value = [0, threshold + 0.2, 0] assert Load("foo", threshold).check() is not None class TestCreate: def test_it_works_with_a_valid_config(self) -> None: assert ( Load.create("name", config_section({"threshold": "3.2"}))._threshold == 3.2 ) def test_raises_if_threshold_is_not_numeric(self) -> None: with pytest.raises(ConfigurationError): Load.create("name", config_section({"threshold": "narf"})) class TestNetworkBandwidth(CheckTest): def create_instance(self, name: str) -> Check: return NetworkBandwidth(name, psutil.net_if_addrs().keys(), 0, 0) @staticmethod @pytest.fixture def serve_data_url(httpserver: HTTPServer) -> str: httpserver.expect_request("").respond_with_json({"foo": "bar"}) return httpserver.url_for("") def test_detects_non_mocked_activity(self, serve_data_url: str) -> None: check = NetworkBandwidth("name", psutil.net_if_addrs().keys(), 0, 0) # make some traffic requests.get(serve_data_url, timeout=5) assert check.check() is not None @pytest.fixture def _mock_interfaces(self, mocker: MockerFixture) -> None: mock = mocker.patch("psutil.net_if_addrs") mock.return_value = {"foo": None, "bar": None, "baz": None} class TestCreate: @pytest.mark.usefixtures("_mock_interfaces") def test_it_works_with_a_valid_config(self) -> None: check = NetworkBandwidth.create( "name", config_section( { "interfaces": "foo, baz", "threshold_send": "200", "threshold_receive": "300", } ), ) assert set(check._interfaces) == {"foo", "baz"} assert check._threshold_send == 200 assert check._threshold_receive == 300 @pytest.mark.usefixtures("_mock_interfaces") def test_default_values_work(self) -> None: check = NetworkBandwidth.create( "name", config_section({"interfaces": "foo, baz"}) ) assert set(check._interfaces) == {"foo", "baz"} assert check._threshold_send == 100 assert check._threshold_receive == 100 @pytest.mark.parametrize( ("config", "error_match"), [ ( { "interfaces": "foo, NOTEXIST", "threshold_send": "200", "threshold_receive": "300", }, r"does not exist", ), ( { "threshold_send": "200", "threshold_receive": "300", }, r"configuration key: \'interfaces\'", ), ( { "interfaces": "", "threshold_send": "200", "threshold_receive": "300", }, r"No interfaces configured", ), ( { "interfaces": "foo, bar", "threshold_send": "xxx", }, r"Threshold in wrong format", ), ( { "interfaces": "foo, bar", "threshold_receive": "xxx", }, r"Threshold in wrong format", ), ], ) @pytest.mark.usefixtures("_mock_interfaces") def test_raises_with_an_invalid_config( self, config: Mapping[str, str], error_match: str ) -> None: with pytest.raises(ConfigurationError, match=error_match): NetworkBandwidth.create("name", config_section(config)) @pytest.mark.parametrize( ("send_threshold", "receive_threshold", "match"), [(sys.float_info.max, 0, "receive"), (0, sys.float_info.max, "sending")], ) def test_detects_activity_in_direction( self, send_threshold: float, receive_threshold: float, match: str, serve_data_url: str, ) -> None: check = NetworkBandwidth( "name", psutil.net_if_addrs().keys(), send_threshold, receive_threshold ) # make some traffic requests.get(serve_data_url, timeout=5) res = check.check() assert res is not None assert match in res def test_reports_no_activity_below_threshold(self, serve_data_url: str) -> None: check = NetworkBandwidth( "name", psutil.net_if_addrs().keys(), sys.float_info.max, sys.float_info.max ) # make some traffic requests.get(serve_data_url, timeout=5) assert check.check() is None def test_internal_state_updating_works(self, serve_data_url: str) -> None: check = NetworkBandwidth( "name", psutil.net_if_addrs().keys(), sys.float_info.max, sys.float_info.max ) check.check() old_state = check._previous_values requests.get(serve_data_url, timeout=5) check.check() assert old_state != check._previous_values def test_delta_calculation_send_work(self, mocker: MockerFixture) -> None: first = mocker.MagicMock() type(first).bytes_sent = mocker.PropertyMock(return_value=1000) type(first).bytes_recv = mocker.PropertyMock(return_value=800) mocker.patch("psutil.net_io_counters").return_value = { "eth0": first, } with freeze_time("2019-10-01 10:00:00"): check = NetworkBandwidth("name", ["eth0"], 0, sys.float_info.max) second = mocker.MagicMock() type(second).bytes_sent = mocker.PropertyMock(return_value=1222) type(second).bytes_recv = mocker.PropertyMock(return_value=900) mocker.patch("psutil.net_io_counters").return_value = { "eth0": second, } with freeze_time("2019-10-01 10:00:01"): res = check.check() assert res is not None assert " 222.0 " in res def test_delta_calculation_receive_work(self, mocker: MockerFixture) -> None: first = mocker.MagicMock() type(first).bytes_sent = mocker.PropertyMock(return_value=1000) type(first).bytes_recv = mocker.PropertyMock(return_value=800) mocker.patch("psutil.net_io_counters").return_value = { "eth0": first, } with freeze_time("2019-10-01 10:00:00"): check = NetworkBandwidth("name", ["eth0"], sys.float_info.max, 0) second = mocker.MagicMock() type(second).bytes_sent = mocker.PropertyMock(return_value=1222) type(second).bytes_recv = mocker.PropertyMock(return_value=900) mocker.patch("psutil.net_io_counters").return_value = { "eth0": second, } with freeze_time("2019-10-01 10:00:01"): res = check.check() assert res is not None assert " 100.0 " in res class TestPing(CheckTest): def create_instance(self, name: str) -> Check: return Ping(name, "8.8.8.8") def test_calls_ping_correctly(self, mocker: MockerFixture) -> None: mock = mocker.patch("subprocess.call") mock.return_value = 1 hosts = ["abc", "129.123.145.42"] assert Ping("name", hosts).check() is None assert mock.call_count == len(hosts) for (args, _), host in zip(mock.call_args_list, hosts): assert args[0][-1] == host def test_raises_if_the_ping_binary_is_missing(self, mocker: MockerFixture) -> None: mock = mocker.patch("subprocess.call") mock.side_effect = FileNotFoundError() with pytest.raises(SevereCheckError): Ping("name", ["test"]).check() def test_detect_activity_if_ping_succeeds(self, mocker: MockerFixture) -> None: mock = mocker.patch("subprocess.call") mock.return_value = 0 assert Ping("name", ["foo"]).check() is not None class TestCreate: def test_raises_if_hosts_are_missing(self) -> None: with pytest.raises(ConfigurationError): Ping.create("name", config_section()) def test_comma_separated_hosts_are_split(self) -> None: ping = Ping.create("name", config_section({"hosts": "a,b,c"})) assert ping._hosts == ["a", "b", "c"] class TestFile(CheckTest): def create_instance(self, name: str) -> Check: return File(name, Path("asdf")) class TestCreate: def test_it_works_with_a_valid_config(self) -> None: check = File.create("name", config_section({"path": "/tmp/test"})) assert check._path == Path("/tmp/test") def test_raises_without_path(self) -> None: with pytest.raises(ConfigurationError): File.create("name", config_section()) def test_extracts_data_from_file(self, tmp_path: Path) -> None: test_file = tmp_path / "file" test_file.write_text("42\n\n") assert File("name", test_file).check( datetime.now(timezone.utc) ) == datetime.fromtimestamp(42, timezone.utc) def test_reports_no_wakeup_if_file_does_not_exist(self, tmp_path: Path) -> None: assert File("name", tmp_path / "narf").check(datetime.now(timezone.utc)) is None def test_raises_on_permissions_errors(self, tmp_path: Path) -> None: file_path = tmp_path / "test" file_path.write_bytes(b"2314898") file_path.chmod(0) with pytest.raises(TemporaryCheckError): File("name", file_path).check(datetime.now(timezone.utc)) def test_raises_on_io_errors(self, tmp_path: Path, mocker: MockerFixture) -> None: file_path = tmp_path / "test" file_path.write_bytes(b"2314898") mocker.patch("pathlib.Path.read_text").side_effect = IOError with pytest.raises(TemporaryCheckError): File("name", file_path).check(datetime.now(timezone.utc)) def test_raises_if_file_contents_are_not_a_timestamp(self, tmp_path: Path) -> None: test_file = tmp_path / "filexxx" test_file.write_text("nonumber\n\n") with pytest.raises(TemporaryCheckError): File("name", test_file).check(datetime.now(timezone.utc)) autosuspend-7.2.0/tests/test_checks_logs.py000066400000000000000000000213511475670032600211450ustar00rootroot00000000000000from datetime import timedelta, timezone from pathlib import Path import re from freezegun import freeze_time import pytest import pytz from autosuspend.checks import ConfigurationError, TemporaryCheckError from autosuspend.checks.logs import LastLogActivity from . import CheckTest from .utils import config_section class TestLastLogActivity(CheckTest): def create_instance(self, name: str) -> LastLogActivity: return LastLogActivity( name=name, log_file=Path("some_file"), pattern=re.compile("^(.*)$"), delta=timedelta(minutes=10), encoding="ascii", default_timezone=timezone.utc, ) def test_is_active(self, tmpdir: Path) -> None: file_path = tmpdir / "test.log" file_path.write_text("2020-02-02 12:12:23", encoding="ascii") with freeze_time("2020-02-02 12:15:00"): assert ( LastLogActivity( "test", file_path, re.compile(r"^(.*)$"), timedelta(minutes=10), "ascii", timezone.utc, ).check() is not None ) def test_is_not_active(self, tmpdir: Path) -> None: file_path = tmpdir / "test.log" file_path.write_text("2020-02-02 12:12:23", encoding="ascii") with freeze_time("2020-02-02 12:35:00"): assert ( LastLogActivity( "test", file_path, re.compile(r"^(.*)$"), timedelta(minutes=10), "ascii", timezone.utc, ).check() is None ) def test_uses_last_line(self, tmpdir: Path) -> None: file_path = tmpdir / "test.log" # last line is too old and must be used file_path.write_text( "\n".join(["2020-02-02 12:12:23", "1900-01-01"]), encoding="ascii" ) with freeze_time("2020-02-02 12:15:00"): assert ( LastLogActivity( "test", file_path, re.compile(r"^(.*)$"), timedelta(minutes=10), "ascii", timezone.utc, ).check() is None ) def test_ignores_lines_that_do_not_match(self, tmpdir: Path) -> None: file_path = tmpdir / "test.log" file_path.write_text("ignored", encoding="ascii") assert ( LastLogActivity( "test", file_path, re.compile(r"^foo(.*)$"), timedelta(minutes=10), "ascii", timezone.utc, ).check() is None ) def test_uses_pattern(self, tmpdir: Path) -> None: file_path = tmpdir / "test.log" file_path.write_text("foo2020-02-02 12:12:23bar", encoding="ascii") with freeze_time("2020-02-02 12:15:00"): assert ( LastLogActivity( "test", file_path, re.compile(r"^foo(.*)bar$"), timedelta(minutes=10), "ascii", timezone.utc, ).check() is not None ) def test_uses_given_timezone(self, tmpdir: Path) -> None: file_path = tmpdir / "test.log" # would match if timezone wasn't used file_path.write_text("2020-02-02 12:12:00", encoding="ascii") with freeze_time("2020-02-02 12:15:00"): assert ( LastLogActivity( "test", file_path, re.compile(r"^(.*)$"), timedelta(minutes=10), "ascii", timezone(offset=timedelta(hours=10)), ).check() is None ) def test_prefers_parsed_timezone(self, tmpdir: Path) -> None: file_path = tmpdir / "test.log" # would not match if provided timezone wasn't used file_path.write_text("2020-02-02T12:12:01-01:00", encoding="ascii") with freeze_time("2020-02-02 13:15:00"): assert ( LastLogActivity( "test", file_path, re.compile(r"^(.*)$"), timedelta(minutes=10), "ascii", timezone.utc, ).check() is not None ) def test_fails_if_dates_cannot_be_parsed(self, tmpdir: Path) -> None: file_path = tmpdir / "test.log" # would match if timezone wasn't used file_path.write_text("202000xxx", encoding="ascii") with pytest.raises(TemporaryCheckError): LastLogActivity( "test", file_path, re.compile(r"^(.*)$"), timedelta(minutes=10), "ascii", timezone.utc, ).check() def test_fails_if_dates_are_in_the_future(self, tmpdir: Path) -> None: file_path = tmpdir / "test.log" # would match if timezone wasn't used file_path.write_text("2022-01-01", encoding="ascii") with freeze_time("2020-02-02 12:15:00"), pytest.raises(TemporaryCheckError): LastLogActivity( "test", file_path, re.compile(r"^(.*)$"), timedelta(minutes=10), "ascii", timezone.utc, ).check() def test_fails_if_file_cannot_be_read(self, tmpdir: Path) -> None: file_path = tmpdir / "test.log" with pytest.raises(TemporaryCheckError): LastLogActivity( "test", file_path, re.compile(r"^(.*)$"), timedelta(minutes=10), "ascii", timezone.utc, ).check() def test_create(self) -> None: created = LastLogActivity.create( "thename", config_section( { "name": "somename", "log_file": "/some/file", "pattern": "^foo(.*)bar$", "minutes": "42", "encoding": "utf-8", "timezone": "Europe/Berlin", } ), ) assert created.log_file == Path("/some/file") assert created.pattern == re.compile(r"^foo(.*)bar$") assert created.delta == timedelta(minutes=42) assert created.encoding == "utf-8" assert created.default_timezone == pytz.timezone("Europe/Berlin") def test_create_handles_pattern_errors(self) -> None: with pytest.raises(ConfigurationError): LastLogActivity.create( "thename", config_section( { "name": "somename", "log_file": "/some/file", "pattern": "^^foo((.*)bar$", } ), ) def test_create_handles_delta_errors(self) -> None: with pytest.raises(ConfigurationError): LastLogActivity.create( "thename", config_section( { "name": "somename", "log_file": "/some/file", "pattern": "(.*)", "minutes": "test", } ), ) def test_create_handles_negative_deltas(self) -> None: with pytest.raises(ConfigurationError): LastLogActivity.create( "thename", config_section( { "name": "somename", "log_file": "/some/file", "pattern": "(.*)", "minutes": "-42", } ), ) def test_create_handles_missing_pattern_groups(self) -> None: with pytest.raises(ConfigurationError): LastLogActivity.create( "thename", config_section( { "name": "somename", "log_file": "/some/file", "pattern": ".*", } ), ) def test_create_handles_missing_keys(self) -> None: with pytest.raises(ConfigurationError): LastLogActivity.create( "thename", config_section( { "name": "somename", } ), ) autosuspend-7.2.0/tests/test_checks_mpd.py000066400000000000000000000066561475670032600207740ustar00rootroot00000000000000from typing import Any import mpd import pytest from pytest_mock import MockerFixture from autosuspend.checks import Check, ConfigurationError, TemporaryCheckError from autosuspend.checks.mpd import Mpd from . import CheckTest from .utils import config_section class TestMpd(CheckTest): def create_instance(self, name: str) -> Check: # concrete values are never used in the tests return Mpd(name, None, None, None) # type: ignore def test_playing(self, monkeypatch: Any) -> None: check = Mpd("test", None, None, None) # type: ignore def get_state() -> dict: return {"state": "play"} monkeypatch.setattr(check, "_get_state", get_state) assert check.check() is not None def test_not_playing(self, monkeypatch: Any) -> None: check = Mpd("test", None, None, None) # type: ignore def get_state() -> dict: return {"state": "pause"} monkeypatch.setattr(check, "_get_state", get_state) assert check.check() is None def test_correct_mpd_interaction(self, mocker: MockerFixture) -> None: mock_instance = mocker.MagicMock(spec=mpd.MPDClient) mock_instance.status.return_value = {"state": "play"} timeout_property = mocker.PropertyMock() type(mock_instance).timeout = timeout_property mock = mocker.patch("autosuspend.checks.mpd.MPDClient") mock.return_value = mock_instance host = "foo" port = 42 timeout = 17 assert Mpd("name", host, port, timeout).check() is not None timeout_property.assert_called_once_with(timeout) mock_instance.connect.assert_called_once_with(host, port) mock_instance.status.assert_called_once_with() mock_instance.close.assert_called_once_with() mock_instance.disconnect.assert_called_once_with() @pytest.mark.parametrize("exception_type", [ConnectionError, mpd.ConnectionError]) def test_handle_connection_errors(self, exception_type: type) -> None: check = Mpd("test", None, None, None) # type: ignore def _get_state() -> dict: raise exception_type() # https://github.com/python/mypy/issues/2427 check._get_state = _get_state # type: ignore with pytest.raises(TemporaryCheckError): check.check() def test_create(self) -> None: check = Mpd.create( "name", config_section( { "host": "host", "port": "1234", "timeout": "12", } ), ) assert check._host == "host" assert check._port == 1234 assert check._timeout == 12 def test_create_port_no_number(self) -> None: with pytest.raises(ConfigurationError): Mpd.create( "name", config_section( { "host": "host", "port": "string", "timeout": "12", } ), ) def test_create_timeout_no_number(self) -> None: with pytest.raises(ConfigurationError): Mpd.create( "name", config_section( { "host": "host", "port": "10", "timeout": "string", } ), ) autosuspend-7.2.0/tests/test_checks_smb.py000066400000000000000000000027761475670032600207740ustar00rootroot00000000000000from pathlib import Path import subprocess import pytest from pytest_mock import MockerFixture from autosuspend.checks import Check, SevereCheckError, TemporaryCheckError from autosuspend.checks.smb import Smb from . import CheckTest class TestSmb(CheckTest): def create_instance(self, name: str) -> Check: return Smb(name) def test_no_connections(self, datadir: Path, mocker: MockerFixture) -> None: mocker.patch("subprocess.check_output").return_value = ( datadir / "smbstatus_no_connections" ).read_bytes() assert Smb("foo").check() is None def test_with_connections(self, datadir: Path, mocker: MockerFixture) -> None: mocker.patch("subprocess.check_output").return_value = ( datadir / "smbstatus_with_connections" ).read_bytes() res = Smb("foo").check() assert res is not None assert len(res.splitlines()) == 3 def test_call_error(self, mocker: MockerFixture) -> None: mocker.patch( "subprocess.check_output", side_effect=subprocess.CalledProcessError(2, "cmd"), ) with pytest.raises(TemporaryCheckError): Smb("foo").check() def test_missing_executable(self, mocker: MockerFixture) -> None: mocker.patch("subprocess.check_output", side_effect=FileNotFoundError) with pytest.raises(SevereCheckError): Smb("foo").check() def test_create(self) -> None: assert isinstance(Smb.create("name", None), Smb) autosuspend-7.2.0/tests/test_checks_smb/000077500000000000000000000000001475670032600204065ustar00rootroot00000000000000autosuspend-7.2.0/tests/test_checks_smb/smbstatus_no_connections000066400000000000000000000004471475670032600254610ustar00rootroot00000000000000 Samba version 4.7.0 PID Username Group Machine Protocol Version Encryption Signing ---------------------------------------------------------------------------------------------------------------------------------------- autosuspend-7.2.0/tests/test_checks_smb/smbstatus_with_connections000066400000000000000000000004211475670032600260100ustar00rootroot00000000000000 Samba version 3.5.1 PID Username Group Machine ------------------------------------------------------------------- 14944 it 131.169.214.117 (131.169.214.117) 14944 it 131.169.214.117 (131.169.214.117) autosuspend-7.2.0/tests/test_checks_stub.py000066400000000000000000000027751475670032600211670ustar00rootroot00000000000000from datetime import datetime, timedelta, timezone import pytest from autosuspend.checks import Check, ConfigurationError from autosuspend.checks.stub import Periodic from . import CheckTest from .utils import config_section class TestPeriodic(CheckTest): def create_instance(self, name: str) -> Check: delta = timedelta(seconds=10, minutes=42) return Periodic(name, delta) def test_create(self) -> None: check = Periodic.create( "name", config_section({"unit": "seconds", "value": "13"}) ) assert check._delta == timedelta(seconds=13) def test_create_wrong_unit(self) -> None: with pytest.raises(ConfigurationError): Periodic.create("name", config_section({"unit": "asdfasdf", "value": "13"})) def test_create_not_numeric(self) -> None: with pytest.raises(ConfigurationError): Periodic.create( "name", config_section({"unit": "seconds", "value": "asdfasd"}) ) def test_create_no_unit(self) -> None: with pytest.raises(ConfigurationError): Periodic.create("name", config_section({"value": "13"})) def test_create_float(self) -> None: Periodic.create( "name", config_section({"unit": "seconds", "value": "21312.12"}) ) def test_check(self) -> None: delta = timedelta(seconds=10, minutes=42) check = Periodic("test", delta) now = datetime.now(timezone.utc) assert check.check(now) == now + delta autosuspend-7.2.0/tests/test_checks_systemd.py000066400000000000000000000113261475670032600216720ustar00rootroot00000000000000from datetime import datetime, timedelta, timezone import re from unittest.mock import Mock from dbus.proxies import ProxyObject import pytest from pytest_mock import MockerFixture from autosuspend.checks import Check, ConfigurationError, TemporaryCheckError from autosuspend.checks.systemd import ( LogindSessionsIdle, next_timer_executions, SystemdTimer, ) from . import CheckTest from .utils import config_section @pytest.mark.skip(reason="No dbusmock implementation available") def test_next_timer_executions() -> None: assert next_timer_executions() is not None class TestSystemdTimer(CheckTest): @staticmethod @pytest.fixture def next_timer_executions(mocker: MockerFixture) -> Mock: return mocker.patch("autosuspend.checks.systemd.next_timer_executions") def create_instance(self, name: str) -> Check: return SystemdTimer(name, re.compile(".*")) def test_create_handles_incorrect_expressions(self) -> None: with pytest.raises(ConfigurationError): SystemdTimer.create("somename", config_section({"match": "(.*"})) def test_create_raises_if_match_is_missing(self) -> None: with pytest.raises(ConfigurationError): SystemdTimer.create("somename", config_section()) def test_works_without_timers(self, next_timer_executions: Mock) -> None: next_timer_executions.return_value = {} now = datetime.now(timezone.utc) assert SystemdTimer("foo", re.compile(".*")).check(now) is None def test_ignores_non_matching_timers(self, next_timer_executions: Mock) -> None: now = datetime.now(timezone.utc) next_timer_executions.return_value = {"ignored": now} assert SystemdTimer("foo", re.compile("needle")).check(now) is None def test_finds_matching_timers(self, next_timer_executions: Mock) -> None: pattern = "foo" now = datetime.now(timezone.utc) next_timer_executions.return_value = {pattern: now} assert SystemdTimer("foo", re.compile(pattern)).check(now) is now def test_selects_the_closest_execution_if_multiple_match( self, next_timer_executions: Mock ) -> None: now = datetime.now(timezone.utc) next_timer_executions.return_value = { "later": now + timedelta(minutes=1), "matching": now, } assert SystemdTimer("foo", re.compile(".*")).check(now) is now class TestLogindSessionsIdle(CheckTest): def create_instance(self, name: str) -> Check: return LogindSessionsIdle(name, ["tty", "x11", "wayland"], ["active", "online"]) def test_active(self, logind: ProxyObject) -> None: logind.AddSession("c1", "seat0", 1042, "auser", True) check = LogindSessionsIdle("test", ["test"], ["active", "online"]) assert check.check() is not None @pytest.mark.skip(reason="No known way to set idle hint in dbus mock right now") def test_inactive(self, logind: ProxyObject) -> None: logind.AddSession("c1", "seat0", 1042, "auser", False) check = LogindSessionsIdle("test", ["test"], ["active", "online"]) assert check.check() is None def test_ignore_unknow_type(self, logind: ProxyObject) -> None: logind.AddSession("c1", "seat0", 1042, "auser", True) check = LogindSessionsIdle("test", ["not_test"], ["active", "online"]) assert check.check() is None def test_ignore_unknown_class(self, logind: ProxyObject) -> None: logind.AddSession("c1", "seat0", 1042, "user", True) check = LogindSessionsIdle( "test", ["test"], ["active", "online"], ["nosuchclass"] ) assert check.check() is None def test_configure_defaults(self) -> None: check = LogindSessionsIdle.create("name", config_section()) assert check._types == ["tty", "x11", "wayland"] assert check._states == ["active", "online"] def test_configure_types(self) -> None: check = LogindSessionsIdle.create( "name", config_section({"types": "test, bla,foo"}) ) assert check._types == ["test", "bla", "foo"] def test_configure_states(self) -> None: check = LogindSessionsIdle.create( "name", config_section({"states": "test, bla,foo"}) ) assert check._states == ["test", "bla", "foo"] def test_configure_classes(self) -> None: check = LogindSessionsIdle.create( "name", config_section({"classes": "test, bla,foo"}) ) assert check._classes == ["test", "bla", "foo"] @pytest.mark.usefixtures("_logind_dbus_error") def test_dbus_error(self) -> None: check = LogindSessionsIdle("test", ["test"], ["active", "online"]) with pytest.raises(TemporaryCheckError): check.check() autosuspend-7.2.0/tests/test_checks_util.py000066400000000000000000000074221475670032600211610ustar00rootroot00000000000000from collections.abc import Callable from pathlib import Path from unittest.mock import ANY import pytest from pytest_httpserver import HTTPServer from pytest_mock import MockerFixture import requests from autosuspend.checks import ConfigurationError, TemporaryCheckError from autosuspend.checks.util import NetworkMixin from .utils import config_section class TestNetworkMixin: def test_collect_missing_url(self) -> None: with pytest.raises(ConfigurationError, match=r"^Lacks 'url'.*"): NetworkMixin.collect_init_args(config_section()) def test_username_missing(self) -> None: with pytest.raises(ConfigurationError, match=r"^Username and.*"): NetworkMixin.collect_init_args( config_section({"url": "required", "password": "lacks username"}) ) def test_password_missing(self) -> None: with pytest.raises(ConfigurationError, match=r"^Username and.*"): NetworkMixin.collect_init_args( config_section({"url": "required", "username": "lacks password"}) ) def test_collect_default_timeout(self) -> None: args = NetworkMixin.collect_init_args(config_section({"url": "required"})) assert args["timeout"] == 5 def test_collect_timeout(self) -> None: args = NetworkMixin.collect_init_args( config_section({"url": "required", "timeout": "42"}) ) assert args["timeout"] == 42 def test_collect_invalid_timeout(self) -> None: with pytest.raises(ConfigurationError, match=r"^Configuration error .*"): NetworkMixin.collect_init_args( config_section({"url": "required", "timeout": "xx"}) ) def test_request(self, datadir: Path, serve_file: Callable[[Path], str]) -> None: reply = NetworkMixin( serve_file(datadir / "xml_with_encoding.xml"), 5, ).request() assert reply is not None assert reply.status_code == 200 def test_requests_exception(self, mocker: MockerFixture) -> None: mock_method = mocker.patch("requests.Session.get") mock_method.side_effect = requests.exceptions.ReadTimeout() with pytest.raises(TemporaryCheckError): NetworkMixin("url", timeout=5).request() def test_smoke(self, datadir: Path, serve_file: Callable[[Path], str]) -> None: response = NetworkMixin(serve_file(datadir / "data.txt"), timeout=5).request() assert response is not None assert response.text == "iamhere\n" def test_exception_404(self, httpserver: HTTPServer) -> None: with pytest.raises(TemporaryCheckError): NetworkMixin(httpserver.url_for("/does/not/exist"), timeout=5).request() def test_authentication( self, datadir: Path, serve_protected: Callable[[Path], tuple[str, str, str]] ) -> None: url, username, password = serve_protected(datadir / "data.txt") NetworkMixin(url, 5, username=username, password=password).request() def test_invalid_authentication( self, datadir: Path, serve_protected: Callable[[Path], tuple[str, str, str]] ) -> None: with pytest.raises(TemporaryCheckError): NetworkMixin( serve_protected(datadir / "data.txt")[0], 5, username="userx", password="pass", ).request() def test_file_url(self) -> None: NetworkMixin("file://" + __file__, 5).request() def test_content_type(self, mocker: MockerFixture) -> None: mock_method = mocker.patch("requests.Session.get") content_type = "foo/bar" NetworkMixin("url", timeout=5, accept=content_type).request() mock_method.assert_called_with( ANY, timeout=ANY, headers={"Accept": content_type} ) autosuspend-7.2.0/tests/test_checks_util/000077500000000000000000000000001475670032600206025ustar00rootroot00000000000000autosuspend-7.2.0/tests/test_checks_util/data.txt000066400000000000000000000000101475670032600222430ustar00rootroot00000000000000iamhere autosuspend-7.2.0/tests/test_checks_util/xml_with_encoding.xml000066400000000000000000000034541475670032600250330ustar00rootroot00000000000000 autosuspend-7.2.0/tests/test_checks_wakeup.py000066400000000000000000000006511475670032600214750ustar00rootroot00000000000000import pytest from autosuspend.checks import Wakeup @pytest.mark.parametrize( "name", [ "Calendar", "Command", "File", "Periodic", "SystemdTimer", "XPath", "XPathDelta", ], ) def test_legacy_check_names_are_available(name: str) -> None: res = __import__("autosuspend.checks.wakeup", fromlist=[name]) assert issubclass(getattr(res, name), Wakeup) autosuspend-7.2.0/tests/test_checks_xorg.py000066400000000000000000000202251475670032600211570ustar00rootroot00000000000000from getpass import getuser import logging from pathlib import Path import re import subprocess from typing import Any import pytest from pytest_mock import MockerFixture from autosuspend.checks import ( Check, ConfigurationError, SevereCheckError, TemporaryCheckError, ) from autosuspend.checks.xorg import ( list_sessions_logind, list_sessions_sockets, XIdleTime, XorgSession, ) from autosuspend.util.systemd import LogindDBusException from . import CheckTest from .utils import config_section class TestListSessionsSockets: def test_empty(self, tmp_path: Path) -> None: assert list_sessions_sockets(tmp_path) == [] @pytest.mark.parametrize("number", [0, 10, 1024]) def test_extracts_valid_sockets(self, tmp_path: Path, number: int) -> None: session_sock = tmp_path / f"X{number}" session_sock.touch() assert list_sessions_sockets(tmp_path) == [ XorgSession(number, session_sock.owner()) ] @pytest.mark.parametrize("invalid_number", ["", "string", " "]) def test_ignores_and_warns_on_invalid_numbers( self, tmp_path: Path, invalid_number: str, caplog: Any, ) -> None: (tmp_path / f"X{invalid_number}").touch() with caplog.at_level(logging.WARNING): assert list_sessions_sockets(tmp_path) == [] assert caplog.records != [] def test_ignores_and_warns_on_unknown_users( self, tmp_path: Path, mocker: MockerFixture, caplog: Any, ) -> None: (tmp_path / "X0").touch() mocker.patch("pathlib.Path.owner").side_effect = KeyError() with caplog.at_level(logging.WARNING): assert list_sessions_sockets(tmp_path) == [] assert caplog.records != [] def test_ignores_other_files( self, tmp_path: Path, ) -> None: (tmp_path / "asdf").touch() assert list_sessions_sockets(tmp_path) == [] def test_returns_multiple(self, tmp_path: Path) -> None: (tmp_path / "X0").touch() (tmp_path / "X1").touch() assert len(list_sessions_sockets(tmp_path)) == 2 _LIST_LOGIND_SESSIONS_TO_PATCH = "autosuspend.checks.xorg.list_logind_sessions" class TestListSessionsLogind: def test_extracts_valid_sessions(self, mocker: MockerFixture) -> None: username = "test_user" display = 42 mocker.patch(_LIST_LOGIND_SESSIONS_TO_PATCH).return_value = [ ("id", {"Name": username, "Display": f":{display}"}) ] assert list_sessions_logind() == [XorgSession(display, username)] def test_ignores_sessions_with_missing_properties( self, mocker: MockerFixture ) -> None: mocker.patch(_LIST_LOGIND_SESSIONS_TO_PATCH).return_value = [ ("id", {"Name": "someuser"}), ("id", {"Display": ":42"}), ] assert list_sessions_logind() == [] def test_ignores_and_warns_on_invalid_display_numbers( self, mocker: MockerFixture, caplog: Any, ) -> None: mocker.patch(_LIST_LOGIND_SESSIONS_TO_PATCH).return_value = [ ("id", {"Name": "someuser", "Display": "XXX"}), ] with caplog.at_level(logging.WARNING): assert list_sessions_logind() == [] assert caplog.records != [] class TestXIdleTime(CheckTest): def create_instance(self, name: str) -> Check: # concrete values are never used in the test return XIdleTime(name, 10, "sockets", None, None) # type: ignore def test_smoke(self, mocker: MockerFixture) -> None: check = XIdleTime("name", 100, "logind", re.compile(r"a^"), re.compile(r"a^")) mocker.patch.object(check, "_provide_sessions").return_value = [ XorgSession(42, getuser()), ] co_mock = mocker.patch("subprocess.check_output") co_mock.return_value = "123" res = check.check() assert res is not None assert " 0.123 " in res args, kwargs = co_mock.call_args assert getuser() in args[0] assert kwargs["env"]["DISPLAY"] == ":42" assert getuser() in kwargs["env"]["XAUTHORITY"] def test_no_activity(self, mocker: MockerFixture) -> None: check = XIdleTime("name", 100, "logind", re.compile(r"a^"), re.compile(r"a^")) mocker.patch.object(check, "_provide_sessions").return_value = [ XorgSession(42, getuser()), ] mocker.patch("subprocess.check_output").return_value = "120000" assert check.check() is None def test_multiple_sessions(self, mocker: MockerFixture) -> None: check = XIdleTime("name", 100, "logind", re.compile(r"a^"), re.compile(r"a^")) mocker.patch.object(check, "_provide_sessions").return_value = [ XorgSession(42, getuser()), XorgSession(17, "root"), ] co_mock = mocker.patch("subprocess.check_output") co_mock.side_effect = [ "120000", "123", ] res = check.check() assert res is not None assert " 0.123 " in res assert co_mock.call_count == 2 # check second call for correct values, not checked before args, kwargs = co_mock.call_args_list[1] assert "root" in args[0] assert kwargs["env"]["DISPLAY"] == ":17" assert "root" in kwargs["env"]["XAUTHORITY"] def test_handle_call_error(self, mocker: MockerFixture) -> None: check = XIdleTime("name", 100, "logind", re.compile(r"a^"), re.compile(r"a^")) mocker.patch.object(check, "_provide_sessions").return_value = [ XorgSession(42, getuser()), ] mocker.patch( "subprocess.check_output", ).side_effect = subprocess.CalledProcessError(2, "foo") with pytest.raises(TemporaryCheckError): check.check() def test_create_default(self) -> None: check = XIdleTime.create("name", config_section()) assert check._timeout == 600 assert check._ignore_process_re == re.compile(r"a^") assert check._ignore_users_re == re.compile(r"a^") assert check._provide_sessions == list_sessions_sockets def test_create(self) -> None: check = XIdleTime.create( "name", config_section( { "timeout": "42", "ignore_if_process": ".*test", "ignore_users": "test.*test", "method": "logind", } ), ) assert check._timeout == 42 assert check._ignore_process_re == re.compile(r".*test") assert check._ignore_users_re == re.compile(r"test.*test") assert check._provide_sessions == list_sessions_logind def test_create_no_int(self) -> None: with pytest.raises(ConfigurationError): XIdleTime.create("name", config_section({"timeout": "string"})) def test_create_broken_process_re(self) -> None: with pytest.raises(ConfigurationError): XIdleTime.create("name", config_section({"ignore_if_process": "[[a-9]"})) def test_create_broken_users_re(self) -> None: with pytest.raises(ConfigurationError): XIdleTime.create("name", config_section({"ignore_users": "[[a-9]"})) def test_create_unknown_method(self) -> None: with pytest.raises(ConfigurationError): XIdleTime.create("name", config_section({"method": "asdfasdf"})) def test_list_sessions_logind_dbus_error(self, mocker: MockerFixture) -> None: check = XIdleTime.create("name", config_section()) mocker.patch.object(check, "_provide_sessions").side_effect = ( LogindDBusException() ) with pytest.raises(TemporaryCheckError): check._safe_provide_sessions() def test_sudo_not_found(self, mocker: MockerFixture) -> None: check = XIdleTime("name", 100, "logind", re.compile(r"a^"), re.compile(r"a^")) mocker.patch.object(check, "_provide_sessions").return_value = [ XorgSession(42, getuser()), ] mocker.patch("subprocess.check_output").side_effect = FileNotFoundError with pytest.raises(SevereCheckError): check.check() autosuspend-7.2.0/tests/test_checks_xpath.py000066400000000000000000000247631475670032600213370ustar00rootroot00000000000000from collections.abc import Callable from datetime import datetime, timedelta, timezone from pathlib import Path from typing import Any import pytest from pytest_mock import MockerFixture from autosuspend.checks import Activity, Check, ConfigurationError, TemporaryCheckError from autosuspend.checks.xpath import ( XPathActivity, XPathDeltaWakeup, XPathMixin, XPathWakeup, ) from . import CheckTest from .utils import config_section class _XPathMixinSub(XPathMixin, Activity): def __init__(self, name: str, **kwargs: Any) -> None: Activity.__init__(self, name) XPathMixin.__init__(self, **kwargs) def check(self) -> str | None: pass class TestXPathMixin: def test_smoke(self, datadir: Path, serve_file: Callable[[Path], str]) -> None: result = _XPathMixinSub( "foo", xpath="/b", url=serve_file(datadir / "xml_with_encoding.xml"), timeout=5, ).evaluate() assert result is not None assert len(result) == 0 def test_broken_xml(self, mocker: MockerFixture) -> None: mock_reply = mocker.MagicMock() content_property = mocker.PropertyMock() type(mock_reply).content = content_property content_property.return_value = b"//broken" mocker.patch("requests.Session.get", return_value=mock_reply) with pytest.raises(TemporaryCheckError): _XPathMixinSub("foo", xpath="/b", url="nourl", timeout=5).evaluate() def test_xml_with_encoding(self, mocker: MockerFixture) -> None: mock_reply = mocker.MagicMock() content_property = mocker.PropertyMock() type(mock_reply).content = content_property content_property.return_value = ( b'' ) mocker.patch("requests.Session.get", return_value=mock_reply) _XPathMixinSub("foo", xpath="/b", url="nourl", timeout=5).evaluate() def test_xpath_prevalidation(self) -> None: with pytest.raises(ConfigurationError, match=r"^Invalid xpath.*"): _XPathMixinSub.create( "name", config_section({"xpath": "|34/ad", "url": "required"}) ) @pytest.mark.parametrize("entry", ["xpath", "url"]) def test_missing_config_entry(self, entry: str) -> None: section = config_section({"xpath": "/valid", "url": "required"}) del section[entry] with pytest.raises(ConfigurationError, match=r"^Lacks '" + entry + "'.*"): _XPathMixinSub.create("name", section) def test_invalid_config_entry(self) -> None: with pytest.raises(ConfigurationError, match=r"^Configuration error .*"): _XPathMixinSub.create( "name", config_section( {"xpath": "/valid", "url": "required", "timeout": "xxx"} ), ) class TestXPathActivity(CheckTest): def create_instance(self, name: str) -> Check: return XPathActivity( name=name, url="url", timeout=5, username="userx", password="pass", xpath="/b", ) def test_matching(self, mocker: MockerFixture) -> None: mock_reply = mocker.MagicMock() content_property = mocker.PropertyMock() type(mock_reply).content = content_property content_property.return_value = "" mock_method = mocker.patch("requests.Session.get", return_value=mock_reply) url = "nourl" assert XPathActivity("foo", xpath="/a", url=url, timeout=5).check() is not None mock_method.assert_called_once_with(url, timeout=5, headers=None) content_property.assert_called_once_with() def test_not_matching(self, mocker: MockerFixture) -> None: mock_reply = mocker.MagicMock() content_property = mocker.PropertyMock() type(mock_reply).content = content_property content_property.return_value = "" mocker.patch("requests.Session.get", return_value=mock_reply) assert XPathActivity("foo", xpath="/b", url="nourl", timeout=5).check() is None def test_create(self) -> None: check: XPathActivity = XPathActivity.create( "name", config_section( { "url": "url", "xpath": "/xpath", "username": "user", "password": "pass", "timeout": "42", } ), ) assert check._xpath == "/xpath" assert check._url == "url" assert check._username == "user" assert check._password == "pass" assert check._timeout == 42 def test_network_errors_are_passed( self, datadir: Path, serve_protected: Callable[[Path], tuple[str, str, str]] ) -> None: with pytest.raises(TemporaryCheckError): XPathActivity( name="name", url=serve_protected(datadir / "data.txt")[0], timeout=5, username="wrong", password="wrong", xpath="/b", ).request() class TestXPathWakeup(CheckTest): def create_instance(self, name: str) -> Check: return XPathWakeup(name, xpath="/a", url="nourl", timeout=5) def test_matching(self, mocker: MockerFixture) -> None: mock_reply = mocker.MagicMock() content_property = mocker.PropertyMock() type(mock_reply).content = content_property content_property.return_value = '' mock_method = mocker.patch("requests.Session.get", return_value=mock_reply) url = "nourl" assert XPathWakeup("foo", xpath="/a/@value", url=url, timeout=5).check( datetime.now(timezone.utc) ) == datetime.fromtimestamp(42.3, timezone.utc) mock_method.assert_called_once_with(url, timeout=5, headers=None) content_property.assert_called_once_with() def test_not_matching(self, mocker: MockerFixture) -> None: mock_reply = mocker.MagicMock() content_property = mocker.PropertyMock() type(mock_reply).content = content_property content_property.return_value = "" mocker.patch("requests.Session.get", return_value=mock_reply) assert ( XPathWakeup("foo", xpath="/b", url="nourl", timeout=5).check( datetime.now(timezone.utc) ) is None ) def test_not_a_string(self, mocker: MockerFixture) -> None: mock_reply = mocker.MagicMock() content_property = mocker.PropertyMock() type(mock_reply).content = content_property content_property.return_value = "" mocker.patch("requests.Session.get", return_value=mock_reply) with pytest.raises(TemporaryCheckError): XPathWakeup("foo", xpath="/a", url="nourl", timeout=5).check( datetime.now(timezone.utc) ) def test_not_a_number(self, mocker: MockerFixture) -> None: mock_reply = mocker.MagicMock() content_property = mocker.PropertyMock() type(mock_reply).content = content_property content_property.return_value = '' mocker.patch("requests.Session.get", return_value=mock_reply) with pytest.raises(TemporaryCheckError): XPathWakeup("foo", xpath="/a/@value", url="nourl", timeout=5).check( datetime.now(timezone.utc) ) def test_multiple_min(self, mocker: MockerFixture) -> None: mock_reply = mocker.MagicMock() content_property = mocker.PropertyMock() type(mock_reply).content = content_property content_property.return_value = """ """ mocker.patch("requests.Session.get", return_value=mock_reply) assert XPathWakeup("foo", xpath="//a/@value", url="nourl", timeout=5).check( datetime.now(timezone.utc) ) == datetime.fromtimestamp(10, timezone.utc) def test_create(self) -> None: check: XPathWakeup = XPathWakeup.create( "name", config_section( { "xpath": "/valid", "url": "nourl", "timeout": "20", } ), ) assert check._xpath == "/valid" class TestXPathDeltaWakeup(CheckTest): def create_instance(self, name: str) -> Check: return XPathDeltaWakeup(name, xpath="/a", url="nourl", timeout=5, unit="days") @pytest.mark.parametrize( ("unit", "factor"), [ ("microseconds", 0.000001), ("milliseconds", 0.001), ("seconds", 1), ("minutes", 60), ("hours", 60 * 60), ("days", 60 * 60 * 24), ("weeks", 60 * 60 * 24 * 7), ], ) def test_smoke(self, mocker: MockerFixture, unit: str, factor: float) -> None: mock_reply = mocker.MagicMock() content_property = mocker.PropertyMock() type(mock_reply).content = content_property content_property.return_value = '' mocker.patch("requests.Session.get", return_value=mock_reply) url = "nourl" now = datetime.now(timezone.utc) result = XPathDeltaWakeup( "foo", xpath="/a/@value", url=url, timeout=5, unit=unit ).check(now) assert result == now + timedelta(seconds=42) * factor def test_create(self) -> None: check = XPathDeltaWakeup.create( "name", config_section( { "xpath": "/valid", "url": "nourl", "timeout": "20", "unit": "weeks", } ), ) assert check._unit == "weeks" def test_create_wrong_unit(self) -> None: with pytest.raises(ConfigurationError): XPathDeltaWakeup.create( "name", config_section( { "xpath": "/valid", "url": "nourl", "timeout": "20", "unit": "unknown", } ), ) def test_init_wrong_unit(self) -> None: with pytest.raises(ValueError, match=r".*unit.*"): XPathDeltaWakeup( "name", url="url", xpath="/a", timeout=5, unit="unknownunit" ) autosuspend-7.2.0/tests/test_checks_xpath/000077500000000000000000000000001475670032600207515ustar00rootroot00000000000000autosuspend-7.2.0/tests/test_checks_xpath/xml_with_encoding.xml000066400000000000000000000034541475670032600252020ustar00rootroot00000000000000 autosuspend-7.2.0/tests/test_integration.py000066400000000000000000000152561475670032600212130ustar00rootroot00000000000000from collections.abc import Iterable from datetime import datetime, timedelta, timezone import logging from pathlib import Path from typing import Any from freezegun import freeze_time import pytest from pytest_mock import MockerFixture import autosuspend pytestmark = pytest.mark.integration SUSPENSION_FILE = "would_suspend" SCHEDULED_FILE = "wakeup_at" WOKE_UP_FILE = "test-woke-up" LOCK_FILE = "test-woke-up.lock" NOTIFY_FILE = "notify" def configure_config(config: str, datadir: Path, tmp_path: Path) -> Path: out_path = tmp_path / config with out_path.open("w") as out_config: out_config.write( (datadir / config).read_text().replace("@TMPDIR@", str(tmp_path)), ) return out_path @pytest.fixture def _rapid_sleep(mocker: MockerFixture) -> Iterable[None]: with freeze_time() as frozen_time: sleep_mock = mocker.patch("time.sleep") sleep_mock.side_effect = lambda seconds: frozen_time.tick( timedelta(seconds=seconds) ) yield @pytest.mark.usefixtures("_rapid_sleep") def test_no_suspend_if_matching(datadir: Path, tmp_path: Path) -> None: autosuspend.main( [ "-c", str(configure_config("dont_suspend.conf", datadir, tmp_path)), "-d", "daemon", "-r", "10", ] ) assert not (tmp_path / SUSPENSION_FILE).exists() @pytest.mark.usefixtures("_rapid_sleep") def test_suspend(tmp_path: Path, datadir: Path) -> None: autosuspend.main( [ "-c", str(configure_config("would_suspend.conf", datadir, tmp_path)), "-d", "daemon", "-r", "10", ] ) assert (tmp_path / SUSPENSION_FILE).exists() @pytest.mark.usefixtures("_rapid_sleep") def test_wakeup_scheduled(tmp_path: Path, datadir: Path) -> None: # configure when to wake up now = datetime.now(timezone.utc) wakeup_at = now + timedelta(hours=4) (tmp_path / "wakeup_time").write_text(str(wakeup_at.timestamp())) autosuspend.main( [ "-c", str(configure_config("would_schedule.conf", datadir, tmp_path)), "-d", "daemon", "-r", "10", ] ) assert (tmp_path / SUSPENSION_FILE).exists() assert (tmp_path / SCHEDULED_FILE).exists() assert int((tmp_path / SCHEDULED_FILE).read_text()) == int( round((wakeup_at - timedelta(seconds=30)).timestamp()) ) @pytest.mark.usefixtures("_rapid_sleep") def test_woke_up_file_removed(tmp_path: Path, datadir: Path) -> None: (tmp_path / WOKE_UP_FILE).touch() autosuspend.main( [ "-c", str(configure_config("dont_suspend.conf", datadir, tmp_path)), "-d", "daemon", "-r", "5", ] ) assert not (tmp_path / WOKE_UP_FILE).exists() @pytest.mark.usefixtures("_rapid_sleep") def test_notify_call(tmp_path: Path, datadir: Path) -> None: autosuspend.main( [ "-c", str(configure_config("notify.conf", datadir, tmp_path)), "-d", "daemon", "-r", "10", ] ) assert (tmp_path / SUSPENSION_FILE).exists() assert (tmp_path / NOTIFY_FILE).exists() assert len((tmp_path / NOTIFY_FILE).read_text()) == 0 @pytest.mark.usefixtures("_rapid_sleep") def test_notify_call_wakeup(tmp_path: Path, datadir: Path) -> None: # configure when to wake up now = datetime.now(timezone.utc) wakeup_at = now + timedelta(hours=4) (tmp_path / "wakeup_time").write_text(str(wakeup_at.timestamp())) autosuspend.main( [ "-c", str(configure_config("notify_wakeup.conf", datadir, tmp_path)), "-d", "daemon", "-r", "10", ] ) assert (tmp_path / SUSPENSION_FILE).exists() assert (tmp_path / NOTIFY_FILE).exists() assert int((tmp_path / NOTIFY_FILE).read_text()) == int( round((wakeup_at - timedelta(seconds=10)).timestamp()) ) def test_error_no_checks_configured(tmp_path: Path, datadir: Path) -> None: with pytest.raises(autosuspend.ConfigurationError): autosuspend.main( [ "-c", str(configure_config("no_checks.conf", datadir, tmp_path)), "-d", "daemon", "-r", "10", ] ) @pytest.mark.usefixtures("_rapid_sleep") def test_temporary_errors_logged(tmp_path: Path, datadir: Path, caplog: Any) -> None: autosuspend.main( [ "-c", str(configure_config("temporary_error.conf", datadir, tmp_path)), "-d", "daemon", "-r", "10", ] ) warnings = [ r for r in caplog.record_tuples if r[1] == logging.WARNING and "XPath" in r[2] and "failed" in r[2] ] assert len(warnings) > 0 def test_loop_defaults(tmp_path: Path, datadir: Path, mocker: MockerFixture) -> None: loop = mocker.patch("autosuspend.loop") loop.side_effect = StopIteration with pytest.raises(StopIteration): autosuspend.main( [ "-c", str(configure_config("minimal.conf", datadir, tmp_path)), "-d", "daemon", "-r", "10", ] ) args, kwargs = loop.call_args assert args[1] == 60 assert kwargs["run_for"] == 10 assert kwargs["woke_up_file"] == Path("/var/run/autosuspend-just-woke-up") def test_hook_success(tmp_path: Path, datadir: Path) -> None: autosuspend.main( [ "-c", str(configure_config("would_suspend.conf", datadir, tmp_path)), "-d", "presuspend", ] ) assert (tmp_path / WOKE_UP_FILE).exists() def test_hook_call_wakeup(tmp_path: Path, datadir: Path) -> None: # configure when to wake up now = datetime.now(timezone.utc) wakeup_at = now + timedelta(hours=4) (tmp_path / "wakeup_time").write_text(str(wakeup_at.timestamp())) autosuspend.main( [ "-c", str(configure_config("would_schedule.conf", datadir, tmp_path)), "-d", "presuspend", ] ) assert (tmp_path / SCHEDULED_FILE).exists() assert int((tmp_path / SCHEDULED_FILE).read_text()) == int( round((wakeup_at - timedelta(seconds=30)).timestamp()) ) def test_version(tmp_path: Path, datadir: Path) -> None: autosuspend.main( [ "-c", str(configure_config("would_schedule.conf", datadir, tmp_path)), "version", ] ) autosuspend-7.2.0/tests/test_integration/000077500000000000000000000000001475670032600206305ustar00rootroot00000000000000autosuspend-7.2.0/tests/test_integration/dont_suspend.conf000066400000000000000000000004101475670032600241770ustar00rootroot00000000000000[general] interval = 2 idle_time = 5 suspend_cmd = touch @TMPDIR@/would_suspend wakeup_cmd = echo {timestamp:d} > @TMPDIR@/wakeup_at woke_up_file = @TMPDIR@/test-woke-up lock_file = @TMPDIR@/test-woke-up.lock [check.ExternalCommand] enabled = True command = true autosuspend-7.2.0/tests/test_integration/minimal.conf000066400000000000000000000002421475670032600231230ustar00rootroot00000000000000[general] suspend_cmd = touch @TMPDIR@/would_suspend wakeup_cmd = echo {timestamp:d} > @TMPDIR@/wakeup_at [check.ExternalCommand] enabled = True command = false autosuspend-7.2.0/tests/test_integration/no_checks.conf000066400000000000000000000003501475670032600234310ustar00rootroot00000000000000[general] interval = 2 idle_time = 5 suspend_cmd = touch @TMPDIR@/would_suspend wakeup_cmd = echo {timestamp:d} > @TMPDIR@/wakeup_at woke_up_file = @TMPDIR@/test-woke-up [check.ExternalCommand] # lacks enabled=True command = false autosuspend-7.2.0/tests/test_integration/notify.conf000066400000000000000000000005631475670032600230130ustar00rootroot00000000000000[general] interval = 2 idle_time = 5 suspend_cmd = touch @TMPDIR@/would_suspend wakeup_cmd = echo {timestamp:.0f} > @TMPDIR@/wakeup_at notify_cmd_wakeup = echo {timestamp:.0f} > @TMPDIR@/notify notify_cmd_no_wakeup = touch @TMPDIR@/notify woke_up_file = @TMPDIR@/test-woke-up lock_file = @TMPDIR@/test-woke-up.lock [check.ExternalCommand] enabled = True command = false autosuspend-7.2.0/tests/test_integration/notify_wakeup.conf000066400000000000000000000006771475670032600243750ustar00rootroot00000000000000[general] interval = 2 idle_time = 5 suspend_cmd = touch @TMPDIR@/would_suspend wakeup_cmd = echo {timestamp:.0f} > @TMPDIR@/wakeup_at notify_cmd_wakeup = echo {timestamp:.0f} > @TMPDIR@/notify notify_cmd_no_wakeup = touch @TMPDIR@/notify woke_up_file = @TMPDIR@/test-woke-up lock_file = @TMPDIR@/test-woke-up.lock wakeup_delta = 10 [check.ExternalCommand] enabled = True command = false [wakeup.File] enabled = True path = @TMPDIR@/wakeup_time autosuspend-7.2.0/tests/test_integration/temporary_error.conf000066400000000000000000000004351475670032600247340ustar00rootroot00000000000000[general] interval = 20 idle_time = 50 suspend_cmd = touch @TMPDIR@/would_suspend wakeup_cmd = echo {timestamp:d} > @TMPDIR@/wakeup_at woke_up_file = @TMPDIR@/test-woke-up lock_file = @TMPDIR@/test-woke-up.lock [check.XPath] enabled = True xpath = /a url = asdfjlkasdjkfkasdlfjaklsdf autosuspend-7.2.0/tests/test_integration/would_schedule.conf000066400000000000000000000005051475670032600245050ustar00rootroot00000000000000[general] interval = 2 idle_time = 5 suspend_cmd = touch @TMPDIR@/would_suspend wakeup_cmd = echo {timestamp:.0f} > @TMPDIR@/wakeup_at woke_up_file = @TMPDIR@/test-woke-up lock_file = @TMPDIR@/test-woke-up.lock [check.ExternalCommand] enabled = True command = false [wakeup.File] enabled = True path = @TMPDIR@/wakeup_time autosuspend-7.2.0/tests/test_integration/would_suspend.conf000066400000000000000000000004111475670032600243660ustar00rootroot00000000000000[general] interval = 2 idle_time = 5 suspend_cmd = touch @TMPDIR@/would_suspend wakeup_cmd = echo {timestamp:d} > @TMPDIR@/wakeup_at woke_up_file = @TMPDIR@/test-woke-up lock_file = @TMPDIR@/test-woke-up.lock [check.ExternalCommand] enabled = True command = false autosuspend-7.2.0/tests/test_util.py000066400000000000000000000015761475670032600176450ustar00rootroot00000000000000from autosuspend.util import logger_by_class, logger_by_class_instance class DummyClass: pass class TestLoggerByClass: def test_smoke(self) -> None: logger = logger_by_class(DummyClass) assert logger is not None assert logger.name == "tests.test_util.DummyClass" def test_name(self) -> None: logger = logger_by_class(DummyClass, "foo") assert logger is not None assert logger.name == "tests.test_util.DummyClass.foo" class TestLoggerByClassInstance: def test_smoke(self) -> None: logger = logger_by_class_instance(DummyClass()) assert logger is not None assert logger.name == "tests.test_util.DummyClass" def test_name(self) -> None: logger = logger_by_class_instance(DummyClass(), "foo") assert logger is not None assert logger.name == "tests.test_util.DummyClass.foo" autosuspend-7.2.0/tests/test_util_systemd.py000066400000000000000000000011211475670032600213770ustar00rootroot00000000000000from dbus.proxies import ProxyObject import pytest from autosuspend.util.systemd import list_logind_sessions, LogindDBusException def test_list_logind_sessions_empty(logind: ProxyObject) -> None: assert len(list(list_logind_sessions())) == 0 logind.AddSession("c1", "seat0", 1042, "auser", True) sessions = list(list_logind_sessions()) assert len(sessions) == 1 assert sessions[0][0] == "c1" @pytest.mark.usefixtures("_logind_dbus_error") def test_list_logind_sessions_dbus_error() -> None: with pytest.raises(LogindDBusException): list_logind_sessions() autosuspend-7.2.0/tests/utils.py000066400000000000000000000004701475670032600167610ustar00rootroot00000000000000from collections.abc import Mapping import configparser def config_section( entries: Mapping[str, str] | None = None, ) -> configparser.SectionProxy: parser = configparser.ConfigParser() section_name = "a_section" parser.read_dict({section_name: entries or {}}) return parser[section_name] autosuspend-7.2.0/tox.ini000066400000000000000000000041461475670032600154240ustar00rootroot00000000000000[tox] envlist = coverage-clean,test-py310-psutil59-dateutil28-tzlocal2, test-py{310,311,312,313}-psutillatest-dateutillatest-tzlocal{4,latest}, integration-py{310,311,312,313}, mindeps, check, docs, coverage [testenv] extras = test setenv = COVERAGE_FILE = ./.coverage.{envname} deps = psutil59: psutil>=5.9,<5.10 psutillatest: psutil dateutil28: python-dateutil>=2.8,<2.9 dateutillatest: python-dateutil tzlocal2: tzlocal<3 tzlocal4: tzlocal>3,<5 tzlocallatest: tzlocal>4 commands = {envbindir}/python -V {envbindir}/python -c 'import psutil; print(psutil.__version__)' {envbindir}/python -c 'import dateutil; print(dateutil.__version__)' test: {envbindir}/pytest --cov -m "not integration" {posargs} integration: {envbindir}/pytest --cov -m "integration" {posargs} depends = coverage-clean [testenv:coverage-clean] deps = coverage skip_install = true commands = coverage erase depends = [testenv:coverage] depends = test-py310-psutil{59,latest}-dateutil{28,latest}, test-py{310,311,312,313}-psutillatest-dateutillatest, integration-py{310,311,312,313} deps = coverage skip_install = true setenv = commands = - coverage combine {envbindir}/coverage html {envbindir}/coverage report [testenv:mindeps] description = tests whether the project can be used without any extras extras = deps = depends = commands = {envbindir}/python -V {envbindir}/python -c "import autosuspend; import autosuspend.checks.activity; import autosuspend.checks.wakeup" {envbindir}/autosuspend -c tests/data/mindeps-test.conf daemon -r 1 [testenv:check] depends = deps = -rrequirements-check.txt commands = {envbindir}/python -V {envbindir}/ruff check src tests {envbindir}/isort --check src tests {envbindir}/black --check src tests {envbindir}/mypy src tests [testenv:docs] basepython = python3.13 depends = deps = -rrequirements-doc.txt commands = {envbindir}/sphinx-build -W -b html -d {envtmpdir}/doctrees doc/source {envtmpdir}/html [gh-actions] python = 3.10: py310, coverage 3.11: py311, coverage 3.12: py312, coverage 3.13: py313, coverage