From 331cff9b3b1bd077676f3cd7b587f69914dd2161 Mon Sep 17 00:00:00 2001 From: NeoNexus DeMortis Date: Thu, 26 Oct 2023 13:46:56 -0500 Subject: [PATCH] Moving from `dzfg` -> `drfg`. --- .editorconfig | 31 + .eslintignore | 1 + .eslintrc | 35 + .github/FUNDING.yml | 1 + .github/workflows/codeql-analysis.yml | 69 + .gitignore | 1 + .idea/.gitignore | 5 + .idea/codeStyles/codeStyleConfig.xml | 5 + .idea/dzfg.iml | 14 + .idea/jsLibraryMappings.xml | 6 + .idea/modules.xml | 8 + .idea/runConfigurations/Run_Tests.xml | 14 + .idea/vcs.xml | 6 + .travis.yml | 6 + LICENSE | 674 +++++++++ README.md | 252 ++++ example-update.png | Bin 0 -> 32498 bytes license.svg | 26 + package-lock.json | 1961 +++++++++++++++++++++++++ package.json | 62 + src/bin.js | 104 ++ src/lib.js | 275 ++++ src/utilities.js | 125 ++ test/.eslintrc | 6 + test/index.test.js | 148 ++ 25 files changed, 3835 insertions(+) create mode 100644 .editorconfig create mode 100644 .eslintignore create mode 100644 .eslintrc create mode 100644 .github/FUNDING.yml create mode 100644 .github/workflows/codeql-analysis.yml create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/codeStyles/codeStyleConfig.xml create mode 100644 .idea/dzfg.iml create mode 100644 .idea/jsLibraryMappings.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/runConfigurations/Run_Tests.xml create mode 100644 .idea/vcs.xml create mode 100644 .travis.yml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 example-update.png create mode 100644 license.svg create mode 100644 package-lock.json create mode 100644 package.json create mode 100755 src/bin.js create mode 100644 src/lib.js create mode 100644 src/utilities.js create mode 100644 test/.eslintrc create mode 100644 test/index.test.js diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..37f9649 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,31 @@ +################################################ +# ╔═╗╔╦╗╦╔╦╗╔═╗╦═╗┌─┐┌─┐┌┐┌┌─┐┬┌─┐ +# ║╣ ║║║ ║ ║ ║╠╦╝│ │ ││││├┤ ││ ┬ +# o╚═╝═╩╝╩ ╩ ╚═╝╩╚═└─┘└─┘┘└┘└ ┴└─┘ +# +# > Formatting conventions for your Sails app. +# +# This file (`.editorconfig`) exists to help +# maintain consistent formatting throughout the +# files in your Sails app. +# +# For the sake of convention, the Sails team's +# preferred settings are included here out of the +# box. You can also change this file to fit your +# team's preferences (for example, if all of the +# developers on your team have a strong preference +# for tabs over spaces), +# +# To review what each of these options mean, see: +# http://editorconfig.org/ +# +################################################ +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..f109da4 --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +test/coverage/**/* diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..8064474 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,35 @@ +{ + "env": { + "node": true, + "es6": true + }, + "extends": "eslint:recommended", + "overrides": [ + ], + "parserOptions": { + "ecmaVersion": "latest" + }, + "rules": { + "indent": [ + "error", + 4 + ], + "linebreak-style": [ + "error", + "unix" + ], + "no-control-regex": "off", + "quotes": [ + "error", + "single" + ], + "semi": [ + "error", + "always" + ], + "semi-style": [ + "warn", + "last" + ] + } +} diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..cfaa49c --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: neonexus diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000..4eee81e --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,69 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +name: "CodeQL" + +on: + push: + branches: [ "release", "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "release", "master" ] + schedule: + - cron: '42 4 * * 3' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + timeout-minutes: 360 + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'javascript' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] + # Learn more: + # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + # - name: Autobuild + # uses: github/codeql-action/autobuild@v1 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..b58b603 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,5 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..a55e7a1 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/dzfg.iml b/.idea/dzfg.iml new file mode 100644 index 0000000..ecadd0e --- /dev/null +++ b/.idea/dzfg.iml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/jsLibraryMappings.xml b/.idea/jsLibraryMappings.xml new file mode 100644 index 0000000..d23208f --- /dev/null +++ b/.idea/jsLibraryMappings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..9b7ddb6 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/Run_Tests.xml b/.idea/runConfigurations/Run_Tests.xml new file mode 100644 index 0000000..e96c7ce --- /dev/null +++ b/.idea/runConfigurations/Run_Tests.xml @@ -0,0 +1,14 @@ + + + project + + $PROJECT_DIR$/node_modules/mocha + $PROJECT_DIR$ + true + bdd + + TEST_FILE + $PROJECT_DIR$/test/index.test.js + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..54adc71 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,6 @@ +dist: jammy +language: node_js +node_js: node +cache: + directories: [ node_modules ] +install: npm install diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. 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 +them 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 prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. 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. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey 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; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + 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. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +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. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md new file mode 100644 index 0000000..b41f630 --- /dev/null +++ b/README.md @@ -0,0 +1,252 @@ +# drfg - Download Release From GitHub + +[![npm](https://img.shields.io/npm/dm/drfg?logo=npm&style=plastic)](https://www.npmjs.com/package/drfg) +[![Build Status](https://img.shields.io/travis/com/neonexus/drfg/release?style=plastic&logo=travis)](https://app.travis-ci.com/neonexus/drfg) +[![NPM version](https://img.shields.io/npm/v/drfg/latest?style=plastic&logo=npm&color=blue)](https://www.npmjs.com/package/drfg) +[![GitHub version](https://img.shields.io/github/v/release/neonexus/drfg?style=plastic&logo=github&label=GitHub@latest)](https://github.com/neonexus/drfg) +[](LICENSE) + +The idea of this package, is to make downloading / extracting / installing a GitHub repo's latest release a breeze (without creating a new repo, or using Git for that matter). Both in the terminal, and programmatically, if you are into that kind of thing... + +`npx drfg ` + +## This works for ALMOST any GitHub repo... + +As long as the repository is using [GitHub's releases](https://docs.github.com/en/repositories/releasing-projects-on-github/about-releases) feature, this script will work. + +If a call to `https://api.github.com/repos/{username/repo}/releases/latest` returns a `tag_name` and `zipball_url`, then the script will download the zipball from `zipball_url`. + +Additionally, if the repo contains a `package.json` after extraction, `npm install` will be run automatically, unless disabled. See [advanced usage](#a-little-more-advanced) for more. + +## Table of Contents + +* [Terminal Usage](#terminal-usage) + * [Simple Usage](#simple-usage) + * [A Little More Advanced](#a-little-more-advanced) +* [Installing `drfg` Globally](#installing-drfg-globally) + * [Additional Script Names](#additional-script-names) +* [Programmatic Usage](#programmatic-usage) + * [Advanced Usage](#advanced-usage) + * [Getting Version Info for a Repo](#getting-version-info-for-a-repo) + * [Don't Forget to Handle Errors...](#dont-forget-to-handle-errors) + +## Terminal Usage + +### Simple Usage + +`npx drfg ` + +OR + +`npx drfg ` + +For example, if you wanted to "clone" (download the zipball, and extract it) the repo [sails-react-bootstrap-webpack](https://github.com/neonexus/sails-react-bootstrap-webpack), just do something like: + +```shell +npx drfg neonexus/sails-react-bootstrap-webpack +``` + +This will download / extract / `npm install` the repo into a new folder `sails-react-bootstrap-webpack`. To change where the files get extracted to, add a second parameter to the command: + +```shell +npx drfg neonexus/sails-react-bootstrap-webpack my-new-site +```` + +This will extract into `my-new-site`, instead of the repo name. + +### A Little More Advanced + +If the repo contains a `package.json` in the root, but you don't want to `npm install` after extraction, just add `no-npm`: + +`npx drfg ` + +**OR** + +`npx drfg ` + +You can also provide a specific version to download / extract: + +`npx drfg ` + +**NOTE:** When supplying a version, you **MUST** supply a folder name. + +This will download `v4.2.3`, into the folder `new-site`, and will skip the `npm install` step: +```shell +npx drfg neonexus/sails-react-bootstrap-webpack new-site v4.2.3 no-npm +``` + +## Installing `drfg` Globally + +```shell +npm i -g drfg +``` + +You can install `drfg` globally and run it directly (if you have your `$PATH` set correctly). This will save a small amount of time, as `npx` won't have to download and install, before running `drfg`. +However, this also means you won't always be using the most up-to-date version of `drfg` (GitHub may change their API, security issues may arise, etc.). + +To help ensure you are always up-to-date, `drfg` will use itself to check if there is an update, and if your current version doesn't match the latest version on GitHub, it will let you know. +This does **not** prevent you from using the version you already have installed, but it will ask you if you want to continue (you can simply hit enter). + +![Example Update](example-update.png) + +### Additional Script Names + +Once installed globally, you can use `drfg` directly, with one of its few different binary names: + +* `drfg` +* `dl-zip-from-gh` +* `download-zip-from-gh` +* `download-zip-from-github` +* `download-zipball-from-gh` +* `download-zipball-from-github` + +## Programmatic Usage + +Using `.then()`: + +```javascript +const drfg = require('drfg'); + +drfg.downloadAndExtract('username/my-repo', 'new-folder').then( + (downloadInfo) => {} +); + +// OR + +drfg.downloadAndExtract({ + repo: 'username/my-repo', + destinationFolder: 'my-clone' +}).then((downloadInfo) => {}); +``` + +Using `await`: +```javascript +const drfg = require('drfg'); + +const downloadInfo = await drfg.downloadAndExtract( + 'username/my-repo', + 'new-folder' +); + +// OR + +const downloadInfo = await drfg.downloadAndExtract({ + repo: 'username/my-repo', + destinationFolder: 'my-clone' +}); +``` + +`downloadInfo` will look something like: + +```json5 +{ + version: 'v1.0.1', + downloadTime: '714.82 ms', + extractionTime: '55.17 ms', + installationTime: '2.75 s', + totalTime: '3.61 s', + zipballSize: '45.11 KiB', + extractedSize: '146.73 KiB', + installedSize: '19.45 MiB' +} +``` + +### Advanced usage + +```javascript +const drfg = require('drfg'); + +// Parameters are: destination-folder, repo, version, skipNpmInstall +const downloadInfo = await drfg.downloadAndExtract( + 'username/my-repo', + 'new-folder', + 'v1.0.1', + true +); + +// OR +const downloadInfo = await drfg.downloadAndExtract({ + repo: 'username/my-repo', + version: 'v1.0.1', + skipInstall: true, + destinationFolder: 'my-clone' +}); +``` + +### Getting Version Info for a Repo + +```javascript +const drfg = require('drfg'); + +const latestVersionInfo = await drfg.getVersionInfo('username/my-repo'); + +// OR + +drfg.getVersionInfo('username/my-repo').then((latestVersionInfo) => { + // Do stuff with the info... +}); +``` + +`latestVersionInfo` will look something like: + +```json5 +{ + name: 'v1.0.1 (2023-10-01)', + description: 'The version description body. This will likely contain markdown.', + version: 'v1.0.1', + isDraft: false, + isPrerelease: false, + createdAt: '2023-10-01T04:19:00Z', + publishedAt: '2023-10-01T04:19:00Z', + + // `userSite` is the HTML URL for human use: + userSite: 'https://github.com/{username/repo}/releases/tag/v1.0.1', + + // `zipball` is the URL drfg will use internally to download the repo: + zipball: 'https://api.github.com/repos/{username/repo}/zipball/v1.0.1' +} +``` + +Get info for a specific version: + +```javascript +const drfg = require('drfg'); + +const latestVersionInfo = await drfg.getVersionInfo( + 'username/my-repo', + 'v1.0.1' +); +``` + +### Don't Forget to Handle Errors... + +```javascript +const drfg = require('drfg'); + +drfg.downloadAndExtract('username/my-repo', 'new-folder') + .then((successMessage) => {}) + .catch((e) => {}); + +drfg.getVersionInfo('username/my-repo') + .then((latestVersionInfo) => {}) + .catch((e) => {}); + +// OR + +try { + const downloadInfo = await drfg.downloadAndExtract( + 'username/my-repo', + 'new-folder' + ); +} catch (e) { + // Display the error... +} + +try { + const latestVersionInfo = await drfg.getVersionInfo( + 'username/my-repo' + ); +} catch (e) { + // Display the error... +} +``` diff --git a/example-update.png b/example-update.png new file mode 100644 index 0000000000000000000000000000000000000000..25543390be6675f98ac7b5f17774e84f4c77e984 GIT binary patch literal 32498 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

lmDBWI?8sx2k3AzC@i|*jKdLHQ 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 + + + + + + + + + + + + + + + + + + license + + MIT + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..770faab --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1961 @@ +{ + "name": "drfg", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "drfg", + "version": "0.0.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/neonexus" + } + ], + "license": "GPL-3.0-only", + "dependencies": { + "extract-zip": "^2.0.1" + }, + "bin": { + "dl-rl-from-gh": "src/bin.js", + "download-release-from-gh": "src/bin.js", + "download-release-from-github": "src/bin.js", + "download-rl-from-gh": "src/bin.js", + "download-rl-from-github": "src/bin.js", + "drfg": "src/bin.js" + }, + "devDependencies": { + "chai": "4.3.10", + "chai-spies": "1.0.0", + "eslint": "8.52.0", + "mocha": "10.2.0" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.9.1.tgz", + "integrity": "sha512-Y27x+MBLjXa+0JWDhykM3+JE+il3kHKAEqabfEWq3SDhZjLYb6/BHL/JKFnH3fe207JaXkyDo685Oc2Glt6ifA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.52.0.tgz", + "integrity": "sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "dev": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@types/node": { + "version": "20.8.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.4.tgz", + "integrity": "sha512-ZVPnqU58giiCjSxjVUESDtdPk4QR5WQhhINbc9UBrKLU68MX5BF6kbQzTrkwbolyr0X8ChBpXfavr5mZFKZQ5A==", + "optional": true, + "dependencies": { + "undici-types": "~5.25.1" + } + }, + "node_modules/@types/yauzl": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.1.tgz", + "integrity": "sha512-CHzgNU3qYBnp/O4S3yv2tXPlvMTq0YWSTVg2/JYLqWZGHwwgJGAwd00poay/11asPq8wLFwHzubyInqHIFmmiw==", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "engines": { + "node": "*" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chai": { + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz", + "integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.0.8" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chai-spies": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/chai-spies/-/chai-spies-1.0.0.tgz", + "integrity": "sha512-elF2ZUczBsFoP07qCfMO/zeggs8pqCf3fZGyK5+2X4AndS8jycZYID91ztD9oQ7d/0tnS963dPkd0frQEThDsg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + }, + "peerDependencies": { + "chai": "*" + } + }, + "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/chalk/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/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/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/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/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/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.52.0.tgz", + "integrity": "sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.52.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.1.tgz", + "integrity": "sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/globals": { + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-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-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/js-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-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", + "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", + "dev": true, + "dependencies": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/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/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/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/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/undici-types": { + "version": "5.25.3", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.25.3.tgz", + "integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==", + "optional": true + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/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/workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..878168a --- /dev/null +++ b/package.json @@ -0,0 +1,62 @@ +{ + "name": "drfg", + "version": "0.0.1", + "description": "Download Release From GitHub, extract it into a new directory, and install dependencies, with a single command.", + "bin": { + "drfg": "src/bin.js", + "dl-rl-from-gh": "src/bin.js", + "download-rl-from-gh": "src/bin.js", + "download-rl-from-github": "src/bin.js", + "download-release-from-gh": "src/bin.js", + "download-release-from-github": "src/bin.js" + }, + "main": "src/lib.js", + "scripts": { + "lint": "./node_modules/eslint/bin/eslint.js . --max-warnings=0 --report-unused-disable-directives && echo '✔ Your .js files look good.'", + "test": "npm run lint && mocha -R spec --ui bdd --timeout 5000 \"test/**/*.test.js\" && echo '✔ Done ✔\n'" + }, + "repository": { + "type": "git", + "url": "git://github.com/neonexus/drfg.git" + }, + "files": [ + "src/bin.js", + "src/lib.js", + "src/utilities.js" + ], + "keywords": [ + "download", + "release", + "tar", + "tarball", + "zipball", + "zip", + "github", + "extract", + "extraction", + "cloning", + "clone", + "install" + ], + "author": "NeoNexus DeMortis ", + "license": "GPL-3.0-only", + "bugs": { + "url": "https://github.com/neonexus/drfg/issues" + }, + "homepage": "https://github.com/neonexus/drfg#readme", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/neonexus" + } + ], + "dependencies": { + "extract-zip": "^2.0.1" + }, + "devDependencies": { + "chai": "4.3.10", + "chai-spies": "1.0.0", + "eslint": "8.52.0", + "mocha": "10.2.0" + } +} diff --git a/src/bin.js b/src/bin.js new file mode 100755 index 0000000..8be9a70 --- /dev/null +++ b/src/bin.js @@ -0,0 +1,104 @@ +#!/usr/bin/env node + +const startTime = process.hrtime(); + +const drfg = require('./lib'); +const {blankLine, colors, fixTime, question, starBox} = require('./utilities'); +const currentLibVersion = 'v' + require('../package.json').version; + +const repo = process.argv[2] || ''; +let destinationFolder = process.argv[3] || ''; +let version = process.argv[4]; +const skipInstall = (process.argv[5] === 'no-npm' || destinationFolder === 'no-npm' || version === 'no-npm'); + +if (!destinationFolder || !destinationFolder.length || destinationFolder === 'no-npm') { + destinationFolder = repo.includes('/') ? repo.split('/')[1] : ''; +} + +if (version === 'no-npm') { + version = null; +} + +/** + * Validate Inputs + */ +if (!repo || repo === '' || repo === 'fail' || !repo.includes('/') || !destinationFolder || destinationFolder === '' || destinationFolder === 'fail') { + blankLine(); + starBox( + colors.red + 'Usage: npx drfg ' + colors.reset + + '\n\n' + colors.bold + '' + colors.reset + ' should be in the format ' + colors.bold + '"username/my-awesome-repo"' + colors.reset + '.' + + '\n' + colors.bold + '' + colors.reset + ' is the optional folder name to extract the repo into (defaults to repo name).' + ); + blankLine(); + process.exit(1); +} + +/** + * Are we globally installed? + */ +if (!process.env.npm_execpath) { + // We seem to be running from a globally installed instance... Let's check for updates. + blankLine(); + starBox('It appears this module (drfg) was installed globally.', false, true); + blankLine(); + console.log('Checking for updates...'); + + drfg.getVersionInfo('neonexus/drfg').then((currentInfo) => { + if (currentInfo.version !== currentLibVersion) { + blankLine(); + starBox( + ' It seems there\'s an update.\nTo update your local instance:' + + '\n\n ' + colors.blue + 'npm i -g drfg' + colors.reset + + '\n\nInstalled Version: ' + colors.invert + ' ' + currentLibVersion + ' ' + colors.reset + + '\n Latest Version: ' + colors.bold + colors.invert + ' ' + currentInfo.version + ' ' + colors.reset + , true // alignLeft + , false // isSmall + , 10 // padding + ); + + question('Would you like to continue? (Y/n)', (answer) => { + if (answer.toLowerCase() === 'n' || answer.toLowerCase() === 'no') { + console.log('Stopping...'); + } else { + getOnWithIt(); + } + }); + } else { + console.log('We\'re up-to-date. Moving on...'); + getOnWithIt(); + } + }).catch((e) => { + blankLine(); + console.error('There was an error checking with the GitHub API...'); + console.error(e); + blankLine(); + }); +} else { + getOnWithIt(); +} + +function getOnWithIt() { + drfg.downloadAndExtract(repo, destinationFolder, version, skipInstall).then((downloadInfo) => { + const timeElapsed = process.hrtime(startTime); + + blankLine(); + starBox( + ' Repository: ' + colors.bold + repo + colors.reset + + '\n Version: ' + colors.bold + downloadInfo.version + colors.reset + + '\n Extracted to: ' + colors.bold + destinationFolder + colors.reset + + '\n\n Download Time: ' + colors.bold + downloadInfo.downloadTime + colors.reset + + '\n Extraction Time: ' + colors.bold + downloadInfo.extractionTime + colors.reset + + '\n Installation Time: ' + colors.bold + downloadInfo.installationTime + colors.reset + + '\nTotal Library Time: ' + colors.bold + downloadInfo.totalTime + colors.reset + + '\nTotal Elapsed Time: ' + colors.invert + ' ' + fixTime(timeElapsed) + ' ' + colors.reset + + '\n\n Size of Download: ' + colors.bold + downloadInfo.zipballSize + colors.reset + + '\n Uncompressed Size: ' + colors.bold + downloadInfo.extractedSize + colors.reset + + '\n Post-Install Size: ' + colors.invert + ' ' + downloadInfo.installedSize + ' ' + colors.reset + , true // alignLeft + , true // isSmall + ); + blankLine(); + }).catch((e) => { + console.error(e); + }); +} diff --git a/src/lib.js b/src/lib.js new file mode 100644 index 0000000..4f26f5f --- /dev/null +++ b/src/lib.js @@ -0,0 +1,275 @@ +const fs = require('fs'); +const https = require('https'); +const path = require('path'); +const {spawn} = require('child_process'); +const unzip = require('extract-zip'); + +const {fixTime, formatBytes, getDirectorySize} = require('./utilities'); +const currentVersion = require('../package.json').version; + +const zipFilePath = path.join(process.cwd(), 'drfg-temp.zip'); +const extractionPoint = path.join(process.cwd(), 'drfg-temp-extraction'); + +const reqOptions = { + headers: { + 'User-Agent': 'npx drfg (v' + currentVersion + ')' + } +}; + +// Private functions +const lib = { + downloadZipball: (zipballUrl) => new Promise((resolve, reject) => { + if (!zipballUrl || zipballUrl === '') { + return reject('Zipball URL is required.'); + } + + const zipFile = fs.createWriteStream(zipFilePath); + + function downloadIt(currentUrl) { + https.get(currentUrl, reqOptions, (res) => { + if (res.statusCode === 302) { + return downloadIt(res.headers.location); + } else if (res.statusCode !== 200) { + return reject('Bad status code when downloading zipball: ' + res.statusCode); + } + + let zipballSize = 0; + + // Track the size of the zipball + res.on('data', (chunk) => { + zipballSize += chunk.length; + }); + + res.pipe(zipFile); + + zipFile.on('finish', () => { + zipFile.close(); + + return resolve(zipballSize); + }); + }); + } + + downloadIt(zipballUrl); + }), + + extractZipball: (destinationFolder) => new Promise((resolve, reject) => { + let dirToBeMoved; + let extractedSize = 0; + + unzip(zipFilePath, { + dir: extractionPoint, + onEntry: (entry) => { + extractedSize += entry.uncompressedSize; + + if (!dirToBeMoved) { + // This is the folder that gets extracted inside the extraction-point folder. + // Something like: my-repo-of-awesome-4.2.2 + dirToBeMoved = path.join(extractionPoint, entry.fileName); + } + } + }).then(() => { + // Move our extracted folder to its final destination. + try { + fs.renameSync(dirToBeMoved, path.join(process.cwd(), destinationFolder)); + } catch (e) { + return reject(e); + } + + lib.__cleanup(); + + return resolve(extractedSize); + }).catch((e) => { + lib.__cleanup(); + + return reject(e); + }); + }), + + runNpmInstall: (destinationFolder) => new Promise((resolve, reject) => { + const npmInstall = spawn('npm', ['install'], {cwd: destinationFolder, stdio: 'inherit'}); + + npmInstall.on('error', (err) => { + return reject(err); + }); + + npmInstall.on('close', (code) => { + if (code === 0) { + return resolve(); + } + + return reject('npm install failed with code: ' + code); + }); + }), + + __cleanup: () => { + // Delete the downloaded zip file. + fs.unlinkSync(zipFilePath); + + // Delete the extraction point. + fs.rmdirSync(extractionPoint); + } +}; + +// Public functions +const drfg = { + /** + * @typedef optionsObj + * @property {string} repo - The GitHub repo to download / clone. Should be in the form: repo/my-awesome-repo + * @property {string} destinationFolder - The place to extract the repo into. Relative to the current working directory. + * @property {string} [version=latest] - The release version to download. Blank / empty / falsy or 'latest' will force a lookup of the latest version. + * @property {boolean} [skipInstall=false] - Whether to skip `npm install` or not. + */ + /** + * @typedef downloadInfoObj + * @property {string} version - The version that was downloaded. + * @property {string} downloadTime - Total time to download. + * @property {string} extractionTime - Time to extract the zipball. + * @property {string} installationTime - Total npm installation time. + * @property {string} totalTime - Total runtime of the library (download / extract / install). + * @property {string} zipballSize - Total size of the zipball. + * @property {string} extractedSize - Total size after extraction. + * @property {string} installedSize - Total size after installation. + */ + /** + * Download and extract the zipball from a GitHub repo. Defaults to the latest release. + * + * @param {string|optionsObj} repo - The GitHub repo to download / clone. Should be in the form: repo/my-awesome-repo + * @param {string} destinationFolder - The place to extract the repo into. Relative to the current working directory. + * @param {string} [version=latest] - The release version to download. Blank / empty / falsy or 'latest' will force a lookup of the latest version. + * @param {boolean} [skipInstall=false] - Whether to skip `npm install` or not. + * + * @returns {Promise} The version that was downloaded, and all the time calculations for downloading, extracting and installing. + */ + downloadAndExtract: (repo, destinationFolder, version = 'latest', skipInstall = false) => new Promise((resolve, reject) => { + const startTime = process.hrtime(); + + if (typeof repo === 'object') { + if (!repo.repo || repo.repo === '' || !repo.destinationFolder || repo.destinationFolder === '') { + return reject('When calling `drfg.downloadAndExtract(options)`, `options.repo` and `options.destinationFolder` are required!'); + } + + repo = repo.repo; + version = repo.version || ''; + skipInstall = repo.skipInstall || false; + destinationFolder = repo.destinationFolder; + } + + if (!repo.includes('/')) { + return reject('Repository must be in the format "username/repo".'); + } + + if (fs.existsSync(destinationFolder)) { + return reject('The folder "' + destinationFolder + '" already exists.'); + } + + const downloadStartTime = process.hrtime(); + + drfg.getVersionInfo(repo, version).then((release) => { + lib.downloadZipball(release.zipball).then((zipballSize) => { + const downloadTimeElapsed = process.hrtime(downloadStartTime); + const extractionStartTime = process.hrtime(); + + lib.extractZipball(destinationFolder).then(async (extractedSize) => { + const extractionTimeElapsed = process.hrtime(extractionStartTime); + + const installationStartTime = process.hrtime(); + + if (!skipInstall && fs.existsSync(path.join(process.cwd(), destinationFolder, 'package.json'))) { + await lib.runNpmInstall(destinationFolder).catch(() => {}); + } + + const installationTimeElapsed = process.hrtime(installationStartTime); + let installedSize = extractedSize; + + if (!skipInstall) { + installedSize += getDirectorySize(path.join(process.cwd(), destinationFolder, 'node_modules')); + } + + return resolve({ + version: release.version, + downloadTime: fixTime(downloadTimeElapsed), + extractionTime: fixTime(extractionTimeElapsed), + installationTime: fixTime(installationTimeElapsed), + totalTime: fixTime(process.hrtime(startTime)), + zipballSize: formatBytes(zipballSize), + extractedSize: formatBytes(extractedSize), + installedSize: formatBytes(installedSize) + }); + }).catch((e) => { + return reject(e); + }); + }).catch((e) => { + return reject(e); + }); + }).catch((e) => { + return reject(e); + }); + }), + + /** + * @typedef versionAndUrls + * @property {string} name - The name of the version. Likely the same as the version tag. + * @property {string} description - The version description. Will likely contain markdown. + * @property {string} version - The version returned from the API. + * @property {boolean} isDraft - If the version is a draft or not. + * @property {boolean} isPrerelease - If this version is a prerelease version or not. + * @property {string} createdAt - The datetime stamp of the version creation. + * @property {string} publishedAt - The datetime stamp of the version publication. + * @property {string} userSite - An HTML URL for human use, to view this version info. + * @property {string} zipball - The URL for the zipball. + */ + /** + * Get version data and URLs + * + * @param {string} repo - The GitHub repo to get info for. + * @param {string} [version=latest] - The version to get the info for. Defaults to latest. + * + * @returns {Promise} + */ + getVersionInfo: (repo, version = 'latest') => new Promise((resolve, reject) => { + if (!version || version === '') { + version = 'latest'; + } + + if (version !== 'latest') { + version = 'tags/' + version; // Why GitHub, why?... + } + + const reqUrl = 'https://api.github.com/repos/' + repo + '/releases/' + version; + https.get(reqUrl, reqOptions, (res) => { + let data = ''; + + res.on('data', (chunk) => { + data += chunk; + }); + + res.on('end', () => { + const release = JSON.parse(data); + + if (release.message === 'Not Found' || !release.tag_name || release.tag_name === '' || !release.zipball_url || release.zipball_url === '') { + return reject( + '\nVersion "' + version.replace('tags/', '') + '" of the repo "' + repo + '" does not seem to exist. Make sure the repo is using GitHub Releases.' + + '\n\nRequest made to: ' + reqUrl + '\n' + ); + } + + return resolve({ + name: release.name, + description: release.body, + version: release.tag_name, + isDraft: release.draft, + isPrerelease: release.prerelease, + createdAt: release.created_at, + publishedAt: release.published_at, + userSite: release.html_url, + zipball: release.zipball_url + }); + }); + }).on('error', (err) => { + return reject(err); + }); + }) +}; + +module.exports = drfg; diff --git a/src/utilities.js b/src/utilities.js new file mode 100644 index 0000000..33623d5 --- /dev/null +++ b/src/utilities.js @@ -0,0 +1,125 @@ +const fs = require('fs'); +const path = require('path'); + +const utils = { + colors: { + blue: '\x1b[34m', + bold: '\x1b[1m', + invert: '\x1b[7m', + red: '\x1b[31m', + reset: '\x1b[0m' + }, + + blankLine: () => { + console.log(''); + }, + + fixTime: (timeElapsed) => { + const executionTimeInMilliseconds = timeElapsed[0] * 1000 + timeElapsed[1] / 1e6; + + let timeOut; + + // Determine whether to display in milliseconds, seconds, or minutes + if (executionTimeInMilliseconds < 1000) { + timeOut = executionTimeInMilliseconds.toFixed(2) + ' ms'; + } else if (executionTimeInMilliseconds < 60000) { + const executionTimeInSeconds = executionTimeInMilliseconds / 1000; + timeOut = executionTimeInSeconds.toFixed(2) + ' s'; + } else { + const executionTimeInMinutes = executionTimeInMilliseconds / 60000; + timeOut = executionTimeInMinutes.toFixed(2) + ' m'; + } + + return timeOut; + }, + + // Convert bytes to human-readable + formatBytes: (bytes) => { + if (bytes === 0) return '0 Bytes'; + + const sizes = ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB']; + const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))); + const finalNumber = bytes / Math.pow(1024, i); + + return ((i > 0) ? finalNumber.toFixed(2) : finalNumber) + ' ' + sizes[i]; + }, + + getDirectorySize: (directory) => { + let size = 0; + const files = fs.readdirSync(directory); + + for (const file of files) { + const filePath = path.join(directory, file); + const stats = fs.statSync(filePath); + + if (stats.isDirectory()) { + size += utils.getDirectorySize(filePath); + } else { + size += stats.size; + } + } + + return size; + }, + + question: (q, a, readline = require('readline')) => { + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + + rl.question(q + ': ', (answer) => { + rl.close(); + a(answer); + }); + }, + + starBox: (text, alignLeft = false, isSmall = false, padding = 2) => { + // Remove color codes from the text for formatting purposes + const cleanLines = text.replace(/\x1b\[[0-9;]*m/g, '').split('\n'); + const lines = text.split('\n'); + const maxLength = cleanLines.reduce((max, line) => Math.max(max, line.length), 0); + const fullLine = '*'.repeat(maxLength + (padding * 2) + 2); + const emptyLine = `*${' '.repeat(maxLength + (padding * 2))}*`; + + const box = [fullLine]; + + if (!isSmall) { + box.push(emptyLine); + } + + lines.forEach((line, index) => { + let paddedText = '*'; + + if (alignLeft) { + const rightPadding = maxLength - cleanLines[index].length + padding; + paddedText += ' '.repeat(padding) + line + ' '.repeat(rightPadding) + '*'; + } else { + const totalSpaces = maxLength - cleanLines[index].length; + + if (totalSpaces % 2 === 0) { + const spaces = (totalSpaces / 2) + padding; + paddedText += ' '.repeat(spaces) + line + ' '.repeat(spaces); + } else { + const leftSpaces = Math.floor(totalSpaces / 2); + const rightSpaces = Math.ceil(totalSpaces / 2); + paddedText += ' '.repeat(leftSpaces + padding) + line + ' '.repeat(rightSpaces + padding); + } + + paddedText += '*'; + } + + box.push(paddedText); + }); + + if (!isSmall) { + box.push(emptyLine); + } + + box.push(fullLine); + + console.log(box.join('\n')); + } +}; + +module.exports = utils; diff --git a/test/.eslintrc b/test/.eslintrc new file mode 100644 index 0000000..3284eae --- /dev/null +++ b/test/.eslintrc @@ -0,0 +1,6 @@ +{ + "rules": { + "no-undef": "off", + "prefer-arrow-callback": "off" + } +} diff --git a/test/index.test.js b/test/index.test.js new file mode 100644 index 0000000..faefa3b --- /dev/null +++ b/test/index.test.js @@ -0,0 +1,148 @@ +const chai = require('chai'); +const should = chai.should(); +const fs = require('fs'); +const path = require('path'); +const utils = require('../src/utilities'); + +chai.use(require('chai-spies')); + +describe('drfg - Download Zipball From Github', () => { + afterEach((done) => { + chai.spy.restore(); + done(); + }); + + describe('Utility Functions', () => { + it('should have color options', (done) => { + utils.colors.should.be.an('object'); + Object.keys(utils.colors).should.have.lengthOf(5); + + should.exist(utils.colors.blue); + should.exist(utils.colors.bold); + should.exist(utils.colors.invert); + should.exist(utils.colors.red); + should.exist(utils.colors.reset); + + done(); + }); + + it('should create blank lines in the console', (done) => { + const og = console.log; + console.log = () => {}; + const consoleLogSpy = chai.spy.on(console, 'log'); + + utils.blankLine(); + + consoleLogSpy.should.have.been.called.once.with(''); + + console.log = og; + done(); + }); + + it('should fix time calculations', (done) => { + const oneMilli = 1000000; // 1 million nanoseconds in 1 millisecond + + const milliseconds = utils.fixTime([0, oneMilli * 3]); + milliseconds.should.eq('3.00 ms'); + + const seconds = utils.fixTime([2, oneMilli * 750]); + seconds.should.eq('2.75 s'); + + const minutes = utils.fixTime([240, oneMilli * 12000]); + minutes.should.eq('4.20 m'); + + done(); + }); + + it('should format bytes', (done) => { + const zeroBytes = utils.formatBytes(0); + zeroBytes.should.eq('0 Bytes'); + + const someBytes = utils.formatBytes(512); + someBytes.should.eq('512 Bytes'); + + const kilobytes = utils.formatBytes(2048); + kilobytes.should.eq('2.00 KiB'); + + const megabytes = utils.formatBytes(44480594); + megabytes.should.eq('42.42 MiB'); + + const gigabytes = utils.formatBytes(3382300000); + gigabytes.should.eq('3.15 GiB'); + + const terabytes = utils.formatBytes(76328100000000); + terabytes.should.eq('69.42 TiB'); + + done(); + }); + + it('should calculate the size of a directory recursively', (done) => { + const githubSize = fs.statSync(path.join(__dirname, '../.github/FUNDING.yml')).size + fs.statSync(path.join(__dirname, '../.github/workflows/codeql.yml')).size; + + const utilsCalc = utils.getDirectorySize(path.join(__dirname, '../.github')); + + utilsCalc.should.be.eq(githubSize); + + done(); + }); + + it('should be able to ask the user questions in the console', (done) => { + const readlineInterface = chai.spy.interface({ + question: chai.spy((q, a) => { + q.should.eq('Test: '); + a('neat'); + }), + close: chai.spy() + }); + + const answerSpy = chai.spy(); + + utils.question( + 'Test', + answerSpy, + { + createInterface: (input) => { + input.should.have.property('input', process.stdin); + input.should.have.property('output', process.stdout); + + return readlineInterface; + } + } + ); + + answerSpy.should.have.been.called.once.with('neat'); + readlineInterface.question.should.have.been.called.once; + readlineInterface.close.should.have.been.called.once; + + done(); + }); + + it('should create star boxes', (done) => { + const og = console.log; + console.log = () => {}; + const consoleLogSpy = chai.spy.on(console, 'log'); + + utils.starBox('Nice Test!'); + consoleLogSpy.should.on.nth(1).to.have.been.called.with('****************\n* *\n* Nice Test! *\n* *\n****************'); + + utils.starBox('Multiple\nlines\ntest'); + consoleLogSpy.should.on.nth(2).to.have.been.called.with('**************\n* *\n* Multiple *\n* lines *\n* test *\n* *\n**************'); + + utils.starBox('Left test\n Neat', true); + consoleLogSpy.should.on.nth(3).to.have.been.called.with( + '********************\n* *\n* Left test *\n* Neat *\n* *\n********************' + ); + + utils.starBox('Small test', false, true); + consoleLogSpy.should.on.nth(4).to.have.been.called.with('****************\n* Small test *\n****************'); + + utils.starBox('Padding test', false, false, 5); + consoleLogSpy.should.on.nth(5).to.have.been.called.with( + '************************\n* *\n* Padding test *\n* *\n************************' + ); + + console.log = og; + done(); + }); + }); +});