commit 89384d3545dc522d5ae9d60add32c766b20a0faf Author: Ghost Date: Thu Apr 9 18:57:44 2026 +0800 Init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..624d637 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +dist/ +test.mjs diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..0c9c5c7 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,9 @@ +{ + "semi": false, + "tabWidth": 2, + "singleQuote": true, + "trailingComma": "none", + "bracketSpacing": true, + "arrowParens": "avoid", + "printWidth": 80 +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5ef7619 --- /dev/null +++ b/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 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 Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are 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. + + 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. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + 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 Affero 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. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + 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 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 work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero 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 Affero 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 Affero 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 Affero 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) 2025 FurryR + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + 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 AGPL, see +. diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..f77a9b4 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,43 @@ +import js from '@eslint/js' +import globals from 'globals' +import tseslint from 'typescript-eslint' +import json from '@eslint/json' +import markdown from '@eslint/markdown' +import { defineConfig } from 'eslint/config' + +export default defineConfig([ + { + files: ['**/*.{js,mjs,cjs,ts,mts,cts}'], + plugins: { js }, + extends: ['js/recommended'] + }, + { + files: ['**/*.{js,mjs,cjs,ts,mts,cts}'], + languageOptions: { globals: globals.node } + }, + tseslint.configs.recommended, + { + files: ['**/*.json'], + plugins: { json }, + language: 'json/json', + extends: ['json/recommended'] + }, + { + files: ['**/*.jsonc'], + plugins: { json }, + language: 'json/jsonc', + extends: ['json/recommended'] + }, + { + files: ['**/*.json5'], + plugins: { json }, + language: 'json/json5', + extends: ['json/recommended'] + }, + { + files: ['**/*.md'], + plugins: { markdown }, + language: 'markdown/gfm', + extends: ['markdown/recommended'] + } +]) diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..aa70d94 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,4913 @@ +{ + "name": "lionheart", + "version": "1.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "lionheart", + "version": "1.1.0", + "license": "AGPL-3.0-only", + "dependencies": { + "axios": "^1.12.2", + "http-proxy-agent": "^7.0.2", + "json-bigint": "^1.0.0", + "md5": "^2.3.0", + "node-forge": "^1.3.1", + "pako": "^2.1.0", + "socks-proxy-agent": "^8.0.5" + }, + "devDependencies": { + "@eslint/js": "^9.28.0", + "@eslint/json": "^0.13.1", + "@eslint/markdown": "^6.4.0", + "@types/json-bigint": "^1.0.4", + "@types/md5": "^2.3.5", + "@types/node": "^22.15.29", + "@types/node-forge": "^1.3.11", + "@types/pako": "^2.0.3", + "@typescript/native-preview": "^7.0.0-dev.20250607.1", + "eslint": "^9.28.0", + "globals": "^16.2.0", + "prettier": "^3.5.3", + "tsup": "^8.5.0", + "typescript-eslint": "^8.33.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", + "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz", + "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz", + "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz", + "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz", + "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz", + "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz", + "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz", + "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz", + "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz", + "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz", + "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz", + "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz", + "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz", + "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz", + "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz", + "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz", + "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz", + "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz", + "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz", + "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz", + "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz", + "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz", + "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz", + "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz", + "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/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-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", + "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", + "dev": true, + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/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/@eslint/config-helpers": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.2.tgz", + "integrity": "sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.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": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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": "9.28.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.28.0.tgz", + "integrity": "sha512-fnqSjGWd/CoIp4EXIxWVK/sHA6DOHN4+8Ix2cX5ycOY7LG0UY8nHCU5pIp2eaE1Mc7Qd8kHspYNzYXT2ojPLzg==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/json": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/@eslint/json/-/json-0.13.1.tgz", + "integrity": "sha512-AGzO7cR0QqSEfJdx9jT4SHQ6BJ5K0G8kN7WNGI1Hgy5AVbUhBKfFoN0gNo86j97aqkU57mqFUW9ytMPdEnVARA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.15.1", + "@eslint/plugin-kit": "^0.3.4", + "@humanwhocodes/momoa": "^3.3.8", + "natural-compare": "^1.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/markdown": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@eslint/markdown/-/markdown-6.6.0.tgz", + "integrity": "sha512-IsWPy2jU3gaQDlioDC4sT4I4kG1hX1OMWs/q2sWwJrPoMASHW/Z4SDw+6Aql6EsHejGbagYuJbFq9Zvx+Y1b1Q==", + "dev": true, + "license": "MIT", + "workspaces": [ + "examples/*" + ], + "dependencies": { + "@eslint/core": "^0.14.0", + "@eslint/plugin-kit": "^0.3.1", + "github-slugger": "^2.0.0", + "mdast-util-from-markdown": "^2.0.2", + "mdast-util-frontmatter": "^2.0.1", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-frontmatter": "^2.0.0", + "micromark-extension-gfm": "^3.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/markdown/node_modules/@eslint/core": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", + "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", + "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.15.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "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/momoa": { + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/momoa/-/momoa-3.3.8.tgz", + "integrity": "sha512-/3PZzor2imi/RLLcnHztkwA79txiVvW145Ve2cp5dxRcH5qOUNJPToasqLFHniTfw4B4lT7jGDdBOPXbXYlIMQ==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "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/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.41.1.tgz", + "integrity": "sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.41.1.tgz", + "integrity": "sha512-DXdQe1BJ6TK47ukAoZLehRHhfKnKg9BjnQYUu9gzhI8Mwa1d2fzxA1aw2JixHVl403bwp1+/o/NhhHtxWJBgEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.41.1.tgz", + "integrity": "sha512-5afxvwszzdulsU2w8JKWwY8/sJOLPzf0e1bFuvcW5h9zsEg+RQAojdW0ux2zyYAz7R8HvvzKCjLNJhVq965U7w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.41.1.tgz", + "integrity": "sha512-egpJACny8QOdHNNMZKf8xY0Is6gIMz+tuqXlusxquWu3F833DcMwmGM7WlvCO9sB3OsPjdC4U0wHw5FabzCGZg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.41.1.tgz", + "integrity": "sha512-DBVMZH5vbjgRk3r0OzgjS38z+atlupJ7xfKIDJdZZL6sM6wjfDNo64aowcLPKIx7LMQi8vybB56uh1Ftck/Atg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.41.1.tgz", + "integrity": "sha512-3FkydeohozEskBxNWEIbPfOE0aqQgB6ttTkJ159uWOFn42VLyfAiyD9UK5mhu+ItWzft60DycIN1Xdgiy8o/SA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.41.1.tgz", + "integrity": "sha512-wC53ZNDgt0pqx5xCAgNunkTzFE8GTgdZ9EwYGVcg+jEjJdZGtq9xPjDnFgfFozQI/Xm1mh+D9YlYtl+ueswNEg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.41.1.tgz", + "integrity": "sha512-jwKCca1gbZkZLhLRtsrka5N8sFAaxrGz/7wRJ8Wwvq3jug7toO21vWlViihG85ei7uJTpzbXZRcORotE+xyrLA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.41.1.tgz", + "integrity": "sha512-g0UBcNknsmmNQ8V2d/zD2P7WWfJKU0F1nu0k5pW4rvdb+BIqMm8ToluW/eeRmxCared5dD76lS04uL4UaNgpNA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.41.1.tgz", + "integrity": "sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.41.1.tgz", + "integrity": "sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.41.1.tgz", + "integrity": "sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.41.1.tgz", + "integrity": "sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.41.1.tgz", + "integrity": "sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.41.1.tgz", + "integrity": "sha512-oIE6M8WC9ma6xYqjvPhzZYk6NbobIURvP/lEbh7FWplcMO6gn7MM2yHKA1eC/GvYwzNKK/1LYgqzdkZ8YFxR8g==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.41.1.tgz", + "integrity": "sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.41.1.tgz", + "integrity": "sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.41.1.tgz", + "integrity": "sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.41.1.tgz", + "integrity": "sha512-+psFT9+pIh2iuGsxFYYa/LhS5MFKmuivRsx9iPJWNSGbh2XVEjk90fmpUEjCnILPEPJnikAU6SFDiEUyOv90Pg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.41.1.tgz", + "integrity": "sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dev": true, + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "dev": true + }, + "node_modules/@types/json-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@types/json-bigint/-/json-bigint-1.0.4.tgz", + "integrity": "sha512-ydHooXLbOmxBbubnA7Eh+RpBzuaIiQjh8WGJYQB50JFGFrdxW7JzVlyEV7fAXw0T2sqJ1ysTneJbiyNLqZRAag==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/md5": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@types/md5/-/md5-2.3.5.tgz", + "integrity": "sha512-/i42wjYNgE6wf0j2bcTX6kuowmdL/6PE4IVitMpm2eYKBUuYCprdcWVK+xEF0gcV6ufMCRhtxmReGfc6hIK7Jw==", + "dev": true + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "dev": true, + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "22.15.29", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.29.tgz", + "integrity": "sha512-LNdjOkUDlU1RZb8e1kOIUpN1qQUlzGkEtbVNo53vbrwDg5om6oduhm4SiUaPW5ASTXhAiP0jInWG8Qx9fVlOeQ==", + "dev": true, + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/node-forge": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", + "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/pako": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.3.tgz", + "integrity": "sha512-bq0hMV9opAcrmE0Byyo0fY3Ew4tgOevJmQ9grUhpXQhYfyLJ1Kqg3P33JT5fdbT2AjeAjR51zqqVjAL/HMkx7Q==", + "dev": true + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.33.0.tgz", + "integrity": "sha512-CACyQuqSHt7ma3Ns601xykeBK/rDeZa3w6IS6UtMQbixO5DWy+8TilKkviGDH6jtWCo8FGRKEK5cLLkPvEammQ==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.33.0", + "@typescript-eslint/type-utils": "8.33.0", + "@typescript-eslint/utils": "8.33.0", + "@typescript-eslint/visitor-keys": "8.33.0", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.33.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.33.0.tgz", + "integrity": "sha512-JaehZvf6m0yqYp34+RVnihBAChkqeH+tqqhS0GuX1qgPpwLvmTPheKEs6OeCK6hVJgXZHJ2vbjnC9j119auStQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "8.33.0", + "@typescript-eslint/types": "8.33.0", + "@typescript-eslint/typescript-estree": "8.33.0", + "@typescript-eslint/visitor-keys": "8.33.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.33.0.tgz", + "integrity": "sha512-d1hz0u9l6N+u/gcrk6s6gYdl7/+pp8yHheRTqP6X5hVDKALEaTn8WfGiit7G511yueBEL3OpOEpD+3/MBdoN+A==", + "dev": true, + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.33.0", + "@typescript-eslint/types": "^8.33.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.33.0.tgz", + "integrity": "sha512-LMi/oqrzpqxyO72ltP+dBSP6V0xiUb4saY7WLtxSfiNEBI8m321LLVFU9/QDJxjDQG9/tjSqKz/E3380TEqSTw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.33.0", + "@typescript-eslint/visitor-keys": "8.33.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.33.0.tgz", + "integrity": "sha512-sTkETlbqhEoiFmGr1gsdq5HyVbSOF0145SYDJ/EQmXHtKViCaGvnyLqWFFHtEXoS0J1yU8Wyou2UGmgW88fEug==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.33.0.tgz", + "integrity": "sha512-lScnHNCBqL1QayuSrWeqAL5GmqNdVUQAAMTaCwdYEdWfIrSrOGzyLGRCHXcCixa5NK6i5l0AfSO2oBSjCjf4XQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "8.33.0", + "@typescript-eslint/utils": "8.33.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.33.0.tgz", + "integrity": "sha512-DKuXOKpM5IDT1FA2g9x9x1Ug81YuKrzf4mYX8FAVSNu5Wo/LELHWQyM1pQaDkI42bX15PWl0vNPt1uGiIFUOpg==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.33.0.tgz", + "integrity": "sha512-vegY4FQoB6jL97Tu/lWRsAiUUp8qJTqzAmENH2k59SJhw0Th1oszb9Idq/FyyONLuNqT1OADJPXfyUNOR8SzAQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/project-service": "8.33.0", + "@typescript-eslint/tsconfig-utils": "8.33.0", + "@typescript-eslint/types": "8.33.0", + "@typescript-eslint/visitor-keys": "8.33.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.33.0.tgz", + "integrity": "sha512-lPFuQaLA9aSNa7D5u2EpRiqdAUhzShwGg/nhpBlc4GR6kcTABttCuyjFs8BcEZ8VWrjCBof/bePhP3Q3fS+Yrw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.33.0", + "@typescript-eslint/types": "8.33.0", + "@typescript-eslint/typescript-estree": "8.33.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.33.0.tgz", + "integrity": "sha512-7RW7CMYoskiz5OOGAWjJFxgb7c5UNjTG292gYhWeOAcFmYCtVCSqjqSBj5zMhxbXo2JOW95YYrUWJfU0zrpaGQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.33.0", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript/native-preview": { + "version": "7.0.0-dev.20250607.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview/-/native-preview-7.0.0-dev.20250607.1.tgz", + "integrity": "sha512-bsuApqSBq86rh2RC9VW0TOzskKwUckV8hLbDQRRuwpcEb3J2XbhryTfspQIjhpFDfjSk0vEgvhGA/BINvdKl0Q==", + "dev": true, + "bin": { + "tsgo": "bin/tsgo.js" + }, + "engines": { + "node": ">=20.6.0" + }, + "optionalDependencies": { + "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20250607.1", + "@typescript/native-preview-darwin-x64": "7.0.0-dev.20250607.1", + "@typescript/native-preview-linux-arm": "7.0.0-dev.20250607.1", + "@typescript/native-preview-linux-arm64": "7.0.0-dev.20250607.1", + "@typescript/native-preview-linux-x64": "7.0.0-dev.20250607.1", + "@typescript/native-preview-win32-arm64": "7.0.0-dev.20250607.1", + "@typescript/native-preview-win32-x64": "7.0.0-dev.20250607.1" + } + }, + "node_modules/@typescript/native-preview-darwin-arm64": { + "version": "7.0.0-dev.20250607.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-arm64/-/native-preview-darwin-arm64-7.0.0-dev.20250607.1.tgz", + "integrity": "sha512-i1tpSz6aajOdudeZ46OqLMw4L/tmgVpoGaTrVC8rVQNf4+npFzvdLJcBECVqZONEEEcg+iJgTwchRjRu0QlWOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.6.0" + } + }, + "node_modules/@typescript/native-preview-darwin-x64": { + "version": "7.0.0-dev.20250607.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-x64/-/native-preview-darwin-x64-7.0.0-dev.20250607.1.tgz", + "integrity": "sha512-1uDL8uOiHtIHU3swngGnS7k5WD+jplhnkFoa0WrdIkYmOsm+85r/cD4HIcrdHdAL2U3db7Un984NsMyI70RlSA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.6.0" + } + }, + "node_modules/@typescript/native-preview-linux-arm": { + "version": "7.0.0-dev.20250607.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm/-/native-preview-linux-arm-7.0.0-dev.20250607.1.tgz", + "integrity": "sha512-xuBtCpd+E2kh7jxrNzX14N4aL4kAGQXWfOPJlUHBueX1PI9BwKrfuDxahbjoutrK5m45Dwm3roiDFOiIkJjoIQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.6.0" + } + }, + "node_modules/@typescript/native-preview-linux-arm64": { + "version": "7.0.0-dev.20250607.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm64/-/native-preview-linux-arm64-7.0.0-dev.20250607.1.tgz", + "integrity": "sha512-wIodgimMxbSSOJ/E/DLd7tDY66gLQz87c0BA5G30x92GI8IK58OvIv77mN+v3zUYp4nVlRqr8hozXBWWunqs8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.6.0" + } + }, + "node_modules/@typescript/native-preview-linux-x64": { + "version": "7.0.0-dev.20250607.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-x64/-/native-preview-linux-x64-7.0.0-dev.20250607.1.tgz", + "integrity": "sha512-hsKjPTDGn95w7yC5JshWzyoXNSbSg4pPfoFBnHfBwHlgUO0Mf+PQaFBq9trEkgAgnH725PxH18QWKJrlwtXP2A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.6.0" + } + }, + "node_modules/@typescript/native-preview-win32-arm64": { + "version": "7.0.0-dev.20250607.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-arm64/-/native-preview-win32-arm64-7.0.0-dev.20250607.1.tgz", + "integrity": "sha512-v4DMFli8v2c2NKbnscaik2aWamL8Q+OOeLQ3a5kZz80EUUkoop27YhNLEsVJccGtoqeyWjPaJdHGZhj2XfDQAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.6.0" + } + }, + "node_modules/@typescript/native-preview-win32-x64": { + "version": "7.0.0-dev.20250607.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-x64/-/native-preview-win32-x64-7.0.0-dev.20250607.1.tgz", + "integrity": "sha512-RmUTenoe+6wotEYoM9ycICVBLt9lqTpzvco5rLs6lynOrOgYoEmCoj+oX17kKk8/009NMXQ3CNxkz1du1C16ug==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.6.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "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-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.12.2", + "resolved": "https://registry.npmmirror.com/axios/-/axios-1.12.2.tgz", + "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "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/bignumber.js": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.0.tgz", + "integrity": "sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA==", + "engines": { + "node": "*" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/bundle-require": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz", + "integrity": "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==", + "dev": true, + "dependencies": { + "load-tsconfig": "^0.2.3" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "esbuild": ">=0.18" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "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/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/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "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/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "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/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "dev": true, + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "engines": { + "node": "*" + } + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.1.0.tgz", + "integrity": "sha512-Wy+JTSbFThEOXQIR2L6mxJvEs+veIzpmqD7ynWxMXGpnk3smkHQOp6forLdHsKpAMW9iJpaBBIxz285t1n1C3w==", + "dev": true, + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "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/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dev": true, + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz", + "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.5", + "@esbuild/android-arm": "0.25.5", + "@esbuild/android-arm64": "0.25.5", + "@esbuild/android-x64": "0.25.5", + "@esbuild/darwin-arm64": "0.25.5", + "@esbuild/darwin-x64": "0.25.5", + "@esbuild/freebsd-arm64": "0.25.5", + "@esbuild/freebsd-x64": "0.25.5", + "@esbuild/linux-arm": "0.25.5", + "@esbuild/linux-arm64": "0.25.5", + "@esbuild/linux-ia32": "0.25.5", + "@esbuild/linux-loong64": "0.25.5", + "@esbuild/linux-mips64el": "0.25.5", + "@esbuild/linux-ppc64": "0.25.5", + "@esbuild/linux-riscv64": "0.25.5", + "@esbuild/linux-s390x": "0.25.5", + "@esbuild/linux-x64": "0.25.5", + "@esbuild/netbsd-arm64": "0.25.5", + "@esbuild/netbsd-x64": "0.25.5", + "@esbuild/openbsd-arm64": "0.25.5", + "@esbuild/openbsd-x64": "0.25.5", + "@esbuild/sunos-x64": "0.25.5", + "@esbuild/win32-arm64": "0.25.5", + "@esbuild/win32-ia32": "0.25.5", + "@esbuild/win32-x64": "0.25.5" + } + }, + "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": "9.28.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.28.0.tgz", + "integrity": "sha512-ocgh41VhRlf9+fVpe7QKzwLj9c92fDiqOj8Y3Sd4/ZmVA4Btx4PlUYPq4pp9JDyupkf1upbEXecxL2mwNV7jPQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.20.0", + "@eslint/config-helpers": "^0.2.1", + "@eslint/core": "^0.14.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.28.0", + "@eslint/plugin-kit": "^0.3.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.3.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", + "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/@eslint/core": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", + "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/eslint/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/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/eslint/node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/espree/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/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "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/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/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/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/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fault": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", + "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==", + "dev": true, + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/fdir": { + "version": "6.4.5", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.5.tgz", + "integrity": "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw==", + "dev": true, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "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/fix-dts-default-cjs-exports": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fix-dts-default-cjs-exports/-/fix-dts-default-cjs-exports-1.0.1.tgz", + "integrity": "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==", + "dev": true, + "dependencies": { + "magic-string": "^0.30.17", + "mlly": "^1.7.4", + "rollup": "^4.34.8" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "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/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/github-slugger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", + "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==", + "dev": true, + "license": "ISC" + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.2.0.tgz", + "integrity": "sha512-O+7l9tPdHCU320IigZZPj5zmRCFG9xHmx9cU8FqU2Rp+JN714seHV+2S9+JslCpY4gJwU2vOGox0wzgae/MCEg==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/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/ip-address": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", + "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "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/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "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-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "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, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/load-tsconfig": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", + "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.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/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", + "dev": true + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-frontmatter": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-frontmatter/-/mdast-util-frontmatter-2.0.1.tgz", + "integrity": "sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "escape-string-regexp": "^5.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-extension-frontmatter": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-frontmatter/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", + "dev": true, + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-frontmatter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-frontmatter/-/micromark-extension-frontmatter-2.0.0.tgz", + "integrity": "sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==", + "dev": true, + "dependencies": { + "fault": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "dev": true, + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "dev": true, + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "dev": true, + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "dev": true, + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "dev": true, + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "dev": true, + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "dev": true, + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/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/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mlly": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz", + "integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==", + "dev": true, + "dependencies": { + "acorn": "^8.14.0", + "pathe": "^2.0.1", + "pkg-types": "^1.3.0", + "ufo": "^1.5.4" + } + }, + "node_modules/mlly/node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "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==" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/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/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" + }, + "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-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "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/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.41.1.tgz", + "integrity": "sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.7" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.41.1", + "@rollup/rollup-android-arm64": "4.41.1", + "@rollup/rollup-darwin-arm64": "4.41.1", + "@rollup/rollup-darwin-x64": "4.41.1", + "@rollup/rollup-freebsd-arm64": "4.41.1", + "@rollup/rollup-freebsd-x64": "4.41.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.41.1", + "@rollup/rollup-linux-arm-musleabihf": "4.41.1", + "@rollup/rollup-linux-arm64-gnu": "4.41.1", + "@rollup/rollup-linux-arm64-musl": "4.41.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.41.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.41.1", + "@rollup/rollup-linux-riscv64-gnu": "4.41.1", + "@rollup/rollup-linux-riscv64-musl": "4.41.1", + "@rollup/rollup-linux-s390x-gnu": "4.41.1", + "@rollup/rollup-linux-x64-gnu": "4.41.1", + "@rollup/rollup-linux-x64-musl": "4.41.1", + "@rollup/rollup-win32-arm64-msvc": "4.41.1", + "@rollup/rollup-win32-ia32-msvc": "4.41.1", + "@rollup/rollup-win32-x64-msvc": "4.41.1", + "fsevents": "~2.3.2" + } + }, + "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/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "license": "MIT", + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "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/string-width-cjs/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/string-width-cjs/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/string-width-cjs/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-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/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/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/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/sucrase/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sucrase/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true + }, + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "dev": true, + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "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/tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true + }, + "node_modules/tsup": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.5.0.tgz", + "integrity": "sha512-VmBp77lWNQq6PfuMqCHD3xWl22vEoWsKajkF8t+yMBawlUS8JzEI+vOVMeuNZIuMML8qXRizFKi9oD5glKQVcQ==", + "dev": true, + "dependencies": { + "bundle-require": "^5.1.0", + "cac": "^6.7.14", + "chokidar": "^4.0.3", + "consola": "^3.4.0", + "debug": "^4.4.0", + "esbuild": "^0.25.0", + "fix-dts-default-cjs-exports": "^1.0.0", + "joycon": "^3.1.1", + "picocolors": "^1.1.1", + "postcss-load-config": "^6.0.1", + "resolve-from": "^5.0.0", + "rollup": "^4.34.8", + "source-map": "0.8.0-beta.0", + "sucrase": "^3.35.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.11", + "tree-kill": "^1.2.2" + }, + "bin": { + "tsup": "dist/cli-default.js", + "tsup-node": "dist/cli-node.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@microsoft/api-extractor": "^7.36.0", + "@swc/core": "^1", + "postcss": "^8.4.12", + "typescript": ">=4.5.0" + }, + "peerDependenciesMeta": { + "@microsoft/api-extractor": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "postcss": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/tsup/node_modules/source-map": { + "version": "0.8.0-beta.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", + "dev": true, + "dependencies": { + "whatwg-url": "^7.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "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, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.33.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.33.0.tgz", + "integrity": "sha512-5YmNhF24ylCsvdNW2oJwMzTbaeO4bg90KeGtMjUw0AGtHksgEPLRTUil+coHwCfiu4QjVJFnjp94DmU6zV7DhQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.33.0", + "@typescript-eslint/parser": "8.33.0", + "@typescript-eslint/utils": "8.33.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/ufo": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", + "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", + "dev": true + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true + }, + "node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "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/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dev": true, + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "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/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/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" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..de9c89a --- /dev/null +++ b/package.json @@ -0,0 +1,45 @@ +{ + "name": "lionheart", + "version": "1.1.0", + "description": "A client framework for maimai DX (SDGB)", + "module": "dist/index.mjs", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "build": "tsup", + "build:watch": "tsup --watch", + "lint:type": "tsgo --noEmit", + "lint:format": "prettier --check ./src/**/*.ts", + "format": "prettier --write ./src", + "lint": "eslint ./src", + "lint:fix": "eslint ./src --fix" + }, + "author": "FurryR", + "license": "AGPL-3.0-only", + "dependencies": { + "axios": "^1.12.2", + "http-proxy-agent": "^7.0.2", + "json-bigint": "^1.0.0", + "md5": "^2.3.0", + "node-forge": "^1.3.1", + "pako": "^2.1.0", + "socks-proxy-agent": "^8.0.5" + }, + "devDependencies": { + "@eslint/js": "^9.28.0", + "@eslint/json": "^0.13.1", + "@eslint/markdown": "^6.4.0", + "@types/json-bigint": "^1.0.4", + "@types/md5": "^2.3.5", + "@types/node": "^22.15.29", + "@types/node-forge": "^1.3.11", + "@types/pako": "^2.0.3", + "@typescript/native-preview": "^7.0.0-dev.20250607.1", + "eslint": "^9.28.0", + "globals": "^16.2.0", + "prettier": "^3.5.3", + "tsup": "^8.5.0", + "typescript-eslint": "^8.33.0" + } +} diff --git a/src/client.ts b/src/client.ts new file mode 100644 index 0000000..009ac68 --- /dev/null +++ b/src/client.ts @@ -0,0 +1,476 @@ +import forge from 'node-forge' +import md5 from 'md5' +import * as Encryption from './encryption' +import JSONBigInt from 'json-bigint' +import type { Api as ApiTyping } from './typings/api' +import { User } from './user' +import pako from 'pako' +import { Http } from '.' +// import { request } from './utils/http' + +const JSONParser = JSONBigInt({ + useNativeBigInt: true +}) + +export interface CreateOption { + url: string // 服务器地址(不用加 at. ai. 等前缀) + codename: string // 游戏代号 + chipId: string // 基板 ID + version: string // 游戏版本 (1.40, 1.50) + encryption: { + aime: Encryption.Aime.EncryptionParam + maimai: Encryption.Maimai.EncryptionParam + } + fetch?: Client['fetch'] +} + +export interface OptionImages { + patch: URL[] // TODO: Single URL? + options: URL[] +} + +export class Client { + public cookies: Map = new Map() + + private _Obfuscator(apiName: string) { + const ObfuscateParam = this.encryption.maimai.obfuscateParam + return md5(apiName + ObfuscateParam, { encoding: 'hex' }).toLowerCase() + } + + private _generateUserAgent(apiName: string) { + return `${this._Obfuscator(apiName)}#${this.shortChipId}` + } + + private _generateUserAgentWithId(apiName: string, userId: number) { + return `${this._Obfuscator(apiName)}#${userId}` + } + + /** + * Request an API from Maimai server. + * @param apiName The name of the API to request, e.g. 'GetUserPreviewApi'. + * @param data The data to send to the API, which should match the API's request format. + * @param options Optional parameters, such as `userId` for generating user-specific User-Agent. + * @param options.userId The user ID to include in the User-Agent header. If not provided, a generic User-Agent will be used. + * @param options.maxTimeout The maximum timeout in seconds to wait for a successful response. Default is 60. + * @return A promise that resolves to the API's response data, which should match the API's response format. + * @throws An error if the API request fails or returns an unexpected response. + * @example + * ```typescript + * const client = await Client.create('example.com', 'SDEZ', 'A000-00000000000', '1.40'); + * const userId = 1234567890; + * const preview = await client.request('GetUserPreviewApi', { + * userId, + * segaIdAuthKey: '' + * }); + * console.log(preview); + * ``` + * @template T The type of the API name, which should match the keys of `ApiTyping`. + * @protected This API is for internal uses. Avoid using it directly in your code. + */ + public async request( + apiName: T, + data: ApiTyping[T][0], + options?: { userId?: number; maxTimeout?: number } + ): Promise + + public async request( + apiName: string, + data: unknown, + options?: { userId?: number; maxTimeout?: number } + ): Promise { + const realName = `${apiName}Maimai${this.region}` + const endpoint = this.url.maimai + this._Obfuscator(realName) + const headers: Record = { + 'User-Agent': options?.userId + ? this._generateUserAgentWithId(realName, options.userId) + : this._generateUserAgent(realName), + 'Content-Type': 'application/json', + 'Content-Encoding': 'deflate', + 'Mai-Encoding': this.version, + charset: 'UTF-8', + expect: '100-continue' + } + if (options?.userId && this.cookies.has(options.userId)) { + headers['Cookie'] = this.cookies.get(options.userId)! + } + const body = Encryption.Maimai.encrypt( + pako.deflate(new TextEncoder().encode(JSONParser.stringify(data))) + .buffer as ArrayBuffer, + this.encryption.maimai + ) as Uint8Array + let req = null + let startTime = Date.now() + while (1) { + req = await this.fetch(endpoint, { + method: 'POST', + headers, + body + }) + if (req.status === 200) break + if (Date.now() - startTime < (options?.maxTimeout ?? 10) * 1000) continue + } + if (!req || req.status !== 200) + throw new Error( + `${apiName} request failed: ${req?.status} ${req?.statusText}` + ) + const setCookie = req.headers.get('set-cookie') + if (setCookie && options?.userId) { + this.cookies.set(options.userId, setCookie) + } + let v = await req.arrayBuffer() + // fs.writeFileSync(`./${apiName}-${Date.now()}.bin`, Buffer.from(v)) + v = Encryption.Maimai.decrypt(v, this.encryption.maimai) + .buffer as ArrayBuffer + try { + v = pako.inflate(v).buffer as ArrayBuffer + } catch { + // error handling intentionally omitted + } + // console.log(`${apiName} -> ${JSONParser.stringify(data)}`) + const decrypted = new TextDecoder().decode(v) + return JSONParser.parse(decrypted === '' ? 'null' : decrypted) + } + + async login( + userId: number, + token: string, + options?: { + isContinue?: boolean + eventMode?: boolean + readOnly?: boolean + dateTime?: Date + loginDateTime?: Date + } + ) { + const isAlreadyLogin = this.preview(userId, token).then(v => !!v?.isLogin) + + const dateTime = Math.floor((options?.dateTime ?? this.created).getTime() / 1000) + const loginDateTime = Math.floor((options?.loginDateTime ?? new Date()).getTime() / 1000) + + const payload = { + userId, + accessCode: '', + regionId: this.place.region.id, + placeId: this.place.id, + clientId: this.shortChipId, + dateTime, + loginDateTime, + isContinue: !!options?.isContinue, + genericFlag: +!!options?.eventMode, + token + } + + const resp = options?.readOnly + ? { returnCode: 100, loginId: null, loginDateTime: 0, token: null } + : await this.request('UserLoginApi', payload, { userId }) + if (resp?.returnCode === 102 || resp?.returnCode === 100) { + return new User( + userId, + null, + options?.dateTime ?? null, + this, + await isAlreadyLogin, + resp.loginDateTime ? new Date(resp.loginDateTime * 1000) : (options?.loginDateTime ?? new Date()), + resp?.token ?? token + ) + } else if (resp?.returnCode !== 1) { + throw new Error(`Login failed: ${resp?.returnCode}`) + } + + return new User( + userId, + resp.loginId, + options?.dateTime ?? new Date(dateTime * 1000), + this, + true, + options?.loginDateTime ?? new Date(loginDateTime * 1000), + resp?.token ?? token + ) + } + + preview(userId: number, token: string) { + return this.request( + 'GetUserPreviewApi', + { + userId, + segaIdAuthKey: '', + token, + clientId: this.shortChipId + }, + { + userId + } + ) + } + + async parseQR(qrCode: string): Promise<{ userId: number; token: string }> { + const timestamp = qrCode.slice(8, 20) + const data = qrCode.slice(20) + + const json = (await this.fetch( + `http://${this.url.aime}/wc_aime/api/get_data`, + { + method: 'POST', + headers: { + 'User-Agent': 'WC_AIME_LIB' + }, + body: JSONParser.stringify({ + chipID: this.chipId, + openGameID: 'MAID', + key: (() => { + const key = forge.sha256.create() + key.update( + `${this.chipId}${timestamp}XcW5FW4cPArBXEk4vzKz3CIrMuA5EVVW` + ) + return key.digest().toHex().toUpperCase() + })(), + qrCode: data, + timestamp + }) + } + ).then(v => v.json())) as { + errorID: number + userID: number + token: string + [key: string]: unknown + } + if (json?.errorID !== 0) { + throw new Error(`QR code parse failed: ${json?.errorID}`) + } + return { + userId: json?.userID, + token: json?.token + } + } + + async getOpt(): Promise { + const response = await this.fetch( + `http://${this.url.allnet}/net/delivery/instruction`, + { + method: 'POST', + headers: { + 'User-Agent': `${this.codename};Windows/Lite`, + 'Content-Type': 'application/x-www-form-urlencoded' + }, + body: Encryption.Aime.encode( + `title_id=${this.codename}&title_ver=${this.version}&client_id=${this.shortChipId}\r\n`, + this.encryption.aime + ) + } + ) + .then(v => v.arrayBuffer()) + .then(v => Encryption.Aime.decode(v, this.encryption.aime)) + + type Configuration = { + result: string + uri: string + } + + // The URI may contain both an app download instruction URL + // and an optional image download instruction URL. + // These two URLs are concatenated together, with the optional image URL prefixed by |. + // This means if only an optional image is available, it will have the format: |https://url.to/opt + + const configuration: Configuration = Object.fromEntries( + response.split('&').map(v => { + const [key, value] = v.split('=') + return [key, decodeURIComponent(value ?? '')] + }) + ) as Configuration + + if (configuration.result !== '1') { + throw new Error(`GetOpt failed: ${configuration.result}`) + } + + const uris = configuration.uri.split('|').filter(v => v !== '') + const installUrls: string[] = [] + + for (const uri of uris) { + const config = await this.fetch(uri).then(v => v.text()) + + // For each Patch Image delivery instruction (パッチイメージ配信指示書ソース), + // there is one INSTALL1= URL, + // which points to a *.app file. + + // For each Option Image delivery instruction (オプションイメージ配信指示書ソース), + // there is one INSTALL1= URL in the COMMON section, + // as well as all historical options in the OPTIONAL section, start as INSTALL\d+=. + + // We assume that the file extension of the patch image is always .app, + // and the file extension of the option image is always .opt. + // Therefore, we only need to extract the INSTALL\d+= URLs from the config, and seperate them later. + + installUrls.push( + ...Array.from(config.matchAll(/INSTALL\d+=([^\r\n]+)/g)).map( + match => match[1] + ) + ) + } + return { + patch: installUrls.filter(v => v.endsWith('.app')).map(v => new URL(v)), + options: installUrls.filter(v => v.endsWith('.opt')).map(v => new URL(v)) + } + } + + static async create(options: CreateOption) { + if (!options.fetch) { + options.fetch = Http.makeRequest({ + rejectUnauthorized: false + }) + } + const response = await options + .fetch(`http://at.${options.url}/net/initialize`, { + method: 'POST', + headers: { + 'User-Agent': `${options.codename};Windows/Lite`, + 'Content-Type': 'application/x-www-form-urlencoded' + }, + body: Encryption.Aime.encode( + `title_id=${options.codename}&title_ver=${options.version}&client_id=${options.chipId + .replace(/-/g, '') + .slice(0, 11)}&token=${Math.floor(Math.random() * 4294967296)}\r\n`, + options.encryption.aime + ) + }) + .then(v => v.arrayBuffer()) + .then(v => Encryption.Aime.decode(v, options.encryption.aime)) + .then(v => v.slice(0, -2)) + + type Configuration = { + result: string + place_id: string + uri1: string + uri2: string + name: string + nickname: string + setting: string + region0: string + region_name0: string + region_name1: string + region_name2: string + region_name3: string + country: string + location_type: string + utc_time: string + client_timezone: string + res_ver: string + } + + const configuration: Configuration = Object.fromEntries( + response.split('&').map(v => { + const [key, value] = v.split('=') + return [key, decodeURIComponent(value ?? '')] + }) + ) as Configuration + return new Client({ + url: { + aime: 'ai.' + options.url, + allnet: 'at.' + options.url, + maimai: configuration.uri1 + 'Maimai2Servlet/' + }, + chipId: options.chipId, + version: options.version, + codename: options.codename, + created: new Date(), + region: configuration.country === 'CHN' ? 'Chn' : 'Exp', + placeInfo: { + id: parseInt(configuration.place_id, 16), + name: configuration.name, + nickname: configuration.nickname, + region: { + id: parseInt(configuration.region0), + name: [ + configuration.region_name0, + configuration.region_name1, + configuration.region_name2, + configuration.region_name3 + ] + } + }, + encryption: options.encryption, + fetch: options.fetch + }) + } + + public readonly fetch: ( + url: string | URL, + options?: { + method?: 'GET' | 'POST' + headers?: Record + body?: string | ArrayBuffer | Uint8Array + } + ) => Promise + public readonly url: { + aime: string + maimai: string + allnet: string + } + public readonly chipId: string + public readonly created: Date + public readonly version: string + public readonly region: 'Chn' | 'Exp' + public readonly codename: string + public readonly place: { + id: number + name: string + nickname: string + region: { + id: number + name: string[] + } + } + public readonly encryption: { + aime: Encryption.Aime.EncryptionParam + maimai: Encryption.Maimai.EncryptionParam + } + constructor({ + url, + chipId, + created, + version, + region, + codename, + placeInfo, + encryption, + fetch + }: { + url: { + aime: string + maimai: string + allnet: string + } + chipId: string + created: Date + version: string + region: 'Chn' | 'Exp' + codename: string + placeInfo: { + id: number + name: string + nickname: string + region: { + id: number + name: string[] + } + } + encryption: { + aime: Encryption.Aime.EncryptionParam + maimai: Encryption.Maimai.EncryptionParam + } + fetch: Client['fetch'] + }) { + this.url = url + this.chipId = chipId + this.created = created + this.version = version + this.region = region + this.codename = codename + this.place = placeInfo + this.encryption = encryption + this.fetch = fetch + } + get shortChipId(): string { + // A63E-01E12030000 -> A63E01E1203 + return this.chipId.replace(/-/g, '').slice(0, 11) + } +} diff --git a/src/containers/Character.ts b/src/containers/Character.ts new file mode 100644 index 0000000..94d41d9 --- /dev/null +++ b/src/containers/Character.ts @@ -0,0 +1,99 @@ +import { UserCharacter } from '../typings/api' + +import { values, entries } from '../utils/iterator' + +// TODO: 添加 nextAwake 和 nextAwakePercent 属性 (getter) +export interface Character { + point: number // 累计点数 + level: number + awakening: number +} + +export class CharacterSet { + private _character: Map = new Map() + private _modifiedCharacter: (Character & { + characterId: number + isNew: boolean + })[] = [] + get(characterId: number): Character | undefined { + const character = this._character.get(characterId) + if (!character) return undefined + return new Proxy(character, { + set: (target, prop, value) => { + if ( + ['point', 'level', ' awakening', 'useCount'].includes(prop as string) + ) { + Reflect.set(target, prop, value) + const modifiedCharacter = this._modifiedCharacter.find( + c => c.characterId === characterId + ) + if (modifiedCharacter) { + Reflect.set(modifiedCharacter, prop, value) + } else { + this._modifiedCharacter.push({ + characterId, + awakening: target.awakening, + level: target.level, + point: target.point, + isNew: false + }) + } + return Reflect.set(target, prop, value) + } + return false // 不允许修改其他属性 + }, + deleteProperty: () => false, + isExtensible: () => false + }) + } + set(characterId: number, character: Partial) { + const original = this._character.get(characterId) + const chr: Character = { + point: character?.point ?? 0, + level: character?.level ?? 0, + awakening: character?.awakening ?? 0 + } + if (original) { + Object.assign(original, chr) + } else { + this._character.set(characterId, { + ...chr + }) + this._modifiedCharacter.push({ + characterId, + ...chr, + isNew: true + }) + } + } + export(): { value: UserCharacter; isNew: boolean }[] { + return this._modifiedCharacter.map(v => ({ + value: { + characterId: v.characterId, + point: v.point, + level: v.level, + awakening: v.awakening, + useCount: 0 + }, + isNew: v.isNew + })) + } + keys() { + return this._character.keys() + } + values() { + return values(this) + } + entries() { + return entries(this) + } + constructor(data: UserCharacter[]) { + data.forEach(detail => { + this._character.set(detail.characterId, { + point: detail.point, + level: detail.level, + awakening: detail.awakening + }) + }) + } +} diff --git a/src/containers/Item.ts b/src/containers/Item.ts new file mode 100644 index 0000000..afcbe3c --- /dev/null +++ b/src/containers/Item.ts @@ -0,0 +1,95 @@ +import { UserItem, UserItemKind } from '../typings/api' + +import { values, entries } from '../utils/iterator' + +export interface Item { + stock: number // 库存数量 +} + +export function unpackPresent(itemId: number) { + // 从 itemId 抽出 itemKind 和真正的 itemId。 + /** + * 参照代码: + * itemType = (ItemKind)(presentId / (long)ConstParameter.PresentKindConvert); + * itemId = (int)(presentId % (long)ConstParameter.PresentKindConvert); + * 其中: + * public static int PresentKindConvert = 1000000; + */ + const kind = Math.floor(itemId / 1000000) as UserItemKind + const id = itemId % 1000000 + return [kind, id] +} + +export function packPresent(itemKind: UserItemKind, itemId: number): number { + return itemKind * 1000000 + itemId +} + +export class ItemSet { + private _item: Map = new Map() + private _modifiedItem: (Item & { + id: number + isNew: boolean + })[] = [] + get(itemId: number): number | undefined { + return this._item.get(itemId)?.stock + } + set(itemId: number, stock: number) { + const original = this._item.get(itemId) + const item: Item = { + stock + } + if (original) { + original.stock = stock + const modifiedItem = this._modifiedItem.find(i => i.id === itemId) + if (modifiedItem) { + modifiedItem.stock = stock + } else { + this._modifiedItem.push({ + id: itemId, + stock, + isNew: false + }) + } + } else { + this._item.set(itemId, { + ...item + }) + this._modifiedItem.push({ + id: itemId, + stock, + isNew: true + }) + } + } + export(): { value: UserItem; isNew: boolean }[] { + return this._modifiedItem.map(v => ({ + value: { + itemKind: this.present ? UserItemKind.Present : this.itemKind, + itemId: this.present ? packPresent(this.itemKind, v.id) : v.id, + stock: v.stock, + isValid: true + }, + isNew: v.isNew + })) + } + keys() { + return this._item.keys() + } + values() { + return values(this) + } + entries() { + return entries(this) + } + constructor( + data: UserItem[], + public readonly itemKind: UserItemKind, + public readonly present: boolean + ) { + data.forEach(detail => { + this._item.set(detail.itemId, { + stock: detail.stock + }) + }) + } +} diff --git a/src/containers/Mission.ts b/src/containers/Mission.ts new file mode 100644 index 0000000..e1985c0 --- /dev/null +++ b/src/containers/Mission.ts @@ -0,0 +1,14 @@ +import { UserWeeklyData, UserMissionData } from '../typings/api' + +export class Mission { + public weekly: UserWeeklyData + public mission: UserMissionData[] + constructor(weekly: UserWeeklyData, mission: UserMissionData[]) { + this.weekly = { + beforeLoginWeek: weekly.beforeLoginWeek ?? '1900-01-01 00:00:00', + lastLoginWeek: weekly.lastLoginWeek ?? '1900-01-01 00:00:00', + friendBonusFlag: !!weekly.friendBonusFlag + } + this.mission = mission + } +} diff --git a/src/containers/Score.ts b/src/containers/Score.ts new file mode 100644 index 0000000..408ae14 --- /dev/null +++ b/src/containers/Score.ts @@ -0,0 +1,284 @@ +import { + MusicClearRankID, + MusicDifficultyID, + PlayComboFlagID, + PlaySyncFlagID, + UserMusicDetail +} from '../typings/api' + +import { values, entries } from '../utils/iterator' + +export interface ScoreEntryInit { + playCount: number + achievement: number + comboStatus: PlayComboFlagID + syncStatus: PlaySyncFlagID + deluxscoreMax: number + extNum1: number // 理论值次数 +} + +interface ScoreInternal { + [MusicDifficultyID.Basic]?: ScoreEntryInit + [MusicDifficultyID.Advanced]?: ScoreEntryInit + [MusicDifficultyID.Expert]?: ScoreEntryInit + [MusicDifficultyID.Master]?: ScoreEntryInit + [MusicDifficultyID.ReMaster]?: ScoreEntryInit + [MusicDifficultyID.Utage]?: ScoreEntryInit +} + +export interface ScoreEntry extends ScoreEntryInit { + get scoreRank(): MusicClearRankID // calculated by achievement +} + +export interface Score { + get [MusicDifficultyID.Basic](): ScoreEntry | undefined + get [MusicDifficultyID.Advanced](): ScoreEntry | undefined + get [MusicDifficultyID.Expert](): ScoreEntry | undefined + get [MusicDifficultyID.Master](): ScoreEntry | undefined + get [MusicDifficultyID.ReMaster](): ScoreEntry | undefined + get [MusicDifficultyID.Utage](): ScoreEntry | undefined + set [MusicDifficultyID.Basic](value: ScoreEntryInit) + set [MusicDifficultyID.Advanced](value: ScoreEntryInit) + set [MusicDifficultyID.Expert](value: ScoreEntryInit) + set [MusicDifficultyID.Master](value: ScoreEntryInit) + set [MusicDifficultyID.ReMaster](value: ScoreEntryInit) + set [MusicDifficultyID.Utage](value: ScoreEntryInit) +} + +export function convertAchievementToScoreRank( + achievement: number, + utage?: boolean +): MusicClearRankID { + const allRanks = [ + MusicClearRankID.Rank_D, + MusicClearRankID.Rank_C, + MusicClearRankID.Rank_B, + MusicClearRankID.Rank_BB, + MusicClearRankID.Rank_BBB, + MusicClearRankID.Rank_A, + MusicClearRankID.Rank_AA, + MusicClearRankID.Rank_AAA, + MusicClearRankID.Rank_S, + MusicClearRankID.Rank_SP, + MusicClearRankID.Rank_SS, + MusicClearRankID.Rank_SSP, + MusicClearRankID.Rank_SSS + ] as const + const border = [ + 499999, // D: 0.0000% ~ 49.9999% + 599999, // C: 50.0000% ~ 59.9999% + 699999, // B: 60.0000% ~ 69.9999% + 749999, // BB: 70.0000% ~ 74.9999% + 799999, // BBB: 75.0000% ~ 79.9999% + 899999, // A: 80.0000% ~ 89.9999% + 939999, // AA: 90.0000% ~ 93.9999% + 969999, // AAA: 94.0000% ~ 96.9999% + 979999, // S: 97.0000% ~ 97.9999% + 989999, // S+: 98.0000% ~ 98.9999% + 994999, // SS: 99.0000% ~ 99.4999% + 999999, // SS+: 99.5000% ~ 99.9999% + 1004999 // SSS: 100.0000% ~ 104.9999% + ] as const + let rank = -1 + for (let i = 0; i < border.length; i++) { + const rankBorder = border[i] * (+!!utage + 1) + if (achievement <= rankBorder) { + rank = allRanks[i] + break + } + } + if (rank === -1 && achievement >= 1005000 && achievement <= 1010000) { + rank = MusicClearRankID.Rank_SSSP + } + return rank +} + +export class ScoreSet { + private _score: Map = new Map() + private _modifiedScore: (ScoreEntryInit & { + musicId: number + level: MusicDifficultyID + isNew: boolean + })[] = [] + static resettable(data: UserMusicDetail[]): [ScoreSet, () => void] { + const scoreSet = new ScoreSet(data) + return [scoreSet, () => (scoreSet._modifiedScore = [])] + } + constructor(data: UserMusicDetail[]) { + for (const detail of data) { + const score: ScoreInternal = this._score.get(detail.musicId) || {} + const entry = { + playCount: detail.playCount, + achievement: detail.achievement, + comboStatus: detail.comboStatus, + syncStatus: detail.syncStatus, + deluxscoreMax: detail.deluxscoreMax, + extNum1: detail.extNum1 + } + score[detail.level] = entry + this._score.set(detail.musicId, score) + } + } + get size(): number { + return this._score.size + } + get(musicId: number): Score { + let score = this._score.get(musicId) + if (!score) { + score = {} + this._score.set(musicId, score) + } + return new Proxy(score as Score, { + get: (target, prop) => { + if ( + !Reflect.has(target, prop) || + !(Number(prop) in MusicDifficultyID) + ) { + return undefined + } + const entry = Reflect.get(target, prop) as ScoreEntryInit + return new Proxy(entry as ScoreEntry, { + get: (entryTarget, entryProp) => { + if (entryProp === 'scoreRank') { + return convertAchievementToScoreRank( + entryTarget.achievement, + entryTarget.achievement > 1010000 && + Number(prop) === MusicDifficultyID.Utage + ) + } + return Reflect.get(entryTarget, entryProp) + }, + set: (entryTarget, entryProp, value) => { + if ( + [ + 'playCount', + 'achievement', + 'comboStatus', + 'syncStatus', + 'deluxscoreMax', + 'extNum1' + ].includes(entryProp as string) + ) { + Reflect.set(entryTarget, entryProp, value) + const existingScore = this._modifiedScore.find( + e => e.musicId === musicId && e.level === Number(prop) + ) + if (existingScore) { + Reflect.set(existingScore, entryProp, value) + } else { + this._modifiedScore.push({ + achievement: entryTarget.achievement, + playCount: entryTarget.playCount, + comboStatus: entryTarget.comboStatus, + syncStatus: entryTarget.syncStatus, + deluxscoreMax: entryTarget.deluxscoreMax, + musicId: musicId, + extNum1: entryTarget.extNum1, + level: Number(prop), + isNew: false + }) + } + return true + } + return false + }, + has: (_, entryProp) => { + return [ + 'playCount', + 'achievement', + 'comboStatus', + 'syncStatus', + 'deluxscoreMax', + 'scoreRank', + 'extNum1' + ].includes(entryProp as string) + }, + isExtensible: () => false, + ownKeys: () => [ + 'playCount', + 'achievement', + 'comboStatus', + 'syncStatus', + 'deluxscoreMax', + 'scoreRank', + 'extNum1' + ] + }) + }, + set: (target, prop, value) => { + if ( + Object.values(MusicDifficultyID) + .map(v => String(v)) + .includes(prop as string) + ) { + const isNew = !Reflect.has(target, prop) + value = { + playCount: Number(value?.playCount || 0), + achievement: Number(value?.achievement || 0), + comboStatus: Number(value?.comboStatus || PlayComboFlagID.None), + syncStatus: Number(value?.syncStatus || PlaySyncFlagID.None), + deluxscoreMax: Number(value?.deluxscoreMax || 0), + extNum1: Number(value?.extNum1 || 0) + } + Reflect.set(target, prop, value) + const entry: ScoreEntryInit & { + musicId: number + level: MusicDifficultyID + } = { + ...value, + musicId: musicId, + level: Number(prop) + } + const existingIndex = this._modifiedScore.findIndex( + e => e.musicId === entry.musicId && e.level === entry.level + ) + if (existingIndex !== -1) { + this._modifiedScore[existingIndex] = Object.assign({}, entry, { + isNew: this._modifiedScore[existingIndex].isNew || isNew + }) + } else { + this._modifiedScore.push({ + ...entry, + isNew + }) + } + return true + } + return false + }, + deleteProperty: () => false + }) + } + set(musicId: number, value: ScoreInternal) { + const dummyScore = this.get(musicId) + Object.assign(dummyScore, value) + } + keys() { + return this._score.keys() + } + values() { + return values(this) + } + export(): { value: UserMusicDetail; isNew: boolean }[] { + return this._modifiedScore.map(entry => ({ + value: { + musicId: entry.musicId, + level: entry.level, + playCount: entry.playCount, + achievement: entry.achievement, + comboStatus: entry.comboStatus, + syncStatus: entry.syncStatus, + deluxscoreMax: entry.deluxscoreMax, + scoreRank: convertAchievementToScoreRank( + entry.achievement, + entry.achievement > 1010000 && entry.level === MusicDifficultyID.Utage + ), + extNum1: entry.extNum1 + }, + isNew: entry.isNew + })) + } + entries() { + return entries(this) + } +} diff --git a/src/containers/index.ts b/src/containers/index.ts new file mode 100644 index 0000000..ac54ac0 --- /dev/null +++ b/src/containers/index.ts @@ -0,0 +1,4 @@ +export * from './Score' +export * from './Character' +export * from './Item' +export * from './Mission' diff --git a/src/encryption/aime.ts b/src/encryption/aime.ts new file mode 100644 index 0000000..513613e --- /dev/null +++ b/src/encryption/aime.ts @@ -0,0 +1,45 @@ +import forge from 'node-forge' + +export interface EncryptionParam { + key: string +} + +export function encode( + data: string, + options: EncryptionParam & { + iv?: Uint8Array + } +): Uint8Array { + const iv = + (options?.iv as Uint8Array) ?? + crypto.getRandomValues(new Uint8Array(16)) + const cipher = forge.cipher.createCipher( + 'AES-CBC', + forge.util.createBuffer(options.key) + ) + cipher.start({ iv: forge.util.createBuffer(iv) }) + cipher.update(forge.util.createBuffer(data)) + cipher.finish() + const encrypted = cipher.output.getBytes() + return new Uint8Array([ + ...iv, + ...Array.from(encrypted).map(v => v.charCodeAt(0)) + ]) +} +/** + * @param {Uint8Array} data + */ +export function decode(data: ArrayBuffer, options: EncryptionParam): string { + const iv = data.slice(0, 16) + const decipher = forge.cipher.createDecipher( + 'AES-CBC', + forge.util.createBuffer(options.key) + ) + decipher.start({ iv: forge.util.createBuffer(iv) }) + decipher.update(forge.util.createBuffer(data.slice(16))) + decipher.finish() + const decrypted = decipher.output.getBytes() + return new TextDecoder().decode( + new Uint8Array(Array.from(decrypted).map(v => v.charCodeAt(0))) + ) +} diff --git a/src/encryption/index.ts b/src/encryption/index.ts new file mode 100644 index 0000000..e077a5e --- /dev/null +++ b/src/encryption/index.ts @@ -0,0 +1,2 @@ +export * as Aime from './aime' +export * as Maimai from './maimai' diff --git a/src/encryption/maimai.ts b/src/encryption/maimai.ts new file mode 100644 index 0000000..237cfd8 --- /dev/null +++ b/src/encryption/maimai.ts @@ -0,0 +1,39 @@ +import forge from 'node-forge' + +export interface EncryptionParam { + key: string + iv: string + obfuscateParam: string +} + +export function encrypt( + data: ArrayBuffer, + options: EncryptionParam +): Uint8Array { + // aes encrypt + const cipher = forge.cipher.createCipher( + 'AES-CBC', + forge.util.createBuffer(options.key) + ) + cipher.start({ iv: options.iv }) + cipher.update(forge.util.createBuffer(data)) + cipher.finish() + return Uint8Array.from( + Array.from(cipher.output.getBytes()).map(v => v.charCodeAt(0)) + ) +} +export function decrypt( + data: ArrayBuffer, + options: EncryptionParam +): Uint8Array { + // aes decrypt + const decipher = forge.cipher.createDecipher( + 'AES-CBC', + forge.util.createBuffer(options.key) + ) + decipher.start({ iv: options.iv }) + decipher.update(forge.util.createBuffer(data)) + decipher.finish() + const decrypted = decipher.output.getBytes() + return new Uint8Array(Array.from(decrypted).map(v => v.charCodeAt(0))) +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..15b1e1a --- /dev/null +++ b/src/index.ts @@ -0,0 +1,9 @@ +export * from './encryption' +export * from './client' +export * from './user' +export * as Http from './utils/http' +export * as Date from './utils/date' +export * as Event from './utils/event' +export * as Iterator from './utils/iterator' +export * as Api from './typings/api' +export * from './containers' diff --git a/src/typings/api/GetUserActivityApi.ts b/src/typings/api/GetUserActivityApi.ts new file mode 100644 index 0000000..1489963 --- /dev/null +++ b/src/typings/api/GetUserActivityApi.ts @@ -0,0 +1,9 @@ +import { UserActivity } from './base' + +export interface Request { + userId: number +} + +export interface Response { + userActivity: UserActivity +} diff --git a/src/typings/api/GetUserCardApi.ts b/src/typings/api/GetUserCardApi.ts new file mode 100644 index 0000000..81cc9c2 --- /dev/null +++ b/src/typings/api/GetUserCardApi.ts @@ -0,0 +1,21 @@ +export interface Request { + userId: number + nextIndex: number | bigint + maxCount: number +} + +export interface CardItem { + cardId: number + cardTypeId: number + charaId: number + mapId: number + startDate: string + endDate: string +} +export interface Response { + userId: number + length: number + nextIndex: number | bigint + userCardList: CardItem[] | null + serialIdList: unknown[] | null // Unused? +} diff --git a/src/typings/api/GetUserCharacterApi.ts b/src/typings/api/GetUserCharacterApi.ts new file mode 100644 index 0000000..43eeb9f --- /dev/null +++ b/src/typings/api/GetUserCharacterApi.ts @@ -0,0 +1,22 @@ +import { UserCharacter } from './base' + +export interface Request { + userId: number +} + +export interface Response { + userId: number + length: number + userCharacterList: UserCharacter[] | null + // userCharacterList: + // | { + // characterId: number + // point: number + // useCount: number + // level: number + // nextAwake: number + // nextAwakePercent: number + // awakening: number // 0 = not awakened, 1 = first awakening, etc. + // }[] + // | null +} diff --git a/src/typings/api/GetUserChargeApi.ts b/src/typings/api/GetUserChargeApi.ts new file mode 100644 index 0000000..261d336 --- /dev/null +++ b/src/typings/api/GetUserChargeApi.ts @@ -0,0 +1,17 @@ +export interface Request { + userId: number +} + +export interface Response { + userId: number + length: number + userChargeList: + | { + chargeId: number + stock: number + purchaseDate: string // "YYYY-MM-DD HH:mm:ss" + validDate: string // "YYYY-MM-DD HH:mm:ss" + extNum1: number + }[] + | null +} diff --git a/src/typings/api/GetUserCourseApi.ts b/src/typings/api/GetUserCourseApi.ts new file mode 100644 index 0000000..ee2e709 --- /dev/null +++ b/src/typings/api/GetUserCourseApi.ts @@ -0,0 +1,13 @@ +import { UserCourse } from './base' + +export interface Request { + userId: number + nextIndex: number | bigint +} + +export interface Response { + userId: number + length: number + nextIndex: number | bigint + userCourseList: UserCourse[] | null +} diff --git a/src/typings/api/GetUserDataApi.ts b/src/typings/api/GetUserDataApi.ts new file mode 100644 index 0000000..2ab3398 --- /dev/null +++ b/src/typings/api/GetUserDataApi.ts @@ -0,0 +1,11 @@ +import { UserDetail } from './base' + +export interface Request { + userId: number +} + +export interface Response { + userId: number + userData: UserDetail + banState: 0 | 1 | 2 // 0 = not banned, 1 = warning, 2 = banned +} diff --git a/src/typings/api/GetUserExtendApi.ts b/src/typings/api/GetUserExtendApi.ts new file mode 100644 index 0000000..5e770f5 --- /dev/null +++ b/src/typings/api/GetUserExtendApi.ts @@ -0,0 +1,10 @@ +import { UserExtend } from './base' + +export interface Request { + userId: number +} + +export interface Response { + userId: number + userExtend: UserExtend +} diff --git a/src/typings/api/GetUserFavoriteApi.ts b/src/typings/api/GetUserFavoriteApi.ts new file mode 100644 index 0000000..8ed8cd2 --- /dev/null +++ b/src/typings/api/GetUserFavoriteApi.ts @@ -0,0 +1,12 @@ +import { UserFavorite } from './base' + +export interface Request { + userId: number + itemKind: number +} + +export interface Response { + userId: number + // NOTE: 无法参考来自 C# 的数据; typo? + userFavorite: UserFavorite +} diff --git a/src/typings/api/GetUserFavoriteItemApi.ts b/src/typings/api/GetUserFavoriteItemApi.ts new file mode 100644 index 0000000..3a8c7fb --- /dev/null +++ b/src/typings/api/GetUserFavoriteItemApi.ts @@ -0,0 +1,20 @@ +export interface Request { + userId: number + kind: number + nextIndex: number | bigint + maxCount: number + isAllFavoriteItem: boolean +} + +export interface FavoriteItem { + orderId: number + id: number +} + +export interface Response { + userId: number + kind: number + length: number + nextIndex: number | bigint + userFavoriteItemList: FavoriteItem[] | null +} diff --git a/src/typings/api/GetUserGhostApi.ts b/src/typings/api/GetUserGhostApi.ts new file mode 100644 index 0000000..3aa7d1c --- /dev/null +++ b/src/typings/api/GetUserGhostApi.ts @@ -0,0 +1,10 @@ +import { UserGhost } from './base/UserGhost' + +export interface Request { + userId: number +} + +export interface Response { + userId: number + userGhostList: UserGhost[] +} diff --git a/src/typings/api/GetUserItemApi.ts b/src/typings/api/GetUserItemApi.ts new file mode 100644 index 0000000..3c170ce --- /dev/null +++ b/src/typings/api/GetUserItemApi.ts @@ -0,0 +1,15 @@ +import { UserItem, UserItemKind } from './base' + +export interface Request { + userId: number + nextIndex: number | bigint + maxCount: number +} + +export interface Response { + userId: number + length: number + nextIndex: number | bigint + itemKind: UserItemKind + userItemList: UserItem[] | null // Array of user items, can be null if no items are present +} diff --git a/src/typings/api/GetUserLoginBonusApi.ts b/src/typings/api/GetUserLoginBonusApi.ts new file mode 100644 index 0000000..b38782c --- /dev/null +++ b/src/typings/api/GetUserLoginBonusApi.ts @@ -0,0 +1,14 @@ +import { UserLoginBonus } from './base' + +export interface Request { + userId: number + nextIndex: number | bigint + maxCount: number +} + +export interface Response { + userId: number + length: number + nextIndex: number | bigint + userLoginBonusList: UserLoginBonus[] | null +} diff --git a/src/typings/api/GetUserMapApi.ts b/src/typings/api/GetUserMapApi.ts new file mode 100644 index 0000000..73d745c --- /dev/null +++ b/src/typings/api/GetUserMapApi.ts @@ -0,0 +1,14 @@ +import { UserMap } from './base' + +export interface Request { + userId: number + nextIndex: number | bigint + maxCount: number +} + +export interface Response { + userId: number + length: number + nextIndex: number | bigint + userMapList: UserMap[] | null +} diff --git a/src/typings/api/GetUserMissionDataApi.ts b/src/typings/api/GetUserMissionDataApi.ts new file mode 100644 index 0000000..0077657 --- /dev/null +++ b/src/typings/api/GetUserMissionDataApi.ts @@ -0,0 +1,11 @@ +import { UserMissionData, UserWeeklyData } from './base' + +export interface Request { + userId: number // 用户 ID +} + +export interface Response { + userId: number + userWeeklyData: UserWeeklyData + userMissionDataList: UserMissionData[] +} diff --git a/src/typings/api/GetUserMusicApi.ts b/src/typings/api/GetUserMusicApi.ts new file mode 100644 index 0000000..359bced --- /dev/null +++ b/src/typings/api/GetUserMusicApi.ts @@ -0,0 +1,19 @@ +import { UserMusicDetail } from './base' + +export interface Request { + userId: number + nextIndex: number | bigint + maxCount: number +} + +interface ScoreItem { + userMusicDetailList: UserMusicDetail[] | null + length: number +} + +export interface Response { + userId: number + length: number + nextIndex: number | bigint + userMusicList: ScoreItem[] | null +} diff --git a/src/typings/api/GetUserOptionApi.ts b/src/typings/api/GetUserOptionApi.ts new file mode 100644 index 0000000..1f442cd --- /dev/null +++ b/src/typings/api/GetUserOptionApi.ts @@ -0,0 +1,10 @@ +import { UserOption } from './base' + +export interface Request { + userId: number +} + +export interface Response { + userId: number + userOption: UserOption +} diff --git a/src/typings/api/GetUserPreviewApi.ts b/src/typings/api/GetUserPreviewApi.ts new file mode 100644 index 0000000..bfa3773 --- /dev/null +++ b/src/typings/api/GetUserPreviewApi.ts @@ -0,0 +1,28 @@ +export interface Request { + userId: number + segaIdAuthKey: string + token: string + clientId: string +} + +export interface Response { + userId: number + userName: string + isLogin: boolean + lastGameId: number | null + lastRomVersion: string + lastDataVersion: string + lastLoginDate: string // "YYYY-MM-DD HH:mm:ss" + lastPlayDate: string // "YYYY-MM-DD HH:mm:ss" + playerRating: number + nameplateId: number + iconId: number + trophyId: number + isNetMember: 1 | 0 // 1 for true, 0 for false + isInherit: boolean + totalAwake: number + dispRate: number + dailyBonusDate: string // "YYYY-MM-DD HH:mm:ss" + headPhoneVolume: number | null // Nullable + banState: 0 | 1 | 2 // 0 = not banned, 1 = warning, 2 = banned +} diff --git a/src/typings/api/GetUserRatingApi.ts b/src/typings/api/GetUserRatingApi.ts new file mode 100644 index 0000000..6b65194 --- /dev/null +++ b/src/typings/api/GetUserRatingApi.ts @@ -0,0 +1,10 @@ +import { UserRating } from './base' + +export interface Request { + userId: number +} + +export interface Response { + userId: number + userRating: UserRating +} diff --git a/src/typings/api/GetUserRecommendRateMusicApi.ts b/src/typings/api/GetUserRecommendRateMusicApi.ts new file mode 100644 index 0000000..3ca358c --- /dev/null +++ b/src/typings/api/GetUserRecommendRateMusicApi.ts @@ -0,0 +1,16 @@ +import { MusicDifficultyID } from './base/MusicDifficultyID' + +export interface Request { + userId: number +} + +interface RecommendRateMusicItem { + musicId: number + level: MusicDifficultyID + averageAchievement: number +} + +export interface Response { + userId: number + userRecommendRateMusicIdList: RecommendRateMusicItem[] +} diff --git a/src/typings/api/GetUserRecommendSelectMusicApi.ts b/src/typings/api/GetUserRecommendSelectMusicApi.ts new file mode 100644 index 0000000..22e6b6c --- /dev/null +++ b/src/typings/api/GetUserRecommendSelectMusicApi.ts @@ -0,0 +1,8 @@ +export interface Request { + userId: number +} + +export interface Response { + userId: number + userRecommendSelectionMusicIdList: number[] +} diff --git a/src/typings/api/GetUserRegionApi.ts b/src/typings/api/GetUserRegionApi.ts new file mode 100644 index 0000000..56008f2 --- /dev/null +++ b/src/typings/api/GetUserRegionApi.ts @@ -0,0 +1,15 @@ +export interface Request { + userId: number +} + +export interface RegionItem { + regionId: number + playCount: number + created: string +} + +export interface Response { + userId: number + length: number + userRegionList: RegionItem[] | null +} diff --git a/src/typings/api/UploadUserPlaylogListApi.ts b/src/typings/api/UploadUserPlaylogListApi.ts new file mode 100644 index 0000000..1a2f9b9 --- /dev/null +++ b/src/typings/api/UploadUserPlaylogListApi.ts @@ -0,0 +1,127 @@ +import { MusicClearRankID } from './base' +import { MusicDifficultyID } from './base' +import { PlayComboFlagID } from './base' +import { PlaySyncFlagID } from './base' +import { UdemaeID } from './base' + +export interface Playlog { + userId: number // 0 + orderId: number // 0,未使用 + playlogId: bigint + version: number + placeId: number + placeName: string + loginDate: number + playDate: string // yyyy-mm-dd + userPlayDate: string // yyyy-mm-dd hh:mm:ss.0 + type: number // 来自谱面文件, 未使用 + musicId: number + level: MusicDifficultyID + trackNo: number // starting from 1 + vsMode: number // 0 = 无, 1 = 友人对战, 2 = npc, 3 = 其它 + vsStatus: number // 0 = 无, 1 = 我方胜利, 2 = 对方胜利 + vsUserName: string // 友人对战时的对手名称 + vsUserRating: number // 对方 rating + vsUserAchievement: number // 对方达成率 + vsUserGradeRank: number // 对方段位 (框: 待分析) + vsRank: number // 对方 rank? + playerNum: number // 玩家是几人模式 + playedUserId1: number // 玩家2的用户ID + playedUserName1: string // 玩家2的名称 + playedMusicLevel1: MusicDifficultyID // 玩家2的音乐难度 + playedUserId2: number // 玩家3的用户ID + playedUserName2: string // 玩家3的名称 + playedMusicLevel2: MusicDifficultyID // 玩家3的音乐难度 + playedUserId3: number // 玩家4的用户ID + playedUserName3: string // 玩家4的名称 + playedMusicLevel3: MusicDifficultyID // 玩家4的音乐难度 + characterId1: number // 当前玩家 的 角色1 id + characterAwakening1: number // 当前玩家 的 角色1 觉醒等级 + characterLevel1: number + characterId2: number // 当前玩家 的 角色2 id + characterAwakening2: number // 当前玩家 的 角色2 觉醒等级 + characterLevel2: number + characterId3: number // 当前玩家 的 角色3 id + characterAwakening3: number // 当前玩家 的 角色3 觉醒等级 + characterLevel3: number + characterId4: number // 当前玩家 的 角色4 id + characterAwakening4: number // 当前玩家 的 角色4 觉醒等级 + characterLevel4: number + characterId5: number // 当前玩家 的 角色5 id + characterAwakening5: number // 当前玩家 的 角色5 觉醒等级 + characterLevel5: number + achievement: number // 达成率 + deluxscore: number // dx 分 + scoreRank: MusicClearRankID // 评级 + maxCombo: number // 最大连击 + totalCombo: number // 总连击 + maxSync: number // 最大同步 + totalSync: number // 总同步 + tapCriticalPerfect: number // tap critical perfect 数 + tapPerfect: number // tap perfect 数 + tapGreat: number // tap great 数 + tapGood: number // tap good 数 + tapMiss: number // tap miss 数 + holdCriticalPerfect: number // hold critical perfect 数 + holdPerfect: number // hold perfect 数 + holdGreat: number // hold great 数 + holdGood: number // hold good 数 + holdMiss: number // hold miss 数 + slideCriticalPerfect: number // slide critical perfect 数 + slidePerfect: number // slide perfect 数 + slideGreat: number // slide great 数 + slideGood: number // slide good 数 + slideMiss: number // slide miss 数 + touchCriticalPerfect: number // touch critical perfect 数 + touchPerfect: number // touch perfect 数 + touchGreat: number // touch great 数 + touchGood: number // touch good 数 + touchMiss: number // touch miss 数 + breakCriticalPerfect: number // 绝赞 critical perfect 数 + breakPerfect: number // 绝赞 perfect 数 + breakGreat: number // 绝赞 great 数 + breakGood: number // 绝赞 good 数 + breakMiss: number // 绝赞 miss 数 + isTap: boolean // 铺面有无 tap + isHold: boolean // 铺面有无 hold + isSlide: boolean // 铺面有无 slide + isTouch: boolean // 铺面有无 touch + isBreak: boolean // 铺面有无绝赞 + isCriticalDisp: boolean // 有无 critical 显示 + isFastLateDisp: boolean // 有无 fast/late 显示 (一定为 true) + fastCount: number // fast 数 + lateCount: number // late 数 + isAchieveNewRecord: boolean // 是否达成新记录 + isDeluxscoreNewRecord: boolean // 是否达成 dx 分新记录 + comboStatus: PlayComboFlagID // 连击状态 + syncStatus: PlaySyncFlagID // 同步状态 + isClear: boolean // 是否通关 + beforeRating: number // 通关前 rating + afterRating: number // 通关后 rating + beforeGrade: number // 通关前友人对战 pt + afterGrade: number // 通关后友人对战 pt + afterGradeRank: UdemaeID // 通关后友人对战等级(B5, B4, etc.) + beforeDeluxRating: number // 通关前 dx rating 同 beforeRating + afterDeluxRating: number // 通关后 dx rating 同 afterRating + isPlayTutorial: boolean // 是否为教程 + isEventMode: boolean // 是否为大会模式 + isFreedomMode: boolean // 是否为自由模式 + playMode: number // 0 = 通常, 1 = 段位考核, 2 = 自由模式 + isNewFree: boolean // 是否为首局免费 + trialPlayAchievement: number // 教程达成率 没玩设置为 -1 + extNum1: number // 完美挑战曲开始生命 * 10000 - 当前生命 + extNum2: number // 段位考核 id 或 0 (如果没有使用段位考核模式) + extNum4: number // category id + extBool1: boolean // 是否为宴会场 buddies 谱 + extBool2: boolean // 是否随机选择 +} + +export interface Request { + userId: number + userPlaylogList: Playlog[] +} + +export interface Response { + returnCode: number + apiName: string +} diff --git a/src/typings/api/UpsertUserAllApi.ts b/src/typings/api/UpsertUserAllApi.ts new file mode 100644 index 0000000..d946eb2 --- /dev/null +++ b/src/typings/api/UpsertUserAllApi.ts @@ -0,0 +1,93 @@ +import { Playlog } from './UploadUserPlaylogListApi' +import { + MusicDifficultyID, + UserCharacter, + UserCharge, + UserCourse, + UserDetail, + UserExtend, + UserFavorite, + UserGamePlaylog, + UserGhost, + UserItem, + UserLoginBonus, + UserMap, + UserMusicDetail, + UserOption, + UserRating, + UserActivity, + UserMissionData, + UserWeeklyData, + UserIntimate, + UserShopStock, + UserGetPoint, + UserTradeItem, + UserFavoriteItem, + UserKaleidxScope +} from './base' + +export interface Request { + userId: number + playlogId: bigint + isEventMode: boolean + isFreePlay: boolean + loginDateTime: number + userPlaylogList: Playlog[] + upsertUserAll: { + userData: (UserDetail & { banState: number })[] + userExtend: UserExtend[] + userOption: UserOption[] + userCharacterList: UserCharacter[] + userGhost: UserGhost[] + userMapList: UserMap[] + userLoginBonusList: UserLoginBonus[] + userRatingList: UserRating[] + userItemList: UserItem[] + userMusicDetailList: UserMusicDetail[] + userCourseList: UserCourse[] + userFriendSeasonRankingList: unknown[] // TODO: Define type NOTE: 友人对战在 SDGB 未使用,原因为政策问题 + userChargeList: UserCharge[] + userFavoriteList: UserFavorite[] + userActivityList: UserActivity[] + userMissionDataList: UserMissionData[] + userWeeklyData: UserWeeklyData + userGamePlaylogList: UserGamePlaylog[] + user2pPlaylog: { + userId1: number + userId2: number + userName1: string + userName2: string + regionId: number + placeId: number + user2pPlaylogDetailList: { + musicId: number + level: MusicDifficultyID + achievement: number + deluxscore: number + userPlayDate: string // "yyyy-MM-dd HH:mm:ss.0" + }[] + } + userIntimateList: UserIntimate[] + userShopItemStockList: UserShopStock[] + userGetPointList: UserGetPoint[] + userTradeItemList: UserTradeItem[] + userFavoritemusicList: UserFavoriteItem[] + userKaleidxScopeList: UserKaleidxScope[] + isNewCharacterList: string + isNewMapList: string + isNewLoginBonusList: string + isNewItemList: string + isNewMusicDetailList: string + isNewCourseList: string + isNewFavoriteList: string + isNewFriendSeasonRankingList: string // 未使用 + isNewUserIntimateList: string + isNewFavoritemusicList: string // 一定为 "0" (奇异搞笑) + isNewKaleidxScopeList: string + } +} + +export interface Response { + returnCode: number + apiName: string +} diff --git a/src/typings/api/UpsertUserChargelogApi.ts b/src/typings/api/UpsertUserChargelogApi.ts new file mode 100644 index 0000000..381ac14 --- /dev/null +++ b/src/typings/api/UpsertUserChargelogApi.ts @@ -0,0 +1,12 @@ +import { UserCharge, UserChargelog } from './base' + +export interface Request { + userId: number + userChargelog: UserChargelog + userCharge: UserCharge +} + +export interface Response { + returnCode: number + apiName: string +} diff --git a/src/typings/api/UserLoginApi.ts b/src/typings/api/UserLoginApi.ts new file mode 100644 index 0000000..dabbfd6 --- /dev/null +++ b/src/typings/api/UserLoginApi.ts @@ -0,0 +1,22 @@ +export interface Request { + userId: number + accessCode: string + regionId: number + placeId: number + clientId: string + dateTime: number + loginDateTime: number + isContinue: boolean + genericFlag: number + token: string +} + +export interface Response { + returnCode: number + loginDateTime: number + token: string + lastLoginDate: string + loginCount: number + consecutiveLoginCount: number + loginId: bigint +} diff --git a/src/typings/api/UserLogoutApi.ts b/src/typings/api/UserLogoutApi.ts new file mode 100644 index 0000000..659d51a --- /dev/null +++ b/src/typings/api/UserLogoutApi.ts @@ -0,0 +1,22 @@ +export enum LogoutType { + None = 0, + Logout = 1, + Cancel = 2, + Error = 3, + TestIn = 4, + Quit = 5 +} + +export interface Request { + userId: number + accessCode: string + regionId: number + placeId: number + clientId: string + loginDateTime: number + type: LogoutType +} + +export interface Response { + returnCode: number +} diff --git a/src/typings/api/base/MusicClearRankID.ts b/src/typings/api/base/MusicClearRankID.ts new file mode 100644 index 0000000..3b3ec46 --- /dev/null +++ b/src/typings/api/base/MusicClearRankID.ts @@ -0,0 +1,16 @@ +export enum MusicClearRankID { + Rank_D = 0, + Rank_C = 1, + Rank_B = 2, + Rank_BB = 3, + Rank_BBB = 4, + Rank_A = 5, + Rank_AA = 6, + Rank_AAA = 7, + Rank_S = 8, + Rank_SP = 9, // S+ + Rank_SS = 10, + Rank_SSP = 11, // SS+ + Rank_SSS = 12, + Rank_SSSP = 13 // SSS+ +} diff --git a/src/typings/api/base/MusicDifficultyID.ts b/src/typings/api/base/MusicDifficultyID.ts new file mode 100644 index 0000000..4e3a84e --- /dev/null +++ b/src/typings/api/base/MusicDifficultyID.ts @@ -0,0 +1,9 @@ +export enum MusicDifficultyID { + Basic = 0, + Advanced = 1, + Expert = 2, + Master = 3, + ReMaster = 4, + // Strong = 5, // Strong is not used in the game + Utage = 10 +} diff --git a/src/typings/api/base/PlayComboFlagID.ts b/src/typings/api/base/PlayComboFlagID.ts new file mode 100644 index 0000000..98e2060 --- /dev/null +++ b/src/typings/api/base/PlayComboFlagID.ts @@ -0,0 +1,7 @@ +export enum PlayComboFlagID { + None = 0, + FullCombo = 1, // Full Combo (Silver) + FullComboPlus = 2, // Full Combo+ (Gold) + AllPerfect = 3, // All Perfect + AllPerfectPlus = 4 // All Perfect+ +} diff --git a/src/typings/api/base/PlaySyncFlagID.ts b/src/typings/api/base/PlaySyncFlagID.ts new file mode 100644 index 0000000..696f026 --- /dev/null +++ b/src/typings/api/base/PlaySyncFlagID.ts @@ -0,0 +1,8 @@ +export enum PlaySyncFlagID { + None = 0, + FullSync = 1, // ChainLow + FullSyncPlus = 2, // ChainHi + FullSyncDX = 3, // SyncLow + FullSyncDXPlus = 4, // SyncHi + SyncPlay = 5 +} diff --git a/src/typings/api/base/UdemaeID.ts b/src/typings/api/base/UdemaeID.ts new file mode 100644 index 0000000..46cd151 --- /dev/null +++ b/src/typings/api/base/UdemaeID.ts @@ -0,0 +1,28 @@ +export enum UdemaeID { + Class_B5 = 0, + Class_B4 = 1, + Class_B3 = 2, + Class_B2 = 3, + Class_B1 = 4, + Class_A5 = 5, + Class_A4 = 6, + Class_A3 = 7, + Class_A2 = 8, + Class_A1 = 9, + Class_S5 = 10, + Class_S4 = 11, + Class_S3 = 12, + Class_S2 = 13, + Class_S1 = 14, + Class_SS5 = 15, + Class_SS4 = 16, + Class_SS3 = 17, + Class_SS2 = 18, + Class_SS1 = 19, + Class_SSS5 = 20, + Class_SSS4 = 21, + Class_SSS3 = 22, + Class_SSS2 = 23, + Class_SSS1 = 24, + Class_LEGEND = 25 +} diff --git a/src/typings/api/base/UserActivity.ts b/src/typings/api/base/UserActivity.ts new file mode 100644 index 0000000..38a7846 --- /dev/null +++ b/src/typings/api/base/UserActivity.ts @@ -0,0 +1,42 @@ +export interface UserAct { + kind: 1 | 2 // 1: PlayResult, 2: PlayMusic + id: ActivityCode + sortNumber: number + param1: number + param2: number + param3: number + param4: number +} + +export enum ActivityCode { + PlayDX = 10, // 0, 0, 0, 0 + RankS = 20, // + RankSP = 21, + RankSS = 22, + RankSSP = 23, + RankSSS = 24, + RankSSSP = 25, + FullCombo = 30, + FullComboP = 31, + AllPerfect = 32, + AllPerfectP = 33, + FullSync = 40, + FullSyncP = 41, + FullSyncDx = 42, + FullSyncDxP = 43, + ClassUp_old = 50, + DxRate = 60, + AwakeMax = 70, + AwakePreMax = 71, + MapComplete = 80, + TransmissionMusic = 100, + TaskMusicClear = 110, + ChallengeMusicClear = 120, + RankUp = 130, + ClassUp = 140 +} + +export interface UserActivity { + playList: UserAct[] // 最大长度: 15 + musicList: UserAct[] // 最大长度: 10 +} diff --git a/src/typings/api/base/UserCharacter.ts b/src/typings/api/base/UserCharacter.ts new file mode 100644 index 0000000..b1d13ed --- /dev/null +++ b/src/typings/api/base/UserCharacter.ts @@ -0,0 +1,7 @@ +export interface UserCharacter { + characterId: number + point: number // 累计点数 + level: number + awakening: number + useCount: number // 使用其游玩的 pc 数 +} diff --git a/src/typings/api/base/UserCharge.ts b/src/typings/api/base/UserCharge.ts new file mode 100644 index 0000000..fea9420 --- /dev/null +++ b/src/typings/api/base/UserCharge.ts @@ -0,0 +1,19 @@ +export interface UserChargelog { + chargeId: number + price: number + purchaseDate: string + // Provided by the actual client but unnecessary for the API. It is possible to provide it for bypassing manual anticheat. + playCount?: number + // Provided by the actual client but unnecessary for the API. It is possible to provide it for bypassing manual anticheat. + playerRating?: number + placeId: number + regionId: number + clientId: string +} + +export interface UserCharge { + chargeId: number + stock: number + purchaseDate: string + validDate: string +} diff --git a/src/typings/api/base/UserCourse.ts b/src/typings/api/base/UserCourse.ts new file mode 100644 index 0000000..de36015 --- /dev/null +++ b/src/typings/api/base/UserCourse.ts @@ -0,0 +1,15 @@ +export interface UserCourse { + courseId: number + isLastClear: boolean + totalRestlife: number + totalAchievement: number + totalDeluxscore: number + bestAchievement: number + bestDeluxscore: number + bestAchievementDate: string // "YYYY-MM-DD HH:mm:ss" + bestDeluxscoreDate: string // "YYYY-MM-DD HH:mm:ss" + playCount: number + clearDate: string // "YYYY-MM-DD HH:mm:ss" + lastPlayDate: string // "YYYY-MM-DD HH:mm:ss" + extNum1: number // TODO: 分析 +} diff --git a/src/typings/api/base/UserDetail.ts b/src/typings/api/base/UserDetail.ts new file mode 100644 index 0000000..5484456 --- /dev/null +++ b/src/typings/api/base/UserDetail.ts @@ -0,0 +1,86 @@ +import { UdemaeID } from './UdemaeID' + +export interface UserDetail { + accessCode: string | null + userName: string + isNetMember: 1 | 0 + point: number + totalPoint: number + playerRating: number + playerOldRating: number + playerNewRating: number + highestRating: number + gradeRating: number + musicRating: number + gradeRank: UdemaeID + courseRank: number + classRank: number + frameId: number + iconId: number + // trophyId: number + plateId: number + titleId: number + partnerId: number + charaSlot: number[] + charaLockSlot: number[] + contentBit: number + selectMapId: number + playCount: number + currentPlayCount: number + playVsCount: number + playSyncCount: number + winCount: number + helpCount: number + comboCount: number + totalDeluxscore: number + totalBasicDeluxscore: number + totalAdvancedDeluxscore: number + totalExpertDeluxscore: number + totalMasterDeluxscore: number + totalReMasterDeluxscore: number + totalSync: number + totalBasicSync: number + totalAdvancedSync: number + totalExpertSync: number + totalMasterSync: number + totalReMasterSync: number + totalAchievement: number + totalBasicAchievement: number + totalAdvancedAchievement: number + totalExpertAchievement: number + totalMasterAchievement: number + totalReMasterAchievement: number + eventWatchedDate: string // "YYYY-MM-DD HH:mm:ss" + lastGameId: string | null + lastRomVersion: string + lastDataVersion: string + lastLoginDate: string // "YYYY-MM-DD HH:mm:ss" + lastPlayDate: string // "YYYY-MM-DD HH:mm:ss" + lastPairLoginDate: string // "YYYY-MM-DD HH:mm:ss" + lastTrialPlayDate: string // "YYYY-MM-DD HH:mm:ss" + lastPlayCredit: number + lastPlayMode: number + lastPlaceId: number + lastPlaceName: string | null + lastAllNetId: number + lastRegionId: number + lastRegionName: string + lastClientId: string | null + lastCountryCode: string + lastSelectEMoney: number + lastSelectTicket: number + lastSelectCourse: number + lastCountCourse: number + firstGameId: string + firstRomVersion: string + firstDataVersion: string + firstPlayDate: string // "YYYY-MM-DD HH:mm:ss" + compatibleCmVersion: string + totalAwake: number + dailyBonusDate: string // "YYYY-MM-DD HH:mm:ss" + dailyCourseBonusDate: string // "YYYY-MM-DD HH:mm:ss" + mapStock: number + renameCredit: number + friendRegistSkip: number + dateTime: number | null +} diff --git a/src/typings/api/base/UserExtend.ts b/src/typings/api/base/UserExtend.ts new file mode 100644 index 0000000..4ec8762 --- /dev/null +++ b/src/typings/api/base/UserExtend.ts @@ -0,0 +1,16 @@ +export interface UserExtend { + selectMusicId: number + selectDifficultyId: number + categoryIndex: number + musicIndex: number + extraFlag: number + selectScoreType: number + selectResultDetails: boolean + selectResultScoreViewType: number + sortCategorySetting: number + sortMusicSetting: number + selectedCardList: number[] + encountMapNpcList: { npcId: number; musicId: number }[] + extendContentBit: number + playStatusSetting: number +} diff --git a/src/typings/api/base/UserFavorite.ts b/src/typings/api/base/UserFavorite.ts new file mode 100644 index 0000000..410e096 --- /dev/null +++ b/src/typings/api/base/UserFavorite.ts @@ -0,0 +1,5 @@ +export interface UserFavorite { + userId?: number // 0 TODO: 分析 + itemKind: number + itemIdList: number[] +} diff --git a/src/typings/api/base/UserFavoriteItem.ts b/src/typings/api/base/UserFavoriteItem.ts new file mode 100644 index 0000000..79f2163 --- /dev/null +++ b/src/typings/api/base/UserFavoriteItem.ts @@ -0,0 +1,4 @@ +export interface UserFavoriteItem { + orderId: number // 下标 + id: number // 乐曲 id +} diff --git a/src/typings/api/base/UserGamePlaylog.ts b/src/typings/api/base/UserGamePlaylog.ts new file mode 100644 index 0000000..90c76ab --- /dev/null +++ b/src/typings/api/base/UserGamePlaylog.ts @@ -0,0 +1,16 @@ +export interface UserGamePlaylog { + playlogId: bigint + version: string + playDate: string // "yyyy-MM-dd HH:mm:ss.0" + playMode: number + useTicketId: number + playCredit: number + playTrack: number + clientId: string + isPlayTutorial: boolean + isEventMode: boolean + isNewFree: boolean + playCount: number + playSpecial: number + playOtherUserId: number +} diff --git a/src/typings/api/base/UserGetPoint.ts b/src/typings/api/base/UserGetPoint.ts new file mode 100644 index 0000000..970003f --- /dev/null +++ b/src/typings/api/base/UserGetPoint.ts @@ -0,0 +1,10 @@ +export enum MaiMileGetKind { + Mission = 1, + FriendBonus = 2, + Present = 3 +} + +export interface UserGetPoint { + getKind: MaiMileGetKind // 获得方式 + point: number // 获得的点数 +} diff --git a/src/typings/api/base/UserGhost.ts b/src/typings/api/base/UserGhost.ts new file mode 100644 index 0000000..4f8f5ec --- /dev/null +++ b/src/typings/api/base/UserGhost.ts @@ -0,0 +1,24 @@ +import { MusicDifficultyID } from './MusicDifficultyID' +import { UdemaeID } from './UdemaeID' + +export interface UserGhost { + name: string + iconId: number + plateId: number + titleId: number + rate: number + udemaeRate: number + courseRank: number + classRank: UdemaeID + classValue: number + playDatetime: string + shopId: number + regionCode: number + typeId: MusicDifficultyID + musicId: number + difficulty: number // TODO: お願いだから誰か分析してくれ + version: number + resultBitList: number[] + resultNum: number + achievement: number +} diff --git a/src/typings/api/base/UserIntimate.ts b/src/typings/api/base/UserIntimate.ts new file mode 100644 index 0000000..e43d4f7 --- /dev/null +++ b/src/typings/api/base/UserIntimate.ts @@ -0,0 +1,5 @@ +export interface UserIntimate { + partnerId: number // 伙伴 ID + intimateLevel: number // 亲密度等级 + intimateCountRewarded: number // 下一个会得到奖励的亲密度等级 +} diff --git a/src/typings/api/base/UserItem.ts b/src/typings/api/base/UserItem.ts new file mode 100644 index 0000000..509ed61 --- /dev/null +++ b/src/typings/api/base/UserItem.ts @@ -0,0 +1,21 @@ +export enum UserItemKind { + Plate = 1, // Name plate + Title = 2, // Title + Icon = 3, // User icon + Present = 4, // Present + Music = 5, // Music (DX Charts) + MusicMas = 6, // Music (Master Difficulty) + MusicRem = 7, // Music (Re:Master Difficulty) + MusicSrg = 8, // (Unused) Music (Strong Difficulty) + Character = 9, // Character + Partner = 10, // Partner + Frame = 11, // Frame + Ticket = 12 // Ticket +} + +export interface UserItem { + itemKind: UserItemKind + itemId: number + stock: number + isValid: boolean +} diff --git a/src/typings/api/base/UserKaleidxScope.ts b/src/typings/api/base/UserKaleidxScope.ts new file mode 100644 index 0000000..3623f16 --- /dev/null +++ b/src/typings/api/base/UserKaleidxScope.ts @@ -0,0 +1,20 @@ +export interface UserKaleidxScope { + gateId: number // 门 ID + isGateFound: boolean // 是否已发现门 + isKeyFound: boolean // 是否已发现钥匙 + isClear: boolean // 是否已通关 + + // 以下没打设置为 0 + totalRestLife: number // 剩余生命 + totalAchievement: number // 总达成率 + totalDeluxscore: number // 总 dx 分数 + bestAchievement: number // 最佳达成率 + bestDeluxscore: number // 最佳 dx 分数 + bestAchievementDate: string // "YYYY-MM-DD HH:mm:ss.0" 没玩设置为空 + bestDeluxscoreDate: string // "YYYY-MM-DD HH:mm:ss.0" 没玩设置为空 + playCount: number // 游戏次数 + clearDate: string // "YYYY-MM-DD HH:mm:ss.0" 没玩设置为空 + lastPlayDate: string // "YYYY-MM-DD HH:mm:ss.0" 没玩设置为空 + + isInfoWatched: boolean // 是否已观看信息 +} diff --git a/src/typings/api/base/UserLoginBonus.ts b/src/typings/api/base/UserLoginBonus.ts new file mode 100644 index 0000000..e38c80c --- /dev/null +++ b/src/typings/api/base/UserLoginBonus.ts @@ -0,0 +1,6 @@ +export interface UserLoginBonus { + bonusId: number + point: number + isCurrent: boolean + isComplete: boolean +} diff --git a/src/typings/api/base/UserMap.ts b/src/typings/api/base/UserMap.ts new file mode 100644 index 0000000..dec59fb --- /dev/null +++ b/src/typings/api/base/UserMap.ts @@ -0,0 +1,8 @@ +export interface UserMap { + mapId: number + distance: number + isLock: boolean + isClear: boolean + isComplete: boolean + unlockFlag: 0 | 1 // 0: Not unlocked, 1: Unlocked +} diff --git a/src/typings/api/base/UserMissionData.ts b/src/typings/api/base/UserMissionData.ts new file mode 100644 index 0000000..64432cf --- /dev/null +++ b/src/typings/api/base/UserMissionData.ts @@ -0,0 +1,22 @@ +import { MusicClearRankID } from './MusicClearRankID' + +export enum MissionLevelID { + Level1 = 0, // 任务难度 1 + Level2 = 1 // 任务难度 2 +} + +export enum MissionTypeID { + Login = 0, // 登入指定天数 + MusicPlay = 1, // 游玩曲目 (游玩指定曲目?) + Buddy = 2 // 拼机 (游玩指定曲目?) +} + +export interface UserMissionData { + type: MissionTypeID + difficulty: MissionLevelID + targetGenreId: number // 1 = 在 type 为 MusicPlay 的情况下,任务指定了流派 + targetGenreTableId: number // 在 type 不是 Login 的情况下,可指定乐曲 ID, 否则设置为 0 + conditionGenreId: number // 1 = 在 type 为 MusicPlay 的情况下,任务指定了流派 + conditionGenreTableId: MusicClearRankID // 在游玩乐曲条件下需要至少达到的评级 (sega 程序员在这里整了两个完全相同的 enum,奇异搞笑,因此使用 ClearRank 替代) + clearFlag: boolean // 是否已完成任务 +} diff --git a/src/typings/api/base/UserMusicDetail.ts b/src/typings/api/base/UserMusicDetail.ts new file mode 100644 index 0000000..5b11f7c --- /dev/null +++ b/src/typings/api/base/UserMusicDetail.ts @@ -0,0 +1,17 @@ +import { MusicClearRankID } from './MusicClearRankID' +import { MusicDifficultyID } from './MusicDifficultyID' +import { PlayComboFlagID } from './PlayComboFlagID' +import { PlaySyncFlagID } from './PlaySyncFlagID' + +export interface UserMusicDetail { + musicId: number + level: MusicDifficultyID + playCount: number + achievement: number + comboStatus: PlayComboFlagID + syncStatus: PlaySyncFlagID + deluxscoreMax: number + scoreRank: MusicClearRankID + extNum1: number // 理论值次数 + // extNum2: number // 0, 未使用 +} diff --git a/src/typings/api/base/UserOption.ts b/src/typings/api/base/UserOption.ts new file mode 100644 index 0000000..1ab7dc9 --- /dev/null +++ b/src/typings/api/base/UserOption.ts @@ -0,0 +1,243 @@ +export enum OptionKindID { + Basic = 0, // 适合新手的设置 + Advance = 1, // 适合中级玩家的设置 + Expert = 2, // 适合高级玩家的设置 + Custom = 3 // 自定义设置 +} + +export enum OptionGameTapID { + Default = 0, // 默认 (dx) + Legacy = 1, // 经典 + Bear = 2, // 滴蜡熊 note + Bar = 3, // 条形 + Any = 4 // tapくん? +} + +export enum OptionGameHoldID { + Default = 0, // 默认 (dx) + Legacy = 1 // 经典 +} + +export enum OptionGameSlideID { + Default = 0, // 默认 (dx) + Legacy = 1 // 经典 +} + +export enum OptionStarTypeID { + Blue = 0, // 蓝色星星头 + Red = 1 // 红色星星头 +} + +export enum OptionGameOutlineID { + Hide = 0, // 隐藏 + Dot = 1, // 散点 + Simple = 2, // 简洁 + Sensor = 3, // 传感器 + Maimai = 4, // maimai + GreeN = 5, // maimai GreeN + ORANGE = 6, // maimai ORANGE + PiNK = 7, // maimai PiNK + MURASAKi = 8, // maimai MURASAKi + MiLK = 9, // maimai MiLK + FiNALE = 10, // maimai FiNALE + DX = 11, // maimai DX (无印) + Splash = 12, // maimai DX Splash + UNiVERSE = 13, // maimai DX UNiVERSE + FESTiVAL = 14, // maimai DX FESTiVAL + BUDDiES = 15, // maimai DX BUDDiES + PRiSM = 16 // maimai DX PRiSM +} + +export enum OptionNoteSizeID { + Small = 0, + Middle = 1, + Big = 2 +} + +export type OptionSlideSizeID = OptionNoteSizeID + +export enum OptionTouchSizeID { + Small = 0, // 小 + Middle = 1 // 中 + // 无效 +} + +export enum OptionMirrorID { + Normal = 0, // 正常 + LR = 1, // 左右镜像 + UD = 2, // 上下镜像 + UDLR = 3 // 上下左右镜像 + // 无效 +} + +export enum OptionMovieBrightnessID { + Dark = 0, // 暗 + Darker = 1, // 较暗 + Brighter = 2, + Bright = 3 // 亮 + // 无效 +} + +export enum OptionDispRateID { + AllDisp = 0, // 全部显示 + DispRateDan = 1, // 显示 rating 和段位 + DispRateClass = 2, // 显示 rating 和友人对战等级 + DispDanClass = 3, // 显示段位和友人对战等级 + DispRate = 4, // 显示 rating + DispDan = 5, // 显示段位 + DispClass = 6, // 显示友人对战等级 + Hide = 7 // 隐藏 + // 无效 +} + +export enum OptionCenterDisplayID { + Off = 0, // 关闭 + Combo = 1, // 显示连击 + AchievementPlus = 2, // 显示达成率 (0%+) + AchievementMinus1 = 3, // 显示达成率 (100%-) + AchievementMinus2 = 4, // 显示达成率 (101%-) + SBorder = 5, // 显示距离 S 评级最低达成率的容差 + SSBorder = 6, // 显示距离 SS 评级最低达成率的容差 + SSSBorder = 7, // 显示距离 SSS 评级最低达成率的容差 + BestBorder = 8, // 显示距离最佳达成率的容差 + DeluxScore = 9, // 显示 DX 分数 (+) + DeluxScoreMinus = 10, // 显示 DX 分数 (-) + DeluxScoreStar = 11 // 显示 DX 分数 (-, 带星级显示) + // 无效 +} + +export enum OptionDispChainID { + Off = 0, // 关闭 + Achievement = 1, // VS 达成率 + Sync = 2 // Sync 连击数 + // 无效 +} + +export enum OptionTrackSkipID { + Off = 0, // 关闭 + Push = 1, // 按钮跳过 + AutoS = 2, // 自动 (S) + AutoSS = 3, // 自动 (SS) + AutoSSS = 4, // 自动 (SSS) + AutoBest = 5, // 自动 (个人最佳成绩) + AutoLife300 = 6, // 自动 (完美挑战 300 生命值) + AutoLife100 = 7, // 自动 (完美挑战 100 生命值) + AutoLife50 = 8, // 自动 (完美挑战 50 生命值) + AutoLife10 = 9, // 自动 (完美挑战 10 生命值) + AutoLife1 = 10 // 自动 (完美挑战 1 生命值) +} + +export enum OptionTouchEffectID { + Off = 0, // 关闭 + Outline = 1, // 仅在外圈显示 + On = 2 // 开启 + // 无效 +} + +export enum OptionSubMonitorID { + AnimationType1 = 0, // 动画类型1 + CharacterOnly = 1, + AchievementOnly = 2 + // 无效 +} +export enum OptionSubMonitorAchievementID { + AchievementPlus = 0, // 达成率 (0%+) + AchievementMinus = 1 // 达成率 (101%-) + // 无效 +} + +export enum OptionAppealID { + Off = 0, // 关闭 + Together = 1, // 不一起来玩吗! + Tiho = 2, // 一起前进吗? + GoldPass = 3, // 想玩 4 曲小分队! + FullSync = 4, // 以完全同步为目标吧! + AllPlay = 5 // 全制霸者募集! + // 无效 +} + +export enum OptionOutFrameDisplayID { + Off = 0, // 关闭 + AchievementPlus = 1, // 显示达成率 (0%+) + AchievementMinus1 = 2, // 显示达成率 (100%-) + AchievementMinus2 = 3, // 显示达成率 (101%-) + DxScorePlus = 4, // 显示 DX 分数 (+) + DxScoreMinus = 5, // 显示 DX 分数 (-) + FastLate = 6, // 显示 Fast/Late + Judge = 7 // 显示详细判定 + // 无效 +} + +export enum SortTabID { + Genre = 0, // 流派 + All = 1, // 全曲 + Version = 2, // 版本 + Level = 3, // 等级 + Name = 4, // 名称 + Rank = 5 // 评级 + // 无效 +} + +export enum SortMusicID { + ID = 0, // 按追加顺序? + Level = 1, // 按等级 + Rank = 2, // 按评级 + ApFc = 3, // 按完成状态 (AP/FC) + Sync = 4, // 按同步状态 + Name = 5, // 按名称 + DxScore = 6, // 按 DX 分数 + BPM = 7 // 按 BPM + // 无效 +} + +export interface UserOption { + optionKind: OptionKindID + noteSpeed: number // 0 = 1.0, 1 = 1.25, 2 = 1.5, 3 = 1.75, 4 = 2.0 ..., 36 = 10.0, 37 = Sonic 速, -1 = Invalid + slideSpeed: number // 0 = -1.0, 1 = -0.9, ..., 20 = +1.0, -1 = Invalid + touchSpeed: number // touch 速度,同 noteSpeed + noteSize: OptionNoteSizeID + slideSize: OptionSlideSizeID + touchSize: OptionTouchSizeID + tapDesign: OptionGameTapID + holdDesign: OptionGameHoldID + slideDesign: OptionGameSlideID + starType: OptionStarTypeID + starRotate: number // 0 = 关闭, 1 = 开启 + adjustTiming: number // A判 0 = -2.0, 1 = -1.9, ..., 40 = +2.0, -1 = Invalid + judgeTiming: number // B判 0 = -2.0, 1 = -1.9, ..., 40 = +2.0, -1 = Invalid + mirrorMode: OptionMirrorID + ansVolume: number // 正解音 0 = 静音, 最大 5, -1 = Invalid + // tempoVolume: number // 节拍 0 = 静音, 最大 5, -1 = Invalid + tapHoldVolume: number // tap hold 0 = 静音, 最大 5, -1 = Invalid + touchHoldVolume: number // touch hold 0 = 静音, 最大 5, -1 = Invalid + breakVolume: number // 绝赞 0 = 静音, 最大 5, -1 = Invalid + exVolume: number // extap 0 = 静音, 最大 5, -1 = Invalid + slideVolume: number // slide 0 = 静音, 最大 5, -1 = Invalid + breakSe: number // 音效种类 不分析 + slideSe: number // 音效种类 不分析 + exSe: number // 音效种类 不分析 + criticalSe: number // 音效种类 不分析 + tapSe: number // 音效种类 不分析 + headPhoneVolume: number // 0 = 音量1, 最大 19, -1 = Invalid + matching: number // 允许友人对战 0 = 关闭, 1 = 开启, -1 = Invalid + brightness: OptionMovieBrightnessID + dispRate: OptionDispRateID // profile 显示 + dispCenter: OptionCenterDisplayID + dispJudge: number // 判定类型0 = 1A, 1 = 1B, 2 = IC, 3 = ID, 4 = IE, 5 = 2A, ..., 12 = 3D, -1 = Invalid + dispJudgePos: number // 通常判定显示位置 0 = 关闭, 1 = 最内侧, 2 = 内侧, 3 = 中间, 4 = 外侧, 5 = 最外侧, -1 = Invalid + dispJudgeTouchPos: number // touch 判定显示位置 0 = 关闭, 1 = 内侧, 2 = 外侧, -1 = Invalid + dispChain: OptionDispChainID + dispBar: number // 是否显示上方条 0 = 关闭, 1 = 开启, -1 = Invalid + trackSkip: OptionTrackSkipID // track skip 设定 + touchEffect: OptionTouchEffectID // 触摸到屏幕时的效果 + outlineDesign: OptionGameOutlineID + submonitorAnimation: OptionSubMonitorID // 上屏显示动画类型 (TODO: 待分析) + submonitorAppeal: OptionAppealID + submonitorAchive: OptionSubMonitorAchievementID // 上屏显示达成率 + sortTab: SortTabID // 曲目分页 + sortMusic: SortMusicID // 曲目排序方式 + damageSeVolume: number // 完美挑战伤害音 0 = 静音, 最大 5, -1 = Invalid + touchVolume: number // touch 0 = 静音, 最大 5, -1 = Invalid + outFrameType: OptionOutFrameDisplayID + breakSlideVolume: number // 绝赞 slide 0 = 静音, 最大 5, -1 = Invalid +} diff --git a/src/typings/api/base/UserRating.ts b/src/typings/api/base/UserRating.ts new file mode 100644 index 0000000..af0fcd0 --- /dev/null +++ b/src/typings/api/base/UserRating.ts @@ -0,0 +1,41 @@ +import { MusicDifficultyID } from './MusicDifficultyID' + +export interface UserRate { + musicId: number + level: MusicDifficultyID + romVersion: number + achievement: number +} + +export interface UserRating { + rating: number // 0? + ratingList: UserRate[] + newRatingList: UserRate[] + nextRatingList: UserRate[] + nextNewRatingList: UserRate[] + udemae: { + maxLoseNum: number + npcTotalWinNum: number + npcTotalLoseNum: number + npcMaxWinNum: number + npcMaxLoseNum: number + npcWinNum: number + npcLoseNum: number + rate: number + classValue: number + maxRate: number + maxClassValue: number + totalWinNum: number + totalLoseNum: number + maxWinNum: number + // MaxLoseNum: number + winNum: number + loseNum: number + // NpcTotalWinNum: number + // NpcTotalLoseNum: number + // NpcMaxWinNum: number + // NpcMaxLoseNum: number + // NpcWinNum: number + // NpcLoseNum: number + } +} diff --git a/src/typings/api/base/UserShopStock.ts b/src/typings/api/base/UserShopStock.ts new file mode 100644 index 0000000..c5d245e --- /dev/null +++ b/src/typings/api/base/UserShopStock.ts @@ -0,0 +1,4 @@ +export interface UserShopStock { + shopItemId: number // 商店物品 ID + tradeCount: number // 交易次数 +} diff --git a/src/typings/api/base/UserTradeItem.ts b/src/typings/api/base/UserTradeItem.ts new file mode 100644 index 0000000..5509f23 --- /dev/null +++ b/src/typings/api/base/UserTradeItem.ts @@ -0,0 +1,5 @@ +export interface UserTradeItem { + shopItemId: number // 商店物品 ID + point: number // 交易所需点数 + tradeCount: number // 交易次数 +} diff --git a/src/typings/api/base/UserWeeklyData.ts b/src/typings/api/base/UserWeeklyData.ts new file mode 100644 index 0000000..42aa88d --- /dev/null +++ b/src/typings/api/base/UserWeeklyData.ts @@ -0,0 +1,5 @@ +export interface UserWeeklyData { + lastLoginWeek: string // "yyyy/mm/dd",从 GetGameWeeklyDataApi 下载 + beforeLoginWeek: string // "yyyy/mm/dd",从 GetGameWeeklyDataApi 下载 + friendBonusFlag: boolean // 是否有好友奖励 +} diff --git a/src/typings/api/base/index.ts b/src/typings/api/base/index.ts new file mode 100644 index 0000000..638256d --- /dev/null +++ b/src/typings/api/base/index.ts @@ -0,0 +1,28 @@ +export * from './MusicClearRankID' +export * from './MusicDifficultyID' +export * from './PlayComboFlagID' +export * from './PlaySyncFlagID' +export * from './UdemaeID' +export * from './UserCharacter' +export * from './UserCharge' +export * from './UserCourse' +export * from './UserDetail' +export * from './UserExtend' +export * from './UserFavorite' +export * from './UserGamePlaylog' +export * from './UserGhost' +export * from './UserItem' +export * from './UserLoginBonus' +export * from './UserMap' +export * from './UserMusicDetail' +export * from './UserOption' +export * from './UserRating' +export * from './UserActivity' +export * from './UserMissionData' +export * from './UserWeeklyData' +export * from './UserIntimate' +export * from './UserShopStock' +export * from './UserGetPoint' +export * from './UserFavoriteItem' +export * from './UserTradeItem' +export * from './UserKaleidxScope' diff --git a/src/typings/api/index.ts b/src/typings/api/index.ts new file mode 100644 index 0000000..a446e35 --- /dev/null +++ b/src/typings/api/index.ts @@ -0,0 +1,81 @@ +import * as GetUserPreviewApi from './GetUserPreviewApi' +import * as UserLoginApi from './UserLoginApi' +import * as GetUserDataApi from './GetUserDataApi' +import * as GetUserCardApi from './GetUserCardApi' +import * as GetUserCharacterApi from './GetUserCharacterApi' +import * as GetUserItemApi from './GetUserItemApi' +import * as GetUserCourseApi from './GetUserCourseApi' +import * as GetUserChargeApi from './GetUserChargeApi' +import * as GetUserRatingApi from './GetUserRatingApi' +import * as GetUserMusicApi from './GetUserMusicApi' +import * as GetUserActivityApi from './GetUserActivityApi' +import * as GetUserExtendApi from './GetUserExtendApi' +import * as GetUserOptionApi from './GetUserOptionApi' +import * as GetUserRecommendSelectMusicApi from './GetUserRecommendSelectMusicApi' +import * as GetUserRecommendRateMusicApi from './GetUserRecommendRateMusicApi' +import * as GetUserRegionApi from './GetUserRegionApi' +import * as GetUserFavoriteApi from './GetUserFavoriteApi' +import * as GetUserFavoriteItemApi from './GetUserFavoriteItemApi' +import * as GetUserGhostApi from './GetUserGhostApi' +import * as GetUserMapApi from './GetUserMapApi' +import * as GetUserLoginBonusApi from './GetUserLoginBonusApi' +import * as UserLogoutApi from './UserLogoutApi' +import * as UpsertUserChargelogApi from './UpsertUserChargelogApi' +import * as UploadUserPlaylogListApi from './UploadUserPlaylogListApi' +import * as UpsertUserAllApi from './UpsertUserAllApi' +import * as GetUserMissionDataApi from './GetUserMissionDataApi' + +export * from './base' + +export interface Api { + GetUserPreviewApi: [GetUserPreviewApi.Request, GetUserPreviewApi.Response] + UserLoginApi: [UserLoginApi.Request, UserLoginApi.Response] + GetUserDataApi: [GetUserDataApi.Request, GetUserDataApi.Response] + GetUserCardApi: [GetUserCardApi.Request, GetUserCardApi.Response] + GetUserCharacterApi: [ + GetUserCharacterApi.Request, + GetUserCharacterApi.Response + ] + GetUserItemApi: [GetUserItemApi.Request, GetUserItemApi.Response] + GetUserCourseApi: [GetUserCourseApi.Request, GetUserCourseApi.Response] + GetUserChargeApi: [GetUserChargeApi.Request, GetUserChargeApi.Response] + GetUserRatingApi: [GetUserRatingApi.Request, GetUserRatingApi.Response] + GetUserMusicApi: [GetUserMusicApi.Request, GetUserMusicApi.Response] + GetUserActivityApi: [GetUserActivityApi.Request, GetUserActivityApi.Response] + GetUserExtendApi: [GetUserExtendApi.Request, GetUserExtendApi.Response] + GetUserOptionApi: [GetUserOptionApi.Request, GetUserOptionApi.Response] + GetUserRecommendSelectMusicApi: [ + GetUserRecommendSelectMusicApi.Request, + GetUserRecommendSelectMusicApi.Response + ] + GetUserRecommendRateMusicApi: [ + GetUserRecommendRateMusicApi.Request, + GetUserRecommendRateMusicApi.Response + ] + GetUserRegionApi: [GetUserRegionApi.Request, GetUserRegionApi.Response] + GetUserFavoriteApi: [GetUserFavoriteApi.Request, GetUserFavoriteApi.Response] + GetUserFavoriteItemApi: [ + GetUserFavoriteItemApi.Request, + GetUserFavoriteItemApi.Response + ] + GetUserGhostApi: [GetUserGhostApi.Request, GetUserGhostApi.Response] + GetUserMapApi: [GetUserMapApi.Request, GetUserMapApi.Response] + GetUserLoginBonusApi: [ + GetUserLoginBonusApi.Request, + GetUserLoginBonusApi.Response + ] + UserLogoutApi: [UserLogoutApi.Request, UserLogoutApi.Response] + UpsertUserChargelogApi: [ + UpsertUserChargelogApi.Request, + UpsertUserChargelogApi.Response + ] + UploadUserPlaylogListApi: [ + UploadUserPlaylogListApi.Request, + UploadUserPlaylogListApi.Response + ] + UpsertUserAllApi: [UpsertUserAllApi.Request, UpsertUserAllApi.Response] + GetUserMissionDataApi: [ + GetUserMissionDataApi.Request, + GetUserMissionDataApi.Response + ] +} diff --git a/src/user.ts b/src/user.ts new file mode 100644 index 0000000..c145608 --- /dev/null +++ b/src/user.ts @@ -0,0 +1,1648 @@ +import type { Client } from './client' +import { type Api, type UserItem, type UserMusicDetail } from './typings/api' +import { MusicDifficultyID } from './typings/api/base/MusicDifficultyID' +import { PlayComboFlagID } from './typings/api/base/PlayComboFlagID' +import { PlaySyncFlagID } from './typings/api/base/PlaySyncFlagID' +import { CardItem } from './typings/api/GetUserCardApi' +import { UserItemKind } from './typings/api/base/UserItem' +import { Playlog } from './typings/api/UploadUserPlaylogListApi' +import { LogoutType } from './typings/api/UserLogoutApi' +import { toLocalDateString, toLocalDateTimeString } from './utils/date' +import { + CharacterSet, + convertAchievementToScoreRank, + ScoreEntry, + ScoreSet, + ItemSet, + unpackPresent, + Mission +} from './containers' +import { AwaitableEventEmitter, AwaitableEventMap } from './utils/event' + +type RequestAllApi = + | 'GetUserItemApi' + | 'GetUserCardApi' + | 'GetUserCourseApi' + | 'GetUserMusicApi' + | 'GetUserFavoriteItemApi' + | 'GetUserMapApi' + | 'GetUserLoginBonusApi' + +export interface Present { + [UserItemKind.Plate]: ItemSet + [UserItemKind.Title]: ItemSet + [UserItemKind.Icon]: ItemSet + [UserItemKind.Music]: ItemSet + [UserItemKind.Character]: ItemSet + [UserItemKind.Partner]: ItemSet + [UserItemKind.Frame]: ItemSet + [UserItemKind.Ticket]: ItemSet + // NOTE: 若 Present 的 itemId 经解析后指向 UserItemKind.Present, UserItemKind.MusicMas, UserItemKind.MusicRem, UserItemKind.MusicSrg 则会直接忽略此礼物。 +} + +export interface SaveEventMap extends AwaitableEventMap { + progress: [ProgressEvent] +} + +export interface SaveProgress { + loaded: number + total: number +} + +interface CacheMap { + root?: { + get data(): NonNullable + get card(): NonNullable + get character(): NonNullable + get item(): NonNullable + get course(): NonNullable + get charge(): NonNullable + get favorite(): NonNullable + get ghost(): NonNullable + get map(): NonNullable + get loginBonus(): NonNullable + get region(): NonNullable + get recommendRateMusic(): NonNullable + get recommendSelectMusic(): NonNullable< + CacheMap['root.recommendSelectMusic'] + > + get option(): NonNullable + get extend(): NonNullable + get rating(): NonNullable + get score(): NonNullable + get activity(): NonNullable + get mission(): NonNullable + } + 'root.item'?: { + get [UserItemKind.Plate](): NonNullable + get [UserItemKind.Title](): NonNullable + get [UserItemKind.Partner](): NonNullable + get [UserItemKind.Icon](): NonNullable + get [UserItemKind.Present](): NonNullable + get [UserItemKind.Frame](): NonNullable + get [UserItemKind.Ticket](): NonNullable + get [UserItemKind.Music](): NonNullable + get [UserItemKind.MusicMas](): NonNullable + get [UserItemKind.MusicRem](): NonNullable + // get musicSrg(): NonNullable + } + 'root.item.present'?: Promise + 'root.favorite'?: { + get icon(): NonNullable + get plate(): NonNullable + get title(): NonNullable + get character(): NonNullable + get frame(): NonNullable + get music(): NonNullable + get rival(): NonNullable + } + 'root.data'?: Promise< + Api['GetUserDataApi'][1]['userData'] & { banState: number } + > + 'root.card'?: Promise<{ + card: NonNullable + serialId: NonNullable + }> + 'root.map'?: Promise> + // TODO: Watch Modification + 'root.character'?: Promise + 'root.item.plate'?: Promise + 'root.item.title'?: Promise + 'root.item.partner'?: Promise + 'root.item.icon'?: Promise + 'root.item.frame'?: Promise + 'root.item.ticket'?: Promise + 'root.item.music'?: Promise + 'root.item.musicMas'?: Promise + 'root.item.musicRem'?: Promise + // 'root.item.musicSrg'?: Promise< + // NonNullable + // > + 'root.ghost'?: Promise + 'root.favorite.icon'?: Promise< + Api['GetUserFavoriteApi'][1]['userFavorite']['itemIdList'] + > + 'root.favorite.plate'?: Promise< + Api['GetUserFavoriteApi'][1]['userFavorite']['itemIdList'] + > + 'root.favorite.title'?: Promise< + Api['GetUserFavoriteApi'][1]['userFavorite']['itemIdList'] + > + 'root.favorite.character'?: Promise< + Api['GetUserFavoriteApi'][1]['userFavorite']['itemIdList'] + > + 'root.favorite.frame'?: Promise< + Api['GetUserFavoriteApi'][1]['userFavorite']['itemIdList'] + > + 'root.favorite.music'?: Promise< + NonNullable + > + 'root.favorite.rival'?: Promise< + NonNullable + > + 'root.loginBonus'?: Promise< + NonNullable + > + 'root.course'?: Promise< + NonNullable + > + 'root.charge'?: Promise< + NonNullable + > + 'root.score'?: Promise + 'root.score.reset'?: () => void + 'root.rating'?: Promise + 'root.region'?: Promise< + NonNullable + > + 'root.recommendRateMusic'?: Promise< + Api['GetUserRecommendRateMusicApi'][1]['userRecommendRateMusicIdList'] + > + 'root.recommendSelectMusic'?: Promise< + Api['GetUserRecommendSelectMusicApi'][1]['userRecommendSelectionMusicIdList'] + > + 'root.option'?: Promise + 'root.extend'?: Promise + 'root.activity'?: Promise + 'root.mission'?: Promise +} + +export type PlaylogInit = Partial & { + musicId: number + level: MusicDifficultyID + achievement: number + tapCriticalPerfect: number + tapPerfect: number + tapGreat: number + tapGood: number + tapMiss: number + holdCriticalPerfect: number + holdPerfect: number + holdGreat: number + holdGood: number + holdMiss: number + slideCriticalPerfect: number + slidePerfect: number + slideGreat: number + slideGood: number + slideMiss: number + touchCriticalPerfect: number + touchPerfect: number + touchGreat: number + touchGood: number + touchMiss: number + breakCriticalPerfect: number + breakPerfect: number + breakGreat: number + breakGood: number + breakMiss: number + fastCount: number + lateCount: number + isCriticalDisp: boolean + comboStatus: PlayComboFlagID + syncStatus: PlaySyncFlagID + maxCombo: number +} + +export function calculateDeluxscore(playlog: PlaylogInit) { + const tapDx = + playlog.tapCriticalPerfect * 3 + playlog.tapPerfect * 2 + playlog.tapGreat + const holdDx = + playlog.holdCriticalPerfect * 3 + + playlog.holdPerfect * 2 + + playlog.holdGreat + const slideDx = + playlog.slideCriticalPerfect * 3 + + playlog.slidePerfect * 2 + + playlog.slideGreat + const touchDx = + playlog.touchCriticalPerfect * 3 + + playlog.touchPerfect * 2 + + playlog.touchGreat + const breakDx = + playlog.breakCriticalPerfect * 3 + + playlog.breakPerfect * 2 + + playlog.breakGreat + return tapDx + holdDx + slideDx + touchDx + breakDx +} + +export function splitPresent(data: UserItem[]): Present { + const present: Map = new Map([ + [UserItemKind.Plate, []], + [UserItemKind.Title, []], + [UserItemKind.Icon, []], + [UserItemKind.Music, []], + [UserItemKind.Character, []], + [UserItemKind.Partner, []], + [UserItemKind.Frame, []], + [UserItemKind.Ticket, []] + ]) + for (const item of data) { + if (item.itemKind === UserItemKind.Present && item.isValid) { + const [kind, id] = unpackPresent(item.itemId) + if (present.has(kind)) { + present.get(kind)?.push({ + itemKind: kind, + itemId: id, + stock: item.stock, + isValid: item.isValid + }) + } + } + } + return { + [UserItemKind.Plate]: new ItemSet( + present.get(UserItemKind.Plate) ?? [], + UserItemKind.Plate, + true + ), + [UserItemKind.Title]: new ItemSet( + present.get(UserItemKind.Title) ?? [], + UserItemKind.Title, + true + ), + [UserItemKind.Icon]: new ItemSet( + present.get(UserItemKind.Icon) ?? [], + UserItemKind.Icon, + true + ), + [UserItemKind.Music]: new ItemSet( + present.get(UserItemKind.Music) ?? [], + UserItemKind.Music, + true + ), + [UserItemKind.Character]: new ItemSet( + present.get(UserItemKind.Character) ?? [], + UserItemKind.Character, + true + ), + [UserItemKind.Partner]: new ItemSet( + present.get(UserItemKind.Partner) ?? [], + UserItemKind.Partner, + true + ), + [UserItemKind.Frame]: new ItemSet( + present.get(UserItemKind.Frame) ?? [], + UserItemKind.Frame, + true + ), + [UserItemKind.Ticket]: new ItemSet( + present.get(UserItemKind.Ticket) ?? [], + UserItemKind.Ticket, + true + ) + } +} + +export class User { + private _cache: CacheMap = {} + constructor( + public id: number, + public loginId: bigint | null, + public dateTime: Date | null, + public client: Client, + public isLogin: boolean, + public loginDateTime: Date, + public token: string = '' + ) { } + + private _useCache( + key: T, + fn: () => NonNullable + ) { + if (this._cache[key]) { + return this._cache[key] + } + const result = fn() + this._cache[key] = result + return result + } + + private _queryCache( + key: T + ): CacheMap[T] | undefined { + return this._cache[key] + } + + get data() { + // 被 sbga 大粪代码气笑了 + const requestAll = async ( + apiName: T, + data: Api[T][0] + ): Promise => { + const result: Api[T][1][] = [] + let index = 0n + while (true) { + const resp = (await this.client.request( + apiName, + Object.assign({}, data, { + nextIndex: + index === 0n + ? BigInt( + (data as { nextIndex?: number | bigint }).nextIndex ?? 0n + ) + : index + }), + { + userId: this.id + } + )) as Api[T][1] & { + nextIndex: number | bigint + } + result.push(resp) + index = BigInt(resp.nextIndex) + if (index === 0n) break + } + return result + } + const getItem = async (kind: UserItemKind) => { + return ( + await requestAll('GetUserItemApi', { + userId: this.id, + nextIndex: BigInt(kind) * 10000000000n, + maxCount: 100 + }) + ) + .map(v => v.userItemList ?? []) + .flat() + } + const getFavorite = async ( + kind: number + ): Promise => { + return ( + await this.client.request( + 'GetUserFavoriteApi', + { + userId: this.id, + itemKind: kind + }, + { + userId: this.id + } + ) + ).userFavorite.itemIdList + } + const getFavoriteItem = async (kind: UserItemKind) => { + return ( + await requestAll('GetUserFavoriteItemApi', { + userId: this.id, + kind, + isAllFavoriteItem: false, + nextIndex: 0n, + maxCount: 100 + }) + ) + .map(v => v.userFavoriteItemList ?? []) + .flat() + } + /* eslint-disable-next-line @typescript-eslint/no-this-alias */ + const self = this + return this._useCache('root', () => ({ + get data() { + return self._useCache('root.data', async () => { + const res = await self.client.request( + 'GetUserDataApi', + { + userId: self.id + }, + { userId: self.id } + ) + if (res === null) throw new Error('User data is null') + const result = { + ...res.userData, + banState: res.banState + } + const obj = result as unknown as Record + delete obj['friendCode'] + delete obj['nameplateId'] + delete obj['cmLastEmoneyBrand'] + delete obj['trophyId'] + delete obj['cmLastEmoneyCredit'] + return result + }) + }, + get card() { + return self._useCache( + 'root.card', + async () => + ( + await requestAll('GetUserCardApi', { + userId: self.id, + nextIndex: 0, + maxCount: 10000 + }) + ) + .map(v => [v.userCardList ?? [], v.serialIdList ?? []] as const) + .reduce((previous, current) => { + if (Array.isArray(previous)) { + return { + card: [...previous[0], ...current[0]], + serialId: [...previous[1], ...current[1]] + } as unknown as readonly [CardItem[], unknown[]] + } else { + const p = previous as unknown as { + card: CardItem[] + serialId: unknown[] + } + p.card.push(...current[0]) + p.serialId.push(...current[1]) + return previous + } + }) as unknown as { + card: CardItem[] + serialId: unknown[] + } + ) + }, + get character() { + return self._useCache( + 'root.character', + async () => + new CharacterSet( + ( + await self.client.request( + 'GetUserCharacterApi', + { + userId: self.id + }, + { userId: self.id } + ) + ).userCharacterList ?? [] + ) // TODO: Character Instance + ) + }, + get item() { + return self._useCache('root.item', () => ({ + get [UserItemKind.Plate]() { + return self._useCache( + 'root.item.plate', + async () => + new ItemSet( + await getItem(UserItemKind.Plate), + UserItemKind.Plate, + false + ) + ) + }, + get [UserItemKind.Title]() { + return self._useCache( + 'root.item.title', + async () => + new ItemSet( + await getItem(UserItemKind.Title), + UserItemKind.Title, + false + ) + ) + }, + get [UserItemKind.Partner]() { + return self._useCache( + 'root.item.partner', + async () => + new ItemSet( + await getItem(UserItemKind.Partner), + UserItemKind.Partner, + false + ) + ) + }, + get [UserItemKind.Icon]() { + return self._useCache( + 'root.item.icon', + async () => + new ItemSet( + await getItem(UserItemKind.Icon), + UserItemKind.Icon, + false + ) + ) + }, + get [UserItemKind.Present]() { + return self._useCache('root.item.present', async () => + splitPresent(await getItem(UserItemKind.Present)) + ) + }, + get [UserItemKind.Frame]() { + return self._useCache( + 'root.item.frame', + async () => + new ItemSet( + await getItem(UserItemKind.Frame), + UserItemKind.Frame, + false + ) + ) + }, + get [UserItemKind.Ticket]() { + return self._useCache( + 'root.item.ticket', + async () => + new ItemSet( + await getItem(UserItemKind.Ticket), + UserItemKind.Ticket, + false + ) + ) + }, + get [UserItemKind.Music]() { + return self._useCache( + 'root.item.music', + async () => + new ItemSet( + await getItem(UserItemKind.Music), + UserItemKind.Music, + false + ) + ) + }, + get [UserItemKind.MusicMas]() { + return self._useCache( + 'root.item.musicMas', + async () => + new ItemSet( + await getItem(UserItemKind.MusicMas), + UserItemKind.MusicMas, + false + ) + ) + }, + get [UserItemKind.MusicRem]() { + return self._useCache( + 'root.item.musicRem', + async () => + new ItemSet( + await getItem(UserItemKind.MusicRem), + UserItemKind.MusicRem, + false + ) + ) + } + // get musicSrg() { + // return self._useCache('root.item.musicSrg', () => + // getItem(UserItemKind.MusicSrg) + // ) + // }, + })) + }, + get course() { + return self._useCache('root.course', async () => + ( + await requestAll('GetUserCourseApi', { + userId: self.id, + nextIndex: 0n + }) + ) + .map(v => v.userCourseList ?? []) + .flat() + ) + }, + get charge() { + return self._useCache( + 'root.charge', + async () => + ( + await self.client.request( + 'GetUserChargeApi', + { + userId: self.id + }, + { userId: self.id } + ) + ).userChargeList ?? [] + ) + }, + get favorite() { + return self._useCache('root.favorite', () => ({ + get icon() { + return self._useCache('root.favorite.icon', () => getFavorite(1)) + }, + get plate() { + return self._useCache('root.favorite.plate', () => getFavorite(2)) + }, + get title() { + return self._useCache('root.favorite.title', () => getFavorite(3)) + }, + get character() { + return self._useCache('root.favorite.character', () => + getFavorite(4) + ) + }, + get frame() { + return self._useCache('root.favorite.frame', () => getFavorite(5)) + }, + get music() { + return self._useCache('root.favorite.music', () => + getFavoriteItem(1) + ) + }, + get rival() { + return self._useCache('root.favorite.rival', () => + getFavoriteItem(2) + ) + } + })) + }, + get ghost() { + return self._useCache( + 'root.ghost', + async () => + ( + await self.client.request( + 'GetUserGhostApi', + { + userId: self.id + }, + { userId: self.id } + ) + ).userGhostList // TODO: Ghost Instance + ) + }, + get map() { + return self._useCache('root.map', async () => + ( + await requestAll('GetUserMapApi', { + userId: self.id, + nextIndex: 0n, + maxCount: 10000 + }) + ) + .map(v => v.userMapList ?? []) + .flat() + ) + }, + get loginBonus() { + return self._useCache('root.loginBonus', async () => + ( + await requestAll('GetUserLoginBonusApi', { + userId: self.id, + nextIndex: 0n, + maxCount: 10000 + }) + ) + .map(v => v.userLoginBonusList ?? []) + .flat() + ) + }, + get region() { + return self._useCache( + 'root.region', + async () => + ( + await self.client.request( + 'GetUserRegionApi', + { + userId: self.id + }, + { userId: self.id } + ) + ).userRegionList ?? [] + ) + }, + get recommendRateMusic() { + return self._useCache( + 'root.recommendRateMusic', + async () => + ( + await self.client.request( + 'GetUserRecommendRateMusicApi', + { + userId: self.id + }, + { userId: self.id } + ) + ).userRecommendRateMusicIdList + ) + }, + get recommendSelectMusic() { + return self._useCache( + 'root.recommendSelectMusic', + async () => + ( + await self.client.request( + 'GetUserRecommendSelectMusicApi', + { + userId: self.id + }, + { userId: self.id } + ) + ).userRecommendSelectionMusicIdList + ) + }, + get option() { + return self._useCache('root.option', async () => { + const result = ( + await self.client.request( + 'GetUserOptionApi', + { + userId: self.id + }, + { userId: self.id } + ) + ).userOption + const obj = result as unknown as Record + delete obj['tempoVolume'] + return result + }) + }, + get extend() { + return self._useCache( + 'root.extend', + async () => + ( + await self.client.request( + 'GetUserExtendApi', + { + userId: self.id + }, + { userId: self.id } + ) + ).userExtend + ) + }, + get rating() { + return self._useCache('root.rating', async () => { + const result = ( + await self.client.request( + 'GetUserRatingApi', + { + userId: self.id + }, + { userId: self.id } + ) + ).userRating + const obj = result.udemae as unknown as Record + delete obj['MaxLoseNum'] + delete obj['NpcLoseNum'] + delete obj['NpcMaxLoseNum'] + delete obj['NpcMaxWinNum'] + delete obj['NpcTotalLoseNum'] + delete obj['NpcTotalWinNum'] + delete obj['NpcWinNum'] + return result + }) + }, + get score() { + return self._useCache('root.score', async () => { + const res = ScoreSet.resettable( + ( + await requestAll('GetUserMusicApi', { + userId: self.id, + nextIndex: 0n, + maxCount: 10000 + }) + ) + .map(v => v.userMusicList ?? []) + .flat() + .map(v => v.userMusicDetailList ?? []) + .flat() + .map(v => { + const obj = v as unknown as Record + delete obj['extNum2'] + return v + }) + ) + self._cache['root.score.reset'] = res[1] + return res[0] + }) + }, + get activity() { + return self._useCache( + 'root.activity', + async () => + ( + await self.client.request( + 'GetUserActivityApi', + { + userId: self.id + }, + { userId: self.id } + ) + ).userActivity ?? {} + ) + }, + get mission() { + return self._useCache('root.mission', async () => { + const result = await self.client.request( + 'GetUserMissionDataApi', + { + userId: self.id + }, + { userId: self.id } + ) + return new Mission(result.userWeeklyData, result.userMissionDataList) + }) + } + })) + } + + async [Symbol.asyncDispose]() { + if (this.isLogin) { + await this.logout() + } + } + + async refresh() { + if (this.isLogin) { + await this.logout() + } + + const newInstance = await this.client.login(this.id, this.token, { + isContinue: true + }) + this.isLogin = newInstance.isLogin + this.loginId = newInstance.loginId + this.token = newInstance.token + this.loginDateTime = newInstance.loginDateTime + } + + async charge(options: { + kind: number + stock: number + price: number + purchaseDate?: Date + validDate?: Date + }) { + options.purchaseDate = options.purchaseDate ?? new Date() + // validDate 默认为 purchaseDate + 90 天的凌晨4点 + options.validDate = + options.validDate ?? + new Date( + options.purchaseDate.getFullYear(), + options.purchaseDate.getMonth(), + options.purchaseDate.getDate() + 90, + 4, + 0, + 0, + 0 + ) + await this.client.request( + 'UpsertUserChargelogApi', + { + userId: this.id, + userCharge: { + chargeId: options.kind, + stock: options.stock, + purchaseDate: toLocalDateTimeString(options.purchaseDate), + validDate: toLocalDateTimeString(options.validDate) + }, + userChargelog: { + chargeId: options.kind, + clientId: this.client.shortChipId, + regionId: this.client.place.region.id, + placeId: this.client.place.id, + price: options.price, + purchaseDate: toLocalDateTimeString(options.purchaseDate) + } + }, + { + userId: this.id + } + ) + } + + async logout(type = LogoutType.Logout) { + if (!this.isLogin) { + throw new Error('Unable to logout: User already logged out') + } + if (!this.loginDateTime) { + throw new Error('Unable to logout: loginDateTime is null') + } + this.isLogin = false + const timestamp = Math.floor(this.loginDateTime.getTime() / 1000) + await this.client.request( + 'UserLogoutApi', + { + userId: this.id, + accessCode: '', + regionId: this.client.place.region.id, + placeId: this.client.place.id, + clientId: this.client.shortChipId, + loginDateTime: timestamp, + type + }, + { + userId: this.id + } + ) + } + + async update(options: { + romVersion: string + dataVersion: string + }): Promise { + const data = await this.data.data + data.lastRomVersion = options.romVersion + data.lastDataVersion = options.dataVersion + } + + /** + * Saves the current user's state including play log, user data, character information, + * and various game details to the backend. + * + * This asynchronous function aggregates new and existing play log data, generates additional + * play logs if necessary, and prepares comprehensive user data by merging character details, + * item lists, mission data, and various other game state information. + * + * The function performs the following main steps: + * - Verifies that the user is logged in; throws an error if not. + * - Generates random numbers and special values used in play logs. + * - Constructs play log entries by merging pre-existing log entries with newly generated ones. + * - Fetches and validates the user's selected characters, ensuring that necessary character + * information is retrieved or defaulted. + * - Aggregates and organizes several parts of the user data, including scores, achievements, + * and other related information. + * - Sequentially uploads each play log entry and then sends the complete user state update + * to the backend via client API requests. + * + * @param options - An optional object containing: + * - playlog: An array of play log entries (of types Playlog or PlaylogInit) to be used in the save. + * - eventMode: A boolean indicating if the event mode should be enabled. + * - freePlay: A boolean indicating if the free play mode should be enabled. + * + * @throws {Error} Throws if the user is not logged in correctly or if character information is + * missing or invalid. + * + * @returns A Promise that resolves when the save process (including all API requests) has completed. + */ + save(options?: { + playlog?: (Playlog | PlaylogInit)[] + eventMode?: boolean + freePlay?: boolean + }): AwaitableEventEmitter { + const emitter = AwaitableEventEmitter.fromAsync(async () => { + const [ + data, + character, + score, + option, + extend, + charge, + mission, + activity, + rating + ] = await Promise.all([ + this.data.data, + this.data.character, + this.data.score, + this.data.option, + this.data.extend, + this.data.charge, + this.data.mission, + this.data.activity, + this.data.rating + ]) + const generatePlaySpecial = () => { + // 生成一个 1 至 1037933 的随机整数 + let num = (Math.floor(Math.random() * 1037933) + 1) * 2069 + num += 1024 // += GameManager.CalcSpecialNum(); + let num2 = 0 + for (let i = 0; i < 32; i++) { + num2 <<= 1 + num2 += num % 2 + num >>= 1 + } + return num2 + } + const generatePlaylog = (detail: UserMusicDetail) => { + return { + level: detail.level, + achievement: 0, + musicId: detail.musicId, + tapCriticalPerfect: 0, + tapPerfect: 0, + tapGreat: 0, + tapGood: 0, + tapMiss: 1, + holdCriticalPerfect: 0, + holdPerfect: 0, + holdGreat: 0, + holdGood: 0, + holdMiss: 0, + slideCriticalPerfect: 0, + slidePerfect: 0, + slideGreat: 0, + slideGood: 0, + slideMiss: 0, + touchCriticalPerfect: 0, + touchPerfect: 0, + touchGreat: 0, + touchGood: 0, + touchMiss: 0, + breakCriticalPerfect: 0, + breakPerfect: 0, + breakGreat: 0, + breakGood: 0, + breakMiss: 0, + fastCount: 0, + lateCount: 0, + isCriticalDisp: false, + comboStatus: PlayComboFlagID.None, + syncStatus: PlaySyncFlagID.None, + maxCombo: 0, + totalCombo: 0, + maxSync: 0, + totalSync: 0 + } satisfies PlaylogInit + } + interface CharacterData { + characterId1: number + characterAwakening1: number + characterLevel1: number + characterId2: number + characterAwakening2: number + characterLevel2: number + characterId3: number + characterAwakening3: number + characterLevel3: number + characterId4: number + characterAwakening4: number + characterLevel4: number + characterId5: number + characterAwakening5: number + characterLevel5: number + } + const convertVersionNumber = (version: string): number => { + // 1.41.00 -> 1041000 + const parts = version.split('.') + if (parts.length !== 3) { + throw new Error(`Invalid version format: ${version}`) + } + const major = parseInt(parts[0], 10) + const minor = parseInt(parts[1], 10) + const patch = parseInt(parts[2], 10) + if (isNaN(major) || isNaN(minor) || isNaN(patch)) { + throw new Error(`Invalid version number: ${version}`) + } + return major * 1000000 + minor * 1000 + patch + } + const getUserSelectedCharacter = ( + option: Partial + ): CharacterData => { + const raiseException = (message: string) => { + throw new Error(message) + } + const final: CharacterData = { + characterId1: 0, + characterAwakening1: 0, + characterLevel1: 0, + characterId2: 0, + characterAwakening2: 0, + characterLevel2: 0, + characterId3: 0, + characterAwakening3: 0, + characterLevel3: 0, + characterId4: 0, + characterAwakening4: 0, + characterLevel4: 0, + characterId5: 0, + characterAwakening5: 0, + characterLevel5: 0 + } + for (let i = 0; i < 5; i++) { + const originalId = option[ + `characterId${i + 1}` as keyof CharacterData + ] as number | undefined + const originalAwakening = option[ + `characterAwakening${i + 1}` as keyof CharacterData + ] as number | undefined + const originalLevel = option[ + `characterLevel${i + 1}` as keyof CharacterData + ] as number | undefined + if (originalId !== undefined) { + Object.assign(final, { + [`characterId${i + 1}` as keyof CharacterData]: originalId, + [`characterAwakening${i + 1}` as keyof CharacterData]: + originalAwakening ?? + character.get(originalId)?.awakening ?? + raiseException(`Character ${i + 1} not found`), + [`characterLevel${i + 1}` as keyof CharacterData]: + originalLevel ?? + character.get(originalId)?.level ?? + raiseException(`Character ${i + 1} not found`) + }) + } else if ( + originalLevel !== undefined || + originalAwakening !== undefined + ) { + throw new Error( + `Character ${i + 1} ID is required if level or awakening is set` + ) + } else { + const fill = character.get(data.charaSlot[i]) + if (!fill) throw new Error(`Character ${i + 1} not found`) + Object.assign(final, { + [`characterId${i + 1}` as keyof CharacterData]: data.charaSlot[i], + [`characterAwakening${i + 1}` as keyof CharacterData]: + fill.awakening, + [`characterLevel${i + 1}` as keyof CharacterData]: fill.level + }) + } + } + return final + } + if (!this.loginId || !this.dateTime) + throw new Error('Not logged in correctly') + // TODO: type hint + + let playlog: (Playlog | PlaylogInit)[] = options?.playlog ?? [] + const musicDetail = score.export() + const generatedPlaylog = musicDetail + .map(v => generatePlaylog(v.value)) + .filter(v => { + return !playlog.some( + v2 => v2.level === v.level && v2.musicId === v.musicId + ) + }) satisfies PlaylogInit[] + playlog = playlog.concat(generatedPlaylog) + if (playlog.length === 0) { + const ids = Array.from(score.keys()) + const randomId = ids[Math.floor(Math.random() * ids.length)] + const entry = Object.entries(score.get(randomId)).find(([, v]) => { + return !!v + }) + if (!entry) + throw new Error('At least one playlog is required for new user') + const level = Number(entry[0]) as MusicDifficultyID + const detail = entry[1] as ScoreEntry + const generated: UserMusicDetail = { + musicId: randomId, + achievement: detail.achievement, + comboStatus: detail.comboStatus, + syncStatus: detail.syncStatus, + deluxscoreMax: detail.deluxscoreMax, + level, + playCount: detail.playCount, + scoreRank: detail.scoreRank, + extNum1: detail.extNum1 + } + musicDetail.push({ value: generated, isNew: false }) + playlog.push(generatePlaylog(generated)) + } + const playlogList: Playlog[] = await Promise.all( + Array.from(playlog.entries()).map( + async ([index, log]) => + Object.assign( + {}, + { + userId: 0, + orderId: 0, + playlogId: this.loginId, + version: convertVersionNumber(data.lastRomVersion), + placeId: this.client.place.id, + placeName: this.client.place.name, + loginDate: Math.floor(this.dateTime!.getTime() / 1000), + playDate: '', + userPlayDate: '', + type: 0, + trackNo: (index % 4) + 1, + vsMode: 0, + vsStatus: 0, + vsUserName: '', + vsUserRating: 0, + vsUserAchievement: 0, + vsUserGradeRank: 0, + vsRank: 0, + playerNum: 1, + playedUserId1: 0, + playedUserName1: '', + playedMusicLevel1: MusicDifficultyID.Basic, + playedUserId2: 0, + playedUserName2: '', + playedMusicLevel2: MusicDifficultyID.Basic, + playedUserId3: 0, + playedUserName3: '', + playedMusicLevel3: MusicDifficultyID.Basic, + ...getUserSelectedCharacter(log), + scoreRank: convertAchievementToScoreRank( + log.achievement, + log.achievement > 1010000 && + log.level === MusicDifficultyID.Utage + ), // TODO: utage + isTap: + log.tapCriticalPerfect + + log.tapPerfect + + log.tapGreat + + log.tapGood + + log.tapMiss > + 0, + isHold: + log.holdCriticalPerfect + + log.holdPerfect + + log.holdGreat + + log.holdGood + + log.holdMiss > + 0, + isSlide: + log.slideCriticalPerfect + + log.slidePerfect + + log.slideGreat + + log.slideGood + + log.slideMiss > + 0, + isTouch: + log.touchCriticalPerfect + + log.touchPerfect + + log.touchGreat + + log.touchGood + + log.touchMiss > + 0, + isBreak: + log.breakCriticalPerfect + + log.breakPerfect + + log.breakGreat + + log.breakGood + + log.breakMiss > + 0, + isFastLateDisp: true, + deluxscore: calculateDeluxscore(log as PlaylogInit), + isAchieveNewRecord: false, + isDeluxscoreNewRecord: false, + isClear: log.achievement >= 500000, + totalCombo: + log.tapCriticalPerfect + + log.tapPerfect + + log.tapGreat + + log.tapGood + + log.tapMiss + + (log.holdCriticalPerfect + + log.holdPerfect + + log.holdGreat + + log.holdGood + + log.holdMiss) + + (log.slideCriticalPerfect + + log.slidePerfect + + log.slideGreat + + log.slideGood + + log.slideMiss) + + (log.touchCriticalPerfect + + log.touchPerfect + + log.touchGreat + + log.touchGood + + log.touchMiss) + + (log.breakCriticalPerfect + + log.breakPerfect + + log.breakGreat + + log.breakGood + + log.breakMiss), + maxSync: + log.tapCriticalPerfect + + log.tapPerfect + + log.tapGreat + + log.tapGood + + log.tapMiss + + (log.holdCriticalPerfect + + log.holdPerfect + + log.holdGreat + + log.holdGood + + log.holdMiss) + + (log.slideCriticalPerfect + + log.slidePerfect + + log.slideGreat + + log.slideGood + + log.slideMiss) + + (log.touchCriticalPerfect + + log.touchPerfect + + log.touchGreat + + log.touchGood + + log.touchMiss) + + (log.breakCriticalPerfect + + log.breakPerfect + + log.breakGreat + + log.breakGood + + log.breakMiss), + totalSync: 0, + beforeRating: log.beforeRating ?? data.playerRating, + afterRating: log.afterRating ?? data.playerRating, + beforeGrade: log.beforeGrade ?? data.gradeRating, + afterGrade: log.afterGrade ?? data.gradeRating, + afterGradeRank: log.afterGradeRank ?? data.gradeRank, + beforeDeluxRating: log.beforeDeluxRating ?? data.playerRating, + afterDeluxRating: log.afterDeluxRating ?? data.playerRating, + isPlayTutorial: false, + isEventMode: false, + isFreedomMode: false, + playMode: 0, + isNewFree: false, + trialPlayAchievement: -1, + extNum1: 0, + extNum2: 0, + extNum4: 0, + extBool1: + log.achievement > 1010000 && + log.level === MusicDifficultyID.Utage, // NOTE: This is a workaround for Buddy charts (バディ譜面) + extBool2: false + }, + log + ) satisfies Playlog + ) + ) + const groupedPlaylogList = [] + const groupedMusicDetailList = [] + for (let i = 0; i < playlogList.length; i += 4) { + groupedPlaylogList.push(playlogList.slice(i, i + 4)) + groupedMusicDetailList.push(musicDetail.slice(i, i + 4)) + } + let lastPlayDate = new Date() + // let lastPlayDate = new Date( + // fromLocalDateTimeString(data.lastPlayDate).getTime() + 1000 + // ) + for (let [index, group] of groupedPlaylogList.entries()) { + if (index > 0) { + const currentDate = new Date() + if (currentDate.getTime() - lastPlayDate.getTime() < 1000) { + await new Promise(resolve => + setTimeout( + resolve, + 1100 - (currentDate.getTime() - lastPlayDate.getTime()) + ) + ) + } + await this.refresh() + lastPlayDate = new Date() + } + group = group.map(v => + Object.assign({}, v, { + playlogId: this.loginId, + playDate: toLocalDateString(lastPlayDate), + userPlayDate: toLocalDateTimeString(lastPlayDate, true) + }) + ) + if (index === 0) { + await new Promise(resolve => setTimeout(resolve, 70000)) + await this.client.request('UpsertUserAllApi', { + userId: this.id, + playlogId: this.loginId, + isEventMode: !!(options?.eventMode ?? false), + isFreePlay: !!(options?.freePlay ?? false), + loginDateTime: Math.floor(this.loginDateTime.getTime() / 1000), + userPlaylogList: group, + upsertUserAll: { + userData: [ + Object.assign({}, data, { + dateTime: Math.floor(this.dateTime.getTime() / 1000), + lastGameId: this.client.codename, + lastPlaceId: this.client.place.id, + lastPlaceName: this.client.place.name, + lastRegionId: this.client.place.region.id, + lastRegionName: this.client.place.region.name[0], + lastClientId: this.client.shortChipId, + accessCode: '', + lastLoginDate: toLocalDateTimeString(this.dateTime, true), // TODO: compare + lastPlayDate: toLocalDateTimeString(lastPlayDate, true) + } satisfies Partial< + Api['UpsertUserAllApi'][0]['upsertUserAll']['userData'][0] + >) satisfies Api['UpsertUserAllApi'][0]['upsertUserAll']['userData'][0] + // data + ], + userExtend: [extend], + userOption: [option], + ...(await (async () => { + const character = + (await this._queryCache('root.character'))?.export() ?? [] + return { + userCharacterList: character.map(v => v.value), + isNewCharacterList: character + .map(v => (v.isNew ? '1' : '0')) + .join('') + } + })()), + ...(await (async () => { + const itemList = [ + ...((await this._queryCache('root.item.plate'))?.export() ?? + []), + ...((await this._queryCache('root.item.title'))?.export() ?? + []), + ...((await this._queryCache('root.item.partner'))?.export() ?? + []), + ...((await this._queryCache('root.item.icon'))?.export() ?? + []), + ...((await this._queryCache('root.item.present'))?.[ + UserItemKind.Plate + ]?.export() ?? []), + ...((await this._queryCache('root.item.present'))?.[ + UserItemKind.Title + ]?.export() ?? []), + ...((await this._queryCache('root.item.present'))?.[ + UserItemKind.Icon + ]?.export() ?? []), + ...((await this._queryCache('root.item.present'))?.[ + UserItemKind.Music + ]?.export() ?? []), + ...((await this._queryCache('root.item.present'))?.[ + UserItemKind.Character + ]?.export() ?? []), + ...((await this._queryCache('root.item.present'))?.[ + UserItemKind.Partner + ]?.export() ?? []), + ...((await this._queryCache('root.item.present'))?.[ + UserItemKind.Frame + ]?.export() ?? []), + ...((await this._queryCache('root.item.present'))?.[ + UserItemKind.Ticket + ]?.export() ?? []), + ...((await this._queryCache('root.item.frame'))?.export() ?? + []), + ...((await this._queryCache('root.item.ticket'))?.export() ?? + []), + ...((await this._queryCache('root.item.music'))?.export() ?? + []), + ...(( + await this._queryCache('root.item.musicMas') + )?.export() ?? []), + ...(( + await this._queryCache('root.item.musicRem') + )?.export() ?? []) + ] + return { + userItemList: itemList.map(v => v.value), + isNewItemList: itemList + .map(v => (v.isNew ? '1' : '0')) + .join('') + } + })()), + userGhost: [], // TODO: lazy update + userMapList: [], // TODO: lazy update + userLoginBonusList: [], // TODO: lazy update + userRatingList: [rating], + ...(() => { + return { + userMusicDetailList: groupedMusicDetailList[index].map( + v => v.value + ), + isNewMusicDetailList: groupedMusicDetailList[index] + .map(v => (v.isNew ? '1' : '0')) + .join('') + } + })(), + userCourseList: [], // await this.data.course, // TODO: lazy update + userFavoriteList: [], // TODO: lazy update + userFriendSeasonRankingList: [], // TODO: lazy update + userWeeklyData: mission.weekly, + userMissionDataList: mission.mission, + userIntimateList: [], // TODO: lazy update + isNewUserIntimateList: '', + userKaleidxScopeList: [], // TODO: lazy update + isNewKaleidxScopeList: '', + userShopItemStockList: [], // sega 程序员没有设置,故意的还是不小心的? + userGetPointList: [], // TODO: lazy update + userTradeItemList: [], // TODO: lazy update + userFavoritemusicList: [], // TODO: lazy update + isNewFavoritemusicList: '', + userChargeList: charge.map(v => ({ + chargeId: v.chargeId, + stock: v.stock, + purchaseDate: v.purchaseDate, + validDate: v.validDate + })), + userActivityList: [activity], // TODO: slice activity data & activity container + userGamePlaylogList: [ + { + playlogId: this.loginId, + version: data.lastRomVersion, + playDate: toLocalDateTimeString(lastPlayDate, true), + playMode: 0, + useTicketId: -1, + playCredit: options?.freePlay ? 0 : 1, + playTrack: group.length, + clientId: this.client.shortChipId, + isPlayTutorial: false, + isEventMode: !!(options?.eventMode ?? false), + isNewFree: false, + playCount: 0, + playSpecial: generatePlaySpecial(), + playOtherUserId: 0 + } + ], + user2pPlaylog: { + userId1: 0, + userId2: 0, + userName1: '', + userName2: '', + regionId: 0, + placeId: 0, + user2pPlaylogDetailList: [] + }, + // 对应的槽位变更: 0, 对应的槽位追加: 1 + // TODO: 根据变更计算字段 + isNewMapList: '', + isNewLoginBonusList: '', + isNewCourseList: '', + isNewFavoriteList: '', + isNewFriendSeasonRankingList: '' + } + } satisfies Api['UpsertUserAllApi'][0], { + userId: this.id + }) + } else { + await new Promise(resolve => setTimeout(resolve, 70000)) + await this.client.request('UpsertUserAllApi', { + userId: this.id, + playlogId: this.loginId, + isEventMode: !!(options?.eventMode ?? false), + isFreePlay: !!(options?.freePlay ?? false), + loginDateTime: Math.floor(this.loginDateTime.getTime() / 1000), + userPlaylogList: group, + upsertUserAll: { + userData: [ + Object.assign({}, data, { + dateTime: Math.floor(this.dateTime.getTime() / 1000), + lastGameId: this.client.codename, + lastPlaceId: this.client.place.id, + lastPlaceName: this.client.place.name, + lastRegionId: this.client.place.region.id, + lastRegionName: this.client.place.region.name[0], + lastClientId: this.client.shortChipId, + accessCode: '', + lastLoginDate: toLocalDateTimeString(this.dateTime, true), + lastPlayDate: toLocalDateTimeString(lastPlayDate, true) + } satisfies Partial< + Api['UpsertUserAllApi'][0]['upsertUserAll']['userData'][0] + >) satisfies Api['UpsertUserAllApi'][0]['upsertUserAll']['userData'][0] + // data + ], + userExtend: [extend], + userOption: [option], + userCharacterList: [], + isNewCharacterList: '', + userItemList: [], + isNewItemList: '', + userGhost: [], + userMapList: [], + userLoginBonusList: [], + userRatingList: [rating], + ...(() => { + return { + userMusicDetailList: groupedMusicDetailList[index].map( + v => v.value + ), + isNewMusicDetailList: groupedMusicDetailList[index] + .map(v => (v.isNew ? '1' : '0')) + .join('') + } + })(), + userCourseList: [], + userFavoriteList: [], + userFriendSeasonRankingList: [], + userWeeklyData: mission.weekly, + userMissionDataList: mission.mission, + userIntimateList: [], + isNewUserIntimateList: '', + userKaleidxScopeList: [], + isNewKaleidxScopeList: '', + userShopItemStockList: [], + userGetPointList: [], + userTradeItemList: [], + userFavoritemusicList: [], + isNewFavoritemusicList: '', + userChargeList: charge.map(v => ({ + chargeId: v.chargeId, + stock: v.stock, + purchaseDate: v.purchaseDate, + validDate: v.validDate + })), + userActivityList: [activity], + userGamePlaylogList: [ + { + playlogId: this.loginId, + version: data.lastRomVersion, + playDate: toLocalDateTimeString(lastPlayDate, true), + playMode: 0, + useTicketId: -1, + playCredit: options?.freePlay ? 0 : 1, + playTrack: group.length, + clientId: this.client.shortChipId, + isPlayTutorial: false, + isEventMode: !!(options?.eventMode ?? false), + isNewFree: false, + playCount: 0, + playSpecial: generatePlaySpecial(), + playOtherUserId: 0 + } + ], + user2pPlaylog: { + userId1: 0, + userId2: 0, + userName1: '', + userName2: '', + regionId: 0, + placeId: 0, + user2pPlaylogDetailList: [] + }, + isNewMapList: '', + isNewLoginBonusList: '', + isNewCourseList: '', + isNewFavoriteList: '', + isNewFriendSeasonRankingList: '' + } + } satisfies Api['UpsertUserAllApi'][0], { userId: this.id }) + } + emitter.emit('progress', { + loaded: index + 1, + total: groupedPlaylogList.length + }) + } + this._cache['root.score.reset']?.() + }) + return emitter + } +} diff --git a/src/utils/date.ts b/src/utils/date.ts new file mode 100644 index 0000000..66da749 --- /dev/null +++ b/src/utils/date.ts @@ -0,0 +1,38 @@ +export function toLocalDateTimeString( + date: Date, + includeMilliseconds = false +): string { + return toUTCDateTimeString( + new Date(date.getTime() - date.getTimezoneOffset() * 60000), + includeMilliseconds + ) +} + +export function toUTCDateTimeString( + date: Date, + includeMilliseconds = false +): string { + return ( + date.toISOString().split('.')[0].replace('T', ' ') + + (includeMilliseconds ? '.0' : '') + ) +} + +export function toUTCDateString(date: Date): string { + return date.toISOString().split('T')[0] +} + +export function toLocalDateString(date: Date): string { + return toLocalDateTimeString(date).split(' ')[0] +} + +export function fromLocalDateTimeString(dateString: string): Date { + return new Date( + fromUTCDateTimeString(dateString).getTime() + + new Date().getTimezoneOffset() * 60000 + ) +} + +export function fromUTCDateTimeString(dateString: string): Date { + return new Date(dateString.replace(' ', 'T') + 'Z') +} diff --git a/src/utils/event.ts b/src/utils/event.ts new file mode 100644 index 0000000..a7eadb9 --- /dev/null +++ b/src/utils/event.ts @@ -0,0 +1,86 @@ +export class DoneEvent extends Event { + public readonly result: T + constructor(result: T) { + super('done') + this.result = result + } +} + +export interface AwaitableEventMap extends Record { + done: [DoneEvent] + error: [CustomEvent] +} + +export class AwaitableEventEmitter< + ResultT, + T extends AwaitableEventMap +> implements PromiseLike +{ + private listeners: { + [K in keyof T]?: ((...args: T[K]) => void)[] + } = {} + private result?: Promise + + on(event: K, listener: (...args: T[K]) => void) { + if (!this.listeners[event]) { + this.listeners[event] = [] + } + this.listeners[event]!.push(listener) + } + + off(event: K, listener: (...args: T[K]) => void) { + if (!this.listeners[event]) return + this.listeners[event] = this.listeners[event]!.filter(l => l !== listener) + } + + once(event: K, listener: (...args: T[K]) => void) { + const onceListener = (...args: T[K]) => { + listener(...args) + this.off(event, onceListener) + } + this.on(event, onceListener) + } + + emit(event: K, ...args: T[K]) { + if (!this.listeners[event]) return + for (const listener of this.listeners[event]!) { + listener(...args) + } + } + + then( + onfulfilled?: + | ((value: ResultT) => TResult1 | PromiseLike) + | null + | undefined, + onrejected?: + | ((reason: any) => TResult2 | PromiseLike) + | null + | undefined + ): PromiseLike { + if (!this.result) { + this.result = new Promise((resolve, reject) => { + this.once('done', event => resolve(event.result)) + this.once('error', event => reject(event.detail)) + }) + } + return this.result.then(onfulfilled, onrejected) + } + + static fromAsync< + T, + EventMap extends AwaitableEventMap = AwaitableEventMap + >(promise: PromiseLike | (() => PromiseLike)) { + if (typeof promise === 'function') promise = promise() + const emitter = new AwaitableEventEmitter() + promise.then( + result => { + emitter.emit('done', new DoneEvent(result)) + }, + error => { + emitter.emit('error', new CustomEvent('error', { detail: error })) + } + ) + return emitter + } +} diff --git a/src/utils/http.ts b/src/utils/http.ts new file mode 100644 index 0000000..702dc90 --- /dev/null +++ b/src/utils/http.ts @@ -0,0 +1,179 @@ +import https from 'node:https' +import http from 'node:http' +import { HttpProxyAgent } from 'http-proxy-agent' +import { SocksProxyAgent } from 'socks-proxy-agent' + +export class HTTPResponse implements Response { + public readonly status: number + public readonly headers: Headers + public readonly statusText: string + get ok(): boolean { + return this.status >= 200 && this.status < 300 + } + get type(): ResponseType { + return 'default' + } + + get redirected() { + return false + } + + constructor( + private _incomingMessage: http.IncomingMessage, + public readonly url: string + ) { + this.status = _incomingMessage.statusCode || 200 + this.statusText = _incomingMessage.statusMessage || 'OK' + this.headers = new Headers( + Object.entries(_incomingMessage.headers).filter(v => !!v[1]) as [ + string, + string + ][] + ) + this.body = new ReadableStream>({ + start: controller => { + this._incomingMessage.on('data', chunk => { + controller.enqueue( + new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.length) + ) + }) + this._incomingMessage.on('end', () => { + controller.close() + }) + this._incomingMessage.on('error', err => { + controller.error(err) + }) + }, + pull: () => { + // No-op, as the stream is pulled automatically by the consumer + }, + cancel: () => { + this._incomingMessage.destroy() + } + }) + } + clone(): HTTPResponse { + throw new Error('HTTPResponse does not support cloning') + } + public readonly body: ReadableStream> + private _bodyUsed: boolean = false + public get bodyUsed(): boolean { + return this._bodyUsed + } + private async _consumeBody(): Promise> { + if (this._bodyUsed) { + return Promise.reject(new TypeError('Body already used')) + } + this._bodyUsed = true + const reader = this.body.getReader() + const chunks: Uint8Array[] = [] + while (true) { + const { done, value } = await reader.read() + if (done) break + if (value) chunks.push(value) + } + const length = chunks.reduce((acc, chunk) => acc + chunk.length, 0) + const result = new Uint8Array(length) + let offset = 0 + for (const chunk of chunks) { + result.set(chunk, offset) + offset += chunk.length + } + return result + } + + async arrayBuffer(): Promise { + const bytes = await this._consumeBody() + return bytes.buffer + } + + async blob(): Promise { + const bytes = await this._consumeBody() + return new Blob([bytes]) + } + + async bytes(): Promise> { + return this._consumeBody() + } + + async formData(): Promise { + const textData = await this.text() + const formData = new FormData() + const params = new URLSearchParams(textData) + for (const [key, value] of params) { + formData.append(key, value) + } + return formData + } + + async json() { + const textData = await this.text() + return JSON.parse(textData) + } + + async text(): Promise { + const bytes = await this._consumeBody() + const decoder = new TextDecoder('utf-8') + return decoder.decode(bytes) + } +} + +export function makeRequest(opt?: { + proxy?: string | URL + hosts?: Record + rejectUnauthorized?: boolean +}) { + if (typeof opt?.proxy === 'string') { + opt.proxy = new URL(opt.proxy) + } + const agent = + opt?.proxy?.protocol === 'http:' || opt?.proxy?.protocol === 'https:' + ? new HttpProxyAgent(opt.proxy) + : opt?.proxy?.protocol === 'socks:' || opt?.proxy?.protocol === 'socks5:' + ? new SocksProxyAgent(opt.proxy) + : undefined + return function ( + url: string | URL, + options?: { + method?: 'GET' | 'POST' + headers?: Record + body?: string | ArrayBuffer | Uint8Array + } + ): Promise { + if (typeof url === 'string') { + url = new URL(url) + } + const hostname = opt?.hosts?.[url.hostname] ?? url.hostname + const client = (url.protocol === 'https:' ? https.request : http.request)({ + method: options?.method || 'GET', + agent, + rejectUnauthorized: opt?.rejectUnauthorized, + host: `${hostname}`, + port: url.port + ? parseInt(url.port) + : url.protocol === 'https:' + ? 443 + : 80, + path: url.pathname, + search: url.search, + headers: Object.assign( + { + host: url.host + }, + options?.headers || {} + ) + }) + return new Promise((resolve, reject) => { + client.on('response', res => { + resolve(new HTTPResponse(res, url.toString())) + }) + client.on('error', err => { + reject(err) + }) + if (options?.body) { + client.write(options.body) + } + client.end() + }) + } +} diff --git a/src/utils/iterator.ts b/src/utils/iterator.ts new file mode 100644 index 0000000..b30771d --- /dev/null +++ b/src/utils/iterator.ts @@ -0,0 +1,53 @@ +export interface IteratorCompatible { + keys(): IterableIterator + get(key: KeyT): ValueT | undefined +} + +export function values< + KeyT, + ValueT, + T extends IteratorCompatible +>(target: T): Iterable { + return { + [Symbol.iterator]: (): Iterator => { + const keys = Array.from(target.keys()) + let index = 0 + return { + next: () => { + if (index < keys.length) { + const key = keys[index] + index++ + return { value: target.get(key) as ValueT, done: false } + } + return { value: undefined, done: true } + } + } + } + } +} + +export function entries< + KeyT, + ValueT, + T extends IteratorCompatible +>(target: T): Iterable<[KeyT, ValueT]> { + return { + [Symbol.iterator]: (): Iterator<[KeyT, ValueT]> => { + const keys = Array.from(target.keys()) + let index = 0 + return { + next: () => { + if (index < keys.length) { + const key = keys[index] + const value = target.get(key) + if (value === undefined) + throw new Error(`Unexpected undefined value for key: ${key}`) + index++ + return { value: [key, value], done: false } + } + return { value: undefined, done: true } + } + } + } + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..51ee1f3 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "esnext", // Use ESNext as the output + "module": "nodenext", // Keep commonjs for Node; change to "esnext" if you want native ESM output + "lib": ["esnext", "dom"], // Update lib to ESNext features + "types": ["node"], + "strict": true, + "noImplicitAny": true, + "noImplicitThis": true, + "alwaysStrict": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "strictBindCallApply": true, + "strictPropertyInitialization": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "moduleResolution": "nodenext", + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "outDir": "./dist" + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "**/*.spec.ts"] +} diff --git a/tsup.config.ts b/tsup.config.ts new file mode 100644 index 0000000..f28e2f7 --- /dev/null +++ b/tsup.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from 'tsup' + +export default defineConfig({ + entry: ['src/index.ts'], // Entry point for your library/application + format: ['cjs', 'esm'], // Bundle in CommonJS and ES Module formats + outDir: 'dist', + dts: true, // Generate TypeScript declaration files + name: 'MaimaiAPI', + target: 'esnext', + clean: true, // Clean output directory before each build + minify: false // Set to true if you need minification +})