pax_global_header 0000666 0000000 0000000 00000000064 14756700326 0014524 g ustar 00root root 0000000 0000000 52 comment=8c35af88ac6b9488d5d3298e15ee18757289486b
autosuspend-7.2.0/ 0000775 0000000 0000000 00000000000 14756700326 0014104 5 ustar 00root root 0000000 0000000 autosuspend-7.2.0/.editorconfig 0000664 0000000 0000000 00000000251 14756700326 0016557 0 ustar 00root root 0000000 0000000 root = 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/ 0000775 0000000 0000000 00000000000 14756700326 0015444 5 ustar 00root root 0000000 0000000 autosuspend-7.2.0/.github/workflows/ 0000775 0000000 0000000 00000000000 14756700326 0017501 5 ustar 00root root 0000000 0000000 autosuspend-7.2.0/.github/workflows/ci.yml 0000664 0000000 0000000 00000011242 14756700326 0020617 0 ustar 00root root 0000000 0000000 name: 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/.gitignore 0000664 0000000 0000000 00000000322 14756700326 0016071 0 ustar 00root root 0000000 0000000 /.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.yaml 0000664 0000000 0000000 00000000320 14756700326 0017326 0 ustar 00root root 0000000 0000000 ---
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.json 0000664 0000000 0000000 00000001401 14756700326 0017016 0 ustar 00root root 0000000 0000000 {
"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.txt 0000664 0000000 0000000 00000035562 14756700326 0015742 0 ustar 00root root 0000000 0000000 GNU 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.in 0000664 0000000 0000000 00000000020 14756700326 0015632 0 ustar 00root root 0000000 0000000 include VERSION
autosuspend-7.2.0/README.md 0000664 0000000 0000000 00000002003 14756700326 0015356 0 ustar 00root root 0000000 0000000 # autosuspend
[](https://github.com/languitar/autosuspend/actions) [](https://codecov.io/gh/languitar/autosuspend) [](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
[](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/VERSION 0000664 0000000 0000000 00000000012 14756700326 0015145 0 ustar 00root root 0000000 0000000 7.2
7.2.0
autosuspend-7.2.0/commitlint.config.js 0000664 0000000 0000000 00000000101 14756700326 0020055 0 ustar 00root root 0000000 0000000 module.exports = {extends: ['@commitlint/config-conventional']};
autosuspend-7.2.0/cosmic-ray.toml 0000664 0000000 0000000 00000000427 14756700326 0017052 0 ustar 00root root 0000000 0000000 [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/ 0000775 0000000 0000000 00000000000 14756700326 0015015 5 ustar 00root root 0000000 0000000 autosuspend-7.2.0/data/autosuspend-detect-suspend.service 0000664 0000000 0000000 00000000444 14756700326 0023700 0 ustar 00root root 0000000 0000000 [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.conf 0000664 0000000 0000000 00000001033 14756700326 0021657 0 ustar 00root root 0000000 0000000 [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.conf 0000664 0000000 0000000 00000003401 14756700326 0020234 0 ustar 00root root 0000000 0000000 ## 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.service 0000664 0000000 0000000 00000000522 14756700326 0020750 0 ustar 00root root 0000000 0000000 [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/ 0000775 0000000 0000000 00000000000 14756700326 0014651 5 ustar 00root root 0000000 0000000 autosuspend-7.2.0/doc/source/ 0000775 0000000 0000000 00000000000 14756700326 0016151 5 ustar 00root root 0000000 0000000 autosuspend-7.2.0/doc/source/api.rst 0000664 0000000 0000000 00000000442 14756700326 0017454 0 ustar 00root root 0000000 0000000 Python 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.rst 0000664 0000000 0000000 00000036572 14756700326 0022160 0 ustar 00root root 0000000 0000000 .. _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.rst 0000664 0000000 0000000 00000012067 14756700326 0022370 0 ustar 00root root 0000000 0000000 .. _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.rst 0000664 0000000 0000000 00000000464 14756700326 0020636 0 ustar 00root root 0000000 0000000 Changelog
#########
|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.py 0000664 0000000 0000000 00000006307 14756700326 0017456 0 ustar 00root root 0000000 0000000 #!/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.inc 0000664 0000000 0000000 00000013005 14756700326 0022511 0 ustar 00root root 0000000 0000000 Syntax
~~~~~~
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.rst 0000664 0000000 0000000 00000000264 14756700326 0022553 0 ustar 00root root 0000000 0000000 Configuration 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.rst 0000664 0000000 0000000 00000002611 14756700326 0020636 0 ustar 00root root 0000000 0000000 Debugging
#########
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.inc 0000664 0000000 0000000 00000001656 14756700326 0021177 0 ustar 00root root 0000000 0000000 |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.rst 0000664 0000000 0000000 00000001753 14756700326 0025714 0 ustar 00root root 0000000 0000000 .. _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.rst 0000664 0000000 0000000 00000011005 14756700326 0017447 0 ustar 00root root 0000000 0000000 .. _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.md 0000664 0000000 0000000 00000015740 14756700326 0022447 0 ustar 00root root 0000000 0000000 # [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.rst 0000664 0000000 0000000 00000003343 14756700326 0020015 0 ustar 00root root 0000000 0000000 |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.rst 0000664 0000000 0000000 00000005322 14756700326 0021406 0 ustar 00root root 0000000 0000000 .. _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.rst 0000664 0000000 0000000 00000001330 14756700326 0021151 0 ustar 00root root 0000000 0000000 :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.rst 0000664 0000000 0000000 00000000574 14756700326 0021011 0 ustar 00root root 0000000 0000000 :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.rst 0000664 0000000 0000000 00000014017 14756700326 0021473 0 ustar 00root root 0000000 0000000 3.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.rst 0000664 0000000 0000000 00000003140 14756700326 0020374 0 ustar 00root root 0000000 0000000 Command 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.rst 0000664 0000000 0000000 00000000637 14756700326 0020425 0 ustar 00root root 0000000 0000000 Support 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.rst 0000664 0000000 0000000 00000002222 14756700326 0022774 0 ustar 00root root 0000000 0000000 .. _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.json 0000664 0000000 0000000 00001537275 14756700326 0017344 0 ustar 00root root 0000000 0000000 {
"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.json 0000664 0000000 0000000 00000000414 14756700326 0016371 0 ustar 00root root 0000000 0000000 {
"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.toml 0000664 0000000 0000000 00000003362 14756700326 0017024 0 ustar 00root root 0000000 0000000 [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.json 0000664 0000000 0000000 00000001115 14756700326 0016620 0 ustar 00root root 0000000 0000000 {
"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.txt 0000664 0000000 0000000 00000000202 14756700326 0020435 0 ustar 00root root 0000000 0000000 ruff==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.txt 0000664 0000000 0000000 00000000203 14756700326 0020126 0 ustar 00root root 0000000 0000000 sphinx-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.cfg 0000664 0000000 0000000 00000001732 14756700326 0015730 0 ustar 00root root 0000000 0000000 [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.py 0000664 0000000 0000000 00000003377 14756700326 0015630 0 ustar 00root root 0000000 0000000 from 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/ 0000775 0000000 0000000 00000000000 14756700326 0014673 5 ustar 00root root 0000000 0000000 autosuspend-7.2.0/src/autosuspend/ 0000775 0000000 0000000 00000000000 14756700326 0017245 5 ustar 00root root 0000000 0000000 autosuspend-7.2.0/src/autosuspend/__init__.py 0000775 0000000 0000000 00000062440 14756700326 0021367 0 ustar 00root root 0000000 0000000 #!/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/ 0000775 0000000 0000000 00000000000 14756700326 0020505 5 ustar 00root root 0000000 0000000 autosuspend-7.2.0/src/autosuspend/checks/__init__.py 0000664 0000000 0000000 00000006647 14756700326 0022633 0 ustar 00root root 0000000 0000000 """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.py 0000664 0000000 0000000 00000001520 14756700326 0022711 0 ustar 00root root 0000000 0000000 from 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.py 0000664 0000000 0000000 00000005251 14756700326 0022500 0 ustar 00root root 0000000 0000000 import 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.py 0000664 0000000 0000000 00000024737 14756700326 0022004 0 ustar 00root root 0000000 0000000 from 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.py 0000664 0000000 0000000 00000003157 14756700326 0022036 0 ustar 00root root 0000000 0000000 import 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.py 0000664 0000000 0000000 00000007251 14756700326 0022012 0 ustar 00root root 0000000 0000000 import 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.py 0000664 0000000 0000000 00000030404 14756700326 0022217 0 ustar 00root root 0000000 0000000 """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.py 0000664 0000000 0000000 00000007145 14756700326 0022032 0 ustar 00root root 0000000 0000000 from 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.py 0000664 0000000 0000000 00000002771 14756700326 0021646 0 ustar 00root root 0000000 0000000 import 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.py 0000664 0000000 0000000 00000002326 14756700326 0021643 0 ustar 00root root 0000000 0000000 import 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.py 0000664 0000000 0000000 00000001502 14756700326 0022032 0 ustar 00root root 0000000 0000000 import 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.py 0000664 0000000 0000000 00000011735 14756700326 0022556 0 ustar 00root root 0000000 0000000 from 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.py 0000664 0000000 0000000 00000007460 14756700326 0022043 0 ustar 00root root 0000000 0000000 import 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.py 0000664 0000000 0000000 00000000752 14756700326 0022357 0 ustar 00root root 0000000 0000000 from 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.py 0000664 0000000 0000000 00000017411 14756700326 0022042 0 ustar 00root root 0000000 0000000 from 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.py 0000664 0000000 0000000 00000010404 14756700326 0022202 0 ustar 00root root 0000000 0000000 from 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/ 0000775 0000000 0000000 00000000000 14756700326 0020222 5 ustar 00root root 0000000 0000000 autosuspend-7.2.0/src/autosuspend/util/__init__.py 0000664 0000000 0000000 00000000737 14756700326 0022342 0 ustar 00root root 0000000 0000000 import 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.py 0000664 0000000 0000000 00000000411 14756700326 0022364 0 ustar 00root root 0000000 0000000 from 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.py 0000664 0000000 0000000 00000002524 14756700326 0022267 0 ustar 00root root 0000000 0000000 from 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/ 0000775 0000000 0000000 00000000000 14756700326 0015246 5 ustar 00root root 0000000 0000000 autosuspend-7.2.0/tests/__init__.py 0000664 0000000 0000000 00000000464 14756700326 0017363 0 ustar 00root root 0000000 0000000 import 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.py 0000664 0000000 0000000 00000005510 14756700326 0017446 0 ustar 00root root 0000000 0000000 from 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/ 0000775 0000000 0000000 00000000000 14756700326 0016157 5 ustar 00root root 0000000 0000000 autosuspend-7.2.0/tests/data/mindeps-test.conf 0000664 0000000 0000000 00000000441 14756700326 0021441 0 ustar 00root root 0000000 0000000 [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.py 0000664 0000000 0000000 00000057051 14756700326 0021241 0 ustar 00root root 0000000 0000000 import 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.py 0000664 0000000 0000000 00000001224 14756700326 0020116 0 ustar 00root root 0000000 0000000 import 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.py 0000664 0000000 0000000 00000001226 14756700326 0022034 0 ustar 00root root 0000000 0000000 import 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.py 0000664 0000000 0000000 00000010506 14756700326 0021617 0 ustar 00root root 0000000 0000000 from 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.py 0000664 0000000 0000000 00000052343 14756700326 0021116 0 ustar 00root root 0000000 0000000 from 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/ 0000775 0000000 0000000 00000000000 14756700326 0020535 5 ustar 00root root 0000000 0000000 autosuspend-7.2.0/tests/test_checks_ical/after-horizon.ics 0000664 0000000 0000000 00000001273 14756700326 0024027 0 ustar 00root root 0000000 0000000 BEGIN: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.ics 0000664 0000000 0000000 00000002426 14756700326 0024066 0 ustar 00root root 0000000 0000000 BEGIN: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.ics 0000664 0000000 0000000 00000000605 14756700326 0026751 0 ustar 00root root 0000000 0000000 BEGIN: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.ics 0000664 0000000 0000000 00000000564 14756700326 0024563 0 ustar 00root root 0000000 0000000 BEGIN: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.ics 0000664 0000000 0000000 00000001316 14756700326 0024077 0 ustar 00root root 0000000 0000000 BEGIN: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.ics 0000664 0000000 0000000 00000001273 14756700326 0024170 0 ustar 00root root 0000000 0000000 BEGIN: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.ics 0000664 0000000 0000000 00000001415 14756700326 0023432 0 ustar 00root root 0000000 0000000 BEGIN: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.ics 0000664 0000000 0000000 00000001167 14756700326 0023045 0 ustar 00root root 0000000 0000000 BEGIN: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.ics 0000664 0000000 0000000 00000001401 14756700326 0022603 0 ustar 00root root 0000000 0000000 BEGIN: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.ics 0000664 0000000 0000000 00000001273 14756700326 0023316 0 ustar 00root root 0000000 0000000 BEGIN: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.ics 0000664 0000000 0000000 00000001716 14756700326 0023075 0 ustar 00root root 0000000 0000000 BEGIN: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.ics 0000664 0000000 0000000 00000005315 14756700326 0026415 0 ustar 00root root 0000000 0000000 BEGIN: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.ics 0000664 0000000 0000000 00000001273 14756700326 0023135 0 ustar 00root root 0000000 0000000 BEGIN: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.ics 0000664 0000000 0000000 00000002640 14756700326 0025252 0 ustar 00root root 0000000 0000000 BEGIN: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.ics 0000664 0000000 0000000 00000001365 14756700326 0024531 0 ustar 00root root 0000000 0000000 BEGIN: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.ics 0000664 0000000 0000000 00000002610 14756700326 0023740 0 ustar 00root root 0000000 0000000 BEGIN: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.py 0000664 0000000 0000000 00000011021 14756700326 0021143 0 ustar 00root root 0000000 0000000 from 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/ 0000775 0000000 0000000 00000000000 14756700326 0020576 5 ustar 00root root 0000000 0000000 autosuspend-7.2.0/tests/test_checks_json/invalid.json 0000664 0000000 0000000 00000000011 14756700326 0023107 0 ustar 00root root 0000000 0000000 {"broken
autosuspend-7.2.0/tests/test_checks_kodi.py 0000664 0000000 0000000 00000017736 14756700326 0021143 0 ustar 00root root 0000000 0000000 import 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.py 0000664 0000000 0000000 00000051221 14756700326 0021337 0 ustar 00root root 0000000 0000000 from 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.py 0000664 0000000 0000000 00000021351 14756700326 0021145 0 ustar 00root root 0000000 0000000 from 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.py 0000664 0000000 0000000 00000006656 14756700326 0020774 0 ustar 00root root 0000000 0000000 from 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.py 0000664 0000000 0000000 00000002776 14756700326 0020774 0 ustar 00root root 0000000 0000000 from 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/ 0000775 0000000 0000000 00000000000 14756700326 0020406 5 ustar 00root root 0000000 0000000 autosuspend-7.2.0/tests/test_checks_smb/smbstatus_no_connections 0000664 0000000 0000000 00000000447 14756700326 0025461 0 ustar 00root root 0000000 0000000
Samba version 4.7.0
PID Username Group Machine Protocol Version Encryption Signing
----------------------------------------------------------------------------------------------------------------------------------------
autosuspend-7.2.0/tests/test_checks_smb/smbstatus_with_connections 0000664 0000000 0000000 00000000421 14756700326 0026010 0 ustar 00root root 0000000 0000000
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.py 0000664 0000000 0000000 00000002775 14756700326 0021167 0 ustar 00root root 0000000 0000000 from 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.py 0000664 0000000 0000000 00000011326 14756700326 0021672 0 ustar 00root root 0000000 0000000 from 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.py 0000664 0000000 0000000 00000007422 14756700326 0021161 0 ustar 00root root 0000000 0000000 from 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/ 0000775 0000000 0000000 00000000000 14756700326 0020602 5 ustar 00root root 0000000 0000000 autosuspend-7.2.0/tests/test_checks_util/data.txt 0000664 0000000 0000000 00000000010 14756700326 0022243 0 ustar 00root root 0000000 0000000 iamhere
autosuspend-7.2.0/tests/test_checks_util/xml_with_encoding.xml 0000664 0000000 0000000 00000003454 14756700326 0025033 0 ustar 00root root 0000000 0000000
autosuspend-7.2.0/tests/test_checks_wakeup.py 0000664 0000000 0000000 00000000651 14756700326 0021475 0 ustar 00root root 0000000 0000000 import 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.py 0000664 0000000 0000000 00000020225 14756700326 0021157 0 ustar 00root root 0000000 0000000 from 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.py 0000664 0000000 0000000 00000024763 14756700326 0021337 0 ustar 00root root 0000000 0000000 from 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/ 0000775 0000000 0000000 00000000000 14756700326 0020751 5 ustar 00root root 0000000 0000000 autosuspend-7.2.0/tests/test_checks_xpath/xml_with_encoding.xml 0000664 0000000 0000000 00000003454 14756700326 0025202 0 ustar 00root root 0000000 0000000
autosuspend-7.2.0/tests/test_integration.py 0000664 0000000 0000000 00000015256 14756700326 0021213 0 ustar 00root root 0000000 0000000 from 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/ 0000775 0000000 0000000 00000000000 14756700326 0020630 5 ustar 00root root 0000000 0000000 autosuspend-7.2.0/tests/test_integration/dont_suspend.conf 0000664 0000000 0000000 00000000410 14756700326 0024177 0 ustar 00root root 0000000 0000000 [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.conf 0000664 0000000 0000000 00000000242 14756700326 0023123 0 ustar 00root root 0000000 0000000 [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.conf 0000664 0000000 0000000 00000000350 14756700326 0023431 0 ustar 00root root 0000000 0000000 [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.conf 0000664 0000000 0000000 00000000563 14756700326 0023013 0 ustar 00root root 0000000 0000000 [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.conf 0000664 0000000 0000000 00000000677 14756700326 0024375 0 ustar 00root root 0000000 0000000 [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.conf 0000664 0000000 0000000 00000000435 14756700326 0024734 0 ustar 00root root 0000000 0000000 [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.conf 0000664 0000000 0000000 00000000505 14756700326 0024505 0 ustar 00root root 0000000 0000000 [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.conf 0000664 0000000 0000000 00000000411 14756700326 0024366 0 ustar 00root root 0000000 0000000 [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.py 0000664 0000000 0000000 00000001576 14756700326 0017645 0 ustar 00root root 0000000 0000000 from 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.py 0000664 0000000 0000000 00000001121 14756700326 0021377 0 ustar 00root root 0000000 0000000 from 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.py 0000664 0000000 0000000 00000000470 14756700326 0016761 0 ustar 00root root 0000000 0000000 from 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.ini 0000664 0000000 0000000 00000004146 14756700326 0015424 0 ustar 00root root 0000000 0000000 [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